1 {$INCLUDE ../shared/a_modes.inc}
5 // Implementation restrictions:
6 // - File must start with LFH or EOCD signature
7 // - EOCD must be located strictly at the end of file
8 // - Multi-disk ZIP files are not supported
9 // - Expect UTF-8 or CP1251 encoded names
10 // - ZIP64 not supported
11 // - Encryption not supported
12 // - Zero-length file names not supported
13 // - CDR holds most actual data about file, LFH mostly ignored
14 // - Attributes, comments and extra data are ignored and not saved
15 // - Store and Deflate compression supported
19 uses Classes
, WADEDITOR
;
31 stream
: TMemoryStream
;
37 list
: array of TResource
;
40 PResource
= ^TResource
;
43 TZIPEditor
= class sealed(WADEDITOR
.TWADEditor
)
45 FSection
: array of TSection
;
50 function FindSectionIDRAW(name
: AnsiString
; caseSensitive
: Boolean): Integer;
51 function FindSectionRAW(name
: AnsiString
; caseSensitive
: Boolean): PSection
;
52 function InsertSectionRAW(name
: AnsiString
; mtime
: UInt32
): PSection
;
54 function FindSectionID(name
: AnsiString
): Integer;
55 function FindSection(name
: AnsiString
): PSection
;
56 function InsertSection(name
: AnsiString
; mtime
: UInt32
): PSection
;
58 function InsertFileInfo(const section
, name
: AnsiString
; pos
, csize
, usize
, comp
, crc
, mtime
, flags
: UInt32
): PResource
;
59 function Preload(p
: PResource
): Boolean;
60 function GetSourceStream(p
: PResource
): TStream
;
62 procedure ReadLFH(s
: TStream
; fname
: AnsiString
; xcsize
, xusize
, xcomp
, xcrc
, xtime
, xflags
: UInt32
);
63 procedure ReadCDR(s
: TStream
; cdrid
: Integer);
64 function FindEOCD(s
: TStream
): Boolean;
65 procedure ReadEOCD(s
: TStream
);
67 procedure WriteLFH(s
: TStream
; flags
, comp
, mtime
, crc
, csize
, usize
: UInt32
; const afname
: AnsiString
);
68 procedure WriteCDR(s
: TStream
; flags
, comp
, mtime
, crc
, csize
, usize
, eattr
, offset
: UInt32
; const afname
: AnsiString
; cdrid
: Integer);
69 procedure SaveToStream(s
: TStream
);
73 destructor Destroy(); override;
74 procedure FreeWAD(); override;
75 function ReadFile2(FileName
: string): Boolean; override;
76 function ReadMemory(Data
: Pointer; Len
: LongWord
): Boolean; override;
77 procedure CreateImage(); override;
78 function AddResource(Data
: Pointer; Len
: LongWord
; Name
, Section
: String): Boolean; override; overload
;
79 function AddResource(FileName
, Name
, Section
: String): Boolean; override; overload
;
80 function AddAlias(Res
, Alias
: String): Boolean; override;
81 procedure AddSection(Name
: String); override;
82 procedure RemoveResource(Section
, Resource
: String); override;
83 procedure SaveTo(FileName
: String); override;
84 function HaveResource(Section
, Resource
: String): Boolean; override;
85 function HaveSection(Section
: string): Boolean; override;
86 function GetResource(Section
, Resource
: String; var pData
: Pointer; var Len
: Integer): Boolean; override;
87 function GetSectionList(): SArray
; override;
88 function GetResourcesList(Section
: String): SArray
; override;
90 function GetLastError
: Integer; override;
91 function GetLastErrorStr
: String; override;
92 function GetResourcesCount
: Word; override;
93 function GetVersion
: Byte; override;
98 uses SysUtils
, StrUtils
, DateUtils
, Math
, utils
, zstream
, crc
, e_log
;
101 ZIP_SIGN_CDR
= 'PK'#1#2;
102 ZIP_SIGN_LFH
= 'PK'#3#4;
103 ZIP_SIGN_EOCD
= 'PK'#5#6;
108 ZIP_COMP_REDUCE1
= 2;
109 ZIP_COMP_REDUCE2
= 3;
110 ZIP_COMP_REDUCE3
= 4;
111 ZIP_COMP_REDUCE4
= 5;
112 ZIP_COMP_IMPLODE
= 6;
113 ZIP_COMP_TOKENIZED
= 7;
114 ZIP_COMP_DEFLATE
= 8;
115 ZIP_COMP_DEFLATE64
= 9;
116 ZIP_COMP_TERSE1
= 10;
120 ZIP_COMP_TERSE2
= 18;
127 ZIP_COMP_WAVPACK
= 97;
132 ZIP_SYSTEM
= 0; // DOS / FAT
133 ZIP_MAXVERSION
= 63; // Max supported version
136 ZIP_ENCRYPTION_MASK
= (1 << 0) or (1 << 6) or (1 << 13);
137 ZIP_COMP_MASK
= (1 << 1) or (1 << 2) or (1 << 4) or (1 << 12);
138 ZIP_DATA_MASK
= (1 << 3);
139 ZIP_PATCH_MASK
= (1 << 5);
140 ZIP_UTF8_MASK
= (1 << 11);
141 ZIP_STREAM_MASK
= (1 << 14);
143 function IsASCII(const s
: AnsiString
): Boolean;
146 for i
:= 1 to Length(s
) do
157 function IsUTF8(const s
: AnsiString
): Boolean;
158 var i
, j
, len
: Integer;
161 i
:= 1; len
:= Length(s
);
166 $80..$BF: exit
; // invalid encoding
170 otherwise exit
; // invalid encoding
175 if i
> len
then exit
; // invlid length
178 else exit
; // invalid encoding
187 function DosToStr(dostime
: UInt32
): AnsiString
;
190 DateTimeToString(Result
, 'yyyy/mm/dd hh:nn:ss', DosDateTimeToDateTime(dostime
));
191 except on e
: EConvertError
do
192 Result
:= 'INVALID ($' + IntToHex(dostime
, 8) + ')';
196 procedure ToSectionFile(fname
: AnsiString
; out section
, name
: AnsiString
); inline;
199 i
:= LastDelimiter('/', fname
);
200 section
:= Copy(fname
, 1, i
- 1);
201 name
:= Copy(fname
, i
+ 1)
204 function GetFileName(const Section
, Name
: AnsiString
): AnsiString
; inline;
209 Result
:= Section
+ '/' + Name
;
212 function PrepString(const s
: AnsiString
; caseSensitive
, extSensitive
: Boolean): AnsiString
; inline;
216 if caseSensitive
= False then
218 Result
:= UpperCase(Result
);
220 if extSensitive
= False then
222 i
:= Pos('.', Result
); // fix dotfiles
224 SetLength(Result
, i
- 1);
228 function FindResourceIDRAW(p
: PSection
; name
: AnsiString
; caseSensitive
, extSensitive
: Boolean): Integer;
229 var i
: Integer; pname
: AnsiString
;
233 pname
:= PrepString(name
, caseSensitive
, extSensitive
);
234 for i
:= 0 to High(p
.list
) do
236 if PrepString(p
.list
[i
].name
, caseSensitive
, extSensitive
) = pname
then
246 function FindResourceID(p
: PSection
; name
: AnsiString
): Integer;
249 i
:= FindResourceIDRAW(p
, name
, True, True); // CaSeNaMe.Ext
252 i
:= FindResourceIDRAW(p
, name
, False, True); // CASENAME.EXT
255 i
:= FindResourceIDRAW(p
, name
, True, False); // CaSeNaMe
258 i
:= FindResourceIDRAW(p
, name
, False, False); // CASENAME
265 function FindResource(p
: PSection
; name
: AnsiString
): PResource
;
268 i
:= FindResourceID(p
, name
);
277 function TZIPEditor
.FindSectionIDRAW(name
: AnsiString
; caseSensitive
: Boolean): Integer;
278 var i
: Integer; pname
: AnsiString
;
280 if FSection
<> nil then
282 pname
:= PrepString(name
, caseSensitive
, True);
283 for i
:= 0 to High(FSection
) do
285 if PrepString(FSection
[i
].name
, caseSensitive
, True) = pname
then
295 function TZIPEditor
.FindSectionRAW(name
: AnsiString
; caseSensitive
: Boolean): PSection
;
298 i
:= FindSectionIDRAW(name
, caseSensitive
);
300 Result
:= @FSection
[i
]
305 function TZIPEditor
.InsertSectionRAW(name
: AnsiString
; mtime
: UInt32
): PSection
;
308 if FSection
= nil then i
:= 0 else i
:= Length(FSection
);
309 SetLength(FSection
, i
+ 1);
310 FSection
[i
] := Default(TSection
);
311 FSection
[i
].name
:= name
;
312 FSection
[i
].mtime
:= mtime
;
313 Result
:= @FSection
[i
];
318 function TZIPEditor
.FindSectionID(name
: AnsiString
): Integer;
319 var fixName
: AnsiString
;
321 fixName
:= StringReplace(name
, '\', '/', [rfReplaceAll
], TStringReplaceAlgorithm
.sraManySmall
);
322 Result
:= FindSectionIDRAW(fixName
, True); // CaSeNaMe
324 Result
:= FindSectionIDRAW(fixName
, False); // CASENAME
327 function TZIPEditor
.FindSection(name
: AnsiString
): PSection
;
328 var fixName
: AnsiString
;
330 fixName
:= StringReplace(name
, '\', '/', [rfReplaceAll
], TStringReplaceAlgorithm
.sraManySmall
);
331 Result
:= FindSectionRAW(fixName
, True); // CaSeNaMe
333 Result
:= FindSectionRAW(fixName
, False); // CASENAME
336 function TZIPEditor
.InsertSection(name
: AnsiString
; mtime
: UInt32
): PSection
;
338 Result
:= FindSection(name
);
340 Result
:= InsertSectionRAW(name
, mtime
);
345 function TZIPEditor
.InsertFileInfo(const section
, name
: AnsiString
; pos
, csize
, usize
, comp
, crc
, mtime
, flags
: UInt32
): PResource
;
346 var p
: PSection
; i
: Integer;
348 p
:= FindSectionRAW(section
, True);
350 p
:= InsertSectionRAW(section
, mtime
);
351 if p
.list
= nil then i
:= 0 else i
:= Length(p
.list
);
352 SetLength(p
.list
, i
+ 1);
353 p
.list
[i
] := Default(TResource
);
354 p
.list
[i
].name
:= name
;
355 p
.list
[i
].pos
:= pos
;
356 p
.list
[i
].csize
:= csize
;
357 p
.list
[i
].usize
:= usize
;
358 p
.list
[i
].comp
:= comp
;
359 p
.list
[i
].chksum
:= crc
;
360 p
.list
[i
].mtime
:= mtime
;
361 p
.list
[i
].flags
:= flags
;
362 p
.list
[i
].stream
:= nil;
363 Result
:= @p
.list
[i
];
368 function TZIPEditor
.AddAlias(Res
, Alias
: String): Boolean;
370 // Hard-links not supported in ZIP
371 // However, they never created by editor
375 function TZIPEditor
.AddResource(Data
: Pointer; Len
: LongWord
; Name
, Section
: String): Boolean;
376 const compress
: Boolean = True;
377 const level
: TCompressionLevel
= TCompressionLevel
.clMax
;
378 var s
: TMemoryStream
; cs
: TCompressionStream
; p
: PResource
;
379 var comp
, crc
: UInt32
;
381 Name
:= win2utf(Name
);
382 Section
:= win2utf(Section
);
386 s
:= TMemoryStream
.Create();
388 if compress
and (Len
> 0) then
390 cs
:= TCompressionStream
.Create(level
, s
, True);
392 cs
.WriteBuffer(PByte(Data
)[0], Len
);
394 comp
:= ZIP_COMP_DEFLATE
;
399 if (Len
= 0) or (compress
= False) or (s
.Size
>= Len
) then
401 s
.Seek(0, TSeekOrigin
.soBeginning
);
403 s
.WriteBuffer(PByte(Data
)[0], Len
);
404 comp
:= ZIP_COMP_STORE
;
405 Assert(s
.Size
= Len
);
407 crc
:= crc32(0, nil, 0);
408 crc
:= crc32(crc
, data
, len
);
409 p
:= InsertFileInfo(Section
, Name
, $ffffffff, s
.Size
, Len
, comp
, crc
, DateTimeToDosDateTime(Now()), 0);
419 function TZIPEditor
.AddResource(FileName
, Name
, Section
: String): Boolean;
420 var s
: TFileStream
; ptr
: PByte
;
423 FLastError
:= DFWAD_ERROR_READWAD
;
425 s
:= TFileStream
.Create(FileName
, fmOpenRead
or fmShareDenyWrite
);
429 s
.ReadBuffer(ptr
[0], s
.Size
);
430 Result
:= AddResource(ptr
, s
.Size
, Name
, Section
);
431 if Result
= True then FLastError
:= DFWAD_NOERROR
;
441 if gWADEditorLogLevel
>= DFWAD_LOG_INFO
then
442 e_WriteLog('DFZIP: AddResource: failed to open file ' + FileName
, MSG_NOTIFY
);
443 FLastError
:= DFWAD_ERROR_CANTOPENWAD
;
448 constructor TZIPEditor
.Create();
452 FLastError
:= DFWAD_NOERROR
;
457 destructor TZIPEditor
.Destroy();
463 procedure TZIPEditor
.FreeWAD();
466 if FSection
<> nil then
468 for i
:= 0 to High(FSection
) do
470 if FSection
[i
].list
<> nil then
472 for j
:= 0 to High(FSection
[i
].list
) do
474 if FSection
[i
].list
[j
].stream
<> nil then
476 FreeAndNil(FSection
[i
].list
[j
].stream
);
479 SetLength(FSection
[i
].list
, 0);
482 SetLength(FSection
, 0);
484 if FStream
<> nil then
488 FLastError
:= DFWAD_NOERROR
;
492 function TZIPEditor
.Preload(p
: PResource
): Boolean;
493 var s
: TMemoryStream
;
498 Result
:= p
.stream
<> nil;
499 if (p
.stream
= nil) and (FStream
<> nil) then
501 s
:= TMemoryStream
.Create();
505 FStream
.Seek(p
.pos
, TSeekOrigin
.soBeginning
);
506 s
.CopyFrom(FStream
, p
.csize
);
508 Assert(s
.Size
= p
.csize
); // wtf, random size if copied zero bytes!
518 procedure TZIPEditor
.CreateImage();
521 if FStream
= nil then
523 if gWADEditorLogLevel
>= DFWAD_LOG_DEBUG
then
524 e_WriteLog('DFZIP: CreateImage: File not assigned', MSG_NOTIFY
);
525 FLastError
:= DFWAD_ERROR_WADNOTLOADED
;
527 else if FStream
is TMemoryStream
then
529 if gWADEditorLogLevel
>= DFWAD_LOG_DEBUG
then
530 e_WriteLog('DFZIP: CreateImage: Memory stream', MSG_NOTIFY
);
531 FLastError
:= DFWAD_NOERROR
;
535 if FSection
<> nil then
537 for i
:= 0 to High(FSection
) do
539 if FSection
[i
].list
<> nil then
541 for j
:= 0 to High(FSection
[i
].list
) do
543 if Preload(@FSection
[i
].list
[j
]) = False then
545 if gWADEditorLogLevel
>= DFWAD_LOG_WARN
then
546 e_WriteLog('DFZIP: CreateImage: failed to preload resource [' + FSection
[i
].name
+ '][' + FSection
[i
].list
[j
].name
+ ']', MSG_WARNING
);
547 FLastError
:= DFWAD_ERROR_CANTOPENWAD
;
555 FLastError
:= DFWAD_NOERROR
;
559 procedure TZIPEditor
.AddSection(Name
: String);
561 Name
:= win2utf(Name
);
562 if InsertSection(Name
, DateTimeToDosDateTime(Now())) = nil then
563 raise Exception
.Create('DFZIP: AddSection[' + Name
+ ']: failed to insert');
566 function TZIPEditor
.HaveResource(Section
, Resource
: String): Boolean;
568 Section
:= win2utf(Section
);
569 Resource
:= win2utf(Resource
);
570 Result
:= FindResource(FindSection(Section
), Resource
) <> nil;
573 function TZIPEditor
.HaveSection(Section
: String): Boolean;
575 Section
:= win2utf(Section
);
576 Result
:= FindSection(Section
) <> nil;
579 function TZIPEditor
.GetSourceStream(p
: PResource
): TStream
;
583 if p
.stream
<> nil then
586 src
.Seek(0, TSeekOrigin
.soBeginning
);
588 else if FStream
<> nil then
591 src
.Seek(p
.pos
, TSeekOrigin
.soBeginning
);
596 function TZIPEditor
.GetResource(Section
, Resource
: String; var pData
: Pointer; var Len
: Integer): Boolean;
597 var p
: PResource
; ptr
: PByte
; src
: TStream
; tmp
: TDecompressionStream
; crc
: UInt32
;
599 Section
:= win2utf(Section
);
600 Resource
:= win2utf(Resource
);
601 FLastError
:= DFWAD_ERROR_CANTOPENWAD
;
605 p
:= FindResource(FindSection(Section
), Resource
);
608 src
:= GetSourceStream(p
);
614 Assert(p
.csize
= p
.usize
);
615 GetMem(ptr
, p
.usize
);
618 src
.ReadBuffer(ptr
[0], p
.usize
);
624 except on e
: EReadError
do
625 if gWADEditorLogLevel
>= DFWAD_LOG_WARN
then
626 e_WriteLog('DFZIP: Failed to read STOREd data, reason: ' + e
.Message, MSG_WARNING
);
631 tmp
:= TDecompressionStream
.Create(src
, True);
633 GetMem(ptr
, p
.usize
);
635 tmp
.ReadBuffer(ptr
[0], p
.usize
);
645 on e
: EStreamError
do
647 if gWADEditorLogLevel
>= DFWAD_LOG_INFO
then
648 e_WriteLog('DFZIP: Failed to decompress DEFLATEd data, reason: ' + e
.Message, MSG_WARNING
);
653 if gWADEditorLogLevel
>= DFWAD_LOG_INFO
then
654 e_WriteLog('DFZIP: Unsupported compression method: ' + IntToStr(p
.comp
), MSG_WARNING
);
659 if gWADEditorLogLevel
>= DFWAD_LOG_WARN
then
660 e_WriteLog('DFZIP: No available source for file data', MSG_WARNING
);
661 FLastError
:= DFWAD_ERROR_WADNOTLOADED
;
663 if Result
= True then
665 crc
:= crc32(0, nil, 0);
666 crc
:= crc32(crc
, ptr
, p
.usize
);
667 Result
:= crc
= p
.chksum
;
668 if Result
= True then
672 FLastError
:= DFWAD_NOERROR
;
676 if gWADEditorLogLevel
>= DFWAD_LOG_INFO
then
677 e_WriteLog('DFZIP: File integrity check failed: expected CRC32 $' + IntToHex(p
.chksum
, 8) + ', calculated CRC32 $' + IntToHex(crc
, 8), MSG_WARNING
);
684 if gWADEditorLogLevel
>= DFWAD_LOG_DEBUG
then
685 e_WriteLog('DFZIP: Resource not found', MSG_NOTIFY
);
686 FLastError
:= DFWAD_ERROR_RESOURCENOTFOUND
;
690 function TZIPEditor
.GetResourcesList(Section
: String): SArray
;
691 var p
: PSection
; i
: Integer;
693 Section
:= win2utf(Section
);
695 p
:= FindSection(Section
);
696 if (p
<> nil) and (p
.list
<> nil) then
698 SetLength(Result
, Length(p
.list
));
699 for i
:= 0 to High(p
.list
) do
701 Result
[i
] := utf2win(p
.list
[i
].name
);
706 function TZIPEditor
.GetSectionList(): SArray
;
710 if FSection
<> nil then
712 SetLength(Result
, Length(FSection
));
713 for i
:= 0 to High(FSection
) do
715 Result
[i
] := utf2win(FSection
[i
].name
);
720 procedure TZIPEditor
.ReadLFH(s
: TStream
; fname
: AnsiString
; xcsize
, xusize
, xcomp
, xcrc
, xtime
, xflags
: UInt32
);
721 var sig
: packed array [0..3] of Char;
722 var va
, vb
, flags
, comp
: UInt16
;
723 var mtime
, crc
, csize
, usize
: UInt32
;
724 var fnlen
, extlen
: UInt16
;
725 var mypos
, datapos
: UInt64
;
726 var section
, name
: AnsiString
;
730 if mypos
+ 30 <= s
.Size
then
732 s
.ReadBuffer(sig
[0], 4);
733 if sig
= ZIP_SIGN_LFH
then
735 va
:= s
.ReadByte(); // Min Version
736 vb
:= s
.ReadByte(); // Min System
737 flags
:= LEtoN(s
.ReadWord());
738 comp
:= LEtoN(s
.ReadWord());
739 mtime
:= LEtoN(s
.ReadDWord());
740 crc
:= LEtoN(s
.ReadDWord());
741 csize
:= LEtoN(s
.ReadDWord());
742 usize
:= LEtoN(s
.ReadDWord());
743 fnlen
:= LEtoN(s
.ReadWord());
744 extlen
:= LEtoN(s
.ReadWord());
745 datapos
:= s
.Position
+ fnlen
+ extlen
;
746 if gWADEditorLogLevel
>= DFWAD_LOG_DEBUG
then
748 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Min Version : ' + IntToStr(va
), MSG_NOTIFY
);
749 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Min System : ' + IntToStr(vb
), MSG_NOTIFY
);
750 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Flags : $' + IntToHex(flags
, 4), MSG_NOTIFY
);
751 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Compression : ' + IntToStr(comp
), MSG_NOTIFY
);
752 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Modification Time : ' + DosToStr(mtime
), MSG_NOTIFY
);
753 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': CRC-32 : $' + IntToHex(crc
, 8), MSG_NOTIFY
);
754 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Compressed size : ' + IntToStr(csize
), MSG_NOTIFY
);
755 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Decompressed size : ' + IntToStr(usize
), MSG_NOTIFY
);
756 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Name Length : ' + IntToStr(fnlen
), MSG_NOTIFY
);
757 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Extension Length : ' + IntToStr(extlen
), MSG_NOTIFY
);
758 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': <DATA OFFSET> : $' + IntToHex(datapos
, 8), MSG_NOTIFY
);
760 if (va
>= 10) and (va
<= ZIP_MAXVERSION
) then
762 if datapos
+ xcsize
<= s
.Size
then
764 ToSectionFile(fname
, section
, name
);
767 p
:= FindSectionRAW(section
, True);
769 p
:= InsertSectionRAW(section
, xtime
);
773 p
:= InsertFileInfo(section
, name
, datapos
, xcsize
, xusize
, xcomp
, xcrc
, xtime
, xflags
and ZIP_COMP_MASK
);
776 raise Exception
.Create('Failed to register resource [' + fname
+ ']');
779 raise Exception
.Create('Invalid LFH size (corrupted file?)');
783 FLastError
:= DFWAD_ERROR_WRONGVERSION
;
784 raise Exception
.Create('Unsupported CDR version ' + IntToStr(va
) + ', not in range [10..' + IntToStr(ZIP_MAXVERSION
) + ']');
788 raise Exception
.Create('Invalid LFH signature $' +IntToHex(Ord(sig
[0]), 2) + ' $' +IntToHex(Ord(sig
[1]), 2) + ' $' +IntToHex(Ord(sig
[2]), 2) + ' $' +IntToHex(Ord(sig
[3]), 2) + ' (corrupted file?)');
791 raise Exception
.Create('Invalid LFH size (corrupted file?)');
794 procedure TZIPEditor
.ReadCDR(s
: TStream
; cdrid
: Integer);
795 var sig
: packed array [0..3] of Char;
796 var vva
, vvb
, va
, vb
, flags
, comp
: UInt16
;
797 var mtime
, crc
, csize
, usize
: UInt32
;
798 var fnlen
, extlen
, comlen
, disk
, iattr
: UInt16
;
799 var eattr
, offset
: UInt32
;
800 var mypos
, next
: UInt64
;
802 var aname
: AnsiString
;
803 var cvtbug
, utf8
: Boolean;
806 s
.ReadBuffer(sig
[0], 4);
807 if sig
= ZIP_SIGN_CDR
then
809 // Valid Central Directory Signature
810 vva
:= s
.ReadByte(); // Writer Version
811 vvb
:= s
.ReadByte(); // Writer System
812 va
:= s
.ReadByte(); // Min Version
813 vb
:= s
.ReadByte(); // Min System
814 flags
:= LEtoN(s
.ReadWord());
815 comp
:= LEtoN(s
.ReadWord());
816 mtime
:= LEtoN(s
.ReadDWord());
817 crc
:= LEtoN(s
.ReadDWord());
818 csize
:= LEtoN(s
.ReadDWord());
819 usize
:= LEtoN(s
.ReadDWord());
820 fnlen
:= LEtoN(s
.ReadWord());
821 extlen
:= LEtoN(s
.ReadWord());
822 comlen
:= LEtoN(s
.ReadWord());
823 disk
:= LEtoN(s
.ReadWord());
824 iattr
:= LEtoN(s
.ReadWord());
825 eattr
:= LEtoN(s
.ReadDWord());
826 offset
:= LEtoN(s
.ReadDWord());
827 next
:= s
.Position
+ fnlen
+ extlen
+ comlen
;
829 if gWADEditorLogLevel
>= DFWAD_LOG_DEBUG
then
831 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Writer Version : ' + IntToStr(vva
), MSG_NOTIFY
);
832 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Writer System : ' + IntToStr(vvb
), MSG_NOTIFY
);
833 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Min Version : ' + IntToStr(va
), MSG_NOTIFY
);
834 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Min System : ' + IntToStr(vb
), MSG_NOTIFY
);
835 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Flags : $' + IntToHex(flags
, 4), MSG_NOTIFY
);
836 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Compression : ' + IntToStr(comp
), MSG_NOTIFY
);
837 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Modification Time : ' + DosToStr(mtime
), MSG_NOTIFY
);
838 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': CRC-32 : $' + IntToHex(crc
, 8), MSG_NOTIFY
);
839 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Compressed size : ' + IntToStr(csize
), MSG_NOTIFY
);
840 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Decompressed size : ' + IntToStr(usize
), MSG_NOTIFY
);
841 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Name Length : ' + IntToStr(fnlen
), MSG_NOTIFY
);
842 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Extension Length : ' + IntToStr(extlen
), MSG_NOTIFY
);
843 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Comment Length : ' + IntToStr(comlen
), MSG_NOTIFY
);
844 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Disk : ' + IntToStr(disk
), MSG_NOTIFY
);
845 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Internal Attrib : $' + IntToHex(iattr
, 4), MSG_NOTIFY
);
846 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': External Attrib : $' + IntToHex(eattr
, 8), MSG_NOTIFY
);
847 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': LFH Offset : $' + IntToHex(offset
, 8), MSG_NOTIFY
);
850 if (vva
= $10) and (vvb
= $0A) and (va
= $10) and (vb
= $00) and (flags
= (1 << 10)) and (mtime
= 0) and (iattr
= 0) and (eattr
= 0) then
852 // HACK: Editor and wadcvt for long time sets incorrent flag for UTF-8
853 flags
:= ZIP_UTF8_MASK
;
856 if gWADEditorLogLevel
>= DFWAD_LOG_DEBUG
then
857 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': WADCVT BUG : ' + BoolToStr(cvtbug
, True), MSG_NOTIFY
);
858 if (va
>= 10) and (va
<= ZIP_MAXVERSION
) then
860 if (flags
and ZIP_ENCRYPTION_MASK
) = 0 then
862 if (csize
<> $ffffffff) and (usize
<> $ffffffff) and (disk
<> $ffff) and (offset
<> $ffffffff) then
866 if (next
<= s
.Size
) and (fnlen
> 0) then
870 if csize
<> usize
then
871 raise Exception
.Create('Compressed size ' + IntToStr(csize
) + ' != Descompressed size ' + IntToStr(usize
) + 'for STORE method (corrupted file?)');
895 raise Exception
.Create('Encrypted archives not supported');
897 raise Exception
.Create('Unknown compression method ' + IntToStr(comp
));
899 GetMem(name
, UInt32(fnlen
) + 1);
901 s
.ReadBuffer(name
[0], fnlen
);
905 if (flags
and ZIP_UTF8_MASK
= 0) and (IsUTF8(name
) = False) then
907 aname
:= win2utf(aname
);
910 if gWADEditorLogLevel
>= DFWAD_LOG_DEBUG
then
912 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': UTF-8 Comatible : ' + BoolToStr(utf8
, True), MSG_NOTIFY
);
913 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Name : "' + aname
+ '"', MSG_NOTIFY
);
915 s
.Seek(offset
, TSeekOrigin
.soBeginning
);
916 ReadLFH(s
, aname
, csize
, usize
, comp
, crc
, mtime
, flags
);
918 s
.Seek(next
, TSeekOrigin
.soBeginning
);
923 raise Exception
.Create('Empty files names not supported');
926 raise Exception
.Create('Splitted archives not supported');
930 FLastError
:= DFWAD_ERROR_WRONGVERSION
;
931 raise Exception
.Create('ZIP64 not supported');
936 FLastError
:= DFWAD_ERROR_READWAD
;
937 raise Exception
.Create('Encrypted archives not supported');
942 FLastError
:= DFWAD_ERROR_WRONGVERSION
;
943 raise Exception
.Create('Unsupported CDR version ' + IntToStr(va
) + ', not in range [10..' + IntToStr(ZIP_MAXVERSION
) + ']');
947 raise Exception
.Create('Invalid CDR signature $' + IntToHex(Ord(sig
[0]), 2) + ' $' +IntToHex(Ord(sig
[1]), 2) + ' $' +IntToHex(Ord(sig
[2]), 2) + ' $' +IntToHex(Ord(sig
[3]), 2) + ' (corrupted file?)');
950 function TZIPEditor
.FindEOCD(s
: TStream
): Boolean;
951 const maxedir
= 20; // end of central directory entry
952 const maxecdir
= maxedir
+ 65536; // + comment
953 var sig
: packed array [0..3] of Char; off
, lim
: Int64;
956 if s
.Size
>= maxedir
then
958 if s
.Size
< maxecdir
then lim
:= s
.Size
else lim
:= maxecdir
;
959 lim
:= lim
- maxedir
;
961 while (off
<= lim
) and (Result
= False) do
963 s
.Seek(s
.Size
- off
, TSeekOrigin
.soBeginning
);
964 s
.ReadBuffer(sig
[0], 4);
965 Result
:= sig
= ZIP_SIGN_EOCD
;
971 procedure TZIPEditor
.ReadEOCD(s
: TStream
);
972 var sig
: packed array [0..3] of Char;
973 var idisk
, ndisk
, nrec
, total
, comlen
: UInt16
;
974 var csize
, cpos
, i
: UInt32
;
977 FLastError
:= DFWAD_ERROR_FILENOTWAD
;
979 s
.ReadBuffer(sig
[0], 4);
980 if (sig
= ZIP_SIGN_LFH
) or (sig
= ZIP_SIGN_EOCD
) then
984 // End of Central Directory found
985 FLastError
:= DFWAD_ERROR_READWAD
;
986 mypos
:= s
.Position
- 4;
987 idisk
:= LEtoN(s
.ReadWord());
988 ndisk
:= LEtoN(s
.ReadWord());
989 nrec
:= LEtoN(s
.ReadWord());
990 total
:= LEtoN(s
.ReadWord());
991 csize
:= LEtoN(s
.ReadDWord());
992 cpos
:= LEtoN(s
.ReadDWord());
993 comlen
:= LEtoN(s
.ReadWord());
994 if gWADEditorLogLevel
>= DFWAD_LOG_DEBUG
then
996 e_WriteLog('==============================================', MSG_NOTIFY
);
997 e_WriteLog('EOCD @' + IntToHex(mypos
, 8) + ': Disk ID : ' + IntToStr(idisk
), MSG_NOTIFY
);
998 e_WriteLog('EOCD @' + IntToHex(mypos
, 8) + ': Disk ID with CD : ' + IntToStr(ndisk
), MSG_NOTIFY
);
999 e_WriteLog('EOCD @' + IntToHex(mypos
, 8) + ': Available CDR''s : ' + IntToStr(nrec
), MSG_NOTIFY
);
1000 e_WriteLog('EOCD @' + IntToHex(mypos
, 8) + ': Total CDR''s : ' + IntToStr(total
), MSG_NOTIFY
);
1001 e_WriteLog('EOCD @' + IntToHex(mypos
, 8) + ': CD Length : ' + IntToStr(csize
), MSG_NOTIFY
);
1002 e_WriteLog('EOCD @' + IntToHex(mypos
, 8) + ': CD Offset : $' + IntToHex(cpos
, 8), MSG_NOTIFY
);
1003 e_WriteLog('EOCD @' + IntToHex(mypos
, 8) + ': Comment Length : ' + IntToStr(comlen
), MSG_NOTIFY
);
1005 if (idisk
<> $ffff) and (ndisk
<> $ffff) and (nrec
<> $ffff) and (total
<> $ffff) and (csize
<> $ffffffff) and (cpos
<> $ffffffff) then
1007 if s
.Position
+ comlen
= s
.Size
then
1009 if (idisk
= 0) and (ndisk
= 0) and (nrec
= total
) then
1011 if (nrec
* 46 <= csize
) and (UInt64(cpos
) + csize
<= s
.Size
) then
1016 s
.Seek(cpos
, TSeekOrigin
.soBeginning
);
1019 if gWADEditorLogLevel
>= DFWAD_LOG_DEBUG
then
1020 e_WriteLog('==============================================', MSG_NOTIFY
);
1024 if gWADEditorLogLevel
>= DFWAD_LOG_DEBUG
then
1025 e_WriteLog('==============================================', MSG_NOTIFY
);
1029 raise Exception
.Create('Central Directory too big (corrupted file?)');
1032 raise Exception
.Create('Splitted archives not supported');
1035 raise Exception
.Create('EOCD too big (corrupted file?)');
1038 raise Exception
.Create('ZIP64 not supported');
1041 raise Exception
.Create('EOCD not found (corrupted file?)');
1044 raise Exception
.Create('Not DFZIP formated file');
1047 function TZIPEditor
.ReadFile2(FileName
: String): Boolean;
1054 s
:= TFileStream
.Create(FileName
, fmOpenRead
or fmShareDenyWrite
);
1058 FLastError
:= DFWAD_NOERROR
;
1067 if gWADEditorLogLevel
>= DFWAD_LOG_INFO
then
1068 e_WriteLog('ZIP: Failed to read ZIP from file ' + FileName
+ ', reason: ' + e
.Message, MSG_WARNING
);
1073 on e
: EFOpenError
do
1075 if gWADEditorLogLevel
>= DFWAD_LOG_INFO
then
1076 e_WriteLog('DFZIP: Failed to open file ' + FileName
+ ', reason: ' + e
.Message, MSG_WARNING
);
1077 if FileExists(FileName
) then
1078 FLastError
:= DFWAD_ERROR_CANTOPENWAD
1080 FLastError
:= DFWAD_ERROR_WADNOTFOUND
;
1085 function TZIPEditor
.ReadMemory(Data
: Pointer; Len
: LongWord
): Boolean;
1086 var s
: TMemoryStream
;
1091 s
:= TMemoryStream
.Create
;
1094 s
.WriteBuffer(PByte(Data
)[0], Len
);
1095 s
.Seek(0, soBeginning
);
1098 FLastError
:= DFWAD_NOERROR
;
1107 if gWADEditorLogLevel
>= DFWAD_LOG_INFO
then
1108 e_WriteLog('DFZIP: Failed to read ZIP from memory, reason: ' + e
.Message, MSG_WARNING
);
1114 procedure TZIPEditor
.RemoveResource(Section
, Resource
: String);
1115 var p
: PSection
; i
: Integer;
1117 Section
:= win2utf(Section
);
1118 Resource
:= win2utf(Resource
);
1119 p
:= FindSection(Section
);
1120 i
:= FindResourceID(p
, Resource
);
1123 if p
.list
[i
].stream
<> nil then
1124 FreeAndNil(p
.list
[i
].stream
);
1125 for i
:= i
+ 1 to High(p
.list
) do
1127 p
.list
[i
- 1] := p
.list
[i
];
1129 SetLength(p
.list
, High(p
.list
));
1133 function GetZIPVersion(const afname
: AnsiString
; flags
, comp
: UInt16
): UInt8
;
1136 version
:= 10; // Base version
1138 ZIP_COMP_STORE
: version
:= 10;
1139 ZIP_COMP_SHRUNK
: version
:= 10;
1140 ZIP_COMP_REDUCE1
: version
:= 10;
1141 ZIP_COMP_REDUCE2
: version
:= 10;
1142 ZIP_COMP_REDUCE3
: version
:= 10;
1143 ZIP_COMP_REDUCE4
: version
:= 10;
1144 ZIP_COMP_IMPLODE
: version
:= 10;
1145 ZIP_COMP_TOKENIZED
: version
:= 20;
1146 ZIP_COMP_DEFLATE
: version
:= 20;
1147 ZIP_COMP_DEFLATE64
: version
:= 21;
1148 ZIP_COMP_TERSE1
: version
:= 25; // PKWARE DCL Implode
1149 ZIP_COMP_BZIP2
: version
:= 46;
1150 ZIP_COMP_LZMA
: version
:= 63;
1151 ZIP_COMP_CMPSC
: version
:= 63;
1152 ZIP_COMP_TERSE2
: version
:= 63;
1153 ZIP_COMP_LZ77
: version
:= 63;
1154 ZIP_COMP_ZSTD1
: version
:= 63;
1155 ZIP_COMP_ZSTD2
: version
:= 63;
1156 ZIP_COMP_MP3
: version
:= 63;
1157 ZIP_COMP_XZ
: version
:= 63;
1158 ZIP_COMP_JPEG
: version
:= 63;
1159 ZIP_COMP_WAVPACK
: version
:= 63;
1160 ZIP_COMP_PPMD
: version
:= 63;
1161 ZIP_COMP_AE
: version
:= 63;
1163 if afname
[Length(afname
)] = '/' then
1164 version
:= Max(20, version
); // Folder
1165 if flags
and ZIP_UTF8_MASK
<> 0 then
1166 version
:= Max(63, version
); // UTF-8 name
1170 procedure TZIPEditor
.WriteLFH(s
: TStream
; flags
, comp
, mtime
, crc
, csize
, usize
: UInt32
; const afname
: AnsiString
);
1171 var fname
: PChar
; version
: UInt8
; fnlen
: UInt16
; mypos
: UInt64
;
1173 mypos
:= s
.Position
;
1174 fname
:= PChar(afname
);
1175 fnlen
:= Length(fname
);
1176 if IsASCII(afname
) = False then
1177 flags
:= flags
or ZIP_UTF8_MASK
;
1178 version
:= GetZIPVersion(afname
, flags
, comp
);
1179 if gWADEditorLogLevel
>= DFWAD_LOG_DEBUG
then
1181 e_WriteLog('==============================================', MSG_NOTIFY
);
1182 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Min Version : ' + IntToStr(version
), MSG_NOTIFY
);
1183 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Min System : ' + IntToStr(ZIP_SYSTEM
), MSG_NOTIFY
);
1184 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Flags : $' + IntToHex(flags
, 4), MSG_NOTIFY
);
1185 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Compression : ' + IntToStr(comp
), MSG_NOTIFY
);
1186 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Modification Time : ' + DosToStr(mtime
), MSG_NOTIFY
);
1187 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': CRC-32 : $' + IntToHex(crc
, 8), MSG_NOTIFY
);
1188 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Compressed size : ' + IntToStr(csize
), MSG_NOTIFY
);
1189 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Decompressed size : ' + IntToStr(usize
), MSG_NOTIFY
);
1190 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Name Length : ' + IntToStr(fnlen
), MSG_NOTIFY
);
1191 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Extension Length : ' + IntToStr(0), MSG_NOTIFY
);
1192 e_WriteLog('LFH @' + IntToHex(mypos
, 8) + ': Name : "' + fname
+ '"', MSG_NOTIFY
);
1194 s
.WriteBuffer(ZIP_SIGN_LFH
, 4); // LFH Signature
1195 s
.WriteByte(version
); // Min version
1196 s
.WriteByte(ZIP_SYSTEM
); // System
1197 WriteInt(s
, UInt16(flags
)); // Flags
1198 WriteInt(s
, UInt16(comp
)); // Compression method
1199 WriteInt(s
, UInt32(mtime
)); // Modification time/date
1200 WriteInt(s
, UInt32(crc
)); // CRC-32
1201 WriteInt(s
, UInt32(csize
)); // Compressed size
1202 WriteInt(s
, UInt32(usize
)); // Decompressed size
1203 WriteInt(s
, UInt16(fnlen
)); // Name field length
1204 WriteInt(s
, UInt16(0)); // Extra field length
1205 s
.WriteBuffer(fname
[0], fnlen
); // File Name
1208 procedure TZIPEditor
.WriteCDR(s
: TStream
; flags
, comp
, mtime
, crc
, csize
, usize
, eattr
, offset
: UInt32
; const afname
: AnsiString
; cdrid
: Integer);
1209 var fname
: PChar
; version
: UInt8
; fnlen
: UInt16
; mypos
: UInt64
;
1211 mypos
:= s
.Position
;
1212 fname
:= PChar(afname
);
1213 fnlen
:= Length(fname
);
1214 if IsASCII(afname
) = False then
1215 flags
:= flags
or ZIP_UTF8_MASK
;
1216 version
:= GetZIPVersion(afname
, flags
, comp
);
1217 if gWADEditorLogLevel
>= DFWAD_LOG_DEBUG
then
1219 e_WriteLog('==============================================', MSG_NOTIFY
);
1220 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Writer Version : ' + IntToStr(ZIP_MAXVERSION
), MSG_NOTIFY
);
1221 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Writer System : ' + IntToStr(ZIP_SYSTEM
), MSG_NOTIFY
);
1222 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Min Version : ' + IntToStr(version
), MSG_NOTIFY
);
1223 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Min System : ' + IntToStr(ZIP_SYSTEM
), MSG_NOTIFY
);
1224 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Flags : $' + IntToHex(flags
, 4), MSG_NOTIFY
);
1225 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Compression : ' + IntToStr(comp
), MSG_NOTIFY
);
1226 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Modification Time : ' + DosToStr(mtime
), MSG_NOTIFY
);
1227 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': CRC-32 : $' + IntToHex(crc
, 8), MSG_NOTIFY
);
1228 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Compressed size : ' + IntToStr(csize
), MSG_NOTIFY
);
1229 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Decompressed size : ' + IntToStr(usize
), MSG_NOTIFY
);
1230 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Name Length : ' + IntToStr(fnlen
), MSG_NOTIFY
);
1231 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Extension Length : ' + IntToStr(0), MSG_NOTIFY
);
1232 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Comment Length : ' + IntToStr(0), MSG_NOTIFY
);
1233 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Disk : ' + IntToStr(0), MSG_NOTIFY
);
1234 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Internal Attrib : $' + IntToHex(0, 4), MSG_NOTIFY
);
1235 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': External Attrib : $' + IntToHex(eattr
, 8), MSG_NOTIFY
);
1236 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': LFH Offset : $' + IntToHex(offset
, 8), MSG_NOTIFY
);
1237 e_WriteLog('CDR#' + IntToStr(cdrid
) + ' @' + IntToHex(mypos
, 8) + ': Name : "' + fname
+ '"', MSG_NOTIFY
);
1239 s
.WriteBuffer(ZIP_SIGN_CDR
, 4); // CDR Signature
1240 s
.WriteByte(ZIP_MAXVERSION
); // Used version
1241 s
.WriteByte(ZIP_SYSTEM
); // Used system
1242 s
.WriteByte(version
); // Min version
1243 s
.WriteByte(ZIP_SYSTEM
); // Min system
1244 WriteInt(s
, UInt16(flags
)); // Flags
1245 WriteInt(s
, UInt16(comp
)); // Compression method
1246 WriteInt(s
, UInt32(mtime
)); // Modification time/date
1247 WriteInt(s
, UInt32(crc
)); // CRC-32
1248 WriteInt(s
, UInt32(csize
)); // Compressed size
1249 WriteInt(s
, UInt32(usize
)); // Decompressed size
1250 WriteInt(s
, UInt16(fnlen
)); // Name field length
1251 WriteInt(s
, UInt16(0)); // Extra field length
1252 WriteInt(s
, UInt16(0)); // Comment field length
1253 WriteInt(s
, UInt16(0)); // Disk
1254 WriteInt(s
, UInt16(0)); // Internal attributes
1255 WriteInt(s
, UInt32(eattr
)); // External attributes
1256 WriteInt(s
, UInt32(offset
)); // LFH offset
1257 s
.WriteBuffer(fname
[0], fnlen
); // File Name
1260 procedure TZIPEditor
.SaveToStream(s
: TStream
);
1262 var start
, offset
, loffset
, size
, zcrc
, count
: UInt32
;
1264 var afname
: AnsiString
;
1267 // Write LFH headers and data
1268 start
:= s
.Position
;
1269 zcrc
:= crc32(0, nil, 0);
1270 if FSection
<> nil then
1272 for i
:= 0 to High(FSection
) do
1274 if FSection
[i
].list
<> nil then
1276 for j
:= 0 to High(FSection
[i
].list
) do
1278 p
:= @FSection
[i
].list
[j
];
1279 afname
:= GetFileName(FSection
[i
].name
, p
.name
);
1280 WriteLFH(s
, p
.flags
, p
.comp
, p
.mtime
, p
.chksum
, p
.csize
, p
.usize
, afname
);
1281 if p
.stream
<> nil then
1283 Assert(p
.stream
.Size
= p
.csize
);
1284 p
.stream
.SaveToStream(s
);
1286 else if FStream
<> nil then
1288 FStream
.Seek(p
.pos
, TSeekOrigin
.soBeginning
);
1289 s
.CopyFrom(FStream
, p
.csize
);
1293 raise Exception
.Create('No data source available (somethig very wrong)');
1299 afname
:= GetFileName(FSection
[i
].name
, '');
1300 WriteLFH(s
, 0, ZIP_COMP_STORE
, FSection
[i
].mtime
, zcrc
, 0, 0, afname
);
1304 // Write CDR headers
1307 offset
:= s
.Position
- start
;
1308 if FSection
<> nil then
1310 for i
:= 0 to High(FSection
) do
1312 if FSection
[i
].list
<> nil then
1314 for j
:= 0 to High(FSection
[i
].list
) do
1316 p
:= @FSection
[i
].list
[j
];
1317 afname
:= GetFileName(FSection
[i
].name
, p
.name
);
1318 WriteCDR(s
, p
.flags
, p
.comp
, p
.mtime
, p
.chksum
, p
.csize
, p
.usize
, $00, loffset
, afname
, i
);
1319 loffset
:= loffset
+ 30 + Length(afname
) + p
.csize
;
1325 afname
:= GetFileName(FSection
[i
].name
, '');
1326 WriteCDR(s
, 0, ZIP_COMP_STORE
, FSection
[i
].mtime
, zcrc
, 0, 0, $10, loffset
, afname
, i
);
1327 loffset
:= loffset
+ 30 + Length(afname
) + 0;
1332 Assert(loffset
= offset
);
1333 Assert(count
< $ffff);
1334 size
:= s
.Position
- start
- offset
;
1335 // Write EOCD header
1336 mypos
:= s
.Position
;
1337 if gWADEditorLogLevel
>= DFWAD_LOG_DEBUG
then
1339 e_WriteLog('==============================================', MSG_NOTIFY
);
1340 e_WriteLog('EOCD @' + IntToHex(mypos
, 8) + ': Disk ID : ' + IntToStr(0), MSG_NOTIFY
);
1341 e_WriteLog('EOCD @' + IntToHex(mypos
, 8) + ': Disk ID with CD : ' + IntToStr(0), MSG_NOTIFY
);
1342 e_WriteLog('EOCD @' + IntToHex(mypos
, 8) + ': Available CDR''s : ' + IntToStr(count
), MSG_NOTIFY
);
1343 e_WriteLog('EOCD @' + IntToHex(mypos
, 8) + ': Total CDR''s : ' + IntToStr(count
), MSG_NOTIFY
);
1344 e_WriteLog('EOCD @' + IntToHex(mypos
, 8) + ': CD Length : ' + IntToStr(size
), MSG_NOTIFY
);
1345 e_WriteLog('EOCD @' + IntToHex(mypos
, 8) + ': CD Offset : $' + IntToHex(offset
, 8), MSG_NOTIFY
);
1346 e_WriteLog('EOCD @' + IntToHex(mypos
, 8) + ': Comment Length : ' + IntToStr(0), MSG_NOTIFY
);
1347 e_WriteLog('==============================================', MSG_NOTIFY
);
1349 s
.WriteBuffer(ZIP_SIGN_EOCD
, 4); // EOCD Signature
1350 WriteInt(s
, UInt16(0)); // Disk
1351 WriteInt(s
, UInt16(0)); // Num of Disks
1352 WriteInt(s
, UInt16(count
)); // Num of CDRs
1353 WriteInt(s
, UInt16(count
)); // Total CDR entries
1354 WriteInt(s
, UInt32(size
)); // Central Directory size
1355 WriteInt(s
, UInt32(offset
)); // Central Directory offset
1356 WriteInt(s
, UInt16(0)); // Comment field length
1359 procedure TZIPEditor
.SaveTo(FileName
: String);
1363 s
:= TFileStream
.Create(FileName
, fmCreate
);
1372 if gWADEditorLogLevel
>= DFWAD_LOG_INFO
then
1373 e_WriteLog('ZIP: Failed to create file ' + FileName
+ ', reason: ' + e
.Message, MSG_WARNING
);
1379 function TZIPEditor
.GetLastError
: Integer;
1381 Result
:= FLastError
;
1384 function TZIPEditor
.GetLastErrorStr
: String;
1387 DFWAD_NOERROR
: Result
:= '';
1388 DFWAD_ERROR_WADNOTFOUND
: Result
:= 'DFZIP file not found';
1389 DFWAD_ERROR_CANTOPENWAD
: Result
:= 'Can''t open DFZIP file';
1390 DFWAD_ERROR_RESOURCENOTFOUND
: Result
:= 'Resource not found';
1391 DFWAD_ERROR_FILENOTWAD
: Result
:= 'File is not DFZIP';
1392 DFWAD_ERROR_WADNOTLOADED
: Result
:= 'DFZIP file is not loaded';
1393 DFWAD_ERROR_READRESOURCE
: Result
:= 'Read resource error';
1394 DFWAD_ERROR_READWAD
: Result
:= 'Read DFZIP error';
1395 otherwise Result
:= IntToStr(FLastError
);
1399 function TZIPEditor
.GetResourcesCount
: Word;
1403 if FSection
<> nil then
1405 Result
:= Result
+ Length(FSection
);
1406 for i
:= 0 to High(FSection
) do
1407 if FSection
[i
].list
<> nil then
1408 Result
:= Result
+ Length(FSection
[i
].list
);
1412 function TZIPEditor
.GetVersion
: Byte;
1418 gWADEditorFactory
.RegisterEditor('DFZIP', TZIPEditor
);