unit CombineFilesUnit; interface uses Windows, Messages, SysUtils, Classes; type TFileInfoData = record Size:Cardinal; Offset:Cardinal; Name:array[0..30] of Char; end; TCombineFilesHeader = record NumberOfFiles:integer; FileInfo:array[0..19] of TFileInfoData; end; TMemoryStreamArray = array[0..19] of TMemoryStream; function CombineFiles(MS:TMemoryStream;FileList:TStrings):Boolean; function GetFileHeader(CombFile:string;var CFH:TCombineFilesHeader):Boolean; procedure SplitCombFile(CombFile:string; var MSA:TMemoryStreamArray;var CFH:TCombineFilesHeader); function FindFileIndex(CombFile, FileName:string):integer; function ExtractFileFromCombined(CombFile:string; indx:integer; MS:TMemoryStream):Boolean; function AddCombineFile(SourceFile,AddFile:string):Boolean; function DeleteFromCombFile(CombFile:string; DeleteIndex:integer):Boolean; implementation function CombineFiles(MS:TMemoryStream;FileList:TStrings):Boolean; var CFH:TCombineFilesHeader; EachFile:TMemoryStream; i,Num:integer; begin result := false; if FileList.Count = 0 then exit; if FileList.Count > 20 then Num := 20 else Num := FileList.Count; FillChar(CFH,SizeOf(TCombineFilesHeader),#0); MS.Clear; EachFile := TMemoryStream.Create; try CFH.NumberOfFiles := Num; MS.Position := SizeOf(CFH); for i := 0 to Num-1 do begin EachFile.LoadFromFile(FileList[i]); with CFH.FileInfo[i] do begin Size := EachFile.Size; Offset := MS.Position; StrPLCopy(Name,ExtractFileName(FileList[i]),30); end; MS.Write(EachFile.Memory^,EachFile.Size); end; MS.Position := 0; MS.Write(CFH,SizeOf(CFH)); result := true; finally EachFile.Free; end; end; function GetFileHeader(CombFile:string;var CFH:TCombineFilesHeader):Boolean; var FS:TFileStream; begin FS := TFileStream.Create(CombFile,fmOpenRead or fmShareDenyWrite); try result := false; FS.Position := 0; FS.Read(CFH,SizeOf(CFH)); result := true; finally FS.Free; end; end; procedure SplitCombFile(CombFile:string; var MSA:TMemoryStreamArray;var CFH:TCombineFilesHeader); var MS:TMemoryStream; i:integer; begin if not GetFileHeader(CombFile,CFH) then exit; MS := TMemoryStream.Create; for i := 0 to CFH.NumberOfFiles-1 do MSA[i] := TMemoryStream.Create; try MS.LoadFromFile(CombFile); for i := 0 to CFH.NumberOfFiles-1 do begin MSA[i].SetSize(CFH.FileInfo[i].Size); MS.Position := CFH.FileInfo[i].Offset; MS.Read(MSA[i].Memory^,CFH.FileInfo[i].Size); end; finally MS.Free; end; end; function FindFileIndex(CombFile, FileName:string):integer; var CFH:TCombineFilesHeader; i:integer; begin result := -1; if GetFileHeader(CombFile,CFH) then for i := 0 to CFH.NumberOfFiles-1 do if UpperCase(CFH.FileInfo[i].Name) = UpperCase(FileName) then begin result := i; exit; end; end; function ExtractFileFromCombined(CombFile:string; indx:integer; MS:TMemoryStream):Boolean; var CFH:TCombineFilesHeader; Source:TMemoryStream; begin result := false; if not GetFileHeader(CombFile,CFH) then exit; if (indx < 0) or (indx > CFH.NumberOfFiles-1) then exit; Source := TMemoryStream.Create; try Source.LoadFromFile(CombFile); MS.Clear; Source.Position := CFH.FileInfo[indx].Offset; MS.SetSize(CFH.FileInfo[indx].Size); Source.Read(MS.Memory^,CFH.FileInfo[indx].Size); result := true; finally Source.Free; end; end; function AddCombineFile(SourceFile,AddFile:string):Boolean; var CFH:TCombineFilesHeader; MS,EachFile:TMemoryStream; Num:integer; begin MS := TMemoryStream.Create; EachFile := TMemoryStream.Create; try MS.LoadFromFile(SourceFile); MS.Position := 0; MS.Read(CFH,SizeOf(CFH)); Num := CFH.NumberOfFiles; if Num < 20 then begin EachFile.LoadFromFile(AddFile); CFH.NumberOfFiles := Num+1; MS.Position := MS.Size; with CFH.FileInfo[Num] do begin Size := EachFile.Size; Offset := MS.Position; StrPLCopy(Name,ExtractFileName(AddFile),30); end; MS.Write(EachFile.Memory^,EachFile.Size); MS.Position := 0; MS.Write(CFH,SizeOf(CFH)); MS.SaveToFile(SourceFile); result := true; end else result := false; finally MS.Free; EachFile.Free; end; end; function DeleteFromCombFile(CombFile:string; DeleteIndex:integer):Boolean; var CFH,NEWCFH:TCombineFilesHeader; Source,NewComb,Each:TMemoryStream; i,indx:integer; begin result := false; if GetFileHeader(CombFile,CFH) then begin if (DeleteIndex > CFH.NumberOfFiles-1) or (DeleteIndex < 0) then Exit; end else Exit; FillChar(NEWCFH,SizeOf(TCombineFilesHeader),#0); Source := TMemoryStream.Create; NewComb := TMemoryStream.Create; Each := TMemoryStream.Create; try Source.LoadFromFile(CombFile); indx := 0; NEWCFH.NumberOfFiles := CFH.NumberOfFiles-1; NewComb.Position := SizeOf(NEWCFH); for i := 0 to CFH.NumberOfFiles-1 do begin if i = DeleteIndex then continue; NEWCFH.FileInfo[indx] := CFH.FileInfo[i]; NEWCFH.FileInfo[indx].Offset := NewComb.Position; Source.Position := CFH.FileInfo[i].Offset; Each.SetSize(CFH.FileInfo[i].Size); Source.Read(Each.Memory^,CFH.FileInfo[i].Size); NewComb.Write(Each.Memory^,CFH.FileInfo[i].Size); Each.Clear; Inc(indx) end; NewComb.Position := 0; NewComb.Write(NEWCFH,SizeOf(CFH)); NewComb.SaveToFile(CombFile); result := true; finally Source.Free; NewComb.Free; Each.Free; end; end; end.