unit SetupEnt;

{
  Inno Setup
  Copyright (C) 1997-2004 Jordan Russell
  Portions by Martijn Laan
  For conditions of distribution and use, see LICENSE.TXT.

  Functions for handling records with embedded long strings

  $jrsoftware: issrc/Projects/SetupEnt.pas,v 1.6 2009/05/27 10:03:49 mlaan Exp $
}

interface

uses
  Compress, MyTypes;

procedure SECompressedBlockRead(const R: TAbstractBlockReader; var Buf;
  const Count: Cardinal; const NumWideStrings, NumAnsiStrings: Integer); overload;
procedure SECompressedBlockRead(const R: TAbstractBlockReader; var Buf;
  const Sizes: TMySetupHeaderInfo); overload;
procedure SECompressedBlockSkip(const R: TAbstractBlockReader;
  const Count: Cardinal; const NumWideStrings, NumAnsiStrings: Integer); overload;
procedure SECompressedBlockSkip(const R: TAbstractBlockReader;
  const Sizes: TMySetupHeaderInfo); overload;

implementation

uses
  PtrMath;

procedure SECompressedBlockRead(const R: TAbstractBlockReader; var Buf;
  const Count: Cardinal; const NumWideStrings, NumAnsiStrings: Integer);
var
  P: Pointer;
  I: Integer;
  Len: Integer;
  S: WideString;
  AnsiS: AnsiString;
begin
  P := @Buf;
  for I := 1 to NumWideStrings do begin
    R.Read(Len, SizeOf(Len));
    SetLength(S, Len div SizeOf(WideChar));
    if Len > 0 then
      R.Read(S[1], Len);
    WideString(P^) := S;
    P := PtrAdd(P, SizeOf(Pointer));
  end;
  for I := 1 to NumAnsiStrings do begin
    R.Read(Len, SizeOf(Len));
    SetLength(AnsiS, Len);
    if Len > 0 then
      R.Read(AnsiS[1], Len);
    AnsiString(P^) := AnsiS;
    P := PtrAdd(P, SizeOf(Pointer));
  end;
  R.Read(P^, Count - (Cardinal(NumWideStrings + NumAnsiStrings) * 4 {file uses 32bit lengths for strings}));
end;

procedure SECompressedBlockRead(const R: TAbstractBlockReader; var Buf;
  const Sizes: TMySetupHeaderInfo);
{$IFDEF WIN64}
type
  TByteArray = packed array[0..65535] of Byte;
var
  Buffer: array of Byte;
  Win32TypeInfo: TWin32TypeInfoItems;
  Source: Pointer;
  Destination: ^TByteArray;
  FieldIndex: Integer;
{$ENDIF}
begin
  {$IFDEF WIN64}
  // Allocate temporary buffer to be transformed from x32 to x64
  // Note that strings are loaded as pointers in SECompressedBlockRead, so I can't use Sizes.SizeInFile here!
  SetLength(Buffer, Sizes.Size);
  SECompressedBlockRead(R, Buffer[0], Sizes.SizeInFile, Sizes.NumStrings, Sizes.NumAnsiStrings);
  // Prepare the conversion data
  Source := @Buffer[0];
  Destination := @Buf;
  Win32TypeInfo := Sizes.GetWin32TypeInfo;
  // Reset everything to zero
  FillChar(Destination^, Sizes.Size, 0);
  // Strings get copied as-is since they have already been made into pointers
  for FieldIndex := 0 to Pred(Sizes.NumStrings + Sizes.NumAnsiStrings) do
  begin
    Move(Source^, Destination[Win32TypeInfo[FieldIndex].Win64Offset], Sizeof(Pointer));
    Source := PtrAdd(Source, Sizeof(Pointer));
  end;
  // Other fields need to be converted from x32 to x64
  for FieldIndex := (Sizes.NumStrings + Sizes.NumAnsiStrings) to Pred(Length(Win32TypeInfo)) do
  begin
    Move(Source^, Destination[Win32TypeInfo[FieldIndex].Win64Offset], Win32TypeInfo[FieldIndex].Win32Size);
    Source := PtrAdd(Source, Win32TypeInfo[FieldIndex].Win32Size);
  end;
  {$ELSE}
  SECompressedBlockRead(R, Buf, Sizes.Size, Sizes.NumStrings, Sizes.NumAnsiStrings);
  {$ENDIF}
end;

procedure SECompressedBlockSkip(const R: TAbstractBlockReader;
  const Count: Cardinal; const NumWideStrings, NumAnsiStrings: Integer);
var
//  P: Pointer;
  I: Integer;
  Len: Integer;
  S: WideString;
  AnsiS: AnsiString;
begin
//  P := @Buf;
  for I := 1 to NumWideStrings do begin
    R.Read(Len, SizeOf(Len));
    SetLength(S, Len div SizeOf(WideChar));
    if Len > 0 then
      R.Read(S[1], Len);
//    String(P^) := S;
//    P := PtrAdd(P, SizeOf(Pointer));
  end;
  for I := 1 to NumAnsiStrings do begin
    R.Read(Len, SizeOf(Len));
    SetLength(AnsiS, Len);
    if Len > 0 then
      R.Read(AnsiS[1], Len);
//    AnsiString(P^) := AnsiS;
//    P :+ PtrAdd(P, SizeOf(Pointer));
  end;
  R.Skip(Count - (Cardinal(NumWideStrings + NumAnsiStrings) * 4 {file uses 32bit lengths for strings}));
end;

procedure SECompressedBlockSkip(const R: TAbstractBlockReader;
  const Sizes: TMySetupHeaderInfo);
begin
  SECompressedBlockSkip(R, {$IFDEF WIN64} Sizes.SizeInFile {$ELSE} Sizes.Size {$ENDIF}, Sizes.NumStrings, Sizes.NumAnsiStrings);
end;

end.
