208 lines
6.2 KiB
ObjectPascal
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.
|