Subversion Repositories autosfx

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 daniel-mar 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