Compression.LZMADecompressor.pas 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. unit Compression.LZMADecompressor;
  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 LZMA/LZMA2 SDK decompression OBJs in
  8. Compression.LZMADecompressor\Lzma2Decode, used by Setup.
  9. }
  10. interface
  11. uses
  12. Windows, SysUtils, Compression.Base;
  13. type
  14. TLZMACustomDecompressor = class(TCustomDecompressor)
  15. private
  16. FReachedEnd: Boolean;
  17. FHeaderProcessed: Boolean;
  18. FNextIn: ^Byte;
  19. FAvailIn: Cardinal;
  20. FBuffer: array[0..65535] of Byte;
  21. protected
  22. function DecodeToBuf(var Dest; var DestLen: Cardinal; const Src;
  23. var SrcLen: Cardinal; var Status: Integer): Integer; virtual; abstract;
  24. procedure FreeDecoder; virtual; abstract;
  25. procedure ProcessHeader; virtual; abstract;
  26. public
  27. destructor Destroy; override;
  28. procedure DecompressInto(var Buffer; Count: Longint); override;
  29. procedure Reset; override;
  30. end;
  31. { Internally-used records }
  32. TLZMA1InternalDecoderState = array[0..24] of LongWord;
  33. TLZMA2InternalDecoderState = array[0..28] of LongWord;
  34. TLZMA1Decompressor = class(TLZMACustomDecompressor)
  35. private
  36. FDecoderState: TLZMA1InternalDecoderState;
  37. protected
  38. function DecodeToBuf(var Dest; var DestLen: Cardinal; const Src;
  39. var SrcLen: Cardinal; var Status: Integer): Integer; override;
  40. procedure FreeDecoder; override;
  41. procedure ProcessHeader; override;
  42. end;
  43. TLZMA2Decompressor = class(TLZMACustomDecompressor)
  44. private
  45. FDecoderState: TLZMA2InternalDecoderState;
  46. protected
  47. function DecodeToBuf(var Dest; var DestLen: Cardinal; const Src;
  48. var SrcLen: Cardinal; var Status: Integer): Integer; override;
  49. procedure FreeDecoder; override;
  50. procedure ProcessHeader; override;
  51. end;
  52. implementation
  53. type
  54. TLZMASRes = type Integer;
  55. PLZMAISzAlloc = ^TLZMAISzAlloc;
  56. TLZMAISzAlloc = record
  57. Alloc: function(p: PLZMAISzAlloc; size: Cardinal): Pointer; cdecl;
  58. Free: procedure(p: PLZMAISzAlloc; address: Pointer); cdecl;
  59. end;
  60. const
  61. { SRes (TLZMASRes) }
  62. SZ_OK = 0;
  63. SZ_ERROR_DATA = 1;
  64. SZ_ERROR_MEM = 2;
  65. { ELzmaFinishMode }
  66. LZMA_FINISH_ANY = 0;
  67. LZMA_FINISH_END = 1;
  68. { ELzmaStatus }
  69. LZMA_STATUS_NOT_SPECIFIED = 0;
  70. LZMA_STATUS_FINISHED_WITH_MARK = 1;
  71. LZMA_STATUS_NOT_FINISHED = 2;
  72. LZMA_STATUS_NEEDS_MORE_INPUT = 3;
  73. LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK = 4;
  74. SLZMADecompDataError = 'lzmadecomp: Compressed data is corrupted (%d)';
  75. { To ensure we don't allocate inordinate amounts of memory in the event a
  76. stream's header is corrupted, we limit the dictionary size to the maximum
  77. size the compiler currently allows. }
  78. MaxDictionarySize = 1024 shl 20; { 1 GB - same as ssLZMADictionarySize allows in Compile.pas }
  79. { Compiled by Visual Studio 2022 using compile.bat
  80. To enable source debugging recompile using compile-bcc32c.bat
  81. Note that in a speed test the code produced by bcc32c was about 33% slower }
  82. {$L Src\Compression.LZMADecompressor\Lzma2Decode\ISLzmaDec.obj}
  83. function IS_LzmaDec_Init(var state: TLZMA1InternalDecoderState;
  84. stateSize: Cardinal; const props; propsSize: Cardinal;
  85. const alloc: TLZMAISzAlloc): TLZMASRes; cdecl; external name '_IS_LzmaDec_Init';
  86. function IS_LzmaDec_StateSize: Cardinal; cdecl; external name '_IS_LzmaDec_StateSize';
  87. function LzmaDec_DecodeToBuf(var state: TLZMA1InternalDecoderState; var dest;
  88. var destLen: Cardinal; const src; var srcLen: Cardinal; finishMode: Integer;
  89. var status: Integer): TLZMASRes; cdecl; external name '_LzmaDec_DecodeToBuf';
  90. procedure LzmaDec_Free(var state: TLZMA1InternalDecoderState;
  91. const alloc: TLZMAISzAlloc); cdecl; external name '_LzmaDec_Free';
  92. function IS_Lzma2Dec_Init(var state: TLZMA2InternalDecoderState;
  93. stateSize: Cardinal; prop: Byte; const alloc: TLZMAISzAlloc): TLZMASRes; cdecl;
  94. external name '_IS_Lzma2Dec_Init';
  95. function IS_Lzma2Dec_StateSize: Cardinal; cdecl; external name '_IS_Lzma2Dec_StateSize';
  96. function Lzma2Dec_DecodeToBuf(var state: TLZMA2InternalDecoderState; var dest;
  97. var destLen: Cardinal; const src; var srcLen: Cardinal; finishMode: Integer;
  98. var status: Integer): TLZMASRes; cdecl; external name '_Lzma2Dec_DecodeToBuf';
  99. procedure IS_Lzma2Dec_Free(var state: TLZMA2InternalDecoderState;
  100. const alloc: TLZMAISzAlloc); cdecl; external name '_IS_Lzma2Dec_Free';
  101. function _memcpy(dest, src: Pointer; n: Cardinal): Pointer; cdecl;
  102. begin
  103. Move(src^, dest^, n);
  104. Result := dest;
  105. end;
  106. procedure LZMADecompInternalError(const Msg: String);
  107. begin
  108. raise ECompressInternalError.CreateFmt('lzmadecomp: %s', [Msg]);
  109. end;
  110. procedure LZMADecompDataError(const Id: Integer);
  111. begin
  112. raise ECompressDataError.CreateFmt(SLZMADecompDataError, [Id]);
  113. end;
  114. function LZMAAllocFunc(p: PLZMAISzAlloc; size: Cardinal): Pointer; cdecl;
  115. begin
  116. if (size <> 0) and (size <= Cardinal(MaxDictionarySize)) then
  117. Result := VirtualAlloc(nil, size, MEM_COMMIT, PAGE_READWRITE)
  118. else
  119. Result := nil;
  120. end;
  121. procedure LZMAFreeFunc(p: PLZMAISzAlloc; address: Pointer); cdecl;
  122. begin
  123. if Assigned(address) then
  124. VirtualFree(address, 0, MEM_RELEASE);
  125. end;
  126. const
  127. LZMAAlloc: TLZMAISzAlloc = (Alloc: LZMAAllocFunc; Free: LZMAFreeFunc);
  128. { TLZMACustomDecompressor }
  129. destructor TLZMACustomDecompressor.Destroy;
  130. begin
  131. FreeDecoder;
  132. inherited;
  133. end;
  134. procedure TLZMACustomDecompressor.DecompressInto(var Buffer; Count: Longint);
  135. var
  136. NextOut: ^Byte;
  137. AvailOut: Longint;
  138. OutBytes, InBytes: Cardinal;
  139. Code: TLZMASRes;
  140. DecodeStatus: Integer;
  141. begin
  142. if not FHeaderProcessed then begin
  143. { Reset these following a reset }
  144. FNextIn := @FBuffer;
  145. FAvailIn := 0;
  146. ProcessHeader;
  147. FHeaderProcessed := True;
  148. end;
  149. NextOut := @Buffer;
  150. AvailOut := Count;
  151. while AvailOut > 0 do begin
  152. if (FAvailIn = 0) and not FReachedEnd then begin
  153. FNextIn := @FBuffer;
  154. FAvailIn := ReadProc(FBuffer, SizeOf(FBuffer));
  155. if FAvailIn = 0 then
  156. FReachedEnd := True; { not really necessary, but for consistency }
  157. end;
  158. OutBytes := AvailOut;
  159. InBytes := FAvailIn;
  160. Code := DecodeToBuf(NextOut^, OutBytes, FNextIn^, InBytes, DecodeStatus);
  161. case Code of
  162. SZ_OK: ;
  163. SZ_ERROR_DATA: LZMADecompDataError(3);
  164. else
  165. LZMADecompInternalError(Format('DecodeToBuf failed (%d)', [Code]));
  166. end;
  167. { No bytes in or out indicates corruption somewhere. Possibilities:
  168. - The caller is asking for too many bytes.
  169. - It needs more input but there isn't any left.
  170. - It encountered an "end mark" previously and is now refusing to
  171. process any further input. }
  172. if (InBytes = 0) and (OutBytes = 0) then
  173. LZMADecompDataError(4);
  174. Dec(AvailOut, OutBytes);
  175. Inc(NextOut, OutBytes);
  176. Dec(FAvailIn, InBytes);
  177. Inc(FNextIn, InBytes);
  178. end;
  179. end;
  180. procedure TLZMACustomDecompressor.Reset;
  181. begin
  182. FHeaderProcessed := False;
  183. FReachedEnd := False;
  184. end;
  185. { TLZMA1Decompressor }
  186. procedure TLZMA1Decompressor.FreeDecoder;
  187. begin
  188. LzmaDec_Free(FDecoderState, LZMAAlloc);
  189. end;
  190. procedure TLZMA1Decompressor.ProcessHeader;
  191. var
  192. Props: packed record { size = LZMA_PROPS_SIZE (5) }
  193. Misc: Byte;
  194. DictionarySize: LongWord; { little endian, unaligned }
  195. end;
  196. begin
  197. { Read header fields }
  198. if ReadProc(Props, SizeOf(Props)) <> SizeOf(Props) then
  199. LZMADecompDataError(1);
  200. if Props.DictionarySize > LongWord(MaxDictionarySize) then
  201. LZMADecompDataError(5);
  202. var StateSize := IS_LzmaDec_StateSize;
  203. var DecoderStateSize := SizeOf(FDecoderState);
  204. if StateSize <> DecoderStateSize then
  205. LZMADecompDataError(-StateSize);
  206. { Note: IS_LzmaDec_Init will re-use already-allocated memory if it can.
  207. FDecoderState is assumed to be initialized to zero on the first call. }
  208. case IS_LzmaDec_Init(FDecoderState, DecoderStateSize, Props,
  209. SizeOf(Props), LZMAAlloc) of
  210. SZ_OK: ;
  211. SZ_ERROR_MEM: OutOfMemoryError;
  212. else
  213. { SZ_ERROR_UNSUPPORTED and anything else }
  214. LZMADecompDataError(2);
  215. end;
  216. end;
  217. function TLZMA1Decompressor.DecodeToBuf(var Dest; var DestLen: Cardinal;
  218. const Src; var SrcLen: Cardinal; var Status: Integer): Integer;
  219. begin
  220. Result := LzmaDec_DecodeToBuf(FDecoderState, Dest, DestLen, Src, SrcLen,
  221. LZMA_FINISH_ANY, Status);
  222. end;
  223. { TLZMA2Decompressor }
  224. procedure TLZMA2Decompressor.FreeDecoder;
  225. begin
  226. IS_Lzma2Dec_Free(FDecoderState, LZMAAlloc);
  227. end;
  228. procedure TLZMA2Decompressor.ProcessHeader;
  229. { Macro from Lzma2Dec.c: }
  230. function LZMA2_DIC_SIZE_FROM_PROP(p: LongWord): LongWord;
  231. begin
  232. Result := (2 or (p and 1)) shl (p div 2 + 11);
  233. end;
  234. var
  235. Prop: Byte;
  236. begin
  237. { Read header fields }
  238. if ReadProc(Prop, SizeOf(Prop)) <> SizeOf(Prop) then
  239. LZMADecompDataError(1);
  240. if (Prop >= 40) or
  241. (LZMA2_DIC_SIZE_FROM_PROP(Prop) > LongWord(MaxDictionarySize)) then
  242. LZMADecompDataError(5);
  243. var StateSize := IS_Lzma2Dec_StateSize;
  244. var DecoderStateSize := SizeOf(FDecoderState);
  245. if StateSize <> DecoderStateSize then
  246. LZMADecompDataError(-StateSize);
  247. { Note: IS_Lzma2Dec_Init will re-use already-allocated memory if it can.
  248. FDecoderState is assumed to be initialized to zero on the first call. }
  249. case IS_Lzma2Dec_Init(FDecoderState, DecoderStateSize, Prop,
  250. LZMAAlloc) of
  251. SZ_OK: ;
  252. SZ_ERROR_MEM: OutOfMemoryError;
  253. else
  254. { SZ_ERROR_UNSUPPORTED and anything else }
  255. LZMADecompDataError(2);
  256. end;
  257. end;
  258. function TLZMA2Decompressor.DecodeToBuf(var Dest; var DestLen: Cardinal;
  259. const Src; var SrcLen: Cardinal; var Status: Integer): Integer;
  260. begin
  261. Result := Lzma2Dec_DecodeToBuf(FDecoderState, Dest, DestLen, Src, SrcLen,
  262. LZMA_FINISH_ANY, Status);
  263. end;
  264. end.