uses
  derror,
  dparser,
  dstream,
  dstreamfile,
  dstreammem,
  dunpacker,
  dpacker;

var
  Task: TPackTask;
  I: LongInt;
  Error: TError;
  Unpacker: TDojUnpacker;
  FileList: TDojUnpacker.TFileList;
  MemoryStream: TStreamMem;
  OutputStream: TStreamFile;
  Dir, FileName: AnsiString;

procedure PrintUsage();
begin
  Writeln(stderr, 'Usage:');
  Writeln(stderr, '  dpack input1 input2 ... inputN output.dpk');
  Writeln(stderr, '  dpack -e pack.dpk -d dir');
  Writeln(stderr, '  dpack -i pack.dpk');
  Writeln(stderr, '  dpack --cat pack.dpk file');
end;

procedure MkDirP(D: AnsiString; LastPartIsFile: Boolean);
var
  P, L, R: AnsiString;
begin
  P := '';
  while ParseBinary(D, '/', L, R) do begin
    P := P + L;
    {$I-} MkDir(P); {$I+}
    IOResult;
    P := P + '/';
    D := R;
  end;
  if not LastPartIsFile then begin
    {$I-} MkDir(P + '/' + D); {$I+}
    IOResult;
  end;
end;

begin
  if ParamCount = 0 then begin
    PrintUsage;
    Halt(0);
  end;
  if ParamStr(1)[1] <> '-' then begin
    Task.OutputPath := ParamStr(ParamCount);
    SetLength(Task.Files, ParamCount - 1);
    for I := 1 to ParamCount - 1 do begin
      Task.Files[I - 1].Path := ParamStr(I);
      Task.Files[I - 1].Data := ParamStr(I);
    end;
    if not DojPack(@Task, @Error) then begin
      Writeln(stderr, 'dpack error: ', Error.Msg);
      Halt(Error.Id);
    end;
  end else if (ParamStr(1) = '-i') or (ParamStr(1) = '--info') then begin
    Unpacker.Init(ParamStr(2));
    HaltIfError(Unpacker.Error);
    Unpacker.GetFileList(FileList);
    for I := 0 to High(FileList) do begin
      Writeln(FileList[I].Path, #9, FileList[I].Size);
    end;
    Unpacker.Done;
  end else if (ParamStr(1) = '--cat') and (ParamCount = 3) then begin
    Unpacker.Init(ParamStr(2));
    HaltIfError(Unpacker.Error);
    if not Unpacker.InitMemoryStream(ParamStr(3), MemoryStream) then
      HaltIfError(Unpacker.Error);
    while not MemoryStream.Eof do
      Write(AnsiChar(MemoryStream.ReadByte));
    MemoryStream.Done;
    Unpacker.Done;
  end else if (ParamStr(1) = '-e') or (ParamStr(1) = 'extract') then begin
    Dir := ParamStr(3);
    Unpacker.Init(ParamStr(2));
    HaltIfError(Unpacker.Error);
    Unpacker.GetFileList(FileList);
    for I := 0 to High(FileList) do begin
      FileName := Dir + '/' + FileList[I].Path;
      MkDirP(FileName, True);
      if not Unpacker.InitMemoryStream(FileList[I].Path, MemoryStream) then begin
        Writeln('dpack error: ' + Unpacker.Error.Msg);
        continue;
      end;
      OutputStream.Init(FileName, FM_WRITE);
      while OutputStream.CopyFrom(@MemoryStream, 1 shl 20) > 0 do
        ;
      OutputStream.Done;
      MemoryStream.Done;
    end;
    Unpacker.Done;
  end else begin
    Writeln(stderr, 'dpack error: unknown arguments');
    PrintUsage;
    Halt(1);
  end;
end.
