spiegel-keyman/common/windows/delphi/setup/SFX.pas

121 lines
2.8 KiB
ObjectPascal

unit SFX;
interface
uses
Winapi.Windows;
function ProcessArchive(const ExtPath: string): Boolean;
implementation
uses
System.Classes,
System.SysUtils,
System.Zip;
var
StartOfFile: Int64 = -1;
function FindFirstHeader(s: TStream): BOOLEAN;
var
DOSHeader: TImageDosHeader;
PEHeader: TImageFileHeader;
OptHeader: TImageOptionalHeader;
magic: DWord;
n: Integer;
HEADER_SIGNATURE: DWord;
begin
// We store the local header signature in the file one greater than
// the real value, so we don't falsely detect a zip file entry inside
// the SFX code of the .EXE file
Result := False;
HEADER_SIGNATURE:=$04034B51; // intentionally 1 higher than the real sig
dec(HEADER_SIGNATURE); // fix the sig in RAM only
s.Seek(0, TSeekOrigin.soBeginning);
s.Read(DOSHeader, sizeof(DOSHeader));
s.Seek(DOSHeader._lfanew, TSeekOrigin.soBeginning);
s.Read(magic, 4);
if magic <> $4550 then Exit;
s.Read(PEHeader, sizeof(PEHeader));
s.Read(OptHeader, sizeof(OptHeader));
n := OptHeader.FileAlignment;
StartOfFile := -1;
// Scan file by alignment chunks for PK header
while n < s.Size do
begin
s.Seek(n, TSeekOrigin.soBeginning);
s.Read(magic, 4);
if magic = HEADER_SIGNATURE then
begin
StartOfFile := n;
Exit(True);
end;
Inc(n, OptHeader.FileAlignment);
end;
Result := StartOfFile >= 0;
end;
//
// This is not good for very large archives. We are assuming the archive
// is small enough to load into memory which should be fine on all modern PCs
// for Keyman and Keyman Developer. If files get bigger than a couple
// of hundred megs, then we should consider saving to temp disk file instead.
//
// All this is due to RSP-17889. If this gets fixed, we can eliminate the
// copy to TMemoryStream - and probably even the FindFirstHeader call
//
// 2020-07-07: RSP-17889 checked against Delphi 10.4 Sydney and not yet fixed.
// https://quality.embarcadero.com/browse/RSP-17889
function ProcessArchive(const ExtPath: string): Boolean;
var
fs: TFileStream;
ms: TMemoryStream;
begin
try
fs := TFileStream.Create(ParamStr(0), fmOpenRead or fmShareDenyWrite);
ms := TMemoryStream.Create;
try
if not FindFirstHeader(fs) then
Exit(False);
fs.Seek(StartOfFile, TSeekOrigin.soBeginning);
ms.CopyFrom(fs, fs.Size - StartOfFile);
ms.Position := 0;
with TZipFile.Create do
try
Open(ms, zmRead);
ExtractAll(ExtPath);
finally
Free;
end;
finally
fs.Free;
ms.Free;
end;
except
on E:Exception do
begin
raise Exception.Create(
'Failed to extract setup archive. '+
'You may have run out of disk space or there may be a '+
'problem with the source files.'#13#10#13#10+
'The error received was: '+E.Message);
end;
end;
Result := True;
end;
end.