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 // - UTF-8 not supported yet, expected WIN1251 encoding
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
;
29 stream
: TMemoryStream
;
34 list
: array of TResource
;
37 PResource
= ^TResource
;
40 TZIPEditor
= class sealed(WADEDITOR
.TWADEditor
)
42 FSection
: array of TSection
;
47 function FindSectionIDRAW(name
: AnsiString; caseSensitive
: Boolean): Integer;
48 function FindSectionRAW(name
: AnsiString; caseSensitive
: Boolean): PSection
;
49 function InsertSectionRAW(name
: AnsiString): PSection
;
51 function FindSectionID(name
: AnsiString): Integer;
52 function FindSection(name
: AnsiString): PSection
;
53 function InsertSection(name
: AnsiString): PSection
;
55 function InsertFileInfo(const section
, name
: AnsiString; pos
, csize
, usize
, comp
, crc
: UInt32
): PResource
;
56 function Preload(p
: PResource
): Boolean;
57 function GetSourceStream(p
: PResource
): TStream
;
59 function ReadLFH(s
: TStream
; fname
: AnsiString; xcsize
, xusize
, xcomp
, xcrc
: UInt32
): Boolean;
60 function ReadCDR(s
: TStream
): Boolean;
61 function FindEOCD(s
: TStream
): Boolean;
62 function ReadEOCD(s
: TStream
): Boolean;
64 procedure WriteLFH(s
: TStream
; comp
, crc
, csize
, usize
: UInt32
; const afname
: AnsiString);
65 procedure WriteCDR(s
: TStream
; comp
, crc
, csize
, usize
, attr
, offset
: UInt32
; const afname
: AnsiString);
66 procedure SaveToStream(s
: TStream
);
70 destructor Destroy(); override;
71 procedure FreeWAD(); override;
72 function ReadFile2(FileName
: string): Boolean; override;
73 function ReadMemory(Data
: Pointer; Len
: LongWord): Boolean; override;
74 procedure CreateImage(); override;
75 function AddResource(Data
: Pointer; Len
: LongWord; Name
, Section
: String): Boolean; override; overload
;
76 function AddResource(FileName
, Name
, Section
: String): Boolean; override; overload
;
77 function AddAlias(Res
, Alias
: String): Boolean; override;
78 procedure AddSection(Name
: String); override;
79 procedure RemoveResource(Section
, Resource
: String); override;
80 procedure SaveTo(FileName
: String); override;
81 function HaveResource(Section
, Resource
: String): Boolean; override;
82 function HaveSection(Section
: string): Boolean; override;
83 function GetResource(Section
, Resource
: String; var pData
: Pointer; var Len
: Integer): Boolean; override;
84 function GetSectionList(): SArray
; override;
85 function GetResourcesList(Section
: String): SArray
; override;
87 function GetLastError
: Integer; override;
88 function GetLastErrorStr
: String; override;
89 function GetResourcesCount
: Word; override;
90 function GetVersion
: Byte; override;
95 uses SysUtils
, StrUtils
, zstream
, crc
, e_log
;
98 ZIP_SIGN_CDR
= 'PK'#1#2;
99 ZIP_SIGN_LFH
= 'PK'#3#4;
100 ZIP_SIGN_EOCD
= 'PK'#5#6;
104 ZIP_COMP_DEFLATE
= 8;
107 ZIP_SYSTEM
= 0; // DOS / FAT
108 ZIP_VERSION
= 20; // Min version
109 ZIP_MAXVERSION
= 63; // Max supported version
111 procedure ToSectionFile(fname
: AnsiString; out section
, name
: AnsiString); inline;
114 i
:= LastDelimiter('/', fname
);
115 section
:= Copy(fname
, 1, i
- 1);
116 name
:= Copy(fname
, i
+ 1)
119 function GetFileName(const Section
, Name
: AnsiString): AnsiString; inline;
124 Result
:= Section
+ '/' + Name
;
127 function PrepString(const s
: AnsiString; caseSensitive
, extSensitive
: Boolean): AnsiString; inline;
131 if caseSensitive
= False then
133 Result
:= UpperCase(Result
);
135 if extSensitive
= False then
137 i
:= Pos('.', Result
); // fix dotfiles
139 SetLength(Result
, i
- 1);
143 function FindResourceIDRAW(p
: PSection
; name
: AnsiString; caseSensitive
, extSensitive
: Boolean): Integer;
144 var i
: Integer; pname
: AnsiString;
148 pname
:= PrepString(name
, caseSensitive
, extSensitive
);
149 for i
:= 0 to High(p
.list
) do
151 if PrepString(p
.list
[i
].name
, caseSensitive
, extSensitive
) = pname
then
161 function FindResourceID(p
: PSection
; name
: AnsiString): Integer;
164 i
:= FindResourceIDRAW(p
, name
, True, True); // CaSeNaMe.Ext
167 i
:= FindResourceIDRAW(p
, name
, False, True); // CASENAME.EXT
170 i
:= FindResourceIDRAW(p
, name
, True, False); // CaSeNaMe
173 i
:= FindResourceIDRAW(p
, name
, False, False); // CASENAME
180 function FindResource(p
: PSection
; name
: AnsiString): PResource
;
183 i
:= FindResourceID(p
, name
);
192 function TZIPEditor
.FindSectionIDRAW(name
: AnsiString; caseSensitive
: Boolean): Integer;
193 var i
: Integer; pname
: AnsiString;
195 if FSection
<> nil then
197 pname
:= PrepString(name
, caseSensitive
, True);
198 for i
:= 0 to High(FSection
) do
200 if PrepString(FSection
[i
].name
, caseSensitive
, True) = pname
then
210 function TZIPEditor
.FindSectionRAW(name
: AnsiString; caseSensitive
: Boolean): PSection
;
213 i
:= FindSectionIDRAW(name
, caseSensitive
);
215 Result
:= @FSection
[i
]
220 function TZIPEditor
.InsertSectionRAW(name
: AnsiString): PSection
;
223 if FSection
= nil then i
:= 0 else i
:= Length(FSection
);
224 SetLength(FSection
, i
+ 1);
225 FSection
[i
] := Default(TSection
);
226 FSection
[i
].name
:= name
;
227 Result
:= @FSection
[i
];
232 function TZIPEditor
.FindSectionID(name
: AnsiString): Integer;
233 var fixName
: AnsiString;
235 fixName
:= StringReplace(name
, '\', '/', [rfReplaceAll
], TStringReplaceAlgorithm
.sraManySmall
);
236 Result
:= FindSectionIDRAW(fixName
, True); // CaSeNaMe
238 Result
:= FindSectionIDRAW(fixName
, False); // CASENAME
241 function TZIPEditor
.FindSection(name
: AnsiString): PSection
;
242 var fixName
: AnsiString;
244 fixName
:= StringReplace(name
, '\', '/', [rfReplaceAll
], TStringReplaceAlgorithm
.sraManySmall
);
245 Result
:= FindSectionRAW(fixName
, True); // CaSeNaMe
247 Result
:= FindSectionRAW(fixName
, False); // CASENAME
250 function TZIPEditor
.InsertSection(name
: AnsiString): PSection
;
252 Result
:= FindSection(name
);
254 Result
:= InsertSectionRAW(name
);
259 function TZIPEditor
.InsertFileInfo(const section
, name
: AnsiString; pos
, csize
, usize
, comp
, crc
: UInt32
): PResource
;
260 var p
: PSection
; i
: Integer;
262 p
:= FindSectionRAW(section
, True);
264 p
:= InsertSectionRAW(section
);
265 if p
.list
= nil then i
:= 0 else i
:= Length(p
.list
);
266 SetLength(p
.list
, i
+ 1);
267 p
.list
[i
] := Default(TResource
);
268 p
.list
[i
].name
:= name
;
269 p
.list
[i
].pos
:= pos
;
270 p
.list
[i
].csize
:= csize
;
271 p
.list
[i
].usize
:= usize
;
272 p
.list
[i
].comp
:= comp
;
273 p
.list
[i
].chksum
:= crc
;
274 p
.list
[i
].stream
:= nil;
275 Result
:= @p
.list
[i
];
280 function TZIPEditor
.AddAlias(Res
, Alias
: String): Boolean;
282 // Hard-links not supported in ZIP
283 // However, they never created by editor
287 function TZIPEditor
.AddResource(Data
: Pointer; Len
: LongWord; Name
, Section
: String): Boolean;
288 const compress
: Boolean = True;
289 const level
: TCompressionLevel
= TCompressionLevel
.clMax
;
290 var s
: TMemoryStream
; cs
: TCompressionStream
; p
: PResource
;
291 var comp
, crc
: UInt32
;
296 s
:= TMemoryStream
.Create();
298 if compress
and (Len
> 0) then
300 cs
:= TCompressionStream
.Create(level
, s
, True);
302 cs
.WriteBuffer(PByte(Data
)[0], Len
);
304 comp
:= ZIP_COMP_DEFLATE
;
309 if (Len
= 0) or (compress
= False) or (s
.Size
>= Len
) then
311 s
.Seek(0, TSeekOrigin
.soBeginning
);
313 s
.WriteBuffer(PByte(Data
)[0], Len
);
314 comp
:= ZIP_COMP_STORE
;
315 Assert(s
.Size
= Len
);
317 crc
:= crc32(0, nil, 0);
318 crc
:= crc32(crc
, data
, len
);
319 p
:= InsertFileInfo(Section
, Name
, $ffffffff, s
.Size
, Len
, comp
, crc
);
329 function TZIPEditor
.AddResource(FileName
, Name
, Section
: String): Boolean;
330 var s
: TFileStream
; ptr
: PByte;
333 FLastError
:= DFWAD_ERROR_READWAD
;
335 s
:= TFileStream
.Create(FileName
, fmOpenRead
, fmShareDenyWrite
);
339 s
.ReadBuffer(ptr
[0], s
.Size
);
340 Result
:= AddResource(ptr
, s
.Size
, Name
, Section
);
341 if Result
= True then FLastError
:= DFWAD_NOERROR
;
348 except on e
: EFOpenError
do
349 FLastError
:= DFWAD_ERROR_CANTOPENWAD
;
353 constructor TZIPEditor
.Create();
357 FLastError
:= DFWAD_NOERROR
;
358 FVersion
:= ZIP_VERSION
;
362 destructor TZIPEditor
.Destroy();
368 procedure TZIPEditor
.FreeWAD();
371 if FSection
<> nil then
373 for i
:= 0 to High(FSection
) do
375 if FSection
[i
].list
<> nil then
377 for j
:= 0 to High(FSection
[i
].list
) do
379 if FSection
[i
].list
[j
].stream
<> nil then
381 FreeAndNil(FSection
[i
].list
[j
].stream
);
384 SetLength(FSection
[i
].list
, 0);
387 SetLength(FSection
, 0);
389 if FStream
<> nil then
393 FLastError
:= DFWAD_NOERROR
;
394 FVersion
:= ZIP_VERSION
;
397 function TZIPEditor
.Preload(p
: PResource
): Boolean;
398 var s
: TMemoryStream
;
403 Result
:= p
.stream
<> nil;
404 if (p
.stream
= nil) and (FStream
<> nil) then
406 s
:= TMemoryStream
.Create();
410 FStream
.Seek(p
.pos
, TSeekOrigin
.soBeginning
);
411 s
.CopyFrom(FStream
, p
.csize
);
413 Assert(s
.Size
= p
.csize
); // wtf, random size if copied zero bytes!
424 procedure TZIPEditor
.CreateImage();
427 if FStream
= nil then
429 FLastError
:= DFWAD_ERROR_WADNOTLOADED
;
431 else if FStream
is TMemoryStream
then
433 FLastError
:= DFWAD_NOERROR
;
437 if FSection
<> nil then
439 for i
:= 0 to High(FSection
) do
441 if FSection
[i
].list
<> nil then
443 for j
:= 0 to High(FSection
[i
].list
) do
445 if Preload(@FSection
[i
].list
[j
]) = False then
447 FLastError
:= DFWAD_ERROR_CANTOPENWAD
;
455 FLastError
:= DFWAD_NOERROR
;
459 procedure TZIPEditor
.AddSection(Name
: String);
461 if InsertSection(Name
) = nil then
462 raise Exception
.Create('ZIP: AddSection: failed to add section');
465 function TZIPEditor
.HaveResource(Section
, Resource
: String): Boolean;
467 Result
:= FindResource(FindSection(Section
), Resource
) <> nil;
470 function TZIPEditor
.HaveSection(Section
: String): Boolean;
472 Result
:= FindSection(Section
) <> nil;
475 function TZIPEditor
.GetSourceStream(p
: PResource
): TStream
;
479 if p
.stream
<> nil then
482 src
.Seek(0, TSeekOrigin
.soBeginning
);
484 else if FStream
<> nil then
487 src
.Seek(p
.pos
, TSeekOrigin
.soBeginning
);
492 function TZIPEditor
.GetResource(Section
, Resource
: String; var pData
: Pointer; var Len
: Integer): Boolean;
493 var p
: PResource
; ptr
: PByte; src
: TStream
; tmp
: TDecompressionStream
; crc
: UInt32
;
495 FLastError
:= DFWAD_ERROR_CANTOPENWAD
;
499 p
:= FindResource(FindSection(Section
), Resource
);
502 src
:= GetSourceStream(p
);
507 if p
.csize
= p
.usize
then
509 GetMem(ptr
, p
.usize
);
511 src
.ReadBuffer(ptr
[0], p
.usize
);
519 tmp
:= TDecompressionStream
.Create(src
, True);
521 GetMem(ptr
, p
.usize
);
523 tmp
.ReadBuffer(ptr
[0], p
.usize
);
536 FLastError
:= DFWAD_ERROR_WADNOTLOADED
;
538 if Result
= True then
540 crc
:= crc32(0, nil, 0);
541 crc
:= crc32(crc
, ptr
, p
.usize
);
542 Result
:= crc
= p
.chksum
;
543 if Result
= True then
547 FLastError
:= DFWAD_NOERROR
;
557 FLastError
:= DFWAD_ERROR_RESOURCENOTFOUND
;
561 function TZIPEditor
.GetResourcesList(Section
: String): SArray
;
562 var p
: PSection
; i
: Integer;
565 p
:= FindSection(Section
);
566 if (p
<> nil) and (p
.list
<> nil) then
568 SetLength(Result
, Length(p
.list
));
569 for i
:= 0 to High(p
.list
) do
571 Result
[i
] := p
.list
[i
].name
;
576 function TZIPEditor
.GetSectionList(): SArray
;
580 if FSection
<> nil then
582 SetLength(Result
, Length(FSection
));
583 for i
:= 0 to High(FSection
) do
585 Result
[i
] := FSection
[i
].name
;
590 function TZIPEditor
.ReadLFH(s
: TStream
; fname
: AnsiString; xcsize
, xusize
, xcomp
, xcrc
: UInt32
): Boolean;
591 var sig
: packed array [0..3] of Char;
592 var v
, flags
, comp
: UInt16
;
593 var mtime
, crc
, csize
, usize
: UInt32
;
594 var fnlen
, extlen
: UInt16
;
596 var section
, name
: AnsiString;
599 if s
.Position
+ 30 <= s
.Size
then
601 s
.ReadBuffer(sig
[0], 4);
602 if sig
= ZIP_SIGN_LFH
then
604 v
:= LEtoN(s
.ReadWord());
605 flags
:= LEtoN(s
.ReadWord());
606 comp
:= LEtoN(s
.ReadWord());
607 mtime
:= LEtoN(s
.ReadDWord());
608 crc
:= LEtoN(s
.ReadDWord());
609 csize
:= LEtoN(s
.ReadDWord());
610 usize
:= LEtoN(s
.ReadDWord());
611 fnlen
:= LEtoN(s
.ReadWord());
612 extlen
:= LEtoN(s
.ReadWord());
613 datapos
:= s
.Position
+ fnlen
+ extlen
;
614 if datapos
+ xcsize
<= s
.Size
then
617 ToSectionFile(fname
, section
, name
);
619 Result
:= InsertSection(section
) <> nil
621 Result
:= InsertFileInfo(section
, name
, datapos
, xcsize
, xusize
, xcomp
, xcrc
) <> nil;
627 function TZIPEditor
.ReadCDR(s
: TStream
): Boolean;
628 var sig
: packed array [0..3] of Char;
629 var v
, va
, vb
, flags
, comp
: UInt16
;
630 var mtime
, crc
, csize
, usize
: UInt32
;
631 var fnlen
, extlen
, comlen
, disk
, iattr
: UInt16
;
632 var eattr
, offset
: UInt32
;
637 s
.ReadBuffer(sig
[0], 4);
638 if sig
= ZIP_SIGN_CDR
then
640 // Valid Central Directory Signature
641 v
:= LEtoN(s
.ReadWord());
642 va
:= s
.ReadByte(); // Min Version
643 vb
:= s
.ReadByte(); // Min System
644 flags
:= LEtoN(s
.ReadWord());
645 comp
:= LEtoN(s
.ReadWord());
646 mtime
:= LEtoN(s
.ReadDWord());
647 crc
:= LEtoN(s
.ReadDWord());
648 csize
:= LEtoN(s
.ReadDWord());
649 usize
:= LEtoN(s
.ReadDWord());
650 fnlen
:= LEtoN(s
.ReadWord());
651 extlen
:= LEtoN(s
.ReadWord());
652 comlen
:= LEtoN(s
.ReadWord());
653 disk
:= LEtoN(s
.ReadWord());
654 iattr
:= LEtoN(s
.ReadWord());
655 eattr
:= LEtoN(s
.ReadDWord());
656 offset
:= LEtoN(s
.ReadDWord());
657 next
:= s
.Position
+ fnlen
+ extlen
+ comlen
;
659 if va
<= ZIP_MAXVERSION
then
661 if (flags
and ((1 << 0) or (1 << 6) or (1 << 13))) = 0 then
663 // TODO: check bit 11 (UTF8 name and comment)
664 if (csize
<> $ffffffff) and (usize
<> $ffffffff) and (disk
<> $ffff) and (offset
<> $ffffffff) then
670 if (next
<= s
.Size
) and (fnlen
> 0) then
672 // Valid Central Directory Entry
673 GetMem(name
, UInt32(fnlen
) + 1);
675 s
.ReadBuffer(name
[0], fnlen
);
677 s
.Seek(offset
, TSeekOrigin
.soBeginning
);
678 Result
:= ReadLFH(s
, name
, csize
, usize
, comp
, crc
);
680 s
.Seek(next
, TSeekOrigin
.soBeginning
);
689 FLastError
:= DFWAD_ERROR_WRONGVERSION
;
695 FLastError
:= DFWAD_ERROR_READWAD
;
700 // Unsupported version
701 FLastError
:= DFWAD_ERROR_WRONGVERSION
;
706 function TZIPEditor
.FindEOCD(s
: TStream
): Boolean;
707 const maxedir
= 20; // end of central directory entry
708 const maxecdir
= maxedir
+ 65536; // + comment
709 var sig
: packed array [0..3] of Char; off
, lim
: Int64;
712 if s
.Size
>= maxedir
then
714 if s
.Size
< maxecdir
then lim
:= s
.Size
else lim
:= maxecdir
;
715 lim
:= lim
- maxedir
;
717 while (off
<= lim
) and (Result
= False) do
719 s
.Seek(s
.Size
- off
, TSeekOrigin
.soBeginning
);
720 s
.ReadBuffer(sig
[0], 4);
721 Result
:= sig
= ZIP_SIGN_EOCD
;
727 function TZIPEditor
.ReadEOCD(s
: TStream
): Boolean;
728 var sig
: packed array [0..3] of Char;
729 var idisk
, ndisk
, nrec
, total
, comlen
: UInt16
;
730 var csize
, cpos
, i
: UInt32
;
733 FLastError
:= DFWAD_ERROR_FILENOTWAD
;
735 s
.ReadBuffer(sig
[0], 4);
736 if (sig
= ZIP_SIGN_LFH
) or (sig
= ZIP_SIGN_EOCD
) then
740 // End of Central Directory found
741 FLastError
:= DFWAD_ERROR_READWAD
;
742 idisk
:= LEtoN(s
.ReadWord());
743 ndisk
:= LEtoN(s
.ReadWord());
744 nrec
:= LEtoN(s
.ReadWord());
745 total
:= LEtoN(s
.ReadWord());
746 csize
:= LEtoN(s
.ReadDWord());
747 cpos
:= LEtoN(s
.ReadDWord());
748 comlen
:= LEtoN(s
.ReadWord());
749 if (idisk
<> $ffff) and (ndisk
<> $ffff) and (nrec
<> $ffff) and (total
<> $ffff) and (csize
<> $ffffffff) and (cpos
<> $ffffffff) then
752 if s
.Position
+ comlen
= s
.Size
then
754 // Valid End of Central Directory size (located exactly at the end of file)
755 if (idisk
= 0) and (ndisk
= 0) and (nrec
= total
) then
758 if (UInt64(cpos
) + csize
<= s
.Size
) then
760 // Valid Cental Directry Record position and size
764 // At least one Central Directry present
766 s
.Seek(cpos
, TSeekOrigin
.soBeginning
);
767 while (i
< nrec
) and (Result
= True) do
769 Result
:= ReadCDR(s
);
772 // if Result = False then
773 // writeln('Invalid Central Directory #', i - 1);
782 FLastError
:= DFWAD_ERROR_WRONGVERSION
;
788 function TZIPEditor
.ReadFile2(FileName
: String): Boolean;
794 s
:= TFileStream
.Create(FileName
, fmOpenRead
, fmShareDenyWrite
);
796 Result
:= ReadEOCD(s
);
797 if Result
= True then
800 FLastError
:= DFWAD_NOERROR
;
810 except on e
: EFOpenError
do
811 if FileExists(FileName
) then
812 FLastError
:= DFWAD_ERROR_CANTOPENWAD
814 FLastError
:= DFWAD_ERROR_WADNOTFOUND
;
818 function TZIPEditor
.ReadMemory(Data
: Pointer; Len
: LongWord): Boolean;
819 var s
: TMemoryStream
;
823 s
:= TMemoryStream
.Create
;
826 s
.WriteBuffer(PByte(Data
)[0], Len
);
827 s
.Seek(0, soBeginning
);
828 Result
:= ReadEOCD(s
);
829 if Result
= True then
832 FLastError
:= DFWAD_NOERROR
;
845 procedure TZIPEditor
.RemoveResource(Section
, Resource
: String);
846 var p
: PSection
; i
: Integer;
848 p
:= FindSection(Section
);
849 i
:= FindResourceID(p
, Resource
);
852 if p
.list
[i
].stream
<> nil then
853 FreeAndNil(p
.list
[i
].stream
);
854 for i
:= i
+ 1 to High(p
.list
) do
856 p
.list
[i
- 1] := p
.list
[i
];
858 SetLength(p
.list
, High(p
.list
));
862 procedure TZIPEditor
.WriteLFH(s
: TStream
; comp
, crc
, csize
, usize
: UInt32
; const afname
: AnsiString);
863 var fname
: PChar; flen
: UInt16
;
865 fname
:= PChar(afname
);
866 flen
:= Length(fname
);
867 s
.WriteBuffer(ZIP_SIGN_LFH
, 4); // LFH Signature
868 s
.WriteByte(ZIP_VERSION
); // Min version
869 s
.WriteByte(ZIP_SYSTEM
); // System
870 s
.WriteWord(NtoLE(0)); // Flags
871 s
.WriteWord(NtoLE(comp
)); // Compression method
872 s
.WriteDWord(NtoLE(0)); // Modification time/date
873 s
.WriteDWord(NtoLE(crc
)); // CRC-32
874 s
.WriteDWord(NtoLE(csize
)); // Compressed size
875 s
.WriteDWord(NtoLE(usize
)); // Decompressed size
876 s
.WriteWord(NtoLE(flen
)); // Name field length
877 s
.WriteWord(NtoLE(0)); // Extra field length
878 s
.WriteBuffer(fname
[0], flen
); // File Name
881 procedure TZIPEditor
.WriteCDR(s
: TStream
; comp
, crc
, csize
, usize
, attr
, offset
: UInt32
; const afname
: AnsiString);
882 var fname
: PChar; flen
: UInt16
;
884 fname
:= PChar(afname
);
885 flen
:= Length(fname
);
886 s
.WriteBuffer(ZIP_SIGN_CDR
, 4); // CDR Signature
887 s
.WriteByte(ZIP_MAXVERSION
); // Used version
888 s
.WriteByte(ZIP_SYSTEM
); // Used system
889 s
.WriteByte(ZIP_VERSION
); // Min version
890 s
.WriteByte(ZIP_SYSTEM
); // Min system
891 s
.WriteWord(NtoLE(0)); // Flags
892 s
.WriteWord(NtoLE(comp
)); // Compression method
893 s
.WriteDWord(NtoLE(0)); // Modification time/date
894 s
.WriteDWord(NtoLE(crc
)); // CRC-32
895 s
.WriteDWord(NtoLE(csize
)); // Compressed size
896 s
.WriteDWord(NtoLE(usize
)); // Decompressed size
897 s
.WriteWord(NtoLE(flen
)); // Name field length
898 s
.WriteWord(NtoLE(0)); // Extra field length
899 s
.WriteWord(NtoLE(0)); // Comment field length
900 s
.WriteWord(NtoLE(0)); // Disk
901 s
.WriteWord(NtoLE(0)); // Internal attributes
902 s
.WriteDWord(NtoLE(attr
)); // External attributes
903 s
.WriteDWord(NtoLE(offset
)); // LFH offset
904 s
.WriteBuffer(fname
[0], flen
); // File Name
907 procedure TZIPEditor
.SaveToStream(s
: TStream
);
909 var start
, offset
, loffset
, size
, zcrc
, count
: UInt32
;
911 var afname
: AnsiString;
913 // Write LFH headers and data
915 zcrc
:= crc32(0, nil, 0);
916 if FSection
<> nil then
918 for i
:= 0 to High(FSection
) do
920 if FSection
[i
].list
<> nil then
922 for j
:= 0 to High(FSection
[i
].list
) do
924 p
:= @FSection
[i
].list
[j
];
925 afname
:= GetFileName(FSection
[i
].name
, p
.name
);
926 WriteLFH(s
, p
.comp
, p
.chksum
, p
.csize
, p
.usize
, afname
);
927 if p
.stream
<> nil then
929 Assert(p
.stream
.Size
= p
.csize
);
930 p
.stream
.SaveToStream(s
);
932 else if FStream
<> nil then
934 FStream
.Seek(p
.pos
, TSeekOrigin
.soBeginning
);
935 s
.CopyFrom(FStream
, p
.csize
);
939 raise Exception
.Create('ZIP: SaveToStream: No data source available');
945 afname
:= GetFileName(FSection
[i
].name
, '');
946 WriteLFH(s
, ZIP_COMP_STORE
, zcrc
, 0, 0, afname
);
953 offset
:= s
.Position
- start
;
954 if FSection
<> nil then
956 for i
:= 0 to High(FSection
) do
958 if FSection
[i
].list
<> nil then
960 for j
:= 0 to High(FSection
[i
].list
) do
962 p
:= @FSection
[i
].list
[j
];
963 afname
:= GetFileName(FSection
[i
].name
, p
.name
);
964 WriteCDR(s
, p
.comp
, p
.chksum
, p
.csize
, p
.usize
, 0, loffset
- start
, afname
);
965 loffset
:= loffset
+ 30 + Length(afname
) + p
.csize
;
971 afname
:= GetFileName(FSection
[i
].name
, '');
972 WriteCDR(s
, ZIP_COMP_STORE
, zcrc
, 0, 0, $10, loffset
- start
, afname
);
973 loffset
:= loffset
+ 30 + Length(afname
) + 0;
978 Assert(loffset
= offset
);
979 Assert(count
< $ffff);
980 size
:= s
.Position
- start
- offset
;
982 s
.WriteBuffer(ZIP_SIGN_EOCD
, 4); // EOCD Signature
983 s
.WriteWord(NtoLE(0)); // Disk
984 s
.WriteWord(NtoLE(0)); // Num of Disks
985 s
.WriteWord(NtoLE(count
)); // Num of CDRs
986 s
.WriteWord(NtoLE(count
)); // Total CDR entries
987 s
.WriteDWord(NtoLE(size
)); // Central Directory size
988 s
.WriteDWord(NtoLE(offset
)); // Central Directory offset
989 s
.WriteWord(NtoLE(0)); // Comment field length
992 procedure TZIPEditor
.SaveTo(FileName
: String);
995 s
:= TFileStream
.Create(FileName
, fmCreate
);
1003 function TZIPEditor
.GetLastError
: Integer;
1005 Result
:= FLastError
;
1008 function TZIPEditor
.GetLastErrorStr
: String;
1011 DFWAD_NOERROR
: Result
:= '';
1012 DFWAD_ERROR_WADNOTFOUND
: Result
:= 'DFZIP file not found';
1013 DFWAD_ERROR_CANTOPENWAD
: Result
:= 'Can''t open DFZIP file';
1014 DFWAD_ERROR_RESOURCENOTFOUND
: Result
:= 'Resource not found';
1015 DFWAD_ERROR_FILENOTWAD
: Result
:= 'File is not DFZIP';
1016 DFWAD_ERROR_WADNOTLOADED
: Result
:= 'DFZIP file is not loaded';
1017 DFWAD_ERROR_READRESOURCE
: Result
:= 'Read resource error';
1018 DFWAD_ERROR_READWAD
: Result
:= 'Read DFZIP error';
1019 otherwise Result
:= '';
1023 function TZIPEditor
.GetResourcesCount
: Word;
1027 if FSection
<> nil then
1029 Result
:= Result
+ Length(FSection
);
1030 for i
:= 0 to High(FSection
) do
1031 if FSection
[i
].list
<> nil then
1032 Result
:= Result
+ Length(FSection
[i
].list
);
1036 function TZIPEditor
.GetVersion
: Byte;
1042 gWADEditorFactory
.RegisterEditor('DFZIP', TZIPEditor
);