diff --git a/io/IOArchive.pas b/io/IOArchive.pas new file mode 100644 index 0000000..880c740 --- /dev/null +++ b/io/IOArchive.pas @@ -0,0 +1,165 @@ +unit IOArchive; + +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 + PEncodeOptions = ^TEncodeOptions; + + TEncodeOptions = record + + end; + + PDecodeOptions = ^TDecodeOptions; + + TDecodeOptions = record + + end; + +procedure PrintHelp; +procedure Parse(ParamArg: TArray; out Options: TEncodeOptions); + overload; +procedure Parse(ParamArg: TArray; out Options: TDecodeOptions); + overload; +procedure Encode(Input: TArray; Output: TStream; + Options: TEncodeOptions); +procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions); + +implementation + +procedure PrintHelp; +var + I, J: Integer; + S: string; +begin + WriteLn(ErrOutput, 'archive - convert a group of files into an archive'); + WriteLn(ErrOutput, ''); + WriteLn(ErrOutput, 'Usage:'); + WriteLn(ErrOutput, ' xtool archive files1 files2... archive'); + WriteLn(ErrOutput, ''); + WriteLn(ErrOutput, ''); +end; + +procedure Parse(ParamArg: TArray; out Options: TEncodeOptions); +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 Parse(ParamArg: TArray; 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 Encode(Input: TArray; Output: TStream; + Options: TEncodeOptions); +var + I, J: Integer; + K: Word; + I64: Int64; + BaseDir: String; + LList: TArray; + LBytes: TBytes; + FStream: TFileStream; +begin + I := XTOOL_ARCH; + Output.WriteBuffer(I, I.Size); + for I := Low(Input) to High(Input) do + begin + if FileExists(Input[I]) then + BaseDir := ExtractFilePath(TPath.GetFullPath(Input[I])) + else if DirectoryExists(Input[I]) then + BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input[I])) + else + BaseDir := ExtractFilePath(TPath.GetFullPath(Input[I])); + LList := GetFileList([Input[I]], True); + if Length(LList) > 0 then + begin + J := Length(LList); + Output.WriteBuffer(J, J.Size); + for J := Low(LList) to High(LList) do + begin + LBytes := BytesOf(ReplaceText(LList[I], BaseDir, '')); + K := Length(LBytes); + Output.WriteBuffer(K, K.Size); + Output.WriteBuffer(LBytes[0], K); + I64 := FileSize(LList[I]); + Output.WriteBuffer(I64, I64.Size); + FStream := TFileStream.Create(LList[I], fmShareDenyNone); + try + CopyStreamEx(FStream, Output, I64); + finally + FStream.Free; + end; + end; + end; + end; + J := J.MinValue; + Output.WriteBuffer(J, J.Size); +end; + +procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions); +var + I, J: Integer; + K: Word; + I64: Int64; + S: String; + BaseDir: String; + LBytes: TBytes; + FStream: TFileStream; +begin + BaseDir := IncludeTrailingBackSlash(Output); + Input.ReadBuffer(I, I.Size); + while I >= 0 do + begin + for J := 1 to I do + begin + Input.ReadBuffer(K, K.Size); + if Length(LBytes) < K then + SetLength(LBytes, K); + FillChar(LBytes[0], Length(LBytes), 0); + Input.ReadBuffer(LBytes[0], K); + S := BaseDir + StringOf(LBytes); + if not DirectoryExists(ExtractFilePath(S)) then + ForceDirectories(ExtractFilePath(S)); + Input.ReadBuffer(I64, I64.Size); + FStream := TFileStream.Create(S, fmCreate); + try + CopyStreamEx(Input, FStream, I64); + finally + FStream.Free; + end; + end; + Input.ReadBuffer(I, I.Size); + end; +end; + +end. diff --git a/io/IOExecute.pas b/io/IOExecute.pas new file mode 100644 index 0000000..6c7192b --- /dev/null +++ b/io/IOExecute.pas @@ -0,0 +1,477 @@ +unit IOExecute; + +(* xbcm:t4:c256m + xtool execute {options} - - bcm.exe -9 [filein] [fileout] + xtool decode {options} - - bcm.exe -d [filein] [fileout] *) + +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 + PEncodeOptions = ^TEncodeOptions; + + TEncodeOptions = record + ChunkSize, Threads: Integer; + end; + + PDecodeOptions = ^TDecodeOptions; + + TDecodeOptions = record + Threads: Integer; + end; + +procedure PrintHelp; +procedure Parse(ParamArg: TArray; out Options: TEncodeOptions); + overload; +procedure Parse(ParamArg: TArray; out Options: TDecodeOptions); + overload; +procedure Encode(Input, Output: TStream; ParamArg: TArray; + Options: TEncodeOptions); +procedure Decode(Input, Output: TStream; ParamArg: TArray; + Options: TDecodeOptions); + +implementation + +const + FILE_IN = 'data.in'; + FILE_OUT = 'data.out'; + FILE_MODE = 0; + STDIN_MODE = 1; + STDOUT_MODE = 2; + STDIO_MODE = 3; + STATE_READY = 0; + STATE_EXECUTED = 1; + STATE_ERROR = 2; + STATE_DONE = 3; + +procedure PrintHelp; +var + I, J: Integer; + S: string; +begin + WriteLn(ErrOutput, 'execute - parallel program execution'); + WriteLn(ErrOutput, ''); + WriteLn(ErrOutput, 'Usage:'); + WriteLn(ErrOutput, ' xtool execute [parameters] input output [exec_syntax]'); + WriteLn(ErrOutput, ''); + WriteLn(ErrOutput, ''); + WriteLn(ErrOutput, 'Parameters:'); + WriteLn(ErrOutput, ' -c# - chunk size [16mb]'); + WriteLn(ErrOutput, ' -t# - number of working threads [50p]'); + WriteLn(ErrOutput, ''); +end; + +procedure Parse(ParamArg: TArray; out Options: TEncodeOptions); +var + ArgParse: TArgParser; + ExpParse: TExpressionParser; + I: Integer; + S: String; +begin + ArgParse := TArgParser.Create(ParamArg); + ExpParse := TExpressionParser.Create; + try + S := ArgParse.AsString('-c', 0, '16mb'); + S := ReplaceText(S, 'KB', '* 1024^1'); + S := ReplaceText(S, 'MB', '* 1024^2'); + S := ReplaceText(S, 'GB', '* 1024^3'); + S := ReplaceText(S, 'K', '* 1024^1'); + S := ReplaceText(S, 'M', '* 1024^2'); + S := ReplaceText(S, 'G', '* 1024^3'); + Options.ChunkSize := Max(4194304, Round(ExpParse.Evaluate(S))); + S := ArgParse.AsString('-t', 0, '50p'); + S := ReplaceText(S, 'p', '%'); + S := ReplaceText(S, '%', '%*' + CPUCount.ToString); + Options.Threads := Max(1, Round(ExpParse.Evaluate(S))); + finally + ArgParse.Free; + ExpParse.Free; + end; +end; + +procedure Parse(ParamArg: TArray; out Options: TDecodeOptions); +var + ArgParse: TArgParser; + ExpParse: TExpressionParser; + S: String; +begin + ArgParse := TArgParser.Create(ParamArg); + ExpParse := TExpressionParser.Create; + try + S := ArgParse.AsString('-t', 0, '50p'); + S := ReplaceText(S, 'p', '%'); + S := ReplaceText(S, '%', '%*' + CPUCount.ToString); + Options.Threads := Max(1, Round(ExpParse.Evaluate(S))); + finally + ArgParse.Free; + ExpParse.Free; + end; +end; + +function ExtractStr(SubStr, Str: String): String; +var + I: Integer; +begin + Result := Str.Substring(Str.IndexOf(SubStr)); + I := Result.IndexOf(' '); + if I >= 0 then + Result := Result.Substring(0, Result.IndexOf(' ')); +end; + +type + PCtx = ^TCtx; + + TCtx = record + Exec, Param: String; + InFile, OutFile: String; + Mode: Byte; + end; + +procedure Init(ParamArg: TArray; Ctx: PCtx); +var + I: Integer; + S: String; +begin + with Ctx^ do + begin + Exec := ''; + Param := ''; + InFile := FILE_IN; + OutFile := FILE_OUT; + Mode := 0; + for I := Low(ParamArg) to High(ParamArg) do + begin + S := ParamArg[I]; + if ContainsText(S, '') or ContainsText(S, '[stdin]') then + begin + SetBits(Mode, 1, 0, 1); + continue; + end + else if ContainsText(S, '') or ContainsText(S, '[stdout]') then + begin + SetBits(Mode, 1, 1, 1); + continue; + end + else if ContainsText(S, '') or ContainsText(S, '[filein]') then + begin + SetBits(Mode, 0, 0, 1); + if ContainsText(S, '') then + InFile := ExtractStr('', S) + else + InFile := ExtractStr('[filein]', S); + S := ReplaceText(S, InFile, FILE_IN); + InFile := ReplaceText(InFile, '', FILE_IN); + InFile := ReplaceText(InFile, '[filein]', FILE_IN); + end + else if ContainsText(S, '') or ContainsText(S, '[fileout]') then + begin + SetBits(Mode, 0, 1, 1); + if ContainsText(S, '') then + OutFile := ExtractStr('', S) + else + OutFile := ExtractStr('[fileout]', S); + S := ReplaceText(S, OutFile, FILE_OUT); + OutFile := ReplaceText(OutFile, '', FILE_OUT); + OutFile := ReplaceText(OutFile, '[fileout]', FILE_OUT); + end; + if I = 0 then + Exec := ExtractFilePath(Utils.GetModuleName) + S + else + Param := Param + ' ' + IfThen(ContainsText(S, ' ') or (S = ''), + '"' + S + '"', S); + end; + end; +end; + +threadvar TFS: TFileStream; + +procedure Callback(const Buffer: Pointer; Size: Integer); +begin + TFS.WriteBuffer(Buffer^, Size); +end; + +procedure ExecThread(X, Ctx, WorkDir, State: IntPtr); +var + SS: TSharedMemoryStream; + Res: Boolean; +begin + Res := False; + with PCtx(Ctx)^ do + if FileExists(Exec) then + try + case Mode of + FILE_MODE: + Res := Utils.Exec(Exec, Param, PString(WorkDir)^); + STDIN_MODE: + begin + SS := TSharedMemoryStream.Create + (LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName), + '_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), + IncludeTrailingBackSlash(PString(WorkDir)^) + InFile); + try + Res := ExecStdin(Exec, Param, PString(WorkDir)^, + SS.Memory, SS.Size); + finally + SS.Free; + end; + end; + STDOUT_MODE: + begin + TFS := TFileStream.Create + (IncludeTrailingBackSlash(PString(WorkDir)^) + OutFile, + fmCreate); + try + Res := ExecStdout(Exec, Param, PString(WorkDir)^, Callback); + finally + TFS.Free; + end; + end; + STDIO_MODE: + begin + SS := TSharedMemoryStream.Create + (LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName), + '_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), + IncludeTrailingBackSlash(PString(WorkDir)^) + InFile); + TFS := TFileStream.Create + (IncludeTrailingBackSlash(PString(WorkDir)^) + OutFile, + fmCreate); + try + Res := ExecStdio(Exec, Param, PString(WorkDir)^, SS.Memory, + SS.Size, Callback); + finally + SS.Free; + TFS.Free; + end; + end; + end; + except + Res := False + end; + if Res then + PByte(State)^ := STATE_EXECUTED + else + PByte(State)^ := STATE_ERROR; +end; + +procedure Encode(Input, Output: TStream; ParamArg: TArray; + Options: TEncodeOptions); +var + I: Integer; + I64: Int64; + B: Byte; + S: String; + First, Done: Boolean; + FStream: TFileStream; + SStream: TSharedMemoryStream; + LCtx: TCtx; + WorkDir: TArray; + Tasks: TArray; + State: TArray; + + procedure Load(X: Integer); + begin + DeleteFile(IncludeTrailingBackSlash(WorkDir[X]) + LCtx.InFile); + DeleteFile(IncludeTrailingBackSlash(WorkDir[X]) + LCtx.OutFile); + if not Done then + begin + FStream := TFileStream.Create(IncludeTrailingBackSlash(WorkDir[X]) + + LCtx.InFile, fmCreate); + try + Done := CopyStream(Input, FStream, Options.ChunkSize) = 0; + finally + FStream.Free; + end; + end; + if Done then + State[X] := STATE_DONE + else + State[X] := STATE_READY; + if not Done then + Tasks[X].Start; + end; + +begin + I := XTOOL_EXEC; + Output.WriteBuffer(I, I.Size); + Init(ParamArg, @LCtx); + SetLength(WorkDir, Options.Threads); + SetLength(Tasks, Options.Threads); + SetLength(State, Options.Threads); + for I := Low(Tasks) to High(Tasks) do + begin + WorkDir[I] := IncludeTrailingBackSlash(GetCurrentDir) + + LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName), + '_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF1)); + CreateDir(WorkDir[I]); + Tasks[I] := TTask.Create(I, IntPtr(@LCtx), IntPtr(@WorkDir[I]), + IntPtr(@State[I])); + Tasks[I].Perform(ExecThread); + end; + First := True; + Done := False; + try + while State[0] <> STATE_DONE do + begin + if First then + begin + for I := Low(Tasks) to High(Tasks) do + Load(I); + First := False; + end; + for I := Low(Tasks) to High(Tasks) do + begin + Tasks[I].Wait; + if State[I] = STATE_DONE then + continue; + B := 0; + if State[I] = STATE_EXECUTED then + S := IncludeTrailingBackSlash(WorkDir[I]) + LCtx.OutFile + else + begin + S := IncludeTrailingBackSlash(WorkDir[I]) + LCtx.InFile; + B := 1; + end; + SStream := TSharedMemoryStream.Create + (LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName), + '_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S); + try + Output.WriteBuffer(B, B.Size); + I64 := SStream.Size; + Output.WriteBuffer(I64, I64.Size); + CopyStreamEx(SStream, Output, SStream.Size); + finally + SStream.Free; + end; + Load(I); + end; + end; + WaitForAll(Tasks); + B := 0; + Output.WriteBuffer(B, B.Size); + I64 := I64.MinValue; + Output.WriteBuffer(I64, I64.Size); + finally + for I := Low(Tasks) to High(Tasks) do + begin + if DirectoryExists(WorkDir[I]) then + TDirectory.Delete(WorkDir[I], True); + Tasks[I].Free; + end; + end; +end; + +procedure Decode(Input, Output: TStream; ParamArg: TArray; + Options: TDecodeOptions); +var + I: Integer; + S: String; + First, Done: Boolean; + FStream: TFileStream; + SStream: TSharedMemoryStream; + LCtx: TCtx; + WorkDir: TArray; + Tasks: TArray; + State: TArray; + + procedure Load(X: Integer); + var + B: Byte; + I64: Int64; + begin + DeleteFile(IncludeTrailingBackSlash(WorkDir[X]) + LCtx.InFile); + DeleteFile(IncludeTrailingBackSlash(WorkDir[X]) + LCtx.OutFile); + if not Done then + begin + repeat + Input.ReadBuffer(B, B.Size); + Input.ReadBuffer(I64, I64.Size); + if I64 >= 0 then + begin + FStream := TFileStream.Create(IncludeTrailingBackSlash(WorkDir[X]) + + LCtx.InFile, fmCreate); + try + if B = 0 then + CopyStreamEx(Input, FStream, I64) + else + CopyStreamEx(Input, Output, I64); + finally + FStream.Free; + end; + end + else + Done := True; + until (B = 0) or Done; + end; + if Done then + State[X] := STATE_DONE + else + State[X] := STATE_READY; + if not Done then + Tasks[X].Start; + end; + +begin + Init(ParamArg, @LCtx); + SetLength(WorkDir, Options.Threads); + SetLength(Tasks, Options.Threads); + SetLength(State, Options.Threads); + for I := Low(Tasks) to High(Tasks) do + begin + WorkDir[I] := IncludeTrailingBackSlash(GetCurrentDir) + + LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName), + '_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF1)); + CreateDir(WorkDir[I]); + Tasks[I] := TTask.Create(I, IntPtr(@LCtx), IntPtr(@WorkDir[I]), + IntPtr(@State[I])); + Tasks[I].Perform(ExecThread); + end; + First := True; + Done := False; + try + while State[0] <> STATE_DONE do + begin + if First then + begin + for I := Low(Tasks) to High(Tasks) do + Load(I); + First := False; + end; + for I := Low(Tasks) to High(Tasks) do + begin + Tasks[I].Wait; + if State[I] = STATE_DONE then + continue; + if State[I] = STATE_EXECUTED then + S := IncludeTrailingBackSlash(WorkDir[I]) + LCtx.OutFile + else + raise Exception.CreateRes(@SWriteError); + SStream := TSharedMemoryStream.Create + (LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName), + '_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S); + try + CopyStreamEx(SStream, Output, SStream.Size); + finally + SStream.Free; + end; + Load(I); + end; + end; + WaitForAll(Tasks); + finally + for I := Low(Tasks) to High(Tasks) do + begin + if DirectoryExists(WorkDir[I]) then + TDirectory.Delete(WorkDir[I], True); + Tasks[I].Free; + end; + end; +end; + +end. diff --git a/io/IOPatch.pas b/io/IOPatch.pas index 243c670..eec00c0 100644 --- a/io/IOPatch.pas +++ b/io/IOPatch.pas @@ -206,11 +206,12 @@ begin SStream1.Free; SStream2.Free; end; - if B then + if not B then if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize, Options.MaxSize) then continue; end; + ShowMessage(LFilename); LEntry.Op := TPatchOp.opMissing; LEntry.Filename := LList2[I]; LEntry.Size := FileSize(BaseDir2 + LList2[I]); @@ -269,7 +270,7 @@ begin try SS0.Size := Max(SS1.Size, SS2.Size); A := xd3_encode(SS2.Memory, SS2.Size, SS1.Memory, SS1.Size, - SS0.Memory, @Res, SS0.Size, 0) = 0; + SS0.Memory, @Res, SS0.Size, Integer(XD3_NOCOMPRESS)) = 0; if A then SS0.Size := Res; finally @@ -335,7 +336,7 @@ begin Tasks[I].Free; CS.Free; if DirectoryExists(TempDir) then - TDirectory.Delete(TempDir); + TDirectory.Delete(TempDir, True); end; FillChar(LEntry, SizeOf(TEntryStruct2), 0); LEntry.Op := TPatchOp.opNone; @@ -404,7 +405,8 @@ begin try SStream2.Size := LEntry.Size; B := xd3_decode(SStream0.Memory, I64, SStream1.Memory, - SStream1.Size, SStream2.Memory, @Res, SStream2.Size, 0) = 0; + SStream1.Size, SStream2.Memory, @Res, SStream2.Size, + Integer(XD3_NOCOMPRESS)) = 0; finally SStream1.Free; SStream2.Free; diff --git a/io/IOUtils.pas b/io/IOUtils.pas index 3ec1484..0875aa6 100644 --- a/io/IOUtils.pas +++ b/io/IOUtils.pas @@ -10,6 +10,8 @@ uses const XTOOL_IODEC = $314C5458; XTOOL_PATCH = $324C5458; + XTOOL_ARCH = $334C5458; + XTOOL_EXEC = $344C5458; XTOOL_MAPSUF1 = '-tmp'; XTOOL_MAPSUF2 = '_mapped.io'; XTOOL_MAPSUF3 = '.tmp'; diff --git a/precompressor/PrecompEXE.pas b/precompressor/PrecompEXE.pas index 1c3220f..3c9cd8a 100644 --- a/precompressor/PrecompEXE.pas +++ b/precompressor/PrecompEXE.pas @@ -725,12 +725,14 @@ begin ExeStruct^.IsLib[X] := True; continue; end - else if ContainsText(S2, '') then + else if ContainsText(S2, '') or ContainsText(S2, '[stdin]') + then begin SetBits(ExeStruct^.Mode[X], 1, 0, 1); continue; end - else if ContainsText(S2, '') then + else if ContainsText(S2, '') or ContainsText(S2, '[stdout]') + then begin SetBits(ExeStruct^.Mode[X], 1, 1, 1); continue; @@ -741,19 +743,14 @@ begin S3 := IfThen(X = 0, FILE_IN, FILE_OUT); SetBits(ExeStruct^.Mode[X], 0, 0, 1); if ContainsText(S2, '') then - begin - ExeStruct^.InFile[X] := ExtractStr('', S2); - S2 := ReplaceText(S2, ExeStruct^.InFile[X], S3); - ExeStruct^.InFile[X] := ReplaceText(ExeStruct^.InFile[X], - '', S3); - end + ExeStruct^.InFile[X] := ExtractStr('', S2) else - begin ExeStruct^.InFile[X] := ExtractStr('[filein]', S2); - S2 := ReplaceText(S2, ExeStruct^.InFile[X], ''); - ExeStruct^.InFile[X] := ReplaceText(ExeStruct^.InFile[X], - '[filein]', S3); - end; + S2 := ReplaceText(S2, ExeStruct^.InFile[X], S3); + ExeStruct^.InFile[X] := ReplaceText(ExeStruct^.InFile[X], + '', S3); + ExeStruct^.InFile[X] := ReplaceText(ExeStruct^.InFile[X], + '[filein]', S3); end else if ContainsText(S2, '') or ContainsText(S2, '[fileout]') then @@ -761,19 +758,14 @@ begin S3 := IfThen(X = 0, FILE_OUT, FILE_IN); SetBits(ExeStruct^.Mode[X], 0, 1, 1); if ContainsText(S2, '') then - begin - ExeStruct^.OutFile[X] := ExtractStr('', S2); - S2 := ReplaceText(S2, ExeStruct^.OutFile[X], S3); - ExeStruct^.OutFile[X] := ReplaceText(ExeStruct^.OutFile[X], - '', S3); - end + ExeStruct^.OutFile[X] := ExtractStr('', S2) else - begin ExeStruct^.OutFile[X] := ExtractStr('[fileout]', S2); - S2 := ReplaceText(S2, ExeStruct^.OutFile[X], ''); - ExeStruct^.OutFile[X] := ReplaceText(ExeStruct^.OutFile[X], - '[fileout]', S3); - end; + S2 := ReplaceText(S2, ExeStruct^.OutFile[X], S3); + ExeStruct^.OutFile[X] := ReplaceText(ExeStruct^.OutFile[X], + '', S3); + ExeStruct^.OutFile[X] := ReplaceText(ExeStruct^.OutFile[X], + '[fileout]', S3); end; S2 := IfThen((Pos(' ', S2) > 0) or (S2 = ''), '"' + S2 + '"', S2); ExeStruct^.Param[X] := ExeStruct^.Param[X] + ' ' + S2; diff --git a/xtool.dpr b/xtool.dpr index a305c13..2415e83 100644 --- a/xtool.dpr +++ b/xtool.dpr @@ -79,7 +79,9 @@ uses IOFind in 'io\IOFind.pas', IOErase in 'io\IOErase.pas', IOReplace in 'io\IOReplace.pas', + IOArchive in 'io\IOArchive.pas', IOPatch in 'io\IOPatch.pas', + IOExecute in 'io\IOExecute.pas', IODecode in 'io\IODecode.pas', IOUtils in 'io\IOUtils.pas'; @@ -93,6 +95,8 @@ const CommandReplace = 'replace'; CommandExtract = 'extract'; CommandPatch = 'patch'; + CommandArchive = 'archive'; + CommandExecute = 'execute'; CommandDecode = 'decode'; procedure ProgramInfo; @@ -106,7 +110,9 @@ begin WriteLine('Available commands:'); WriteLine(''); WriteLine(' ' + CommandDecode); + WriteLine(' ' + CommandArchive); WriteLine(' ' + CommandErase); + WriteLine(' ' + CommandExecute); WriteLine(' ' + CommandExtract); WriteLine(' ' + CommandFind); WriteLine(' ' + CommandGenerate); @@ -149,6 +155,11 @@ begin end; { changelog + ES_R33 (0.5.2) + - added IO functions (archive, execute) + - fixed issue in patch io function + - removed compression on patch diff files + ES_R32 (0.5.1) - added IO functions (find, extract, patch) - generate database feature and IO functions now can search for streams larger than chunk size @@ -387,6 +398,7 @@ const var I, J: Integer; ParamArg: array [0 .. 1] of TArray; + StrArray: TArray; IsParam: Boolean; Input, Output: TStream; PrecompEnc: PrecompMain.TEncodeOptions; @@ -397,6 +409,10 @@ var ReplaceEnc: IOReplace.TEncodeOptions; PatchEnc: IOPatch.TEncodeOptions; PatchDec: IOPatch.TDecodeOptions; + ArchiveEnc: IOArchive.TEncodeOptions; + ArchiveDec: IOArchive.TDecodeOptions; + ExecuteEnc: IOExecute.TEncodeOptions; + ExecuteDec: IOExecute.TDecodeOptions; IODec: IODecode.TDecodeOptions; IOExt: IODecode.TExtractOptions; @@ -438,14 +454,17 @@ begin begin while Length(ParamArg[1]) < 2 do Insert('-', ParamArg[1], Length(ParamArg[1])); - Input := TBufferedStream.Create(GetInStream(ParamArg[1][0]), True, + Input := TBufferedStream.Create(GetInStream(ParamArg[1, 0]), True, BufferSize); - Output := TBufferedStream.Create(GetOutStream(ParamArg[1][1]), False, + Output := TBufferedStream.Create(GetOutStream(ParamArg[1, 1]), False, BufferSize); - PrecompMain.Parse(ParamArg[0], PrecompEnc); - PrecompMain.Encode(Input, Output, PrecompEnc); - Input.Free; - Output.Free; + try + PrecompMain.Parse(ParamArg[0], PrecompEnc); + PrecompMain.Encode(Input, Output, PrecompEnc); + finally + Input.Free; + Output.Free; + end; end; if ParamStr(1).StartsWith(CommandGenerate, True) then if Length(ParamArg[1]) < 3 then @@ -453,7 +472,7 @@ begin else begin DbgMain.Parse(ParamArg[0], GenerateEnc); - DbgMain.Encode(ParamArg[1][0], ParamArg[1][1], ParamArg[1][2], + DbgMain.Encode(ParamArg[1, 0], ParamArg[1, 1], ParamArg[1, 2], GenerateEnc); end; if ParamStr(1).StartsWith(CommandFind, True) then @@ -462,7 +481,7 @@ begin else begin IOFind.Parse(ParamArg[0], FindEnc); - IOFind.Encode(ParamArg[1][0], ParamArg[1][1], + IOFind.Encode(ParamArg[1, 0], ParamArg[1, 1], ParamArgSafe(1, 2), FindEnc); end; if ParamStr(1).StartsWith(CommandErase, True) then @@ -471,7 +490,7 @@ begin else begin IOErase.Parse(ParamArg[0], EraseEnc); - IOErase.Encode(ParamArg[1][0], ParamArg[1][1], ParamArgSafe(1, 2), + IOErase.Encode(ParamArg[1, 0], ParamArg[1, 1], ParamArgSafe(1, 2), EraseEnc); end; if ParamStr(1).StartsWith(CommandReplace, True) then @@ -480,7 +499,7 @@ begin else begin IOReplace.Parse(ParamArg[0], ReplaceEnc); - IOReplace.Encode(ParamArg[1][0], ParamArg[1][1], ParamArg[1][2], + IOReplace.Encode(ParamArg[1, 0], ParamArg[1, 1], ParamArg[1, 2], ParamArgSafe(1, 3), ReplaceEnc); end; if ParamStr(1).StartsWith(CommandExtract, True) then @@ -490,28 +509,74 @@ begin begin Input := TBufferedStream.Create(GetInStream(ParamArgSafe(1, 0)), True, BufferSize); - Input.ReadBuffer(I, I.Size); - case I of - XTOOL_IODEC: - begin - IODecode.ParseExtract(ParamArg[0], IOExt); - IODecode.Extract(Input, ParamArgSafe(1, 1), - ParamArgSafe(1, 2), IOExt); - end; + try + Input.ReadBuffer(I, I.Size); + case I of + XTOOL_IODEC: + begin + IODecode.ParseExtract(ParamArg[0], IOExt); + IODecode.Extract(Input, ParamArgSafe(1, 1), + ParamArgSafe(1, 2), IOExt); + end; + end; + finally + Input.Free; end; - Input.Free; end; if ParamStr(1).StartsWith(CommandPatch, True) then if Length(ParamArg[1]) < 3 then IOPatch.PrintHelp else begin - Output := TBufferedStream.Create(GetOutStream(ParamArg[1][2]), False, + Output := TBufferedStream.Create(GetOutStream(ParamArg[1, 2]), False, BufferSize); - IOPatch.Parse(ParamArg[0], PatchEnc); - IOPatch.Encode(ParamArg[1][0], ParamArg[1][1], Output, PatchEnc); - Input.Free; - Output.Free; + try + IOPatch.Parse(ParamArg[0], PatchEnc); + IOPatch.Encode(ParamArg[1, 0], ParamArg[1, 1], Output, PatchEnc); + finally + Output.Free; + end; + end; + if ParamStr(1).StartsWith(CommandArchive, True) then + if (Length(ParamArg[0]) = 0) and (Length(ParamArg[1]) = 0) then + IOArchive.PrintHelp + else + begin + while Length(ParamArg[1]) < 2 do + Insert('-', ParamArg[1], Length(ParamArg[1])); + SetLength(StrArray, 0); + for I := 0 to High(ParamArg[1]) - 1 do + Insert(ParamArg[1, I], StrArray, Length(StrArray)); + Output := TBufferedStream.Create + (GetOutStream(ParamArg[1, High(ParamArg[1])]), False, BufferSize); + try + IOArchive.Parse(ParamArg[0], ArchiveEnc); + IOArchive.Encode(StrArray, Output, ArchiveEnc); + finally + Input.Free; + end; + end; + if ParamStr(1).StartsWith(CommandExecute, True) then + if (Length(ParamArg[0]) = 0) and (Length(ParamArg[1]) = 0) then + IOExecute.PrintHelp + else + begin + while Length(ParamArg[1]) < 2 do + Insert('-', ParamArg[1], Length(ParamArg[1])); + SetLength(StrArray, 0); + for I := 2 to High(ParamArg[1]) do + Insert(ParamArg[1, I], StrArray, Length(StrArray)); + Input := TBufferedStream.Create(GetInStream(ParamArg[1, 0]), True, + BufferSize); + Output := TBufferedStream.Create(GetOutStream(ParamArg[1, 1]), False, + BufferSize); + try + IOExecute.Parse(ParamArg[0], ExecuteEnc); + IOExecute.Encode(Input, Output, StrArray, ExecuteEnc); + finally + Input.Free; + Output.Free; + end; end; if ParamStr(1).StartsWith(CommandDecode, True) then if (Length(ParamArg[0]) = 0) and (Length(ParamArg[1]) = 0) then @@ -520,28 +585,53 @@ begin begin Input := TBufferedStream.Create(GetInStream(ParamArgSafe(1, 0)), True, BufferSize); - Input.ReadBuffer(I, I.Size); - case I of - XTOOL_PRECOMP: - begin - Output := TBufferedStream.Create(GetOutStream(ParamArgSafe(1, 1)), - False, BufferSize); - PrecompMain.Parse(ParamArg[0], PrecompDec); - PrecompMain.Decode(Input, Output, PrecompDec); - Output.Free; - end; - XTOOL_IODEC: - begin - IODecode.ParseDecode(ParamArg[0], IODec); - IODecode.Decode(Input, ParamArg[1][1], ParamArg[1][2], IODec); - end; - XTOOL_PATCH: - begin - IOPatch.Parse(ParamArg[0], PatchDec); - IOPatch.Decode(Input, ParamArg[1][1], PatchDec); - end; + try + Input.ReadBuffer(I, I.Size); + case I of + XTOOL_PRECOMP: + begin + Output := TBufferedStream.Create(GetOutStream(ParamArgSafe(1, 1) + ), False, BufferSize); + try + PrecompMain.Parse(ParamArg[0], PrecompDec); + PrecompMain.Decode(Input, Output, PrecompDec); + finally + Output.Free; + end; + end; + XTOOL_IODEC: + begin + IODecode.ParseDecode(ParamArg[0], IODec); + IODecode.Decode(Input, ParamArg[1, 1], ParamArg[1, 2], IODec); + end; + XTOOL_PATCH: + begin + IOPatch.Parse(ParamArg[0], PatchDec); + IOPatch.Decode(Input, ParamArg[1, 1], PatchDec); + end; + XTOOL_ARCH: + begin + IOArchive.Parse(ParamArg[0], ArchiveDec); + IOArchive.Decode(Input, ParamArg[1, 1], ArchiveDec); + end; + XTOOL_EXEC: + begin + SetLength(StrArray, 0); + for I := 2 to High(ParamArg[1]) do + Insert(ParamArg[1, I], StrArray, Length(StrArray)); + Output := TBufferedStream.Create(GetOutStream(ParamArgSafe(1, 1) + ), False, BufferSize); + try + IOExecute.Parse(ParamArg[0], ExecuteDec); + IOExecute.Decode(Input, Output, StrArray, ExecuteDec); + finally + Output.Free; + end; + end; + end; + finally + Input.Free; end; - Input.Free; end; except on E: Exception do diff --git a/xtool.dproj b/xtool.dproj index 7f918cd..c38ec41 100644 --- a/xtool.dproj +++ b/xtool.dproj @@ -138,7 +138,9 @@ + +