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