Subversion Repositories autosfx

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 daniel-mar 1
unit ZMWorkFile19;
2
 
3
(*
4
  ZMWorkFile19.pas - basic in/out for zip files
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-06-20
28
  --------------------------------------------------------------------------- *)
29
(*
30
  if Len < 0 then must process on this segment
31
  ????Full - gives error if not processed non-split
32
  ????Check - gives error if not all done
33
  // Len = int64
34
  function Seek(offset: Int64; From: integer): Int64; virtual;
35
  procedure CopyTo(var dest: TZMWorkFile; Len: Int64; ErrId: Integer); virtual;
36
  // only operate on < 2G at a time
37
  procedure CopyToFull(var dest: TZMWorkFile; Len, ErrId: Integer); virtual;
38
  function Read(var Buffer; ReadLen: Integer): Integer; virtual;
39
  procedure ReadCheck(var Buffer; Len, ErrId: Integer); virtual;
40
  procedure ReadFull(var Buffer; ReadLen, DSErrIdent: Integer); virtual;
41
  function Write(const Buffer; Len: Integer): Integer; virtual;
42
  function WriteCheck(const Buffer; Len, ErrId: Integer): Integer; virtual;
43
  procedure WriteFull(const Buffer; Len, ErrIdent: Integer); virtual;
44
*)
45
interface
46
 
47
uses
48
  Classes, Windows, SysUtils, ZipMstr19, ZMDelZip19, ZMCore19, ZMDrv19;
49
 
50
// file signitures read by OpenEOC
51
type
52
  TZipFileSigs = (zfsNone, zfsLocal, zfsMulti, zfsDOS);
53
 
54
type
55
  TZipNumberScheme = (znsNone, znsVolume, znsName, znsExt);
56
 
57
type
58
  TZipWrites = (zwDefault, zwSingle, zwMultiple);
59
 
60
const
61
  ProgressActions: array [TZipShowProgress] of TActionCodes =
62
    (zacTick, zacProgress, zacXProgress);
63
  MustFitError = -10999;
64
  MustFitFlag = $20000; // much bigger than any 'fixed' field
65
  MustFitMask = $1FFFF; // removes flag limits 'fixed' length
66
 
67
type
68
//  TBytArray = array of Byte;
69
  TByteBuffer = array of Byte;
70
 
71
type
72
  TZMWorkFile = class(TObject)
73
  private
74
    fAllowedSize: Int64;
75
    FBoss: TZMCore;
76
    fBytesRead: Int64;
77
    fBytesWritten: Int64;
78
    fDiskNr: Integer;
79
    fFileName: String;
80
    fFile_Size: Int64;
81
    fHandle: Integer;
82
    fInfo: Cardinal;
83
//    fIsMultiDisk: Boolean;
84
    fIsOpen: Boolean;
85
    fIsTemp: Boolean;
86
    fLastWrite: TFileTime;
87
    fOpenMode: Cardinal;
88
    fRealFileName: String;
89
    fRealFileSize: Int64;
90
    FReqFileName: String;
91
    fShowProgress: TZipShowProgress;
92
    fSig: TZipFileSigs;
93
    fStampDate: Cardinal;
94
    fTotalDisks: Integer;
95
    fWorkDrive: TZMWorkDrive;
96
    fWorker: TZMCore;
97
    FZipDiskAction: TZMDiskAction;
98
    FZipDiskStatus: TZMZipDiskStatus;
99
    WBuf: array of Byte;
100
    function GetConfirmErase: Boolean;
101
    function GetExists: Boolean;
102
    function GetKeepFreeOnAllDisks: Cardinal;
103
    function GetKeepFreeOnDisk1: Cardinal;
104
    function GetLastWritten: Cardinal;
105
    function GetMaxVolumeSize: Int64;
106
    function GetMinFreeVolumeSize: Cardinal;
107
    function GetPosition_F: Int64;
108
    function GetSpanOptions: TZMSpanOpts;
109
    procedure SetBoss(const Value: TZMCore);
110
    procedure SetFileName(const Value: String);
111
    procedure SetHandle(const Value: Integer);
112
    procedure SetKeepFreeOnAllDisks(const Value: Cardinal);
113
    procedure SetKeepFreeOnDisk1(const Value: Cardinal);
114
    procedure SetMaxVolumeSize(const Value: Int64);
115
    procedure SetMinFreeVolumeSize(const Value: Cardinal);
116
    procedure SetPosition(const Value: Int64);
117
    procedure SetSpanOptions(const Value: TZMSpanOpts);
118
    procedure SetWorkDrive(const Value: TZMWorkDrive);
119
  protected
120
    fBufferPosition: Integer;
121
    fConfirmErase: Boolean;
122
    fDiskBuffer: TByteBuffer;
123
    FDiskWritten: Cardinal;
124
    fSavedFileInfo: _BY_HANDLE_FILE_INFORMATION;
125
    fIsMultiPart: Boolean;
126
    FNewDisk: Boolean;
127
    FNumbering: TZipNumberScheme;
128
    function ChangeNumberedName(const FName: String; NewNbr: Cardinal; Remove:
129
        boolean): string;
130
    procedure CheckForDisk(writing, UnformOk: Boolean);
131
    procedure ClearFloppy(const dir: String);
132
    function Copy_File(Source: TZMWorkFile): Integer;
133
    procedure Diag(const msg: String);
134
    function EOS: Boolean;
135
    procedure FlushDiskBuffer;
136
    function GetFileInformation(var FileInfo: _BY_HANDLE_FILE_INFORMATION): Boolean;
137
    function GetPosition: Int64;
138
    function HasSpanSig(const FName: String): boolean;
139
    function IsRightDisk: Boolean;
140
    procedure NewFlushDisk;
141
    function NewSegment: Boolean;
142
    function VolName(Part: Integer): String;
143
    function OldVolName(Part: Integer): String;
144
    function WriteSplit(const Buffer; ToWrite: Integer): Integer;
145
    function ZipFormat(const NewName: String): Integer;
146
    property AllowedSize: Int64 Read fAllowedSize Write fAllowedSize;
147
    property LastWrite: TFileTime read fLastWrite write fLastWrite;
148
    property OpenMode: Cardinal read fOpenMode;
149
  public
150
    constructor Create(wrkr: TZMCore); virtual;
151
    procedure AfterConstruction; override;
152
    function AskAnotherDisk(const DiskFile: String): Integer;
153
    function AskOverwriteSegment(const DiskFile: String; DiskSeq: Integer): Integer;
154
    procedure AssignFrom(Src: TZMWorkFile); virtual;
155
    procedure BeforeDestruction; override;
156
    function CheckRead(var Buffer; Len: Integer): Boolean; overload;
157
    procedure CheckRead(var Buffer; Len, ErrId: Integer); overload;
158
    function CheckReads(var Buffer; const Lens: array of Integer): Boolean;
159
      overload;
160
    procedure CheckReads(var Buffer; const Lens: array of Integer;
161
      ErrId: Integer); overload;
162
    function CheckSeek(offset: Int64; from, ErrId: Integer): Int64;
163
    function CheckWrite(const Buffer; Len: Integer): Boolean; overload;
164
    procedure CheckWrite(const Buffer; Len, ErrId: Integer); overload;
165
    function CheckWrites(const Buffer; const Lens: array of Integer): Boolean;
166
      overload;
167
    procedure CheckWrites(const Buffer; const Lens: array of Integer;
168
      ErrId: Integer); overload;
169
    procedure ClearFileInformation;
170
    function CopyFrom(Source: TZMWorkFile; Len: Int64): Int64;
171
    function CreateMVFileNameEx(const FileName: String;
172
      StripPartNbr, Compat: Boolean): String;
173
    function DoFileWrite(const Buffer; Len: Integer): Integer;
174
    function FileDate: Cardinal;
175
    procedure File_Close;
176
    procedure File_Close_F;
177
    function File_Create(const theName: String): Boolean;
178
    function File_CreateTemp(const Prefix, Where: String): Boolean;
179
    function File_Open(Mode: Cardinal): Boolean;
180
    function File_Rename(const NewName: string; const Safe: Boolean = false)
181
      : Boolean;
182
    function FinishWrite: Integer;
183
    procedure GetNewDisk(DiskSeq: Integer; AllowEmpty: Boolean);
184
    function LastWriteTime(var last_write: TFileTime): Boolean;
185
    function MapNumbering(Opts: TZMSpanOpts): TZMSpanOpts;
186
    procedure ProgReport(prog: TActionCodes; xprog: Integer; const Name: String;
187
        size: Int64);
188
    function Read(var Buffer; Len: Integer): Integer;
189
    function ReadFromFile(var Buffer; Len: Integer): Integer;
190
    function Reads(var Buffer; const Lens: array of Integer): Integer;
191
    function Reads_F(var Buffer; const Lens: array of Integer): Integer;
192
    function ReadTo(strm: TStream; Count: Integer): Integer;
193
    function Read_F(var Buffer; Len: Integer): Integer;
194
    function SaveFileInformation: Boolean;
195
    function Seek(offset: Int64; from: Integer): Int64;
196
    function SeekDisk(Nr: Integer): Integer;
197
    function SetEndOfFile: Boolean;
198
    function VerifyFileInformation: Boolean;
199
    function WBuffer(size: Integer): pByte;
200
    function Write(const Buffer; Len: Integer): Integer;
201
    function WriteFrom(strm: TStream; Count: Integer): Int64;
202
    function Writes(const Buffer; const Lens: array of Integer): Integer;
203
    function Writes_F(const Buffer; const Lens: array of Integer): Integer;
204
    function WriteToFile(const Buffer; Len: Integer): Integer;
205
    function Write_F(const Buffer; Len: Integer): Integer;
206
    property Boss: TZMCore read FBoss write SetBoss;
207
    property BytesRead: Int64 read fBytesRead write fBytesRead;
208
    property BytesWritten: Int64 read fBytesWritten write fBytesWritten;
209
    property ConfirmErase: Boolean read GetConfirmErase write fConfirmErase;
210
    property DiskNr: Integer read fDiskNr write fDiskNr;
211
    property Exists: Boolean read GetExists;
212
    property FileName: String read fFileName write SetFileName;
213
    property File_Size: Int64 read fFile_Size write fFile_Size;
214
    property Handle: Integer read fHandle write SetHandle;
215
    property info: Cardinal read fInfo write fInfo;
216
    property IsMultiPart: Boolean read fIsMultiPart write fIsMultiPart;
217
    property IsOpen: Boolean read fIsOpen;
218
    property IsTemp: Boolean read fIsTemp write fIsTemp;
219
    property KeepFreeOnAllDisks: Cardinal read GetKeepFreeOnAllDisks write
220
      SetKeepFreeOnAllDisks;
221
    property KeepFreeOnDisk1: Cardinal read GetKeepFreeOnDisk1 write
222
      SetKeepFreeOnDisk1;
223
    property LastWritten: Cardinal read GetLastWritten;
224
    property MaxVolumeSize: Int64 read GetMaxVolumeSize write SetMaxVolumeSize;
225
    property MinFreeVolumeSize: Cardinal read GetMinFreeVolumeSize write
226
      SetMinFreeVolumeSize;
227
    property NewDisk: Boolean Read FNewDisk Write FNewDisk;
228
    property Numbering: TZipNumberScheme Read FNumbering Write FNumbering;
229
    property Position: Int64 read GetPosition write SetPosition;
230
    property RealFileName: String read fRealFileName;
231
    property RealFileSize: Int64 read fRealFileSize write fRealFileSize;
232
    property ReqFileName: String Read FReqFileName Write FReqFileName;
233
    property ShowProgress
234
      : TZipShowProgress read fShowProgress write fShowProgress;
235
    property Sig: TZipFileSigs read fSig write fSig;
236
    property SpanOptions: TZMSpanOpts read GetSpanOptions write SetSpanOptions;
237
    // if non-zero set fileDate
238
    property StampDate: Cardinal read fStampDate write fStampDate;
239
    property TotalDisks: Integer read fTotalDisks write fTotalDisks;
240
    property WorkDrive: TZMWorkDrive read fWorkDrive write SetWorkDrive;
241
    property Worker: TZMCore read fWorker write fWorker;
242
  end;
243
 
244
const
245
//  zfi_None: Cardinal = 0;
246
//  zfi_Open: Cardinal = 1;
247
//  zfi_Create: Cardinal = 2;
248
  zfi_Dirty: Cardinal = 4;
249
  zfi_MakeMask: Cardinal = $07;
250
  zfi_Error: Cardinal = 8;
251
//  zfi_NotFound: cardinal = $10;     // named file not found
252
//  zfi_NoLast: cardinal = $20;       // last file not found
253
  zfi_Loading: cardinal = $40;
254
  zfi_Cancelled: cardinal = $80;    // loading was cancelled
255
//  zfi_FileMask: cardinal = $F0;
256
 
257
function FileTimeToLocalDOSTime(const ft: TFileTime): Cardinal;
258
 
259
implementation
260
 
261
uses
262
  Forms, Controls, Dialogs, ZMMsgStr19, ZMCtx19, ZMCompat19, ZMDlg19,
263
  ZMStructs19, ZMUtils19, ZMMsg19, ZMXcpt19;
264
{$I '.\ZipVers19.inc'}
265
{$IFDEF VER180}
266
 {$WARN SYMBOL_DEPRECATED OFF}
267
{$ENDIF}
268
 
269
const
270
  MAX_PARTS = 999;
271
  MaxDiskBufferSize = (4 * 1024 * 1024); // floppies only
272
 
273
const
274
  SZipSet = 'ZipSet_';
275
  SPKBACK = 'PKBACK#';
276
 
277
  (* ? FormatFloppy
278
    *)
279
function FormatFloppy(WND: HWND; const Drive: String): Integer;
280
const
281
  SHFMT_ID_DEFAULT = $FFFF;
282
  { options }
283
  SHFMT_OPT_FULL = $0001;
284
  // SHFMT_OPT_SYSONLY = $0002;
285
  { return values }
286
  // SHFMT_ERROR = $FFFFFFFF;
287
  // -1 Error on last format, drive may be formatable
288
  // SHFMT_CANCEL = $FFFFFFFE;    // -2 last format cancelled
289
  // SHFMT_NOFORMAT = $FFFFFFFD;    // -3 drive is not formatable
290
type
291
  TSHFormatDrive = function(WND: HWND; Drive, fmtID, Options: DWORD): DWORD;
292
    stdcall;
293
var
294
  SHFormatDrive: TSHFormatDrive;
295
var
296
  drv: Integer;
297
  hLib: THandle;
298
  OldErrMode: Integer;
299
begin
300
  Result := -3; // error
301
  if not((Length(Drive) > 1) and (Drive[2] = ':') and CharInSet
302
      (Drive[1], ['A' .. 'Z', 'a' .. 'z'])) then
303
    exit;
304
  if GetDriveType(PChar(Drive)) <> DRIVE_REMOVABLE then
305
    exit;
306
  drv := Ord(Upcase(Drive[1])) - Ord('A');
307
  OldErrMode := SetErrorMode(SEM_FAILCRITICALERRORS or SEM_NOGPFAULTERRORBOX);
308
  hLib := LoadLibrary('Shell32');
309
  if hLib <> 0 then
310
  begin
311
    @SHFormatDrive := GetProcAddress(hLib, 'SHFormatDrive');
312
    if @SHFormatDrive <> nil then
313
      try
314
        Result := SHFormatDrive(WND, drv, SHFMT_ID_DEFAULT, SHFMT_OPT_FULL);
315
      finally
316
        FreeLibrary(hLib);
317
      end;
318
    SetErrorMode(OldErrMode);
319
  end;
320
end;
321
 
322
function FileTimeToLocalDOSTime(const ft: TFileTime): Cardinal;
323
var
324
  lf: TFileTime;
325
  wd: Word;
326
  wt: Word;
327
begin
328
  Result := 0;
329
  if FileTimeToLocalFileTime(ft, lf) and FileTimeToDosDateTime(lf, wd, wt) then
330
    Result := (wd shl 16) or wt;
331
end;
332
 
333
{ TZMWorkFile }
334
 
335
constructor TZMWorkFile.Create(wrkr: TZMCore);
336
begin
337
  inherited Create;
338
  fWorker := wrkr;
339
  fBoss := wrkr;
340
end;
341
 
342
procedure TZMWorkFile.AfterConstruction;
343
begin
344
  inherited;
345
  fDiskBuffer := nil;
346
  fBufferPosition := -1;
347
  fInfo := 0;
348
  fHandle := -1;
349
  fIsMultiPart := false;
350
  fBytesWritten := 0;
351
  fBytesRead := 0;
352
  fOpenMode := 0;
353
  fNumbering := znsNone;
354
  fWorkDrive := TZMWorkDrive.Create;
355
  ClearFileInformation;
356
end;
357
 
358
function TZMWorkFile.AskAnotherDisk(const DiskFile: String): Integer;
359
var
360
  MsgQ: String;
361
  tmpStatusDisk: TZMStatusDiskEvent;
362
begin
363
  MsgQ := Boss.ZipLoadStr(DS_AnotherDisk);
364
  FZipDiskStatus := FZipDiskStatus + [zdsSameFileName];
365
  tmpStatusDisk := worker.Master.OnStatusDisk;
366
  if Assigned(tmpStatusDisk) and not(zaaYesOvrwrt in Worker.AnswerAll) then
367
  begin
368
    FZipDiskAction := zdaOk; // The default action
369
    tmpStatusDisk(Boss.Master, 0, DiskFile, FZipDiskStatus, FZipDiskAction);
370
    case FZipDiskAction of
371
      zdaCancel:
372
        Result := idCancel;
373
      zdaReject:
374
        Result := idNo;
375
      zdaErase:
376
        Result := idOk;
377
      zdaYesToAll:
378
        begin
379
          Result := idOk;
380
//          Worker.AnswerAll := Worker.AnswerAll + [zaaYesOvrwrt];
381
        end;
382
      zdaOk:
383
        Result := idOk;
384
    else
385
      Result := idOk;
386
    end;
387
  end
388
  else
389
    Result := Boss.ZipMessageDlgEx(Boss.ZipLoadStr(FM_Confirm), MsgQ,
390
      zmtWarning + DHC_SpanOvr, [mbOk, mbCancel]);
391
end;
392
 
393
function TZMWorkFile.AskOverwriteSegment(const DiskFile: String; DiskSeq:
394
    Integer): Integer;
395
var
396
  MsgQ: String;
397
  tmpStatusDisk: TZMStatusDiskEvent;
398
begin
399
  // Do we want to overwrite an existing file?
400
  if FileExists(DiskFile) then
401
    if (File_Age(DiskFile) = StampDate) and (Pred(DiskSeq) < DiskNr)
402
      then
403
    begin
404
      MsgQ := Boss.ZipFmtLoadStr(DS_AskPrevFile, [DiskSeq]);
405
      FZipDiskStatus := FZipDiskStatus + [zdsPreviousDisk];
406
    end
407
    else
408
    begin
409
      MsgQ := Boss.ZipFmtLoadStr(DS_AskDeleteFile, [DiskFile]);
410
      FZipDiskStatus := FZipDiskStatus + [zdsSameFileName];
411
    end
412
    else if not WorkDrive.DriveIsFixed then
413
      if (WorkDrive.VolumeSize <> WorkDrive.VolumeSpace) then
414
        FZipDiskStatus := FZipDiskStatus + [zdsHasFiles]
415
        // But not the same name
416
      else
417
        FZipDiskStatus := FZipDiskStatus + [zdsEmpty];
418
  tmpStatusDisk := worker.Master.OnStatusDisk;
419
  if Assigned(tmpStatusDisk) and not(zaaYesOvrwrt in Worker.AnswerAll) then
420
  begin
421
    FZipDiskAction := zdaOk; // The default action
422
    tmpStatusDisk(Boss.Master, DiskSeq, DiskFile, FZipDiskStatus,
423
      FZipDiskAction);
424
    case FZipDiskAction of
425
      zdaCancel:
426
        Result := idCancel;
427
      zdaReject:
428
        Result := idNo;
429
      zdaErase:
430
        Result := idOk;
431
      zdaYesToAll:
432
        begin
433
          Result := idOk;
434
          Worker.AnswerAll := Worker.AnswerAll + [zaaYesOvrwrt];
435
        end;
436
      zdaOk:
437
        Result := idOk;
438
    else
439
      Result := idOk;
440
    end;
441
  end
442
  else if ((FZipDiskStatus * [zdsPreviousDisk, zdsSameFileName]) <> []) and not
443
    ((zaaYesOvrwrt in Worker.AnswerAll) or Worker.Unattended) then
444
  begin
445
    Result := Boss.ZipMessageDlgEx(Boss.ZipLoadStr(FM_Confirm), MsgQ,
446
      zmtWarning + DHC_SpanOvr, [mbYes, mbNo, mbCancel, mbYesToAll]);
447
    if Result = mrYesToAll then
448
    begin
449
      Worker.AnswerAll := Worker.AnswerAll + [zaaYesOvrwrt];
450
      Result := idOk;
451
    end;
452
  end
453
  else
454
    Result := idOk;
455
end;
456
 
457
// Src should not be open but not enforced
458
procedure TZMWorkFile.AssignFrom(Src: TZMWorkFile);
459
begin
460
  if (Src <> Self) and (Src <> nil) then
461
  begin
462
    fDiskBuffer := nil;
463
    fBufferPosition := -1;
464
    Move(Src.fSavedFileInfo, fSavedFileInfo, SizeOf(fSavedFileInfo));
465
    fAllowedSize := Src.fAllowedSize;
466
    fBytesRead := Src.fBytesRead;
467
    fBytesWritten := Src.fBytesWritten;
468
    fDiskNr := Src.fDiskNr;
469
    fFile_Size := Src.fFile_Size;
470
    fFileName := Src.fFileName;
471
    fHandle := -1;  // don't acquire handle
472
    fInfo := Src.fInfo;
473
//    fIsMultiDisk := Src.fIsMultiDisk;
474
    fIsOpen := False;
475
    fIsTemp := Src.fIsTemp;
476
    fLastWrite := Src.fLastWrite;
477
    fNumbering := Src.fNumbering;
478
    fOpenMode := Src.fOpenMode;
479
    fRealFileName := Src.fRealFileName;
480
    fReqFileName := Src.FReqFileName;
481
    fShowProgress := Src.fShowProgress;
482
    fSig := Src.fSig;
483
    fStampDate := Src.fStampDate;
484
    fTotalDisks := Src.fTotalDisks;
485
    fWorkDrive.AssignFrom(Src.WorkDrive);
486
    FZipDiskAction := Src.FZipDiskAction;
487
    FZipDiskStatus := Src.FZipDiskStatus;
488
  end;
489
end;
490
 
491
procedure TZMWorkFile.BeforeDestruction;
492
begin
493
  File_Close;
494
  if IsTemp and FileExists(fRealFileName) then
495
  begin
496
    if Boss.Verbosity >= zvTrace then
497
      Diag('Trace: Deleting ' + fRealFileName);
498
    SysUtils.DeleteFile(fFileName);
499
  end;
500
  FreeAndNil(fWorkDrive);
501
  fDiskBuffer := nil; // ++ discard contents
502
  WBuf := nil;
503
  inherited;
504
end;
505
 
506
// uses 'real' number
507
function TZMWorkFile.ChangeNumberedName(const FName: String; NewNbr: Cardinal;
508
    Remove: boolean): string;
509
var
510
  ext: string;
511
  StripLen: Integer;
512
begin
513
  if DiskNr > 999 then
514
    raise EZipMaster.CreateResDisp(DS_TooManyParts, True);
515
  ext := ExtractFileExt(FName);
516
  StripLen := 0;
517
  if Remove then
518
    StripLen := 3;
519
  Result := Copy(FName, 1, Length(FName) - Length(ext) - StripLen)
520
    + Copy(IntToStr(1000 + NewNbr), 2, 3) + ext;
521
end;
522
 
523
procedure TZMWorkFile.CheckForDisk(writing, UnformOk: Boolean);
524
var
525
  OnGetNextDisktmp: TZMGetNextDiskEvent;
526
  AbortAction: Boolean;
527
  MsgFlag: Integer;
528
  MsgStr: String;
529
  Res: Integer;
530
  SizeOfDisk: Int64;
531
  totDisks: Integer;
532
begin
533
  if TotalDisks <> 1 then // check
534
    IsMultiPart := True;
535
  if WorkDrive.DriveIsFixed then
536
  begin
537
    // If it is a fixed disk we don't want a new one.
538
    NewDisk := false;
539
    Boss.CheckCancel;
540
    exit;
541
  end;
542
  Boss.KeepAlive;       // just ProcessMessages
543
  // First check if we want a new one or if there is a disk (still) present.
544
  while (NewDisk or (not WorkDrive.HasMedia(UnformOk))) do
545
  begin
546
    if Boss.Unattended then
547
      raise EZipMaster.CreateResDisp(DS_NoUnattSpan, True);
548
 
549
    MsgFlag := zmtWarning + DHC_SpanNxtW; // or error?
550
    if DiskNr < 0 then // want last disk
551
    begin
552
      MsgStr := Boss.ZipLoadStr(DS_InsertDisk);
553
      MsgFlag := zmtError + DHC_SpanNxtR;
554
    end
555
    else if writing then
556
    begin
557
      // This is an estimate, we can't know if every future disk has the same space available and
558
      // if there is no disk present we can't determine the size unless it's set by MaxVolumeSize.
559
      SizeOfDisk := WorkDrive.VolumeSize - KeepFreeOnAllDisks;
560
      if (MaxVolumeSize <> 0) and (MaxVolumeSize < WorkDrive.VolumeSize) then
561
        SizeOfDisk := MaxVolumeSize;
562
 
563
      TotalDisks := DiskNr + 1;
564
      if TotalDisks > MAX_PARTS then
565
        raise EZipMaster.CreateResDisp(DS_TooManyParts, True);
566
      if SizeOfDisk > 0 then
567
      begin
568
        totDisks := Trunc((File_Size + 4 + KeepFreeOnDisk1) / SizeOfDisk);
569
        if TotalDisks < totDisks then
570
          TotalDisks := totDisks;
571
        MsgStr := Boss.ZipFmtLoadStr
572
          (DS_InsertVolume, [DiskNr + 1, TotalDisks]);
573
      end
574
      else
575
        MsgStr := Boss.ZipFmtLoadStr(DS_InsertAVolume, [DiskNr + 1]);
576
    end
577
    else
578
    begin // reading - want specific disk
579
      if TotalDisks = 0 then
580
        MsgStr := Boss.ZipFmtLoadStr(DS_InsertAVolume, [DiskNr + 1])
581
      else
582
        MsgStr := Boss.ZipFmtLoadStr(DS_InsertVolume, [DiskNr + 1, TotalDisks]);
583
    end;
584
 
585
    MsgStr := MsgStr + Boss.ZipFmtLoadStr(DS_InDrive, [WorkDrive.DriveStr]);
586
    OnGetNextDisktmp := Worker.Master.OnGetNextDisk;
587
    if Assigned(OnGetNextDisktmp) then
588
    begin
589
      AbortAction := false;
590
      OnGetNextDisktmp(Boss.Master, DiskNr + 1, TotalDisks, Copy
591
          (WorkDrive.DriveStr, 1, 1), AbortAction);
592
      if AbortAction then
593
        Res := idAbort
594
      else
595
        Res := idOk;
596
    end
597
    else
598
      Res := Boss.ZipMessageDlgEx('', MsgStr, MsgFlag, mbOkCancel);
599
 
600
    // Check if user pressed Cancel or memory is running out.
601
    if Res = 0 then
602
      raise EZipMaster.CreateResDisp(DS_NoMem, True);
603
    if Res <> idOk then
604
    begin
605
      Boss.Cancel := GE_Abort;
606
      info := info or zfi_Cancelled;
607
      raise EZipMaster.CreateResDisp(DS_Canceled, false);
608
    end;
609
    NewDisk := false;
610
    Boss.KeepAlive;
611
  end;
612
end;
613
 
614
function TZMWorkFile.CheckRead(var Buffer; Len: Integer): Boolean;
615
begin
616
  if Len < 0 then
617
    Len := -Len;
618
  Result := Read(Buffer, Len) = Len;
619
end;
620
 
621
procedure TZMWorkFile.CheckRead(var Buffer; Len, ErrId: Integer);
622
begin
623
  if Len < 0 then
624
    Len := -Len;
625
  if not CheckRead(Buffer, Len) then
626
  begin
627
    if ErrId = 0 then
628
      ErrId := DS_ReadError;
629
    raise EZipMaster.CreateResDisp(ErrId, True);
630
  end;
631
end;
632
 
633
function TZMWorkFile.CheckReads(var Buffer; const Lens: array of Integer)
634
  : Boolean;
635
var
636
  c: Integer;
637
  i: Integer;
638
begin
639
  c := 0;
640
  for i := Low(Lens) to High(Lens) do
641
    c := c + Lens[i];
642
  Result := Reads(Buffer, Lens) = c;
643
end;
644
 
645
procedure TZMWorkFile.CheckReads(var Buffer; const Lens: array of Integer;
646
  ErrId: Integer);
647
begin
648
  if not CheckReads(Buffer, Lens) then
649
  begin
650
    if ErrId = 0 then
651
      ErrId := DS_ReadError;
652
    raise EZipMaster.CreateResDisp(ErrId, True);
653
  end;
654
end;
655
 
656
function TZMWorkFile.CheckSeek(offset: Int64; from, ErrId: Integer): Int64;
657
begin
658
  Result := Seek(offset, from);
659
  if Result < 0 then
660
  begin
661
    if ErrId = 0 then
662
      raise EZipMaster.CreateResDisp(DS_SeekError, True);
663
    if ErrId = -1 then
664
      ErrId := DS_FailedSeek;
665
    raise EZipMaster.CreateResDisp(ErrId, True);
666
  end;
667
end;
668
 
669
function TZMWorkFile.CheckWrite(const Buffer; Len: Integer): Boolean;
670
begin
671
  if Len < 0 then
672
    Len := -Len;
673
  Result := Write(Buffer, Len) = Len;
674
end;
675
 
676
procedure TZMWorkFile.CheckWrite(const Buffer; Len, ErrId: Integer);
677
begin
678
  if not CheckWrite(Buffer, Len) then
679
  begin
680
    if ErrId = 0 then
681
      ErrId := DS_WriteError;
682
    raise EZipMaster.CreateResDisp(ErrId, True);
683
  end;
684
end;
685
 
686
function TZMWorkFile.CheckWrites(const Buffer; const Lens: array of Integer)
687
  : Boolean;
688
var
689
  c: Integer;
690
  i: Integer;
691
begin
692
  c := 0;
693
  for i := Low(Lens) to High(Lens) do
694
    c := c + Lens[i];
695
  Result := Writes(Buffer, Lens) = c;
696
end;
697
 
698
// must read from current part
699
procedure TZMWorkFile.CheckWrites(const Buffer; const Lens: array of Integer;
700
  ErrId: Integer);
701
begin
702
  if not CheckWrites(Buffer, Lens) then
703
  begin
704
    if ErrId = 0 then
705
      ErrId := DS_WriteError;
706
    raise EZipMaster.CreateResDisp(ErrId, True);
707
  end;
708
end;
709
 
710
procedure TZMWorkFile.ClearFileInformation;
711
begin
712
  ZeroMemory(@fSavedFileInfo, sizeof(_BY_HANDLE_FILE_INFORMATION));
713
end;
714
 
715
procedure TZMWorkFile.ClearFloppy(const dir: String);
716
var
717
  Fname: String;
718
  SRec: TSearchRec;
719
begin
720
  if FindFirst(dir + WILD_ALL, faAnyFile, SRec) = 0 then
721
    repeat
722
      Fname := dir + SRec.Name;
723
      if ((SRec.Attr and faDirectory) <> 0) and (SRec.Name <> DIR_THIS) and
724
        (SRec.Name <> DIR_PARENT) then
725
      begin
726
        Fname := Fname + PathDelim;
727
        ClearFloppy(Fname);
728
        if Boss.Verbosity >= zvTrace then
729
          Boss.ReportMsg(TM_Erasing, [Fname])
730
        else
731
          Boss.KeepAlive;
732
        // allow time for OS to delete last file
733
        RemoveDir(Fname);
734
      end
735
      else
736
      begin
737
        if Boss.Verbosity >= zvTrace then
738
          Boss.ReportMsg(TM_Deleting, [Fname])
739
        else
740
          Boss.KeepAlive;
741
        SysUtils.DeleteFile(Fname);
742
      end;
743
    until FindNext(SRec) <> 0;
744
    SysUtils.FindClose(SRec);
745
end;
746
 
747
function TZMWorkFile.CopyFrom(Source: TZMWorkFile; Len: Int64): Int64;
748
var
749
  BufSize: Cardinal;
750
  SizeR: Integer;
751
  ToRead: Integer;
752
  wb: pByte;
753
begin
754
  BufSize := 10 * 1024; // constant is somewhere
755
  wb := WBuffer(BufSize);
756
  Result := 0;
757
 
758
  while Len > 0 do
759
  begin
760
    ToRead := BufSize;
761
    if Len < BufSize then
762
      ToRead := Len;
763
    SizeR := Source.Read(wb^, ToRead);
764
    if SizeR <> ToRead then
765
    begin
766
      if SizeR < 0 then
767
        Result := SizeR
768
      else
769
        Result := -DS_ReadError;
770
      exit;
771
    end;
772
    if SizeR > 0 then
773
    begin
774
      ToRead := Write(wb^, SizeR);
775
      if SizeR <> ToRead then
776
      begin
777
        if ToRead < 0 then
778
          Result := ToRead
779
        else
780
          Result := -DS_WriteError;
781
        exit;
782
      end;
783
      Len := Len - SizeR;
784
      Result := Result + SizeR;
785
      ProgReport(zacProgress, PR_Copying, Source.FileName, SizeR);
786
    end;
787
  end;
788
end;
789
 
790
function TZMWorkFile.Copy_File(Source: TZMWorkFile): Integer;
791
var
792
  fsize: Int64;
793
  r: Int64;
794
begin
795
  try
796
    if not Source.IsOpen then
797
      Source.File_Open(fmOpenRead);
798
    Result := 0;
799
    fsize := Source.Seek(0, 2);
800
    Source.Seek(0, 0);
801
    ProgReport(zacXItem, PR_Copying, Source.FileName, fsize);
802
    r := self.CopyFrom(Source, fsize);
803
    if r < 0 then
804
      Result := Integer(r);
805
  except
806
    Result := -9; // general error
807
  end;
808
end;
809
 
810
function TZMWorkFile.CreateMVFileNameEx(const FileName: String;
811
  StripPartNbr, Compat: Boolean): String;
812
var
813
  ext: String;
814
begin // changes FileName into multi volume FileName
815
  if Compat then
816
  begin
817
    if DiskNr <> (TotalDisks - 1) then
818
    begin
819
      if DiskNr < 9 then
820
        ext := '.z0'
821
      else
822
        ext := '.z';
823
      ext := ext + IntToStr(succ(DiskNr));
824
    end
825
    else
826
      ext := EXT_ZIP;
827
    Result := ChangeFileExt(FileName, ext);
828
  end
829
  else
830
    Result := ChangeNumberedName(FileName, DiskNr + 1, StripPartNbr);
831
end;
832
 
833
procedure TZMWorkFile.Diag(const msg: String);
834
begin
835
  if Boss.Verbosity >= zvTrace then
836
    Boss.ReportMessage(0, msg);
837
end;
838
 
839
function TZMWorkFile.DoFileWrite(const Buffer; Len: Integer): Integer;
840
begin
841
  Result := FileWrite(fHandle, Buffer, Len);
842
end;
843
 
844
// return true if end of segment
845
// WARNING - repositions to end of segment
846
function TZMWorkFile.EOS: Boolean;
847
begin
848
  Result := FileSeek64(Handle, 0, soFromCurrent) = FileSeek64
849
    (Handle, 0, soFromEnd);
850
end;
851
 
852
function TZMWorkFile.FileDate: Cardinal;
853
begin
854
  Result := FileGetDate(fHandle);
855
end;
856
 
857
procedure TZMWorkFile.File_Close;
858
begin
859
  if fDiskBuffer <> nil then
860
    FlushDiskBuffer;
861
  File_Close_F;
862
//  inherited;
863
end;
864
 
865
procedure TZMWorkFile.File_Close_F;
866
var
867
  th: Integer;
868
begin
869
  if fHandle <> -1 then
870
  begin
871
    th := fHandle;
872
    fHandle := -1;
873
    // if open for writing set date
874
    if (StampDate <> 0) and
875
       ((OpenMode and (SysUtils.fmOpenReadWrite or SysUtils.fmOpenWrite)) <> 0) then
876
    begin
877
      FileSetDate(th, StampDate);
878
      if Boss.Verbosity >= zvTrace then
879
        Diag('Trace: Set file Date ' + fRealFileName + ' to ' + DateTimeToStr
880
            (FileDateToLocalDateTime(StampDate)));
881
    end;
882
    FileClose(th);
883
    if Boss.Verbosity >= zvTrace then
884
      Diag('Trace: Closed ' + fRealFileName);
885
  end;
886
  fIsOpen := false;
887
end;
888
 
889
function TZMWorkFile.File_Create(const theName: String): Boolean;
890
var
891
  n: String;
892
begin
893
  File_Close;
894
  Result := false;
895
  if theName <> '' then
896
  begin
897
    if FileName = '' then
898
      FileName := theName;
899
    n := theName;
900
  end
901
  else
902
    n := FileName;
903
  if n = '' then
904
    exit;
905
  if Boss.Verbosity >= zvTrace then
906
    Diag('Trace: Creating ' + n);
907
  fRealFileName := n;
908
  fHandle := FileCreate(n);
909
  if fHandle <> -1 then
910
    TZMCore(Worker).AddCleanupFile(n);
911
  fBytesWritten := 0;
912
  fBytesRead := 0;
913
  Result := fHandle <> -1;
914
  fIsOpen := Result;
915
  fOpenMode := SysUtils.fmOpenWrite;
916
end;
917
 
918
function TZMWorkFile.File_CreateTemp(const Prefix, Where: String): Boolean;
919
var
920
  Buf: String;
921
  Len: DWORD;
922
  tmpDir: String;
923
begin
924
  Result := false;
925
  if Length(Boss.TempDir) = 0 then
926
  begin
927
    if Length(Where) <> 0 then
928
    begin
929
      tmpDir := ExtractFilePath(Where);
930
      tmpDir := ExpandFileName(tmpDir);
931
    end;
932
//  if Length(Worker.TempDir) = 0 then // Get the system temp dir
933
    if Length(tmpDir) = 0 then // Get the system temp dir
934
    begin
935
      // 1. The path specified by the TMP environment variable.
936
      // 2. The path specified by the TEMP environment variable, if TMP is not defined.
937
      // 3. The current directory, if both TMP and TEMP are not defined.
938
      Len := GetTempPath(0, PChar(tmpDir));
939
      SetLength(tmpDir, Len);
940
      GetTempPath(Len, PChar(tmpDir));
941
    end;
942
  end
943
  else // Use Temp dir provided by ZipMaster
944
  begin
945
    tmpDir := Boss.TempDir;
946
  end;
947
  tmpDir := DelimitPath(tmpDir, True);
948
  SetLength(Buf, MAX_PATH + 12);
949
  if GetTempFileName(PChar(tmpDir), PChar(Prefix), 0, PChar(Buf)) <> 0 then
950
  begin
951
    FileName := PChar(Buf);
952
    IsTemp := True; // delete when finished
953
    if Boss.Verbosity >= zvTrace then
954
      Diag('Trace: Created temporary ' + FileName);
955
    fRealFileName := FileName;
956
    fBytesWritten := 0;
957
    fBytesRead := 0;
958
    fOpenMode := SysUtils.fmOpenWrite;
959
    Result := File_Open(fmOpenWrite);
960
  end;
961
end;
962
 
963
function TZMWorkFile.File_Open(Mode: Cardinal): Boolean;
964
begin
965
  File_Close;
966
  if Boss.Verbosity >= zvTrace then
967
    Diag('Trace: Opening ' + fFileName);
968
  fRealFileName := fFileName;
969
  fHandle := FileOpen(fFileName, Mode);
970
  Result := fHandle <> -1;
971
  fIsOpen := Result;
972
  fOpenMode := Mode;
973
end;
974
 
975
function TZMWorkFile.File_Rename(const NewName: string;
976
  const Safe: Boolean = false): Boolean;
977
begin
978
  if Boss.Verbosity >= zvTrace then
979
    Diag('Trace: Rename ' + RealFileName + ' to ' + NewName);
980
  IsTemp := false;
981
  if IsOpen then
982
    File_Close;
983
  if FileExists(FileName) then
984
  begin
985
    if FileExists(NewName) then
986
    begin
987
      if Boss.Verbosity >= zvTrace then
988
        Diag('Trace: Erasing ' + NewName);
989
      if (EraseFile(NewName, not Safe) <> 0) and (Boss.Verbosity >= zvTrace)
990
        then
991
        Diag('Trace: Erase failed ' + NewName);
992
    end;
993
  end;
994
  Result := RenameFile(FileName, NewName);
995
  if Result then
996
  begin
997
    fFileName := NewName;  // success
998
    fRealFileName := NewName;
999
  end;
1000
end;
1001
 
1002
// rename last part after Write
1003
function TZMWorkFile.FinishWrite: Integer;
1004
var
1005
  fn: String;
1006
  LastName: String;
1007
  MsgStr: String;
1008
  Res: Integer;
1009
  OnStatusDisk: TZMStatusDiskEvent;
1010
begin
1011
  // change extn of last file
1012
  LastName := RealFileName;
1013
  File_Close;
1014
  Result := 0;
1015
 
1016
  if IsMultiPart then
1017
  begin
1018
    if ((Numbering = znsExt) and not AnsiSameText(ExtractFileExt(LastName), EXT_ZIP)) or
1019
      ((Numbering = znsName) and (DiskNr = 0)) then
1020
    begin
1021
      Result := -1;
1022
      fn := FileName;
1023
      if (FileExists(fn)) then
1024
      begin
1025
        MsgStr := Boss.ZipFmtLoadStr(DS_AskDeleteFile, [fn]);
1026
        FZipDiskStatus := FZipDiskStatus + [zdsSameFileName];
1027
        Res := idYes;
1028
        if not(zaaYesOvrwrt in Worker.AnswerAll) then
1029
        begin
1030
          OnStatusDisk := Worker.Master.OnStatusDisk;
1031
          if Assigned(OnStatusDisk) then // 1.77
1032
          begin
1033
            FZipDiskAction := zdaOk; // The default action
1034
            OnStatusDisk(Boss.Master, DiskNr, fn, FZipDiskStatus,
1035
              FZipDiskAction);
1036
            if FZipDiskAction = zdaYesToAll then
1037
            begin
1038
              Worker.AnswerAll := Worker.AnswerAll + [zaaYesOvrwrt];
1039
              FZipDiskAction := zdaOk;
1040
            end;
1041
            if FZipDiskAction = zdaOk then
1042
              Res := idYes
1043
            else
1044
              Res := idNo;
1045
          end
1046
          else
1047
            Res := Boss.ZipMessageDlgEx(MsgStr, Boss.ZipLoadStr(FM_Confirm)
1048
                , zmtWarning + DHC_WrtSpnDel, [mbYes, mbNo]);
1049
        end;
1050
        if (Res = 0) then
1051
          Boss.ShowZipMessage(DS_NoMem, '');
1052
        if (Res = idNo) then
1053
          Boss.ReportMsg(DS_NoRenamePart, [LastName]);
1054
        if (Res = idYes) then
1055
          SysUtils.DeleteFile(fn); // if it exists delete old one
1056
      end;
1057
      if FileExists(LastName) then // should be there but ...
1058
      begin
1059
        RenameFile(LastName, fn);
1060
        Result := 0;
1061
        if Boss.Verbosity >= zvVerbose then
1062
          Boss.Diag(Format('renamed %s to %s', [LastName, fn]));
1063
      end;
1064
    end;
1065
  end;
1066
end;
1067
 
1068
procedure TZMWorkFile.FlushDiskBuffer;
1069
var
1070
  did: Integer;
1071
  Len: Integer;
1072
begin
1073
  Len := fBufferPosition;
1074
  fBufferPosition := -1; // stop retrying on error
1075
  if fDiskBuffer <> nil then
1076
  begin
1077
    Boss.KeepAlive;
1078
    Boss.CheckCancel;
1079
    if Len > 0 then
1080
    begin
1081
      repeat
1082
        did := DoFileWrite(fDiskBuffer[0], Len);
1083
        if did <> Len then
1084
        begin
1085
          NewFlushDisk; // abort or try again on new disk
1086
        end;
1087
      until (did = Len);
1088
    end;
1089
    fDiskBuffer := nil;
1090
  end;
1091
end;
1092
 
1093
function TZMWorkFile.GetConfirmErase: Boolean;
1094
begin
1095
  Result := Worker.ConfirmErase;
1096
end;
1097
 
1098
function TZMWorkFile.GetExists: Boolean;
1099
begin
1100
  Result := false;
1101
  if FileExists(FileName) then
1102
    Result := True;
1103
end;
1104
 
1105
function TZMWorkFile.GetFileInformation(var FileInfo:
1106
    _BY_HANDLE_FILE_INFORMATION): Boolean;
1107
begin
1108
  Result := IsOpen;
1109
  if Result then
1110
    Result := GetFileInformationByHandle(Handle, FileInfo);
1111
  if not Result then
1112
    ZeroMemory(@FileInfo, sizeof(_BY_HANDLE_FILE_INFORMATION));
1113
end;
1114
 
1115
function TZMWorkFile.GetKeepFreeOnAllDisks: Cardinal;
1116
begin
1117
  Result := Worker.KeepFreeOnAllDisks;
1118
end;
1119
 
1120
function TZMWorkFile.GetKeepFreeOnDisk1: Cardinal;
1121
begin
1122
  Result := Worker.KeepFreeOnDisk1;
1123
end;
1124
 
1125
function TZMWorkFile.GetLastWritten: Cardinal;
1126
var
1127
  ft: TFileTime;
1128
begin
1129
  Result := 0;
1130
  if IsOpen and LastWriteTime(ft) then
1131
    Result := FileTimeToLocalDOSTime(ft);
1132
end;
1133
 
1134
function TZMWorkFile.GetMaxVolumeSize: Int64;
1135
begin
1136
  Result := Worker.MaxVolumeSize;
1137
end;
1138
 
1139
function TZMWorkFile.GetMinFreeVolumeSize: Cardinal;
1140
begin
1141
  Result := Worker.MinFreeVolumeSize;
1142
end;
1143
 
1144
procedure TZMWorkFile.GetNewDisk(DiskSeq: Integer; AllowEmpty: Boolean);
1145
begin
1146
  File_Close;
1147
  // Close the file on the old disk first.
1148
  if (TotalDisks <> 1) or (DiskSeq <> 0) then
1149
    IsMultiPart := True;
1150
  DiskNr := DiskSeq;
1151
  while True do
1152
  begin
1153
    repeat
1154
      NewDisk := True;
1155
      File_Close;
1156
      CheckForDisk(false, spTryFormat in SpanOptions);
1157
      if AllowEmpty and WorkDrive.HasMedia(spTryFormat in SpanOptions) then
1158
      begin
1159
        if WorkDrive.VolumeSpace = -1 then
1160
          exit; // unformatted
1161
        if WorkDrive.VolumeSpace = WorkDrive.VolumeSize then
1162
          exit; // empty
1163
      end;
1164
    until IsRightDisk;
1165
 
1166
    if Boss.Verbosity >= zvVerbose then
1167
      Boss.Diag(Boss.ZipFmtLoadStr(TM_GetNewDisk, [FileName]));
1168
    if File_Open(fmShareDenyWrite or fmOpenRead) then
1169
      break; // found
1170
    if WorkDrive.DriveIsFixed then
1171
      raise EZipMaster.CreateResDisp(DS_NoInFile, True)
1172
    else
1173
      Boss.ShowZipMessage(DS_NoInFile, '');
1174
  end;
1175
end;
1176
 
1177
function TZMWorkFile.GetPosition: Int64;
1178
begin
1179
  if fDiskBuffer <> nil then
1180
    Result := fBufferPosition
1181
  else
1182
    Result := GetPosition_F;
1183
end;
1184
 
1185
function TZMWorkFile.GetPosition_F: Int64;
1186
begin
1187
  Result := FileSeek64(fHandle, 0, soFromCurrent); // from current
1188
end;
1189
 
1190
function TZMWorkFile.GetSpanOptions: TZMSpanOpts;
1191
begin
1192
  Result := Worker.SpanOptions;
1193
end;
1194
 
1195
function TZMWorkFile.HasSpanSig(const FName: String): boolean;
1196
var
1197
  fs: TFileStream;
1198
  Sg: Cardinal;
1199
begin
1200
  Result := False;
1201
  if FileExists(FName) then
1202
  begin
1203
    fs := TFileStream.Create(FName, fmOpenRead);
1204
    try
1205
      if (fs.Size > (sizeof(TZipLocalHeader) + sizeof(Sg))) and
1206
        (fs.Read(Sg, sizeof(Sg)) = sizeof(Sg)) then
1207
        Result :=  (Sg = ExtLocalSig) and (fs.Read(Sg, sizeof(Sg)) = sizeof(Sg)) and
1208
          (Sg = LocalFileHeaderSig);
1209
    finally
1210
      fs.Free;
1211
    end;
1212
  end;
1213
end;
1214
 
1215
function TZMWorkFile.IsRightDisk: Boolean;
1216
var
1217
  fn: String;
1218
  VName: string;
1219
begin
1220
  Result := True;
1221
  if (Numbering < znsName) and (not WorkDrive.DriveIsFixed) then
1222
  begin
1223
    VName := WorkDrive.DiskName;
1224
    Boss.Diag('Checking disk ' + VName + ' need ' + VolName(DiskNr));
1225
    if (AnsiSameText(VName, VolName(DiskNr)) or AnsiSameText(VName, OldVolName(DiskNr))) and
1226
        FileExists(FileName) then
1227
    begin
1228
      Numbering := znsVolume;
1229
      Boss.Diag('found volume ' + VName);
1230
      exit;
1231
    end;
1232
  end;
1233
  fn := FileName;
1234
  if Numbering = znsNone then // not known yet
1235
  begin
1236
    FileName := CreateMVFileNameEx(FileName, True, True);
1237
    // make compat name
1238
    if FileExists(FileName) then
1239
    begin
1240
      Numbering := znsExt;
1241
      exit;
1242
    end;
1243
    FileName := fn;
1244
    FileName := CreateMVFileNameEx(FileName, True, false);
1245
    // make numbered name
1246
    if FileExists(FileName) then
1247
    begin
1248
      Numbering := znsName;
1249
      exit;
1250
    end;
1251
    if WorkDrive.DriveIsFixed then
1252
      exit; // always true - only needed name
1253
    FileName := fn; // restore
1254
    Result := false;
1255
    exit;
1256
  end;
1257
  // numbering scheme already known
1258
  if Numbering = znsVolume then
1259
  begin
1260
    Result := false;
1261
    exit;
1262
  end;
1263
  FileName := CreateMVFileNameEx(FileName, True, Numbering = znsExt);
1264
  // fixed drive always true only needed new filename
1265
  if (not WorkDrive.DriveIsFixed) and (not FileExists(FileName)) then
1266
  begin
1267
    FileName := fn; // restore
1268
    Result := false;
1269
  end;
1270
end;
1271
 
1272
function TZMWorkFile.LastWriteTime(var last_write: TFileTime): Boolean;
1273
var
1274
  BHFInfo: TByHandleFileInformation;
1275
begin
1276
  Result := false;
1277
  last_write.dwLowDateTime := 0;
1278
  last_write.dwHighDateTime := 0;
1279
  if IsOpen then
1280
  begin
1281
    Result := GetFileInformationByHandle(fHandle, BHFInfo);
1282
    if Result then
1283
      last_write := BHFInfo.ftLastWriteTime;
1284
  end;
1285
end;
1286
 
1287
function TZMWorkFile.MapNumbering(Opts: TZMSpanOpts): TZMSpanOpts;
1288
var
1289
  spans: TZMSpanOpts;
1290
begin
1291
  Result := Opts;
1292
  if Numbering <> znsNone then
1293
  begin
1294
    // map numbering type only if known
1295
    spans := Opts - [spCompatName] + [spNoVolumeName];
1296
    case Numbering of
1297
      znsVolume:
1298
        spans := spans - [spNoVolumeName];
1299
      znsExt:
1300
        spans := spans + [spCompatName];
1301
    end;
1302
    Result := spans;
1303
  end;
1304
end;
1305
 
1306
procedure TZMWorkFile.NewFlushDisk;
1307
begin
1308
  // need to allow another disk, check size, open file, name disk etc
1309
  raise EZipMaster.CreateResDisp(DS_WriteError, True);
1310
end;
1311
 
1312
function TZMWorkFile.NewSegment: Boolean; // true to 'continue'
1313
var
1314
  DiskFile: String;
1315
  DiskSeq: Integer;
1316
  MsgQ: String;
1317
  Res: Integer;
1318
  SegName: String;
1319
  OnGetNextDisk: TZMGetNextDiskEvent;
1320
  OnStatusDisk: TZMStatusDiskEvent;
1321
begin
1322
  Result := false;
1323
  // If we write on a fixed disk the filename must change.
1324
  // We will get something like: FileNamexxx.zip where xxx is 001,002 etc.
1325
  // if CompatNames are used we get FileName.zxx where xx is 01, 02 etc.. last .zip
1326
  if Numbering = znsNone then
1327
  begin
1328
    if spCompatName in SpanOptions then
1329
      Numbering := znsExt
1330
    else if WorkDrive.DriveIsFixed or (spNoVolumeName in SpanOptions) then
1331
      Numbering := znsName
1332
    else
1333
      Numbering := znsVolume;
1334
  end;
1335
  DiskFile := FileName;
1336
  if Numbering <> znsVolume then
1337
    DiskFile := CreateMVFileNameEx(DiskFile, false, Numbering = znsExt);
1338
  CheckForDisk(True, spWipeFiles in SpanOptions);
1339
 
1340
  OnGetNextDisk := Worker.Master.OnGetNextDisk;
1341
  // Allow clearing of removeable media even if no volume names
1342
  if (not WorkDrive.DriveIsFixed) and (spWipeFiles in SpanOptions) and
1343
    ((FZipDiskAction = zdaErase) or not Assigned(OnGetNextDisk)) then
1344
  begin
1345
    // Do we want a format first?
1346
    if Numbering = znsVolume then
1347
      SegName := VolName(DiskNr)
1348
      // default name
1349
    else
1350
      SegName := SZipSet + IntToStr(succ(DiskNr));
1351
    // Ok=6 NoFormat=-3, Cancel=-2, Error=-1
1352
    case ZipFormat(SegName) of
1353
      // Start formating and wait until BeforeClose...
1354
      - 1:
1355
        raise EZipMaster.CreateResDisp(DS_Canceled, True);
1356
      -2:
1357
        raise EZipMaster.CreateResDisp(DS_Canceled, false);
1358
    end;
1359
  end;
1360
  if WorkDrive.DriveIsFixed or (Numbering <> znsVolume) then
1361
    DiskSeq := DiskNr + 1
1362
  else
1363
  begin
1364
    DiskSeq := StrToIntDef(Copy(WorkDrive.DiskName, 9, 3), 1);
1365
    if DiskSeq < 0 then
1366
      DiskSeq := 1;
1367
  end;
1368
  FZipDiskStatus := [];
1369
  Res := AskOverwriteSegment(DiskFile, DiskSeq);
1370
  if (Res = idYes) and (WorkDrive.DriveIsFixed) and
1371
    (spCompatName in SpanOptions) and FileExists(ReqFileName) then
1372
  begin
1373
    Res := AskOverwriteSegment(ReqFileName, DiskSeq);
1374
    if (Res = idYes) then
1375
      EraseFile(ReqFileName, Worker.HowToDelete = htdFinal);
1376
  end;
1377
  if (Res = 0) or (Res = idCancel) or ((Res = idNo) and WorkDrive.DriveIsFixed)
1378
    then
1379
    raise EZipMaster.CreateResDisp(DS_Canceled, false);
1380
 
1381
  if Res = idNo then
1382
  begin // we will try again...
1383
    FDiskWritten := 0;
1384
    NewDisk := True;
1385
    Result := True;
1386
    exit;
1387
  end;
1388
  // Create the output file.
1389
  if not File_Create(DiskFile) then
1390
  begin // change proposed by Pedro Araujo
1391
    MsgQ := Boss.ZipLoadStr(DS_NoOutFile);
1392
    Res := Boss.ZipMessageDlgEx('', MsgQ, zmtError + DHC_SpanNoOut,
1393
      [mbRetry, mbCancel]);
1394
    if Res = 0 then
1395
      raise EZipMaster.CreateResDisp(DS_NoMem, True);
1396
    if Res <> idRetry then
1397
      raise EZipMaster.CreateResDisp(DS_Canceled, false);
1398
    FDiskWritten := 0;
1399
    NewDisk := True;
1400
    Result := True;
1401
    exit;
1402
  end;
1403
 
1404
  // Get the free space on this disk, correct later if neccessary.
1405
  WorkDrive.VolumeRefresh;
1406
 
1407
  // Set the maximum number of bytes that can be written to this disk(file).
1408
  // Reserve space on/in all the disk/file.
1409
  if (DiskNr = 0) and (KeepFreeOnDisk1 > 0) or (KeepFreeOnAllDisks > 0) then
1410
  begin
1411
    if (KeepFreeOnDisk1 mod WorkDrive.VolumeSecSize) <> 0 then
1412
      KeepFreeOnDisk1 := succ(KeepFreeOnDisk1 div WorkDrive.VolumeSecSize)
1413
        * WorkDrive.VolumeSecSize;
1414
    if (KeepFreeOnAllDisks mod WorkDrive.VolumeSecSize) <> 0 then
1415
      KeepFreeOnAllDisks := succ
1416
        (KeepFreeOnAllDisks div WorkDrive.VolumeSecSize)
1417
        * WorkDrive.VolumeSecSize;
1418
  end;
1419
  AllowedSize := WorkDrive.VolumeSize - KeepFreeOnAllDisks;
1420
  if (MaxVolumeSize > 0) and (MaxVolumeSize < AllowedSize) then
1421
    AllowedSize := MaxVolumeSize;
1422
  // Reserve space on/in the first disk(file).
1423
  if DiskNr = 0 then
1424
    AllowedSize := AllowedSize - KeepFreeOnDisk1;
1425
 
1426
  // Do we still have enough free space on this disk.
1427
  if AllowedSize < MinFreeVolumeSize then // No, too bad...
1428
  begin
1429
    OnStatusDisk := Worker.Master.OnStatusDisk;
1430
    File_Close;
1431
    SysUtils.DeleteFile(DiskFile);
1432
    if Assigned(OnStatusDisk) then // v1.60L
1433
    begin
1434
      if Numbering <> znsVolume then
1435
        DiskSeq := DiskNr + 1
1436
      else
1437
      begin
1438
        DiskSeq := StrToIntDef(Copy(WorkDrive.DiskName, 9, 3), 1);
1439
        if DiskSeq < 0 then
1440
          DiskSeq := 1;
1441
      end;
1442
      FZipDiskAction := zdaOk; // The default action
1443
      FZipDiskStatus := [zdsNotEnoughSpace];
1444
      OnStatusDisk(Boss.Master, DiskSeq, DiskFile, FZipDiskStatus,
1445
        FZipDiskAction);
1446
      if FZipDiskAction = zdaCancel then
1447
        Res := idCancel
1448
      else
1449
        Res := idRetry;
1450
    end
1451
    else
1452
    begin
1453
      MsgQ := Boss.ZipLoadStr(DS_NoDiskSpace);
1454
      Res := Boss.ZipMessageDlgEx('', MsgQ, zmtError + DHC_SpanSpace,
1455
        [mbRetry, mbCancel]);
1456
    end;
1457
    if Res = 0 then
1458
      raise EZipMaster.CreateResDisp(DS_NoMem, True);
1459
    if Res <> idRetry then
1460
      raise EZipMaster.CreateResDisp(DS_Canceled, false);
1461
    FDiskWritten := 0;
1462
 
1463
    NewDisk := True;
1464
    // If all this was on a HD then this wouldn't be useful but...
1465
    Result := True;
1466
  end
1467
  else
1468
  begin
1469
    // ok. it fits and the file is open
1470
    // Set the volume label of this disk if it is not a fixed one.
1471
    if not(WorkDrive.DriveIsFixed or (Numbering <> znsVolume)) then
1472
    begin
1473
      if not WorkDrive.RenameDisk(VolName(DiskNr)) then
1474
        raise EZipMaster.CreateResDisp(DS_NoVolume, True);
1475
    end;
1476
    // if it is a floppy buffer it
1477
    if (not WorkDrive.DriveIsFixed) and (AllowedSize <= MaxDiskBufferSize) then
1478
    begin
1479
      SetLength(fDiskBuffer, AllowedSize);
1480
      fBufferPosition := 0;
1481
    end;
1482
  end;
1483
end;
1484
 
1485
function TZMWorkFile.OldVolName(Part: Integer): String;
1486
begin
1487
  Result := SPKBACK + ' ' + Copy(IntToStr(1001 + Part), 2, 3);
1488
end;
1489
 
1490
procedure TZMWorkFile.ProgReport(prog: TActionCodes; xprog: Integer; const
1491
    Name: String; size: Int64);
1492
var
1493
  actn: TActionCodes;
1494
  msg: String;
1495
begin
1496
  actn := prog;
1497
  if (Name = '') and (xprog > PR_Progress) then
1498
    msg := Boss.ZipLoadStr(xprog)
1499
  else
1500
    msg := Name;
1501
  case ShowProgress of
1502
    zspNone:
1503
      case prog of
1504
        zacItem:
1505
          actn := zacNone;
1506
        zacProgress:
1507
          actn := zacTick;
1508
        zacEndOfBatch:
1509
          actn := zacTick;
1510
        zacCount:
1511
          actn := zacNone;
1512
        zacSize:
1513
          actn := zacTick;
1514
        zacXItem:
1515
          actn := zacNone;
1516
        zacXProgress:
1517
          actn := zacTick;
1518
      end;
1519
    zspExtra:
1520
      case prog of
1521
        zacItem:
1522
          actn := zacNone; // do nothing
1523
        zacProgress:
1524
          actn := zacXProgress;
1525
        zacCount:
1526
          actn := zacNone; // do nothing
1527
        zacSize:
1528
          actn := zacXItem;
1529
      end;
1530
  end;
1531
  if actn <> zacNone then
1532
    Boss.ReportProgress(actn, xprog, msg, size);
1533
end;
1534
 
1535
function TZMWorkFile.Read(var Buffer; Len: Integer): Integer;
1536
var
1537
  bp: PAnsiChar;
1538
  SizeR: Integer;
1539
  ToRead: Integer;
1540
begin
1541
  try
1542
    if IsMultiPart then
1543
    begin
1544
      ToRead := Len;
1545
      if Len < 0 then
1546
        ToRead := -Len;
1547
      bp := @Buffer;
1548
      Result := 0;
1549
      while ToRead > 0 do
1550
      begin
1551
        SizeR := ReadFromFile(bp^, ToRead);
1552
        if SizeR <> ToRead then
1553
        begin
1554
          // Check if we are at the end of a input disk.
1555
          if SizeR < 0 then
1556
          begin
1557
            Result := SizeR;
1558
            exit;
1559
          end;
1560
          // if  error or (len <0 and read some) or (end segment)
1561
          if ((Len < 0) and (SizeR <> 0)) or not EOS then
1562
          begin
1563
            Result := -DS_ReadError;
1564
            exit;
1565
          end;
1566
          // It seems we are at the end, so get a next disk.
1567
          GetNewDisk(DiskNr + 1, false);
1568
        end;
1569
        if SizeR > 0 then
1570
        begin
1571
          Inc(bp, SizeR);
1572
          ToRead := ToRead - SizeR;
1573
          Result := Result + SizeR;
1574
        end;
1575
      end;
1576
    end
1577
    else
1578
      Result := Read_F(Buffer, Len);
1579
  except
1580
    on E: EZipMaster do
1581
      Result := -E.ResId;
1582
    on E: Exception do
1583
      Result := -DS_ReadError;
1584
  end;
1585
end;
1586
 
1587
function TZMWorkFile.ReadFromFile(var Buffer; Len: Integer): Integer;
1588
begin
1589
  if Len < 0 then
1590
    Len := -Len;
1591
  Result := FileRead(fHandle, Buffer, Len);
1592
  if Result > 0 then
1593
    BytesRead := BytesRead + Len
1594
  else if Result < 0 then
1595
  begin
1596
    Result := -DS_ReadError;
1597
  end;
1598
end;
1599
 
1600
function TZMWorkFile.Reads(var Buffer; const Lens: array of Integer): Integer;
1601
var
1602
  i: Integer;
1603
  pb: PAnsiChar;
1604
  r: Integer;
1605
begin
1606
  Result := 0;
1607
  if IsMultiPart then
1608
  begin
1609
    pb := @Buffer;
1610
    for i := Low(Lens) to High(Lens) do
1611
    begin
1612
      r := Read(pb^, -Lens[i]);
1613
      if r < 0 then
1614
      begin
1615
        Result := r;
1616
        break;
1617
      end;
1618
      Result := Result + r;
1619
      Inc(pb, r);
1620
    end;
1621
  end
1622
  else
1623
    Result := Reads_F(Buffer, Lens);
1624
end;
1625
 
1626
function TZMWorkFile.Reads_F(var Buffer; const Lens: array of Integer): Integer;
1627
var
1628
  c: Integer;
1629
  i: Integer;
1630
begin
1631
  c := 0;
1632
  for i := Low(Lens) to High(Lens) do
1633
    c := c + Lens[i];
1634
  Result := ReadFromFile(Buffer, c);
1635
end;
1636
 
1637
function TZMWorkFile.ReadTo(strm: TStream; Count: Integer): Integer;
1638
const
1639
  bsize = 20 * 1024;
1640
var
1641
  done: Integer;
1642
  sz: Integer;
1643
  wbufr: array of Byte;
1644
begin
1645
  Result := 0;
1646
  SetLength(wbufr, bsize);
1647
  while Count > 0 do
1648
  begin
1649
    sz := bsize;
1650
    if sz > Count then
1651
      sz := Count;
1652
    done := Read(wbufr[0], sz);
1653
    if done > 0 then
1654
    begin
1655
      if strm.write(wbufr[0], done) <> done then
1656
        done := -DS_WriteError;
1657
    end;
1658
    if done <> sz then
1659
    begin
1660
      Result := -DS_FileError;
1661
      if done < 0 then
1662
        Result := done;
1663
      break;
1664
    end;
1665
    Count := Count - sz;
1666
    Result := Result + sz;
1667
  end;
1668
end;
1669
 
1670
function TZMWorkFile.Read_F(var Buffer; Len: Integer): Integer;
1671
begin
1672
  Result := ReadFromFile(Buffer, Len);
1673
end;
1674
 
1675
function TZMWorkFile.SaveFileInformation: Boolean;
1676
begin
1677
  Result := GetFileInformation(fSavedFileInfo);
1678
end;
1679
 
1680
function TZMWorkFile.Seek(offset: Int64; from: Integer): Int64;
1681
begin
1682
  Result := FileSeek64(fHandle, offset, from);
1683
end;
1684
 
1685
function TZMWorkFile.SeekDisk(Nr: Integer): Integer;
1686
begin
1687
  if DiskNr <> Nr then
1688
    GetNewDisk(Nr, false);
1689
  Result := Nr;
1690
end;
1691
 
1692
procedure TZMWorkFile.SetBoss(const Value: TZMCore);
1693
begin
1694
  if FBoss <> Value then
1695
  begin
1696
    if Value = nil then
1697
      FBoss := fWorker
1698
    else
1699
      FBoss := Value;
1700
  end;
1701
end;
1702
 
1703
function TZMWorkFile.SetEndOfFile: Boolean;
1704
begin
1705
  if IsOpen then
1706
    Result := Windows.SetEndOfFile(Handle)
1707
  else
1708
    Result := false;
1709
end;
1710
 
1711
procedure TZMWorkFile.SetFileName(const Value: String);
1712
begin
1713
  if fFileName <> Value then
1714
  begin
1715
    if IsOpen then
1716
      File_Close;
1717
    fFileName := Value;
1718
    WorkDrive.DriveStr := Value;
1719
  end;
1720
end;
1721
 
1722
// dangerous - assumes file on same drive
1723
procedure TZMWorkFile.SetHandle(const Value: Integer);
1724
begin
1725
  File_Close;
1726
  fHandle := Value;
1727
  fIsOpen := fHandle <> -1;
1728
end;
1729
 
1730
procedure TZMWorkFile.SetKeepFreeOnAllDisks(const Value: Cardinal);
1731
begin
1732
  Worker.KeepFreeOnAllDisks := Value;
1733
end;
1734
 
1735
procedure TZMWorkFile.SetKeepFreeOnDisk1(const Value: Cardinal);
1736
begin
1737
  Worker.KeepFreeOnDisk1 := Value;
1738
end;
1739
 
1740
procedure TZMWorkFile.SetMaxVolumeSize(const Value: Int64);
1741
begin
1742
  Worker.MaxVolumeSize := Value;
1743
end;
1744
 
1745
procedure TZMWorkFile.SetMinFreeVolumeSize(const Value: Cardinal);
1746
begin
1747
  Worker.MinFreeVolumeSize := Value;
1748
end;
1749
 
1750
procedure TZMWorkFile.SetPosition(const Value: Int64);
1751
begin
1752
  Seek(Value, 0);
1753
end;
1754
 
1755
procedure TZMWorkFile.SetSpanOptions(const Value: TZMSpanOpts);
1756
begin
1757
  Worker.SpanOptions := Value;
1758
end;
1759
 
1760
procedure TZMWorkFile.SetWorkDrive(const Value: TZMWorkDrive);
1761
begin
1762
  if fWorkDrive <> Value then
1763
  begin
1764
    fWorkDrive := Value;
1765
  end;
1766
end;
1767
 
1768
function TZMWorkFile.VerifyFileInformation: Boolean;
1769
var
1770
  info: _BY_HANDLE_FILE_INFORMATION;//TWIN32FindData;
1771
begin
1772
  GetFileInformation(info);
1773
  Result := (info.ftLastWriteTime.dwLowDateTime = fSavedFileInfo.ftLastWriteTime.dwLowDateTime) and
1774
      (info.ftLastWriteTime.dwHighDateTime = fSavedFileInfo.ftLastWriteTime.dwHighDateTime) and
1775
      (info.ftCreationTime.dwLowDateTime = fSavedFileInfo.ftCreationTime.dwLowDateTime) and
1776
      (info.ftCreationTime.dwHighDateTime = fSavedFileInfo.ftCreationTime.dwHighDateTime) and
1777
      (info.nFileSizeLow = fSavedFileInfo.nFileSizeLow) and
1778
      (info.nFileSizeHigh = fSavedFileInfo.nFileSizeHigh) and
1779
      (info.nFileIndexLow = fSavedFileInfo.nFileIndexLow) and
1780
      (info.nFileIndexHigh = fSavedFileInfo.nFileIndexHigh) and
1781
      (info.dwFileAttributes = fSavedFileInfo.dwFileAttributes) and
1782
      (info.dwVolumeSerialNumber = fSavedFileInfo.dwVolumeSerialNumber);
1783
end;
1784
 
1785
function TZMWorkFile.VolName(Part: Integer): String;
1786
begin
1787
  Result := SPKBACK + Copy(IntToStr(1001 + Part), 2, 3);
1788
end;
1789
 
1790
function TZMWorkFile.WBuffer(size: Integer): pByte;
1791
begin
1792
  if size < 1 then
1793
    WBuf := nil
1794
  else if HIGH(WBuf) < size then
1795
  begin
1796
    size := size or $3FF;
1797
    SetLength(WBuf, size + 1); // reallocate
1798
  end;
1799
  Result := @WBuf[0];
1800
end;
1801
 
1802
function TZMWorkFile.Write(const Buffer; Len: Integer): Integer;
1803
begin
1804
  if IsMultiPart then
1805
    Result := WriteSplit(Buffer, Len)
1806
  else
1807
    Result := Write_F(Buffer, Len);
1808
end;
1809
 
1810
function TZMWorkFile.WriteFrom(strm: TStream; Count: Integer): Int64;
1811
const
1812
  bsize = 20 * 1024;
1813
var
1814
  done: Integer;
1815
  maxsize: Integer;
1816
  sz: Integer;
1817
  wbufr: array of Byte;
1818
begin
1819
  Result := 0;
1820
  SetLength(wbufr, bsize);
1821
  maxsize := strm.size - strm.Position;
1822
  if Count > maxsize then
1823
    Count := maxsize;
1824
  while Count > 0 do
1825
  begin
1826
    sz := bsize;
1827
    if sz > Count then
1828
      sz := Count;
1829
    done := strm.Read(wbufr[0], sz);
1830
    if done > 0 then
1831
      done := Write(wbufr[0], done); // split ok?
1832
    if done <> sz then
1833
    begin
1834
      Result := -DS_FileError;
1835
      if done < 0 then
1836
        Result := done;
1837
      break;
1838
    end;
1839
    Count := Count - sz;
1840
    Result := Result + sz;
1841
  end;
1842
end;
1843
 
1844
function TZMWorkFile.Writes(const Buffer; const Lens: array of Integer)
1845
  : Integer;
1846
var
1847
  c: Integer;
1848
  i: Integer;
1849
begin
1850
  if IsMultiPart then
1851
  begin
1852
    c := 0;
1853
    for i := Low(Lens) to High(Lens) do
1854
      c := c + Lens[i];
1855
    Result := Write(Buffer, -c);
1856
  end
1857
  else
1858
    Result := Writes_F(Buffer, Lens);
1859
end;
1860
 
1861
function TZMWorkFile.WriteSplit(const Buffer; ToWrite: Integer): Integer;
1862
var
1863
  Buf: PAnsiChar;
1864
  Len: Cardinal;
1865
  MaxLen: Cardinal;
1866
  MinSize: Cardinal;
1867
  MustFit: Boolean;
1868
  Res: Integer;
1869
begin { WriteSplit }
1870
  try
1871
    Result := 0;
1872
    MustFit := false;
1873
    if ToWrite >= 0 then
1874
    begin
1875
      Len := ToWrite;
1876
      MinSize := 0;
1877
    end
1878
    else
1879
    begin
1880
      Len := -ToWrite;
1881
      MustFit := (Len and MustFitFlag) <> 0;
1882
      Len := Len and MustFitMask;
1883
      MinSize := Len;
1884
    end;
1885
    Buf := @Buffer;
1886
    Boss.KeepAlive;
1887
    Boss.CheckCancel;
1888
 
1889
    // Keep writing until error or Buffer is empty.
1890
    while True do
1891
    begin
1892
      // Check if we have an output file already opened, if not: create one,
1893
      // do checks, gather info.
1894
      if (not IsOpen) then
1895
      begin
1896
        NewDisk := DiskNr <> 0; // allow first disk in drive
1897
        if NewSegment then
1898
        begin
1899
          NewDisk := True;
1900
          continue;
1901
        end;
1902
      end;
1903
 
1904
      // Check if we have at least MinSize available on this disk,
1905
      // headers are not allowed to cross disk boundaries. ( if zero than don't care.)
1906
      if (MinSize <> 0) and (MinSize > AllowedSize) then
1907
      begin // close this part
1908
        // all parts must be same stamp
1909
        if StampDate = 0 then
1910
          StampDate := LastWritten;
1911
        File_Close;
1912
        FDiskWritten := 0;
1913
        NewDisk := True;
1914
        DiskNr := DiskNr + 1; // RCV270299
1915
        if not MustFit then
1916
          continue;
1917
        Result := MustFitError;
1918
        break;
1919
      end;
1920
 
1921
      // Don't try to write more bytes than allowed on this disk.
1922
      MaxLen := HIGH(Integer);
1923
      if AllowedSize < MaxLen then
1924
        MaxLen := Integer(AllowedSize);
1925
      if Len < MaxLen then
1926
        MaxLen := Len;
1927
      if fDiskBuffer <> nil then
1928
      begin
1929
        Move(Buf^, fDiskBuffer[fBufferPosition], MaxLen);
1930
        Res := MaxLen;
1931
        Inc(fBufferPosition, MaxLen);
1932
      end
1933
      else
1934
        Res := WriteToFile(Buf^, MaxLen);
1935
      if Res < 0 then
1936
        raise EZipMaster.CreateResDisp(DS_NoWrite, True);
1937
      // A write error (disk removed?)
1938
 
1939
      Inc(FDiskWritten, Res);
1940
      Inc(Result, Res);
1941
      AllowedSize := AllowedSize - MaxLen;
1942
      if MaxLen = Len then
1943
        break;
1944
 
1945
      // We still have some data left, we need a new disk.
1946
      if StampDate = 0 then
1947
        StampDate := LastWritten;
1948
      File_Close;
1949
      AllowedSize := 0;
1950
      FDiskWritten := 0;
1951
      DiskNr := DiskNr + 1;
1952
      NewDisk := True;
1953
      Inc(Buf, MaxLen);
1954
      Dec(Len, MaxLen);
1955
    end; { while(True) }
1956
  except
1957
    on E: EZipMaster do
1958
    begin
1959
      Result := -E.ResId;
1960
    end;
1961
    on E: Exception do
1962
    begin
1963
      Result := -DS_UnknownError;
1964
    end;
1965
  end;
1966
end;
1967
 
1968
function TZMWorkFile.Writes_F(const Buffer; const Lens: array of Integer)
1969
  : Integer;
1970
var
1971
  c: Integer;
1972
  i: Integer;
1973
begin
1974
  c := 0;
1975
  for i := Low(Lens) to High(Lens) do
1976
    c := c + Lens[i];
1977
  Result := WriteToFile(Buffer, c);
1978
end;
1979
 
1980
function TZMWorkFile.WriteToFile(const Buffer; Len: Integer): Integer;
1981
begin
1982
  if Len < 0 then
1983
    Len := (-Len) and MustFitMask;
1984
  Result := DoFileWrite(Buffer, Len);
1985
  if Result > 0 then
1986
    BytesWritten := BytesWritten + Len;
1987
end;
1988
 
1989
function TZMWorkFile.Write_F(const Buffer; Len: Integer): Integer;
1990
begin
1991
  Result := WriteToFile(Buffer, Len);
1992
end;
1993
 
1994
function TZMWorkFile.ZipFormat(const NewName: String): Integer;
1995
var
1996
  msg: String;
1997
  Res: Integer;
1998
  Vol: String;
1999
begin
2000
  if NewName <> '' then
2001
    Vol := NewName
2002
  else
2003
    Vol := WorkDrive.DiskName;
2004
  if Length(Vol) > 11 then
2005
    Vol := Copy(Vol, 1, 11);
2006
  Result := -3;
2007
  if WorkDrive.DriveIsFloppy then
2008
  begin
2009
    if (spTryFormat in SpanOptions) then
2010
      Result := FormatFloppy(Application.Handle, WorkDrive.DriveStr);
2011
    if Result = -3 then
2012
    begin
2013
      if ConfirmErase then
2014
      begin
2015
        msg := Boss.ZipFmtLoadStr(FM_Erase, [WorkDrive.DriveStr]);
2016
        Res := Boss.ZipMessageDlgEx(Boss.ZipLoadStr(FM_Confirm), msg,
2017
          zmtWarning + DHC_FormErase, [mbYes, mbNo]);
2018
        if Res <> idYes then
2019
        begin
2020
          Result := -3; // no  was -2; // cancel
2021
          exit;
2022
        end;
2023
      end;
2024
      ClearFloppy(WorkDrive.DriveStr);
2025
      Result := 0;
2026
    end;
2027
    WorkDrive.HasMedia(false);
2028
    if (Result = 0) and (Numbering = znsVolume) then
2029
      WorkDrive.RenameDisk(Vol);
2030
  end;
2031
end;
2032
 
2033
end.