forked from synopse/mORMot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SynZip.pas
5629 lines (5273 loc) · 180 KB
/
SynZip.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
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/// low-level access to ZLib compression (1.2.5 engine version)
// - this unit is a part of the freeware Synopse framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SynZip;
{
This file is part of Synopse framework.
Synopse framework. Copyright (c) Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
Version: MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the License.
The Original Code is Synopse framework.
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (c)
the Initial Developer. All Rights Reserved.
Contributor(s):
- Alf
- Andre Heider (dhewg)
- ehansen
- jpdk
- Gigo
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****
ORIGINAL LICENSE:
zlib.h -- interface of the 'zlib' general purpose compression library
version 1.2.5, April 19th, 2010
Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jean-loup Gailly
Mark Adler
Cross-platform ZLib implementation
====================================
Link to original C-compiled ZLib library
- Win32: use fast obj and inline asm
- Linux: use available system library libz.so
Also defines .zip file structure (TFileInfo TFileHeader TLastHeader)
}
{$I Synopse.inc} // define HASINLINE CPU32 CPU64
{.$define USEZLIBSSE}
// if defined (only FPC+Win64), will link static\x86_64-win64sse\*.o static libraries
// from https://github.com/cloudflare/zlib (warning: SSE3/SSE4.2 CPUs only)
{.$define USECFZLIB} // https://github.com/cloudflare/zlib as external dll
{$ifdef USECFZLIB}
{$define USEEXTZLIB}
{$else}
{$ifdef FPC}
{$ifdef MSWINDOWS} // avoid link to zlib1.dll
{.$define USEPASZLIB} // paszlib makes Z_BUF_ERROR with bits = -MAX_WBITS
{$ifdef Win32}
{.$define USEZLIBSSE} // SynZLibSSE static .o files for FPC + Win32 fails
{$endif}
{$ifdef Win64}
{.$define USEEXTZLIB} // use zlib-64.dll as in \fpc-win64 sub-folder
{$endif}
{$else}
// will use zlib.so under Linux/Posix
{$ifdef ANDROID}
{$define USEPASZLIB} // Alf: problem with external zlib.so under Android
{$else}
{$define USEEXTZLIB}
{$endif}
{$endif}
{$else}
{$undef USEZLIBSSE} // Delphi linker is buggy as hell
{$ifndef USEEXTZLIB} // define USEEXTZLIB for the project for better performance
{$ifdef MSWINDOWS}
{$ifdef Win32}
{$define USEINLINEASM}
// if defined, we use a special inlined asm version for uncompress:
// seems 50% faster than BC++ generated .obj, and is 3KB smaller in code size
{$endif}
{$ifdef Win64}
{$define USEDELPHIZLIB} // System.ZLib is (much) slower, but static
{.$define USEEXTZLIB} // use faster zlib-64.dll as in \fpc-win64 sub-folder
{$endif}
{$else}
{$define USEEXTZLIB} // e.g. for Kylix
{$endif}
{$endif USEEXTZLIB}
{$endif}
{$endif USECFZLIB}
interface
uses
{$ifdef MSWINDOWS}
Windows,
{$else}
{$ifdef KYLIX3}
LibC,
{$else}
{$ifndef ANDROID}
clocale,
{$endif}
{$endif}
Types,
{$endif}
{$ifdef USEPASZLIB}
zbase,
paszlib,
{$endif}
SysUtils,
Classes;
type
/// the format used for storing data
TSynZipCompressorFormat = (szcfRaw, szcfZip, szcfGZ);
{$ifdef DELPHI5OROLDER}
type // Delphi 5 doesn't have those base types defined :(
PInteger = ^Integer;
PCardinal = ^Cardinal;
IntegerArray = array[0..$effffff] of Integer;
const
PathDelim = '\';
soCurrent = soFromCurrent;
function IncludeTrailingPathDelimiter(const FileName: TFileName): TFileName;
{$endif}
/// in-memory ZLib DEFLATE compression
// - by default, will use the deflate/.zip header-less format, but you may set
// ZlibFormat=true to add an header, as expected by zlib (and pdf)
function CompressMem(src, dst: pointer; srcLen, dstLen: integer;
CompressionLevel: integer=6; ZlibFormat: Boolean=false) : integer;
/// in-memory ZLib INFLATE decompression
// - by default, will use the deflate/.zip header-less format, but you may set
// ZlibFormat=true to add an header, as expected by zlib (and pdf)
function UnCompressMem(src, dst: pointer; srcLen, dstLen: integer; ZlibFormat: Boolean=false) : integer;
/// ZLib DEFLATE compression from memory into a stream
// - by default, will use the deflate/.zip header-less format, but you may set
// ZlibFormat=true to add an header, as expected by zlib (and pdf)
function CompressStream(src: pointer; srcLen: integer;
tmp: TStream; CompressionLevel: integer=6; ZlibFormat: Boolean=false;
TempBufSize: integer=0): cardinal;
/// ZLib INFLATE decompression from memory into a stream
// - return the number of bytes written into the stream
// - if checkCRC if not nil, it will contain the crc32; if aStream is nil, it
// will only calculate the crc of the the uncompressed memory block
// - by default, will use the deflate/.zip header-less format, but you may set
// ZlibFormat=true to add an header, as expected by zlib (and pdf)
function UnCompressStream(src: pointer; srcLen: integer; tmp: TStream;
checkCRC: PCardinal; ZlibFormat: Boolean=false; TempBufSize: integer=0): cardinal;
type
{$ifdef HASCODEPAGE}
ZipString = type RawByteString;
{$else}
/// define a raw storage string type, used for data buffer management
ZipString = type AnsiString;
{$endif}
{$ifdef FPC}
ZipPtrUInt = PtrUInt;
ZipPtrInt = PtrInt;
{$else}
/// as available in FPC
ZipPtrUInt = {$ifdef CPU64}UInt64{$else}cardinal{$endif};
ZipPtrInt = {$ifdef CPU64}Int64{$else}integer{$endif};
{$endif}
/// ZLib INFLATE decompression from memory into a AnsiString (ZipString) variable
// - return the number of bytes written into the string
// - if checkCRC if not nil, it will contain the crc32; if aStream is nil, it
// will only calculate the crc of the the uncompressed memory block
// - by default, will use the deflate/.zip header-less format, but you may set
// ZlibFormat=true to add an header, as expected by zlib (and pdf)
function UnCompressZipString(src: pointer; srcLen: integer; out data: ZipString;
checkCRC: PCardinal; ZlibFormat: Boolean; TempBufSize: integer=0): cardinal;
/// compress some data, with a proprietary format (including CRC)
function CompressString(const data: ZipString; failIfGrow: boolean = false;
CompressionLevel: integer=6) : ZipString;
/// uncompress some data, with a proprietary format (including CRC)
// - return '' in case of a decompression failure
function UncompressString(const data: ZipString) : ZipString;
/// (un)compress a data content using the gzip algorithm
// - as expected by THttpSocket.RegisterCompress
// - will use internaly a level compression of 1, i.e. fastest available (content
// of 4803 bytes is compressed into 700, and time is 440 us instead of 220 us)
function CompressGZip(var DataRawByteString; Compress: boolean): AnsiString;
/// (un)compress a data content using the Deflate algorithm (i.e. "raw deflate")
// - as expected by THttpSocket.RegisterCompress
// - will use internaly a level compression of 1, i.e. fastest available (content
// of 4803 bytes is compressed into 700, and time is 440 us instead of 220 us)
// - deflate content encoding is pretty inconsistent in practice, so slightly
// slower CompressGZip() is preferred - http://stackoverflow.com/a/9186091
function CompressDeflate(var DataRawByteString; Compress: boolean): AnsiString;
/// (un)compress a data content using the zlib algorithm
// - as expected by THttpSocket.RegisterCompress
// - will use internaly a level compression of 1, i.e. fastest available (content
// of 4803 bytes is compressed into 700, and time is 440 us instead of 220 us)
// - zlib content encoding is pretty inconsistent in practice, so slightly
// slower CompressGZip() is preferred - http://stackoverflow.com/a/9186091
function CompressZLib(var DataRawByteString; Compress: boolean): AnsiString;
/// low-level check of the code returned by the ZLib library
function Check(const Code: Integer; const ValidCodes: array of Integer;
const Context: string=''): integer;
type
PCardinalArray = ^TCardinalArray;
TCardinalArray = array[0..(MaxLongint div SizeOf(cardinal))-1] of cardinal;
/// just hash aString with CRC32 algorithm
// - crc32 is better than adler32 for short strings
function CRC32string(const aString: ZipString): cardinal;
type
/// exception raised internaly in case of Zip errors
ESynZipException = class(Exception);
{$ifdef USEZLIBSSE} // statically linked with new 64-bit TZStream
type
TZLong = ZipPtrUint;
TZCRC = Int64;
{$else}
{$ifdef USECFZLIB} // dynamically linked with new 64-bit TZStream
type
TZLong = ZipPtrUint;
TZCRC = Int64;
const
{$ifdef WIN64}
libz='zlibcf64.dll';
{$else}
libz='zlibcf32.dll';
{$endif}
{$else}
{$ifdef USEEXTZLIB}
{$ifdef MSWINDOWS} // dynamically linked with old 32-bit TZStream
type
TZLong = cardinal;
TZCRC = cardinal;
const
{$ifdef WIN2}
libz='zlib-32.dll'; // as available in \fpc-win32 sub-folder
{$endif}
{$ifdef WIN64}
libz='zlib-64.dll'; // as available in \fpc-win64 sub-folder
{$endif}
{$endif MSWINDOWS}
{$ifdef KYLIX3}
type
TZLong = cardinal;
TZCRC = cardinal;
const
libz = 'libz.so.1';
{$else}
{$ifdef UNIX} // dynamically linked with new 64-bit TZStream
type
TZLong = ZipPtrUint;
TZCRC = cardinal;
const
libz='z';
{$linklib libz}
{$endif UNIX}
{$endif KYLIX3}
{$else} // statically linked with old 32-bit TZStream
type
TZLong = cardinal;
TZCRC = cardinal;
{$endif USEEXTZLIB}
{$endif USECFZLIB}
{$endif USEZLIBSSE}
type
{$ifdef USEPASZLIB}
TZStream = z_stream;
{$else}
/// the internal memory structure as expected by the ZLib library
TZStream = record
next_in: PAnsiChar;
avail_in: cardinal;
total_in: TZLong;
next_out: PAnsiChar;
avail_out: cardinal;
total_out: TZLong;
msg: PAnsiChar;
state: pointer;
zalloc: pointer;
zfree: pointer;
opaque: pointer;
data_type: integer;
adler: TZLong;
reserved: TZLong;
end;
{$endif USEPASZLIB}
/// initialize the internal memory structure as expected by the ZLib library
procedure StreamInit(var Stream: TZStream); overload;
/// prepare the internal memory structure as expected by the ZLib library for compression
function DeflateInit(var Stream: TZStream; CompressionLevel: integer;
ZlibFormat: Boolean): Boolean; overload;
// don't know why using objects below produce an Internal Error DT5830
// under Delphi 2009 Update 3 !!!!!
// -> see http://qc.embarcadero.com/wc/qcmain.aspx?d=79792
// it seems that this compiler doesn't like to compile packed objects,
// but all other versions (including Delphi 2009 Update 2) did
// -> do Codegear knows about regression tests?
type
{$A-} { force packed object (not allowed under Delphi 2009) }
PFileInfo = ^TFileInfo;
/// generic file information structure, as used in .zip file format
// - used in any header, contains info about following block
{$ifndef USERECORDWITHMETHODS}TFileInfo = object
{$else}TFileInfo = record{$endif}
neededVersion : word; // $14
flags : word; // 0
zzipMethod : word; // 0=Z_STORED 8=Z_DEFLATED 12=BZ2 14=LZMA
zlastMod : integer; // time in dos format
zcrc32 : dword; // crc32 checksum of uncompressed data
zzipSize : dword; // size of compressed data
zfullSize : dword; // size of uncompressed data
nameLen : word; // length(name)
extraLen : word; // 0
function SameAs(aInfo: PFileInfo): boolean;
function AlgoID: integer; // 1..15 (1=SynLZ e.g.) from flags
procedure SetAlgoID(Algorithm: integer);
function GetUTF8FileName: boolean;
function ExtraUnicodeFileNamePos: PAnsiChar;
procedure SetUTF8FileName;
procedure UnSetUTF8FileName;
end;
TFileInfoExtra = packed record
id: word;
size: word;
// here is extra data of size `size`
end;
PFileInfoExtra = ^TFileInfoExtra;
// See Info-ZIP Unicode Path Extra Field at https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
const
FileInfoExtra_UnicodePathId = $7075;
type
TFileInfoExtraName = packed record
id: word; // UPath 0x7075
size: word;
version: byte; // version of this extra field, currently 1
nameCRC32: cardinal; // File Name Field CRC32 Checksum
utf8Name: AnsiChar; // UTF-8 version of the entry File Name of `size` bytes length
end;
PFileInfoExtraName = ^TFileInfoExtraName;
/// directory file information structure, as used in .zip file format
// - used at the end of the zip file to recap all entries
TFileHeader = {$ifdef USERECORDWITHMETHODS}record
{$else}object{$endif}
signature : dword; // $02014b50 PK#1#2
madeBy : word; // $0314 = OS + version
fileInfo : TFileInfo;
commentLen : word; // 0
firstDiskNo : word; // 0
intFileAttr : word; // 0 = binary; 1 = text
extFileAttr : dword; // dos file attributes
localHeadOff : dword; // @TLocalFileHeader
function IsFolder: boolean; {$ifdef HASINLINE}inline;{$endif}
procedure Init;
end;
PFileHeader = ^TFileHeader;
/// internal file information structure, as used in .zip file format
// - used locally inside the file stream, followed by the name and then the data
{$ifdef USERECORDWITHMETHODS}TLocalFileHeader = record
{$else}TLocalFileHeader = object{$endif}
signature : dword; // $04034b50 PK#3#4
fileInfo : TFileInfo;
function LocalData: PAnsiChar;
end;
PLocalFileHeader = ^TLocalFileHeader;
/// last header structure, as used in .zip file format
// - this header ends the file and is used to find the TFileHeader entries
TLastHeader = record
signature : dword; // $06054b50 PK#5#6
thisDisk : word; // 0
headerDisk : word; // 0
thisFiles : word; // 1
totalFiles : word; // 1
headerSize : dword; // sizeOf(TFileHeaders + names)
headerOffset : dword; // @TFileHeader
commentLen : word; // 0
end;
PLastHeader = ^TLastHeader;
{$A+}
const
ZLIB_VERSION = '1.2.3';
ZLIB_VERNUM = $1230;
Z_NO_FLUSH = 0;
Z_PARTIAL_FLUSH = 1;
Z_SYNC_FLUSH = 2;
Z_FULL_FLUSH = 3;
Z_FINISH = 4;
Z_BLOCK = 5;
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;
Z_NO_COMPRESSION = 0;
Z_BEST_SPEED = 1;
Z_BEST_COMPRESSION = 9;
Z_DEFAULT_COMPRESSION = -1;
Z_FILTERED = 1;
Z_HUFFMAN_ONLY = 2;
Z_RLE = 3;
Z_FIXED = 4;
Z_DEFAULT_STRATEGY = 0;
Z_BINARY = 0;
Z_ASCII = 1;
Z_UNKNOWN = 2;
Z_STORED = 0;
Z_DEFLATED = 8;
MAX_WBITS = 15; // 32K LZ77 window
DEF_MEM_LEVEL = 8;
Z_NULL = 0;
{$ifdef USEPASZLIB}
function deflateInit2_(var strm: TZStream;
level, method, windowBits, memLevel, strategy: integer;
version: PAnsiChar; stream_size: integer): integer;
function deflate(var strm: TZStream; flush: integer): integer;
function deflateEnd(var strm: TZStream): integer;
function inflateInit2_(var strm: TZStream; windowBits: integer;
version: PAnsiChar; stream_size: integer): integer;
function inflate(var strm: TZStream; flush: integer): integer;
function inflateEnd(var strm: TZStream): integer;
function adler32(adler: cardinal; buf: PAnsiChar; len: cardinal): cardinal;
function crc32(crc: cardinal; buf: PAnsiChar; len: cardinal): cardinal;
function get_crc_table: pointer;
{$else}
{ our very own short implementation of ZLibH }
{$ifdef USEINLINEASM}
function deflateInit2_(var strm: TZStream;
level, method, windowBits, memLevel, strategy: integer;
version: PAnsiChar; stream_size: integer): integer;
function deflate(var strm: TZStream; flush: integer): integer;
function deflateEnd(var strm: TZStream): integer;
function inflateInit2_(var strm: TZStream; windowBits: integer;
version: PAnsiChar; stream_size: integer): integer; stdcall;
function inflate(var strm: TZStream; flush: integer): integer; stdcall;
function inflateEnd(var strm: TZStream): integer; stdcall;
function adler32(adler: cardinal; buf: PAnsiChar; len: cardinal): cardinal;
function crc32(crc: cardinal; buf: PAnsiChar; len: cardinal): cardinal;
function get_crc_table: pointer;
{$else USEINLINEASM}
function deflate(var strm: TZStream; flush: integer): integer; cdecl;
function deflateEnd(var strm: TZStream): integer; cdecl;
function inflate(var strm: TZStream; flush: integer): integer; cdecl;
function inflateEnd(var strm: TZStream): integer; cdecl;
function adler32(adler: TZCRC; buf: PAnsiChar; len: cardinal): TZCRC; cdecl;
function crc32(crc: TZCRC; buf: PAnsiChar; len: cardinal): TZCRC; cdecl;
function deflateInit_(var strm: TZStream; level: integer;
version: PAnsiChar; stream_size: integer): integer; cdecl;
function inflateInit_(var strm: TZStream;
version: PAnsiChar; stream_size: integer): integer; cdecl;
function deflateInit2_(var strm: TZStream;
level, method, windowBits, memLevel, strategy: integer;
version: PAnsiChar; stream_size: integer): integer; cdecl;
function inflateInit2_(var strm: TZStream; windowBits: integer;
version: PAnsiChar; stream_size: integer): integer; cdecl;
{$endif USEINLINEASM}
{$endif USEPASZLIB}
type
/// simple wrapper class to decompress a .gz file into memory or stream/file
{$ifdef USERECORDWITHMETHODS}TGZRead = record
{$else}TGZRead = object{$endif}
private
comp, zsdest: pointer;
zscrc: cardinal;
zssize, zscode: integer;
zs: TZStream;
public
complen: ZipPtrInt;
uncomplen32: cardinal; // modulo 2^32 by gzip design
crc32: cardinal;
unixmodtime: cardinal;
fname, fcomment, extra: PAnsiChar;
/// read and validate the .gz header
// - on success, return true and fill complen/uncomplen/crc32c properties
function Init(gz: PAnsiChar; gzLen: ZipPtrInt): boolean;
/// uncompress the .gz content into a memory buffer
// - warning: won't work as expected if uncomplen32 was truncated to 2^32
function ToMem: ZipString;
/// uncompress the .gz content into a stream
function ToStream(stream: TStream; tempBufSize: integer=0): boolean;
/// uncompress the .gz content into a file
function ToFile(const filename: TFileName; tempBufSize: integer=0): boolean;
/// allow low level iterative decompression using an internal TZStream structure
function ZStreamStart(dest: pointer; destsize: integer): boolean;
/// return true if ZStreamStart() has been successfully called
function ZStreamStarted: boolean; {$ifdef HASINLINE}inline;{$endif}
/// will uncompress into dest/destsize buffer as supplied to ZStreamStart
// - return the number of bytes uncompressed (<=destsize)
// - return 0 if the input stream is finished
function ZStreamNext: integer;
/// any successfull call to ZStreamStart should always run ZStreamDone
// - return true if the crc and the uncompressed size are ok
function ZStreamDone: boolean;
end;
/// uncompress a .gz file content
// - return '' if the .gz content is invalid (e.g. bad crc)
function GZRead(gz: PAnsiChar; gzLen: integer): ZipString;
/// compress a file content into a new .gz file
// - will use TSynZipCompressor for minimal memory use during file compression
function GZFile(const orig, destgz: TFileName; CompressionLevel: Integer=6): boolean;
const
/// operating-system dependent wildchar to match all files in a folder
ZIP_FILES_ALL = {$ifdef MSWINDOWS}'*.*'{$else}'*'{$endif};
type
/// a simple TStream descendant for compressing data into a stream
// - this simple version don't use any internal buffer, but rely
// on Zip library buffering system
// - the version in SynZipFiles is much more powerfull, but this one
// is sufficient for most common cases (e.g. for on the fly .gz backup)
TSynZipCompressor = class(TStream)
private
fInitialized: Boolean;
fDestStream: TStream;
fStrm: TZStream;
fCRC: Cardinal;
fGZFormat: boolean;
fBufferOut: array[word] of byte; // a 64 KB buffer
function FlushBufferOut: integer;
public
/// create a compression stream, writting the compressed data into
// the specified stream (e.g. a file stream)
constructor Create(outStream: TStream; CompressionLevel: Integer;
Format: TSynZipCompressorFormat = szcfRaw);
/// release memory
destructor Destroy; override;
/// this method will raise an error: it's a compression-only stream
function Read(var Buffer; Count: Longint): Longint; override;
/// add some data to be compressed
function Write(const Buffer; Count: Longint): Longint; override;
/// used to return the current position, i.e. the real byte written count
// - for real seek, this method will raise an error: it's a compression-only stream
function Seek(Offset: Longint; Origin: Word): Longint; override;
/// the number of byte written, i.e. the current uncompressed size
function SizeIn: cardinal;
/// the number of byte sent to the destination stream, i.e. the current
// compressed size
function SizeOut: cardinal;
/// write all pending compressed data into outStream
procedure Flush;
/// the current CRC of the written data, i.e. the uncompressed data CRC
property CRC: cardinal read fCRC;
end;
/// stores an entry of a file inside a .zip archive
TZipEntry = record
/// the information of this file, as stored locally in the .zip archive
// - note that infoLocal^.zzipSize/zfullSize/zcrc32 may be 0 if the info
// was stored in a "data descriptor" block after the data: in this case,
// you should use TZipRead.RetrieveFileInfo() instead of this structure
infoLocal: PFileInfo;
/// the information of this file, as stored at the end of the .zip archive
// - may differ from infoLocal^ content, depending of the zipper tool used
infoDirectory: PFileHeader;
/// points to the compressed data in the .zip archive, mapped in memory
data: PAnsiChar;
/// name of the file inside the .zip archive
// - not ASCIIZ: length = infoLocal.nameLen
storedName: PAnsiChar;
/// name of the file inside the .zip archive
// - converted from DOS/OEM or UTF-8 into generic (Unicode) string
zipName: TFileName;
end;
/// read-only access to a .zip archive file
// - can open directly a specified .zip file (will be memory mapped for fast access)
// - can open a .zip archive file content from a resource (embedded in the executable)
// - can open a .zip archive file content from memory
TZipRead = class
private
buf: PByteArray;
FirstFileHeader: PFileHeader;
ReadOffset: cardinal;
{$ifdef MSWINDOWS}
file_, map: ZipPtrUint;
{$else}
file_: THandle;
mapSize: cardinal;
{$endif}
procedure UnMap;
function UnZipStream(aIndex: integer; const aInfo: TFileInfo; aDest: TStream): boolean;
public
/// the number of files inside a .zip archive
Count: integer;
/// the files inside the .zip archive
Entry: array of TZipEntry;
/// open a .zip archive file as Read Only
constructor Create(const aFileName: TFileName; ZipStartOffset: cardinal=0;
Size: cardinal=0); overload;
/// open a .zip archive file directly from a resource
constructor Create(Instance: THandle; const ResName: string; ResType: PChar); overload;
/// open a .zip archive file from its File Handle
constructor Create(aFile: THandle; ZipStartOffset: cardinal=0;
Size: cardinal=0); overload;
/// open a .zip archive file directly from memory
constructor Create(BufZip: PByteArray; Size: cardinal); overload;
/// release associated memory
destructor Destroy; override;
/// get the index of a file inside the .zip archive
function NameToIndex(const aName: TFileName): integer;
/// uncompress a file stored inside the .zip archive into memory
function UnZip(aIndex: integer): ZipString; overload;
/// uncompress a file stored inside the .zip archive into a stream
function UnZip(aIndex: integer; aDest: TStream): boolean; overload;
/// uncompress a file stored inside the .zip archive into a destination directory
function UnZip(aIndex: integer; const DestDir: TFileName;
DestDirIsFileName: boolean=false): boolean; overload;
/// uncompress a file stored inside the .zip archive into memory
function UnZip(const aName: TFileName): ZipString; overload;
/// uncompress a file stored inside the .zip archive into a destination directory
function UnZip(const aName, DestDir: TFileName;
DestDirIsFileName: boolean=false): boolean; overload;
/// uncompress all fields stored inside the .zip archive into the supplied
// destination directory
// - returns -1 on success, or the index in Entry[] of the failing file
function UnZipAll(DestDir: TFileName): integer;
/// retrieve information about a file
// - in some cases (e.g. for a .zip created by latest Java JRE),
// infoLocal^.zzipSize/zfullSize/zcrc32 may equal 0: this method is able
// to retrieve the information either from the ending "central directory",
// or by searching the "data descriptor" block
// - returns TRUE if the Index is correct and the info was retrieved
// - returns FALSE if the information was not successfully retrieved
function RetrieveFileInfo(Index: integer; var Info: TFileInfo): boolean;
end;
/// abstract write-only access for creating a .zip archive
// - update can be done manualy by using a TZipRead instance and the
// AddFromZip() method
TZipWriteAbstract = class
protected
fAppendOffset: cardinal;
fMagic: cardinal;
function InternalAdd(const zipName: TFileName; Buf: pointer; Size: integer): cardinal;
function InternalWritePosition: cardinal; virtual; abstract;
procedure InternalWrite(const buf; len: cardinal); virtual; abstract;
public
/// the total number of entries
Count: integer;
/// the resulting file entries, ready to be written as a .zip catalog
// - those will be appended after the data blocks at the end of the .zip file
Entry: array of record
/// the file name, as stored in the .zip internal directory
// - the path delimiter is forced to '/' just before writing to disk,
// as requested by 4.4.17 of reference PKware appnote
intName: ZipString;
/// the corresponding file header
fhr: TFileHeader;
end;
/// initialize the .zip archive
// - a new .zip file content is prepared
constructor Create;
/// compress (using the deflate method) a memory buffer, and add it to the zip file
// - by default, the 1st of January, 2010 is used if not date is supplied
procedure AddDeflated(const aZipName: TFileName; Buf: pointer; Size: integer;
CompressLevel: integer=6; FileAge: integer=1+1 shl 5+30 shl 9); overload;
/// add a memory buffer to the zip file, without compression
// - content is stored, not deflated
// (in that case, no deflate code is added to the executable)
// - by default, the 1st of January, 2010 is used if not date is supplied
procedure AddStored(const aZipName: TFileName; Buf: pointer; Size: integer;
FileAge: integer=1+1 shl 5+30 shl 9);
/// add a file from an already compressed zip entry
procedure AddFromZip(Source: TZipRead; EntryIndex: integer);
/// append a file content into the destination file
// - useful to add the initial Setup.exe file, e.g.
procedure Append(const Content: ZipString);
/// release associated memory, and close destination archive
destructor Destroy; override;
end;
/// write-only access for creating a .zip archive file
// - not to be used to update a .zip file, but to create a new one
TZipWrite = class(TZipWriteAbstract)
protected
fFileName: TFileName;
function InternalWritePosition: cardinal; override;
procedure InternalWrite(const buf; len: cardinal); override;
public
/// the associated file handle
Handle: integer;
/// initialize the .zip file
// - a new .zip file content is created
constructor Create(const aFileName: TFileName); overload;
/// initialize an existing .zip file in order to add some content to it
// - warning: AddStored/AddDeflated() won't check for duplicate zip entries
// - this method is very fast, and will increase the .zip file in-place
// (the old content is not copied, new data is appended at the file end)
// - "dummy" parameter exists only to disambiguate constructors for C++
constructor CreateFrom(const aFileName: TFileName; dummy: integer=0);
/// compress (using the deflate method) a file, and add it to the zip file
procedure AddDeflated(const aFileName: TFileName; RemovePath: boolean=true;
CompressLevel: integer=6; ZipName: TFileName=''); overload;
/// compress (using the deflate method) all files within a folder, and
// add it to the zip file
// - if Recursive is TRUE, would include files from nested sub-folders
procedure AddFolder(const FolderName: TFileName; const Mask: TFileName=ZIP_FILES_ALL;
Recursive: boolean=true; CompressLevel: integer=6; ZipFolder: TFileName='');
/// release associated memory, and close destination file
destructor Destroy; override;
end;
/// write-only access for creating a .zip archive into a stream
TZipWriteToStream = class(TZipWriteAbstract)
protected
fDest: TStream;
function InternalWritePosition: cardinal; override;
procedure InternalWrite(const buf; len: cardinal); override;
public
/// initialize the .zip archive
// - a new .zip file content is prepared
constructor Create(aDest: TStream);
end;
/// a TSynLogArchiveEvent handler which will compress older .log files
// into .zip archive files
// - resulting file will be named YYYYMM.zip and will be located in the
// aDestinationPath directory, i.e. TSynLogFamily.ArchivePath+'\log\YYYYMM.zip'
function EventArchiveZip(const aOldLogFileName, aDestinationPath: TFileName): boolean;
implementation
{$ifdef USEDELPHIZLIB}
uses
ZLib;
{$endif USEDELPHIZLIB}
{$ifdef Linux}
uses
{$ifdef FPC}
SynFPCLinux,
BaseUnix;
{$else}
SynKylix;
{$endif}
{$endif Linux}
{$ifdef DELPHI5OROLDER}
function IncludeTrailingPathDelimiter(const FileName: TFileName): TFileName;
begin
result := IncludeTrailingBackslash(FileName);
end;
{$endif}
const
// those constants have +1 to avoid finding it in the exe
FIRSTHEADER_SIGNATURE_INC = $04034b50+1; // PK#3#4
LASTHEADER_SIGNATURE_INC = $06054b50+1; // PK#5#6
ENTRY_SIGNATURE_INC = $02014b50+1; // PK#1#2
{ TZipWrite }
var
EventArchiveZipWrite: TZipWrite = nil;
function EventArchiveZip(const aOldLogFileName, aDestinationPath: TFileName): boolean;
var n: integer;
begin
result := false;
if aOldLogFileName='' then
FreeAndNil(EventArchiveZipWrite) else begin
if not FileExists(aOldLogFileName) then
exit;
if EventArchiveZipWrite=nil then
EventArchiveZipWrite := TZipWrite.CreateFrom(
system.copy(aDestinationPath,1,length(aDestinationPath)-1)+'.zip');
n := EventArchiveZipWrite.Count;
EventArchiveZipWrite.AddDeflated(aOldLogFileName,True);
if (EventArchiveZipWrite.Count=n+1) and DeleteFile(aOldLogFileName) then
result := True;
end;
end;
function Is7BitAnsi(P: PChar): boolean;
begin
if P<>nil then
while true do
if ord(P^)=0 then
break else
if ord(P^)<=126 then
inc(P) else begin
result := false;
exit;
end;
result := true;
end;
{ TZipWriteAbstract }
constructor TZipWriteAbstract.Create;
begin
fMagic := FIRSTHEADER_SIGNATURE_INC; // +1 to avoid finding it in the exe generated code
dec(fMagic);
end;
function TZipWriteAbstract.InternalAdd(const zipName: TFileName; Buf: pointer; Size: integer): cardinal;
var
i: integer; // ZipPtrInt make a compilation error on Delphi Win64 :(
begin
with Entry[Count] do begin
fHr.signature := ENTRY_SIGNATURE_INC; // +1 to avoid finding it in the exe
dec(fHr.signature);
fHr.madeBy := $0314; // where $03=Unix (for proper UTF8 filenames) and $14=version
fHr.fileInfo.neededVersion := $14;
result := InternalWritePosition;
fHr.localHeadOff := result-fAppendOffset;
{$ifndef DELPHI5OROLDER}
// Delphi 5 doesn't have UTF8Decode/UTF8Encode functions -> make 7 bit version
if Is7BitAnsi(pointer(zipName)) then begin
{$endif}
{$ifdef UNICODE}
intName := AnsiString(zipName);
{$else} // intName := zipName -> error reference count under Delphi 6
SetString(intName,PAnsiChar(pointer(zipName)),length(zipName));
{$endif}
fHr.fileInfo.UnSetUTF8FileName;
{$ifndef DELPHI5OROLDER}
end else begin
intName := UTF8Encode(WideString(zipName));
fHr.fileInfo.SetUTF8FileName;
end;
{$endif}
fHr.fileInfo.nameLen := length(intName);
fhr.fileInfo.extraLen := 0; // source may have something here
InternalWrite(fMagic,sizeof(fMagic));
InternalWrite(fhr.fileInfo,sizeof(fhr.fileInfo));
for i := 1 to integer(fhr.fileInfo.nameLen) do
if intName[i]='\' then
intName[i] := '/'; // as stated by 4.4.17 of reference PKware appnote
InternalWrite(pointer(intName)^,fhr.fileInfo.nameLen);
end;
if Buf<>nil then begin
InternalWrite(Buf^,Size); // write stored data
inc(Count);
end;
end;
procedure TZipWriteAbstract.AddDeflated(const aZipName: TFileName; Buf: pointer;
Size, CompressLevel, FileAge: integer);
var tmp: pointer;
tmpsize: integer;
begin
if self=nil then
exit;
if Count>=length(Entry) then
SetLength(Entry,length(Entry)+20);
with Entry[Count] do begin
with fhr.fileInfo do begin
zcrc32 := SynZip.crc32(0,Buf,Size);
zfullSize := Size;
zzipMethod := Z_DEFLATED;
zlastMod := FileAge;
tmpsize := (Int64(Size)*11) div 10+12;
Getmem(tmp,tmpSize);
zzipSize := CompressMem(Buf,tmp,Size,tmpSize,CompressLevel);
InternalAdd(aZipName,tmp,zzipSize); // write deflated data and inc(Count)
Freemem(tmp);
end;
end;
end;
procedure TZipWriteAbstract.AddStored(const aZipName: TFileName; Buf: pointer;
Size, FileAge: integer);
begin
if self=nil then
exit;
if Count>=length(Entry) then
SetLength(Entry,length(Entry)+20);
with Entry[Count], fhr.fileInfo do begin
zcrc32 := SynZip.crc32(0,Buf,Size);
zfullSize := Size;
zzipSize := Size;
zlastMod := FileAge;
InternalAdd(aZipName,Buf,Size); // write stored data and inc(Count)
end;
end;
procedure TZipWriteAbstract.AddFromZip(Source: TZipRead; EntryIndex: integer);
var s: ^TZipEntry;
origZipName: TFileName;
begin
if (self=nil) or (Source=nil) then
exit;
if Count>=length(Entry) then
SetLength(Entry,length(Entry)+20);
with Entry[Count] do
if Source.RetrieveFileInfo(EntryIndex, fhr.FileInfo) then begin
s := @Source.Entry[EntryIndex];
// backslash in s^.storedName are replaced to '\' by TZipRead.Create,
// accoding to ZIP file format "All slashes MUST be forward slashes '/' as opposed to
// backwards slashes '\'"
SetString(origZipName,s^.storedName,s^.infoLocal.nameLen);
InternalAdd(origZipName,s^.data,fhr.fileInfo.zzipSize);
end;
end;
procedure TZipWriteAbstract.Append(const Content: ZipString);
begin
if (self=nil) or (fAppendOffset<>0) then
exit;
fAppendOffset := length(Content);
InternalWrite(pointer(Content)^,fAppendOffset);
end;
destructor TZipWriteAbstract.Destroy;
var lhr: TLastHeader;
i: integer;
begin
fillchar(lhr,sizeof(lhr),0);
lhr.signature := LASTHEADER_SIGNATURE_INC;