Inno-Setup-issrc/Projects/Src/Compression.LZMA1SmallDecompressor.pas
2024-10-02 19:29:01 +02:00

208 lines
6.2 KiB
ObjectPascal

unit Compression.LZMA1SmallDecompressor;
{
Inno Setup
Copyright (C) 1997-2024 Jordan Russell
Portions by Martijn Laan
For conditions of distribution and use, see LICENSE.TXT.
Interface to the older, size-optimized LZMA SDK 4.43 decompression OBJ in
Compression.LZMA1SmallDecompressor\LzmaDecode, used by SetupLdr.
}
interface
uses
Windows, SysUtils, Compression.Base, Shared.Int64Em;
type
{ Internally-used record }
TLZMAInternalDecoderState = record
{ NOTE: None of these fields are ever accessed directly by this unit.
They are exposed purely for debugging purposes. }
opaque_Properties: record
lc: Integer;
lp: Integer;
pb: Integer;
DictionarySize: LongWord;
end;
opaque_Probs: Pointer;
opaque_Buffer: Pointer;
opaque_BufferLim: Pointer;
opaque_Dictionary: Pointer;
opaque_Range: LongWord;
opaque_Code: LongWord;
opaque_DictionaryPos: LongWord;
opaque_GlobalPos: LongWord;
opaque_DistanceLimit: LongWord;
opaque_Reps: array[0..3] of LongWord;
opaque_State: Integer;
opaque_RemainLen: Integer;
opaque_TempDictionary: array[0..3] of Byte;
end;
TLZMA1SmallDecompressor = class(TCustomDecompressor)
private
FReachedEnd: Boolean;
FHeaderProcessed: Boolean;
FDecoderState: TLZMAInternalDecoderState;
FHeapBase: Pointer;
FHeapSize: Cardinal;
FBuffer: array[0..65535] of Byte;
procedure DestroyHeap;
procedure DoRead(var Buffer: Pointer; var BufferSize: Cardinal);
procedure ProcessHeader;
public
destructor Destroy; override;
procedure DecompressInto(var Buffer; Count: Longint); override;
procedure Reset; override;
end;
implementation
const
SLZMADataError = 'lzma1smalldecompressor: Compressed data is corrupted (%d)';
procedure LZMADataError(const Id: Integer);
begin
raise ECompressDataError.CreateFmt(SLZMADataError, [Id]);
end;
procedure LZMAInternalError(const Msg: String);
begin
raise ECompressInternalError.CreateFmt('lzma1smalldecompressor: %s', [Msg]);
end;
procedure LZMAInternalErrorFmt(const Msg: String; const Args: array of const);
begin
LZMAInternalError(Format(Msg, Args));
end;
{ TLZMA1SmallDecompressor }
{$L Src\Compression.LZMA1SmallDecompressor\LzmaDecode\LzmaDecodeInno.obj}
type
TLzmaInCallback = record
Read: function(obj: Pointer; var buffer: Pointer; var bufferSize: Cardinal): Integer; cdecl;
end;
const
LZMA_RESULT_OK = 0;
LZMA_RESULT_DATA_ERROR = 1;
LZMA_PROPERTIES_SIZE = 5;
function LzmaMyDecodeProperties(var vs: TLZMAInternalDecoderState;
vsSize: Integer; const propsData; propsDataSize: Integer;
var outPropsSize: LongWord; var outDictionarySize: LongWord): Integer; cdecl; external name '_LzmaMyDecodeProperties';
procedure LzmaMyDecoderInit(var vs: TLZMAInternalDecoderState;
probsPtr: Pointer; dictionaryPtr: Pointer); cdecl; external name '_LzmaMyDecoderInit';
function LzmaDecode(var vs: TLZMAInternalDecoderState;
var inCallback: TLzmaInCallback; var outStream; outSize: Cardinal;
var outSizeProcessed: Cardinal): Integer; cdecl; external name '_LzmaDecode';
type
TLZMADecompressorCallbackData = record
Callback: TLzmaInCallback;
Instance: TLZMA1SmallDecompressor;
end;
function ReadFunc(obj: Pointer; var buffer: Pointer; var bufferSize: Cardinal): Integer; cdecl;
begin
TLZMADecompressorCallbackData(obj^).Instance.DoRead(buffer, bufferSize);
{ Don't bother returning any sort of failure code, because if DoRead failed,
it would've raised an exception }
Result := LZMA_RESULT_OK;
end;
destructor TLZMA1SmallDecompressor.Destroy;
begin
DestroyHeap;
inherited;
end;
procedure TLZMA1SmallDecompressor.DestroyHeap;
begin
FHeapSize := 0;
if Assigned(FHeapBase) then begin
VirtualFree(FHeapBase, 0, MEM_RELEASE);
FHeapBase := nil;
end;
end;
procedure TLZMA1SmallDecompressor.DoRead(var Buffer: Pointer; var BufferSize: Cardinal);
begin
Buffer := @FBuffer;
BufferSize := 0;
if not FReachedEnd then begin
BufferSize := ReadProc(FBuffer, SizeOf(FBuffer));
if BufferSize = 0 then
FReachedEnd := True; { not really necessary, but for consistency }
end;
end;
procedure TLZMA1SmallDecompressor.ProcessHeader;
var
Props: array[0..LZMA_PROPERTIES_SIZE-1] of Byte;
ProbsSize, DictionarySize: LongWord;
NewHeapSize: Cardinal;
begin
{ Read header fields }
if ReadProc(Props, SizeOf(Props)) <> SizeOf(Props) then
LZMADataError(1);
{ Initialize the LZMA decoder state structure, and calculate the size of
the Probs and Dictionary }
FillChar(FDecoderState, SizeOf(FDecoderState), 0);
if LzmaMyDecodeProperties(FDecoderState, SizeOf(FDecoderState), Props,
SizeOf(Props), ProbsSize, DictionarySize) <> LZMA_RESULT_OK then
LZMADataError(3);
if DictionarySize > LongWord(64 shl 20) then
{ sanity check: we only use dictionary sizes <= 64 MB }
LZMADataError(7);
{ Allocate memory for the Probs and Dictionary, and pass the pointers over }
NewHeapSize := ProbsSize + DictionarySize;
if FHeapSize <> NewHeapSize then begin
DestroyHeap;
FHeapBase := VirtualAlloc(nil, NewHeapSize, MEM_COMMIT, PAGE_READWRITE);
if FHeapBase = nil then
OutOfMemoryError;
FHeapSize := NewHeapSize;
end;
LzmaMyDecoderInit(FDecoderState, FHeapBase, Pointer(Cardinal(FHeapBase) + ProbsSize));
FHeaderProcessed := True;
end;
procedure TLZMA1SmallDecompressor.DecompressInto(var Buffer; Count: Longint);
var
CallbackData: TLZMADecompressorCallbackData;
Code: Integer;
OutProcessed: Cardinal;
begin
if not FHeaderProcessed then
ProcessHeader;
CallbackData.Callback.Read := ReadFunc;
CallbackData.Instance := Self;
Code := LzmaDecode(FDecoderState, CallbackData.Callback, Buffer, Count,
OutProcessed);
case Code of
LZMA_RESULT_OK: ;
LZMA_RESULT_DATA_ERROR: LZMADataError(5);
else
LZMAInternalErrorFmt('LzmaDecode failed (%d)', [Code]);
end;
if OutProcessed <> Cardinal(Count) then
LZMADataError(6);
end;
procedure TLZMA1SmallDecompressor.Reset;
begin
FHeaderProcessed := False;
FReachedEnd := False;
end;
end.