Compression.LZMA1SmallDecompressor.pas 6.2 KB

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