CompressZlib.pas 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. unit CompressZlib;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2010 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Declarations for zlib functions & structures
  8. $jrsoftware: issrc/Projects/CompressZlib.pas,v 1.5 2010/09/07 03:09:36 jr Exp $
  9. }
  10. interface
  11. uses
  12. Windows, SysUtils, Compress;
  13. function ZlibInitCompressFunctions(Module: HMODULE): Boolean;
  14. function ZlibInitDecompressFunctions(Module: HMODULE): Boolean;
  15. type
  16. TZAlloc = function(AppData: Pointer; Items, Size: Cardinal): Pointer; stdcall;
  17. TZFree = procedure(AppData, Block: Pointer); stdcall;
  18. TZStreamRec = packed record
  19. next_in: Pointer; { next input byte }
  20. avail_in: Cardinal; { number of bytes available at next_in }
  21. total_in: Cardinal; { total nb of input bytes read so far }
  22. next_out: Pointer; { next output byte should be put here }
  23. avail_out: Cardinal; { remaining free space at next_out }
  24. total_out: Cardinal; { total nb of bytes output so far }
  25. msg: PAnsiChar; { last error message, NULL if no error }
  26. internal: Pointer; { not visible by applications }
  27. zalloc: TZAlloc; { used to allocate the internal state }
  28. zfree: TZFree; { used to free the internal state }
  29. AppData: Pointer; { private data object passed to zalloc and zfree }
  30. data_type: Integer; { best guess about the data type: ascii or binary }
  31. adler: Longint; { adler32 value of the uncompressed data }
  32. reserved: Longint; { reserved for future use }
  33. end;
  34. TZCompressor = class(TCustomCompressor)
  35. private
  36. FCompressionLevel: Integer;
  37. FInitialized: Boolean;
  38. FStrm: TZStreamRec;
  39. FBuffer: array[0..65535] of Byte;
  40. procedure EndCompress;
  41. procedure FlushBuffer;
  42. procedure InitCompress;
  43. protected
  44. procedure DoCompress(const Buffer; Count: Longint); override;
  45. procedure DoFinish; override;
  46. public
  47. constructor Create(AWriteProc: TCompressorWriteProc;
  48. AProgressProc: TCompressorProgressProc; CompressionLevel: Integer;
  49. ACompressorProps: TCompressorProps); override;
  50. destructor Destroy; override;
  51. end;
  52. TZDecompressor = class(TCustomDecompressor)
  53. private
  54. FInitialized: Boolean;
  55. FStrm: TZStreamRec;
  56. FReachedEnd: Boolean;
  57. FBuffer: array[0..65535] of Byte;
  58. public
  59. constructor Create(AReadProc: TDecompressorReadProc); override;
  60. destructor Destroy; override;
  61. procedure DecompressInto(var Buffer; Count: Longint); override;
  62. procedure Reset; override;
  63. end;
  64. implementation
  65. const
  66. SZlibDataError = 'zlib: Compressed data is corrupted';
  67. SZlibInternalError = 'zlib: Internal error. Code %d';
  68. ZLIB_VERSION = '1.2.1'; { Do not change this! }
  69. Z_NO_FLUSH = 0;
  70. Z_PARTIAL_FLUSH = 1;
  71. Z_SYNC_FLUSH = 2;
  72. Z_FULL_FLUSH = 3;
  73. Z_FINISH = 4;
  74. Z_OK = 0;
  75. Z_STREAM_END = 1;
  76. Z_NEED_DICT = 2;
  77. Z_ERRNO = -1;
  78. Z_STREAM_ERROR = -2;
  79. Z_DATA_ERROR = -3;
  80. Z_MEM_ERROR = -4;
  81. Z_BUF_ERROR = -5;
  82. Z_VERSION_ERROR = -6;
  83. var
  84. deflateInit_: function(var strm: TZStreamRec; level: Integer; version: PAnsiChar;
  85. stream_size: Integer): Integer; stdcall;
  86. deflate: function(var strm: TZStreamRec; flush: Integer): Integer; stdcall;
  87. deflateEnd: function(var strm: TZStreamRec): Integer; stdcall;
  88. inflateInit_: function(var strm: TZStreamRec; version: PAnsiChar;
  89. stream_size: Integer): Integer; stdcall;
  90. inflate: function(var strm: TZStreamRec; flush: Integer): Integer; stdcall;
  91. inflateEnd: function(var strm: TZStreamRec): Integer; stdcall;
  92. inflateReset: function(var strm: TZStreamRec): Integer; stdcall;
  93. function ZlibInitCompressFunctions(Module: HMODULE): Boolean;
  94. begin
  95. deflateInit_ := GetProcAddress(Module, 'deflateInit_');
  96. deflate := GetProcAddress(Module, 'deflate');
  97. deflateEnd := GetProcAddress(Module, 'deflateEnd');
  98. Result := Assigned(deflateInit_) and Assigned(deflate) and
  99. Assigned(deflateEnd);
  100. if not Result then begin
  101. deflateInit_ := nil;
  102. deflate := nil;
  103. deflateEnd := nil;
  104. end;
  105. end;
  106. function ZlibInitDecompressFunctions(Module: HMODULE): Boolean;
  107. begin
  108. inflateInit_ := GetProcAddress(Module, 'inflateInit_');
  109. inflate := GetProcAddress(Module, 'inflate');
  110. inflateEnd := GetProcAddress(Module, 'inflateEnd');
  111. inflateReset := GetProcAddress(Module, 'inflateReset');
  112. Result := Assigned(inflateInit_) and Assigned(inflate) and
  113. Assigned(inflateEnd) and Assigned(inflateReset);
  114. if not Result then begin
  115. inflateInit_ := nil;
  116. inflate := nil;
  117. inflateEnd := nil;
  118. inflateReset := nil;
  119. end;
  120. end;
  121. function zlibAllocMem(AppData: Pointer; Items, Size: Cardinal): Pointer; stdcall;
  122. begin
  123. try
  124. GetMem(Result, Items * Size);
  125. except
  126. { trap any exception, because zlib expects a NULL result if it's out
  127. of memory }
  128. Result := nil;
  129. end;
  130. end;
  131. procedure zlibFreeMem(AppData, Block: Pointer); stdcall;
  132. begin
  133. FreeMem(Block);
  134. end;
  135. function Check(const Code: Integer; const ValidCodes: array of Integer): Integer;
  136. var
  137. I: Integer;
  138. begin
  139. if Code = Z_MEM_ERROR then
  140. OutOfMemoryError;
  141. Result := Code;
  142. for I := Low(ValidCodes) to High(ValidCodes) do
  143. if ValidCodes[I] = Code then
  144. Exit;
  145. raise ECompressInternalError.CreateFmt(SZlibInternalError, [Code]);
  146. end;
  147. procedure InitStream(var strm: TZStreamRec);
  148. begin
  149. FillChar(strm, SizeOf(strm), 0);
  150. with strm do begin
  151. zalloc := zlibAllocMem;
  152. zfree := zlibFreeMem;
  153. end;
  154. end;
  155. { TZCompressor }
  156. constructor TZCompressor.Create(AWriteProc: TCompressorWriteProc;
  157. AProgressProc: TCompressorProgressProc; CompressionLevel: Integer;
  158. ACompressorProps: TCompressorProps);
  159. begin
  160. inherited;
  161. FCompressionLevel := CompressionLevel;
  162. InitCompress;
  163. end;
  164. destructor TZCompressor.Destroy;
  165. begin
  166. EndCompress;
  167. inherited;
  168. end;
  169. procedure TZCompressor.InitCompress;
  170. begin
  171. { Note: This really ought to use the more efficient deflateReset when
  172. starting a new stream, but our DLL doesn't currently export it. }
  173. if not FInitialized then begin
  174. InitStream(FStrm);
  175. FStrm.next_out := @FBuffer;
  176. FStrm.avail_out := SizeOf(FBuffer);
  177. Check(deflateInit_(FStrm, FCompressionLevel, zlib_version, SizeOf(FStrm)), [Z_OK]);
  178. FInitialized := True;
  179. end;
  180. end;
  181. procedure TZCompressor.EndCompress;
  182. begin
  183. if FInitialized then begin
  184. FInitialized := False;
  185. deflateEnd(FStrm);
  186. end;
  187. end;
  188. procedure TZCompressor.FlushBuffer;
  189. begin
  190. if FStrm.avail_out < SizeOf(FBuffer) then begin
  191. WriteProc(FBuffer, SizeOf(FBuffer) - FStrm.avail_out);
  192. FStrm.next_out := @FBuffer;
  193. FStrm.avail_out := SizeOf(FBuffer);
  194. end;
  195. end;
  196. procedure TZCompressor.DoCompress(const Buffer; Count: Longint);
  197. begin
  198. InitCompress;
  199. FStrm.next_in := @Buffer;
  200. FStrm.avail_in := Count;
  201. while FStrm.avail_in > 0 do begin
  202. Check(deflate(FStrm, Z_NO_FLUSH), [Z_OK]);
  203. if FStrm.avail_out = 0 then
  204. FlushBuffer;
  205. end;
  206. if Assigned(ProgressProc) then
  207. ProgressProc(Count);
  208. end;
  209. procedure TZCompressor.DoFinish;
  210. begin
  211. InitCompress;
  212. FStrm.next_in := nil;
  213. FStrm.avail_in := 0;
  214. { Note: This assumes FStrm.avail_out > 0. This shouldn't be a problem since
  215. Compress always flushes when FStrm.avail_out reaches 0. }
  216. while Check(deflate(FStrm, Z_FINISH), [Z_OK, Z_STREAM_END]) <> Z_STREAM_END do
  217. FlushBuffer;
  218. FlushBuffer;
  219. EndCompress;
  220. end;
  221. { TZDecompressor }
  222. constructor TZDecompressor.Create(AReadProc: TDecompressorReadProc);
  223. begin
  224. inherited Create(AReadProc);
  225. InitStream(FStrm);
  226. FStrm.next_in := @FBuffer;
  227. FStrm.avail_in := 0;
  228. Check(inflateInit_(FStrm, zlib_version, SizeOf(FStrm)), [Z_OK]);
  229. FInitialized := True;
  230. end;
  231. destructor TZDecompressor.Destroy;
  232. begin
  233. if FInitialized then
  234. inflateEnd(FStrm);
  235. inherited Destroy;
  236. end;
  237. procedure TZDecompressor.DecompressInto(var Buffer; Count: Longint);
  238. begin
  239. FStrm.next_out := @Buffer;
  240. FStrm.avail_out := Count;
  241. while FStrm.avail_out > 0 do begin
  242. if FReachedEnd then { unexpected EOF }
  243. raise ECompressDataError.Create(SZlibDataError);
  244. if FStrm.avail_in = 0 then begin
  245. FStrm.next_in := @FBuffer;
  246. FStrm.avail_in := ReadProc(FBuffer, SizeOf(FBuffer));
  247. { Note: If avail_in is zero while zlib still needs input, inflate() will
  248. return Z_BUF_ERROR. We interpret that as a data error (see below). }
  249. end;
  250. case Check(inflate(FStrm, Z_NO_FLUSH), [Z_OK, Z_STREAM_END, Z_DATA_ERROR, Z_BUF_ERROR]) of
  251. Z_STREAM_END: FReachedEnd := True;
  252. Z_DATA_ERROR, Z_BUF_ERROR: raise ECompressDataError.Create(SZlibDataError);
  253. end;
  254. end;
  255. end;
  256. procedure TZDecompressor.Reset;
  257. begin
  258. FStrm.next_in := @FBuffer;
  259. FStrm.avail_in := 0;
  260. Check(inflateReset(FStrm), [Z_OK]);
  261. FReachedEnd := False;
  262. end;
  263. end.