| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658 |
- {******************************************************************************}
- { CnPack For Delphi/C++Builder }
- { 中国人自己的开放源码第三方开发包 }
- { (C)Copyright 2001-2018 CnPack 开发组 }
- { ------------------------------------ }
- { }
- { 本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修 }
- { 改和重新发布这一程序。 }
- { }
- { 发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有 }
- { 适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。 }
- { }
- { 您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果 }
- { 还没有,可访问我们的网站: }
- { }
- { 网站地址:http://www.cnpack.org }
- { 电子邮件:master@cnpack.org }
- { }
- {******************************************************************************}
- unit CnFilePacker;
- {* |<PRE>
- ================================================================================
- * 软件名称:开发包不可视组件库
- * 单元名称:文件目录打包组件实现单元
- * 单元作者:CnPack开发组 子旻
- * 备 注:
- * 开发平台:PWinXP + Delphi 7.0
- * 兼容测试:PWin9X/2000/XP + Delphi 5/6
- * 本 地 化:该单元中的字符串均符合本地化处理方式
- * 单元标识:$Id$
- * 修改记录:2011.09.04 V0.03
- * 修正一处根目录结构错误的问题。
- * 2009.07.08 V0.02
- * 修正一处指针释放问题,增加对 D2009 的支持。
- * 2008.06.27 V0.01
- * 创建单元(整理而来)
- ================================================================================
- |</PRE>}
- interface
- {$I CnPack.inc}
- uses
- Classes, SysUtils, Windows, CnConsts, CnCompConsts, CnClasses, CnCommon;
- const
- SCnIncCounts = 20;
- SFileNameError = 'Destination FileName is Empty.';
- type
- //文件结构
- //PPackHeader |PPackDir| (TDataBlock|data|,...)
- // 压缩模式
- TCompressMode = (cmNONE, cmCustom, cmZIP, cmRAR);
- TBytes = array of Byte;
- //------------------------------------------------------------------------------
- // 文件头
- //------------------------------------------------------------------------------
- PPackHeader = ^TPackHeader;
- TPackHeader = record
- ZipName: array[0..7] of AnsiChar; //= ('cnpacker');
- FileInfoCount: Cardinal;
- Compress: TCompressMode;
- FileSize: Int64;
- end;
- //------------------------------------------------------------------------------
- // 文件信息
- //------------------------------------------------------------------------------
- PPackFileInformation = ^TPackFileInformation;
- TPackFileInformation = record
- Name: array[0..255] of AnsiChar;
- DataStart: Cardinal;
- end;
- TArrayPackFileInformation = array of TPackFileInformation;
- //------------------------------------------------------------------------------
- // 数据头
- //------------------------------------------------------------------------------
- TDataBlock = record
- FileName: array[0..255] of AnsiChar;
- //MD5:TMD5Digest;
- DataLength: Cardinal;
- end;
- //------------------------------------------------------------------------------
- // 文件描述元
- //------------------------------------------------------------------------------
- TFileCell = record
- ReadFileName: string;
- ConvertFileName: string;
- end;
- TFileCells = array of TFileCell;
- // 压缩接口
- ICnCompress = interface
- ['{F2379CD7-824B-4D8A-89C3-D897BF95F34C}']
- function GetCompressMode: TCompressMode;
- procedure DoCompressData(var AStream: TBytes; var ALength: Cardinal);
- procedure DoDeCompressData(var AStream: TBytes; var ALength: Cardinal);
- end;
- { TCnFilePacker }
- ECnFilePackerException = class(Exception)
- end;
- TCnFilePacker = class(TCnComponent)
- private
- {*文件头}
- FPackHeaderInfo: PPackHeader;
- {*打包文件的文件信息}
- FPackFileInformations: TArrayPackFileInformation;
- {*供外部使用的文件信息}
- FImportPackFileInfo: TArrayPackFileInformation;
- {*供外部使用的文件目录信息}
- FImprotPackDirectoryInfo: TArrayPackFileInformation;
- {*标志是否创建了保存主目录,即解包文件的目录}
- FCreateSavePath: Boolean;
- {*压缩模式}
- FCompressMode: TCompressMode;
- {*是否压缩}
- FCompress: Boolean;
- {*是否包含子目录}
- FPackedSubDirectory: Boolean;
- {*形成文件列表}
- FFiles: TFileCells;
- {*文件信息的当前数量,总数量}
- FCurrent, FCount: Cardinal;
- {*文件信息的数量,打包时=fcurrent,解包时从包中得到的}
- FFileinfoCount: Cardinal;
- {*fDestFilename是打包后的文件的文件路径}
- {*FSavePath是解包后存放的目录}
- FDestFileName, FSavePath: string;
- {*标志是否使用addfile函数增加了文件}
- FAddFilesCount: integer;
- {*传入的自定义压缩类}
- FCompressInterface: ICnCompress;
- {* 属性字段用到的函数,前边加prop区别}
- function GetPropGetPackFileDirectoryInfo: TArrayPackFileInformation;
- function GetPropGetPackFileInformation: TArrayPackFileInformation;
- function GetPropGetPackHeader: TPackHeader;
- {*压缩数据函数}
- procedure CompressData(var AStream: TBytes; var ALength: Cardinal);
- {*解压缩数据函数}
- procedure DeCompressData(var AStream: TBytes; var ALength: Cardinal);
- protected
- FPack, FDestFile: TFileStream;
- procedure CheckFileCellsCounts;
- {*如果需要压缩,不适用压缩接口的话,重载这两个虚函数!}
- procedure DoCompressData(var AStream: TBytes; var ALength: Cardinal); virtual;
- procedure DoDeCompressData(var AStream: TBytes; var ALength: Cardinal); virtual;
- {*得到打包文件的文件头}
- function GetPackHeader: PPackHeader;
- {*分配一块内存并得到打包文件文件的信息,由外部负责释放}
- function GetPackFileInformation: TArrayPackFileInformation;
-
- procedure GetComponentInfo(var AName, Author, Email, Comment: string); override;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- {*形成打包文件的主函数}
- procedure DoPack();
- {*存储一个文件}
- procedure SaveToFile(APackFileInfo: TPackFileInformation);
- {*存储所以文件}
- procedure SaveToFiles;
- {*创建文件目录}
- procedure CreateDirectory;
- {*添加目录}
- procedure AddDircetory(ADirName: string); overload;
- procedure AddDircetory(ARootName, ADirName: string); overload;
- {*添加文件}
- procedure AddFile(ADirName, AFileName: string); overload;
- procedure AddFile(AFileName: string); overload;
- {*添加压缩类}
- procedure AddCompressClass(ACompressClass: TInterfacedClass);
- {*得到文件信息}
- property PackFileInformation: TArrayPackFileInformation read GetPropGetPackFileInformation;
- property PackFileDirectoryInfo: TArrayPackFileInformation read GetPropGetPackFileDirectoryInfo;
- published
- property DestFileName: string read FDestFileName write FDestFileName;
- property SavePath: string read FSavePath write FSavePath;
- property PackedSubDirectory: Boolean read FPackedSubDirectory write FPackedSubDirectory;
- property Compress: Boolean read FCompress write FCompress;
- property CompressMode: TCompressMode read FCompressMode write FCompressMode;
- {*得到文件头信息}
- property PackHeaderInformation: TPackHeader read GetPropGetPackHeader;
- end;
- implementation
- //以最后一个'\' 为界得到后面部分
- function GetFileName(AFileName: string): string;
- var
- Len, i: Cardinal;
- begin
- Len := Length(AFileName);
- for I := Len - 1 downto 1 do
- if AFileName[i] = '\' then
- Break;
- Result := Copy(AFileName, i + 1, Len - i);
- end;
- procedure Check(var ADirName: string);
- begin
- if ADirName[Length(ADirName)] <> '\' then
- ADirName := ADirName + '\';
- end;
- { TCnFilePacker }
- procedure TCnFilePacker.AddCompressClass(ACompressClass: TInterfacedClass);
- begin
- FCompressInterface := ACompressClass.Create as ICnCompress;
- end;
- procedure TCnFilePacker.AddDircetory(ARootName, ADirName: string);
- var
- CurrentDirectory, LastNameofCurrentDirectory: string;
- //递归目录,形成文件列表
- procedure FindFile(ADirName: string);
- var
- SRec: TSearchRec;
- tmpCurrentDirectory, tmpLastNameofCurrentDirectory: string; //保存当前目录层递归没有退栈,nnd
- begin
- if FindFirst(ADirName, faAnyFile, SRec) = 0 then
- begin
- repeat
- CheckFileCellsCounts;
- if (SRec.Name = '.') or (SRec.Name = '..') then
- Continue;
-
- if (SRec.Attr and faDirectory) <> 0 then
- begin
- FFiles[FCurrent].ReadFileName := CurrentDirectory + SRec.Name + '\' + IntToStr(SRec.Attr) + '?';
- FFiles[FCurrent].ConvertFileName := ARootName + LastNameofCurrentDirectory + SRec.Name + '\' + IntToStr(SRec.Attr) + '?';
- Inc(FCurrent);
-
- if FPackedSubDirectory then
- begin
- tmpLastNameofCurrentDirectory := LastNameofCurrentDirectory;
- tmpCurrentDirectory := CurrentDirectory;
- LastNameofCurrentDirectory := LastNameofCurrentDirectory + SRec.Name + '\';
- CurrentDirectory := CurrentDirectory + SRec.Name + '\';
- FindFile(copy(ADirName, 1, Length(ADirName) - 3) + SRec.Name + '\*.*');
- LastNameofCurrentDirectory := tmpLastNameofCurrentDirectory;
- CurrentDirectory := tmpCurrentDirectory;
- Continue;
- end;
- end;
- FFiles[FCurrent].ReadFileName := CurrentDirectory + SRec.Name;
- FFiles[FCurrent].ConvertFileName := ARootName + LastNameofCurrentDirectory + SRec.Name;
- Inc(FCurrent);
- until FindNext(SRec) <> 0;
- SysUtils.FindClose(SRec);
- end;
- end;
- begin
- CheckFileCellsCounts;
- Check(ADirName);
- if ARootName = ' ' then
- ARootName := ''
- else
- Check(ARootName);
-
- CurrentDirectory := ADirName;
- LastNameofCurrentDirectory := _CnExtractFileName(CurrentDirectory);
-
- if Length(ADirName) = 3 then //is 'xyz:\'
- LastNameofCurrentDirectory := '';
- FFiles[FCurrent].ReadFileName := ADirName + IntToStr(GetFileAttributes(PChar(ADirName))) + '?';
- FFiles[FCurrent].ConvertFileName := ARootName + IntToStr(GetFileAttributes(PChar(ADirName))) + '?';
- Inc(FCurrent);
- ADirName := ADirName + '*.*';
- FindFile(ADirName);
- end;
- procedure TCnFilePacker.AddDircetory(ADirName: string);
- begin
- AddDircetory(' ', ADirName);
- end;
- procedure TCnFilePacker.AddFile(AFileName: string);
- begin
- CheckFileCellsCounts;
- FFiles[FCurrent].ReadFileName := '?';
- FFiles[FCurrent].ConvertFileName := '16' + '?';
- Inc(FCurrent);
- FFiles[FCurrent].ReadFileName := AFileName;
- FFiles[FCurrent].ConvertFileName := _CnExtractFilename(AFileName);
- Inc(FCurrent);
- end;
- procedure TCnFilePacker.AddFile(ADirName, AFileName: string);
- begin
- CheckFileCellsCounts;
- check(ADirName);
- FFiles[FCurrent].ReadFileName := ADirName + '?';
- FFiles[FCurrent].ConvertFileName := ADirName + '16' + '?';
- Inc(FCurrent);
- FFiles[FCurrent].ReadFileName := AFileName;
- FFiles[FCurrent].ConvertFileName := ADirName + _CnExtractFilename(AFileName);
- Inc(FCurrent);
- end;
- procedure TCnFilePacker.CheckFileCellsCounts;
- begin
- if FCurrent >= FCount then
- begin
- FCount := FCount + SCnIncCounts;
- SetLength(FFiles, FCount);
- end;
- end;
- procedure TCnFilePacker.CompressData(var AStream: TBytes; var ALength: Cardinal);
- begin
- if FCompressInterface = nil then
- DoCompressData(AStream, ALength)
- else if CompressMode = FCompressInterface.GetCompressMode then
- FCompressInterface.DoCompressData(AStream, ALength);
- end;
- constructor TCnFilePacker.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- //fms := TMemoryStream.Create;
- FCompress := False;
- FPackedSubDirectory := true;
- FAddFilesCount := -1;
- FCurrent := 0;
- FCount := 20;
- SetLength(FFiles, FCount);
- FCreateSavePath := False;
- end;
- procedure TCnFilePacker.CreateDirectory;
- var
- i: Integer;
- S, DirName: string;
- attr: Byte;
- begin
- if not FCreateSavePath then
- begin
- ForceDirectories(SavePath);
- FCreateSavePath := True;
- end;
-
- for I := 0 to FFileinfoCount - 1 do
- begin
- S := {$IFDEF UNICODE}String{$ENDIF}(FPackFileInformations[i].Name);
- if Length(s) < 7 then
- Continue; //xyz:\16?
-
- if s[Length(s)] = '?' then
- begin
- attr := StrToInt(Copy(s, Length(s) - 2, 2));
- s := Copy(s, 1, Length(s) - 3);
- DirName := SavePath + '\' + s;
- ForceDirectories(DirName);
- SetFileAttributes(PChar(dirname), attr);
- end
- end;
- end;
- procedure TCnFilePacker.DeCompressData(var AStream: TBytes; var ALength: Cardinal);
- begin
- if FCompressInterface = nil then
- DoDeCompressData(AStream, ALength)
- else if CompressMode = FCompressInterface.GetCompressMode then
- FCompressInterface.DoDeCompressData(AStream, ALength);
- end;
- destructor TCnFilePacker.Destroy;
- begin
- if FPackHeaderInfo <> nil then
- begin
- FreeMem(FPackHeaderInfo);
- FPackHeaderInfo := nil;
- end;
- inherited;
- end;
- procedure TCnFilePacker.DoCompressData(var AStream: TBytes; var ALength: Cardinal);
- begin
- end;
- procedure TCnFilePacker.DoDeCompressData(var AStream: TBytes; var ALength: Cardinal);
- begin
- end;
- procedure TCnFilePacker.SaveToFile(APackFileInfo: TPackFileInformation);
- var
- f: TFileStream; //临时文件流,保存文件
- db: TDataBlock;
- Tdb: TBytes; //临时缓冲区,存中间数据
- S: string;
- begin
- S := {$IFDEF UNICODE}String{$ENDIF}(APackFileInfo.Name);
- if (s = '') or (s[Length(s)] = '?') then
- Exit;
-
- try
- FDestFile := TFileStream.Create(DestFileName, fmOpenReadWrite);
- if fSavePath[length(fSavePath)] = '\' then
- SetLength(fSavePath, Length(fSavePath) - 1);
- FDestFile.Position := APackFileInfo.DataStart;
- FDestFile.Read(db, SizeOf(db));
-
- if db.DataLength <> 0 then
- begin
- SetLength(Tdb, db.DataLength);
- FDestFile.Read(Tdb[0], db.DataLength);
- f := TFileStream.Create(SavePath + '\' + S, fmCreate or fmOpenReadWrite);
- if CompressMode <> cmNONE then
- DeCompressData(Tdb, db.DataLength);
- f.Write(tdb[0], db.DataLength);
- f.Free;
- end
- else
- begin
- f := TFileStream.Create(SavePath + '\' + S, fmCreate or fmOpenReadWrite);
- f.Free;
- end;
- finally
- FreeAndNil(FDestFile);
- end;
- end;
- function TCnFilePacker.GetPackFileInformation: TArrayPackFileInformation;
- var
- i: Integer;
- db: TDataBlock;
- fms: TFileStream; //临时文件流
- begin
- if FPackHeaderInfo <> nil then
- begin
- FreeMem(FPackHeaderInfo);
- FPackHeaderInfo := nil;
- end;
- FPackHeaderInfo := GetPackHeader;
- CompressMode := FPackHeaderInfo^.Compress;
- if FPackHeaderInfo^.ZipName <> 'CNPACKER' then//文件头不是cnpacker,退出
- Exit;
- Fms := TFileStream.Create(DestFileName, fmOpenRead);
- Fms.Position := SizeOf(TpackHeader);
- SetLength(Result, FPackHeaderInfo^.FileInfoCount);
- FFileinfoCount := FPackHeaderInfo^.FileInfoCount;
-
- for I := 0 to FPackHeaderInfo^.FileInfoCount - 1 do
- begin
- Fms.Read(db, SizeOf(db));
- StrCopy(Result[i].Name, db.FileName);
- Result[i].DataStart := Fms.Position - SizeOf(db);
- Fms.Position := Fms.Position + LongInt(db.DataLength);
- end;
- Fms.Free;
- end;
- function TCnFilePacker.GetPackHeader: PPackHeader;
- var
- fms: TFileStream;
- begin
- GetMem(Result, SizeOf(TPackHeader));
- Fms := TFileStream.Create(DestFileName, fmOpenRead);
- Fms.Position := 0;
- Fms.Read(Result^, SizeOf(TPackHeader));
- FreeAndNil(fms);
- end;
- procedure TCnFilePacker.GetComponentInfo(var AName, Author, Email,
- Comment: string);
- begin
- AName := SCnFilePackerName;
- Author := SCnPack_ZiMin;
- Email := SCnPack_ZiMinEmail;
- Comment := SCnFilePackerComment;
- end;
- function TCnFilePacker.GetPropGetPackFileDirectoryInfo: TArrayPackFileInformation;
- var
- i: Cardinal;
- S: string;
- count, current: Cardinal;
- begin
- count := SCnIncCounts;
- current := 0;
- SetLength(FImprotPackDirectoryInfo, count);
- FPackFileInformations := GetPackFileInformation;
-
- for I := 0 to FFileinfoCount - 1 do
- begin
- S := {$IFDEF UNICODE}String{$ENDIF}(FPackFileInformations[i].Name);
- if S[Length(s)] = '?' then
- begin
- S := IncludeTrailingBackslash(_CnExtractFilePath(S));
- if current = count then
- begin
- count := count + SCnIncCounts;
- SetLength(FImprotPackDirectoryInfo, count);
- end;
-
- StrPCopy(FImprotPackDirectoryInfo[current].Name, {$IFDEF UNICODE}AnsiString{$ENDIF}(S));
- FImprotPackDirectoryInfo[current].DataStart := FPackFileInformations[i].DataStart;
- Inc(current);
- end;
- end;
- SetLength(FImprotPackDirectoryInfo, current);
- Result := FImprotPackDirectoryInfo;
- end;
- function TCnFilePacker.GetPropGetPackFileInformation: TArrayPackFileInformation;
- var
- i: Cardinal;
- S: string;
- count, current: Cardinal;
- begin
- count := SCnIncCounts;
- current := 0;
- SetLength(FImportPackFileInfo, count);
- FPackFileInformations := GetPackFileInformation;
-
- for I := 0 to FFileinfoCount - 1 do
- begin
- S := {$IFDEF UNICODE}String{$ENDIF}(FPackFileInformations[i].Name);
- if S[Length(s)] <> '?' then
- begin
- if current = count then
- begin
- count := count + SCnIncCounts;
- SetLength(FImportPackFileInfo, count);
- end;
-
- FImportPackFileInfo[current].Name := FPackFileInformations[i].Name;
- FImportPackFileInfo[current].DataStart := FPackFileInformations[i].DataStart;
- Inc(current);
- end;
- end;
-
- SetLength(FImportPackFileInfo, current);
- Result := FImportPackFileInfo;
- end;
- function TCnFilePacker.GetPropGetPackHeader: TPackHeader;
- begin
- if FPackHeaderInfo <> nil then
- begin
- FreeMem(FPackHeaderInfo);
- FPackHeaderInfo := nil;
- end;
- FPackHeaderInfo := GetPackHeader;
- Result := FPackHeaderInfo^;
- end;
- procedure TCnFilePacker.DoPack();
- var
- ph: TPackHeader;
- db: TDataBlock;
- i: Integer;
- Tdb: TBytes;
- f: TFileStream;
- begin
- FillChar(ph, SizeOf(Tpackheader), #0);
- if DestFileName = '' then
- ECnFilePackerException.Create(SFileNameError);
- if not FileExists(DestFileName) then
- begin
- FPack := TFileStream.Create(DestFileName, fmCreate);
- FPack.Position := 0;
- //步过文件头
- FPack.Seek(SizeOf(TPackHeader), soFromCurrent);
- end
- else
- begin
- FPack := TFileStream.Create(DestFileName, fmOpenReadWrite);
- FPack.Read(ph, SizeOf(ph));
- FPack.Position := FPack.Size;
- end;
-
- //循环all文件
- for I := 0 to FCurrent - 1 do
- begin
- if FFiles[i].ReadFileName[Length(FFiles[i].ReadFileName)] = '?' then
- begin
- strpcopy(db.FileName, {$IFDEF UNICODE}AnsiString{$ENDIF}(Ffiles[i].ConvertFileName));
- db.DataLength := 0;
- FPack.Write(db, SizeOf(db));
- end
- else
- begin
- f := TFileStream.Create(FFiles[i].ReadFileName, fmOpenRead);
- strpcopy(db.FileName, {$IFDEF UNICODE}AnsiString{$ENDIF}(Ffiles[i].ConvertFileName));
- db.DataLength := F.Size;
- if db.DataLength <> 0 then
- begin
- SetLength(Tdb, db.DataLength);
- f.Read(Tdb[0], db.DataLength);
- if CompressMode <> cmNONE then
- CompressData(tdb, db.DataLength);
- FPack.Write(db, SizeOf(db));
- FPack.Write(tdb[0], db.DataLength);
- FreeAndNil(f);
- end
- else
- begin
- FPack.Write(db, SizeOf(db));
- FreeAndNil(f);
- end;
- end;
- end;
-
- //写文件头
- ph.ZipName := 'CNPACKER';
- ph.Compress := CompressMode;
- ph.FileSize := FPack.Size;
- Inc(ph.FileInfoCount, FCurrent);
- FPack.Position := 0;
- FPack.Write(ph, SizeOf(ph));
- FreeAndNil(FPack);
- FCurrent := 0;
- FCount := 20;
- SetLength(Ffiles, FCount);
- end;
- procedure TCnFilePacker.SaveToFiles;
- var
- i: integer;
- begin
- if FPackFileInformations = nil then
- FPackFileInformations := self.GetPackFileInformation; //先得到目录,
- Self.CreateDirectory; //创建目录
- for I := 0 to Length(FPackFileInformations) - 1 do
- begin
- Self.SaveToFile(FPackFileInformations[i]); //枚举调用解包每个文件
- end;
- FreeAndNil(FDestFile);
- end;
- end.
|