Compression.LZMA1SmallDecompressor.pas 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. unit Compression.LZMA1SmallDecompressor;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Interface to the older, size-optimized LZMA SDK 4.43 decompression OBJ in
  8. Compression.LZMA1SmallDecompressor\LzmaDecode, used by SetupLdr.
  9. This OBJ uses UInt32 for dictionary and buffer sizes, even in its 64-bit
  10. build, because LZMA_SYSTEM_SIZE_T is not defined.
  11. }
  12. interface
  13. uses
  14. Windows, SysUtils, Compression.Base;
  15. type
  16. { Internally-used record }
  17. TLZMAInternalDecoderState = record
  18. { NOTE: None of these fields are ever accessed directly by this unit.
  19. They are exposed purely for debugging purposes. }
  20. opaque_Properties: record
  21. lc: Integer;
  22. lp: Integer;
  23. pb: Integer;
  24. DictionarySize: LongWord;
  25. end;
  26. opaque_Probs: Pointer;
  27. opaque_Buffer: Pointer;
  28. opaque_BufferLim: Pointer;
  29. opaque_Dictionary: Pointer;
  30. opaque_Range: LongWord;
  31. opaque_Code: LongWord;
  32. opaque_DictionaryPos: LongWord;
  33. opaque_GlobalPos: LongWord;
  34. opaque_DistanceLimit: LongWord;
  35. opaque_Reps: array[0..3] of LongWord;
  36. opaque_State: Integer;
  37. opaque_RemainLen: Integer;
  38. opaque_TempDictionary: array[0..3] of Byte;
  39. end;
  40. TLZMA1SmallDecompressor = class(TCustomDecompressor)
  41. private
  42. FReachedEnd: Boolean;
  43. FHeaderProcessed: Boolean;
  44. FDecoderState: TLZMAInternalDecoderState;
  45. FHeapBase: Pointer;
  46. FHeapSize: Cardinal;
  47. FBuffer: array[0..65535] of Byte;
  48. procedure DestroyHeap;
  49. procedure DoRead(var Buffer: Pointer; var BufferSize: Cardinal);
  50. procedure ProcessHeader;
  51. public
  52. destructor Destroy; override;
  53. procedure DecompressInto(var Buffer; Count: Cardinal); override;
  54. procedure Reset; override;
  55. end;
  56. implementation
  57. const
  58. SLZMADataError = 'lzma1smalldecompressor: Compressed data is corrupted (%d)';
  59. procedure LZMADataError(const Id: Integer);
  60. begin
  61. raise ECompressDataError.CreateFmt(SLZMADataError, [Id]);
  62. end;
  63. procedure LZMAInternalError(const Msg: String);
  64. begin
  65. raise ECompressInternalError.CreateFmt('lzma1smalldecompressor: %s', [Msg]);
  66. end;
  67. procedure LZMAInternalErrorFmt(const Msg: String; const Args: array of const);
  68. begin
  69. LZMAInternalError(Format(Msg, Args));
  70. end;
  71. { TLZMA1SmallDecompressor }
  72. {$IFNDEF WIN64}
  73. {$L Src\Compression.LZMA1SmallDecompressor\LzmaDecode\LzmaDecodeInno-x86.obj}
  74. {$ELSE}
  75. {$L Src\Compression.LZMA1SmallDecompressor\LzmaDecode\LzmaDecodeInno-x64.obj}
  76. {$ENDIF}
  77. type
  78. TLzmaInCallback = record
  79. Read: function(obj: Pointer; var buffer: Pointer; var bufferSize: Cardinal): Integer; cdecl;
  80. end;
  81. const
  82. LZMA_RESULT_OK = 0;
  83. LZMA_RESULT_DATA_ERROR = 1;
  84. LZMA_PROPERTIES_SIZE = 5;
  85. function LzmaMyDecodeProperties(var vs: TLZMAInternalDecoderState;
  86. vsSize: Integer; const propsData; propsDataSize: Integer;
  87. var outPropsSize: LongWord; var outDictionarySize: LongWord): Integer; cdecl; external name {$IFNDEF WIN64} '_LzmaMyDecodeProperties' {$ELSE} 'LzmaMyDecodeProperties' {$ENDIF};
  88. procedure LzmaMyDecoderInit(var vs: TLZMAInternalDecoderState;
  89. probsPtr: Pointer; dictionaryPtr: Pointer); cdecl; external name {$IFNDEF WIN64} '_LzmaMyDecoderInit' {$ELSE} 'LzmaMyDecoderInit' {$ENDIF};
  90. function LzmaDecode(var vs: TLZMAInternalDecoderState;
  91. var inCallback: TLzmaInCallback; var outStream; outSize: Cardinal;
  92. var outSizeProcessed: Cardinal): Integer; cdecl; external name {$IFNDEF WIN64} '_LzmaDecode' {$ELSE} 'LzmaDecode' {$ENDIF};
  93. type
  94. TLZMADecompressorCallbackData = record
  95. Callback: TLzmaInCallback;
  96. Instance: TLZMA1SmallDecompressor;
  97. end;
  98. function ReadFunc(obj: Pointer; var buffer: Pointer; var bufferSize: Cardinal): Integer; cdecl;
  99. begin
  100. TLZMADecompressorCallbackData(obj^).Instance.DoRead(buffer, bufferSize);
  101. { Don't bother returning any sort of failure code, because if DoRead failed,
  102. it would've raised an exception }
  103. Result := LZMA_RESULT_OK;
  104. end;
  105. destructor TLZMA1SmallDecompressor.Destroy;
  106. begin
  107. DestroyHeap;
  108. inherited;
  109. end;
  110. procedure TLZMA1SmallDecompressor.DestroyHeap;
  111. begin
  112. FHeapSize := 0;
  113. if Assigned(FHeapBase) then begin
  114. VirtualFree(FHeapBase, 0, MEM_RELEASE);
  115. FHeapBase := nil;
  116. end;
  117. end;
  118. procedure TLZMA1SmallDecompressor.DoRead(var Buffer: Pointer; var BufferSize: Cardinal);
  119. begin
  120. Buffer := @FBuffer;
  121. BufferSize := 0;
  122. if not FReachedEnd then begin
  123. BufferSize := ReadProc(FBuffer, SizeOf(FBuffer));
  124. if BufferSize = 0 then
  125. FReachedEnd := True; { not really necessary, but for consistency }
  126. end;
  127. end;
  128. procedure TLZMA1SmallDecompressor.ProcessHeader;
  129. var
  130. Props: array[0..LZMA_PROPERTIES_SIZE-1] of Byte;
  131. ProbsSize, DictionarySize: LongWord;
  132. NewHeapSize: Cardinal;
  133. begin
  134. { Read header fields }
  135. if ReadProc(Props, SizeOf(Props)) <> SizeOf(Props) then
  136. LZMADataError(1);
  137. { Initialize the LZMA decoder state structure, and calculate the size of
  138. the Probs and Dictionary }
  139. FillChar(FDecoderState, SizeOf(FDecoderState), 0);
  140. if LzmaMyDecodeProperties(FDecoderState, SizeOf(FDecoderState), Props,
  141. SizeOf(Props), ProbsSize, DictionarySize) <> LZMA_RESULT_OK then
  142. LZMADataError(3);
  143. if DictionarySize > LongWord(64 shl 20) then
  144. { sanity check: we only use dictionary sizes <= 64 MB }
  145. LZMADataError(7);
  146. { Allocate memory for the Probs and Dictionary, and pass the pointers over }
  147. NewHeapSize := ProbsSize + DictionarySize;
  148. if FHeapSize <> NewHeapSize then begin
  149. DestroyHeap;
  150. FHeapBase := VirtualAlloc(nil, NewHeapSize, MEM_COMMIT, PAGE_READWRITE);
  151. if FHeapBase = nil then
  152. OutOfMemoryError;
  153. FHeapSize := NewHeapSize;
  154. end;
  155. LzmaMyDecoderInit(FDecoderState, FHeapBase, PByte(FHeapBase) + ProbsSize);
  156. FHeaderProcessed := True;
  157. end;
  158. procedure TLZMA1SmallDecompressor.DecompressInto(var Buffer; Count: Cardinal);
  159. var
  160. CallbackData: TLZMADecompressorCallbackData;
  161. Code: Integer;
  162. OutProcessed: Cardinal;
  163. begin
  164. if not FHeaderProcessed then
  165. ProcessHeader;
  166. CallbackData.Callback.Read := ReadFunc;
  167. CallbackData.Instance := Self;
  168. Code := LzmaDecode(FDecoderState, CallbackData.Callback, Buffer, Count,
  169. OutProcessed);
  170. case Code of
  171. LZMA_RESULT_OK: ;
  172. LZMA_RESULT_DATA_ERROR: LZMADataError(5);
  173. else
  174. LZMAInternalErrorFmt('LzmaDecode failed (%d)', [Code]);
  175. end;
  176. if OutProcessed <> Cardinal(Count) then
  177. LZMADataError(6);
  178. end;
  179. procedure TLZMA1SmallDecompressor.Reset;
  180. begin
  181. FHeaderProcessed := False;
  182. FReachedEnd := False;
  183. end;
  184. end.