unit dpacker;

interface

uses
  derror,
  dtypes,
  dstream,
  dstreamfile,
  dunpacker;

type
TPackTaskFile = record
  Path: AnsiString;
  Data: TData;
end;

PPackTask = ^TPackTask;
TPackTask = record
  OutputPath: AnsiString;
  Files: array of TPackTaskFile;
end;

function DojPack(Task: PPackTask; Error: PError = nil): Boolean;

implementation

function DojPack(Task: PPackTask; Error: PError = nil): Boolean;
var
  I: LongInt;
  Header: TDojPackHeader;
  Files: array of TDojPackFileInfo;
  OutputStream: PStream;
  Offset, Size: TInt64;
begin
  OutputStream := OutFile(Task.OutputPath);

  Header.Magic := DOJ_PACK_MAGIC;
  Header.Format := DOJ_PACK_FORMAT_BIN;
  Header.Version := DOJ_PACK_VERSION;
  Header.FileListOffset := SizeOf(TDojPackHeader);
  Header.FilesCount := Length(Task.Files);
  Header.ContentOffset := SizeOf(TDojPackHeader); // will be calculated later

  for I := 0 to Header.FilesCount - 1 do
    Inc(Header.ContentOffset, Length(Task.Files[I].Path) + SizeOf(TUint32) + 2 * SizeOf(Int64));

  if OutputStream.Write(@Header, SizeOf(Header)) < SizeOf(Header) then
    Exit(SetupError(Error, 1, 'can not write pack header'));

  Offset := 0;
  for I := 0 to Header.FilesCount - 1 do begin
    OutputStream.WriteStr(Task.Files[I].Path);
    OutputStream.WriteUint64(Offset);
    Size := Task.Files[I].Data.Stream.Size;
    OutputStream.WriteUint64(Size);
    Inc(Offset, Size);
  end;

  for I := 0 to Header.FilesCount - 1 do begin
    Size := Task.Files[I].Data.Stream.Size;
    if OutputStream.CopyFrom(Task.Files[I].Data.Stream, Size) < Size then begin
      Exit(SetupError(Error, 1, 'can not write file ' + Task.Files[I].Path))
    end;
    Task.Files[I].Data.Done;
  end;

  OutputStream.Free;
  Exit(True);
end;

end.
