Compression.LZMADecompressor.pas 11 KB

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