X-Git-Url: https://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fshared%2FWADEDITOR_dfzip.pas;h=8d168a0c8266c5b35ad9f8af2faa8f00a6f8d20c;hb=7550b9364b1b54842bd371128c99bd878c8e67cc;hp=f7abf9e9bb1d8ac3a7532870861d4040d5f9247d;hpb=952e5c9c629e44e260c55f2756d72a199d8d9052;p=d2df-editor.git diff --git a/src/shared/WADEDITOR_dfzip.pas b/src/shared/WADEDITOR_dfzip.pas index f7abf9e..8d168a0 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 @@ -26,11 +26,13 @@ interface usize: UInt32; comp: UInt32; chksum: UInt32; + mtime: UInt32; stream: TMemoryStream; end; TSection = record name: AnsiString; + mtime: UInt32; list: array of TResource; end; @@ -46,23 +48,23 @@ interface function FindSectionIDRAW(name: AnsiString; caseSensitive: Boolean): Integer; function FindSectionRAW(name: AnsiString; caseSensitive: Boolean): PSection; - function InsertSectionRAW(name: AnsiString): PSection; + function InsertSectionRAW(name: AnsiString; mtime: UInt32): PSection; function FindSectionID(name: AnsiString): Integer; function FindSection(name: AnsiString): PSection; - function InsertSection(name: AnsiString): PSection; + function InsertSection(name: AnsiString; mtime: UInt32): PSection; - function InsertFileInfo(const section, name: AnsiString; pos, csize, usize, comp, crc: UInt32): PResource; + function InsertFileInfo(const section, name: AnsiString; pos, csize, usize, comp, crc, mtime: UInt32): PResource; function Preload(p: PResource): Boolean; function GetSourceStream(p: PResource): TStream; - function ReadLFH(s: TStream; fname: AnsiString; xcsize, xusize, xcomp, xcrc: UInt32): Boolean; - function ReadCDR(s: TStream): Boolean; + procedure ReadLFH(s: TStream; fname: AnsiString; xcsize, xusize, xcomp, xcrc, xtime: UInt32); + procedure ReadCDR(s: TStream; cdrid: Integer); function FindEOCD(s: TStream): Boolean; - function ReadEOCD(s: TStream): Boolean; + 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 WriteLFH(s: TStream; comp, mtime, crc, csize, usize: UInt32; const afname: AnsiString); + procedure WriteCDR(s: TStream; comp, mtime, crc, csize, usize, eattr, offset: UInt32; const afname: AnsiString; cdrid: Integer); procedure SaveToStream(s: TStream); public @@ -92,7 +94,7 @@ interface implementation - uses SysUtils, StrUtils, zstream, crc, e_log; + uses SysUtils, StrUtils, DateUtils, Math, utils, zstream, crc, e_log; const ZIP_SIGN_CDR = 'PK'#1#2; @@ -100,14 +102,92 @@ implementation ZIP_SIGN_EOCD = 'PK'#5#6; const - ZIP_COMP_STORE = 0; - ZIP_COMP_DEFLATE = 8; + ZIP_COMP_STORE = 0; + ZIP_COMP_SHRUNK = 1; + ZIP_COMP_REDUCE1 = 2; + ZIP_COMP_REDUCE2 = 3; + ZIP_COMP_REDUCE3 = 4; + ZIP_COMP_REDUCE4 = 5; + ZIP_COMP_IMPLODE = 6; + ZIP_COMP_TOKENIZED = 7; + ZIP_COMP_DEFLATE = 8; + ZIP_COMP_DEFLATE64 = 9; + ZIP_COMP_TERSE1 = 10; + ZIP_COMP_BZIP2 = 12; + ZIP_COMP_LZMA = 14; + ZIP_COMP_CMPSC = 16; + ZIP_COMP_TERSE2 = 18; + ZIP_COMP_LZ77 = 19; + ZIP_COMP_ZSTD1 = 20; + ZIP_COMP_ZSTD2 = 93; + ZIP_COMP_MP3 = 94; + ZIP_COMP_XZ = 95; + ZIP_COMP_JPEG = 96; + ZIP_COMP_WAVPACK = 97; + ZIP_COMP_PPMD = 98; + ZIP_COMP_AE = 99; 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; + + function IsUTF8(const s: AnsiString): Boolean; + var i, j, len: Integer; + begin + Result := False; + i := 1; len := Length(s); + while i <= len do + begin + case Ord(s[i]) of + $00..$7F: j := 0; + $80..$BF: exit; // invalid encoding + $C0..$DF: j := 1; + $E0..$EF: j := 2; + $F0..$F7: j := 3; + otherwise exit; // invalid encoding + end; + Inc(i); + while j > 0 do + begin + if i > len then exit; // invlid length + case Ord(s[i]) of + $80..$BF: ; // ok + else exit; // invalid encoding + end; + Inc(i); + Dec(j); + end; + end; + Result := True; + end; + + function DosToStr(dostime: UInt32): AnsiString; + begin + try + DateTimeToString(Result, 'yyyy/mm/dd hh:nn:ss', DosDateTimeToDateTime(dostime)); + except on e: EConvertError do + Result := 'INVALID ($' + IntToHex(dostime, 8) + ')'; + end; + end; + procedure ToSectionFile(fname: AnsiString; out section, name: AnsiString); inline; var i: SizeInt; begin @@ -217,13 +297,14 @@ implementation Result := nil; end; - function TZIPEditor.InsertSectionRAW(name: AnsiString): PSection; + function TZIPEditor.InsertSectionRAW(name: AnsiString; mtime: UInt32): PSection; var i: Integer; begin if FSection = nil then i := 0 else i := Length(FSection); SetLength(FSection, i + 1); FSection[i] := Default(TSection); FSection[i].name := name; + FSection[i].mtime := mtime; Result := @FSection[i]; end; @@ -247,21 +328,21 @@ implementation Result := FindSectionRAW(fixName, False); // CASENAME end; - function TZIPEditor.InsertSection(name: AnsiString): PSection; + function TZIPEditor.InsertSection(name: AnsiString; mtime: UInt32): PSection; begin Result := FindSection(name); if Result = nil then - Result := InsertSectionRAW(name); + Result := InsertSectionRAW(name, mtime); end; - function TZIPEditor.InsertFileInfo(const section, name: AnsiString; pos, csize, usize, comp, crc: UInt32): PResource; + function TZIPEditor.InsertFileInfo(const section, name: AnsiString; pos, csize, usize, comp, crc, mtime: UInt32): PResource; var p: PSection; i: Integer; begin p := FindSectionRAW(section, True); if p = nil then - p := InsertSectionRAW(section); + p := InsertSectionRAW(section, mtime); if p.list = nil then i := 0 else i := Length(p.list); SetLength(p.list, i + 1); p.list[i] := Default(TResource); @@ -271,6 +352,7 @@ implementation p.list[i].usize := usize; p.list[i].comp := comp; p.list[i].chksum := crc; + p.list[i].mtime := mtime; p.list[i].stream := nil; Result := @p.list[i]; end; @@ -316,7 +398,7 @@ implementation end; crc := crc32(0, nil, 0); crc := crc32(crc, data, len); - p := InsertFileInfo(Section, Name, $ffffffff, s.Size, Len, comp, crc); + p := InsertFileInfo(Section, Name, $ffffffff, s.Size, Len, comp, crc, DateTimeToDosDateTime(Now())); p.stream := s; Result := True; except @@ -332,7 +414,7 @@ implementation Result := False; FLastError := DFWAD_ERROR_READWAD; try - s := TFileStream.Create(FileName, fmOpenRead, fmShareDenyWrite); + s := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); try GetMem(ptr, s.Size); try @@ -345,8 +427,13 @@ implementation finally s.Free(); end; - except on e: EFOpenError do - FLastError := DFWAD_ERROR_CANTOPENWAD; + except + on e: EFOpenError do + begin + if gWADEditorLogLevel >= DFWAD_LOG_INFO then + e_WriteLog('DFZIP: AddResource: failed to open file ' + FileName, MSG_NOTIFY); + FLastError := DFWAD_ERROR_CANTOPENWAD; + end; end; end; @@ -355,7 +442,7 @@ implementation FSection := nil; FStream := nil; FLastError := DFWAD_NOERROR; - FVersion := ZIP_VERSION; + FVersion := 10; FreeWAD(); end; @@ -391,7 +478,7 @@ implementation FreeAndNil(FStream); end; FLastError := DFWAD_NOERROR; - FVersion := ZIP_VERSION; + FVersion := 10; end; function TZIPEditor.Preload(p: PResource): Boolean; @@ -415,7 +502,6 @@ implementation Result := True; except s.Free(); - raise; end; end; end; @@ -426,10 +512,14 @@ implementation begin if FStream = nil then begin + if gWADEditorLogLevel >= DFWAD_LOG_DEBUG then + e_WriteLog('DFZIP: CreateImage: File not assigned', MSG_NOTIFY); FLastError := DFWAD_ERROR_WADNOTLOADED; end else if FStream is TMemoryStream then begin + if gWADEditorLogLevel >= DFWAD_LOG_DEBUG then + e_WriteLog('DFZIP: CreateImage: Memory stream', MSG_NOTIFY); FLastError := DFWAD_NOERROR; end else @@ -444,6 +534,8 @@ implementation begin if Preload(@FSection[i].list[j]) = False then begin + if gWADEditorLogLevel >= DFWAD_LOG_WARN then + e_WriteLog('DFZIP: CreateImage: failed to preload resource [' + FSection[i].name + '][' + FSection[i].list[j].name + ']', MSG_WARNING); FLastError := DFWAD_ERROR_CANTOPENWAD; exit; end; @@ -458,8 +550,8 @@ implementation procedure TZIPEditor.AddSection(Name: String); begin - if InsertSection(Name) = nil then - raise Exception.Create('ZIP: AddSection: failed to add section'); + if InsertSection(Name, DateTimeToDosDateTime(Now())) = nil then + raise Exception.Create('DFZIP: AddSection[' + Name + ']: failed to insert'); end; function TZIPEditor.HaveResource(Section, Resource: String): Boolean; @@ -504,18 +596,24 @@ implementation begin case p.comp of ZIP_COMP_STORE: - if p.csize = p.usize then begin + 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: - begin + try tmp := TDecompressionStream.Create(src, True); try GetMem(ptr, p.usize); @@ -524,15 +622,28 @@ implementation Result := True; except FreeMem(ptr); + raise; end; finally tmp.Free(); end; + except + on e: EStreamError do + begin + if gWADEditorLogLevel >= DFWAD_LOG_INFO then + e_WriteLog('DFZIP: Failed to decompress DEFLATEd data, reason: ' + e.Message, MSG_WARNING); + raise e; + end; end; + otherwise + if gWADEditorLogLevel >= DFWAD_LOG_INFO then + e_WriteLog('DFZIP: Unsupported compression method: ' + IntToStr(p.comp), MSG_WARNING); end; end else begin + if gWADEditorLogLevel >= DFWAD_LOG_WARN then + e_WriteLog('DFZIP: No available source for file data', MSG_WARNING); FLastError := DFWAD_ERROR_WADNOTLOADED; end; if Result = True then @@ -548,12 +659,16 @@ implementation end else begin + if gWADEditorLogLevel >= DFWAD_LOG_INFO then + e_WriteLog('DFZIP: File integrity check failed: expected CRC32 $' + IntToHex(p.chksum, 8) + ', calculated CRC32 $' + IntToHex(crc, 8), MSG_WARNING); FreeMem(ptr); end; end; end else begin + if gWADEditorLogLevel >= DFWAD_LOG_DEBUG then + e_WriteLog('DFZIP: Resource not found', MSG_NOTIFY); FLastError := DFWAD_ERROR_RESOURCENOTFOUND; end; end; @@ -587,21 +702,23 @@ implementation end; end; - function TZIPEditor.ReadLFH(s: TStream; fname: AnsiString; xcsize, xusize, xcomp, xcrc: UInt32): Boolean; + procedure TZIPEditor.ReadLFH(s: TStream; fname: AnsiString; xcsize, xusize, xcomp, xcrc, xtime: UInt32); var sig: packed array [0..3] of Char; - var v, flags, comp: UInt16; + var va, vb, flags, comp: UInt16; var mtime, crc, csize, usize: UInt32; var fnlen, extlen: UInt16; - var datapos: UInt64; + var mypos, datapos: UInt64; var section, name: AnsiString; + var p: Pointer; begin - Result := False; - if s.Position + 30 <= s.Size then + mypos := s.Position; + if mypos + 30 <= s.Size then begin s.ReadBuffer(sig[0], 4); if sig = ZIP_SIGN_LFH then begin - v := LEtoN(s.ReadWord()); + va := s.ReadByte(); // Min Version + vb := s.ReadByte(); // Min System flags := LEtoN(s.ReadWord()); comp := LEtoN(s.ReadWord()); mtime := LEtoN(s.ReadDWord()); @@ -611,36 +728,74 @@ implementation fnlen := LEtoN(s.ReadWord()); extlen := LEtoN(s.ReadWord()); datapos := s.Position + fnlen + extlen; - if datapos + xcsize <= s.Size then + if gWADEditorLogLevel >= DFWAD_LOG_DEBUG then + begin + e_WriteLog('LFH @' + IntToHex(mypos, 8) + ': Min Version : ' + IntToStr(va), MSG_NOTIFY); + e_WriteLog('LFH @' + IntToHex(mypos, 8) + ': Min System : ' + IntToStr(vb), 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 : ' + DosToStr(mtime), 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(extlen), MSG_NOTIFY); + e_WriteLog('LFH @' + IntToHex(mypos, 8) + ': : $' + IntToHex(datapos, 8), MSG_NOTIFY); + end; + if (va >= 10) and (va <= ZIP_MAXVERSION) then begin - // Valid Record Size - ToSectionFile(fname, section, name); - if name = '' then - Result := InsertSection(section) <> nil + if datapos + xcsize <= s.Size then + begin + ToSectionFile(fname, section, name); + if name = '' then + begin + p := FindSectionRAW(section, True); + if p = nil then + p := InsertSectionRAW(section, xtime); + end + else + begin + p := InsertFileInfo(section, name, datapos, xcsize, xusize, xcomp, xcrc, xtime); + end; + if p = nil then + raise Exception.Create('Failed to register resource [' + fname + ']'); + end else - Result := InsertFileInfo(section, name, datapos, xcsize, xusize, xcomp, xcrc) <> nil; + raise Exception.Create('Invalid LFH size (corrupted file?)'); + end + else + begin + FLastError := DFWAD_ERROR_WRONGVERSION; + raise Exception.Create('Unsupported CDR version ' + IntToStr(va) + ', not in range [10..' + IntToStr(ZIP_MAXVERSION) + ']'); end; - end; - end; + end + else + 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?)'); + end + else + raise Exception.Create('Invalid LFH size (corrupted file?)'); end; - function TZIPEditor.ReadCDR(s: TStream): Boolean; + procedure TZIPEditor.ReadCDR(s: TStream; cdrid: Integer); var sig: packed array [0..3] of Char; - var v, va, vb, flags, comp: UInt16; + var vva, vvb, va, vb, flags, comp: UInt16; var mtime, crc, csize, usize: UInt32; var fnlen, extlen, comlen, disk, iattr: UInt16; var eattr, offset: UInt32; - var next: UInt64; + var mypos, next: UInt64; var name: PChar; + var aname: AnsiString; + var cvtbug, utf8: Boolean; begin - Result := False; + mypos := s.Position; s.ReadBuffer(sig[0], 4); if sig = ZIP_SIGN_CDR then begin // Valid Central Directory Signature - v := LEtoN(s.ReadWord()); - va := s.ReadByte(); // Min Version - vb := s.ReadByte(); // Min System + vva := s.ReadByte(); // Writer Version + vvb := s.ReadByte(); // Writer System + va := s.ReadByte(); // Min Version + vb := s.ReadByte(); // Min System flags := LEtoN(s.ReadWord()); comp := LEtoN(s.ReadWord()); mtime := LEtoN(s.ReadDWord()); @@ -656,51 +811,125 @@ implementation offset := LEtoN(s.ReadDWord()); next := s.Position + fnlen + extlen + comlen; FVersion := va; - if va <= ZIP_MAXVERSION then + if gWADEditorLogLevel >= DFWAD_LOG_DEBUG then + begin + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Writer Version : ' + IntToStr(vva), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Writer System : ' + IntToStr(vvb), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Min Version : ' + IntToStr(va), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Min System : ' + IntToStr(vb), 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 : ' + DosToStr(mtime), 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(extlen), MSG_NOTIFY); + 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(eattr, 8), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': LFH Offset : $' + IntToHex(offset, 8), MSG_NOTIFY); + end; + cvtbug := False; + 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 - if (flags and ((1 << 0) or (1 << 6) or (1 << 13))) = 0 then + // HACK: Editor and wadcvt for long time sets incorrent flag for UTF-8 + flags := ZIP_UTF8_MASK; + cvtbug := True; + end; + if gWADEditorLogLevel >= DFWAD_LOG_DEBUG then + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': WADCVT BUG : ' + BoolToStr(cvtbug, True), MSG_NOTIFY); + if (va >= 10) and (va <= ZIP_MAXVERSION) then + begin + if (flags and ZIP_ENCRYPTION_MASK) = 0 then begin - // TODO: check bit 11 (UTF8 name and comment) if (csize <> $ffffffff) and (usize <> $ffffffff) and (disk <> $ffff) and (offset <> $ffffffff) then begin - // Old Style ZIP if disk = 0 then begin - // Single Volume ZIP if (next <= s.Size) and (fnlen > 0) then begin - // Valid Central Directory Entry + case comp of + ZIP_COMP_STORE: + if csize <> usize then + raise Exception.Create('Compressed size ' + IntToStr(csize) + ' != Descompressed size ' + IntToStr(usize) + 'for STORE method (corrupted file?)'); + ZIP_COMP_SHRUNK, + ZIP_COMP_REDUCE1, + ZIP_COMP_REDUCE2, + ZIP_COMP_REDUCE3, + ZIP_COMP_REDUCE4, + ZIP_COMP_IMPLODE, + ZIP_COMP_DEFLATE, + ZIP_COMP_DEFLATE64, + ZIP_COMP_TERSE1, + ZIP_COMP_BZIP2, + ZIP_COMP_LZMA, + ZIP_COMP_CMPSC, + ZIP_COMP_TERSE2, + ZIP_COMP_LZ77, + ZIP_COMP_ZSTD1, + ZIP_COMP_ZSTD2, + ZIP_COMP_MP3, + ZIP_COMP_XZ, + ZIP_COMP_JPEG, + ZIP_COMP_WAVPACK, + ZIP_COMP_PPMD: + ; // ok + ZIP_COMP_AE: + raise Exception.Create('Encrypted archives not supported'); + otherwise + raise Exception.Create('Unknown compression method ' + IntToStr(comp)); + end; GetMem(name, UInt32(fnlen) + 1); try s.ReadBuffer(name[0], fnlen); name[fnlen] := #0; + aname := name; + utf8 := True; + if (flags and ZIP_UTF8_MASK = 0) and (IsUTF8(name) = False) then + begin + aname := win2utf(aname); + utf8 := False; + end; + if gWADEditorLogLevel >= DFWAD_LOG_DEBUG then + begin + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': UTF-8 Comatible : ' + BoolToStr(utf8, True), MSG_NOTIFY); + e_WriteLog('CDR#' + IntToStr(cdrid) + ' @' + IntToHex(mypos, 8) + ': Name : "' + aname + '"', MSG_NOTIFY); + end; s.Seek(offset, TSeekOrigin.soBeginning); - Result := ReadLFH(s, name, csize, usize, comp, crc); + ReadLFH(s, aname, csize, usize, comp, crc, mtime); finally s.Seek(next, TSeekOrigin.soBeginning); FreeMem(name); end; - end; - end; + end + else + raise Exception.Create('Empty files names not supported'); + end + else + raise Exception.Create('Splitted archives not supported'); end else begin - // ZIP64 FLastError := DFWAD_ERROR_WRONGVERSION; + raise Exception.Create('ZIP64 not supported'); end; end else begin - // Encrypted file FLastError := DFWAD_ERROR_READWAD; + raise Exception.Create('Encrypted archives not supported'); end; end else begin - // Unsupported version FLastError := DFWAD_ERROR_WRONGVERSION; + raise Exception.Create('Unsupported CDR version ' + IntToStr(va) + ', not in range [10..' + IntToStr(ZIP_MAXVERSION) + ']'); end; - end; + end + else + 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?)'); end; function TZIPEditor.FindEOCD(s: TStream): Boolean; @@ -724,12 +953,12 @@ implementation end; end; - function TZIPEditor.ReadEOCD(s: TStream): Boolean; + procedure TZIPEditor.ReadEOCD(s: TStream); var sig: packed array [0..3] of Char; var idisk, ndisk, nrec, total, comlen: UInt16; var csize, cpos, i: UInt32; + var mypos: UInt64; begin - Result := False; FLastError := DFWAD_ERROR_FILENOTWAD; FVersion := 0; s.ReadBuffer(sig[0], 4); @@ -739,6 +968,7 @@ implementation begin // End of Central Directory found FLastError := DFWAD_ERROR_READWAD; + mypos := s.Position - 4; idisk := LEtoN(s.ReadWord()); ndisk := LEtoN(s.ReadWord()); nrec := LEtoN(s.ReadWord()); @@ -746,43 +976,57 @@ implementation csize := LEtoN(s.ReadDWord()); cpos := LEtoN(s.ReadDWord()); comlen := LEtoN(s.ReadWord()); + if gWADEditorLogLevel >= DFWAD_LOG_DEBUG then + begin + e_WriteLog('==============================================', MSG_NOTIFY); + e_WriteLog('EOCD @' + IntToHex(mypos, 8) + ': Disk ID : ' + IntToStr(idisk), MSG_NOTIFY); + e_WriteLog('EOCD @' + IntToHex(mypos, 8) + ': Disk ID with CD : ' + IntToStr(ndisk), MSG_NOTIFY); + e_WriteLog('EOCD @' + IntToHex(mypos, 8) + ': Available CDR''s : ' + IntToStr(nrec), MSG_NOTIFY); + e_WriteLog('EOCD @' + IntToHex(mypos, 8) + ': Total CDR''s : ' + IntToStr(total), MSG_NOTIFY); + e_WriteLog('EOCD @' + IntToHex(mypos, 8) + ': CD Length : ' + IntToStr(csize), MSG_NOTIFY); + e_WriteLog('EOCD @' + IntToHex(mypos, 8) + ': CD Offset : $' + IntToHex(cpos, 8), MSG_NOTIFY); + e_WriteLog('EOCD @' + IntToHex(mypos, 8) + ': Comment Length : ' + IntToStr(comlen), MSG_NOTIFY); + end; if (idisk <> $ffff) and (ndisk <> $ffff) and (nrec <> $ffff) and (total <> $ffff) and (csize <> $ffffffff) and (cpos <> $ffffffff) then begin - // Old Style ZIP if s.Position + comlen = s.Size then begin - // Valid End of Central Directory size (located exactly at the end of file) if (idisk = 0) and (ndisk = 0) and (nrec = total) then begin - // Single volume ZIP - if (UInt64(cpos) + csize <= s.Size) then + if (nrec * 46 <= csize) and (UInt64(cpos) + csize <= s.Size) then begin - // Valid Cental Directry Record position and size - Result := True; if total > 0 then begin - // At least one Central Directry present i := 0; s.Seek(cpos, TSeekOrigin.soBeginning); - while (i < nrec) and (Result = True) do + while i < nrec do begin - Result := ReadCDR(s); + if gWADEditorLogLevel >= DFWAD_LOG_DEBUG then + e_WriteLog('==============================================', MSG_NOTIFY); + ReadCDR(s, i); Inc(i); end; - // if Result = False then - // writeln('Invalid Central Directory #', i - 1); + if gWADEditorLogLevel >= DFWAD_LOG_DEBUG then + e_WriteLog('==============================================', MSG_NOTIFY); end; - end; - end; - end; + end + else + raise Exception.Create('Central Directory too big (corrupted file?)'); + end + else + raise Exception.Create('Splitted archives not supported'); + end + else + raise Exception.Create('EOCD too big (corrupted file?)'); end else - begin - // ZIP64 - FLastError := DFWAD_ERROR_WRONGVERSION; - end; - end; - end; + raise Exception.Create('ZIP64 not supported'); + end + else + raise Exception.Create('EOCD not found (corrupted file?)'); + end + else + raise Exception.Create('Not DFZIP formated file'); end; function TZIPEditor.ReadFile2(FileName: String): Boolean; @@ -791,27 +1035,35 @@ implementation FreeWAD(); Result := False; try - s := TFileStream.Create(FileName, fmOpenRead, fmShareDenyWrite); try - Result := ReadEOCD(s); - if Result = True then - begin + s := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); + try + ReadEOCD(s); FStream := s; FLastError := DFWAD_NOERROR; - end - else - begin - FStream := nil; + Result := True; + except s.Free(); + raise; end; except - s.Free(); + on e: Exception do + begin + if gWADEditorLogLevel >= DFWAD_LOG_INFO then + e_WriteLog('ZIP: Failed to read ZIP from file ' + FileName + ', reason: ' + e.Message, MSG_WARNING); + FreeWAD(); + end; + end; + except + on e: EFOpenError do + begin + if gWADEditorLogLevel >= DFWAD_LOG_INFO then + e_WriteLog('DFZIP: Failed to open file ' + FileName + ', reason: ' + e.Message, MSG_WARNING); + if FileExists(FileName) then + FLastError := DFWAD_ERROR_CANTOPENWAD + else + FLastError := DFWAD_ERROR_WADNOTFOUND; end; - except on e: EFOpenError do - if FileExists(FileName) then - FLastError := DFWAD_ERROR_CANTOPENWAD - else - FLastError := DFWAD_ERROR_WADNOTFOUND; end; end; @@ -820,25 +1072,27 @@ implementation begin FreeWAD(); Result := False; - s := TMemoryStream.Create; try - s.SetSize(Len); - s.WriteBuffer(PByte(Data)[0], Len); - s.Seek(0, soBeginning); - Result := ReadEOCD(s); - if Result = True then - begin + s := TMemoryStream.Create; + try + s.SetSize(Len); + s.WriteBuffer(PByte(Data)[0], Len); + s.Seek(0, soBeginning); + ReadEOCD(s); FStream := s; FLastError := DFWAD_NOERROR; - end - else - begin - FStream := nil; + Result := True; + except s.Free(); + raise; end; except - s.Free(); - raise; + on e: Exception do + begin + if gWADEditorLogLevel >= DFWAD_LOG_INFO then + e_WriteLog('DFZIP: Failed to read ZIP from memory, reason: ' + e.Message, MSG_WARNING); + FreeWAD(); + end; end; end; @@ -859,49 +1113,133 @@ implementation end; end; - procedure TZIPEditor.WriteLFH(s: TStream; comp, crc, csize, usize: UInt32; const afname: AnsiString); - var fname: PChar; flen: UInt16; + 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, mtime, crc, csize, usize: UInt32; const afname: AnsiString); + 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 : ' + DosToStr(mtime), 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(mtime)); // 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, mtime, 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 : ' + DosToStr(mtime), 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(mtime)); // 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); @@ -909,6 +1247,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; @@ -923,7 +1262,7 @@ implementation begin p := @FSection[i].list[j]; afname := GetFileName(FSection[i].name, p.name); - WriteLFH(s, p.comp, p.chksum, p.csize, p.usize, afname); + WriteLFH(s, p.comp, p.mtime, p.chksum, p.csize, p.usize, afname); if p.stream <> nil then begin Assert(p.stream.Size = p.csize); @@ -936,20 +1275,20 @@ implementation end else begin - raise Exception.Create('ZIP: SaveToStream: No data source available'); + raise Exception.Create('No data source available (somethig very wrong)'); end; end; end else begin afname := GetFileName(FSection[i].name, ''); - WriteLFH(s, ZIP_COMP_STORE, zcrc, 0, 0, afname); + WriteLFH(s, ZIP_COMP_STORE, FSection[i].mtime, zcrc, 0, 0, afname); end; end; end; // Write CDR headers count := 0; - loffset := start; + loffset := 0; offset := s.Position - start; if FSection <> nil then begin @@ -961,7 +1300,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.mtime, p.chksum, p.csize, p.usize, $00, loffset, afname, i); loffset := loffset + 30 + Length(afname) + p.csize; Inc(count); end; @@ -969,7 +1308,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, FSection[i].mtime, zcrc, 0, 0, $10, loffset, afname, i); loffset := loffset + 30 + Length(afname) + 0; Inc(count); end; @@ -979,24 +1318,46 @@ 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); var s: TFileStream; begin - s := TFileStream.Create(FileName, fmCreate); try - SaveToStream(s); - finally - s.Free(); + s := TFileStream.Create(FileName, fmCreate); + try + SaveToStream(s); + finally + s.Free(); + end; + except + on e: Exception do + begin + if gWADEditorLogLevel >= DFWAD_LOG_INFO then + e_WriteLog('ZIP: Failed to create file ' + FileName + ', reason: ' + e.Message, MSG_WARNING); + raise e; + end; end; end; @@ -1016,7 +1377,7 @@ implementation DFWAD_ERROR_WADNOTLOADED: Result := 'DFZIP file is not loaded'; DFWAD_ERROR_READRESOURCE: Result := 'Read resource error'; DFWAD_ERROR_READWAD: Result := 'Read DFZIP error'; - otherwise Result := ''; + otherwise Result := IntToStr(FLastError); end; end;