399 lines
12 KiB
ObjectPascal
399 lines
12 KiB
ObjectPascal
(* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is TurboPower Abbrevia
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* TurboPower Software
|
|
*
|
|
* Portions created by the Initial Developer are Copyright (C) 1997-2002
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Craig Peterson <capeterson@users.sourceforge.net>
|
|
*
|
|
* ***** END LICENSE BLOCK ***** *)
|
|
|
|
{*********************************************************}
|
|
{* ABBREVIA: AbSpanSt.pas *}
|
|
{*********************************************************}
|
|
{* ABBREVIA: TAbSpan*Stream Classes *}
|
|
{*********************************************************}
|
|
{* Streams to handle splitting ZIP files or spanning *}
|
|
{* them to diskettes *}
|
|
{*********************************************************}
|
|
|
|
unit AbSpanSt;
|
|
|
|
{$I AbDefine.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes,
|
|
AbArcTyp;
|
|
|
|
type
|
|
{ TAbSpanBaseStream interface ============================================== }
|
|
TAbSpanBaseStream = class(TStream)
|
|
protected {private}
|
|
FArchiveName: string;
|
|
|
|
FOnRequestImage: TAbRequestImageEvent;
|
|
|
|
protected {methods}
|
|
function GetImageName( ImageNumber: Integer ): string;
|
|
|
|
public {methods}
|
|
constructor Create( const ArchiveName: string );
|
|
|
|
public {events}
|
|
property OnRequestImage : TAbRequestImageEvent
|
|
read FOnRequestImage
|
|
write FOnRequestImage;
|
|
end;
|
|
|
|
{ TAbSpanReadStream interface ============================================== }
|
|
TAbSpanReadStream = class(TAbSpanBaseStream)
|
|
protected {private}
|
|
FCurrentImage: LongWord;
|
|
FIsSplit: Boolean;
|
|
FLastImage: LongWord;
|
|
FStream: TStream;
|
|
|
|
FOnRequestNthDisk : TAbRequestNthDiskEvent;
|
|
|
|
protected {methods}
|
|
procedure GotoImage( ImageNumber: Integer );
|
|
procedure SetOnRequestImage(Value: TAbRequestImageEvent);
|
|
|
|
public {methods}
|
|
constructor Create( const ArchiveName: string; CurrentImage: LongWord;
|
|
Stream: TStream );
|
|
destructor Destroy;
|
|
override;
|
|
function Read(var Buffer; Count: Longint): Longint;
|
|
override;
|
|
function Write(const Buffer; Count: Longint): Longint;
|
|
override;
|
|
function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
|
|
override;
|
|
procedure SeekImage( Image: LongWord; const Offset: Int64);
|
|
|
|
public {events}
|
|
property OnRequestImage
|
|
write SetOnRequestImage;
|
|
property OnRequestNthDisk : TAbRequestNthDiskEvent
|
|
read FOnRequestNthDisk
|
|
write FOnRequestNthDisk;
|
|
end;
|
|
|
|
{ TAbSpanWriteStream interface ============================================= }
|
|
TAbSpanWriteStream = class(TAbSpanBaseStream)
|
|
protected {private}
|
|
FCurrentImage: LongWord;
|
|
FImageSize: Int64;
|
|
FStream: TStream;
|
|
FThreshold: Int64;
|
|
|
|
FOnRequestBlankDisk : TAbRequestDiskEvent;
|
|
|
|
protected {methods}
|
|
procedure NewImage;
|
|
|
|
public {methods}
|
|
constructor Create( const ArchiveName: string; Stream: TStream;
|
|
Threshold: Int64 );
|
|
destructor Destroy;
|
|
override;
|
|
function Read(var Buffer; Count: Longint): Longint;
|
|
override;
|
|
function Write(const Buffer; Count: Longint): Longint;
|
|
override;
|
|
function WriteUnspanned(const Buffer; Count: Longint;
|
|
FailOnSpan: Boolean = False): Boolean;
|
|
function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
|
|
override;
|
|
function ReleaseStream: TStream;
|
|
|
|
public {properties}
|
|
property CurrentImage : LongWord
|
|
read FCurrentImage;
|
|
|
|
public {events}
|
|
property OnRequestBlankDisk : TAbRequestDiskEvent
|
|
read FOnRequestBlankDisk
|
|
write FOnRequestBlankDisk;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
{$IFDEF MSWINDOWS}
|
|
Windows,
|
|
{$ENDIF}
|
|
Math, RTLConsts, SysUtils, AbUtils, AbExcept;
|
|
|
|
|
|
{============================================================================}
|
|
{ TAbSpanBaseStream implementation ========================================= }
|
|
constructor TAbSpanBaseStream.Create( const ArchiveName: string );
|
|
begin
|
|
inherited Create;
|
|
FArchiveName := ArchiveName;
|
|
end;
|
|
{------------------------------------------------------------------------------}
|
|
function TAbSpanBaseStream.GetImageName( ImageNumber: Integer ): string;
|
|
var
|
|
Abort : Boolean;
|
|
Ext : string;
|
|
begin
|
|
{generate default name}
|
|
Ext := ExtractFileExt(FArchiveName);
|
|
if (Length(Ext) < 2) then
|
|
Ext := '.' + Format('%.2d', [ImageNumber])
|
|
else
|
|
Ext := Ext[1] + Ext[2] + Format('%.2d', [ImageNumber]);
|
|
Result := ChangeFileExt(FArchiveName, Ext);
|
|
{call event}
|
|
if Assigned(FOnRequestImage) then begin
|
|
Abort := False;
|
|
FOnRequestImage(Self, ImageNumber, Result, Abort);
|
|
if Abort then
|
|
raise EAbUserAbort.Create;
|
|
end;
|
|
end;
|
|
|
|
{============================================================================}
|
|
{ TAbSpanReadStream implementation ========================================= }
|
|
constructor TAbSpanReadStream.Create( const ArchiveName: string;
|
|
CurrentImage: LongWord; Stream: TStream );
|
|
begin
|
|
inherited Create(ArchiveName);
|
|
FCurrentImage := CurrentImage;
|
|
FIsSplit := FileExists(GetImageName(1)) or not AbDriveIsRemovable(ArchiveName);
|
|
FLastImage := CurrentImage;
|
|
FStream := Stream;
|
|
end;
|
|
{------------------------------------------------------------------------------}
|
|
destructor TAbSpanReadStream.Destroy;
|
|
begin
|
|
FreeAndNil(FStream);
|
|
inherited;
|
|
end;
|
|
{------------------------------------------------------------------------------}
|
|
procedure TAbSpanReadStream.GotoImage( ImageNumber: Integer );
|
|
var
|
|
Abort: Boolean;
|
|
ImageName: string;
|
|
begin
|
|
{ switch to the requested image. ImageNumber is passed in as 0-based to
|
|
match the zip spec, but all of the callbacks receive 1-based values. }
|
|
FreeAndNil(FStream);
|
|
FCurrentImage := ImageNumber;
|
|
Inc(ImageNumber);
|
|
ImageName := FArchiveName;
|
|
if FIsSplit then begin
|
|
{ the last image uses the original filename }
|
|
if FCurrentImage <> FLastImage then
|
|
ImageName := GetImageName(ImageNumber)
|
|
end
|
|
else if Assigned(FOnRequestNthDisk) then begin
|
|
Abort := False;
|
|
repeat
|
|
FOnRequestNthDisk(Self, ImageNumber, Abort);
|
|
if Abort then
|
|
raise EAbUserAbort.Create;
|
|
until AbGetDriveFreeSpace(ImageName) <> -1;
|
|
end
|
|
else
|
|
raise EAbUserAbort.Create;
|
|
FStream := TFileStream.Create(ImageName, fmOpenRead or fmShareDenyWrite);
|
|
end;
|
|
{------------------------------------------------------------------------------}
|
|
function TAbSpanReadStream.Read(var Buffer; Count: Longint): Longint;
|
|
var
|
|
BytesRead, BytesLeft: LongInt;
|
|
PBuf: PByte;
|
|
begin
|
|
{ read until the buffer's full, switching images if necessary }
|
|
Result := 0;
|
|
if FStream = nil then
|
|
Exit;
|
|
PBuf := @Buffer;
|
|
BytesLeft := Count;
|
|
while Result < Count do begin
|
|
BytesRead := FStream.Read(PBuf^, BytesLeft);
|
|
Inc(Result, BytesRead);
|
|
Inc(PBuf, BytesRead);
|
|
Dec(BytesLeft, BytesRead);
|
|
if BytesRead < BytesLeft then begin
|
|
if FCurrentImage <> FLastImage then
|
|
GotoImage(FCurrentImage + 1)
|
|
else
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
{------------------------------------------------------------------------------}
|
|
function TAbSpanReadStream.Write(const Buffer; Count: Longint): Longint;
|
|
begin
|
|
raise EAbException.Create('TAbSpanReadStream.Write unsupported');
|
|
end;
|
|
{------------------------------------------------------------------------------}
|
|
function TAbSpanReadStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
|
|
begin
|
|
if FStream = nil then
|
|
Result := 0
|
|
else if (Offset = 0) and (Origin = soCurrent) then
|
|
Result := FStream.Position
|
|
else
|
|
raise EAbException.Create('TAbSpanReadStream.Seek unsupported');
|
|
end;
|
|
{------------------------------------------------------------------------------}
|
|
procedure TAbSpanReadStream.SeekImage( Image: LongWord; const Offset: Int64);
|
|
begin
|
|
if FStream = nil then
|
|
Exit;
|
|
if FCurrentImage <> Image then
|
|
GotoImage(Image);
|
|
FStream.Position := Offset;
|
|
end;
|
|
{------------------------------------------------------------------------------}
|
|
procedure TAbSpanReadStream.SetOnRequestImage(Value: TAbRequestImageEvent);
|
|
begin
|
|
FOnRequestImage := Value;
|
|
FIsSplit := FileExists(GetImageName(1)) or not AbDriveIsRemovable(FArchiveName);
|
|
end;
|
|
|
|
{============================================================================}
|
|
{ TAbSpanWriteStream implementation ======================================== }
|
|
constructor TAbSpanWriteStream.Create( const ArchiveName: string;
|
|
Stream: TStream; Threshold: Int64 );
|
|
begin
|
|
inherited Create(ArchiveName);
|
|
FCurrentImage := 0;
|
|
FStream := Stream;
|
|
FThreshold := Threshold;
|
|
end;
|
|
{------------------------------------------------------------------------------}
|
|
destructor TAbSpanWriteStream.Destroy;
|
|
begin
|
|
FStream.Free;
|
|
inherited;
|
|
end;
|
|
{------------------------------------------------------------------------------}
|
|
procedure TAbSpanWriteStream.NewImage;
|
|
var
|
|
Abort: Boolean;
|
|
begin
|
|
{ start a new span or blank disk. FCurrentImage is 0-based to match the zip
|
|
spec, but all of the callbacks receive 1-based values. }
|
|
FreeAndNil(FStream);
|
|
Inc(FCurrentImage);
|
|
if FThreshold > 0 then
|
|
RenameFile(FArchiveName, GetImageName(FCurrentImage))
|
|
else begin
|
|
if Assigned(FOnRequestBlankDisk) then begin
|
|
Abort := False;
|
|
repeat
|
|
FOnRequestBlankDisk(Self, Abort);
|
|
if Abort then
|
|
raise EAbUserAbort.Create;
|
|
until AbGetDriveFreeSpace(FArchiveName) <> -1;
|
|
end
|
|
else
|
|
raise EAbUserAbort.Create;
|
|
AbSetSpanVolumeLabel(AbDrive(FArchiveName), FCurrentImage);
|
|
end;
|
|
FStream := TFileStream.Create(FArchiveName, fmCreate or fmShareDenyWrite);
|
|
FImageSize := 0;
|
|
end;
|
|
{------------------------------------------------------------------------------}
|
|
function TAbSpanWriteStream.Read(var Buffer; Count: Longint): Longint;
|
|
begin
|
|
raise EAbException.Create('TAbSpanWriteStream.Read unsupported');
|
|
end;
|
|
{------------------------------------------------------------------------------}
|
|
function TAbSpanWriteStream.Write(const Buffer; Count: Longint): Longint;
|
|
var
|
|
BytesWritten, BytesLeft: LongInt;
|
|
PBuf: PByte;
|
|
begin
|
|
{ write until the buffer is done, starting new spans if necessary }
|
|
Result := 0;
|
|
if FStream = nil then
|
|
Exit;
|
|
PBuf := @Buffer;
|
|
BytesLeft := Count;
|
|
while Result < Count do begin
|
|
if FThreshold > 0 then
|
|
BytesWritten := FStream.Write(PBuf^, Min(BytesLeft, FThreshold - FImageSize))
|
|
else
|
|
BytesWritten := FStream.Write(PBuf^, BytesLeft);
|
|
Inc(FImageSize, BytesWritten);
|
|
Inc(Result, BytesWritten);
|
|
Inc(PBuf, BytesWritten);
|
|
Dec(BytesLeft, BytesWritten);
|
|
if BytesWritten < BytesLeft then
|
|
NewImage;
|
|
end;
|
|
end;
|
|
{------------------------------------------------------------------------------}
|
|
function TAbSpanWriteStream.WriteUnspanned(const Buffer; Count: Longint;
|
|
FailOnSpan: Boolean = False): Boolean;
|
|
var
|
|
BytesWritten: LongInt;
|
|
begin
|
|
{ write as a contiguous block, starting a new span if there isn't room.
|
|
FailOnSpan (and result = false) can be used to update data before it's
|
|
written again }
|
|
if FStream = nil then
|
|
raise EWriteError.Create(SWriteError);
|
|
if (FThreshold > 0) and (FThreshold - FImageSize < Count) then
|
|
BytesWritten := 0
|
|
else
|
|
BytesWritten := FStream.Write(Buffer, Count);
|
|
if BytesWritten < Count then begin
|
|
if BytesWritten > 0 then
|
|
FStream.Size := FStream.Size - BytesWritten;
|
|
NewImage;
|
|
if FailOnSpan then
|
|
BytesWritten := 0
|
|
else begin
|
|
BytesWritten := Count;
|
|
FStream.WriteBuffer(Buffer, Count);
|
|
end;
|
|
end;
|
|
Inc(FImageSize, BytesWritten);
|
|
Result := (BytesWritten = Count);
|
|
end;
|
|
{------------------------------------------------------------------------------}
|
|
function TAbSpanWriteStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
|
|
begin
|
|
if FStream = nil then
|
|
Result := 0
|
|
else if (Offset = 0) and (Origin = soCurrent) then
|
|
Result := FStream.Position
|
|
else
|
|
raise EAbException.Create('TAbSpanWriteStream.Seek unsupported');
|
|
end;
|
|
{------------------------------------------------------------------------------}
|
|
function TAbSpanWriteStream.ReleaseStream: TStream;
|
|
begin
|
|
Result := FStream;
|
|
FStream := nil;
|
|
end;
|
|
{------------------------------------------------------------------------------}
|
|
end.
|