unit IODecode;

{$POINTERMATH ON}

interface

uses
  Threading, Utils, SynCommons, SynCrypto, ParseClass, ParseExpr,
  IOUtils,
  WinAPI.Windows, WinAPI.ShlObj,
  System.SysUtils, System.Classes, System.SyncObjs, System.Math, System.Types,
  System.StrUtils, System.RTLConsts, System.TimeSpan, System.Diagnostics,
  System.IOUtils, System.Generics.Defaults, System.Generics.Collections;

type
  PDecodeOptions = ^TDecodeOptions;

  TDecodeOptions = record

  end;

  PExtractOptions = ^TExtractOptions;

  TExtractOptions = record

  end;

procedure PrintHelpExtract;
procedure ParseDecode(ParamArg: TArray<string>; out Options: TDecodeOptions);
procedure ParseExtract(ParamArg: TArray<string>; out Options: TExtractOptions);
procedure Decode(Input1: TStream; Input2, Output: String;
  Options: TDecodeOptions);
procedure Extract(Input1: TStream; Input2, Output: String;
  Options: TExtractOptions);

implementation

procedure PrintHelpExtract;
var
  I, J: Integer;
  S: string;
begin
  WriteLn(ErrOutput, 'extract - extract sectors from files');
  WriteLn(ErrOutput, '');
  WriteLn(ErrOutput, 'Usage:');
  WriteLn(ErrOutput,
    '  xtool extract decode_data original_data extracted_streams ');
  WriteLn(ErrOutput, '');
end;

procedure ParseDecode(ParamArg: TArray<string>; out Options: TDecodeOptions);
var
  ArgParse: TArgParser;
  ExpParse: TExpressionParser;
  S: String;
begin
  ArgParse := TArgParser.Create(ParamArg);
  ExpParse := TExpressionParser.Create;
  try
    S := '';
  finally
    ArgParse.Free;
    ExpParse.Free;
  end;
end;

procedure ParseExtract(ParamArg: TArray<string>; out Options: TExtractOptions);
var
  ArgParse: TArgParser;
  ExpParse: TExpressionParser;
  S: String;
begin
  ArgParse := TArgParser.Create(ParamArg);
  ExpParse := TExpressionParser.Create;
  try
    S := '';
  finally
    ArgParse.Free;
    ExpParse.Free;
  end;
end;

procedure Decode(Input1: TStream; Input2, Output: String;
  Options: TDecodeOptions);
var
  I, J: Integer;
  LEntry: TEntryStruct1;
  LBytes: TBytes;
  LFilename: String;
  BaseDir1, BaseDir2: String;
  SS1, SS2: TFileStreamEx;
begin
  if FileExists(Input2) then
    BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
  else if DirectoryExists(Input2) then
    BaseDir1 := IncludeTrailingPathDelimiter(TPath.GetFullPath(Input2))
  else
    BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
  if FileExists(Output) then
    BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
  else if DirectoryExists(Output) then
    BaseDir2 := IncludeTrailingPathDelimiter(TPath.GetFullPath(Output))
  else
    BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output));
  while true do
  begin
    try
      Input1.ReadBuffer(I, I.Size);
    except
      break;
    end;
    SetLength(LBytes, I);
    Input1.ReadBuffer(LBytes[0], I);
    SS1 := TFileStreamEx.Create(BaseDir2 + StringOf(LBytes));
    try
      Input1.ReadBuffer(I, I.Size);
      for J := 0 to I - 1 do
      begin
        Input1.ReadBuffer(LEntry, SizeOf(TEntryStruct1));
        LFilename := BaseDir1 + LEntry.Filename;
        if FileExists(LFilename) then
        begin
          SS2 := TFileStreamEx.Create(LFilename);
          try
            SS1.Position := LEntry.Position;
            SS2.CopyTo(SS1, Min(SS2.Size, LEntry.Size));
          finally
            SS2.Free;
          end;
          WriteLn(ErrOutput, Format('Restored %s', [LEntry.Filename]));
        end;
      end;
    finally
      SS1.Free;
    end;
  end;
end;

procedure Extract(Input1: TStream; Input2, Output: String;
  Options: TExtractOptions);
var
  I, J: Integer;
  LEntry: TEntryStruct1;
  LBytes: TBytes;
  LFilename: String;
  BaseDir1, BaseDir2: String;
  FS1, FS2: TFileStream;
begin
  if FileExists(Input2) then
    BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
  else if DirectoryExists(Input2) then
    BaseDir1 := IncludeTrailingPathDelimiter(TPath.GetFullPath(Input2))
  else
    BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
  if FileExists(Output) then
    BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
  else if DirectoryExists(Output) then
    BaseDir2 := IncludeTrailingPathDelimiter(TPath.GetFullPath(Output))
  else
    BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output));
  while true do
  begin
    try
      Input1.ReadBuffer(I, I.Size);
    except
      break;
    end;
    SetLength(LBytes, I);
    Input1.ReadBuffer(LBytes[0], I);
    FS1 := TFileStream.Create(BaseDir1 + StringOf(LBytes), fmShareDenyNone);
    try
      Input1.ReadBuffer(I, I.Size);
      for J := 0 to I - 1 do
      begin
        Input1.ReadBuffer(LEntry, SizeOf(TEntryStruct1));
        LFilename := BaseDir2 + LEntry.Filename;
        ForceDirectories(ExtractFilePath(LFilename));
        FS2 := TFileStream.Create(LFilename, fmCreate);
        try
          FS1.Position := LEntry.Position;
          CopyStreamEx(FS1, FS2, LEntry.Size);
        finally
          FS2.Free;
        end;
        WriteLn(ErrOutput, Format('Extracted %s', [LEntry.Filename]));
      end;
    finally
      FS1.Free;
    end;
  end;
end;

end.
