820 lines
25 KiB
ObjectPascal
820 lines
25 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: AbCabTyp.pas *}
|
|
{*********************************************************}
|
|
{* ABBREVIA: Cabinet Archive *}
|
|
{* Based on info from the FCI/FDI Library Description, *}
|
|
{* included in the Microsoft Cabinet SDK *}
|
|
{*********************************************************}
|
|
|
|
unit AbCabTyp;
|
|
|
|
{$I AbDefine.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
Windows, Classes, AbFciFdi, AbArcTyp, AbUtils;
|
|
|
|
type
|
|
TAbCabItem = class(TAbArchiveItem)
|
|
protected {private}
|
|
FPartialFile : Boolean;
|
|
FRawFileName : AnsiString;
|
|
public
|
|
property PartialFile : Boolean
|
|
read FPartialFile
|
|
write FPartialFile;
|
|
property RawFileName : AnsiString
|
|
read FRawFileName
|
|
write FRawFileName;
|
|
end;
|
|
|
|
type
|
|
TAbCabCompressionType = (ctNone, ctMSZIP, ctLZX);
|
|
TAbCabinetMode = (cmRead, cmWrite);
|
|
TAbCabStatus = (csFile, csFolder, csCabinet);
|
|
|
|
|
|
const
|
|
faExtractAndExecute = $040;
|
|
faUTF8Name = $080;
|
|
AbDefCabSpanningThreshold = 0;
|
|
AbDefFolderThreshold = 0;
|
|
AbDefCompressionType = ctMSZIP;
|
|
AbDefReserveHeaderSize = 0;
|
|
AbDefReserveFolderSize = 0;
|
|
AbDefReserveDataSize = 0;
|
|
AbDefLZXWindowSize = 18;
|
|
|
|
CompressionTypeMap : array[TAbCabCompressionType] of Word = (0, 1, 4611);
|
|
|
|
type
|
|
TAbCabArchive = class(TAbArchive)
|
|
protected {private}
|
|
{internal variables}
|
|
FCabName : AnsiString;
|
|
FCabPath : AnsiString;
|
|
FFCICabInfo : FCICabInfo;
|
|
FFCIContext : HFCI;
|
|
FFDIContext : HFDI;
|
|
FFDICabInfo : FDICabInfo;
|
|
FErrors : CabErrorRecord;
|
|
FItemInProgress : TAbCabItem;
|
|
FItemStream : TStream;
|
|
FIIPName : string;
|
|
FItemProgress : DWord;
|
|
FNextCabinet : string;
|
|
FNextDisk : string;
|
|
FTempFileID : Integer;
|
|
|
|
{property variables}
|
|
FCurrentCab : Word;
|
|
FCabSize : Longint;
|
|
FCompressionType : TAbCabCompressionType;
|
|
FFileCount : Word;
|
|
FFolderThreshold : LongWord;
|
|
FFolderCount : Word;
|
|
FHasPrev : Boolean;
|
|
FHasNext : Boolean;
|
|
FSetID : Word;
|
|
|
|
{internal methods}
|
|
procedure CloseCabFile;
|
|
procedure CreateCabFile;
|
|
function CreateItem( const FileSpec : string ): TAbArchiveItem;
|
|
override;
|
|
procedure DoCabItemProgress(BytesCompressed : DWord;
|
|
var Abort : Boolean);
|
|
procedure DoGetNextCabinet(CabIndex : Integer; var CabName : string;
|
|
var Abort : Boolean);
|
|
procedure ExtractItemAt(Index : Integer; const NewName : string);
|
|
override;
|
|
procedure ExtractItemToStreamAt(Index : Integer; OutStream : TStream);
|
|
override;
|
|
function GetItem(ItemIndex : Integer) : TAbCabItem;
|
|
procedure LoadArchive;
|
|
override;
|
|
procedure OpenCabFile;
|
|
procedure PutItem( Index : Integer; Value : TAbCabItem );
|
|
procedure SaveArchive;
|
|
override;
|
|
procedure SetFolderThreshold(Value : LongWord);
|
|
procedure SetSetID(Value : Word);
|
|
procedure SetSpanningThreshold(Value : Int64);
|
|
override;
|
|
procedure TestItemAt(Index : Integer);
|
|
override;
|
|
|
|
public {methods}
|
|
constructor Create(const FileName : string; Mode : Word);
|
|
override;
|
|
constructor CreateFromStream(aStream : TStream; const aArchiveName : string);
|
|
override;
|
|
destructor Destroy;
|
|
override;
|
|
procedure Add(aItem : TAbArchiveItem);
|
|
override;
|
|
procedure NewCabinet;
|
|
procedure NewFolder;
|
|
|
|
public {properties}
|
|
property CurrentCab : Word
|
|
read FCurrentCab;
|
|
property CabSize : Longint
|
|
read FCabSize;
|
|
property CompressionType : TAbCabCompressionType
|
|
read FCompressionType
|
|
write FCompressionType;
|
|
property FolderThreshold : LongWord
|
|
read FFolderThreshold
|
|
write SetFolderThreshold;
|
|
property FolderCount : Word
|
|
read FFolderCount;
|
|
property HasPrev : Boolean
|
|
read FHasPrev;
|
|
property HasNext : Boolean
|
|
read FHasNext;
|
|
property Items[Index : Integer] : TAbCabItem
|
|
read GetItem
|
|
write PutItem; default;
|
|
property ItemProgress : DWord
|
|
read FItemProgress
|
|
write FItemProgress;
|
|
property SetID : Word
|
|
read FSetID
|
|
write SetSetID;
|
|
end;
|
|
|
|
function VerifyCab(const Fn : string) : TAbArchiveType; overload;
|
|
function VerifyCab(Strm : TStream) : TAbArchiveType; overload;
|
|
|
|
implementation
|
|
|
|
uses
|
|
SysUtils,
|
|
{$IFDEF HasAnsiStrings}
|
|
System.AnsiStrings,
|
|
{$ENDIF}
|
|
AbCharset, AbConst, AbExcept;
|
|
|
|
{$WARN UNIT_PLATFORM OFF}
|
|
{$WARN SYMBOL_PLATFORM OFF}
|
|
|
|
type
|
|
PWord = ^Word;
|
|
PInteger = ^Integer;
|
|
|
|
{ == FDI/FCI Callback Functions - cdecl calling convention ================= }
|
|
function FXI_GetMem(uBytes : Integer) : Pointer;
|
|
cdecl;
|
|
{allocate memory}
|
|
begin
|
|
Result := nil;
|
|
if (uBytes > 0) then
|
|
GetMem(Result, uBytes);
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
procedure FXI_FreeMem(lpBuffer : Pointer);
|
|
cdecl;
|
|
{free memory}
|
|
begin
|
|
FreeMem(lpBuffer);
|
|
end;
|
|
|
|
|
|
{ == FCI Callback Functions - cdecl calling convention ===================== }
|
|
function FCI_FileOpen(lpPathName: PAnsiChar; Flag, Mode: Integer;
|
|
PError: PInteger; Archive: TAbCabArchive) : PtrInt;
|
|
cdecl;
|
|
{open a file}
|
|
begin
|
|
Result := _lcreat(lpPathName, 0);
|
|
if (Result = -1) then
|
|
raise EAbFCIFileOpenError.Create;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function FCI_FileRead(hFile: PtrInt; lpBuffer: Pointer;
|
|
uBytes: UINT; PError: PInteger; Archive: TAbCabArchive) : UINT;
|
|
cdecl;
|
|
{read from a file}
|
|
begin
|
|
Result := _lread(hFile, lpBuffer, uBytes);
|
|
if (Result = UINT(-1)) then
|
|
raise EAbFCIFileReadError.Create;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function FCI_FileWrite(hFile: PtrInt; lpBuffer: Pointer;
|
|
uBytes: UINT; PError: PInteger; Archive: TAbCabArchive) : UINT;
|
|
cdecl;
|
|
{write to a file}
|
|
begin
|
|
Result := _lwrite(hFile, lpBuffer, uBytes);
|
|
if (Result = UINT(-1)) then
|
|
raise EAbFCIFileWriteError.Create;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function FCI_FileClose(hFile: PtrInt; PError: PInteger;
|
|
Archive: TAbCabArchive) : Integer;
|
|
cdecl;
|
|
{close a file}
|
|
begin
|
|
Result := _lclose(hFile);
|
|
if (Result = -1) then
|
|
raise EAbFCIFileCloseError.Create;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function FCI_FileSeek(hFile: PtrInt; Offset: Longint;
|
|
Origin: Integer; PError: PInteger; Archive: TAbCabArchive) : Longint;
|
|
cdecl;
|
|
{reposition file pointer}
|
|
begin
|
|
Result := _llseek(hFile, Offset, Origin);
|
|
if (Result = -1) then
|
|
raise EAbFCIFileSeekError.Create;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function FCI_FileDelete(lpFilename: PAnsiChar; PError: PInteger;
|
|
Archive: TAbCabArchive) : Boolean;
|
|
cdecl;
|
|
{delete a file}
|
|
begin
|
|
Result := SysUtils.DeleteFile(string(lpFilename));
|
|
if not Result then
|
|
raise EAbFCIFileDeleteError.Create;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function FCI_GetNextCab(lpCCab: PFCICabInfo; PrevCab: Longint;
|
|
Archive: TAbCabArchive) : Boolean;
|
|
cdecl;
|
|
{get next cabinet filename}
|
|
var
|
|
CabName : string;
|
|
Abort : Boolean;
|
|
begin
|
|
Abort := False;
|
|
with lpCCab^ do begin
|
|
CabName := string(szCab);
|
|
{obtain next cabinet. Make index zero-based}
|
|
Archive.DoGetNextCabinet(Pred(iCab), CabName, Abort);
|
|
if not Abort then
|
|
AbStrPLCopy(szCab, AnsiString(CabName), Length(szCab));
|
|
end;
|
|
Result := not Abort;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function FCI_FileDest(PCCab: PFCICabInfo; PFilename: PAnsiChar; cbFile: Longint;
|
|
Continuation: Boolean; Archive: TAbCabArchive) : Integer;
|
|
cdecl;
|
|
{currently not used}
|
|
begin
|
|
Result := 0;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function FCI_GetOpenInfo(lpPathname: Pointer; PDate, PTime, PAttribs : PWord;
|
|
PError: PInteger; Archive: TAbCabArchive) : PtrInt;
|
|
cdecl;
|
|
{open a file and return date/attributes}
|
|
var
|
|
AttrEx: TAbAttrExRec;
|
|
I, DT: Integer;
|
|
RawName: RawByteString;
|
|
begin
|
|
Result := FileOpen(string(lpPathname), fmOpenRead or fmShareDenyNone);
|
|
if (Result = -1) then
|
|
raise EAbFCIFileOpenError.Create;
|
|
if not AbFileGetAttrEx(string(lpPathname), AttrEx) then
|
|
raise EAbFileNotFound.Create;
|
|
PAttribs^ := AttrEx.Attr;
|
|
DT := DateTimeToFileDate(AttrEx.Time);
|
|
PDate^ := DT shr 16;
|
|
PTime^ := DT and $0FFFF;
|
|
Archive.ItemProgress := 0;
|
|
Archive.FItemInProgress.UncompressedSize := AttrEx.Size;
|
|
RawName := Archive.FItemInProgress.RawFileName;
|
|
for I := 1 to Length(RawName) do
|
|
if Ord(RawName[I]) > 127 then
|
|
PAttribs^ := PAttribs^ or faUTF8Name;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function FCI_Status(Status: Word; cb1, cb2: DWord;
|
|
Archive: TAbCabArchive) : Longint; cdecl;
|
|
{keep archive informed}
|
|
var
|
|
Abort : Boolean;
|
|
begin
|
|
Result := 0;
|
|
if (Status = Word(csCabinet)) then begin
|
|
Archive.DoSave;
|
|
Archive.FCabSize := cb2;
|
|
Result := cb2;
|
|
end else if (Status = Word(csFolder)) then
|
|
Archive.FCabSize := Archive.FCabSize + Longint(cb2)
|
|
else if (Status = Word(csFile)) then begin
|
|
Archive.DoCabItemProgress(cb2, Abort);
|
|
Result := Longint(Abort);
|
|
end;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function FCI_GetTempFile(lpTempName: PAnsiChar; TempNameSize: Integer;
|
|
Archive: TAbCabArchive) : PtrInt; cdecl;
|
|
{obtain temporary filename}
|
|
var
|
|
TempPath : array[0..255] of AnsiChar;
|
|
begin
|
|
Archive.FTempFileID := Archive.FTempFileID + 1;
|
|
if (Archive.TempDirectory <> '') then
|
|
AbStrPLCopy(TempPath, AnsiString(Archive.TempDirectory), Length(TempPath))
|
|
else
|
|
GetTempPathA(255, TempPath);
|
|
GetTempFileNameA(TempPath, 'VMS', Archive.FTempFileID, lpTempName);
|
|
Result := 1;
|
|
end;
|
|
|
|
{ == FDI Callback Functions - cdecl calling convention ===================== }
|
|
function FDI_FileOpen(lpPathName: PAnsiChar; Flag, Mode: Integer) : PtrInt;
|
|
cdecl;
|
|
{open a file}
|
|
begin
|
|
try
|
|
Result := PtrInt(TFileStream.Create(string(lpPathName), fmOpenRead or fmShareDenyWrite));
|
|
except on EFOpenError do
|
|
Result := -1;
|
|
end;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function FDI_FileRead(hFile: PtrInt; lpBuffer: Pointer; uBytes: UINT) : UINT;
|
|
cdecl;
|
|
{read from a file}
|
|
begin
|
|
Result := TStream(hFile).Read(lpBuffer^, uBytes);
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function FDI_FileWrite(hFile: PtrInt; lpBuffer: Pointer; uBytes: UINT) : UINT;
|
|
cdecl;
|
|
{write to a file}
|
|
begin
|
|
Result := TStream(hFile).Write(lpBuffer^, uBytes);
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function FDI_FileClose(hFile : PtrInt) : Longint;
|
|
cdecl;
|
|
{close a file}
|
|
begin
|
|
try
|
|
TStream(hFile).Free;
|
|
Result := 0;
|
|
except
|
|
Result := -1;
|
|
end;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function FDI_FileSeek(hFile : PtrInt; Offset : Longint; Origin : Integer) : Longint;
|
|
cdecl;
|
|
{reposition file pointer}
|
|
begin
|
|
Result := TStream(hFile).Seek(Offset, Origin);
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function FDI_EnumerateFiles(fdint : FDINOTIFICATIONTYPE;
|
|
pfdin : PFDINotification) : PtrInt;
|
|
cdecl;
|
|
{Enumerate the files and build the archive file list}
|
|
var
|
|
Item : TAbCabItem;
|
|
Archive : TAbCabArchive;
|
|
begin
|
|
Result := 0;
|
|
Archive := pfdin^.pv;
|
|
with Archive do case fdint of
|
|
FDINT_Cabinet_Info :
|
|
begin
|
|
FSetID := pfdin^.setID;
|
|
FCurrentCab := pfdin^.iCabinet;
|
|
FNextCabinet := string(pfdin^.psz1);
|
|
FNextDisk := string(pfdin^.psz2);
|
|
Result := 0;
|
|
end;
|
|
FDINT_Copy_File, FDINT_Partial_File :
|
|
begin
|
|
Item := TAbCabItem.Create;
|
|
with Item do begin
|
|
RawFileName := AnsiString(pfdin^.psz1);
|
|
if (pfdin^.attribs and faUTF8Name) = faUTF8Name then
|
|
Filename := UTF8ToString(RawFileName)
|
|
else
|
|
Filename := string(RawFileName);
|
|
UnCompressedSize := pfdin^.cb;
|
|
LastModFileDate := pfdin^.date;
|
|
LastModFileTime := pfdin^.time;
|
|
ExternalFileAttributes := pfdin^.attribs;
|
|
IsEncrypted := False; {encryption not implemented at this time}
|
|
PartialFile := (fdint = FDINT_Partial_File);
|
|
end;
|
|
FItemList.Add(Item);
|
|
Result := 0;
|
|
end;
|
|
end;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function FDI_ExtractFiles(fdint : FDINOTIFICATIONTYPE;
|
|
pfdin : PFDINotification) : PtrInt;
|
|
cdecl;
|
|
{extract file from cabinet}
|
|
var
|
|
Archive : TAbCabArchive;
|
|
begin
|
|
Result := 0;
|
|
Archive := pfdin^.pv;
|
|
case fdint of
|
|
FDINT_Copy_File :
|
|
begin
|
|
if (AnsiString(pfdin^.psz1) = Archive.FItemInProgress.RawFileName) then
|
|
if Archive.FIIPName <> '' then
|
|
Result := Integer(TFileStream.Create(Archive.FIIPName, fmCreate))
|
|
else
|
|
Result := Integer(Archive.FItemStream)
|
|
else
|
|
Result := 0;
|
|
end;
|
|
FDINT_Next_Cabinet :
|
|
begin
|
|
if pfdin^.fdie = FDIError_None then
|
|
Result := 0
|
|
else
|
|
Result := -1;
|
|
end;
|
|
FDINT_Close_File_Info :
|
|
begin
|
|
if Archive.FIIPName <> '' then begin
|
|
FileSetDate(TFileStream(pfdin^.hf).Handle,
|
|
Longint(pfdin^.date) shl 16 + pfdin^.time);
|
|
TFileStream(pfdin^.hf).Free;
|
|
FileSetAttr(Archive.FIIPName, pfdin^.attribs);
|
|
end;
|
|
Result := 1;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
{ == TAbCabArchive ========================================================= }
|
|
function VerifyCab(const Fn : string) : TAbArchiveType;
|
|
var
|
|
Stream : TFileStream;
|
|
begin
|
|
Stream := TFileStream.Create(FN, fmOpenRead or fmShareDenyNone);
|
|
try
|
|
Result := VerifyCab(Stream);
|
|
finally
|
|
Stream.Free;
|
|
end;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function VerifyCab(Strm : TStream) : TAbArchiveType; overload;
|
|
var
|
|
Context : HFDI;
|
|
Info : FDICabInfo;
|
|
Errors : CabErrorRecord;
|
|
StartPos : int64;
|
|
begin
|
|
Result := atUnknown;
|
|
Context := FDICreate(@FXI_GetMem, @FXI_FreeMem, @FDI_FileOpen,
|
|
@FDI_FileRead, @FDI_FileWrite, @FDI_FileClose, @FDI_FileSeek, cpuDefault,
|
|
@Errors);
|
|
if Context = nil then
|
|
Exit;
|
|
try
|
|
StartPos := Strm.Position;
|
|
if FDIIsCabinet(Context, Integer(Strm), @Info) then
|
|
Result := atCab;
|
|
Strm.Position := StartPos;
|
|
finally
|
|
FDIDestroy(Context);
|
|
end;
|
|
end;
|
|
|
|
|
|
{ == TAbCabArchive ========================================================= }
|
|
constructor TAbCabArchive.Create(const FileName : string; Mode : Word );
|
|
begin
|
|
{Mode is used to identify which interface to use: }
|
|
{ fmOpenWrite - FCI, fmOpenRead - FDI}
|
|
inherited CreateInit;
|
|
if (Mode and fmCreate) = fmCreate then FMode := fmOpenWrite
|
|
else FMode := Mode and fmOpenWrite;
|
|
FArchiveName := FileName;
|
|
FCabName := AnsiString(ExtractFileName(FileName));
|
|
FCabPath := AnsiString(ExtractFilePath(FileName));
|
|
SpanningThreshold := AbDefCabSpanningThreshold;
|
|
FFolderThreshold := AbDefFolderThreshold;
|
|
FItemInProgress := nil;
|
|
FItemProgress := 0;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
constructor TAbCabArchive.CreateFromStream(aStream : TStream;
|
|
const aArchiveName : string);
|
|
begin
|
|
raise EAbCabException.Create('TAbCabArchive does not support CreateFromStream');
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
destructor TAbCabArchive.Destroy;
|
|
begin
|
|
CloseCabFile;
|
|
inherited Destroy;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
procedure TAbCabArchive.Add(aItem : TAbArchiveItem);
|
|
{add a file to the cabinet}
|
|
var
|
|
Confirm, ItemAdded : Boolean;
|
|
Item : TAbCabItem;
|
|
begin
|
|
ItemAdded := False;
|
|
try
|
|
CheckValid;
|
|
if (FMode <> fmOpenWrite) then begin
|
|
DoProcessItemFailure(aItem, ptAdd, ecCabError, 0);
|
|
Exit;
|
|
end;
|
|
if FItemList.IsActiveDupe(aItem.FileName) then begin
|
|
DoProcessItemFailure(aItem, ptAdd, ecAbbrevia, AbDuplicateName);
|
|
Exit;
|
|
end;
|
|
DoConfirmProcessItem(aItem, ptAdd, Confirm);
|
|
if not Confirm then
|
|
Exit;
|
|
Item := TAbCabItem(aItem);
|
|
FItemInProgress := Item;
|
|
Item.Action := aaAdd;
|
|
|
|
Item.RawFileName := UTF8Encode(Item.FileName);
|
|
if not FCIAddFile(FFCIContext, Pointer(Item.DiskFileName),
|
|
PAnsiChar(Item.RawFileName), False, @FCI_GetNextCab, @FCI_Status,
|
|
@FCI_GetOpenInfo, CompressionTypeMap[FCompressionType]) then
|
|
raise EAbFCIAddFileError.Create;
|
|
FItemList.Add(Item);
|
|
ItemAdded := True;
|
|
|
|
FIsDirty := True;
|
|
finally
|
|
if not ItemAdded then
|
|
aItem.Free;
|
|
end;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
procedure TAbCabArchive.CloseCabFile;
|
|
{Make sure the Cabinet DLL is shut down}
|
|
var
|
|
Abort : Boolean;
|
|
begin
|
|
if (FFDIContext <> nil) then begin
|
|
FDIDestroy(FFDIContext);
|
|
FFDIContext := nil;
|
|
end;
|
|
if (FFCIContext <> nil) then begin
|
|
FCIFlushCabinet(FFCIContext, False, @FCI_GetNextCab, @FCI_Status);
|
|
FCIDestroy(FFCIContext);
|
|
FFCIContext := nil;
|
|
end;
|
|
DoArchiveProgress(0, Abort);
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
procedure TAbCabArchive.CreateCabFile;
|
|
{create a new cabinet}
|
|
begin
|
|
{set cabinet parameters}
|
|
with FFCICabInfo do begin
|
|
if (SpanningThreshold > 0) then
|
|
cb := SpanningThreshold
|
|
else
|
|
cb := AbDefCabSpanningThreshold;
|
|
if (FolderThreshold > 0) then
|
|
cbFolderThresh := FolderThreshold
|
|
else
|
|
cbFolderThresh := AbDefFolderThreshold;
|
|
cbReserveCFHeader := AbDefReserveHeaderSize;
|
|
cbReserveCFFolder := AbDefReserveFolderSize;
|
|
cbReserveCFData := AbDefReserveDataSize;
|
|
iCab := 1;
|
|
iDisk := 0;
|
|
fFailOnIncompressible := 0;
|
|
setID := SetID;
|
|
AbStrPCopy(szDisk, '');
|
|
AbStrPLCopy(szCab, FCabName, Length(szCab));
|
|
AbStrPLCopy(szCabPath, FCabPath, Length(szCabPath));
|
|
end;
|
|
|
|
{obtain an FCI context}
|
|
FFCIContext := FCICreate(@FErrors, @FCI_FileDest, @FXI_GetMem, @FXI_FreeMem,
|
|
@FCI_FileOpen, @FCI_FileRead, @FCI_FileWrite, @FCI_FileClose, @FCI_FileSeek,
|
|
@FCI_FileDelete, @FCI_GetTempFile, @FFCICabInfo, Self);
|
|
if (FFCIContext = nil) then
|
|
if FErrors.ErrorPresent then begin
|
|
CloseCabFile;
|
|
raise EAbFCICreateError.Create;
|
|
end;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
function TAbCabArchive.CreateItem( const FileSpec : string ): TAbArchiveItem;
|
|
{create a new item for the file list}
|
|
begin
|
|
Result := TAbCabItem.Create;
|
|
with TAbCabItem(Result) do begin
|
|
CompressedSize := 0;
|
|
DiskFileName := ExpandFileName(FileSpec);
|
|
FileName := FixName(FileSpec);
|
|
end;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
procedure TAbCabArchive.DoCabItemProgress(BytesCompressed : DWord;
|
|
var Abort : Boolean);
|
|
{fire OnCabItemProgress event}
|
|
var
|
|
Progress : Byte;
|
|
begin
|
|
Abort := False;
|
|
if Assigned(FOnArchiveItemProgress) then begin
|
|
Inc(FItemProgress, BytesCompressed);
|
|
Progress := AbPercentage(FItemProgress,
|
|
FItemInProgress.UnCompressedSize);
|
|
FOnArchiveItemProgress(Self, FItemInProgress, Progress, Abort);
|
|
end;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
procedure TAbCabArchive.DoGetNextCabinet(CabIndex : Integer;
|
|
var CabName : string; var Abort : Boolean);
|
|
{fire OnRequestImage event}
|
|
begin
|
|
Abort := False;
|
|
if Assigned(FOnRequestImage) then
|
|
FOnRequestImage(Self, CabIndex, CabName, Abort)
|
|
else
|
|
AbIncFilename(CabName, CabIndex);
|
|
end;
|
|
{----------------------------------------------------------------------------}
|
|
procedure TAbCabArchive.ExtractItemAt(Index : Integer; const NewName : string);
|
|
{extract a file from the cabinet}
|
|
begin
|
|
FItemInProgress := GetItem(Index);
|
|
FIIPName := NewName;
|
|
try
|
|
if not FDICopy(FFDIContext, PAnsiChar(FCabName), PAnsiChar(FCabPath), 0,
|
|
@FDI_ExtractFiles, nil, Self) then
|
|
DoProcessItemFailure(FItemInProgress, ptExtract, ecCabError, FErrors.ErrorCode);
|
|
finally
|
|
FIIPName := '';
|
|
end;
|
|
end;
|
|
{----------------------------------------------------------------------------}
|
|
procedure TAbCabArchive.ExtractItemToStreamAt(Index : Integer; OutStream : TStream);
|
|
begin
|
|
FItemInProgress := GetItem(Index);
|
|
FItemStream := OutStream;
|
|
try
|
|
if not FDICopy(FFDIContext, PAnsiChar(FCabName), PAnsiChar(FCabPath), 0,
|
|
@FDI_ExtractFiles, nil, Self) then
|
|
DoProcessItemFailure(FItemInProgress, ptExtract, ecCabError, FErrors.ErrorCode);
|
|
finally
|
|
FItemStream := nil;
|
|
end;
|
|
end;
|
|
{----------------------------------------------------------------------------}
|
|
function TAbCabArchive.GetItem(ItemIndex : Integer) : TAbCabItem;
|
|
{fetch an item from the file list}
|
|
begin
|
|
Result := TAbCabItem(FItemList.Items[ItemIndex]);
|
|
end;
|
|
{----------------------------------------------------------------------------}
|
|
procedure TAbCabArchive.LoadArchive;
|
|
{Open existing cabinet or create a new one}
|
|
begin
|
|
if (FMode = fmOpenRead) then begin
|
|
FFDIContext := FDICreate(@FXI_GetMem, @FXI_FreeMem, @FDI_FileOpen,
|
|
@FDI_FileRead, @FDI_FileWrite, @FDI_FileClose, @FDI_FileSeek,
|
|
cpuDefault, @FErrors);
|
|
if (FFDIContext = nil) then
|
|
raise EAbFDICreateError.Create;
|
|
OpenCabFile;
|
|
end else
|
|
CreateCabFile;
|
|
end;
|
|
{----------------------------------------------------------------------------}
|
|
procedure TAbCabArchive.NewCabinet;
|
|
{flush current cabinet and start a new one}
|
|
begin
|
|
if not FCIFlushCabinet(FFCIContext, True, @FCI_GetNextCab, @FCI_Status) then
|
|
raise EAbFCIFlushCabinetError.Create;
|
|
end;
|
|
{----------------------------------------------------------------------------}
|
|
procedure TAbCabArchive.NewFolder;
|
|
{flush current folder and start a new one}
|
|
begin
|
|
if not FCIFlushFolder(FFCIContext, @FCI_GetNextCab, @FCI_Status) then
|
|
raise EAbFCIFlushFolderError.Create;
|
|
end;
|
|
{----------------------------------------------------------------------------}
|
|
procedure TAbCabArchive.OpenCabFile;
|
|
{Open an existing cabinet}
|
|
var
|
|
Abort : Boolean;
|
|
Stream : TFileStream;
|
|
begin
|
|
{verify that the archive can be opened and is a cabinet}
|
|
Stream := TFileStream.Create(FArchiveName, fmOpenRead or fmShareDenyNone);
|
|
try
|
|
if not FDIIsCabinet(FFDIContext, PtrInt(Stream), @FFDICabInfo) then begin
|
|
CloseCabFile;
|
|
raise EAbInvalidCabFile.Create;
|
|
end;
|
|
finally
|
|
Stream.Free;
|
|
end;
|
|
|
|
{store information about the cabinet}
|
|
FCabSize := FFDICabInfo.cbCabinet;
|
|
FFolderCount := FFDICabInfo.cFolders;
|
|
FFileCount := FFDICabInfo.cFiles;
|
|
FCurrentCab := FFDICabInfo.iCabinet;
|
|
FHasPrev := FFDICabInfo.hasPrev;
|
|
FHasNext := FFDICabInfo.hasNext;
|
|
|
|
{Enumerate the files and build the file list}
|
|
if not FDICopy(FFDIContext, PAnsiChar(FCabName), PAnsiChar(FCabPath), 0,
|
|
@FDI_EnumerateFiles, nil, Self) then begin
|
|
CloseCabFile;
|
|
raise EAbFDICopyError.Create;
|
|
end;
|
|
DoArchiveProgress(100, Abort);
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
procedure TAbCabArchive.PutItem( Index : Integer; Value : TAbCabItem );
|
|
{replace an existing item in the file list}
|
|
begin
|
|
FItemList.Items[Index] := Value;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
procedure TAbCabArchive.SaveArchive;
|
|
begin
|
|
{ No-op; file is flushed in destructor }
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
procedure TAbCabArchive.SetFolderThreshold(Value : LongWord);
|
|
{set maximum compression boundary}
|
|
begin
|
|
if (Value > 0) then
|
|
FFolderThreshold := Value
|
|
else
|
|
FFolderThreshold := AbDefFolderThreshold;
|
|
FFCICabInfo.cbFolderThresh := FFolderThreshold;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
procedure TAbCabArchive.SetSetID(Value : Word);
|
|
{set cabinet SetID}
|
|
begin
|
|
FSetID := Value;
|
|
FFCICabInfo.SetID := Value;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
procedure TAbCabArchive.SetSpanningThreshold(Value : Int64);
|
|
{set maximum cabinet size}
|
|
begin
|
|
if (Value > 0) then
|
|
FSpanningThreshold := Value
|
|
else
|
|
FSpanningThreshold := AbDefCabSpanningThreshold;
|
|
FFCICabInfo.cb := FSpanningThreshold;
|
|
end;
|
|
{ -------------------------------------------------------------------------- }
|
|
procedure TAbCabArchive.TestItemAt(Index : Integer);
|
|
begin
|
|
{not implemented for cabinet archives}
|
|
end;
|
|
|
|
end.
|