Subversion Repositories autosfx

Rev

Blame | Last modification | View Log | RSS feed

  1. unit ZMEOC19;
  2.  
  3. (*
  4.   ZMEOC19.pas - EOC handling
  5.     Copyright (C) 2009, 2010  by Russell J. Peters, Roger Aelbrecht,
  6.       Eric W. Engler and Chris Vleghert.
  7.  
  8.         This file is part of TZipMaster Version 1.9.
  9.  
  10.     TZipMaster is free software: you can redistribute it and/or modify
  11.     it under the terms of the GNU Lesser General Public License as published by
  12.     the Free Software Foundation, either version 3 of the License, or
  13.     (at your option) any later version.
  14.  
  15.     TZipMaster is distributed in the hope that it will be useful,
  16.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.     GNU Lesser General Public License for more details.
  19.  
  20.     You should have received a copy of the GNU Lesser General Public License
  21.     along with TZipMaster.  If not, see <http://www.gnu.org/licenses/>.
  22.  
  23.     contact: problems@delphizip.org (include ZipMaster in the subject).
  24.     updates: http://www.delphizip.org
  25.     DelphiZip maillist subscribe at http://www.freelists.org/list/delphizip
  26.  
  27.   modified 2010-05-12
  28. ---------------------------------------------------------------------------*)
  29.  
  30. interface
  31.  
  32. uses
  33.   Classes, ZipMstr19, ZMStructs19, ZMWorkFile19, ZMCompat19, ZMCore19;
  34.  
  35. type
  36.   TZMEOC = class(TZMWorkFile)
  37.   private
  38.     fCentralDiskNo: Integer;
  39.     fCentralEntries: Cardinal;
  40.     fCentralOffset: Int64;
  41.     fCentralSize: Int64;
  42.     fEOCOffset: Int64;
  43.     fMultiDisk:     Boolean;
  44.     fOffsetDelta: Int64;
  45.     fTotalEntries:  Cardinal;
  46.     fVersionMadeBy: Word;
  47.     fVersionNeeded: Word;
  48.     fZ64:           Boolean;
  49.     fZ64VSize: Int64;
  50.     FZipComment: AnsiString;
  51.     function GetEOC64(Ret: Integer): Integer;
  52.     function GetZipCommentLen: Integer;
  53.     procedure SetZipComment(const Value: AnsiString);
  54.     procedure SetZipCommentLen(const Value: Integer);
  55.   protected
  56.     function OpenEOC1: Integer;
  57.   public
  58.     constructor Create(Mstr: TZMCore); override;
  59.     procedure AfterConstruction; override;
  60.     procedure AssignFrom(Src: TZMWorkFile); override;
  61.     function OpenEOC(EOConly: boolean): Integer;
  62.     function OpenLast(EOConly: boolean; OpenRes: Integer): integer;
  63.     function WriteEOC: Integer;
  64.     property CentralDiskNo: Integer read fCentralDiskNo write fCentralDiskNo;
  65.     property CentralEntries: Cardinal read fCentralEntries write fCentralEntries;
  66.     property CentralOffset: Int64 read fCentralOffset write fCentralOffset;
  67.     property CentralSize: Int64 read fCentralSize write fCentralSize;
  68.     property EOCOffset: Int64 read fEOCOffset write fEOCOffset;
  69.     property MultiDisk: Boolean read fMultiDisk write fMultiDisk;
  70.     property OffsetDelta: Int64 read fOffsetDelta write fOffsetDelta;
  71.     property TotalEntries: Cardinal read fTotalEntries write fTotalEntries;
  72.     property VersionMadeBy: Word read fVersionMadeBy write fVersionMadeBy;
  73.     property VersionNeeded: Word read fVersionNeeded write fVersionNeeded;
  74.     property Z64: Boolean read fZ64 write fZ64;
  75.     property Z64VSize: Int64 read fZ64VSize write fZ64VSize;
  76.     property ZipComment: AnsiString read FZipComment write SetZipComment;
  77.     property ZipCommentLen: Integer read GetZipCommentLen write SetZipCommentLen;
  78.   end;                     { TZMEOC }
  79.  
  80. const
  81.   zfi_EOC: cardinal = $800;         // valid EOC found
  82.  
  83. const
  84.   EOCBadStruct = 2;
  85.   EOCBadComment = 1;
  86.   EOCWant64 = 64;
  87.  
  88.  
  89. implementation
  90.  
  91. uses Windows, SysUtils, ZMXcpt19, ZMMsg19, ZMUtils19;
  92.  
  93. function NameOfPart(const fn: String; Compat: Boolean): String;
  94. var
  95.   r, n: Integer;
  96.   SRec: TSearchRec;
  97.   fs: String;
  98. begin
  99.   Result := '';
  100.   if Compat then
  101.     fs := fn + '.z??*'
  102.   else
  103.     fs := fn + '???.zip';
  104.   r := FindFirst(fs, faAnyFile, SRec);
  105.   while r = 0 do
  106.   begin
  107.     if Compat then
  108.     begin
  109.       fs := UpperCase(Copy(ExtractFileExt(SRec.Name), 3, 20));
  110.       if fs = 'IP' then
  111.         n := 99999
  112.       else
  113.         n := StrToIntDef(fs, 0);
  114.     end
  115.     else
  116.       n := StrToIntDef(Copy(SRec.Name, Length(SRec.Name) - 6, 3), 0);
  117.     if n > 0 then
  118.     begin
  119.       Result := SRec.Name; // possible name
  120.       break;
  121.     end;
  122.     r := FindNext(SRec);
  123.   end;
  124.   SysUtils.FindClose(SRec);
  125. end;
  126.  
  127. {TZMEOC}
  128. constructor TZMEOC.Create(Mstr: TZMCore);
  129. begin
  130.   inherited Create(Mstr);
  131. end;
  132.  
  133. procedure TZMEOC.AfterConstruction;
  134. begin
  135.   inherited;
  136.   fMultiDisk := false;
  137.   fEOCOffset := 0;
  138.   fZipComment := '';
  139. end;
  140.  
  141. procedure TZMEOC.AssignFrom(Src: TZMWorkFile);
  142. var
  143.   theSrc: TZMEOC;
  144. begin
  145.   inherited;
  146.   if (Src is TZMEOC) and (Src <> Self) then
  147.   begin
  148.     theSrc := TZMEOC(Src);
  149.     fCentralDiskNo := theSrc.fCentralDiskNo;
  150.     fCentralEntries := theSrc.fCentralEntries;
  151.     fCentralOffset := theSrc.fCentralOffset;
  152.     fCentralSize := theSrc.fCentralSize;
  153.     fEOCOffset := theSrc.fEOCOffset;
  154.     fMultiDisk := theSrc.fMultiDisk;
  155.     fOffsetDelta := theSrc.fOffsetDelta;
  156.     fTotalEntries := theSrc.fTotalEntries;
  157.     fVersionMadeBy := theSrc.fVersionMadeBy;
  158.     fVersionNeeded := theSrc.fVersionNeeded;
  159.     fZ64 := theSrc.fZ64;
  160.     fZ64VSize := theSrc.fZ64VSize;
  161.     FZipComment := theSrc.FZipComment;
  162.   end;
  163. end;
  164.  
  165. function TZMEOC.GetEOC64(Ret: Integer): Integer;
  166. var
  167.   posn: Int64;
  168.   Loc: TZip64EOCLocator;
  169.   eoc64: TZipEOC64;
  170.   CEnd: Int64;  // end of central directory
  171.   function IsLocator(Locp: PZip64EOCLocator): boolean;
  172.   begin
  173.     Result := false;
  174.     if (Locp^.LocSig <> EOC64LocatorSig) then
  175.       exit;
  176.     if (DiskNr = MAX_WORD) and (Locp^.NumberDisks < MAX_WORD) then
  177.       exit;
  178.     Result := true;
  179.   end;
  180. begin
  181.   Result := Ret;
  182.   if (Result <= 0) or ((Result and EOCWant64) = 0) then
  183.     exit;
  184.   CEnd := EOCOffset;
  185.   posn := EOCOffset - sizeof(TZip64EOCLocator);
  186.   if posn >= 0 then
  187.   begin
  188.     if Seek(posn, 0) < 0 then
  189.     begin
  190.       result := -DS_FailedSeek;
  191.       exit;
  192.     end;
  193.     if Read(Loc, sizeof(TZip64EOCLocator)) <> sizeof(TZip64EOCLocator) then
  194.     begin
  195.       Result := -DS_EOCBadRead;
  196.       exit;
  197.     end;
  198.     if (IsLocator(@Loc)) then
  199.     begin
  200.       // locator found
  201.       fZ64 := true;  // in theory anyway - if it has locator it must be Z64
  202.       TotalDisks := Loc.NumberDisks;
  203.       DiskNr := Loc.NumberDisks - 1; // is last disk
  204.       if Integer(Loc.EOC64DiskStt) <> DiskNr then
  205.       begin
  206.         Result := -DS_EOCBadRead;
  207.         exit;
  208.         { TODO 1 : handle EOC64 not same disk as locator }
  209. //        SeekDisk(Loc.EOC64DiskStt);
  210.         // TODO set up for new disk
  211.         // test for cancel ?
  212.       end;
  213.       if Seek(Loc.EOC64RelOfs, 0) < 0 then
  214.       begin
  215.         Result := -DS_FailedSeek;
  216.         exit;
  217.       end;
  218.       if Read(eoc64, sizeof(TZipEOC64)) <> sizeof(TZipEOC64) then
  219.       begin
  220.         Result := -DS_EOCBadRead;
  221.         exit;
  222.       end;
  223.       if (eoc64.EOC64Sig = EndCentral64Sig) then
  224.       begin
  225.         // read EOC64
  226.         fVersionNeeded := eoc64.VersionNeed;
  227.         if ((VersionNeeded and VerMask) > ZIP64_VER) or
  228.               (eoc64.vsize < (sizeof(TZipEOC64) - 12)) then
  229.         begin
  230.           Result := -DS_Unsupported;
  231.           exit;
  232.         end;
  233.         CEnd := Loc.EOC64RelOfs;
  234.         fVersionMadeBy := eoc64.VersionMade;
  235.         fZ64VSize := eoc64.vsize + 12;
  236.         if CentralDiskNo = MAX_WORD then
  237.         begin
  238.           CentralDiskNo := eoc64.CentralDiskNo;
  239.           fZ64 := true;
  240.         end;
  241.         if TotalEntries = MAX_WORD then
  242.         begin
  243.           TotalEntries := Cardinal(eoc64.TotalEntries);
  244.           fZ64 := true;
  245.         end;
  246.         if CentralEntries = MAX_WORD then
  247.         begin
  248.           CentralEntries := Cardinal(eoc64.CentralEntries);
  249.           fZ64 := true;
  250.         end;
  251.         if CentralSize = MAX_UNSIGNED then
  252.         begin
  253.           CentralSize := eoc64.CentralSize;
  254.           fZ64 := true;
  255.         end;
  256.         if CentralOffset = MAX_UNSIGNED then
  257.         begin
  258.           CentralOffset := eoc64.CentralOffset;
  259.           fZ64 := true;
  260.         end;
  261.       end;
  262.     end;
  263.     // check structure
  264.     OffsetDelta := CEnd - CentralSize - CentralOffset;
  265.     if OffsetDelta <> 0 then
  266.       Result := Result or EOCBadStruct;
  267.   end;
  268. end;
  269.  
  270. function TZMEOC.GetZipCommentLen: Integer;
  271. begin
  272.   Result := Length(ZipComment);
  273. end;
  274.  
  275. (*? TZMEOC.OpenEOC
  276. // Function to find the EOC record at the end of the archive (on the last disk.)
  277. // We can get a return value or an exception if not found.
  278.  1.73 28 June 2003 RP change handling split files
  279.  return
  280.     <0 - -reason for not finding
  281.     >=0 - found
  282.     Warning values (ored)
  283.      1 - bad comment
  284.      2 - bad structure (Central offset wrong)
  285. *)
  286. function TZMEOC.OpenEOC(EOConly: boolean): Integer;
  287. begin
  288.   try
  289.     Result := OpenEOC1;
  290.     if (Result >= EOCWant64) and not EOConly then
  291.       Result := GetEOC64(Result);
  292.     if Result > 0 then
  293.       Result := Result and (EOCBadComment or EOCBadStruct);
  294.   except
  295.     on E: EZipMaster do
  296.     begin
  297.       File_Close;
  298.       Result := -E.ResId;
  299.     end;
  300.     else
  301.     begin
  302.       File_Close;
  303.       raise;
  304.     end;
  305.   end;
  306. end;
  307.  
  308. function TZMEOC.OpenEOC1: Integer;
  309. var
  310.   fEOC: TZipEndOfCentral;
  311.   pEOC: PZipEndOfCentral;
  312.   Size, i, j: Integer;
  313.   Sg: Cardinal;
  314.   ZipBuf: array of AnsiChar;//Byte;
  315.   AfterEOC, clen: integer;
  316. begin
  317.   fZipComment := '';
  318.   MultiDisk := false;
  319.   DiskNr := 0;
  320.   TotalDisks := 0;
  321.   TotalEntries := 0;
  322.   CentralEntries := 0;
  323.   CentralDiskNo := 0;
  324.   CentralOffset := 0;
  325.   CentralSize := 0;
  326.   fEOCOffset := 0;
  327.   fVersionMadeBy := 0;
  328.   fVersionNeeded := 0;
  329.   OffsetDelta := 0;
  330.   pEOC := nil;
  331.  
  332.   Result := 0;
  333.  
  334.   // Open the input archive, presumably the last disk.
  335.   if not IsOpen then
  336.     File_Open(fmOpenRead + fmShareDenyWrite);
  337.   if not IsOpen then
  338.   begin
  339.     if FileExists(FileName) then
  340.       Result := -DS_FileOpen
  341.     else
  342.       Result := -DS_NoInFile;
  343.     Exit;
  344.   end;
  345.  
  346.   // First a check for the first disk of a spanned archive,
  347.   // could also be the last so we don't issue a warning yet.
  348.   Sig := zfsNone;
  349.   try
  350. //  CheckRead(Sg, 4, DS_NoValidZip);
  351.     if Read(Sg, 4) <> 4 then
  352.     begin
  353.       Result := -DS_NoValidZip;
  354.       exit;
  355.     end;
  356.     if (Sg and $FFFF) = IMAGE_DOS_SIGNATURE then
  357.       Sig := zfsDOS
  358.     else
  359.     if (Sg = LocalFileHeaderSig) then
  360.       Sig := zfsLocal
  361.     else
  362.     if (Sg = ExtLocalSig) and (Read(Sg, 4) = 4) and (Sg = LocalFileHeaderSig) then
  363.     begin
  364.       Sig := zfsMulti;
  365.       MultiDisk := True; // will never be true on 'valid' multi-part zip with eoc
  366.     end;
  367.  
  368.     // Next we do a check at the end of the file to speed things up if
  369.     // there isn't a Zip archive ZipComment.
  370.     File_Size := Seek(-SizeOf(TZipEndOfCentral), soFromEnd);
  371.     if File_Size < 0 then
  372.       Result := -DS_NoValidZip
  373.     else
  374.     begin
  375.       File_Size := File_Size + SizeOf(TZipEndOfCentral);
  376.       // Save the archive size as a side effect.
  377.       RealFileSize := File_Size;
  378.       // There could follow a correction on FFileSize.
  379.       if Read(fEOC, SizeOf(TZipEndOfCentral)) <> sizeof(TZipEndOfCentral) then
  380.         Result := -DS_EOCBadRead
  381.       else
  382.       if (fEOC.HeaderSig = EndCentralDirSig) then
  383.       begin
  384.         fEOCOffset := File_Size - SizeOf(TZipEndOfCentral);
  385.         Result := 8;    // something found
  386.         if fEOC.ZipCommentLen <> 0 then
  387.         begin
  388.           fEOC.ZipCommentLen := 0;    // ??? make safe
  389.           Result := EOCBadComment;//1;        // return bad comment
  390.         end;
  391.         pEOC := @fEOC;
  392.       end;
  393.     end;
  394.  
  395.     if Result = 0 then  // did not find it - must have ZipComment
  396.     begin
  397.       Size := 65535 + SizeOf(TZipEndOfCentral);
  398.       if File_Size < Size then
  399.         Size := Integer(File_Size);
  400.       SetLength(ZipBuf, Size);
  401.       if Seek(-Size, soFromEnd) < 0 then
  402.         Result := -DS_FailedSeek
  403.       else
  404.         if Read(PByte(ZipBuf)^, Size) <> Size then
  405.           Result := -DS_EOCBadRead;
  406.       //  end;
  407.       if Result = 0 then
  408.       begin
  409.         for i := Size - SizeOf(TZipEndOfCentral) - 1 downto 0 do
  410.           if PZipEndOfCentral(PAnsiChar(ZipBuf) + i)^.HeaderSig = EndCentralDirSig then
  411.           begin
  412.             fEOCOffset := File_Size - (Size - i);
  413.             pEOC := @ZipBuf[i];
  414.             Result := 8;  // something found
  415.             // If we have ZipComment: Save it
  416.             AfterEOC := Size - (i + SizeOf(TZipEndOfCentral));
  417.             clen := pEOC^.ZipCommentLen;
  418.             if AfterEOC < clen then
  419.               clen := AfterEOC;
  420.             if clen > 0 then
  421.             begin
  422.               SetLength(fZipComment, clen);
  423.               for j := 1 to clen do
  424.                 fZipComment[j] := ZipBuf[i + Sizeof(TZipEndOfCentral) + j - 1];
  425.             end;
  426.             // Check if we really are at the end of the file, if not correct the File_Size
  427.             // and give a warning. (It should be an error but we are nice.)
  428.             if i + SizeOf(TZipEndOfCentral) + clen <> Size then
  429.             begin
  430.               File_Size := File_Size + ((i + SizeOf(TZipEndOfCentral) + clen) - Size);
  431. //              // Now we need a check for WinZip Self Extractor which makes SFX files which
  432. //              // almost always have garbage at the end (Zero filled at 512 byte boundary!)
  433. //              // In this special case 'we' don't give a warning.
  434. //              Unfortunately later versions use a different boundary so the test is invalid
  435.               if i + SizeOf(TZipEndOfCentral) + clen > Size then // HOTFIX-MARX-B
  436.               begin
  437.                 Result := EOCBadComment;  // Comment was cut
  438.               end;
  439.             end;
  440.             break;
  441.           end;  // for
  442.       end;
  443.     end;
  444.     if Result > 0 then
  445.     begin
  446.       MultiDisk := pEOC^.ThisDiskNo > 0; // may not have had proper sig
  447.       DiskNr := pEOC^.ThisDiskNo;
  448.       TotalDisks := pEOC^.ThisDiskNo + 1; //check
  449.       TotalEntries := pEOC^.TotalEntries;
  450.       CentralEntries := pEOC^.CentralEntries;
  451.       CentralDiskNo := pEOC^.CentralDiskNo;
  452.       CentralOffset := pEOC^.CentralOffset;
  453.       CentralSize := pEOC^.CentralSize;
  454.       if (pEOC^.TotalEntries = MAX_WORD) or (pEOC^.CentralOffset = MAX_UNSIGNED)
  455.       or (pEOC^.CentralEntries = MAX_WORD) or (pEOC^.CentralSize = MAX_UNSIGNED)
  456.       or (pEOC^.ThisDiskNo = MAX_WORD) or (pEOC^.CentralDiskNo = MAX_WORD) then
  457.       begin
  458.         Result := Result or EOCWant64;
  459.       end;
  460.     end;
  461.     if Result = 0 then
  462.       Result := -DS_NoValidZip;
  463.     if Result > 0 then
  464.     begin
  465.       Result := Result and (EOCBadComment or EOCBadStruct or EOCWant64); // remove 'found' flag
  466.     end;
  467.   finally
  468.     ZipBuf := nil;
  469.     if Result < 0 then
  470.       File_Close;
  471.   end;
  472. end;
  473.  
  474. // GetLastVolume
  475. function TZMEOC.OpenLast(EOConly: boolean; OpenRes: Integer): integer;
  476. var
  477.   ext: String;
  478.   Finding: Boolean;
  479.   FMVolume: Boolean;
  480.   Fname: String;
  481.   OrigName: String;
  482.   PartNbr: Integer;
  483.   Path: String;
  484.   s: String;
  485.   sName: String;
  486.   Stamp: Integer;
  487.   StampTmp: Integer;
  488.   tmpNumbering: TZipNumberScheme;
  489.   WasNoFile: Boolean;
  490. begin
  491.   WasNoFile := OpenRes = -DS_NoInFile;
  492.   PartNbr := -1;
  493.   Result := -DS_FileOpen; // default failure
  494.   FMVolume := False;
  495.   OrigName := FileName; // save it
  496.   WorkDrive.DriveStr := FileName;
  497.   Path := ExtractFilePath(FileName);
  498.   Numbering := znsNone; // unknown as yet
  499.   tmpNumbering := znsNone;
  500.   try
  501.     WorkDrive.HasMedia(False); // check valid drive
  502.     if WasNoFile then
  503.     begin
  504.       ext := UpperCase(ExtractFileExt(FileName));
  505.       // get the 'base' name for numbered names
  506.       Fname := Copy(FileName, 1, Length(FileName) - Length(ext));
  507.       // remove extension
  508.       FMVolume := True; // file did not exist maybe it is a multi volume
  509.       // if no file exists on harddisk then only Multi volume parts are possible
  510.       if WorkDrive.DriveIsFixed then
  511.       begin
  512.         // filename is of type ArchiveXXX.zip
  513.         // MV files are series with consecutive partnbrs in filename,
  514.         // highest number has EOC
  515.         if ext = EXT_ZIP then
  516.         begin
  517.           Finding := True;
  518.           Stamp := -1;
  519.           while Finding and (PartNbr < 1000) do
  520.           begin
  521.             if Worker.KeepAlive then
  522.               exit; // cancelled
  523.             // add part number and extension to base name
  524.             s := Fname + Copy(IntToStr(1002 + PartNbr), 2, 3) + EXT_ZIPL;
  525.             StampTmp := Integer(File_Age(s));
  526.             if (StampTmp = -1) or ((Stamp <> -1) and (StampTmp <> Stamp)) then
  527.             begin
  528.               // not found or stamp does not match
  529.               Result := -DS_NoInFile;
  530.               exit;
  531.             end;
  532.             if (PartNbr = -1) and not (spAnyTime in {Worker.}SpanOptions) then
  533.               Stamp := StampTmp;
  534.             Inc(PartNbr);
  535.             FileName := s;
  536.             Result := OpenEOC(EOConly);
  537.             if Result >= 0 then
  538.             begin // found possible last part
  539.               Finding := False;
  540.               if (TotalDisks - 1) <> PartNbr then
  541.               begin
  542.                 // was not last disk
  543.                 File_Close; // should happen in 'finally'
  544.                 Result := -DS_FileOpen;
  545.                 exit;
  546.               end;
  547.               Numbering := znsName;
  548.             end;
  549.           end; // while
  550.         end; // if Ext = '.zip'
  551.         if not IsOpen then
  552.         begin
  553.           Result := -DS_NoInFile;
  554.           exit; // not found
  555.         end;
  556.         // should be the same as s
  557.         FileName := Fname + Copy(IntToStr(1001 + PartNbr), 2, 3) + EXT_ZIPL;
  558.         // check if filename.z01 exists then it is part of MV with compat names
  559.         // and cannot be used
  560.         if (FileExists(ChangeFileExt(FileName, '.z01'))) then
  561.         begin
  562.           // ambiguous - cannot be used
  563.           File_Close; // should happen in 'finally'
  564.           exit; // will return DS_FileOpen
  565.         end;
  566.       end // if WorkDrive.Fixed
  567.       else
  568.       begin
  569.         // do we have an MV archive copied to a removable disk
  570.         // accept any MV filename on disk - then we ask for last part
  571.         sName := NameOfPart(Fname, False);
  572.         if sName = '' then
  573.           sName := NameOfPart(Fname, True);
  574.         if sName = '' then // none
  575.         begin
  576.           Result := -DS_NoInFile;   // no file with likely name
  577.           exit;
  578.         end;
  579.         FileName := Path + sName;
  580.       end;
  581.     end; // if not exists
  582.     // zip file exists or we got an acceptable part in multivolume or split
  583.     // archive
  584.     // use class variable for other functions
  585.     while not IsOpen do    // only open if found last part on hd
  586.     begin
  587.       // does this part contains the central dir
  588.       Result := OpenEOC(EOConly); // don't load on success
  589.       if Result >= 0 then
  590.         break; // found a 'last' disk
  591.       // it is not the disk with central dir so ask for the last disk
  592.       NewDisk := True; // new last disk
  593.       DiskNr := -1; // read operation
  594.       CheckForDisk(False, False);
  595.       // does the request for new disk
  596.       if WorkDrive.DriveIsFixed then
  597.       begin
  598.         if not FMVolume then
  599.           Result := -DS_NoValidZip;
  600.         break;//exit; // file with EOC is not on fixed disk
  601.       end;
  602.       if FMVolume then
  603.       begin // we have removable disks with multi volume archives
  604.         // get the file name on this disk
  605.         tmpNumbering := znsName;   // only if part and last part inserted
  606.         sName := NameOfPart(Fname, False);
  607.         if sName = '' then
  608.         begin
  609.           sName := NameOfPart(Fname, True);
  610.           tmpNumbering := znsExt;  // only if last part inserted
  611.         end;
  612.         if sName = '' then // none
  613.         begin
  614.           Result := -DS_NoInFile;   // no file with likely name
  615. //          exit;
  616.           FMVolume := False;
  617.           break;
  618.         end;
  619.         FileName := Path + sName;
  620.       end;
  621.     end; // while
  622.     if FMVolume then
  623.     // got a multi volume part so we need more checks
  624.     begin // is this first file of a multi-part
  625.       if (Sig <> zfsMulti) and ((TotalDisks = 1) and (PartNbr >= 0)) then
  626.         Result := -DS_FileOpen   // check
  627.       else
  628.       // part and EOC equal?
  629.         if WorkDrive.DriveIsFixed and (TotalDisks <> (PartNbr + 1)) then
  630.       begin
  631.         File_Close; // should happen in 'finally'
  632.         Result := -DS_NoValidZip;
  633.       end;
  634.     end;
  635.   finally
  636.     if Result < 0 then
  637.     begin
  638.       File_Close; // close filehandle if OpenLast
  639.       FileName := ''; // don't use the file
  640.     end //;
  641.     else
  642.       if (Numbering <> znsVolume) and (tmpNumbering <> znsNone) then
  643.         Numbering := tmpNumbering;
  644.   end;
  645. end;
  646.  
  647.  
  648. procedure TZMEOC.SetZipComment(const Value: AnsiString);
  649. begin
  650.   fZipComment := Value;
  651. end;
  652.  
  653. procedure TZMEOC.SetZipCommentLen(const Value: Integer);
  654. var
  655.   c: AnsiString;
  656. begin
  657.   if (Value <> ZipCommentLen) and (Value < Length(ZipComment))then
  658.   begin
  659.     c := ZipComment;
  660.     SetLength(c, Value);
  661.     ZipComment := c;
  662.   end;
  663. end;
  664.  
  665. // returns >0 ok = bytes written, <0 -ErrNo
  666. function TZMEOC.WriteEOC: Integer;
  667. type
  668.   TEOCrecs = packed record
  669.     loc: TZip64EOCLocator;
  670.     eoc: TZipEndOfCentral;
  671.   end;
  672.   pEOCrecs = ^TEOCrecs;
  673. var
  674.   t: Integer;
  675.   er: array of byte;
  676.   erz: Integer;
  677.   peoc: pEOCrecs;
  678.   eoc64: TZipEOC64;
  679.   Need64: Boolean;
  680.   clen: Integer;
  681. begin
  682.   Result := -DS_EOCBadWrite;  // keeps compiler happy
  683.   TotalDisks := DiskNr + 1; //check
  684.   Need64 := false;
  685.   ZeroMemory(@eoc64, sizeof(eoc64));
  686.   clen := Length(ZipComment);
  687.   ASSERT(clen = ZipCommentLen, ' ZipComment length error');
  688.   erz := sizeof(TEOCrecs) + clen;
  689.   SetLength(er, erz + 1);
  690.   peoc := @er[0];
  691.   peoc^.eoc.HeaderSig := EndCentralDirSig;
  692.   peoc^.loc.LocSig := EOC64LocatorSig;
  693.   if clen > 0 then
  694.     Move(ZipComment[1], er[sizeof(TEOCrecs)], clen);
  695.   peoc^.eoc.ZipCommentLen := clen;
  696.   // check Zip64 needed
  697.   if TotalDisks > MAX_WORD then
  698.   begin
  699.     peoc^.eoc.ThisDiskNo := Word(-1);
  700.     Need64 := true;
  701.   end
  702.   else
  703.     peoc^.eoc.ThisDiskNo := Word(TotalDisks -1); //check
  704.  
  705.   if CentralDiskNo >= MAX_WORD then
  706.   begin
  707.     peoc^.eoc.CentralDiskNo := Word(-1);
  708.     Need64 := true;
  709.   end
  710.   else
  711.     peoc^.eoc.CentralDiskNo := CentralDiskNo;
  712.  
  713.   if TotalEntries >= MAX_WORD then
  714.   begin
  715.     peoc^.eoc.TotalEntries := Word(-1);
  716.     Need64 := true;
  717.   end
  718.   else
  719.     peoc^.eoc.TotalEntries := Word(TotalEntries);
  720.  
  721.   if CentralEntries >= MAX_WORD then
  722.   begin
  723.     peoc^.eoc.CentralEntries := Word(-1);
  724.     Need64 := true;
  725.   end
  726.   else
  727.     peoc^.eoc.CentralEntries := Word(CentralEntries);
  728.  
  729.   if CentralSize >= MAX_UNSIGNED then
  730.   begin
  731.     peoc^.eoc.CentralSize := Cardinal(-1);
  732.     Need64 := true;
  733.   end
  734.   else
  735.     peoc^.eoc.CentralSize := CentralSize;
  736.  
  737.   if (CentralOffset >= MAX_UNSIGNED) then
  738.   begin
  739.     peoc^.eoc.CentralOffset := Cardinal(-1);
  740.     Need64 := true;
  741.   end
  742.   else
  743.     peoc^.eoc.CentralOffset := CentralOffset;
  744.  
  745.   if not Need64 then
  746.   begin
  747.     // write 'normal' EOC
  748.     erz := erz - sizeof(TZip64EOCLocator); // must not split
  749.     Result := Write(er[sizeof(TZip64EOCLocator)],-(erz or MustFitFlag));
  750.     if Result <> erz then
  751.     begin
  752.       if Result = MustFitError then
  753.       begin
  754.         if DiskNr >= MAX_WORD then
  755.           Need64 := true
  756.         else
  757.         begin
  758.           peoc^.eoc.ThisDiskNo := Word(DiskNr);
  759.           Result := Write(er[sizeof(TZip64EOCLocator)],-(erz or MustFitFlag));
  760.           if Result <> erz then
  761.             Result := -DS_EOCBadWrite;
  762.         end;
  763.       end
  764.       else
  765.         Result := -DS_EOCBadWrite;
  766.     end;
  767.   end;
  768.  
  769.   if Need64 then
  770.   begin
  771.     Z64 := true;
  772.     eoc64.EOC64Sig := EndCentral64Sig;
  773.     eoc64.vsize := sizeof(eoc64) - 12;
  774.     eoc64.VersionMade := ZIP64_VER;
  775.     eoc64.VersionNeed := ZIP64_VER;
  776.     eoc64.ThisDiskNo := DiskNr;
  777.     eoc64.CentralDiskNo := CentralDiskNo;
  778.     eoc64.TotalEntries := TotalEntries;
  779.     eoc64.CentralEntries := CentralEntries;
  780.     eoc64.CentralSize := CentralSize;
  781.     eoc64.CentralOffset := CentralOffset;
  782.     peoc^.loc.EOC64RelOfs := Position;
  783.     peoc^.loc.EOC64DiskStt := DiskNr;
  784.     Result := Write(eoc64, -sizeof(TZipEOC64));
  785.     if Result = sizeof(TZipEOC64) then
  786.     begin
  787.       peoc^.loc.NumberDisks := DiskNr + 1;   // may be new disk
  788.       if DiskNr >= MAX_WORD then
  789.         peoc^.eoc.ThisDiskNo := MAX_WORD
  790.       else
  791.         peoc^.eoc.ThisDiskNo := Word(DiskNr);// + 1);
  792.       Result := sizeof(TZipEndOfCentral) + peoc^.eoc.ZipCommentLen; // if it works
  793.       t := Write(er[0],-(erz or MustFitFlag));
  794.       if t <> erz then
  795.       begin
  796.         if t = MustFitError then
  797.         begin
  798.           peoc^.loc.NumberDisks := DiskNr + 1;
  799.           if DiskNr >= MAX_WORD then
  800.             peoc^.eoc.ThisDiskNo := MAX_WORD
  801.           else
  802.             peoc^.eoc.ThisDiskNo := Word(DiskNr);// + 1);
  803.           t := Write(er[0], -erz);
  804.           if t <> erz then
  805.             Result := -DS_EOCBadWrite;
  806.         end
  807.         else
  808.           Result := -DS_EOCBadWrite;
  809.       end;
  810.     end;
  811.   end;
  812. end;
  813.  
  814. end.
  815.  
  816.