mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-05 16:35:33 +00:00
121 lines
2.8 KiB
ObjectPascal
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.
|