X-Git-Url: https://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fshared%2FWADEDITOR_dfzip.pas;h=1d319af191172c33d437120e2721cb470044225b;hb=971b787a69c8385dd3b6ea46e7a2b3b6c62d8e6d;hp=7412af8a12aea7c08fb4a57ed902829e9ec76c03;hpb=6f6c01c59fa41269434569e5e69b966b83ea18d7;p=d2df-editor.git diff --git a/src/shared/WADEDITOR_dfzip.pas b/src/shared/WADEDITOR_dfzip.pas index 7412af8..1d319af 100644 --- a/src/shared/WADEDITOR_dfzip.pas +++ b/src/shared/WADEDITOR_dfzip.pas @@ -6,7 +6,7 @@ unit WADEDITOR_dfzip; // - File must start with LFH or EOCD signature // - EOCD must be located strictly at the end of file // - Multi-disk ZIP files are not supported -// - UTF-8 not supported yet, expected WIN1251 encoding +// - Expect UTF-8 or CP1251 encoded names // - ZIP64 not supported // - Encryption not supported // - Zero-length file names not supported @@ -62,7 +62,7 @@ interface procedure ReadEOCD(s: TStream); procedure WriteLFH(s: TStream; comp, crc, csize, usize: UInt32; const afname: AnsiString); - procedure WriteCDR(s: TStream; comp, crc, csize, usize, attr, offset: UInt32; const afname: AnsiString); + procedure WriteCDR(s: TStream; comp, crc, csize, usize, eattr, offset: UInt32; const afname: AnsiString; cdrid: Integer); procedure SaveToStream(s: TStream); public @@ -92,7 +92,7 @@ interface implementation - uses SysUtils, StrUtils, zstream, crc, e_log; + uses SysUtils, StrUtils, Math, utils, zstream, crc, e_log; const ZIP_SIGN_CDR = 'PK'#1#2; @@ -127,9 +127,26 @@ implementation const ZIP_SYSTEM = 0; // DOS / FAT - ZIP_VERSION = 20; // Min version ZIP_MAXVERSION = 63; // Max supported version + const + ZIP_ENCRYPTION_MASK = (1 << 0) or (1 << 6) or (1 << 13); + ZIP_UTF8_MASK = (1 << 11); + + function IsASCII(const s: AnsiString): Boolean; + var i: Integer; + begin + for i := 1 to Length(s) do + begin + if s[i] >= #$80 then + begin + Result := False; + exit; + end; + end; + Result := True; + end; + procedure ToSectionFile(fname: AnsiString; out section, name: AnsiString); inline; var i: SizeInt; begin @@ -382,7 +399,7 @@ implementation FSection := nil; FStream := nil; FLastError := DFWAD_NOERROR; - FVersion := ZIP_VERSION; + FVersion := 10; FreeWAD(); end; @@ -418,7 +435,7 @@ implementation FreeAndNil(FStream); end; FLastError := DFWAD_NOERROR; - FVersion := ZIP_VERSION; + FVersion := 10; end; function TZIPEditor.Preload(p: PResource): Boolean; @@ -442,7 +459,6 @@ implementation Result := True; except s.Free(); - raise; end; end; end; @@ -541,10 +557,16 @@ implementation Assert(p.csize = p.usize); GetMem(ptr, p.usize); try - src.ReadBuffer(ptr[0], p.usize); - Result := True; - except - FreeMem(ptr); + try + src.ReadBuffer(ptr[0], p.usize); + Result := True; + except + FreeMem(ptr); + raise; + end; + except on e: EReadError do + if gWADEditorLogLevel >= DFWAD_LOG_WARN then + e_WriteLog('DFZIP: Failed to read STOREd data, reason: ' + e.Message, MSG_WARNING); end; end; ZIP_COMP_DEFLATE: @@ -557,20 +579,22 @@ implementation Result := True; except FreeMem(ptr); + raise; end; finally tmp.Free(); end; except - on e: Exception do + on e: EStreamError do begin if gWADEditorLogLevel >= DFWAD_LOG_INFO then - e_WriteLog('DFZIP: Failed to decompress by DEFLATE method, reason: ' + e.Message, MSG_WARNING); + e_WriteLog('DFZIP: Failed to decompress DEFLATEd data, reason: ' + e.Message, MSG_WARNING); raise e; end; end; otherwise - raise Exception.Create('Unknown compression method: ' + IntToStr(p.comp)); + if gWADEditorLogLevel >= DFWAD_LOG_INFO then + e_WriteLog('DFZIP: Unsupported compression method: ' + IntToStr(p.comp), MSG_WARNING); end; end else @@ -710,7 +734,6 @@ implementation end; procedure TZIPEditor.ReadCDR(s: TStream; cdrid: Integer); - const ZIP_ENCRYPTION_MASK = (1 << 0) or (1 << 6) or (1 << 13); var sig: packed array [0..3] of Char; var vva, vvb, va, vb, flags, comp: UInt16; var mtime, crc, csize, usize: UInt32; @@ -718,6 +741,7 @@ implementation var eattr, offset: UInt32; var mypos, next: UInt64; var name: PChar; + var aname: AnsiString; begin mypos := s.Position; s.ReadBuffer(sig[0], 4); @@ -760,9 +784,16 @@ implementation e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Comment Length : ' + IntToStr(comlen), MSG_NOTIFY); e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Disk : ' + IntToStr(disk), MSG_NOTIFY); e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Internal Attrib : $' + IntToHex(iattr, 4), MSG_NOTIFY); - e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': External Attrib : $' + IntToHex(iattr, 8), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': External Attrib : $' + IntToHex(eattr, 8), MSG_NOTIFY); e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': LFH Offset : $' + IntToHex(offset, 8), MSG_NOTIFY); end; + 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 + begin + // HACK: Editor and wadcvt for long time sets incorrent flag for UTF-8 + if gWADEditorLogLevel >= DFWAD_LOG_DEBUG then + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': WADCVT BUG : YES', MSG_NOTIFY); + flags := ZIP_UTF8_MASK; + end; if (va >= 10) and (va <= ZIP_MAXVERSION) then begin if (flags and ZIP_ENCRYPTION_MASK) = 0 then @@ -802,17 +833,26 @@ implementation ZIP_COMP_AE: raise Exception.Create('Encrypted archives not supported'); otherwise - raise Exception.Create('Unsupported compression method ' + IntToStr(comp)); + raise Exception.Create('Unknown compression method ' + IntToStr(comp)); end; - // TODO: check bit 11 (UTF8 name and comment) GetMem(name, UInt32(fnlen) + 1); try s.ReadBuffer(name[0], fnlen); name[fnlen] := #0; + aname := name; + if (flags and ZIP_UTF8_MASK = 0) and (IsASCII(name) = False) then + begin + // TODO: Detect UTF-8 + // Older or invalid packers does not set bit 11 + // Some newer packers may write UTF-8 names into extended data + if gWADEditorLogLevel >= DFWAD_LOG_WARN then + e_WriteLog('ZIP: Non UTF-8 encoded name, threat it as CP1251', MSG_WARNING); + aname := win2utf(aname); + end; if gWADEditorLogLevel >= DFWAD_LOG_DEBUG then - e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Name : "' + name + '"', MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Name : "' + aname + '"', MSG_NOTIFY); s.Seek(offset, TSeekOrigin.soBeginning); - ReadLFH(s, name, csize, usize, comp, crc); + ReadLFH(s, aname, csize, usize, comp, crc); finally s.Seek(next, TSeekOrigin.soBeginning); FreeMem(name); @@ -940,7 +980,7 @@ implementation raise Exception.Create('EOCD not found (corrupted file?)'); end else - raise Exception.Create('Not DFZIP file'); + raise Exception.Create('Not DFZIP formated file'); end; function TZIPEditor.ReadFile2(FileName: String): Boolean; @@ -966,14 +1006,13 @@ implementation if gWADEditorLogLevel >= DFWAD_LOG_INFO then e_WriteLog('ZIP: Failed to read ZIP from file ' + FileName + ', reason: ' + e.Message, MSG_WARNING); FreeWAD(); - raise e; end; end; except on e: EFOpenError do begin if gWADEditorLogLevel >= DFWAD_LOG_INFO then - e_WriteLog('ZIP: Failed to open file ' + FileName + ', reason: ' + e.Message, MSG_WARNING); + e_WriteLog('DFZIP: Failed to open file ' + FileName + ', reason: ' + e.Message, MSG_WARNING); if FileExists(FileName) then FLastError := DFWAD_ERROR_CANTOPENWAD else @@ -1005,9 +1044,8 @@ implementation on e: Exception do begin if gWADEditorLogLevel >= DFWAD_LOG_INFO then - e_WriteLog('ZIP: Failed to read ZIP from memory, reason: ' + e.Message, MSG_WARNING); + e_WriteLog('DFZIP: Failed to read ZIP from memory, reason: ' + e.Message, MSG_WARNING); FreeWAD(); - raise e; end; end; end; @@ -1029,49 +1067,133 @@ implementation end; end; + function GetZIPVersion(const afname: AnsiString; flags, comp: UInt16): UInt8; + var version: UInt8; + begin + version := 10; // Base version + case comp of + ZIP_COMP_STORE: version := 10; + ZIP_COMP_SHRUNK: version := 10; + ZIP_COMP_REDUCE1: version := 10; + ZIP_COMP_REDUCE2: version := 10; + ZIP_COMP_REDUCE3: version := 10; + ZIP_COMP_REDUCE4: version := 10; + ZIP_COMP_IMPLODE: version := 10; + ZIP_COMP_TOKENIZED: version := 20; + ZIP_COMP_DEFLATE: version := 20; + ZIP_COMP_DEFLATE64: version := 21; + ZIP_COMP_TERSE1: version := 25; // PKWARE DCL Implode + ZIP_COMP_BZIP2: version := 46; + ZIP_COMP_LZMA: version := 63; + ZIP_COMP_CMPSC: version := 63; + ZIP_COMP_TERSE2: version := 63; + ZIP_COMP_LZ77: version := 63; + ZIP_COMP_ZSTD1: version := 63; + ZIP_COMP_ZSTD2: version := 63; + ZIP_COMP_MP3: version := 63; + ZIP_COMP_XZ: version := 63; + ZIP_COMP_JPEG: version := 63; + ZIP_COMP_WAVPACK: version := 63; + ZIP_COMP_PPMD: version := 63; + ZIP_COMP_AE: version := 63; + end; + if afname[Length(afname)] = '/' then + version := Max(20, version); // Folder + if flags and ZIP_UTF8_MASK <> 0 then + version := Max(63, version); // UTF-8 name + Result := version; + end; + procedure TZIPEditor.WriteLFH(s: TStream; comp, crc, csize, usize: UInt32; const afname: AnsiString); - var fname: PChar; flen: UInt16; + var fname: PChar; version: UInt8; fnlen, flags: UInt16; mypos: UInt64; begin + mypos := s.Position; fname := PChar(afname); - flen := Length(fname); + fnlen := Length(fname); + flags := 0; + if IsASCII(afname) = False then + flags := flags or ZIP_UTF8_MASK; + version := GetZIPVersion(afname, flags, comp); + if gWADEditorLogLevel >= DFWAD_LOG_DEBUG then + begin + e_WriteLog('==============================================', MSG_NOTIFY); + e_WriteLog('LFH @' + IntToHex(mypos, 8) + ': Min Version : ' + IntToStr(version), MSG_NOTIFY); + e_WriteLog('LFH @' + IntToHex(mypos, 8) + ': Min System : ' + IntToStr(ZIP_SYSTEM), MSG_NOTIFY); + e_WriteLog('LFH @' + IntToHex(mypos, 8) + ': Flags : $' + IntToHex(flags, 4), MSG_NOTIFY); + e_WriteLog('LFH @' + IntToHex(mypos, 8) + ': Compression : ' + IntToStr(comp), MSG_NOTIFY); + e_WriteLog('LFH @' + IntToHex(mypos, 8) + ': Modification Time : $' + IntToHex(0, 8), MSG_NOTIFY); + e_WriteLog('LFH @' + IntToHex(mypos, 8) + ': CRC-32 : $' + IntToHex(crc, 8), MSG_NOTIFY); + e_WriteLog('LFH @' + IntToHex(mypos, 8) + ': Compressed size : ' + IntToStr(csize), MSG_NOTIFY); + e_WriteLog('LFH @' + IntToHex(mypos, 8) + ': Decompressed size : ' + IntToStr(usize), MSG_NOTIFY); + e_WriteLog('LFH @' + IntToHex(mypos, 8) + ': Name Length : ' + IntToStr(fnlen), MSG_NOTIFY); + e_WriteLog('LFH @' + IntToHex(mypos, 8) + ': Extension Length : ' + IntToStr(0), MSG_NOTIFY); + e_WriteLog('LFH @' + IntToHex(mypos, 8) + ': Name : "' + fname + '"', MSG_NOTIFY); + end; s.WriteBuffer(ZIP_SIGN_LFH, 4); // LFH Signature - s.WriteByte(ZIP_VERSION); // Min version + s.WriteByte(version); // Min version s.WriteByte(ZIP_SYSTEM); // System - s.WriteWord(NtoLE(0)); // Flags - s.WriteWord(NtoLE(comp)); // Compression method - s.WriteDWord(NtoLE(0)); // Modification time/date - s.WriteDWord(NtoLE(crc)); // CRC-32 - s.WriteDWord(NtoLE(csize)); // Compressed size - s.WriteDWord(NtoLE(usize)); // Decompressed size - s.WriteWord(NtoLE(flen)); // Name field length - s.WriteWord(NtoLE(0)); // Extra field length - s.WriteBuffer(fname[0], flen); // File Name + WriteInt(s, UInt16(flags)); // Flags + WriteInt(s, UInt16(comp)); // Compression method + WriteInt(s, UInt32(0)); // Modification time/date + WriteInt(s, UInt32(crc)); // CRC-32 + WriteInt(s, UInt32(csize)); // Compressed size + WriteInt(s, UInt32(usize)); // Decompressed size + WriteInt(s, UInt16(fnlen)); // Name field length + WriteInt(s, UInt16(0)); // Extra field length + s.WriteBuffer(fname[0], fnlen); // File Name end; - procedure TZIPEditor.WriteCDR(s: TStream; comp, crc, csize, usize, attr, offset: UInt32; const afname: AnsiString); - var fname: PChar; flen: UInt16; + procedure TZIPEditor.WriteCDR(s: TStream; comp, crc, csize, usize, eattr, offset: UInt32; const afname: AnsiString; cdrid: Integer); + var fname: PChar; version: UInt8; fnlen, flags: UInt16; mypos: UInt64; begin + mypos := s.Position; fname := PChar(afname); - flen := Length(fname); + fnlen := Length(fname); + flags := 0; + if IsASCII(afname) = False then + flags := flags or ZIP_UTF8_MASK; + version := GetZIPVersion(afname, flags, comp); + if gWADEditorLogLevel >= DFWAD_LOG_DEBUG then + begin + e_WriteLog('==============================================', MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Writer Version : ' + IntToStr(ZIP_MAXVERSION), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Writer System : ' + IntToStr(ZIP_SYSTEM), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Min Version : ' + IntToStr(version), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Min System : ' + IntToStr(ZIP_SYSTEM), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Flags : $' + IntToHex(flags, 4), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Compression : ' + IntToStr(comp), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Modification Time : $' + IntToHex(0, 8), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': CRC-32 : $' + IntToHex(crc, 8), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Compressed size : ' + IntToStr(csize), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Decompressed size : ' + IntToStr(usize), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Name Length : ' + IntToStr(fnlen), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Extension Length : ' + IntToStr(0), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Comment Length : ' + IntToStr(0), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Disk : ' + IntToStr(0), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Internal Attrib : $' + IntToHex(0, 4), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': External Attrib : $' + IntToHex(eattr, 8), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': LFH Offset : $' + IntToHex(offset, 8), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Name : "' + fname + '"', MSG_NOTIFY); + end; s.WriteBuffer(ZIP_SIGN_CDR, 4); // CDR Signature s.WriteByte(ZIP_MAXVERSION); // Used version s.WriteByte(ZIP_SYSTEM); // Used system - s.WriteByte(ZIP_VERSION); // Min version + s.WriteByte(version); // Min version s.WriteByte(ZIP_SYSTEM); // Min system - s.WriteWord(NtoLE(0)); // Flags - s.WriteWord(NtoLE(comp)); // Compression method - s.WriteDWord(NtoLE(0)); // Modification time/date - s.WriteDWord(NtoLE(crc)); // CRC-32 - s.WriteDWord(NtoLE(csize)); // Compressed size - s.WriteDWord(NtoLE(usize)); // Decompressed size - s.WriteWord(NtoLE(flen)); // Name field length - s.WriteWord(NtoLE(0)); // Extra field length - s.WriteWord(NtoLE(0)); // Comment field length - s.WriteWord(NtoLE(0)); // Disk - s.WriteWord(NtoLE(0)); // Internal attributes - s.WriteDWord(NtoLE(attr)); // External attributes - s.WriteDWord(NtoLE(offset)); // LFH offset - s.WriteBuffer(fname[0], flen); // File Name + WriteInt(s, UInt16(flags)); // Flags + WriteInt(s, UInt16(comp)); // Compression method + WriteInt(s, UInt32(0)); // Modification time/date + WriteInt(s, UInt32(crc)); // CRC-32 + WriteInt(s, UInt32(csize)); // Compressed size + WriteInt(s, UInt32(usize)); // Decompressed size + WriteInt(s, UInt16(fnlen)); // Name field length + WriteInt(s, UInt16(0)); // Extra field length + WriteInt(s, UInt16(0)); // Comment field length + WriteInt(s, UInt16(0)); // Disk + WriteInt(s, UInt16(0)); // Internal attributes + WriteInt(s, UInt32(eattr)); // External attributes + WriteInt(s, UInt32(offset)); // LFH offset + s.WriteBuffer(fname[0], fnlen); // File Name end; procedure TZIPEditor.SaveToStream(s: TStream); @@ -1079,6 +1201,7 @@ implementation var start, offset, loffset, size, zcrc, count: UInt32; var p: PResource; var afname: AnsiString; + var mypos: UInt64; begin // Write LFH headers and data start := s.Position; @@ -1119,7 +1242,7 @@ implementation end; // Write CDR headers count := 0; - loffset := start; + loffset := 0; offset := s.Position - start; if FSection <> nil then begin @@ -1131,7 +1254,7 @@ implementation begin p := @FSection[i].list[j]; afname := GetFileName(FSection[i].name, p.name); - WriteCDR(s, p.comp, p.chksum, p.csize, p.usize, 0, loffset - start, afname); + WriteCDR(s, p.comp, p.chksum, p.csize, p.usize, $00, loffset, afname, i); loffset := loffset + 30 + Length(afname) + p.csize; Inc(count); end; @@ -1139,7 +1262,7 @@ implementation else begin afname := GetFileName(FSection[i].name, ''); - WriteCDR(s, ZIP_COMP_STORE, zcrc, 0, 0, $10, loffset - start, afname); + WriteCDR(s, ZIP_COMP_STORE, zcrc, 0, 0, $10, loffset, afname, i); loffset := loffset + 30 + Length(afname) + 0; Inc(count); end; @@ -1149,14 +1272,27 @@ implementation Assert(count < $ffff); size := s.Position - start - offset; // Write EOCD header + mypos := s.Position; + if gWADEditorLogLevel >= DFWAD_LOG_DEBUG then + begin + e_WriteLog('==============================================', MSG_NOTIFY); + e_WriteLog('EOCD @' + IntToHex(mypos, 8) + ': Disk ID : ' + IntToStr(0), MSG_NOTIFY); + e_WriteLog('EOCD @' + IntToHex(mypos, 8) + ': Disk ID with CD : ' + IntToStr(0), MSG_NOTIFY); + e_WriteLog('EOCD @' + IntToHex(mypos, 8) + ': Available CDR''s : ' + IntToStr(count), MSG_NOTIFY); + e_WriteLog('EOCD @' + IntToHex(mypos, 8) + ': Total CDR''s : ' + IntToStr(count), MSG_NOTIFY); + e_WriteLog('EOCD @' + IntToHex(mypos, 8) + ': CD Length : ' + IntToStr(size), MSG_NOTIFY); + e_WriteLog('EOCD @' + IntToHex(mypos, 8) + ': CD Offset : $' + IntToHex(offset, 8), MSG_NOTIFY); + e_WriteLog('EOCD @' + IntToHex(mypos, 8) + ': Comment Length : ' + IntToStr(0), MSG_NOTIFY); + e_WriteLog('==============================================', MSG_NOTIFY); + end; s.WriteBuffer(ZIP_SIGN_EOCD, 4); // EOCD Signature - s.WriteWord(NtoLE(0)); // Disk - s.WriteWord(NtoLE(0)); // Num of Disks - s.WriteWord(NtoLE(count)); // Num of CDRs - s.WriteWord(NtoLE(count)); // Total CDR entries - s.WriteDWord(NtoLE(size)); // Central Directory size - s.WriteDWord(NtoLE(offset)); // Central Directory offset - s.WriteWord(NtoLE(0)); // Comment field length + WriteInt(s, UInt16(0)); // Disk + WriteInt(s, UInt16(0)); // Num of Disks + WriteInt(s, UInt16(count)); // Num of CDRs + WriteInt(s, UInt16(count)); // Total CDR entries + WriteInt(s, UInt32(size)); // Central Directory size + WriteInt(s, UInt32(offset)); // Central Directory offset + WriteInt(s, UInt16(0)); // Comment field length end; procedure TZIPEditor.SaveTo(FileName: String);