home *** CD-ROM | disk | FTP | other *** search
/ Chip 2003 January / Chip_2003-01_cd1.bin / zkuste / delphi / kompon / d56 / CABD.ZIP / Code / CabSTComps.pas < prev    next >
Pascal/Delphi Source File  |  2002-08-27  |  41KB  |  1,345 lines

  1. unit CabSTComps;
  2.  
  3. (*
  4.  *  Copyright (c) Ravil Batyrshin, 2001-2002
  5.  *  All Rights Reserved
  6.  *  Version 1.20
  7.  *  Aravil Software
  8.  *  web site: aravilsoft.tripod.com
  9.  *  e-mail: aravilsoft@bigfoot.com, wizeman@mail.ru
  10.  *)
  11.  
  12. {$IFNDEF VER130}
  13.     {$WARN SYMBOL_PLATFORM OFF}
  14. {$ENDIF}
  15. {$RANGECHECKS OFF}
  16. {$BOOLEVAL OFF}
  17.  
  18. interface
  19.  
  20. uses
  21.     Windows, ShellApi, SysUtils, Classes, CabIntf
  22.     {$IFDEF VER130}, FileCtrl {$ENDIF};
  23.  
  24. type
  25.     TSTCabCompressionType = (
  26.         cctNone,
  27.         cctMsZip,
  28.         cctLzx
  29.     );
  30.  
  31.     TSTCabLzxLevel = 15..21;
  32.  
  33. const
  34.     DefCabCompressionType = cctLzx;
  35.     DefCabLzxLevel = 21;
  36.  
  37. type
  38.     TSTCabWriter = class;
  39.     TSTCabReader = class;
  40.  
  41.     TSTCabAddFileOption = (
  42.         cafoExecuteOnExtract
  43.     );
  44.  
  45.     TSTCabAddFileOptions = set of TSTCabAddFileOption;
  46.  
  47.     TSTCabAddFilesOption = (
  48.         cafsoRecurseSubdirs
  49.     );
  50.  
  51.     TSTCabAddFilesOptions = set of TSTCabAddFilesOption;
  52.  
  53.     TSTCabExtractFilesOption = (
  54.         cefsoProcessOnlyOneVolume
  55.     );
  56.  
  57.     TSTCabExtractFilesOptions = set of TSTCabExtractFilesOption;
  58.  
  59.     ESTCabinet = class(Exception)
  60.     private
  61.         FCabError: Integer; // FCI/FDI error code
  62.         FWinError: DWORD; // Optional Win32 error code
  63.         FErrorPresent: Boolean;
  64.         FIsCompressing: Boolean;
  65.     public
  66.         constructor Create(const anERF: TERF; anIsCompressing: Boolean);
  67.  
  68.         property CabError: Integer read FCabError;
  69.         property WinError: DWORD read FWinError;
  70.         property ErrorPresent: Boolean read FErrorPresent;
  71.         property IsCompressing: Boolean read FIsCompressing;
  72.     end;
  73.  
  74.     TSTCabWriterFilePlacedEvent = procedure(Sender: TSTCabWriter;
  75.         const aCCab: TCCAB; const aFileName: String; aFileSize: Longint;
  76.         aContinuation: Boolean; var anAbort: Boolean) of object;
  77.     TSTCabWriterGetNextCabinetEvent = procedure(Sender: TSTCabWriter;
  78.         var aCCab: TCCAB; aPrevCabSize: ULONG; var anAbort: Boolean) of object;
  79.     TSTCabWriterFileStatusEvent = procedure(Sender: TSTCabWriter;
  80.         aCompressedBlockSize, anUncompressedBlockSize: ULONG; var anAbort: Boolean) of object;
  81.     TSTCabWriterFolderStatusEvent = procedure(Sender: TSTCabWriter;
  82.         aCopiedSize, aTotalSize: ULONG; var anAbort: Boolean) of object;
  83.     TSTCabWriterCabinetStatusEvent = procedure(Sender: TSTCabWriter;
  84.         aPrevEstimatedSize, anActualSize: ULONG; var aDesiredSize: ULONG;
  85.         var anAbort: Boolean) of object;
  86.     TSTCabWriterProgressEvent = procedure(Sender: TSTCabWriter;
  87.         var anAbort: Boolean) of object;
  88.     TSTCabWriterBeforeFileAddEvent = procedure(Sender: TSTCabWriter;
  89.         const aFileName: String; var aSkip: Boolean) of object;
  90.  
  91.     TSTCabWriter = class(TComponent)
  92.     private
  93.         FHandle: HFCI;
  94.         FFileName: String;
  95.         FCabinetSizeThreshold: ULONG;
  96.         FCabinetFileCountThreshold: ULONG;
  97.         FFolderSizeThreshold: ULONG;
  98.         FFolderFileCountThreshold: ULONG;
  99.         FReservePerCabinetSize: UINT;
  100.         FReservePerFolderSize: UINT;
  101.         FReservePerDataSize: UINT;
  102.         FSetID: USHORT;
  103.         FCabinetNameTemplate: String;
  104.         FCabinetPathTemplate: String;
  105.         FDiskLabelTemplate: String;
  106.         FCompressionType: TSTCabCompressionType;
  107.         FCompressionLzxLevel: TSTCabLzxLevel;
  108.  
  109.         FOnFilePlaced: TSTCabWriterFilePlacedEvent;
  110.         FOnGetNextCabinet: TSTCabWriterGetNextCabinetEvent;
  111.         FOnFileStatus: TSTCabWriterFileStatusEvent;
  112.         FOnFolderStatus: TSTCabWriterFolderStatusEvent;
  113.         FOnCabinetStatus: TSTCabWriterCabinetStatusEvent;
  114.         FOnProgress: TSTCabWriterProgressEvent;
  115.         FOnBeforeFileAdd: TSTCabWriterBeforeFileAddEvent;
  116.  
  117.         procedure SetCompressionType(aValue: TSTCabCompressionType);
  118.         procedure SetCompressionLzxLevel(aValue: TSTCabLzxLevel);
  119.     protected
  120.         FCabComp: TCOMP;
  121.         FERF: TERF;
  122.         FOriginalCCAB: TCCAB;
  123.         FFileCountPerCabinet: ULONG;
  124.         FFileCountPerFolder: ULONG;
  125.         FProgressCount: Integer;
  126.  
  127.         procedure CabCheck(aResult: Boolean);
  128.         function GetCabinetName(aCabNo: Integer): String;
  129.         function GetCabinetPath(aDiskNo: Integer): String;
  130.         function GetDiskLabel(aDiskNo: Integer): String;
  131.         procedure SyncCabComp;
  132.         procedure FlushCabinet(aGetNextCab: Boolean);
  133.         // Callbacks
  134.         function DoFilePlaced(pccab: PCCAB; pszFile: PAnsiChar;
  135.             cbFile: Longint; fContinuation: WIN_BOOL): Boolean;
  136.         function DoGetNextCabinet(pccab: PCCAB; cbPrevCab: ULONG): Boolean;
  137.         function DoStatus(typeStatus: UINT; cb1: ULONG; cb2: ULONG): Longint;
  138.     public
  139.         constructor Create(aOwner: TComponent); override;
  140.         destructor Destroy; override;
  141.  
  142.         procedure Open(const aFileName: String);
  143.         procedure Close(aFlushCab: Boolean = True);
  144.         procedure AddFile(const aSrcFileName, aDstFileName: String;
  145.             anOptions: TSTCabAddFileOptions = []);
  146.         procedure AddFiles(const aSrcPath, aDstPath: String;
  147.             anOptions: TSTCabAddFilesOptions = []);
  148.         procedure StartNewCabinet;
  149.         procedure StartNewFolder;
  150.  
  151.         property Handle: HFCI read FHandle;
  152.         property FileName: String read FFileName;
  153.     published
  154.         property CabinetSizeThreshold: ULONG read FCabinetSizeThreshold write FCabinetSizeThreshold default 0;
  155.         property CabinetFileCountThreshold: ULONG read FCabinetFileCountThreshold write FCabinetFileCountThreshold default 0;
  156.         property FolderSizeThreshold: ULONG read FFolderSizeThreshold write FFolderSizeThreshold default 0;
  157.         property FolderFileCountThreshold: ULONG read FFolderFileCountThreshold write FFolderFileCountThreshold default 0;
  158.         property ReservePerCabinetSize: UINT read FReservePerCabinetSize write FReservePerCabinetSize default 0;
  159.         property ReservePerFolderSize: UINT read FReservePerFolderSize write FReservePerFolderSize default 0;
  160.         property ReservePerDataSize: UINT read FReservePerDataSize write FReservePerDataSize default 0;
  161.         property SetID: USHORT read FSetID write FSetID default 0;
  162.         property CabinetNameTemplate: String read FCabinetNameTemplate write FCabinetNameTemplate;
  163.         property CabinetPathTemplate: String read FCabinetPathTemplate write FCabinetPathTemplate;
  164.         property DiskLabelTemplate: String read FDiskLabelTemplate write FDiskLabelTemplate;
  165.         property CompressionType: TSTCabCompressionType read FCompressionType write SetCompressionType default DefCabCompressionType;
  166.         property CompressionLzxLevel: TSTCabLzxLevel read FCompressionLzxLevel write SetCompressionLzxLevel default DefCabLzxLevel;
  167.  
  168.         property OnFilePlaced: TSTCabWriterFilePlacedEvent read FOnFilePlaced write FOnFilePlaced;
  169.         property OnGetNextCabinet: TSTCabWriterGetNextCabinetEvent read FOnGetNextCabinet write FOnGetNextCabinet;
  170.         property OnFileStatus: TSTCabWriterFileStatusEvent read FOnFileStatus write FOnFileStatus;
  171.         property OnFolderStatus: TSTCabWriterFolderStatusEvent read FOnFolderStatus write FOnFolderStatus;
  172.         property OnCabinetStatus: TSTCabWriterCabinetStatusEvent read FOnCabinetStatus write FOnCabinetStatus;
  173.         property OnProgress: TSTCabWriterProgressEvent read FOnProgress write FOnProgress;
  174.         property OnBeforeFileAdd: TSTCabWriterBeforeFileAddEvent read FOnBeforeFileAdd write FOnBeforeFileAdd;
  175.     end;
  176.  
  177.     TSTCabReaderOption = (
  178.         croExecuteOnExtract,
  179.         croDontOverwriteReadOnlyFiles
  180.     );
  181.  
  182.     TSTCabReaderOptions = set of TSTCabReaderOption;
  183.  
  184.     TSTCabReaderCabinetInfoEvent = procedure(Sender: TSTCabReader;
  185.         const aNextCabName, aNextDiskName, aCabPath: String;
  186.         aSetID, aCabNo: USHORT; var anAbort: Boolean) of object;
  187.     TSTCabReaderCopyFileEvent = procedure(Sender: TSTCabReader;
  188.         const aSrcFileName: String; anUncompressedSize: Longint;
  189.         aDate, aTime, anAttrs, aFolderNo: USHORT; var aDstFullFileName: String;
  190.         var anAbort: Boolean) of object;
  191.     TSTCabReaderCopyPartialFileEvent = procedure(Sender: TSTCabReader;
  192.         const aSrcFileName, aStartCabName, aStartDiskName, aDstFullFileName: String;
  193.         var anAbort: Boolean) of object;
  194.     TSTCabReaderCloseFileEvent = procedure(Sender: TSTCabReader;
  195.         const aSrcFileName: String; aDate, aTime, anAttrs, aFolderNo: USHORT;
  196.         var anExec: Boolean; const aDstFullFileName: String; var anAbort: Boolean) of object;
  197.     TSTCabReaderNextCabinetEvent = procedure(Sender: TSTCabReader;
  198.         const aCabName, aDiskName: String; var aCabPath: String;
  199.         anError: TFDIERROR; var anAbort: Boolean) of object;
  200.     TSTCabReaderUnrecognizedNotifyEvent = procedure(Sender: TSTCabReader;
  201.         aNotifyType: TFDINOTIFICATIONTYPE; var aNotify: TFDINOTIFICATION;
  202.         var aResult: Integer) of object;
  203.  
  204.     TSTCabReaderEnumFilesEntriesEvent = procedure(Sender: TSTCabReader;
  205.         const aFileName: String; anUncompressedSize: UINT4; aFlags: UINT2;
  206.         aDate, aTime, anAttrs: UINT2; var anAbort: Boolean) of object;
  207.  
  208.     TSTCabReader = class(TComponent)
  209.     private
  210.         FHandle: HFDI;
  211.         FFileName: String;
  212.         FDstPath: String;
  213.         FOptions: TSTCabReaderOptions;
  214.  
  215.         FOnCabinetInfo: TSTCabReaderCabinetInfoEvent;
  216.         FOnCopyFile: TSTCabReaderCopyFileEvent;
  217.         FOnCopyPartialFile: TSTCabReaderCopyPartialFileEvent;
  218.         FOnCloseFile: TSTCabReaderCloseFileEvent;
  219.         FOnNextCabinet: TSTCabReaderNextCabinetEvent;
  220.         FOnUnrecognizedNotify: TSTCabReaderUnrecognizedNotifyEvent;
  221.  
  222.         function GetHandle: HFDI;
  223.     protected
  224.         FNextFileName: String;
  225.         FNextDiskName: String;
  226.         FCurrentFileName: String;
  227.         FCurrentFileName2: String;
  228.         FIsOnPartialFile: Boolean;
  229.         FERF: TERF;
  230.         FSrcFileNames: TStringList;
  231.         FDstFileNames: TStringList;
  232.  
  233.         procedure CabCheck(aResult: Boolean);
  234.         procedure HandleNeeded;
  235.         procedure DestroyHandle;
  236.         // Callbacks
  237.         function DoNotify(fdint: TFDINOTIFICATIONTYPE; pfdin: PFDINOTIFICATION): Integer;
  238.     public
  239.         constructor Create(aOwner: TComponent); override;
  240.         destructor Destroy; override;
  241.  
  242.         function IsCabinet(const aFileName: String; var aCabInfo: TFDICABINETINFO): Boolean;
  243.         procedure ExtractFiles(const aFileName, aDstPath: String;
  244.             anOptions: TSTCabExtractFilesOptions = []);
  245.         function EnumFilesEntries(const aFileName: String;
  246.             aCallback: TSTCabReaderEnumFilesEntriesEvent): Boolean;
  247.  
  248.         property Handle: HFDI read GetHandle;
  249.         property FileName: String read FFileName;
  250.         property DstPath: String read FDstPath;
  251.         property NextFileName: String read FNextFileName;
  252.         property NextDiskName: String read FNextDiskName;
  253.         property CurrentFileName: String read FCurrentFileName;
  254.         property IsOnPartialFile: Boolean read FIsOnPartialFile;
  255.     published
  256.         property Options: TSTCabReaderOptions read FOptions write FOptions default [];
  257.  
  258.         property OnCabinetInfo: TSTCabReaderCabinetInfoEvent read FOnCabinetInfo write FOnCabinetInfo;
  259.         property OnCopyFile: TSTCabReaderCopyFileEvent read FOnCopyFile write FOnCopyFile;
  260.         property OnCopyPartialFile: TSTCabReaderCopyPartialFileEvent read FOnCopyPartialFile write FOnCopyPartialFile;
  261.         property OnCloseFile: TSTCabReaderCloseFileEvent read FOnCloseFile write FOnCloseFile;
  262.         property OnNextCabinet: TSTCabReaderNextCabinetEvent read FOnNextCabinet write FOnNextCabinet;
  263.         property OnUnrecognizedNotify: TSTCabReaderUnrecognizedNotifyEvent read FOnUnrecognizedNotify write FOnUnrecognizedNotify;
  264.     end;
  265.  
  266. implementation
  267.  
  268. uses
  269.     CabSTConsts;
  270.  
  271. { From fcntl.h }
  272. const
  273.     _O_RDONLY =      $0000; //open for reading only
  274.     _O_WRONLY =      $0001; //open for writing only
  275.     _O_RDWR =        $0002; //open for reading and writing
  276.     _O_APPEND =      $0008; //writes done at eof
  277.  
  278.     _O_CREAT =       $0100; //create and open file
  279.     _O_TRUNC =       $0200; //open and truncate
  280.     _O_EXCL =        $0400; //open only if file doesn't already exist
  281.  
  282.     _O_TEXT =        $4000; //file mode is text (translated)
  283.     _O_BINARY =      $8000; //file mode is binary (untranslated)
  284.  
  285.     _O_RAW =         _O_BINARY;
  286.     _O_NOINHERIT =   $0080; //child process doesn't inherit file
  287.     _O_TEMPORARY =   $0040; //temporary file bit
  288.     _O_SHORT_LIVED = $1000; //temporary storage file, try not to flush
  289.  
  290.     _O_SEQUENTIAL =  $0020; //file access is primarily sequential
  291.     _O_RANDOM =      $0010; //file access is primarily random
  292.  
  293. { From sys\stat.h }
  294. const
  295.     _S_IREAD =       $0100; // read permission, owner
  296.     _S_IWRITE =      $0080; // write permission, owner
  297.  
  298.  
  299. var
  300.     LastFdiError: Integer = 0;
  301.  
  302. {$IFDEF VER130}
  303.  
  304. function IncludeTrailingPathDelimiter(const s: String): String;
  305. var
  306.     aLen: Integer;
  307. begin
  308.     Result := s;
  309.     aLen := Length(Result);
  310.     if (aLen > 0) and (Result[aLen] <> '\') then
  311.         Result := Result + '\';
  312. end;
  313.  
  314. procedure RaiseLastOSError;
  315. begin
  316.     RaiseLastWin32Error;
  317. end;
  318.  
  319. {$ENDIF}
  320.  
  321. function MakePath(const aParts: array of String): String;
  322. var
  323.     i, len: Integer;
  324. begin
  325.     Result := '';
  326.     for i := Low(aParts) to High(aParts) do begin
  327.         if Length(aParts[i]) = 0 then
  328.             continue;
  329.         len := Length(Result);
  330.         if len = 0 then begin
  331.             Result := aParts[i];
  332.             continue;
  333.         end;
  334.         if Result[len] = '\' then begin
  335.             if aParts[i][1] = '\' then
  336.                 SetLength(Result, len - 1);
  337.             Result := Result + aParts[i];
  338.         end
  339.         else begin
  340.             if aParts[i][1] <> '\' then
  341.                 Result := Result + '\';
  342.             Result := Result + aParts[i];
  343.         end;
  344.     end;
  345. end;
  346.  
  347. function ExcludeLeadingPathDelimiter(const s: String): String;
  348. begin
  349.     Result := s;
  350.     if (Length(Result) > 0) and (Result[1] = '\') then
  351.         Delete(Result, 1, 1);
  352. end;
  353.  
  354. function OFlagToFileDesiredAccess(oflag: Integer): DWORD;
  355. begin
  356.     case (oflag and 3) of
  357.         _O_RDONLY: Result := GENERIC_READ;
  358.         _O_WRONLY: Result := GENERIC_WRITE;
  359.         else Result := GENERIC_READ or GENERIC_WRITE;
  360.     end;
  361. end;
  362.  
  363. function OFlagToFileShareMode(oflag: Integer): DWORD;
  364. begin
  365.     Result := FILE_SHARE_READ;
  366. end;
  367.  
  368. function OFlagToFileCreationDisposition(oflag: Integer): DWORD;
  369. begin
  370.     if ((oflag and _O_EXCL) <> 0) and ((oflag and _O_CREAT) <> 0) then
  371.         Result := CREATE_NEW
  372.     else if (oflag and _O_TRUNC) <> 0 then
  373.         if (oflag and _O_CREAT) <> 0 then
  374.             Result := CREATE_ALWAYS
  375.         else
  376.             Result := TRUNCATE_EXISTING
  377.     else if (oflag and _O_CREAT) <> 0 then
  378.         Result := OPEN_ALWAYS
  379.     else
  380.         Result := OPEN_EXISTING;
  381. end;
  382.  
  383. function OFlagToFileFlagsAndAttributes(oflag: Integer): DWORD;
  384. begin
  385.     if ((oflag and _O_SHORT_LIVED) <> 0) and ((oflag and _O_CREAT) <> 0) then
  386.         Result := FILE_ATTRIBUTE_TEMPORARY
  387.     else
  388.         Result := FILE_ATTRIBUTE_NORMAL;
  389.  
  390.     if (oflag and _O_RANDOM) <> 0 then
  391.         Result := Result or FILE_FLAG_RANDOM_ACCESS;
  392.     if (oflag and _O_SEQUENTIAL) <> 0 then
  393.         Result := Result or FILE_FLAG_SEQUENTIAL_SCAN;
  394.     if ((oflag and _O_TEMPORARY) <> 0) and ((oflag and _O_CREAT) <> 0) then
  395.         Result := Result or FILE_FLAG_DELETE_ON_CLOSE;
  396. end;
  397.  
  398. function StdFileOpen(pszFile: PAnsiChar; oflag: Integer; pmode: Integer;
  399.     err: PInteger): Integer;
  400. var
  401.     aHandle: THandle;
  402. begin
  403.     aHandle := CreateFile(pszFile, OFlagToFileDesiredAccess(oflag),
  404.         OFlagToFileShareMode(oflag), nil, OFlagToFileCreationDisposition(oflag),
  405.         OFlagToFileFlagsAndAttributes(oflag), 0);
  406.     if aHandle = INVALID_HANDLE_VALUE then begin
  407.         err^ := Integer(GetLastError);
  408.         Result := -1;
  409.     end
  410.     else if ((oflag and _O_APPEND) <> 0) and
  411.             (SetFilePointer(aHandle, 0, nil, FILE_END) = $FFFFFFFF) then begin
  412.         err^ := Integer(GetLastError);
  413.         Result := -1;
  414.         CloseHandle(aHandle);
  415.     end
  416.     else
  417.         Result := Integer(aHandle);
  418. end;
  419.  
  420. function StdFileRead(hf: Integer; memory: PVoid; cb: UINT;
  421.     err: PInteger): UINT;
  422. begin
  423.     if not ReadFile(THandle(hf), memory^, cb, Result, nil) then begin
  424.         err^ := Integer(GetLastError);
  425.         Result := UINT(-1);
  426.     end;
  427. end;
  428.  
  429. function StdFileWrite(hf: Integer; memory: PVoid; cb: UINT;
  430.     err: PInteger): UINT;
  431. begin
  432.     if not WriteFile(THandle(hf), memory^, cb, Result, nil) then begin
  433.         err^ := Integer(GetLastError);
  434.         Result := UINT(-1);
  435.     end;
  436. end;
  437.  
  438. function StdFileClose(hf: Integer; err: PInteger): Integer;
  439. begin
  440.     if CloseHandle(THandle(hf)) then
  441.         Result := 0
  442.     else begin
  443.         err^ := Integer(GetLastError);
  444.         Result := -1;
  445.     end;
  446. end;
  447.  
  448. function StdFileSeek(hf: Integer; dist: Longint; seektype: Integer;
  449.     err: PInteger): Longint;
  450. var
  451.     aPos: DWORD;
  452. begin
  453.     aPos := SetFilePointer(THandle(hf), dist, nil, DWORD(seektype));
  454.     if aPos = $FFFFFFFF then begin
  455.         err^ := Integer(GetLastError);
  456.         Result := -1;
  457.     end
  458.     else
  459.         Result := Longint(aPos);
  460. end;
  461.  
  462. function StdFileDelete(pszFile: PAnsiChar; err: PInteger): Integer;
  463. begin
  464.     if Windows.DeleteFile(pszFile) then
  465.         Result := 0
  466.     else begin
  467.         err^ := Integer(GetLastError);
  468.         Result := -1;
  469.     end;
  470. end;
  471.  
  472. function StdFileGetOpenInfo(pszName: PAnsiChar; pdate: PUSHORT;
  473.     ptime: PUSHORT; pattribs: PUSHORT; err: PInteger): Integer;
  474. var
  475.     aHandle: THandle;
  476.     aFileInfo: BY_HANDLE_FILE_INFORMATION;
  477.     aFileTime: TFileTime;
  478. begin
  479.     aHandle := CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ, nil,
  480.         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN, 0);
  481.     if aHandle = INVALID_HANDLE_VALUE then begin
  482.         err^ := Integer(GetLastError);
  483.         Result := -1;
  484.     end
  485.     else if not GetFileInformationByHandle(aHandle, aFileInfo) then begin
  486.         err^ := Integer(GetLastError);
  487.         Result := -1;
  488.         CloseHandle(aHandle);
  489.     end
  490.     else begin
  491.         FileTimeToLocalFileTime(aFileInfo.ftLastWriteTime, aFileTime);
  492.         FileTimeToDosDateTime(aFileTime, pdate^, ptime^);
  493.         pattribs^ := USHORT(aFileInfo.dwFileAttributes and
  494.             (FILE_ATTRIBUTE_READONLY or FILE_ATTRIBUTE_HIDDEN or
  495.             FILE_ATTRIBUTE_SYSTEM or FILE_ATTRIBUTE_ARCHIVE));
  496.         CloseHandle(aHandle);
  497.         Result := StdFileOpen(pszName, _O_RDONLY or _O_BINARY, 0, err);
  498.     end;
  499. end;
  500.  
  501. function StdFileSetInfo(pszName: PAnsiChar; aDate, aTime, anAttrs: USHORT;
  502.     err: PInteger): WIN_BOOL;
  503. var
  504.     aHandle: THandle;
  505.     aLocalFileTime, aFileTime: TFileTime;
  506. begin
  507.     aHandle := CreateFile(pszName, GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ,
  508.         nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN, 0);
  509.     if aHandle = INVALID_HANDLE_VALUE then begin
  510.         err^ := Integer(GetLastError);
  511.         Result := WIN_FALSE;
  512.     end
  513.     else begin
  514.         if DosDateTimeToFileTime(aDate, aTime, aLocalFileTime) and
  515.                 LocalFileTimeToFileTime(aLocalFileTime, aFileTime) then
  516.             SetFileTime(aHandle, @aFileTime, nil, @aFileTime);
  517.         CloseHandle(aHandle);
  518.         SetFileAttributes(pszName, anAttrs and
  519.             (FILE_ATTRIBUTE_READONLY or FILE_ATTRIBUTE_HIDDEN or
  520.             FILE_ATTRIBUTE_SYSTEM or FILE_ATTRIBUTE_ARCHIVE));
  521.         Result := WIN_TRUE;
  522.     end;
  523. end;
  524.  
  525. function StdGetTempFile(pszTempName: PAnsiChar; cbTempName: Integer): WIN_BOOL;
  526. var
  527.     aResult: Boolean;
  528.     aPath: array [0..MAX_PATH] of AnsiChar;
  529.     aName: array [0..MAX_PATH] of AnsiChar;
  530. begin
  531.     aResult := (GetTempPath(MAX_PATH, aPath) <> 0) and
  532.         (GetTempFileName(aPath, 'stc', 0, aName) <> 0) and
  533.         (Integer(StrLen(aName)) < cbTempName);
  534.     if aResult then begin
  535.         StrCopy(pszTempName, aName);
  536.         // Delete temp file, cause MS don't expect file existing
  537.         Windows.DeleteFile(pszTempName);
  538.     end;
  539.     Result := BooleanToWinBool[aResult];
  540. end;
  541.  
  542. { FCI & FDI memory callbacks }
  543.  
  544. function FnAlloc(cb: ULONG): PVoid; cdecl;
  545. begin
  546.     GetMem(Result, cb);
  547. end;
  548.  
  549. procedure FnFree(memory: PVoid); cdecl;
  550. begin
  551.     FreeMem(memory);
  552. end;
  553.  
  554. { FCI callbacks }
  555.  
  556. function FnFciOpen(pszFile: PAnsiChar; oflag: Integer; pmode: Integer;
  557.     err: PInteger; pv: PVoid): Integer; cdecl;
  558. begin
  559.     Result := StdFileOpen(pszFile, oflag, pmode, err);
  560. end;
  561.  
  562. function FnFciRead(hf: Integer; memory: PVoid; cb: UINT;
  563.     err: PInteger; pv: PVoid): UINT; cdecl;
  564. begin
  565.     Result := StdFileRead(hf, memory, cb, err);
  566. end;
  567.  
  568. function FnFciWrite(hf: Integer; memory: PVoid; cb: UINT;
  569.     err: PInteger; pv: PVoid): UINT; cdecl;
  570. begin
  571.     Result := StdFileWrite(hf, memory, cb, err);
  572. end;
  573.  
  574. function FnFciClose(hf: Integer; err: PInteger; pv: PVoid): Integer; cdecl;
  575. begin
  576.     Result := StdFileClose(hf, err);
  577. end;
  578.  
  579. function FnFciSeek(hf: Integer; dist: Longint; seektype: Integer;
  580.     err: PInteger; pv: PVoid): Longint; cdecl;
  581. begin
  582.     Result := StdFileSeek(hf, dist, seektype, err);
  583. end;
  584.  
  585. function FnFciDelete(pszFile: PAnsiChar; err: PInteger; pv: PVoid): Integer; cdecl;
  586. begin
  587.     Result := StdFileDelete(pszFile, err);
  588. end;
  589.  
  590. function FnFciGetOpenInfo(pszName: PAnsiChar; pdate: PUSHORT;
  591.     ptime: PUSHORT; pattribs: PUSHORT; err: PInteger; pv: PVoid): Integer; cdecl;
  592. begin
  593.     Result := StdFileGetOpenInfo(pszName, pdate, ptime, pattribs, err);
  594. end;
  595.  
  596. function FnFciGetTempFile(pszTempName: PAnsiChar; cbTempName: Integer;
  597.     pv: PVoid): WIN_BOOL; cdecl;
  598. begin
  599.     Result := StdGetTempFile(pszTempName, cbTempName);
  600. end;
  601.  
  602. function FnFciFilePlaced(pccab: PCCAB; pszFile: PAnsiChar;
  603.     cbFile: Longint; fContinuation: WIN_BOOL; pv: PVoid): Integer; cdecl;
  604. begin
  605.     if TSTCabWriter(pv).DoFilePlaced(pccab, pszFile, cbFile, fContinuation) then
  606.         Result := 0
  607.     else
  608.         Result := -1;
  609. end;
  610.  
  611. function FnFciGetNextCabinet(pccab: PCCAB; cbPrevCab: ULONG; pv: PVoid): WIN_BOOL; cdecl;
  612. begin
  613.     Result := BooleanToWinBool[TSTCabWriter(pv).DoGetNextCabinet(pccab, cbPrevCab)];
  614. end;
  615.  
  616. function FnFciStatus(typeStatus: UINT; cb1: ULONG; cb2: ULONG; pv: PVoid): Longint; cdecl;
  617. begin
  618.     Result := TSTCabWriter(pv).DoStatus(typeStatus, cb1, cb2);
  619. end;
  620.  
  621. { FDI callbacks }
  622.  
  623. function FnFdiOpen(pszFile: PAnsiChar; oflag: Integer; pmode: Integer): Integer; cdecl;
  624. var
  625.     err: Integer;
  626. begin
  627.     err := 0;
  628.     Result := StdFileOpen(pszFile, oflag, pmode, @err);
  629.     if err <> 0 then
  630.         LastFdiError := err;
  631. end;
  632.  
  633. function FnFdiRead(hf: Integer; pv: PVoid; cb: UINT): UINT; cdecl;
  634. var
  635.     err: Integer;
  636. begin
  637.     err := 0;
  638.     Result := StdFileRead(hf, pv, cb, @err);
  639.     if err <> 0 then
  640.         LastFdiError := err;
  641. end;
  642.  
  643. function FnFdiWrite(hf: Integer; pv: PVoid; cb: UINT): UINT; cdecl;
  644. var
  645.     err: Integer;
  646. begin
  647.     err := 0;
  648.     Result := StdFileWrite(hf, pv, cb, @err);
  649.     if err <> 0 then
  650.         LastFdiError := err;
  651. end;
  652.  
  653. function FnFdiClose(hf: Integer): Integer; cdecl;
  654. var
  655.     err: Integer;
  656. begin
  657.     err := 0;
  658.     Result := StdFileClose(hf, @err);
  659.     if err <> 0 then
  660.         LastFdiError := err;
  661. end;
  662.  
  663. function FnFdiSeek(hf: Integer; dist: Longint; seektype: Integer): Longint; cdecl;
  664. var
  665.     err: Integer;
  666. begin
  667.     err := 0;
  668.     Result := StdFileSeek(hf, dist, seektype, @err);
  669.     if err <> 0 then
  670.         LastFdiError := err;
  671. end;
  672.  
  673. function FnFdiNotify(fdint: TFDINOTIFICATIONTYPE; pfdin: PFDINOTIFICATION): Integer; cdecl;
  674. begin
  675.     Result := TSTCabReader(pfdin^.pv).DoNotify(fdint, pfdin);
  676. end;
  677.  
  678. { Error translation functions }
  679.  
  680. function GetFciErrorMessage(anErrCode: Integer): String;
  681. begin
  682.     case anErrCode of
  683.         Integer(FCIERR_NONE): Result := SFciErrNone;
  684.         Integer(FCIERR_OPEN_SRC): Result := SFciErrOpenSrc;
  685.         Integer(FCIERR_READ_SRC): Result := SFciErrReadSrc;
  686.         Integer(FCIERR_ALLOC_FAIL): Result := SFciErrAllocFail;
  687.         Integer(FCIERR_TEMP_FILE): Result := SFciErrTempFile;
  688.         Integer(FCIERR_BAD_COMPR_TYPE): Result := SFciErrBadComprType;
  689.         Integer(FCIERR_CAB_FILE): Result := SFciErrCabFile;
  690.         Integer(FCIERR_USER_ABORT): Result := SFciErrUserAbort;
  691.         Integer(FCIERR_MCI_FAIL): Result := SFciErrMciFail;
  692.         else Result := SFciErrUnknown;
  693.     end;
  694. end;
  695.  
  696. function GetFdiErrorMessage(anErrCode: Integer): String;
  697. begin
  698.     case anErrCode of
  699.         Integer(FDIERROR_NONE): Result := SFdiErrNone;
  700.         Integer(FDIERROR_CABINET_NOT_FOUND): Result := SFdiErrCabinetNotFound;
  701.         Integer(FDIERROR_NOT_A_CABINET): Result := SFdiErrNotACabinet;
  702.         Integer(FDIERROR_UNKNOWN_CABINET_VERSION): Result := SFdiErrUnknownCabinetVersion;
  703.         Integer(FDIERROR_CORRUPT_CABINET): Result := SFdiErrCorruptCabinet;
  704.         Integer(FDIERROR_ALLOC_FAIL): Result := SFdiErrAllocFail;
  705.         Integer(FDIERROR_BAD_COMPR_TYPE): Result := SFdiErrBadComprType;
  706.         Integer(FDIERROR_MDI_FAIL): Result := SFdiErrMdiFail;
  707.         Integer(FDIERROR_TARGET_FILE): Result := SFdiErrTargetFile;
  708.         Integer(FDIERROR_RESERVE_MISMATCH): Result := SFdiErrReserveMismatch;
  709.         Integer(FDIERROR_WRONG_CABINET): Result := SFdiErrWrongCabinet;
  710.         Integer(FDIERROR_USER_ABORT): Result := SFdiErrUserAbort;
  711.         else Result := SFdiErrUnknown;
  712.     end;
  713. end;
  714.  
  715. { ESTCabinet }
  716.  
  717. constructor ESTCabinet.Create(const anERF: TERF; anIsCompressing: Boolean);
  718. var
  719.     s: String;
  720. begin
  721.     FCabError := anERF.erfOper;
  722.     FWinError := DWORD(anERF.erfType);
  723.     FErrorPresent := anERF.fError <> WIN_FALSE;
  724.     FIsCompressing := anIsCompressing;
  725.     if IsCompressing then begin
  726.         if ErrorPresent then
  727.             s := GetFciErrorMessage(CabError) + ' [0x' + IntToHex(CabError, 4) + ']'
  728.         else
  729.             s := SFciErrUnknown;
  730.     end
  731.     else begin
  732.         if ErrorPresent then
  733.             s := GetFdiErrorMessage(CabError) + ' [0x' + IntToHex(CabError, 4) + ']'
  734.         else
  735.             s := SFdiErrUnknown;
  736.     end;
  737.     if ErrorPresent and (WinError <> 0) then
  738.         s := s + '.'#13#10 + SysErrorMessage(Integer(WinError)) +
  739.             ' [0x' + IntToHex(Integer(WinError), 4) + '].';
  740.     inherited Create(s);
  741. end;
  742.  
  743. { TSTCabWriter }
  744.  
  745. constructor TSTCabWriter.Create(aOwner: TComponent);
  746. begin
  747.     inherited Create(aOwner);
  748.     FCompressionType := DefCabCompressionType;
  749.     FCompressionLzxLevel := DefCabLzxLevel;
  750.     SyncCabComp;
  751. end;
  752.  
  753. destructor TSTCabWriter.Destroy;
  754. begin
  755.     Close(False);
  756.     inherited Destroy;
  757. end;
  758.  
  759. procedure TSTCabWriter.SetCompressionType(aValue: TSTCabCompressionType);
  760. begin
  761.     FCompressionType := aValue;
  762.     SyncCabComp;
  763. end;
  764.  
  765. procedure TSTCabWriter.SetCompressionLzxLevel(aValue: TSTCabLzxLevel);
  766. begin
  767.     FCompressionLzxLevel := aValue;
  768.     SyncCabComp;
  769. end;
  770.  
  771. procedure TSTCabWriter.CabCheck(aResult: Boolean);
  772. begin
  773.     if not aResult then
  774.         raise ESTCabinet.Create(FERF, True);
  775. end;
  776.  
  777. function TSTCabWriter.GetCabinetName(aCabNo: Integer): String;
  778. var
  779.     aPos: Integer;
  780. begin
  781.     Result := CabinetNameTemplate;
  782.     if Result = '' then begin
  783.         Result := ExtractFileName(FileName);
  784.         aPos := LastDelimiter('.', Result);
  785.         if aPos = 0 then
  786.             Result := Result + '*'
  787.         else
  788.             Insert('*', Result, aPos);
  789.     end;
  790.     Result := StringReplace(Result, '*', IntToStr(aCabNo), [rfReplaceAll]);
  791. end;
  792.  
  793. function TSTCabWriter.GetCabinetPath(aDiskNo: Integer): String;
  794. begin
  795.     Result := CabinetPathTemplate;
  796.     if Result = '' then
  797.         Result := ExtractFilePath(FileName);
  798.     Result := StringReplace(Result, '*', IntToStr(aDiskNo), [rfReplaceAll]);
  799.     Result := IncludeTrailingPathDelimiter(Result);
  800. end;
  801.  
  802. function TSTCabWriter.GetDiskLabel(aDiskNo: Integer): String;
  803. begin
  804.     Result := DiskLabelTemplate;
  805.     if Result = '' then
  806.         Result := SDiskLabelTemplateDef;
  807.     Result := StringReplace(Result, '*', IntToStr(aDiskNo), [rfReplaceAll]);
  808. end;
  809.  
  810. procedure TSTCabWriter.SyncCabComp;
  811. begin
  812.     case CompressionType of
  813.         cctNone: FCabComp := tcompTYPE_NONE;
  814.         cctMsZip: FCabComp := tcompTYPE_MSZIP;
  815.         cctLzx: FCabComp := TCOMPfromLZXWindow(CompressionLzxLevel);
  816.         else FCabComp := tcompBAD;
  817.     end;
  818. end;
  819.  
  820. function TSTCabWriter.DoFilePlaced(pccab: PCCAB; pszFile: PAnsiChar;
  821.     cbFile: Longint; fContinuation: WIN_BOOL): Boolean;
  822. var
  823.     anAbort: Boolean;
  824. begin
  825.     anAbort := False;
  826.     if Assigned(OnFilePlaced) then
  827.         OnFilePlaced(Self, pccab^, pszFile, cbFile, fContinuation <> WIN_FALSE, anAbort);
  828.     Result := not anAbort;
  829. end;
  830.  
  831. function TSTCabWriter.DoGetNextCabinet(pccab: PCCAB; cbPrevCab: ULONG): Boolean;
  832. var
  833.     anAbort: Boolean;
  834. begin
  835.     anAbort := False;
  836.     Inc(pccab^.iDisk);
  837.     StrPCopy(pccab^.szDisk, GetDiskLabel(pccab^.iDisk));
  838.     StrPCopy(pccab^.szCab, GetCabinetName(pccab^.iCab));
  839.     StrPCopy(pccab^.szCabPath, GetCabinetPath(pccab^.iDisk));
  840.     if Assigned(OnGetNextCabinet) then
  841.         OnGetNextCabinet(Self, pccab^, cbPrevCab, anAbort);
  842.     if not anAbort then begin
  843.         ForceDirectories(pccab^.szCabPath);
  844.         Windows.DeleteFile(PChar(
  845.             MakePath([String(pccab^.szCabPath), String(pccab^.szCab)])));
  846.     end;
  847.  
  848.     FFileCountPerCabinet := 0;
  849.     FFileCountPerFolder := 0;
  850.     Result := not anAbort;
  851. end;
  852.  
  853. function TSTCabWriter.DoStatus(typeStatus: UINT; cb1: ULONG; cb2: ULONG): Longint;
  854. var
  855.     anAbort: Boolean;
  856.     aDesiredSize: ULONG;
  857. begin
  858.     anAbort := False;
  859.     Result := 0;
  860.     case typeStatus of
  861.         statusFile: begin
  862.             if Assigned(OnFileStatus) then
  863.                 OnFileStatus(Self, cb1, cb2, anAbort);
  864.             if (FProgressCount < 0) and Assigned(OnProgress) and not anAbort then begin
  865.                 OnProgress(Self, anAbort);
  866.                 FProgressCount := 8;
  867.             end;
  868.             Dec(FProgressCount);
  869.         end;
  870.         statusFolder: begin
  871.             if Assigned(OnFolderStatus) then
  872.                 OnFolderStatus(Self, cb1, cb2, anAbort);
  873.             FFileCountPerFolder := 0;
  874.         end;
  875.         statusCabinet: begin
  876.             aDesiredSize := 0;
  877.             if Assigned(OnCabinetStatus) then
  878.                 OnCabinetStatus(Self, cb1, cb2, aDesiredSize, anAbort);
  879.             Result := Longint(aDesiredSize);
  880.             FFileCountPerCabinet := 0;
  881.             FFileCountPerFolder := 0;
  882.         end;
  883.     end;
  884.     if anAbort then
  885.         Result := -1;
  886. end;
  887.  
  888. procedure TSTCabWriter.Open(const aFileName: String);
  889. var
  890.     anAbort: Boolean;
  891. begin
  892.     Close(False);
  893.     FFileName := ExpandFileName(aFileName);
  894.     FillChar(FERF, SizeOf(FERF), 0);
  895.     FillChar(FOriginalCCAB, SizeOf(FOriginalCCAB), 0);
  896.     if CabinetSizeThreshold = 0 then
  897.         FOriginalCCAB.cb := CB_MAX_DISK
  898.     else
  899.         FOriginalCCAB.cb := CabinetSizeThreshold;
  900.     if FolderSizeThreshold = 0 then
  901.         FOriginalCCAB.cbFolderThresh := CB_MAX_DISK //???
  902.     else
  903.         FOriginalCCAB.cbFolderThresh := FolderSizeThreshold;
  904.     FOriginalCCAB.cbReserveCFHeader := ReservePerCabinetSize;
  905.     FOriginalCCAB.cbReserveCFFolder := ReservePerFolderSize;
  906.     FOriginalCCAB.cbReserveCFData := ReservePerDataSize;
  907.     FOriginalCCAB.iDisk := 1;
  908.     FOriginalCCAB.setID := SetID;
  909.     StrPCopy(FOriginalCCAB.szDisk, GetDiskLabel(FOriginalCCAB.iDisk));
  910.     StrPCopy(FOriginalCCAB.szCab, ExtractFileName(FileName));
  911.     StrPCopy(FOriginalCCAB.szCabPath, ExtractFilePath(FileName));
  912.     anAbort := False;
  913.     if Assigned(OnGetNextCabinet) then begin
  914.         OnGetNextCabinet(Self, FOriginalCCAB, 0, anAbort);
  915.         if anAbort then
  916.             exit;
  917.     end;
  918.     StrPCopy(FOriginalCCAB.szCabPath, IncludeTrailingPathDelimiter(FOriginalCCAB.szCabPath));
  919.     ForceDirectories(FOriginalCCAB.szCabPath);
  920.     Windows.DeleteFile(PChar(
  921.         MakePath([String(FOriginalCCAB.szCabPath), String(FOriginalCCAB.szCab)])));
  922.  
  923.     FFileCountPerCabinet := 0;
  924.     FFileCountPerFolder := 0;
  925.     FProgressCount := 0;
  926.  
  927.     FHandle := FCICreate(@FERF, @FnFciFilePlaced, @FnAlloc, @FnFree,
  928.         @FnFciOpen, @FnFciRead, @FnFciWrite, @FnFciClose, @FnFciSeek,
  929.         @FnFciDelete, @FnFciGetTempFile, @FOriginalCCAB, Self);
  930.     CabCheck(FHandle <> nil);
  931. end;
  932.  
  933. procedure TSTCabWriter.Close(aFlushCab: Boolean = True);
  934. begin
  935.     if FHandle <> nil then begin
  936.         if aFlushCab then
  937.             FlushCabinet(False);
  938.         try
  939.             CabCheck(FCIDestroy(FHandle) <> WIN_FALSE);
  940.         finally
  941.             FHandle := nil;
  942.         end;
  943.     end;
  944. end;
  945.  
  946. procedure TSTCabWriter.AddFile(const aSrcFileName, aDstFileName: String;
  947.     anOptions: TSTCabAddFileOptions = []);
  948. var
  949.     aSkip: Boolean;
  950.     anExec: WIN_BOOL;
  951. begin
  952.     if Assigned(OnBeforeFileAdd) then begin
  953.         aSkip := False;
  954.         OnBeforeFileAdd(Self, aSrcFileName, aSkip);
  955.         if aSkip then
  956.             exit;
  957.     end;
  958.     if (CabinetFileCountThreshold <> 0) and
  959.             (FFileCountPerCabinet >= CabinetFileCountThreshold) then
  960.         StartNewCabinet
  961.     else if (FolderFileCountThreshold <> 0) and
  962.             (FFileCountPerFolder >= FolderFileCountThreshold) then
  963.         StartNewFolder;
  964.     anExec := BooleanToWinBool[cafoExecuteOnExtract in anOptions];
  965.     CabCheck(FCIAddFile(Handle, PAnsiChar(aSrcFileName),
  966.         PAnsiChar(ExcludeLeadingPathDelimiter(aDstFileName)),
  967.         anExec, @FnFciGetNextCabinet, @FnFciStatus, @FnFciGetOpenInfo,
  968.         FCabComp) <> WIN_FALSE);
  969.     Inc(FFileCountPerCabinet);
  970.     Inc(FFileCountPerFolder);
  971. end;
  972.  
  973. procedure TSTCabWriter.AddFiles(const aSrcPath, aDstPath: String;
  974.     anOptions: TSTCabAddFilesOptions = []);
  975.  
  976.     procedure ProcessFolder(const aPath, aMask, aCabPath: String);
  977.     var
  978.         aFindPath, aFileName: String;
  979.         aFindData: TWin32FindData;
  980.         hFind: THandle;
  981.         aFolders: TStringList;
  982.         i: Integer;
  983.     begin
  984.         // Process files
  985.         aFindPath := MakePath([aPath, aMask]);
  986.         hFind := Windows.FindFirstFile(PChar(aFindPath), aFindData);
  987.         if hFind = INVALID_HANDLE_VALUE then begin
  988.             if GetLastError <> ERROR_FILE_NOT_FOUND then
  989.                 RaiseLastOSError;
  990.         end
  991.         else begin
  992.             try
  993.                 repeat
  994.                     aFileName := aFindData.cFileName;
  995.                     if (aFindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
  996.                         AddFile(MakePath([aPath, aFileName]), MakePath([aCabPath, aFileName]), []);
  997.                 until not Windows.FindNextFile(hFind, aFindData);
  998.                 if GetLastError <> ERROR_NO_MORE_FILES then
  999.                     RaiseLastOSError;
  1000.             finally
  1001.                 Windows.FindClose(hFind);
  1002.             end;
  1003.         end;
  1004.  
  1005.         if cafsoRecurseSubdirs in anOptions then begin
  1006.             // Process subfolders
  1007.             aFolders := TStringList.Create;
  1008.             try
  1009.                 aFindPath := MakePath([aPath, '*']);
  1010.                 hFind := Windows.FindFirstFile(PChar(aFindPath), aFindData);
  1011.                 if hFind = INVALID_HANDLE_VALUE then begin
  1012.                     if GetLastError <> ERROR_FILE_NOT_FOUND then
  1013.                         RaiseLastOSError;
  1014.                 end
  1015.                 else begin
  1016.                     try
  1017.                         repeat
  1018.                             aFileName := aFindData.cFileName;
  1019.                             if ((aFindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0) and
  1020.                                     (aFileName <> '.') and (aFileName <> '..') then
  1021.                                 aFolders.Add(aFileName);
  1022.                         until not Windows.FindNextFile(hFind, aFindData);
  1023.                         if GetLastError <> ERROR_NO_MORE_FILES then
  1024.                             RaiseLastOSError;
  1025.                     finally
  1026.                         Windows.FindClose(hFind);
  1027.                     end;
  1028.                     for i := 0 to aFolders.Count - 1 do
  1029.                         ProcessFolder(MakePath([aPath, aFolders[i]]), aMask,
  1030.                             MakePath([aCabPath, aFolders[i]]));
  1031.                 end;
  1032.             finally
  1033.                 aFolders.Free;
  1034.             end;
  1035.         end;
  1036.     end;
  1037.  
  1038. begin
  1039.     ProcessFolder(ExtractFilePath(aSrcPath), ExtractFileName(aSrcPath), aDstPath);
  1040. end;
  1041.  
  1042. procedure TSTCabWriter.FlushCabinet(aGetNextCab: Boolean);
  1043. begin
  1044.     CabCheck(FCIFlushCabinet(Handle, BooleanToWinBool[aGetNextCab],
  1045.         @FnFciGetNextCabinet, @FnFciStatus) <> WIN_FALSE);
  1046.  
  1047.     FFileCountPerCabinet := 0;
  1048.     FFileCountPerFolder := 0;
  1049. end;
  1050.  
  1051. procedure TSTCabWriter.StartNewCabinet;
  1052. begin
  1053.     FlushCabinet(True);
  1054. end;
  1055.  
  1056. procedure TSTCabWriter.StartNewFolder;
  1057. begin
  1058.     CabCheck(FCIFlushFolder(Handle, @FnFciGetNextCabinet, @FnFciStatus) <> WIN_FALSE);
  1059.  
  1060.     FFileCountPerFolder := 0;
  1061. end;
  1062.  
  1063. { TSTCabReader }
  1064.  
  1065. constructor TSTCabReader.Create(aOwner: TComponent);
  1066. begin
  1067.     inherited Create(aOwner);
  1068.     FSrcFileNames := TStringList.Create;
  1069.     FDstFileNames := TStringList.Create;
  1070.     FOptions := [];
  1071. end;
  1072.  
  1073. destructor TSTCabReader.Destroy;
  1074. begin
  1075.     DestroyHandle;
  1076.     FSrcFileNames.Free;
  1077.     FDstFileNames.Free;
  1078.     inherited Destroy;
  1079. end;
  1080.  
  1081. function TSTCabReader.GetHandle: HFDI;
  1082. begin
  1083.     HandleNeeded;
  1084.     Result := FHandle;
  1085. end;
  1086.  
  1087. procedure TSTCabReader.CabCheck(aResult: Boolean);
  1088. begin
  1089.     if not aResult then begin
  1090.         FERF.erfType := LastFdiError;
  1091.         raise ESTCabinet.Create(FERF, False);
  1092.     end;
  1093. end;
  1094.  
  1095. procedure TSTCabReader.HandleNeeded;
  1096. begin
  1097.     if FHandle = nil then begin
  1098.         FillChar(FERF, SizeOf(FERF), 0);
  1099.         FHandle := FDICreate(@FnAlloc, @FnFree, @FnFdiOpen, @FnFdiRead, @FnFdiWrite,
  1100.             @FnFdiClose, @FnFdiSeek, cpuUNKNOWN, @FERF);
  1101.         CabCheck(FHandle <> nil);
  1102.     end;
  1103. end;
  1104.  
  1105. procedure TSTCabReader.DestroyHandle;
  1106. begin
  1107.     if FHandle <> nil then begin
  1108.         try
  1109.             CabCheck(FDIDestroy(FHandle) <> WIN_FALSE);
  1110.         finally
  1111.             FHandle := nil;
  1112.         end;
  1113.     end;
  1114. end;
  1115.  
  1116. function TSTCabReader.DoNotify(fdint: TFDINOTIFICATIONTYPE; pfdin: PFDINOTIFICATION): Integer;
  1117. var
  1118.     aFileName, aFilePath: String;
  1119.     err, anIndex, aFileAttrs: Integer;
  1120.     anExec, anAbort: Boolean;
  1121. begin
  1122.     LastFdiError := 0;
  1123.     anAbort := False;
  1124.     case fdint of
  1125.         fdintCABINET_INFO: begin
  1126.             if FCurrentFileName2 <> '' then
  1127.                 FCurrentFileName := FCurrentFileName2;
  1128.             FNextFileName := pfdin^.psz1;
  1129.             if FNextFileName <> '' then
  1130.                 FNextFileName := MakePath([String(pfdin^.psz3), FNextFileName]);
  1131.             FNextDiskName := pfdin^.psz2;
  1132.             if Assigned(OnCabinetInfo) then
  1133.                 OnCabinetInfo(Self, pfdin^.psz1, pfdin^.psz2, pfdin^.psz3,
  1134.                     pfdin^.setID, pfdin^.iCabinet, anAbort);
  1135.             if anAbort then
  1136.                 Result := -1
  1137.             else
  1138.                 Result := 0;
  1139.         end;
  1140.         fdintPARTIAL_FILE: begin
  1141.             anIndex := FSrcFileNames.IndexOf(pfdin^.psz1);
  1142.             if anIndex >= 0 then
  1143.                 aFileName := FDstFileNames[anIndex]
  1144.             else
  1145.                 aFileName := '';
  1146.             if Assigned(OnCopyPartialFile) then
  1147.                 OnCopyPartialFile(Self, pfdin^.psz1, pfdin^.psz2, pfdin^.psz3, aFileName, anAbort);
  1148.             if anAbort then
  1149.                 Result := -1
  1150.             else
  1151.                 Result := 0;
  1152.         end;
  1153.         fdintCOPY_FILE: begin
  1154.             aFileName := MakePath([DstPath, String(pfdin^.psz1)]);
  1155.             if Assigned(OnCopyFile) then
  1156.                 OnCopyFile(Self, pfdin^.psz1, pfdin^.cb, pfdin^.date, pfdin^.time,
  1157.                     pfdin^.attribs, pfdin^.iFolder, aFileName, anAbort);
  1158.             if anAbort then
  1159.                 Result := -1
  1160.             else if aFileName = '' then
  1161.                 Result := 0
  1162.             else begin
  1163.                 ForceDirectories(ExtractFilePath(aFileName));
  1164.                 if FileExists(aFileName) then begin
  1165.                     aFileAttrs := FileGetAttr(aFileName);
  1166.                     if (aFileAttrs and faReadOnly) <> 0 then begin
  1167.                         if croDontOverwriteReadOnlyFiles in Options then begin
  1168.                             Result := 0;
  1169.                             exit;
  1170.                         end;
  1171.                         FileSetAttr(aFileName, aFileAttrs and not faReadOnly);
  1172.                     end;
  1173.                 end;
  1174.                 err := 0;
  1175.                 Result := StdFileOpen(PAnsiChar(aFileName), _O_BINARY or _O_CREAT or
  1176.                     _O_WRONLY or _O_SEQUENTIAL, _S_IREAD or _S_IWRITE, @err);
  1177.                 if err <> 0 then
  1178.                     LastFdiError := err;
  1179.                 if Result <> -1 then begin
  1180.                     FSrcFileNames.Add(pfdin^.psz1);
  1181.                     FDstFileNames.Add(aFileName);
  1182.                 end;
  1183.             end;
  1184.         end;
  1185.         fdintCLOSE_FILE_INFO: begin
  1186.             err := 0;
  1187.             StdFileClose(pfdin^.hf, @err);
  1188.             anIndex := FSrcFileNames.IndexOf(pfdin^.psz1);
  1189.             if anIndex >= 0 then begin
  1190.                 aFileName := FDstFileNames[anIndex];
  1191.                 FSrcFileNames.Delete(anIndex);
  1192.                 FDstFileNames.Delete(anIndex);
  1193.                 StdFileSetInfo(PAnsiChar(aFileName), pfdin^.date, pfdin^.time,
  1194.                     pfdin^.attribs, @err);
  1195.             end
  1196.             else
  1197.                 aFileName := '';
  1198.             anExec := (pfdin^.cb and 1) = 1;
  1199.             if Assigned(OnCloseFile) then
  1200.                 OnCloseFile(Self, pfdin^.psz1, pfdin^.date, pfdin^.time, pfdin^.attribs,
  1201.                     pfdin^.iFolder, anExec, aFileName, anAbort);
  1202.             if anAbort then
  1203.                 Result := -1
  1204.             else begin
  1205.                 if anExec and (croExecuteOnExtract in Options) and (aFileName <> '') then
  1206.                     ShellExecute(0, 'open', PChar(aFileName), nil, nil, SW_SHOWNORMAL);
  1207.                 Result := WIN_TRUE;
  1208.             end;
  1209.         end;
  1210.         fdintNEXT_CABINET: begin
  1211.             if Assigned(OnNextCabinet) then begin
  1212.                 aFilePath := pfdin^.psz3;
  1213.                 OnNextCabinet(Self, pfdin^.psz1, pfdin^.psz2, aFilePath, pfdin^.fdie, anAbort);
  1214.                 if anAbort then
  1215.                     Result := -1
  1216.                 else begin
  1217.                     StrPCopy(pfdin^.psz3, IncludeTrailingPathDelimiter(aFilePath));
  1218.                     Result := 0;
  1219.                 end;
  1220.             end
  1221.             else if pfdin^.fdie = FDIERROR_NONE then
  1222.                 Result := 0
  1223.             else
  1224.                 Result := -1;
  1225.             FCurrentFileName2 := MakePath([String(pfdin^.psz3), String(pfdin^.psz1)]);
  1226.             FIsOnPartialFile := True;
  1227.         end;
  1228.         fdintENUMERATE: begin
  1229.             Result := 0;
  1230.             if Assigned(OnUnrecognizedNotify) then
  1231.                 OnUnrecognizedNotify(Self, fdint, pfdin^, Result);
  1232.         end;
  1233.         else begin
  1234.             Result := 0;
  1235.             if Assigned(OnUnrecognizedNotify) then
  1236.                 OnUnrecognizedNotify(Self, fdint, pfdin^, Result);
  1237.         end;
  1238.     end;
  1239. end;
  1240.  
  1241. function TSTCabReader.IsCabinet(const aFileName: String; var aCabInfo: TFDICABINETINFO): Boolean;
  1242. var
  1243.     hf, err: Integer;
  1244. begin
  1245.     err := 0;
  1246.     hf := StdFileOpen(PAnsiChar(aFileName), _O_RDONLY or _O_BINARY, 0, @err);
  1247.     if hf = -1 then
  1248.         RaiseLastOSError;
  1249.     try
  1250.         Result := FDIIsCabinet(Handle, hf, @aCabInfo) <> WIN_FALSE;
  1251.     finally
  1252.         StdFileClose(hf, @err);
  1253.     end;
  1254. end;
  1255.  
  1256. procedure TSTCabReader.ExtractFiles(const aFileName, aDstPath: String;
  1257.     anOptions: TSTCabExtractFilesOptions = []);
  1258. var
  1259.     s: String;
  1260. begin
  1261.     FFileName := ExpandFileName(aFileName);
  1262.     FDstPath := IncludeTrailingPathDelimiter(aDstPath);
  1263.     FSrcFileNames.Clear;
  1264.     FDstFileNames.Clear;
  1265.     s := FileName;
  1266.     while s <> '' do begin
  1267.         FNextFileName := '';
  1268.         FNextDiskName := '';
  1269.         FCurrentFileName := s;
  1270.         FCurrentFileName2 := '';
  1271.         FIsOnPartialFile := False;
  1272.         CabCheck(FDICopy(Handle, PAnsiChar(ExtractFileName(s)),
  1273.             PAnsiChar(ExtractFilePath(s)), 0, @FnFdiNotify, nil, Self) <> WIN_FALSE);
  1274.         if IsOnPartialFile then
  1275.             s := FCurrentFileName2
  1276.         else if AnsiCompareText(s, NextFileName) = 0 then
  1277.             s := ''
  1278.         else
  1279.             s := NextFileName;
  1280.         if (cefsoProcessOnlyOneVolume in anOptions) or
  1281.                 ((s <> '') and not FileExists(s)) then
  1282.             s := '';
  1283.     end;
  1284. end;
  1285.  
  1286. function TSTCabReader.EnumFilesEntries(const aFileName: String;
  1287.     aCallback: TSTCabReaderEnumFilesEntriesEvent): Boolean;
  1288.  
  1289.     procedure StmSetPosition(aStm: TFileStream; aPos: Longint);
  1290.     begin
  1291.         aStm.Position := aPos;
  1292.         if aStm.Position <> aPos then
  1293.             raise Exception.Create(SCabErrSetPosition);
  1294.     end;
  1295.  
  1296.     function StmReadSzFileName(aStm: TFileStream): String;
  1297.     var
  1298.         aBuf: array[0..MAX_PATH * 3] of AnsiChar;
  1299.         aPtr: PAnsiChar;
  1300.     begin
  1301.         aPtr := @aBuf;
  1302.         while True do begin
  1303.             aStm.ReadBuffer(aPtr^, SizeOf(AnsiChar));
  1304.             if aPtr^ = #0 then
  1305.                 break;
  1306.             Inc(aPtr);
  1307.         end;
  1308.         Result := aBuf;
  1309.     end;
  1310.  
  1311. var
  1312.     aCabInfo: TFDICABINETINFO;
  1313.     aStm: TFileStream;
  1314.     aHeaderEntry: TCFHEADER;
  1315.     aFileEntry: TCFFILE;
  1316.     anAbort: Boolean;
  1317.     aName: String;
  1318.     i: Integer;
  1319. begin
  1320.     Result := IsCabinet(aFileName, aCabInfo);
  1321.     if not Result then
  1322.         exit;
  1323.     aStm := TFileStream.Create(aFileName, fmOpenRead or fmShareDenyWrite);
  1324.     try
  1325.         aStm.ReadBuffer(aHeaderEntry, SizeOf(TCFHEADER));
  1326.         if aHeaderEntry.signature <> cfhdrSIGNATURE then
  1327.             raise Exception.Create(SFdiErrNotACabinet);
  1328.         StmSetPosition(aStm, aHeaderEntry.coffFiles);
  1329.         anAbort := False;
  1330.         for i := 0 to aHeaderEntry.cFiles - 1 do begin
  1331.             aStm.ReadBuffer(aFileEntry, SizeOf(TCFFILE));
  1332.             aName := StmReadSzFileName(aStm);
  1333.             aCallback(Self, aName, aFileEntry.cbFile, aFileEntry.iFolder,
  1334.                 aFileEntry.date, aFileEntry.time, aFileEntry.attribs, anAbort);
  1335.             if anAbort then
  1336.                 break;
  1337.         end;
  1338.     finally
  1339.         aStm.Free;
  1340.     end;
  1341. end;
  1342.  
  1343. end.
  1344.  
  1345.