( ゚Д゚)ノ Delギコ猫のプログラミング相談室part2

このエントリーをはてなブックマークに追加
117104
第一段階:複数ファイルの(ヘッダ尽きでの)結合。

{Files で指定されたファイルをヘッダ付きで結合する
ファイル名は256バイトまでで、それ以上は切り捨てられる
上書き確認はしない}
procedure MakeArchive(const Files:TStrings; const Newfile:string);
var
i,p,Size:integer;
Source:TMemoryStream;
Dest:TMemoryStream;
Buf:array [0..256-1] of Char;
SizeBuf:array [0..16-1] of Byte;
Name:string;
begin
Source:=TMemoryStream.Create;
Dest := TMemoryStream.Create;

try
for i:=0 to Files.Count-1 do
begin
{ファイルを読み込む}
if not FileExists( Files[i] ) then
begin
showmessage('FILE NOT FOUND'+#13+Files[i]);
Exit;
end;

Source.LoadFromFile( Files[i] );


{ファイル名をBufにセット}
Name := ExtractFileName( Files[i] );
StrPLCopy( Buf, Name, SizeOf( Buf ) );

{ヘッダ:最初の256バイトはファイル名を示す}
Dest.WriteBuffer( Buf, 256 );

{ヘッダ:次の 16 バイトはファイルサイズを示す}
Size := Source.Size;
for p:=0 to High( SizeBuf ) do
begin
SizeBuf[p] := Size and $FF;
Size := Size shr 8;
end;

Dest.WriteBuffer( SizeBuf, 16 );

{データ:バイナリを書き込む}
Dest.CopyFrom( Source, 0 );
end;

Dest.SaveToFile( Newfile );

finally
Dest.Free;
Source.Free;
end;
end;
118104:01/09/05 05:10 ID:0bwO4T7E
すまん、インデント忘れ。

{Files で指定されたファイルをヘッダ付きで結合する
ファイル名は256バイトまでで、それ以上は切り捨てられる
上書き確認はしない}
procedure MakeArchive(const Files:TStrings; const Newfile:string);
var
 i,p,Size:integer;
 Source:TMemoryStream;
 Dest:TMemoryStream;
 Buf:array [0..256-1] of Char;
 SizeBuf:array [0..16-1] of Byte;
 Name:string;
begin
 Source:=TMemoryStream.Create;
 Dest := TMemoryStream.Create;

 try
  for i:=0 to Files.Count-1 do
  begin
   {ファイルを読み込む}
   Source.LoadFromFile( Files[i] );

   {ファイル名をBufにセット}
   Name := ExtractFileName( Files[i] );
   StrPLCopy( Buf, Name, SizeOf( Buf ) );

   {ヘッダ:最初の256バイトはファイル名を示す}
   Dest.WriteBuffer( Buf, 256 );

   {ヘッダ:次の 16 バイトはファイルサイズを示す}
   Size := Source.Size;
   for p:=0 to High( SizeBuf ) do
   begin
    SizeBuf[p] := Size and $FF;
    Size := Size shr 8;
   end;

   Dest.WriteBuffer( SizeBuf, 16 );

   {データ:バイナリを書き込む}
   Dest.CopyFrom( Source, 0 );
  end;

  Dest.SaveToFile( Newfile );

 finally
  Dest.Free;
  Source.Free;
 end;
end;
119104:01/09/05 05:12 ID:0bwO4T7E
続き。>>103 で結合したファイルを分離。

{ MakeArchive関数で結合されたアーカイブ Filename を分離し
DestPath ディレクトリ内に保存する ファイル名は 256 バイトまででそれ以上は切り捨て
上書き確認はしない}

procedure SeparateArchive(const Filename, DestPath:string);
var
 Filesize:Int64;
 i:integer;
 Source:TMemoryStream;
 Dest:TMemoryStream;
 Buf:array [0..256-1] of Char;
 SizeBuf:array [0..16-1] of Byte;
 Name:string;
begin
 Source:=TMemoryStream.Create;
 Dest := TMemoryStream.Create;
 try
  Source.LoadFromFile( Filename );

  repeat
   {ヘッダ:最初の256バイトをGET}
   Source.ReadBuffer( Buf, 256 );
   Name := Buf;

   {ヘッダ:次の16バイトをGET}
   Source.ReadBuffer( SizeBuf, 16 );
   Filesize:=0;
   for i:=0 to 16-1 do
    Inc( Filesize, SizeBuf[i] shl (i*8) );

   {ファイルを抽出}
   Dest.Clear;
   Dest.CopyFrom( Source, Filesize );

   {保存}
   Dest.SaveToFile( IncludeTrailingPathDelimiter(DestPath) + Name );
  until Source.Position >= Source.Size;
 finally
  Source.Free;
  Dest.Free;
 end;
end;
120104:01/09/05 05:14 ID:0bwO4T7E
>>119

>続き。>>103 で結合したファイルを分離。
103じゃなくて118だ…

両関数とも動作確認済み。
もうすこし効率よくかけそうなのでアドバイスキボン。
121104:01/09/05 05:33 ID:0bwO4T7E
さらにつくってみた。動作確認済み。

{ MakeArchive関数で結合されたアーカイブ Filename をから、
Target で指定されたファイルを探し出し Stream に抽出する。
アーカイブ内に目的のファイルが存在するならば True を返す}
function ExtractFromArchiveToStream(Stream:TStream;const Archive, Target:string):Boolean;
var
 Filesize:Int64;
 i:integer;
 Source:TMemoryStream;
 Dest:TMemoryStream;
 Buf:array [0..256-1] of Char;
 SizeBuf:array [0..16-1] of Byte;
 Name:string;
begin
 result := False;

 Source:=TMemoryStream.Create;
 Dest := TMemoryStream.Create;
 try
  Source.LoadFromFile( Archive );

  repeat
   {ヘッダ:最初の256バイトをGET}
   Source.ReadBuffer( Buf, 256 );
   Name := Buf;

   {ヘッダ:次の16バイトをGET}
   Source.ReadBuffer( SizeBuf, 16 );
   Filesize:=0;
   for i:=0 to 16-1 do
    Inc( Filesize, SizeBuf[i] shl (i*8) );

   {ターゲットファイルかどうか}
   if AnsiSameText( Name, Target ) then
   begin
    {ファイルを抽出}
    Dest.Clear;
    Dest.CopyFrom( Source, Filesize );
    Dest.SaveToStream( Stream );
    {終了}
    Result := True;
    Exit;
   end
   else
    {POSITION をサイズぶんだけ移動}
    Source.Position := Source.Position + Filesize;
  until Source.Position >= Source.Size;
 finally
  Source.Free;
  Dest.Free;
 end;
end;
122104:01/09/05 05:34 ID:0bwO4T7E
コレで最後。

{ MakeArchive関数で結合されたアーカイブ Filename をから、
Target で指定されたファイルを探し出し DestPath ディレクトリ内に保存する
アーカイブ内に目的のファイルが存在するならば True を返す}
function ExtractFromArchive(const Archive, Target, DestPath:string):Boolean;
var
 Stream:TMemoryStream;
begin
 Result := False;

 Stream := TMemoryStream.Create;
 try
  if ExtractFromArchiveToStream(Stream,Archive,Target) then
  begin
   Stream.SaveToFile( IncludeTrailingPathDelimiter(DestPath)+Target );
   result := True;
  end;
 finally
  Stream.Free;
 end;
end;
123104:01/09/05 05:36 ID:0bwO4T7E
うーむ、ソースUP板に書くべきだったか…?
しかし、これに圧縮関数を組み合わせれば
相当いろんな事ができると思うのだが。

有益な関数だと自負するぞ。
124104:01/09/05 06:00 ID:0bwO4T7E
さらに調子に乗ってみた。動作確認済み。

{ MakeArchive関数で結合されたアーカイブ Archibve から
Target で指定されたファイルを削除する。アーカイブ内に目的のファイルがない場合は
Falseを返す}
function RemoveFromArchive(const Archive, Target:string):Boolean;
var
 Filesize:Int64;
 i:integer;
 Source,Dest:TMemoryStream;
 NameBuf:array [0..256-1] of Char;
 SizeBuf:array [0..16-1] of Byte;
 Name:string;
begin
 result := False;

 Source:=TMemoryStream.Create;
 Dest := TMemoryStream.Create;
 try
  Source.LoadFromFile( Archive );
  repeat
   {ヘッダ:最初の256バイトをGET}
   Source.ReadBuffer( NameBuf, SizeOf( NameBuf ) );
   Name := NameBuf;
   {ヘッダ:次の16バイトをGET}
   Source.ReadBuffer( SizeBuf, SizeOf( SizeBuf ) );
   Filesize:=0;
   for i:=0 to High( SizeBuf ) do
    Inc( Filesize, SizeBuf[i] shl (i*8) );
   {ターゲットファイルかどうか}
   if AnsiSameText( Name, Target ) then
   begin
    { position をサイズ分だけ移動し、読み飛ばす}
    Source.Position := Source.Position + Filesize;
   end
   else
   begin
    {新アーカイブへ内容をコピー}
    Dest.WriteBuffer( NameBuf,SizeOf( NameBuf ) );
    Dest.WriteBuffer( SizeBuf,SizeOf( SizeBuf ) );
    Dest.CopyFrom( Source, Filesize );
   end;
  until Source.Position >= Source.Size;
  Dest.SaveToFile( Archive );
 finally
  Source.Free;
  Dest.Free;
 end;
end;
125104:01/09/05 06:01 ID:0bwO4T7E
とどめだ。動作確認済み。

{ MakeArchive関数で結合されたアーカイブ内のファイル名リストを得る
Files.strings にファイル名が、
Files.objects にファイルサイズ(バイト)が格納される}
procedure ArchivedFiles(const Archive:string; Files:TStrings);
var
 Filesize:Int64;
 i:integer;
 Source:TMemoryStream;
 NameBuf:array [0..256-1] of Char;
 SizeBuf:array [0..16-1] of Byte;
begin
 Files.Clear;
 Source:=TMemoryStream.Create;
 try
  Source.LoadFromFile( Archive );
  repeat
   {ヘッダ:最初の256バイトをGET}
   Source.ReadBuffer( NameBuf, 256 );

   {ヘッダ:次の16バイトをGET}
   Source.ReadBuffer( SizeBuf, 16 );
   Filesize:=0;
   for i:=0 to 16-1 do
    Inc( Filesize, SizeBuf[i] shl (i*8) );
   {ファイル名とサイズを追加}
   Files.AddObject( NameBuf , TObject(Filesize) );
  
   { position をサイズ分だけ移動し、読み飛ばす}
   Source.Position := Source.Position + Filesize;
  until Source.Position >= Source.Size;
 finally
  Source.Free;
 end;
end;