update to 0.5.1
This commit is contained in:
@@ -1,198 +0,0 @@
|
|||||||
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: TEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
SS1, SS2: TSharedMemoryStream;
|
|
||||||
begin
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
BaseDir2 + StringOf(LBytes));
|
|
||||||
try
|
|
||||||
Input1.ReadBuffer(I, I.Size);
|
|
||||||
for J := 0 to I - 1 do
|
|
||||||
begin
|
|
||||||
Input1.ReadBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
LFilename := BaseDir1 + LEntry.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LFilename);
|
|
||||||
try
|
|
||||||
Move(SS2.Memory^, (PByte(SS1.Memory) + LEntry.Position)^,
|
|
||||||
LEntry.Size);
|
|
||||||
finally
|
|
||||||
SS2.Free;
|
|
||||||
end;
|
|
||||||
WriteLn(ErrOutput, Format('Extracted %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: TEntryStruct;
|
|
||||||
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 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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(TEntryStruct));
|
|
||||||
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.
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
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: TEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
SS1, SS2: TSharedMemoryStream;
|
|
||||||
begin
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
BaseDir2 + StringOf(LBytes));
|
|
||||||
try
|
|
||||||
Input1.ReadBuffer(I, I.Size);
|
|
||||||
for J := 0 to I - 1 do
|
|
||||||
begin
|
|
||||||
Input1.ReadBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
LFilename := BaseDir1 + LEntry.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LFilename);
|
|
||||||
try
|
|
||||||
Move(SS2.Memory^, (PByte(SS1.Memory) + LEntry.Position)^,
|
|
||||||
LEntry.Size);
|
|
||||||
finally
|
|
||||||
SS2.Free;
|
|
||||||
end;
|
|
||||||
WriteLn(ErrOutput, Format('Extracted %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: TEntryStruct;
|
|
||||||
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 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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(TEntryStruct));
|
|
||||||
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.
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
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: TEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
SS1, SS2: TSharedMemoryStream;
|
|
||||||
begin
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
BaseDir2 + StringOf(LBytes));
|
|
||||||
try
|
|
||||||
Input1.ReadBuffer(I, I.Size);
|
|
||||||
for J := 0 to I - 1 do
|
|
||||||
begin
|
|
||||||
Input1.ReadBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
LFilename := BaseDir1 + LEntry.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LFilename);
|
|
||||||
try
|
|
||||||
Move(SS2.Memory^, (PByte(SS1.Memory) + LEntry.Position)^,
|
|
||||||
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: TEntryStruct;
|
|
||||||
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 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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(TEntryStruct));
|
|
||||||
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.
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
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: TEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
SS1, SS2: TSharedMemoryStream;
|
|
||||||
begin
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
BaseDir2 + StringOf(LBytes));
|
|
||||||
try
|
|
||||||
Input1.ReadBuffer(I, I.Size);
|
|
||||||
for J := 0 to I - 1 do
|
|
||||||
begin
|
|
||||||
Input1.ReadBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
LFilename := BaseDir1 + LEntry.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LFilename);
|
|
||||||
try
|
|
||||||
Move(SS2.Memory^, (PByte(SS1.Memory) + LEntry.Position)^,
|
|
||||||
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: TEntryStruct;
|
|
||||||
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 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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);
|
|
||||||
if FileExists(Input2) then
|
|
||||||
LFilename := Input2
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + PEntry^.Filename;
|
|
||||||
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(TEntryStruct));
|
|
||||||
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.
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
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: TEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
SS1, SS2: TSharedMemoryStream;
|
|
||||||
begin
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
BaseDir2 + StringOf(LBytes));
|
|
||||||
try
|
|
||||||
Input1.ReadBuffer(I, I.Size);
|
|
||||||
for J := 0 to I - 1 do
|
|
||||||
begin
|
|
||||||
Input1.ReadBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
LFilename := BaseDir1 + LEntry.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LFilename);
|
|
||||||
try
|
|
||||||
Move(SS2.Memory^, (PByte(SS1.Memory) + LEntry.Position)^,
|
|
||||||
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: TEntryStruct;
|
|
||||||
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 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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);
|
|
||||||
if FileExists(Input2) then
|
|
||||||
LFilename := Input2
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + PEntry^.Filename;
|
|
||||||
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(TEntryStruct));
|
|
||||||
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.
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
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: TEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
SS1, SS2: TSharedMemoryStream;
|
|
||||||
begin
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
BaseDir2 + StringOf(LBytes));
|
|
||||||
try
|
|
||||||
Input1.ReadBuffer(I, I.Size);
|
|
||||||
for J := 0 to I - 1 do
|
|
||||||
begin
|
|
||||||
Input1.ReadBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
LFilename := BaseDir1 + LEntry.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LFilename);
|
|
||||||
try
|
|
||||||
Move(SS2.Memory^, (PByte(SS1.Memory) + LEntry.Position)^,
|
|
||||||
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: TEntryStruct;
|
|
||||||
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 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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(TEntryStruct));
|
|
||||||
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.
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
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: TEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
SS1, SS2: TSharedMemoryStream;
|
|
||||||
begin
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
BaseDir2 + StringOf(LBytes));
|
|
||||||
try
|
|
||||||
Input1.ReadBuffer(I, I.Size);
|
|
||||||
for J := 0 to I - 1 do
|
|
||||||
begin
|
|
||||||
Input1.ReadBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
LFilename := BaseDir1 + LEntry.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LFilename);
|
|
||||||
try
|
|
||||||
Move(SS2.Memory^, (PByte(SS1.Memory) + LEntry.Position)^,
|
|
||||||
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: TEntryStruct;
|
|
||||||
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 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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(TEntryStruct));
|
|
||||||
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.
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
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: TSharedMemoryStream;
|
|
||||||
begin
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
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 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LFilename);
|
|
||||||
try
|
|
||||||
Move(SS2.Memory^, (PByte(SS1.Memory) + LEntry.Position)^,
|
|
||||||
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 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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(TEntryStruct));
|
|
||||||
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.
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
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: TSharedMemoryStream;
|
|
||||||
begin
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
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 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LFilename);
|
|
||||||
try
|
|
||||||
Move(SS2.Memory^, (PByte(SS1.Memory) + LEntry.Position)^,
|
|
||||||
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 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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.
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
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: TSharedMemoryStream;
|
|
||||||
begin
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
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 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
try
|
|
||||||
Move(SS2.Memory^, (PByte(SS1.Memory) + LEntry.Position)^,
|
|
||||||
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 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(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.
|
|
||||||
@@ -1,340 +0,0 @@
|
|||||||
unit IOErase;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'erase - blank out sectors within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool erase [parameters] extracted_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
BaseDir: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d', [LList[I], MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[LList[I], Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].ActualSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d',
|
|
||||||
[LList[I], MinSize2]));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
FillChar((PByte(SStream.Memory) + PEntry.Position)^,
|
|
||||||
PEntry.Size, 0);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d',
|
|
||||||
[LList[I], MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
unit IOErase;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'erase - blank out sectors within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool erase [parameters] extracted_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
BaseDir: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d', [LList[I], MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[LList[I], Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].ActualSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
WriteLn(ErrOutput, Format('Found %s in %s at %d',
|
|
||||||
[LEntry.Filename, ReplaceText(LList[I], BaseDir, ''),
|
|
||||||
MinSize2]));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
FillChar((PByte(SStream.Memory) + PEntry.Position)^,
|
|
||||||
PEntry.Size, 0);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d',
|
|
||||||
[LList[I], MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
unit IOErase;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'erase - blank out sectors within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool erase [parameters] extracted_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
BaseDir: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d', [LList[I], MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[LList[I], Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].ActualSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
WriteLn(ErrOutput, Format('Found %s in %s at %s',
|
|
||||||
[LEntry.Filename, ReplaceText(LList[I], BaseDir, ''),
|
|
||||||
MinSize2]));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
FillChar((PByte(SStream.Memory) + PEntry.Position)^,
|
|
||||||
PEntry.Size, 0);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d',
|
|
||||||
[LList[I], MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
unit IOErase;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'erase - blank out sectors within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool erase [parameters] extracted_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
BaseDir: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d', [LList[I], MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[LList[I], Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].ActualSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
WriteLn(ErrOutput, Format('Found %s in %s at %s',
|
|
||||||
[LEntry.Filename, ReplaceText(LList[I], BaseDir, ''),
|
|
||||||
LEntry.Position.ToHexString]));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
FillChar((PByte(SStream.Memory) + PEntry.Position)^,
|
|
||||||
PEntry.Size, 0);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d',
|
|
||||||
[LList[I], MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
unit IOErase;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'erase - blank out sectors within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool erase [parameters] extracted_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
BaseDir: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d', [LList[I], MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[LList[I], Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].ActualSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
WriteLn(ErrOutput, Format('Found %s in %s at %s',
|
|
||||||
[LEntry.Filename, ReplaceText(LList[I], BaseDir, ''),
|
|
||||||
LEntry.Position.ToHexString]));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
FillChar((PByte(SStream.Memory) + PEntry.Position)^,
|
|
||||||
PEntry.Size, 0);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[LList[I], MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
unit IOErase;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'erase - blank out sectors within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool erase [parameters] extracted_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
BaseDir: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d', [LList[I], MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[LList[I], Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].ActualSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
WriteLn(ErrOutput, Format('Found %s in %s at %s',
|
|
||||||
[LEntry.Filename, ReplaceText(LList[I], BaseDir, ''),
|
|
||||||
LEntry.Position.ToHexString]));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
FillChar((PByte(SStream.Memory) + PEntry.Position)^,
|
|
||||||
PEntry.Size, 0);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,342 +0,0 @@
|
|||||||
unit IOErase;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'erase - blank out sectors within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool erase [parameters] extracted_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
BaseDir: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].ActualSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
WriteLn(ErrOutput, Format('Found %s in %s at %s',
|
|
||||||
[LEntry.Filename, ReplaceText(LList[I], BaseDir, ''),
|
|
||||||
LEntry.Position.ToHexString]));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
FillChar((PByte(SStream.Memory) + PEntry.Position)^,
|
|
||||||
PEntry.Size, 0);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,342 +0,0 @@
|
|||||||
unit IOErase;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'erase - blank out sectors within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool erase [parameters] extracted_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
BaseDir: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Larger than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].ActualSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
WriteLn(ErrOutput, Format('Found %s in %s at %s',
|
|
||||||
[LEntry.Filename, ReplaceText(LList[I], BaseDir, ''),
|
|
||||||
LEntry.Position.ToHexString]));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
FillChar((PByte(SStream.Memory) + PEntry.Position)^,
|
|
||||||
PEntry.Size, 0);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,342 +0,0 @@
|
|||||||
unit IOErase;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'erase - blank out sectors within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool erase [parameters] extracted_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
BaseDir: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct1;
|
|
||||||
PEntry: PEntryStruct1;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct1>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Larger than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct1>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct1;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].ActualSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct1));
|
|
||||||
WriteLn(ErrOutput, Format('Found %s in %s at %s',
|
|
||||||
[LEntry.Filename, ReplaceText(LList[I], BaseDir, ''),
|
|
||||||
LEntry.Position.ToHexString]));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct1(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
FillChar((PByte(SStream.Memory) + PEntry.Position)^,
|
|
||||||
PEntry.Size, 0);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,338 +0,0 @@
|
|||||||
unit IOErase;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'erase - blank out sectors within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool erase [parameters] extracted_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
BaseDir: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d', [LList[I], MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[LList[I], Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].ActualSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
FillChar((PByte(SStream.Memory) + PEntry.Position)^,
|
|
||||||
PEntry.Size, 0);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d',
|
|
||||||
[LList[I], MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
unit IOFind;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
unit IOFind;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'find - search for sectors within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool find [parameters] extracted_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
BaseDir: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d', [LList[I], MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[LList[I], Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].ActualSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
WriteLn(ErrOutput, Format('Found %s in %s at %s',
|
|
||||||
[LEntry.Filename, ReplaceText(LList[I], BaseDir, ''),
|
|
||||||
LEntry.Position.ToHexString]));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
FillChar((PByte(SStream.Memory) + PEntry.Position)^,
|
|
||||||
PEntry.Size, 0);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,324 +0,0 @@
|
|||||||
unit IOFind;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'find - search for sectors within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool find [parameters] extracted_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
BaseDir: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d', [LList[I], MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[LList[I], Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].ActualSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
WriteLn(ErrOutput, Format('Found %s in %s at %s',
|
|
||||||
[LEntry.Filename, ReplaceText(LList[I], BaseDir, ''),
|
|
||||||
LEntry.Position.ToHexString]));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,324 +0,0 @@
|
|||||||
unit IOFind;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'find - search for sectors within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool find [parameters] extracted_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
BaseDir: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d', [LList[I], MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[LList[I], Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].ActualSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
WriteLn(ErrOutput, Format('Found %s in %s at %s',
|
|
||||||
[LEntry.Filename, ReplaceText(LList[I], BaseDir, ''),
|
|
||||||
LEntry.Position.ToHexString]));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,325 +0,0 @@
|
|||||||
unit IOFind;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'find - search for sectors within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool find [parameters] extracted_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
BaseDir: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].ActualSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
WriteLn(ErrOutput, Format('Found %s in %s at %s',
|
|
||||||
[LEntry.Filename, ReplaceText(LList[I], BaseDir, ''),
|
|
||||||
LEntry.Position.ToHexString]));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,325 +0,0 @@
|
|||||||
unit IOFind;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'find - search for sectors within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool find [parameters] extracted_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Output: String; Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
BaseDir: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Larger than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].ActualSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
WriteLn(ErrOutput, Format('Found %s in %s at %s',
|
|
||||||
[LEntry.Filename, ReplaceText(LList[I], BaseDir, ''),
|
|
||||||
LEntry.Position.ToHexString]));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir, ''), MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,429 +0,0 @@
|
|||||||
unit IOPatch;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
MinSize, MaxSize: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
PDecodeOptions = ^TDecodeOptions;
|
|
||||||
|
|
||||||
TDecodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TDecodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
uses
|
|
||||||
XDeltaDLL;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'patch - creates patches between two data sets');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput, ' xtool patch [parameters] old_data new_data patch_data');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
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)));
|
|
||||||
S := ArgParse.AsString('--min=', 0, '64k');
|
|
||||||
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.MinSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
S := ArgParse.AsString('--max=', 0, '16g');
|
|
||||||
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.MaxSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
finally
|
|
||||||
ArgParse.Free;
|
|
||||||
ExpParse.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; 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;
|
|
||||||
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
I, J, K: Integer;
|
|
||||||
I64: Int64;
|
|
||||||
B, C: Boolean;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList1, LList2: TArray<String>;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
CS: TCriticalSection;
|
|
||||||
TempDir: String;
|
|
||||||
begin
|
|
||||||
C := FileExists(Input1) and FileExists(Input2);
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList1 := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList1) to High(LList1) do
|
|
||||||
LList1[I] := ReplaceText(LList1[I], BaseDir1, '');
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList2 := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList2) to High(LList2) do
|
|
||||||
LList2[I] := ReplaceText(LList2[I], BaseDir2, '');
|
|
||||||
K := XTOOL_PATCH;
|
|
||||||
Output.WriteBuffer(K, K.Size);
|
|
||||||
if not C then
|
|
||||||
for I := High(LList1) downto Low(LList1) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList1[I], LList2) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDelete;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
Delete(LList1, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if not C then
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList2[I], LList1) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if C then
|
|
||||||
LFilename := Input1
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + LList2[I];
|
|
||||||
if FileSize(LFilename) <> FileSize(BaseDir2 + LList2[I]) then
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[I]);
|
|
||||||
try
|
|
||||||
B := SynCommons.CompareMem(SStream1.Memory, SStream2.Memory,
|
|
||||||
SStream1.Size);
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
TempDir := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF1));
|
|
||||||
if not DirectoryExists(TempDir) then
|
|
||||||
CreateDir(TempDir);
|
|
||||||
SetLength(LList1, 0);
|
|
||||||
CS := TCriticalSection.Create;
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
J := -1;
|
|
||||||
K := 0;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Y, Z: Integer;
|
|
||||||
S1, S2: String;
|
|
||||||
A: Boolean;
|
|
||||||
SS0, SS1, SS2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
Z := Length(LList2);
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
while Y < Z do
|
|
||||||
begin
|
|
||||||
S1 := IncludeTrailingBackSlash(TempDir) + Y.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if C then
|
|
||||||
S2 := Input1
|
|
||||||
else
|
|
||||||
S2 := BaseDir1 + LList2[Y];
|
|
||||||
SS0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
SS1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[Y]);
|
|
||||||
try
|
|
||||||
SS0.Size := Max(SS1.Size, SS2.Size);
|
|
||||||
A := xd3_encode(SS1.Memory, SS1.Size, SS1.Memory, SS1.Size,
|
|
||||||
SS0.Memory, @Res, SS0.Size, 0) = 0;
|
|
||||||
if A then
|
|
||||||
SS0.Size := Res;
|
|
||||||
finally
|
|
||||||
SS0.Free;
|
|
||||||
SS1.Free;
|
|
||||||
SS2.Free;
|
|
||||||
end;
|
|
||||||
if not A then
|
|
||||||
DeleteFile(S1);
|
|
||||||
CS.Acquire;
|
|
||||||
try
|
|
||||||
Insert(S1, LList1, Length(LList1));
|
|
||||||
Inc(K);
|
|
||||||
finally
|
|
||||||
CS.Release;
|
|
||||||
end;
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
Tasks[I].Start;
|
|
||||||
end;
|
|
||||||
I := 0;
|
|
||||||
while I < Length(LList2) do
|
|
||||||
begin
|
|
||||||
while I >= K do
|
|
||||||
Sleep(10);
|
|
||||||
LFilename := IncludeTrailingBackSlash(TempDir) + I.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDifferent;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
I64 := FileSize(LFilename);
|
|
||||||
Output.WriteBuffer(I64, I64.Size);
|
|
||||||
FStream := TFileStream.Create(LFilename, fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, I64);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir1 + LList1[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Inc(I);
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
Tasks[I].Free;
|
|
||||||
CS.Free;
|
|
||||||
if DirectoryExists(TempDir) then
|
|
||||||
TDirectory.Delete(TempDir);
|
|
||||||
end;
|
|
||||||
FillChar(LEntry, SizeOf(TEntryStruct2), 0);
|
|
||||||
LEntry.Op := TPatchOp.opNone;
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
var
|
|
||||||
I64: Int64;
|
|
||||||
B: Boolean;
|
|
||||||
S1, S2: String;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir: String;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream0, SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Output))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output));
|
|
||||||
S1 := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF3));
|
|
||||||
SStream0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
try
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
while LEntry.Op <> TPatchOp.opNone do
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
LFilename := Output
|
|
||||||
else
|
|
||||||
LFilename := BaseDir + LEntry.Filename;
|
|
||||||
case LEntry.Op of
|
|
||||||
TPatchOp.opDelete:
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TPatchOp.opMissing:
|
|
||||||
begin
|
|
||||||
ForceDirectories(ExtractFilePath(LFilename));
|
|
||||||
with TFileStream.Create(LFilename, fmCreate) do
|
|
||||||
try
|
|
||||||
CopyFrom(Input, LEntry.Size);
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
TPatchOp.opDifferent:
|
|
||||||
begin
|
|
||||||
Input.ReadBuffer(I64, I64.Size);
|
|
||||||
SStream0.Size := Max(SStream0.Size, I64);
|
|
||||||
S2 := ChangeFileExt(LFilename, '_' + Random($7FFFFFFF).ToHexString +
|
|
||||||
XTOOL_MAPSUF3);
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
B := False;
|
|
||||||
try
|
|
||||||
SStream2.Size := LEntry.Size;
|
|
||||||
B := xd3_decode(SStream0.Memory, I64, SStream1.Memory,
|
|
||||||
SStream1.Size, SStream2.Memory, @Res, SStream2.Size, 0) = 0;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
begin
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TFile.Move(S2, LFilename);
|
|
||||||
end
|
|
||||||
else if FileExists(S2) then
|
|
||||||
TFile.Delete(S2);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream0.Free;
|
|
||||||
if FileExists(S1) then
|
|
||||||
TFile.Delete(S1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,429 +0,0 @@
|
|||||||
unit IOPatch;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
MinSize, MaxSize: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
PDecodeOptions = ^TDecodeOptions;
|
|
||||||
|
|
||||||
TDecodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TDecodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
uses
|
|
||||||
XDeltaDLL;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'patch - creates patches between two data sets');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput, ' xtool patch [parameters] old_data new_data patch_data');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
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)));
|
|
||||||
S := ArgParse.AsString('--min=', 0, '64k');
|
|
||||||
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.MinSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
S := ArgParse.AsString('--max=', 0, '16g');
|
|
||||||
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.MaxSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
finally
|
|
||||||
ArgParse.Free;
|
|
||||||
ExpParse.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; 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;
|
|
||||||
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
I, J, K: Integer;
|
|
||||||
I64: Int64;
|
|
||||||
B, C: Boolean;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList1, LList2: TArray<String>;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
CS: TCriticalSection;
|
|
||||||
TempDir: String;
|
|
||||||
begin
|
|
||||||
C := FileExists(Input1) and FileExists(Input2);
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList1 := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList1) to High(LList1) do
|
|
||||||
LList1[I] := ReplaceText(LList1[I], BaseDir1, '');
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList2 := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList2) to High(LList2) do
|
|
||||||
LList2[I] := ReplaceText(LList2[I], BaseDir2, '');
|
|
||||||
K := XTOOL_PATCH;
|
|
||||||
Output.WriteBuffer(K, K.Size);
|
|
||||||
if not C then
|
|
||||||
for I := High(LList1) downto Low(LList1) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList1[I], LList2) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDelete;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
Delete(LList1, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if not C then
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList2[I], LList1) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if C then
|
|
||||||
LFilename := Input1
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + LList2[I];
|
|
||||||
if FileSize(LFilename) <> FileSize(BaseDir2 + LList2[I]) then
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[I]);
|
|
||||||
try
|
|
||||||
B := SynCommons.CompareMem(SStream1.Memory, SStream2.Memory,
|
|
||||||
SStream1.Size);
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
TempDir := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF1));
|
|
||||||
if not DirectoryExists(TempDir) then
|
|
||||||
CreateDir(TempDir);
|
|
||||||
SetLength(LList1, 0);
|
|
||||||
CS := TCriticalSection.Create;
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
J := -1;
|
|
||||||
K := 0;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Y, Z: Integer;
|
|
||||||
S1, S2: String;
|
|
||||||
A: Boolean;
|
|
||||||
SS0, SS1, SS2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
Z := Length(LList2);
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
while Y < Z do
|
|
||||||
begin
|
|
||||||
S1 := IncludeTrailingBackSlash(TempDir) + Y.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if C then
|
|
||||||
S2 := Input1
|
|
||||||
else
|
|
||||||
S2 := BaseDir1 + LList2[Y];
|
|
||||||
SS0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
SS1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[Y]);
|
|
||||||
try
|
|
||||||
SS0.Size := Max(SS1.Size, SS2.Size);
|
|
||||||
A := xd3_encode(SS1.Memory, SS1.Size, SS1.Memory, SS1.Size,
|
|
||||||
SS0.Memory, @Res, SS0.Size, 0) = 0;
|
|
||||||
if A then
|
|
||||||
SS0.Size := Res;
|
|
||||||
finally
|
|
||||||
SS0.Free;
|
|
||||||
SS1.Free;
|
|
||||||
SS2.Free;
|
|
||||||
end;
|
|
||||||
if not A then
|
|
||||||
DeleteFile(S1);
|
|
||||||
CS.Acquire;
|
|
||||||
try
|
|
||||||
Insert(S1, LList1, Length(LList1));
|
|
||||||
Inc(K);
|
|
||||||
finally
|
|
||||||
CS.Release;
|
|
||||||
end;
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
Tasks[I].Start;
|
|
||||||
end;
|
|
||||||
I := 0;
|
|
||||||
while I < Length(LList2) do
|
|
||||||
begin
|
|
||||||
while I >= K do
|
|
||||||
Sleep(10);
|
|
||||||
LFilename := IncludeTrailingBackSlash(TempDir) + I.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDifferent;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
I64 := FileSize(LFilename);
|
|
||||||
Output.WriteBuffer(I64, I64.Size);
|
|
||||||
FStream := TFileStream.Create(LFilename, fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, I64);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir1 + LList1[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Inc(I);
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
Tasks[I].Free;
|
|
||||||
CS.Free;
|
|
||||||
if DirectoryExists(TempDir) then
|
|
||||||
TDirectory.Delete(TempDir);
|
|
||||||
end;
|
|
||||||
FillChar(LEntry, SizeOf(TEntryStruct2), 0);
|
|
||||||
LEntry.Op := TPatchOp.opNone;
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
var
|
|
||||||
I64: Int64;
|
|
||||||
B: Boolean;
|
|
||||||
S1, S2: String;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir: String;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream0, SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Output))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output));
|
|
||||||
S1 := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF3));
|
|
||||||
SStream0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
try
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
while LEntry.Op <> TPatchOp.opNone do
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
LFilename := Output
|
|
||||||
else
|
|
||||||
LFilename := BaseDir + LEntry.Filename;
|
|
||||||
case LEntry.Op of
|
|
||||||
TPatchOp.opDelete:
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TPatchOp.opMissing:
|
|
||||||
begin
|
|
||||||
ForceDirectories(ExtractFilePath(LFilename));
|
|
||||||
with TFileStream.Create(LFilename, fmCreate) do
|
|
||||||
try
|
|
||||||
CopyFrom(Input, LEntry.Size);
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
TPatchOp.opDifferent:
|
|
||||||
begin
|
|
||||||
Input.ReadBuffer(I64, I64.Size);
|
|
||||||
SStream0.Size := Max(SStream0.Size, I64);
|
|
||||||
S2 := ChangeFileExt(LFilename, '_' + Random($7FFFFFFF).ToHexString +
|
|
||||||
XTOOL_MAPSUF3);
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
B := False;
|
|
||||||
try
|
|
||||||
SStream2.Size := LEntry.Size;
|
|
||||||
B := xd3_decode(SStream0.Memory, I64, SStream1.Memory,
|
|
||||||
SStream1.Size, SStream2.Memory, @Res, SStream2.Size, 0) = 0;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
begin
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TFile.Move(S2, LFilename);
|
|
||||||
end
|
|
||||||
else if FileExists(S2) then
|
|
||||||
TFile.Delete(S2);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream0.Free;
|
|
||||||
if FileExists(S1) then
|
|
||||||
TFile.Delete(S1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,430 +0,0 @@
|
|||||||
unit IOPatch;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
MinSize, MaxSize: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
PDecodeOptions = ^TDecodeOptions;
|
|
||||||
|
|
||||||
TDecodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TDecodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
uses
|
|
||||||
XDeltaDLL;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'patch - creates patches between two data sets');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput, ' xtool patch [parameters] old_data new_data patch_data');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
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)));
|
|
||||||
S := ArgParse.AsString('--min=', 0, '64k');
|
|
||||||
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.MinSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
S := ArgParse.AsString('--max=', 0, '16g');
|
|
||||||
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.MaxSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
finally
|
|
||||||
ArgParse.Free;
|
|
||||||
ExpParse.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; 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;
|
|
||||||
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
I, J, K: Integer;
|
|
||||||
I64: Int64;
|
|
||||||
B, C: Boolean;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList1, LList2: TArray<String>;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
CS: TCriticalSection;
|
|
||||||
TempDir: String;
|
|
||||||
begin
|
|
||||||
C := FileExists(Input1) and FileExists(Input2);
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList1 := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList1) to High(LList1) do
|
|
||||||
LList1[I] := ReplaceText(LList1[I], BaseDir1, '');
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList2 := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList2) to High(LList2) do
|
|
||||||
LList2[I] := ReplaceText(LList2[I], BaseDir2, '');
|
|
||||||
K := XTOOL_PATCH;
|
|
||||||
Output.WriteBuffer(K, K.Size);
|
|
||||||
if not C then
|
|
||||||
for I := High(LList1) downto Low(LList1) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList1[I], LList2) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDelete;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
Delete(LList1, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if not C then
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList2[I], LList1) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if C then
|
|
||||||
LFilename := Input1
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + LList2[I];
|
|
||||||
if FileSize(LFilename) <> FileSize(BaseDir2 + LList2[I]) then
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[I]);
|
|
||||||
try
|
|
||||||
B := SynCommons.CompareMem(SStream1.Memory, SStream2.Memory,
|
|
||||||
SStream1.Size);
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
TempDir := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF1));
|
|
||||||
if not DirectoryExists(TempDir) then
|
|
||||||
CreateDir(TempDir);
|
|
||||||
SetLength(LList1, 0);
|
|
||||||
CS := TCriticalSection.Create;
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
J := -1;
|
|
||||||
K := 0;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Y, Z: Integer;
|
|
||||||
S1, S2: String;
|
|
||||||
A: Boolean;
|
|
||||||
SS0, SS1, SS2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
Z := Length(LList2);
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
while Y < Z do
|
|
||||||
begin
|
|
||||||
S1 := IncludeTrailingBackSlash(TempDir) + Y.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if C then
|
|
||||||
S2 := Input1
|
|
||||||
else
|
|
||||||
S2 := BaseDir1 + LList2[Y];
|
|
||||||
SS0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
SS1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[Y]);
|
|
||||||
try
|
|
||||||
SS0.Size := Max(SS1.Size, SS2.Size);
|
|
||||||
A := xd3_encode(SS1.Memory, SS1.Size, SS1.Memory, SS1.Size,
|
|
||||||
SS0.Memory, @Res, SS0.Size, 0) = 0;
|
|
||||||
if A then
|
|
||||||
SS0.Size := Res;
|
|
||||||
finally
|
|
||||||
SS0.Free;
|
|
||||||
SS1.Free;
|
|
||||||
SS2.Free;
|
|
||||||
end;
|
|
||||||
if not A then
|
|
||||||
DeleteFile(S1);
|
|
||||||
CS.Acquire;
|
|
||||||
try
|
|
||||||
Insert(S1, LList1, Length(LList1));
|
|
||||||
Inc(K);
|
|
||||||
finally
|
|
||||||
CS.Release;
|
|
||||||
end;
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
Tasks[I].Start;
|
|
||||||
end;
|
|
||||||
I := 0;
|
|
||||||
while I < Length(LList2) do
|
|
||||||
begin
|
|
||||||
while I >= K do
|
|
||||||
Sleep(10);
|
|
||||||
LFilename := IncludeTrailingBackSlash(TempDir) + I.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDifferent;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
I64 := FileSize(LFilename);
|
|
||||||
Output.WriteBuffer(I64, I64.Size);
|
|
||||||
FStream := TFileStream.Create(LFilename, fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, I64);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir1 + LList1[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Inc(I);
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
Tasks[I].Free;
|
|
||||||
CS.Free;
|
|
||||||
if DirectoryExists(TempDir) then
|
|
||||||
TDirectory.Delete(TempDir);
|
|
||||||
end;
|
|
||||||
FillChar(LEntry, SizeOf(TEntryStruct2), 0);
|
|
||||||
LEntry.Op := TPatchOp.opNone;
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
var
|
|
||||||
I64: Int64;
|
|
||||||
B: Boolean;
|
|
||||||
S1, S2: String;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir: String;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream0, SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Output))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output));
|
|
||||||
S1 := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF3));
|
|
||||||
SStream0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
try
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
while LEntry.Op <> TPatchOp.opNone do
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
LFilename := Output
|
|
||||||
else
|
|
||||||
LFilename := BaseDir + LEntry.Filename;
|
|
||||||
case LEntry.Op of
|
|
||||||
TPatchOp.opDelete:
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TPatchOp.opMissing:
|
|
||||||
begin
|
|
||||||
ForceDirectories(ExtractFilePath(LFilename));
|
|
||||||
with TFileStream.Create(LFilename, fmCreate) do
|
|
||||||
try
|
|
||||||
CopyFrom(Input, LEntry.Size);
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
TPatchOp.opDifferent:
|
|
||||||
begin
|
|
||||||
Input.ReadBuffer(I64, I64.Size);
|
|
||||||
SStream0.Size := Max(SStream0.Size, I64);
|
|
||||||
S2 := ChangeFileExt(LFilename, '_' + Random($7FFFFFFF).ToHexString +
|
|
||||||
XTOOL_MAPSUF3);
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
B := False;
|
|
||||||
try
|
|
||||||
SStream2.Size := LEntry.Size;
|
|
||||||
B := xd3_decode(SStream0.Memory, I64, SStream1.Memory,
|
|
||||||
SStream1.Size, SStream2.Memory, @Res, SStream2.Size, 0) = 0;
|
|
||||||
ShowMessage(Res.ToString);
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
begin
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TFile.Move(S2, LFilename);
|
|
||||||
end
|
|
||||||
else if FileExists(S2) then
|
|
||||||
TFile.Delete(S2);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream0.Free;
|
|
||||||
if FileExists(S1) then
|
|
||||||
TFile.Delete(S1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,431 +0,0 @@
|
|||||||
unit IOPatch;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
MinSize, MaxSize: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
PDecodeOptions = ^TDecodeOptions;
|
|
||||||
|
|
||||||
TDecodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TDecodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
uses
|
|
||||||
XDeltaDLL;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'patch - creates patches between two data sets');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput, ' xtool patch [parameters] old_data new_data patch_data');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
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)));
|
|
||||||
S := ArgParse.AsString('--min=', 0, '64k');
|
|
||||||
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.MinSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
S := ArgParse.AsString('--max=', 0, '16g');
|
|
||||||
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.MaxSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
finally
|
|
||||||
ArgParse.Free;
|
|
||||||
ExpParse.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; 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;
|
|
||||||
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
I, J, K: Integer;
|
|
||||||
I64: Int64;
|
|
||||||
B, C: Boolean;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList1, LList2: TArray<String>;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
CS: TCriticalSection;
|
|
||||||
TempDir: String;
|
|
||||||
begin
|
|
||||||
C := FileExists(Input1) and FileExists(Input2);
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList1 := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList1) to High(LList1) do
|
|
||||||
LList1[I] := ReplaceText(LList1[I], BaseDir1, '');
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList2 := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList2) to High(LList2) do
|
|
||||||
LList2[I] := ReplaceText(LList2[I], BaseDir2, '');
|
|
||||||
K := XTOOL_PATCH;
|
|
||||||
Output.WriteBuffer(K, K.Size);
|
|
||||||
if not C then
|
|
||||||
for I := High(LList1) downto Low(LList1) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList1[I], LList2) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDelete;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
Delete(LList1, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if not C then
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList2[I], LList1) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if C then
|
|
||||||
LFilename := Input1
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + LList2[I];
|
|
||||||
if FileSize(LFilename) <> FileSize(BaseDir2 + LList2[I]) then
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[I]);
|
|
||||||
try
|
|
||||||
B := SynCommons.CompareMem(SStream1.Memory, SStream2.Memory,
|
|
||||||
SStream1.Size);
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
TempDir := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF1));
|
|
||||||
if not DirectoryExists(TempDir) then
|
|
||||||
CreateDir(TempDir);
|
|
||||||
SetLength(LList1, 0);
|
|
||||||
CS := TCriticalSection.Create;
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
J := -1;
|
|
||||||
K := 0;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Y, Z: Integer;
|
|
||||||
S1, S2: String;
|
|
||||||
A: Boolean;
|
|
||||||
SS0, SS1, SS2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
Z := Length(LList2);
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
while Y < Z do
|
|
||||||
begin
|
|
||||||
S1 := IncludeTrailingBackSlash(TempDir) + Y.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if C then
|
|
||||||
S2 := Input1
|
|
||||||
else
|
|
||||||
S2 := BaseDir1 + LList2[Y];
|
|
||||||
SS0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
SS1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[Y]);
|
|
||||||
try
|
|
||||||
SS0.Size := Max(SS1.Size, SS2.Size);
|
|
||||||
A := xd3_encode(SS1.Memory, SS1.Size, SS1.Memory, SS1.Size,
|
|
||||||
SS0.Memory, @Res, SS0.Size, 0) = 0;
|
|
||||||
if A then
|
|
||||||
SS0.Size := Res;
|
|
||||||
finally
|
|
||||||
SS0.Free;
|
|
||||||
SS1.Free;
|
|
||||||
SS2.Free;
|
|
||||||
end;
|
|
||||||
if not A then
|
|
||||||
DeleteFile(S1);
|
|
||||||
CS.Acquire;
|
|
||||||
try
|
|
||||||
Insert(S1, LList1, Length(LList1));
|
|
||||||
Inc(K);
|
|
||||||
finally
|
|
||||||
CS.Release;
|
|
||||||
end;
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
Tasks[I].Start;
|
|
||||||
end;
|
|
||||||
I := 0;
|
|
||||||
while I < Length(LList2) do
|
|
||||||
begin
|
|
||||||
while I >= K do
|
|
||||||
Sleep(10);
|
|
||||||
LFilename := IncludeTrailingBackSlash(TempDir) + I.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDifferent;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
I64 := FileSize(LFilename);
|
|
||||||
Output.WriteBuffer(I64, I64.Size);
|
|
||||||
FStream := TFileStream.Create(LFilename, fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, I64);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir1 + LList1[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Inc(I);
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
Tasks[I].Free;
|
|
||||||
CS.Free;
|
|
||||||
if DirectoryExists(TempDir) then
|
|
||||||
TDirectory.Delete(TempDir);
|
|
||||||
end;
|
|
||||||
FillChar(LEntry, SizeOf(TEntryStruct2), 0);
|
|
||||||
LEntry.Op := TPatchOp.opNone;
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
var
|
|
||||||
I64: Int64;
|
|
||||||
B: Boolean;
|
|
||||||
S1, S2: String;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir: String;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream0, SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Output))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output));
|
|
||||||
S1 := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF3));
|
|
||||||
SStream0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
try
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
while LEntry.Op <> TPatchOp.opNone do
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
LFilename := Output
|
|
||||||
else
|
|
||||||
LFilename := BaseDir + LEntry.Filename;
|
|
||||||
case LEntry.Op of
|
|
||||||
TPatchOp.opDelete:
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TPatchOp.opMissing:
|
|
||||||
begin
|
|
||||||
ForceDirectories(ExtractFilePath(LFilename));
|
|
||||||
with TFileStream.Create(LFilename, fmCreate) do
|
|
||||||
try
|
|
||||||
CopyFrom(Input, LEntry.Size);
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
TPatchOp.opDifferent:
|
|
||||||
begin
|
|
||||||
Input.ReadBuffer(I64, I64.Size);
|
|
||||||
SStream0.Position := 0;
|
|
||||||
CopyStreamEx(Input, SStream0, I64);
|
|
||||||
S2 := ChangeFileExt(LFilename, '_' + Random($7FFFFFFF).ToHexString +
|
|
||||||
XTOOL_MAPSUF3);
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
B := False;
|
|
||||||
try
|
|
||||||
SStream2.Size := LEntry.Size;
|
|
||||||
B := xd3_decode(SStream0.Memory, I64, SStream1.Memory,
|
|
||||||
SStream1.Size, SStream2.Memory, @Res, SStream2.Size, 0) = 0;
|
|
||||||
ShowMessage(Res.ToString);
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
begin
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TFile.Move(S2, LFilename);
|
|
||||||
end
|
|
||||||
else if FileExists(S2) then
|
|
||||||
TFile.Delete(S2);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream0.Free;
|
|
||||||
if FileExists(S1) then
|
|
||||||
TFile.Delete(S1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,430 +0,0 @@
|
|||||||
unit IOPatch;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
MinSize, MaxSize: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
PDecodeOptions = ^TDecodeOptions;
|
|
||||||
|
|
||||||
TDecodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TDecodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
uses
|
|
||||||
XDeltaDLL;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'patch - creates patches between two data sets');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput, ' xtool patch [parameters] old_data new_data patch_data');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
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)));
|
|
||||||
S := ArgParse.AsString('--min=', 0, '64k');
|
|
||||||
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.MinSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
S := ArgParse.AsString('--max=', 0, '16g');
|
|
||||||
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.MaxSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
finally
|
|
||||||
ArgParse.Free;
|
|
||||||
ExpParse.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; 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;
|
|
||||||
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
I, J, K: Integer;
|
|
||||||
I64: Int64;
|
|
||||||
B, C: Boolean;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList1, LList2: TArray<String>;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
CS: TCriticalSection;
|
|
||||||
TempDir: String;
|
|
||||||
begin
|
|
||||||
C := FileExists(Input1) and FileExists(Input2);
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList1 := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList1) to High(LList1) do
|
|
||||||
LList1[I] := ReplaceText(LList1[I], BaseDir1, '');
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList2 := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList2) to High(LList2) do
|
|
||||||
LList2[I] := ReplaceText(LList2[I], BaseDir2, '');
|
|
||||||
K := XTOOL_PATCH;
|
|
||||||
Output.WriteBuffer(K, K.Size);
|
|
||||||
if not C then
|
|
||||||
for I := High(LList1) downto Low(LList1) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList1[I], LList2) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDelete;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
Delete(LList1, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if not C then
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList2[I], LList1) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if C then
|
|
||||||
LFilename := Input1
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + LList2[I];
|
|
||||||
if FileSize(LFilename) <> FileSize(BaseDir2 + LList2[I]) then
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[I]);
|
|
||||||
try
|
|
||||||
B := SynCommons.CompareMem(SStream1.Memory, SStream2.Memory,
|
|
||||||
SStream1.Size);
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
TempDir := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF1));
|
|
||||||
if not DirectoryExists(TempDir) then
|
|
||||||
CreateDir(TempDir);
|
|
||||||
SetLength(LList1, 0);
|
|
||||||
CS := TCriticalSection.Create;
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
J := -1;
|
|
||||||
K := 0;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Y, Z: Integer;
|
|
||||||
S1, S2: String;
|
|
||||||
A: Boolean;
|
|
||||||
SS0, SS1, SS2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
Z := Length(LList2);
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
while Y < Z do
|
|
||||||
begin
|
|
||||||
S1 := IncludeTrailingBackSlash(TempDir) + Y.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if C then
|
|
||||||
S2 := Input1
|
|
||||||
else
|
|
||||||
S2 := BaseDir1 + LList2[Y];
|
|
||||||
SS0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
SS1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[Y]);
|
|
||||||
try
|
|
||||||
SS0.Size := Max(SS1.Size, SS2.Size);
|
|
||||||
A := xd3_encode(SS1.Memory, SS1.Size, SS1.Memory, SS1.Size,
|
|
||||||
SS0.Memory, @Res, SS0.Size, 0) = 0;
|
|
||||||
if A then
|
|
||||||
SS0.Size := Res;
|
|
||||||
finally
|
|
||||||
SS0.Free;
|
|
||||||
SS1.Free;
|
|
||||||
SS2.Free;
|
|
||||||
end;
|
|
||||||
if not A then
|
|
||||||
DeleteFile(S1);
|
|
||||||
CS.Acquire;
|
|
||||||
try
|
|
||||||
Insert(S1, LList1, Length(LList1));
|
|
||||||
Inc(K);
|
|
||||||
finally
|
|
||||||
CS.Release;
|
|
||||||
end;
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
Tasks[I].Start;
|
|
||||||
end;
|
|
||||||
I := 0;
|
|
||||||
while I < Length(LList2) do
|
|
||||||
begin
|
|
||||||
while I >= K do
|
|
||||||
Sleep(10);
|
|
||||||
LFilename := IncludeTrailingBackSlash(TempDir) + I.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDifferent;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
I64 := FileSize(LFilename);
|
|
||||||
Output.WriteBuffer(I64, I64.Size);
|
|
||||||
FStream := TFileStream.Create(LFilename, fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, I64);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir1 + LList1[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Inc(I);
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
Tasks[I].Free;
|
|
||||||
CS.Free;
|
|
||||||
if DirectoryExists(TempDir) then
|
|
||||||
TDirectory.Delete(TempDir);
|
|
||||||
end;
|
|
||||||
FillChar(LEntry, SizeOf(TEntryStruct2), 0);
|
|
||||||
LEntry.Op := TPatchOp.opNone;
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
var
|
|
||||||
I64: Int64;
|
|
||||||
B: Boolean;
|
|
||||||
S1, S2: String;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir: String;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream0, SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Output))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output));
|
|
||||||
S1 := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF3));
|
|
||||||
SStream0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
try
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
while LEntry.Op <> TPatchOp.opNone do
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
LFilename := Output
|
|
||||||
else
|
|
||||||
LFilename := BaseDir + LEntry.Filename;
|
|
||||||
case LEntry.Op of
|
|
||||||
TPatchOp.opDelete:
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TPatchOp.opMissing:
|
|
||||||
begin
|
|
||||||
ForceDirectories(ExtractFilePath(LFilename));
|
|
||||||
with TFileStream.Create(LFilename, fmCreate) do
|
|
||||||
try
|
|
||||||
CopyFrom(Input, LEntry.Size);
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
TPatchOp.opDifferent:
|
|
||||||
begin
|
|
||||||
Input.ReadBuffer(I64, I64.Size);
|
|
||||||
SStream0.Position := 0;
|
|
||||||
CopyStreamEx(Input, SStream0, I64);
|
|
||||||
S2 := ChangeFileExt(LFilename, '_' + Random($7FFFFFFF).ToHexString +
|
|
||||||
XTOOL_MAPSUF3);
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
B := False;
|
|
||||||
try
|
|
||||||
SStream2.Size := LEntry.Size;
|
|
||||||
B := xd3_decode(SStream0.Memory, I64, SStream1.Memory,
|
|
||||||
SStream1.Size, SStream2.Memory, @Res, SStream2.Size, 0) = 0;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
begin
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TFile.Move(S2, LFilename);
|
|
||||||
end
|
|
||||||
else if FileExists(S2) then
|
|
||||||
TFile.Delete(S2);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream0.Free;
|
|
||||||
if FileExists(S1) then
|
|
||||||
TFile.Delete(S1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,431 +0,0 @@
|
|||||||
unit IOPatch;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
MinSize, MaxSize: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
PDecodeOptions = ^TDecodeOptions;
|
|
||||||
|
|
||||||
TDecodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TDecodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
uses
|
|
||||||
XDeltaDLL;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'patch - creates patches between two data sets');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput, ' xtool patch [parameters] old_data new_data patch_data');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
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)));
|
|
||||||
S := ArgParse.AsString('--min=', 0, '64k');
|
|
||||||
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.MinSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
S := ArgParse.AsString('--max=', 0, '16g');
|
|
||||||
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.MaxSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
finally
|
|
||||||
ArgParse.Free;
|
|
||||||
ExpParse.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; 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;
|
|
||||||
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
I, J, K: Integer;
|
|
||||||
I64: Int64;
|
|
||||||
B, C: Boolean;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList1, LList2: TArray<String>;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
CS: TCriticalSection;
|
|
||||||
TempDir: String;
|
|
||||||
begin
|
|
||||||
C := FileExists(Input1) and FileExists(Input2);
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList1 := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList1) to High(LList1) do
|
|
||||||
LList1[I] := ReplaceText(LList1[I], BaseDir1, '');
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList2 := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList2) to High(LList2) do
|
|
||||||
LList2[I] := ReplaceText(LList2[I], BaseDir2, '');
|
|
||||||
K := XTOOL_PATCH;
|
|
||||||
Output.WriteBuffer(K, K.Size);
|
|
||||||
if not C then
|
|
||||||
for I := High(LList1) downto Low(LList1) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList1[I], LList2) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDelete;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
Delete(LList1, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if not C then
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList2[I], LList1) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if C then
|
|
||||||
LFilename := Input1
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + LList2[I];
|
|
||||||
if FileSize(LFilename) <> FileSize(BaseDir2 + LList2[I]) then
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[I]);
|
|
||||||
try
|
|
||||||
B := SynCommons.CompareMem(SStream1.Memory, SStream2.Memory,
|
|
||||||
SStream1.Size);
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
TempDir := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF1));
|
|
||||||
if not DirectoryExists(TempDir) then
|
|
||||||
CreateDir(TempDir);
|
|
||||||
SetLength(LList1, 0);
|
|
||||||
CS := TCriticalSection.Create;
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
J := -1;
|
|
||||||
K := 0;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Y, Z: Integer;
|
|
||||||
S1, S2: String;
|
|
||||||
A: Boolean;
|
|
||||||
SS0, SS1, SS2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
Z := Length(LList2);
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
while Y < Z do
|
|
||||||
begin
|
|
||||||
S1 := IncludeTrailingBackSlash(TempDir) + Y.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if C then
|
|
||||||
S2 := Input1
|
|
||||||
else
|
|
||||||
S2 := BaseDir1 + LList2[Y];
|
|
||||||
SS0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
SS1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[Y]);
|
|
||||||
try
|
|
||||||
SS0.Size := Max(SS1.Size, SS2.Size);
|
|
||||||
A := xd3_encode(SS1.Memory, SS1.Size, SS1.Memory, SS1.Size,
|
|
||||||
SS0.Memory, @Res, SS0.Size, 0) = 0;
|
|
||||||
if A then
|
|
||||||
SS0.Size := Res;
|
|
||||||
finally
|
|
||||||
SS0.Free;
|
|
||||||
SS1.Free;
|
|
||||||
SS2.Free;
|
|
||||||
end;
|
|
||||||
if not A then
|
|
||||||
DeleteFile(S1);
|
|
||||||
CS.Acquire;
|
|
||||||
try
|
|
||||||
Insert(S1, LList1, Length(LList1));
|
|
||||||
Inc(K);
|
|
||||||
finally
|
|
||||||
CS.Release;
|
|
||||||
end;
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
Tasks[I].Start;
|
|
||||||
end;
|
|
||||||
I := 0;
|
|
||||||
while I < Length(LList2) do
|
|
||||||
begin
|
|
||||||
while I >= K do
|
|
||||||
Sleep(10);
|
|
||||||
LFilename := IncludeTrailingBackSlash(TempDir) + I.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDifferent;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
I64 := FileSize(LFilename);
|
|
||||||
Output.WriteBuffer(I64, I64.Size);
|
|
||||||
FStream := TFileStream.Create(LFilename, fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, I64);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir1 + LList1[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Inc(I);
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
Tasks[I].Free;
|
|
||||||
CS.Free;
|
|
||||||
if DirectoryExists(TempDir) then
|
|
||||||
TDirectory.Delete(TempDir);
|
|
||||||
end;
|
|
||||||
FillChar(LEntry, SizeOf(TEntryStruct2), 0);
|
|
||||||
LEntry.Op := TPatchOp.opNone;
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
var
|
|
||||||
I64: Int64;
|
|
||||||
B: Boolean;
|
|
||||||
S1, S2: String;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir: String;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream0, SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Output))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output));
|
|
||||||
S1 := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF3));
|
|
||||||
SStream0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
try
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
while LEntry.Op <> TPatchOp.opNone do
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
LFilename := Output
|
|
||||||
else
|
|
||||||
LFilename := BaseDir + LEntry.Filename;
|
|
||||||
case LEntry.Op of
|
|
||||||
TPatchOp.opDelete:
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TPatchOp.opMissing:
|
|
||||||
begin
|
|
||||||
ForceDirectories(ExtractFilePath(LFilename));
|
|
||||||
with TFileStream.Create(LFilename, fmCreate) do
|
|
||||||
try
|
|
||||||
CopyFrom(Input, LEntry.Size);
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
TPatchOp.opDifferent:
|
|
||||||
begin
|
|
||||||
Input.ReadBuffer(I64, I64.Size);
|
|
||||||
SStream0.Position := 0;
|
|
||||||
CopyStreamEx(Input, SStream0, I64);
|
|
||||||
S2 := ChangeFileExt(LFilename, '_' + Random($7FFFFFFF).ToHexString +
|
|
||||||
XTOOL_MAPSUF3);
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
B := False;
|
|
||||||
try
|
|
||||||
SStream2.Size := LEntry.Size;
|
|
||||||
B := xd3_decode(SStream0.Memory, I64, SStream1.Memory,
|
|
||||||
SStream1.Size, SStream2.Memory, @Res, SStream2.Size, 0) = 0;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
begin
|
|
||||||
ShowMessage('');
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TFile.Move(S2, LFilename);
|
|
||||||
end
|
|
||||||
else if FileExists(S2) then
|
|
||||||
TFile.Delete(S2);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream0.Free;
|
|
||||||
if FileExists(S1) then
|
|
||||||
TFile.Delete(S1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,430 +0,0 @@
|
|||||||
unit IOPatch;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
MinSize, MaxSize: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
PDecodeOptions = ^TDecodeOptions;
|
|
||||||
|
|
||||||
TDecodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TDecodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
uses
|
|
||||||
XDeltaDLL;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'patch - creates patches between two data sets');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput, ' xtool patch [parameters] old_data new_data patch_data');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
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)));
|
|
||||||
S := ArgParse.AsString('--min=', 0, '64k');
|
|
||||||
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.MinSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
S := ArgParse.AsString('--max=', 0, '16g');
|
|
||||||
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.MaxSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
finally
|
|
||||||
ArgParse.Free;
|
|
||||||
ExpParse.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; 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;
|
|
||||||
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
I, J, K: Integer;
|
|
||||||
I64: Int64;
|
|
||||||
B, C: Boolean;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList1, LList2: TArray<String>;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
CS: TCriticalSection;
|
|
||||||
TempDir: String;
|
|
||||||
begin
|
|
||||||
C := FileExists(Input1) and FileExists(Input2);
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList1 := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList1) to High(LList1) do
|
|
||||||
LList1[I] := ReplaceText(LList1[I], BaseDir1, '');
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList2 := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList2) to High(LList2) do
|
|
||||||
LList2[I] := ReplaceText(LList2[I], BaseDir2, '');
|
|
||||||
K := XTOOL_PATCH;
|
|
||||||
Output.WriteBuffer(K, K.Size);
|
|
||||||
if not C then
|
|
||||||
for I := High(LList1) downto Low(LList1) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList1[I], LList2) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDelete;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
Delete(LList1, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if not C then
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList2[I], LList1) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if C then
|
|
||||||
LFilename := Input1
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + LList2[I];
|
|
||||||
if FileSize(LFilename) <> FileSize(BaseDir2 + LList2[I]) then
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[I]);
|
|
||||||
try
|
|
||||||
B := SynCommons.CompareMem(SStream1.Memory, SStream2.Memory,
|
|
||||||
SStream1.Size);
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
TempDir := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF1));
|
|
||||||
if not DirectoryExists(TempDir) then
|
|
||||||
CreateDir(TempDir);
|
|
||||||
SetLength(LList1, 0);
|
|
||||||
CS := TCriticalSection.Create;
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
J := -1;
|
|
||||||
K := 0;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Y, Z: Integer;
|
|
||||||
S1, S2: String;
|
|
||||||
A: Boolean;
|
|
||||||
SS0, SS1, SS2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
Z := Length(LList2);
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
while Y < Z do
|
|
||||||
begin
|
|
||||||
S1 := IncludeTrailingBackSlash(TempDir) + Y.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if C then
|
|
||||||
S2 := Input1
|
|
||||||
else
|
|
||||||
S2 := BaseDir1 + LList2[Y];
|
|
||||||
SS0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
SS1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[Y]);
|
|
||||||
try
|
|
||||||
SS0.Size := Max(SS1.Size, SS2.Size);
|
|
||||||
A := xd3_encode(SS1.Memory, SS1.Size, SS1.Memory, SS1.Size,
|
|
||||||
SS0.Memory, @Res, SS0.Size, 0) = 0;
|
|
||||||
if A then
|
|
||||||
SS0.Size := Res;
|
|
||||||
finally
|
|
||||||
SS0.Free;
|
|
||||||
SS1.Free;
|
|
||||||
SS2.Free;
|
|
||||||
end;
|
|
||||||
if not A then
|
|
||||||
DeleteFile(S1);
|
|
||||||
CS.Acquire;
|
|
||||||
try
|
|
||||||
Insert(S1, LList1, Length(LList1));
|
|
||||||
Inc(K);
|
|
||||||
finally
|
|
||||||
CS.Release;
|
|
||||||
end;
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
Tasks[I].Start;
|
|
||||||
end;
|
|
||||||
I := 0;
|
|
||||||
while I < Length(LList2) do
|
|
||||||
begin
|
|
||||||
while I >= K do
|
|
||||||
Sleep(10);
|
|
||||||
LFilename := IncludeTrailingBackSlash(TempDir) + I.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDifferent;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
I64 := FileSize(LFilename);
|
|
||||||
Output.WriteBuffer(I64, I64.Size);
|
|
||||||
FStream := TFileStream.Create(LFilename, fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, I64);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir1 + LList1[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Inc(I);
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
Tasks[I].Free;
|
|
||||||
CS.Free;
|
|
||||||
if DirectoryExists(TempDir) then
|
|
||||||
TDirectory.Delete(TempDir);
|
|
||||||
end;
|
|
||||||
FillChar(LEntry, SizeOf(TEntryStruct2), 0);
|
|
||||||
LEntry.Op := TPatchOp.opNone;
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
var
|
|
||||||
I64: Int64;
|
|
||||||
B: Boolean;
|
|
||||||
S1, S2: String;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir: String;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream0, SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Output))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output));
|
|
||||||
S1 := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF3));
|
|
||||||
SStream0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
try
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
while LEntry.Op <> TPatchOp.opNone do
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
LFilename := Output
|
|
||||||
else
|
|
||||||
LFilename := BaseDir + LEntry.Filename;
|
|
||||||
case LEntry.Op of
|
|
||||||
TPatchOp.opDelete:
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TPatchOp.opMissing:
|
|
||||||
begin
|
|
||||||
ForceDirectories(ExtractFilePath(LFilename));
|
|
||||||
with TFileStream.Create(LFilename, fmCreate) do
|
|
||||||
try
|
|
||||||
CopyFrom(Input, LEntry.Size);
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
TPatchOp.opDifferent:
|
|
||||||
begin
|
|
||||||
Input.ReadBuffer(I64, I64.Size);
|
|
||||||
SStream0.Position := 0;
|
|
||||||
CopyStreamEx(Input, SStream0, I64);
|
|
||||||
S2 := ChangeFileExt(LFilename, '_' + Random($7FFFFFFF).ToHexString +
|
|
||||||
XTOOL_MAPSUF3);
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
B := False;
|
|
||||||
try
|
|
||||||
SStream2.Size := LEntry.Size;
|
|
||||||
B := xd3_decode(SStream0.Memory, I64, SStream1.Memory,
|
|
||||||
SStream1.Size, SStream2.Memory, @Res, SStream2.Size, 0) = 0;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
begin
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TFile.Move(S2, LFilename);
|
|
||||||
end
|
|
||||||
else if FileExists(S2) then
|
|
||||||
TFile.Delete(S2);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream0.Free;
|
|
||||||
if FileExists(S1) then
|
|
||||||
TFile.Delete(S1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,430 +0,0 @@
|
|||||||
unit IOPatch;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
MinSize, MaxSize: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
PDecodeOptions = ^TDecodeOptions;
|
|
||||||
|
|
||||||
TDecodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TDecodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
uses
|
|
||||||
XDeltaDLL;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'patch - creates patches between two data sets');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput, ' xtool patch [parameters] old_data new_data patch_data');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
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)));
|
|
||||||
S := ArgParse.AsString('--min=', 0, '64k');
|
|
||||||
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.MinSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
S := ArgParse.AsString('--max=', 0, '16g');
|
|
||||||
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.MaxSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
finally
|
|
||||||
ArgParse.Free;
|
|
||||||
ExpParse.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; 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;
|
|
||||||
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
I, J, K: Integer;
|
|
||||||
I64: Int64;
|
|
||||||
B, C: Boolean;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList1, LList2: TArray<String>;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
CS: TCriticalSection;
|
|
||||||
TempDir: String;
|
|
||||||
begin
|
|
||||||
C := FileExists(Input1) and FileExists(Input2);
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList1 := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList1) to High(LList1) do
|
|
||||||
LList1[I] := ReplaceText(LList1[I], BaseDir1, '');
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList2 := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList2) to High(LList2) do
|
|
||||||
LList2[I] := ReplaceText(LList2[I], BaseDir2, '');
|
|
||||||
K := XTOOL_PATCH;
|
|
||||||
Output.WriteBuffer(K, K.Size);
|
|
||||||
if not C then
|
|
||||||
for I := High(LList1) downto Low(LList1) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList1[I], LList2) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDelete;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
Delete(LList1, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if not C then
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList2[I], LList1) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if C then
|
|
||||||
LFilename := Input1
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + LList2[I];
|
|
||||||
if FileSize(LFilename) <> FileSize(BaseDir2 + LList2[I]) then
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[I]);
|
|
||||||
try
|
|
||||||
B := SynCommons.CompareMem(SStream1.Memory, SStream2.Memory,
|
|
||||||
SStream1.Size);
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
TempDir := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF1));
|
|
||||||
if not DirectoryExists(TempDir) then
|
|
||||||
CreateDir(TempDir);
|
|
||||||
SetLength(LList1, 0);
|
|
||||||
CS := TCriticalSection.Create;
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
J := -1;
|
|
||||||
K := 0;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Y, Z: Integer;
|
|
||||||
S1, S2: String;
|
|
||||||
A: Boolean;
|
|
||||||
SS0, SS1, SS2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
Z := Length(LList2);
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
while Y < Z do
|
|
||||||
begin
|
|
||||||
S1 := IncludeTrailingBackSlash(TempDir) + Y.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if C then
|
|
||||||
S2 := Input1
|
|
||||||
else
|
|
||||||
S2 := BaseDir1 + LList2[Y];
|
|
||||||
SS0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
SS1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[Y]);
|
|
||||||
try
|
|
||||||
SS0.Size := Max(SS1.Size, SS2.Size);
|
|
||||||
A := xd3_encode(SS1.Memory, SS1.Size, SS2.Memory, SS2.Size,
|
|
||||||
SS0.Memory, @Res, SS0.Size, 0) = 0;
|
|
||||||
if A then
|
|
||||||
SS0.Size := Res;
|
|
||||||
finally
|
|
||||||
SS0.Free;
|
|
||||||
SS1.Free;
|
|
||||||
SS2.Free;
|
|
||||||
end;
|
|
||||||
if not A then
|
|
||||||
DeleteFile(S1);
|
|
||||||
CS.Acquire;
|
|
||||||
try
|
|
||||||
Insert(S1, LList1, Length(LList1));
|
|
||||||
Inc(K);
|
|
||||||
finally
|
|
||||||
CS.Release;
|
|
||||||
end;
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
Tasks[I].Start;
|
|
||||||
end;
|
|
||||||
I := 0;
|
|
||||||
while I < Length(LList2) do
|
|
||||||
begin
|
|
||||||
while I >= K do
|
|
||||||
Sleep(10);
|
|
||||||
LFilename := IncludeTrailingBackSlash(TempDir) + I.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDifferent;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
I64 := FileSize(LFilename);
|
|
||||||
Output.WriteBuffer(I64, I64.Size);
|
|
||||||
FStream := TFileStream.Create(LFilename, fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, I64);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir1 + LList1[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Inc(I);
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
Tasks[I].Free;
|
|
||||||
CS.Free;
|
|
||||||
if DirectoryExists(TempDir) then
|
|
||||||
TDirectory.Delete(TempDir);
|
|
||||||
end;
|
|
||||||
FillChar(LEntry, SizeOf(TEntryStruct2), 0);
|
|
||||||
LEntry.Op := TPatchOp.opNone;
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
var
|
|
||||||
I64: Int64;
|
|
||||||
B: Boolean;
|
|
||||||
S1, S2: String;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir: String;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream0, SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Output))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output));
|
|
||||||
S1 := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF3));
|
|
||||||
SStream0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
try
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
while LEntry.Op <> TPatchOp.opNone do
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
LFilename := Output
|
|
||||||
else
|
|
||||||
LFilename := BaseDir + LEntry.Filename;
|
|
||||||
case LEntry.Op of
|
|
||||||
TPatchOp.opDelete:
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TPatchOp.opMissing:
|
|
||||||
begin
|
|
||||||
ForceDirectories(ExtractFilePath(LFilename));
|
|
||||||
with TFileStream.Create(LFilename, fmCreate) do
|
|
||||||
try
|
|
||||||
CopyFrom(Input, LEntry.Size);
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
TPatchOp.opDifferent:
|
|
||||||
begin
|
|
||||||
Input.ReadBuffer(I64, I64.Size);
|
|
||||||
SStream0.Position := 0;
|
|
||||||
CopyStreamEx(Input, SStream0, I64);
|
|
||||||
S2 := ChangeFileExt(LFilename, '_' + Random($7FFFFFFF).ToHexString +
|
|
||||||
XTOOL_MAPSUF3);
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
B := False;
|
|
||||||
try
|
|
||||||
SStream2.Size := LEntry.Size;
|
|
||||||
B := xd3_decode(SStream0.Memory, I64, SStream1.Memory,
|
|
||||||
SStream1.Size, SStream2.Memory, @Res, SStream2.Size, 0) = 0;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
begin
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TFile.Move(S2, LFilename);
|
|
||||||
end
|
|
||||||
else if FileExists(S2) then
|
|
||||||
TFile.Delete(S2);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream0.Free;
|
|
||||||
if FileExists(S1) then
|
|
||||||
TFile.Delete(S1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,431 +0,0 @@
|
|||||||
unit IOPatch;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
MinSize, MaxSize: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
PDecodeOptions = ^TDecodeOptions;
|
|
||||||
|
|
||||||
TDecodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TDecodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
uses
|
|
||||||
XDeltaDLL;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'patch - creates patches between two data sets');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput, ' xtool patch [parameters] old_data new_data patch_data');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
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)));
|
|
||||||
S := ArgParse.AsString('--min=', 0, '64k');
|
|
||||||
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.MinSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
S := ArgParse.AsString('--max=', 0, '16g');
|
|
||||||
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.MaxSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
finally
|
|
||||||
ArgParse.Free;
|
|
||||||
ExpParse.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; 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;
|
|
||||||
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
I, J, K: Integer;
|
|
||||||
I64: Int64;
|
|
||||||
B, C: Boolean;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList1, LList2: TArray<String>;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
CS: TCriticalSection;
|
|
||||||
TempDir: String;
|
|
||||||
begin
|
|
||||||
C := FileExists(Input1) and FileExists(Input2);
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList1 := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList1) to High(LList1) do
|
|
||||||
LList1[I] := ReplaceText(LList1[I], BaseDir1, '');
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList2 := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList2) to High(LList2) do
|
|
||||||
LList2[I] := ReplaceText(LList2[I], BaseDir2, '');
|
|
||||||
K := XTOOL_PATCH;
|
|
||||||
Output.WriteBuffer(K, K.Size);
|
|
||||||
if not C then
|
|
||||||
for I := High(LList1) downto Low(LList1) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList1[I], LList2) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDelete;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
Delete(LList1, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if not C then
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList2[I], LList1) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if C then
|
|
||||||
LFilename := Input1
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + LList2[I];
|
|
||||||
if FileSize(LFilename) <> FileSize(BaseDir2 + LList2[I]) then
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[I]);
|
|
||||||
try
|
|
||||||
B := SynCommons.CompareMem(SStream1.Memory, SStream2.Memory,
|
|
||||||
SStream1.Size);
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
TempDir := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF1));
|
|
||||||
if not DirectoryExists(TempDir) then
|
|
||||||
CreateDir(TempDir);
|
|
||||||
SetLength(LList1, 0);
|
|
||||||
CS := TCriticalSection.Create;
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
J := -1;
|
|
||||||
K := 0;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Y, Z: Integer;
|
|
||||||
S1, S2: String;
|
|
||||||
A: Boolean;
|
|
||||||
SS0, SS1, SS2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
Z := Length(LList2);
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
while Y < Z do
|
|
||||||
begin
|
|
||||||
S1 := IncludeTrailingBackSlash(TempDir) + Y.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if C then
|
|
||||||
S2 := Input1
|
|
||||||
else
|
|
||||||
S2 := BaseDir1 + LList2[Y];
|
|
||||||
SS0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
SS1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[Y]);
|
|
||||||
try
|
|
||||||
ShowMessage(S2);
|
|
||||||
SS0.Size := Max(SS1.Size, SS2.Size);
|
|
||||||
A := xd3_encode(SS1.Memory, SS1.Size, SS2.Memory, SS2.Size,
|
|
||||||
SS0.Memory, @Res, SS0.Size, 0) = 0;
|
|
||||||
if A then
|
|
||||||
SS0.Size := Res;
|
|
||||||
finally
|
|
||||||
SS0.Free;
|
|
||||||
SS1.Free;
|
|
||||||
SS2.Free;
|
|
||||||
end;
|
|
||||||
if not A then
|
|
||||||
DeleteFile(S1);
|
|
||||||
CS.Acquire;
|
|
||||||
try
|
|
||||||
Insert(S1, LList1, Length(LList1));
|
|
||||||
Inc(K);
|
|
||||||
finally
|
|
||||||
CS.Release;
|
|
||||||
end;
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
Tasks[I].Start;
|
|
||||||
end;
|
|
||||||
I := 0;
|
|
||||||
while I < Length(LList2) do
|
|
||||||
begin
|
|
||||||
while I >= K do
|
|
||||||
Sleep(10);
|
|
||||||
LFilename := IncludeTrailingBackSlash(TempDir) + I.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDifferent;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
I64 := FileSize(LFilename);
|
|
||||||
Output.WriteBuffer(I64, I64.Size);
|
|
||||||
FStream := TFileStream.Create(LFilename, fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, I64);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir1 + LList1[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Inc(I);
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
Tasks[I].Free;
|
|
||||||
CS.Free;
|
|
||||||
if DirectoryExists(TempDir) then
|
|
||||||
TDirectory.Delete(TempDir);
|
|
||||||
end;
|
|
||||||
FillChar(LEntry, SizeOf(TEntryStruct2), 0);
|
|
||||||
LEntry.Op := TPatchOp.opNone;
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
var
|
|
||||||
I64: Int64;
|
|
||||||
B: Boolean;
|
|
||||||
S1, S2: String;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir: String;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream0, SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Output))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output));
|
|
||||||
S1 := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF3));
|
|
||||||
SStream0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
try
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
while LEntry.Op <> TPatchOp.opNone do
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
LFilename := Output
|
|
||||||
else
|
|
||||||
LFilename := BaseDir + LEntry.Filename;
|
|
||||||
case LEntry.Op of
|
|
||||||
TPatchOp.opDelete:
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TPatchOp.opMissing:
|
|
||||||
begin
|
|
||||||
ForceDirectories(ExtractFilePath(LFilename));
|
|
||||||
with TFileStream.Create(LFilename, fmCreate) do
|
|
||||||
try
|
|
||||||
CopyFrom(Input, LEntry.Size);
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
TPatchOp.opDifferent:
|
|
||||||
begin
|
|
||||||
Input.ReadBuffer(I64, I64.Size);
|
|
||||||
SStream0.Position := 0;
|
|
||||||
CopyStreamEx(Input, SStream0, I64);
|
|
||||||
S2 := ChangeFileExt(LFilename, '_' + Random($7FFFFFFF).ToHexString +
|
|
||||||
XTOOL_MAPSUF3);
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
B := False;
|
|
||||||
try
|
|
||||||
SStream2.Size := LEntry.Size;
|
|
||||||
B := xd3_decode(SStream0.Memory, I64, SStream1.Memory,
|
|
||||||
SStream1.Size, SStream2.Memory, @Res, SStream2.Size, 0) = 0;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
begin
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TFile.Move(S2, LFilename);
|
|
||||||
end
|
|
||||||
else if FileExists(S2) then
|
|
||||||
TFile.Delete(S2);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream0.Free;
|
|
||||||
if FileExists(S1) then
|
|
||||||
TFile.Delete(S1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,430 +0,0 @@
|
|||||||
unit IOPatch;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
MinSize, MaxSize: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
PDecodeOptions = ^TDecodeOptions;
|
|
||||||
|
|
||||||
TDecodeOptions = record
|
|
||||||
Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TDecodeOptions);
|
|
||||||
overload;
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
uses
|
|
||||||
XDeltaDLL;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'patch - creates patches between two data sets');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput, ' xtool patch [parameters] old_data new_data patch_data');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
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)));
|
|
||||||
S := ArgParse.AsString('--min=', 0, '64k');
|
|
||||||
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.MinSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
S := ArgParse.AsString('--max=', 0, '16g');
|
|
||||||
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.MaxSize := Max(0, Round(ExpParse.Evaluate(S)));
|
|
||||||
finally
|
|
||||||
ArgParse.Free;
|
|
||||||
ExpParse.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; 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;
|
|
||||||
|
|
||||||
procedure Encode(Input1, Input2: String; Output: TStream;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
I, J, K: Integer;
|
|
||||||
I64: Int64;
|
|
||||||
B, C: Boolean;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList1, LList2: TArray<String>;
|
|
||||||
LBytes: TBytes;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
CS: TCriticalSection;
|
|
||||||
TempDir: String;
|
|
||||||
begin
|
|
||||||
C := FileExists(Input1) and FileExists(Input2);
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList1 := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList1) to High(LList1) do
|
|
||||||
LList1[I] := ReplaceText(LList1[I], BaseDir1, '');
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
LList2 := GetFileList([Input2], True);
|
|
||||||
for I := Low(LList2) to High(LList2) do
|
|
||||||
LList2[I] := ReplaceText(LList2[I], BaseDir2, '');
|
|
||||||
K := XTOOL_PATCH;
|
|
||||||
Output.WriteBuffer(K, K.Size);
|
|
||||||
if not C then
|
|
||||||
for I := High(LList1) downto Low(LList1) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList1[I], LList2) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDelete;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
Delete(LList1, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if not C then
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if IndexText(LList2[I], LList1) < 0 then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
for I := High(LList2) downto Low(LList2) do
|
|
||||||
begin
|
|
||||||
if C then
|
|
||||||
LFilename := Input1
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + LList2[I];
|
|
||||||
if FileSize(LFilename) <> FileSize(BaseDir2 + LList2[I]) then
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[I]);
|
|
||||||
try
|
|
||||||
B := SynCommons.CompareMem(SStream1.Memory, SStream2.Memory,
|
|
||||||
SStream1.Size);
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
if InRange(FileSize(BaseDir2 + LList2[I]), Options.MinSize,
|
|
||||||
Options.MaxSize) then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir2 + LList2[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
Delete(LList2, I, 1);
|
|
||||||
end;
|
|
||||||
TempDir := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF1));
|
|
||||||
if not DirectoryExists(TempDir) then
|
|
||||||
CreateDir(TempDir);
|
|
||||||
SetLength(LList1, 0);
|
|
||||||
CS := TCriticalSection.Create;
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
J := -1;
|
|
||||||
K := 0;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Y, Z: Integer;
|
|
||||||
S1, S2: String;
|
|
||||||
A: Boolean;
|
|
||||||
SS0, SS1, SS2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
Z := Length(LList2);
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
while Y < Z do
|
|
||||||
begin
|
|
||||||
S1 := IncludeTrailingBackSlash(TempDir) + Y.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if C then
|
|
||||||
S2 := Input1
|
|
||||||
else
|
|
||||||
S2 := BaseDir1 + LList2[Y];
|
|
||||||
SS0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
SS1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
SS2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)),
|
|
||||||
BaseDir2 + LList2[Y]);
|
|
||||||
try
|
|
||||||
SS0.Size := Max(SS1.Size, SS2.Size);
|
|
||||||
A := xd3_encode(SS1.Memory, SS1.Size, SS2.Memory, SS2.Size,
|
|
||||||
SS0.Memory, @Res, SS0.Size, 0) = 0;
|
|
||||||
if A then
|
|
||||||
SS0.Size := Res;
|
|
||||||
finally
|
|
||||||
SS0.Free;
|
|
||||||
SS1.Free;
|
|
||||||
SS2.Free;
|
|
||||||
end;
|
|
||||||
if not A then
|
|
||||||
DeleteFile(S1);
|
|
||||||
CS.Acquire;
|
|
||||||
try
|
|
||||||
Insert(S1, LList1, Length(LList1));
|
|
||||||
Inc(K);
|
|
||||||
finally
|
|
||||||
CS.Release;
|
|
||||||
end;
|
|
||||||
Y := AtomicIncrement(J);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
Tasks[I].Start;
|
|
||||||
end;
|
|
||||||
I := 0;
|
|
||||||
while I < Length(LList2) do
|
|
||||||
begin
|
|
||||||
while I >= K do
|
|
||||||
Sleep(10);
|
|
||||||
LFilename := IncludeTrailingBackSlash(TempDir) + I.ToHexString +
|
|
||||||
XTOOL_MAPSUF3;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opDifferent;
|
|
||||||
LEntry.Filename := LList2[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir2 + LList2[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
I64 := FileSize(LFilename);
|
|
||||||
Output.WriteBuffer(I64, I64.Size);
|
|
||||||
FStream := TFileStream.Create(LFilename, fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, I64);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
LEntry.Op := TPatchOp.opMissing;
|
|
||||||
LEntry.Filename := LList1[I];
|
|
||||||
LEntry.Size := FileSize(BaseDir1 + LList1[I]);
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
FStream := TFileStream.Create(BaseDir1 + LList1[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
CopyStreamEx(FStream, Output, LEntry.Size);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Inc(I);
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
Tasks[I].Free;
|
|
||||||
CS.Free;
|
|
||||||
if DirectoryExists(TempDir) then
|
|
||||||
TDirectory.Delete(TempDir);
|
|
||||||
end;
|
|
||||||
FillChar(LEntry, SizeOf(TEntryStruct2), 0);
|
|
||||||
LEntry.Op := TPatchOp.opNone;
|
|
||||||
Output.WriteBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Decode(Input: TStream; Output: String; Options: TDecodeOptions);
|
|
||||||
var
|
|
||||||
I64: Int64;
|
|
||||||
B: Boolean;
|
|
||||||
S1, S2: String;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir: String;
|
|
||||||
LEntry: TEntryStruct2;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream0, SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
Res: NativeUInt;
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output))
|
|
||||||
else if DirectoryExists(Output) then
|
|
||||||
BaseDir := IncludeTrailingBackSlash(TPath.GetFullPath(Output))
|
|
||||||
else
|
|
||||||
BaseDir := ExtractFilePath(TPath.GetFullPath(Output));
|
|
||||||
S1 := IncludeTrailingBackSlash(GetCurrentDir) +
|
|
||||||
LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF3));
|
|
||||||
SStream0 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S1);
|
|
||||||
try
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
while LEntry.Op <> TPatchOp.opNone do
|
|
||||||
begin
|
|
||||||
if FileExists(Output) then
|
|
||||||
LFilename := Output
|
|
||||||
else
|
|
||||||
LFilename := BaseDir + LEntry.Filename;
|
|
||||||
case LEntry.Op of
|
|
||||||
TPatchOp.opDelete:
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TPatchOp.opMissing:
|
|
||||||
begin
|
|
||||||
ForceDirectories(ExtractFilePath(LFilename));
|
|
||||||
with TFileStream.Create(LFilename, fmCreate) do
|
|
||||||
try
|
|
||||||
CopyFrom(Input, LEntry.Size);
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
TPatchOp.opDifferent:
|
|
||||||
begin
|
|
||||||
Input.ReadBuffer(I64, I64.Size);
|
|
||||||
SStream0.Position := 0;
|
|
||||||
CopyStreamEx(Input, SStream0, I64);
|
|
||||||
S2 := ChangeFileExt(LFilename, '_' + Random($7FFFFFFF).ToHexString +
|
|
||||||
XTOOL_MAPSUF3);
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), LFilename);
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF2)), S2);
|
|
||||||
B := False;
|
|
||||||
try
|
|
||||||
SStream2.Size := LEntry.Size;
|
|
||||||
B := xd3_decode(SStream0.Memory, I64, SStream1.Memory,
|
|
||||||
SStream1.Size, SStream2.Memory, @Res, SStream2.Size, 0) = 0;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
if B then
|
|
||||||
begin
|
|
||||||
TFile.Delete(LFilename);
|
|
||||||
TFile.Move(S2, LFilename);
|
|
||||||
end
|
|
||||||
else if FileExists(S2) then
|
|
||||||
TFile.Delete(S2);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
Input.ReadBuffer(LEntry, SizeOf(TEntryStruct2));
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream0.Free;
|
|
||||||
if FileExists(S1) then
|
|
||||||
TFile.Delete(S1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,360 +0,0 @@
|
|||||||
unit IOReplace;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'replace - replace sectors with another within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool replace [parameters] old_streams new_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir1, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d', [LList[I], MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[LList[I], Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].CRCSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Input3) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3))
|
|
||||||
else if DirectoryExists(Input3) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input3))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3));
|
|
||||||
LList := GetFileList([Input3], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir2, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
LFilename := Input2
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + PEntry^.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
LFilename);
|
|
||||||
try
|
|
||||||
Move(SStream2.Memory^,
|
|
||||||
(PByte(SStream1.Memory) + PEntry.Position)^,
|
|
||||||
Min(SStream1.Size, SStream2.Size));
|
|
||||||
finally
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,363 +0,0 @@
|
|||||||
unit IOReplace;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'replace - replace sectors with another within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool replace [parameters] old_streams new_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir1, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d', [LList[I], MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[LList[I], Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].CRCSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Input3) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3))
|
|
||||||
else if DirectoryExists(Input3) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input3))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3));
|
|
||||||
LList := GetFileList([Input3], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir2, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
LFilename := Input2
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + PEntry^.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
LFilename);
|
|
||||||
try
|
|
||||||
Move(SStream2.Memory^,
|
|
||||||
(PByte(SStream1.Memory) + PEntry.Position)^,
|
|
||||||
Min(SStream1.Size, SStream2.Size));
|
|
||||||
finally
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d',
|
|
||||||
[LList[I], MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,363 +0,0 @@
|
|||||||
unit IOReplace;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'replace - replace sectors with another within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool replace [parameters] old_streams new_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir1, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d', [LList[I], MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[LList[I], Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].CRCSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Input3) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3))
|
|
||||||
else if DirectoryExists(Input3) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input3))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3));
|
|
||||||
LList := GetFileList([Input3], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir2, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
LFilename := Input2
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + PEntry^.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
LFilename);
|
|
||||||
try
|
|
||||||
Move(SStream2.Memory^,
|
|
||||||
(PByte(SStream1.Memory) + PEntry.Position)^,
|
|
||||||
Min(SStream1.Size, SStream2.Size));
|
|
||||||
finally
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[LList[I], MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,363 +0,0 @@
|
|||||||
unit IOReplace;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'replace - replace sectors with another within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool replace [parameters] old_streams new_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir1, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d', [LList[I], MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[LList[I], Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].CRCSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Input3) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3))
|
|
||||||
else if DirectoryExists(Input3) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input3))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3));
|
|
||||||
LList := GetFileList([Input3], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir2, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
LFilename := Input2
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + PEntry^.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
LFilename);
|
|
||||||
try
|
|
||||||
Move(SStream2.Memory^,
|
|
||||||
(PByte(SStream1.Memory) + PEntry.Position)^,
|
|
||||||
Min(SStream1.Size, SStream2.Size));
|
|
||||||
finally
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir2, ''), MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,363 +0,0 @@
|
|||||||
unit IOReplace;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'replace - replace sectors with another within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool replace [parameters] old_streams new_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir1, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d', [LList[I], MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[LList[I], Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].CRCSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Input3) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3))
|
|
||||||
else if DirectoryExists(Input3) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input3))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3));
|
|
||||||
LList := GetFileList([Input3], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir2, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
LFilename := Input2
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + PEntry^.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
LFilename);
|
|
||||||
try
|
|
||||||
Move(SStream2.Memory^,
|
|
||||||
(PByte(SStream1.Memory) + PEntry.Position)^,
|
|
||||||
Min(SStream1.Size, SStream2.Size));
|
|
||||||
finally
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir2, ''), MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,363 +0,0 @@
|
|||||||
unit IOReplace;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'replace - replace sectors with another within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool replace [parameters] old_streams new_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir1, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d', [LList[I], MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[LList[I], Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].CRCSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Input3) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3))
|
|
||||||
else if DirectoryExists(Input3) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input3))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3));
|
|
||||||
LList := GetFileList([Input3], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir2, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
LFilename := Input2
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + PEntry^.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
LFilename);
|
|
||||||
try
|
|
||||||
Move(SStream2.Memory^,
|
|
||||||
(PByte(SStream1.Memory) + PEntry.Position)^,
|
|
||||||
Min(SStream1.Size, SStream2.Size));
|
|
||||||
finally
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir2, ''), MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,364 +0,0 @@
|
|||||||
unit IOReplace;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'replace - replace sectors with another within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool replace [parameters] old_streams new_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir1, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('%s is smaller than %d',
|
|
||||||
[ReplaceText(LList[I], BaseDir1, ''), MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('%s is larger than %d',
|
|
||||||
[ReplaceText(LList[I], BaseDir1, ''), Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].CRCSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Input3) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3))
|
|
||||||
else if DirectoryExists(Input3) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input3))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3));
|
|
||||||
LList := GetFileList([Input3], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir2, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
LFilename := Input2
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + PEntry^.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
LFilename);
|
|
||||||
try
|
|
||||||
Move(SStream2.Memory^,
|
|
||||||
(PByte(SStream1.Memory) + PEntry.Position)^,
|
|
||||||
Min(SStream1.Size, SStream2.Size));
|
|
||||||
finally
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir2, ''), MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,364 +0,0 @@
|
|||||||
unit IOReplace;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'replace - replace sectors with another within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool replace [parameters] old_streams new_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct;
|
|
||||||
PEntry: PEntryStruct;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir1, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir1, ''), MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Larger than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir1, ''), Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].CRCSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Input3) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3))
|
|
||||||
else if DirectoryExists(Input3) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input3))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3));
|
|
||||||
LList := GetFileList([Input3], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir2, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
LFilename := Input2
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + PEntry^.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
LFilename);
|
|
||||||
try
|
|
||||||
Move(SStream2.Memory^,
|
|
||||||
(PByte(SStream1.Memory) + PEntry.Position)^,
|
|
||||||
Min(SStream1.Size, SStream2.Size));
|
|
||||||
finally
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir2, ''), MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,364 +0,0 @@
|
|||||||
unit IOReplace;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'replace - replace sectors with another within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool replace [parameters] old_streams new_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct1;
|
|
||||||
PEntry: PEntryStruct1;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct1>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir1, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir1, ''), MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Larger than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir1, ''), Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct1>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct1;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].CRCSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Input3) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3))
|
|
||||||
else if DirectoryExists(Input3) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input3))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3));
|
|
||||||
LList := GetFileList([Input3], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir2, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct1));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct1(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
LFilename := Input2
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + PEntry^.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
LFilename);
|
|
||||||
try
|
|
||||||
Move(SStream2.Memory^,
|
|
||||||
(PByte(SStream1.Memory) + PEntry.Position)^,
|
|
||||||
Min(SStream1.Size, SStream2.Size));
|
|
||||||
finally
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir2, ''), MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,364 +0,0 @@
|
|||||||
unit IOReplace;
|
|
||||||
|
|
||||||
{$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
|
|
||||||
PEncodeOptions = ^TEncodeOptions;
|
|
||||||
|
|
||||||
TEncodeOptions = record
|
|
||||||
ChunkSize, Threads: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
procedure Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
const
|
|
||||||
MinSize1 = 256;
|
|
||||||
MinSize2 = 65536;
|
|
||||||
|
|
||||||
type
|
|
||||||
PScanInfo = ^TScanInfo;
|
|
||||||
|
|
||||||
TScanInfo = record
|
|
||||||
Filename: String[255];
|
|
||||||
CRCSize, ActualSize: Integer;
|
|
||||||
CRC1, CRC2: Cardinal;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
SearchInfo: TArray<TArray<TArray<TScanInfo>>>;
|
|
||||||
SearchCount: TArray<TArray<Integer>>;
|
|
||||||
|
|
||||||
procedure PrintHelp;
|
|
||||||
var
|
|
||||||
I, J: Integer;
|
|
||||||
S: string;
|
|
||||||
begin
|
|
||||||
WriteLn(ErrOutput, 'replace - replace sectors with another within files');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Usage:');
|
|
||||||
WriteLn(ErrOutput,
|
|
||||||
' xtool replace [parameters] old_streams new_streams original_data [decode_data]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
WriteLn(ErrOutput, 'Parameters:');
|
|
||||||
WriteLn(ErrOutput, ' -c# - scanning range [16mb]');
|
|
||||||
WriteLn(ErrOutput, ' -t# - number of working threads [50p]');
|
|
||||||
WriteLn(ErrOutput, '');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure Parse(ParamArg: TArray<string>; out Options: TEncodeOptions);
|
|
||||||
var
|
|
||||||
ArgParse: TArgParser;
|
|
||||||
ExpParse: TExpressionParser;
|
|
||||||
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 Encode(Input1, Input2, Input3, Output: String;
|
|
||||||
Options: TEncodeOptions);
|
|
||||||
const
|
|
||||||
BufferSize = 65536;
|
|
||||||
var
|
|
||||||
Buffer: array [0 .. BufferSize - 1] of Byte;
|
|
||||||
A: Word;
|
|
||||||
B: Byte;
|
|
||||||
I, J, K: Integer;
|
|
||||||
LastStream: Int64;
|
|
||||||
Found1, Found2: Boolean;
|
|
||||||
CountPos: NativeInt;
|
|
||||||
LFilename: String;
|
|
||||||
BaseDir1, BaseDir2: String;
|
|
||||||
LList: TArray<String>;
|
|
||||||
LSInfo: PScanInfo;
|
|
||||||
LEntry: TEntryStruct1;
|
|
||||||
PEntry: PEntryStruct1;
|
|
||||||
LBytes: TBytes;
|
|
||||||
FStream: TFileStream;
|
|
||||||
SStream1, SStream2: TSharedMemoryStream;
|
|
||||||
OStream, MStream: TMemoryStream;
|
|
||||||
DataStore: TDataStore1;
|
|
||||||
Tasks: TArray<TTask>;
|
|
||||||
InfoStore: TArray<TListEx<TEntryStruct1>>;
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo, $10000);
|
|
||||||
SetLength(SearchCount, $10000);
|
|
||||||
for I := Low(SearchInfo) to High(SearchInfo) do
|
|
||||||
begin
|
|
||||||
SetLength(SearchInfo[I], $100);
|
|
||||||
SetLength(SearchCount[I], $100);
|
|
||||||
for J := Low(SearchCount[I]) to High(SearchCount[I]) do
|
|
||||||
SearchCount[I, J] := 0;
|
|
||||||
end;
|
|
||||||
if FileExists(Input1) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1))
|
|
||||||
else if DirectoryExists(Input1) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input1))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input1));
|
|
||||||
LList := GetFileList([Input1], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if InRange(FileSize(LList[I]), MinSize1, Integer.MaxValue) then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
with FStream do
|
|
||||||
try
|
|
||||||
ReadBuffer(Buffer[0], MinSize1);
|
|
||||||
WordRec(A).Bytes[0] := Buffer[0];
|
|
||||||
WordRec(A).Bytes[1] := Buffer[1];
|
|
||||||
B := Buffer[MinSize1 - 1];
|
|
||||||
J := MinSize1;
|
|
||||||
New(LSInfo);
|
|
||||||
LSInfo^.Filename := ReplaceText(LList[I], BaseDir1, '');
|
|
||||||
LSInfo^.CRCSize := J;
|
|
||||||
LSInfo^.ActualSize := FileSize(LList[I]);
|
|
||||||
LSInfo^.CRC1 := Utils.Hash32(0, @Buffer[0], J);
|
|
||||||
LSInfo^.CRC2 := LSInfo^.CRC1;
|
|
||||||
while (J > 0) and (LSInfo^.CRCSize < Options.ChunkSize) do
|
|
||||||
begin
|
|
||||||
J := Read(Buffer[0], Min(Options.ChunkSize - LSInfo^.CRCSize,
|
|
||||||
BufferSize));
|
|
||||||
Inc(LSInfo^.CRCSize, J);
|
|
||||||
LSInfo^.CRC2 := Utils.Hash32(LSInfo^.CRC2, @Buffer[0], J);
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
Free;
|
|
||||||
end;
|
|
||||||
Insert(LSInfo^, SearchInfo[A, B], Length(SearchInfo[A, B]));
|
|
||||||
Inc(SearchCount[A, B]);
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize1 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir1, ''), MinSize1]))
|
|
||||||
else if FileSize(LList[I]) > Integer.MaxValue then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Larger than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir1, ''), Integer.MaxValue]));
|
|
||||||
end;
|
|
||||||
DataStore := TDataStore1.Create(nil, True, Options.Threads,
|
|
||||||
Options.ChunkSize);
|
|
||||||
SetLength(Tasks, Options.Threads);
|
|
||||||
SetLength(InfoStore, Options.Threads);
|
|
||||||
OStream := TMemoryStream.Create;
|
|
||||||
MStream := TMemoryStream.Create;
|
|
||||||
if FileExists(Output) then
|
|
||||||
OStream.LoadFromFile(Output)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
K := XTOOL_IODEC;
|
|
||||||
OStream.WriteBuffer(K, K.Size);
|
|
||||||
end;
|
|
||||||
OStream.Position := OStream.Size;
|
|
||||||
Found1 := False;
|
|
||||||
try
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I] := TListEx<TEntryStruct1>.Create(EntryStructCmp);
|
|
||||||
Tasks[I] := TTask.Create(I);
|
|
||||||
Tasks[I].Perform(
|
|
||||||
procedure(X: IntPtr)
|
|
||||||
var
|
|
||||||
Ptr: PByte;
|
|
||||||
Y: Integer;
|
|
||||||
C: Word;
|
|
||||||
D: Byte;
|
|
||||||
Pos, Size, SizeEx: NativeInt;
|
|
||||||
CRC: Cardinal;
|
|
||||||
E: TEntryStruct1;
|
|
||||||
F: Boolean;
|
|
||||||
begin
|
|
||||||
Ptr := DataStore.Slot(X).Memory;
|
|
||||||
Pos := 0;
|
|
||||||
Size := DataStore.Size(X) - MinSize1;
|
|
||||||
SizeEx := DataStore.ActualSize(X);
|
|
||||||
while Pos < Size do
|
|
||||||
begin
|
|
||||||
C := PWord(Ptr + Pos)^;
|
|
||||||
D := (Ptr + Pos + MinSize1 - 1)^;
|
|
||||||
if (SearchCount[C, D] > 0) then
|
|
||||||
begin
|
|
||||||
F := False;
|
|
||||||
CRC := Utils.Hash32(0, Ptr + Pos, MinSize1);
|
|
||||||
for Y := 0 to SearchCount[C, D] - 1 do
|
|
||||||
begin
|
|
||||||
if (SearchInfo[C, D, Y].CRCSize <= (SizeEx - Pos)) then
|
|
||||||
if (CRC = SearchInfo[C, D, Y].CRC1) and
|
|
||||||
(Utils.Hash32(CRC, Ptr + Pos + MinSize1, SearchInfo[C, D,
|
|
||||||
Y].CRCSize - MinSize1) = SearchInfo[C, D, Y].CRC2) then
|
|
||||||
begin
|
|
||||||
E.Filename := SearchInfo[C, D, Y].Filename;
|
|
||||||
E.Position := DataStore.Position(X) + Pos;
|
|
||||||
E.Size := SearchInfo[C, D, Y].CRCSize;
|
|
||||||
InfoStore[X].Add(E);
|
|
||||||
Inc(Pos, E.Size);
|
|
||||||
F := True;
|
|
||||||
break;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if F then
|
|
||||||
continue;
|
|
||||||
end;
|
|
||||||
Inc(Pos);
|
|
||||||
end;
|
|
||||||
end);
|
|
||||||
end;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2))
|
|
||||||
else if DirectoryExists(Input2) then
|
|
||||||
BaseDir1 := IncludeTrailingBackSlash(TPath.GetFullPath(Input2))
|
|
||||||
else
|
|
||||||
BaseDir1 := ExtractFilePath(TPath.GetFullPath(Input2));
|
|
||||||
if FileExists(Input3) then
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3))
|
|
||||||
else if DirectoryExists(Input3) then
|
|
||||||
BaseDir2 := IncludeTrailingBackSlash(TPath.GetFullPath(Input3))
|
|
||||||
else
|
|
||||||
BaseDir2 := ExtractFilePath(TPath.GetFullPath(Input3));
|
|
||||||
LList := GetFileList([Input3], True);
|
|
||||||
for I := Low(LList) to High(LList) do
|
|
||||||
begin
|
|
||||||
if FileSize(LList[I]) >= MinSize2 then
|
|
||||||
begin
|
|
||||||
FStream := TFileStream.Create(LList[I], fmShareDenyNone);
|
|
||||||
try
|
|
||||||
LastStream := 0;
|
|
||||||
MStream.Position := 0;
|
|
||||||
Found2 := False;
|
|
||||||
DataStore.ChangeInput(FStream);
|
|
||||||
DataStore.Load;
|
|
||||||
LBytes := BytesOf(ReplaceText(LList[I], BaseDir2, ''));
|
|
||||||
K := Length(LBytes);
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
MStream.WriteBuffer(LBytes[0], K);
|
|
||||||
CountPos := MStream.Position;
|
|
||||||
K := 0;
|
|
||||||
MStream.WriteBuffer(K, K.Size);
|
|
||||||
while not DataStore.Done do
|
|
||||||
begin
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Count := 0;
|
|
||||||
Tasks[J].Start;
|
|
||||||
end;
|
|
||||||
WaitForAll(Tasks);
|
|
||||||
for J := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
if LEntry.Position < LastStream then
|
|
||||||
InfoStore[J].Delete(K)
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
Inc(PInteger(PByte(MStream.Memory) + CountPos)^,
|
|
||||||
InfoStore[J].Count);
|
|
||||||
if InfoStore[J].Count > 0 then
|
|
||||||
begin
|
|
||||||
Found1 := True;
|
|
||||||
Found2 := True;
|
|
||||||
end;
|
|
||||||
InfoStore[J].Index := 0;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
while K >= 0 do
|
|
||||||
begin
|
|
||||||
MStream.WriteBuffer(LEntry, SizeOf(TEntryStruct1));
|
|
||||||
LastStream := LEntry.Position + LEntry.Size;
|
|
||||||
K := InfoStore[J].Get(LEntry);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DataStore.Load;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
OStream.WriteBuffer(MStream.Memory^, MStream.Position);
|
|
||||||
finally
|
|
||||||
FStream.Free;
|
|
||||||
end;
|
|
||||||
if Found2 then
|
|
||||||
begin
|
|
||||||
SStream1 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)), LList[I]);
|
|
||||||
try
|
|
||||||
for J := PInteger(PByte(MStream.Memory) + CountPos)^ - 1 downto 0 do
|
|
||||||
begin
|
|
||||||
PEntry := PEntryStruct1(PByte(MStream.Memory) + CountPos +
|
|
||||||
Integer.Size) + J;
|
|
||||||
if FileExists(Input2) then
|
|
||||||
LFilename := Input2
|
|
||||||
else
|
|
||||||
LFilename := BaseDir1 + PEntry^.Filename;
|
|
||||||
if FileExists(LFilename) then
|
|
||||||
begin
|
|
||||||
SStream2 := TSharedMemoryStream.Create
|
|
||||||
(LowerCase(ChangeFileExt(ExtractFileName(Utils.GetModuleName),
|
|
||||||
'_' + Random($7FFFFFFF).ToHexString + XTOOL_MAPSUF)),
|
|
||||||
LFilename);
|
|
||||||
try
|
|
||||||
Move(SStream2.Memory^,
|
|
||||||
(PByte(SStream1.Memory) + PEntry.Position)^,
|
|
||||||
Min(SStream1.Size, SStream2.Size));
|
|
||||||
finally
|
|
||||||
SStream2.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
finally
|
|
||||||
SStream1.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else if FileSize(LList[I]) < MinSize2 then
|
|
||||||
WriteLn(ErrOutput, Format('Skipped %s (Smaller than %d)',
|
|
||||||
[ReplaceText(LList[I], BaseDir2, ''), MinSize2]));
|
|
||||||
end;
|
|
||||||
if Found1 and (Output <> '') then
|
|
||||||
OStream.SaveToFile(Output);
|
|
||||||
finally
|
|
||||||
for I := Low(Tasks) to High(Tasks) do
|
|
||||||
begin
|
|
||||||
InfoStore[I].Free;
|
|
||||||
Tasks[I].Free;
|
|
||||||
end;
|
|
||||||
DataStore.Free;
|
|
||||||
OStream.Free;
|
|
||||||
MStream.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
unit IOUtils;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
uses
|
|
||||||
WinAPI.Windows,
|
|
||||||
System.SysUtils, System.Classes, System.StrUtils, System.Types, System.Math,
|
|
||||||
System.Generics.Defaults, System.Generics.Collections;
|
|
||||||
|
|
||||||
const
|
|
||||||
XTOOL_IODEC = $314C5458;
|
|
||||||
XTOOL_PATCH = $324C5458;
|
|
||||||
XTOOL_MAPSUF = '_mapped.io';
|
|
||||||
|
|
||||||
type
|
|
||||||
PEntryStruct1 = ^TEntryStruct1;
|
|
||||||
|
|
||||||
TEntryStruct1 = packed record
|
|
||||||
Filename: String[255];
|
|
||||||
Position: Int64;
|
|
||||||
Size: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TEntryStructComparer = class(TComparer<TEntryStruct1>)
|
|
||||||
public
|
|
||||||
function Compare(const Left, Right: TEntryStruct1): Integer; override;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TPatchOp = (opDelete, opMissing, opDifferent);
|
|
||||||
|
|
||||||
PEntryStruct2 = ^TEntryStruct2;
|
|
||||||
|
|
||||||
TEntryStruct2 = packed record
|
|
||||||
Op: TPatchOp;
|
|
||||||
Filename: String[255];
|
|
||||||
Size: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
EntryStructCmp: TEntryStructComparer;
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
function TEntryStructComparer.Compare(const Left, Right: TEntryStruct1)
|
|
||||||
: Integer;
|
|
||||||
begin
|
|
||||||
Result := Integer(CompareValue(Left.Position, Right.Position));
|
|
||||||
end;
|
|
||||||
|
|
||||||
initialization
|
|
||||||
|
|
||||||
EntryStructCmp := TEntryStructComparer.Create;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
unit IOUtils;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
uses
|
|
||||||
WinAPI.Windows,
|
|
||||||
System.SysUtils, System.Classes, System.StrUtils, System.Types, System.Math,
|
|
||||||
System.Generics.Defaults, System.Generics.Collections;
|
|
||||||
|
|
||||||
const
|
|
||||||
XTOOL_IODEC = $314C5458;
|
|
||||||
XTOOL_PATCH = $324C5458;
|
|
||||||
XTOOL_MAPSUF1 = '_mapped.io';
|
|
||||||
|
|
||||||
type
|
|
||||||
PEntryStruct1 = ^TEntryStruct1;
|
|
||||||
|
|
||||||
TEntryStruct1 = packed record
|
|
||||||
Filename: String[255];
|
|
||||||
Position: Int64;
|
|
||||||
Size: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TEntryStructComparer = class(TComparer<TEntryStruct1>)
|
|
||||||
public
|
|
||||||
function Compare(const Left, Right: TEntryStruct1): Integer; override;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TPatchOp = (opDelete, opMissing, opDifferent);
|
|
||||||
|
|
||||||
PEntryStruct2 = ^TEntryStruct2;
|
|
||||||
|
|
||||||
TEntryStruct2 = packed record
|
|
||||||
Op: TPatchOp;
|
|
||||||
Filename: String[255];
|
|
||||||
Size: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
EntryStructCmp: TEntryStructComparer;
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
function TEntryStructComparer.Compare(const Left, Right: TEntryStruct1)
|
|
||||||
: Integer;
|
|
||||||
begin
|
|
||||||
Result := Integer(CompareValue(Left.Position, Right.Position));
|
|
||||||
end;
|
|
||||||
|
|
||||||
initialization
|
|
||||||
|
|
||||||
EntryStructCmp := TEntryStructComparer.Create;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
unit IOUtils;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
uses
|
|
||||||
WinAPI.Windows,
|
|
||||||
System.SysUtils, System.Classes, System.StrUtils, System.Types, System.Math,
|
|
||||||
System.Generics.Defaults, System.Generics.Collections;
|
|
||||||
|
|
||||||
const
|
|
||||||
XTOOL_IODEC = $314C5458;
|
|
||||||
XTOOL_PATCH = $324C5458;
|
|
||||||
XTOOL_MAPSUF2 = '_mapped.io';
|
|
||||||
|
|
||||||
type
|
|
||||||
PEntryStruct1 = ^TEntryStruct1;
|
|
||||||
|
|
||||||
TEntryStruct1 = packed record
|
|
||||||
Filename: String[255];
|
|
||||||
Position: Int64;
|
|
||||||
Size: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TEntryStructComparer = class(TComparer<TEntryStruct1>)
|
|
||||||
public
|
|
||||||
function Compare(const Left, Right: TEntryStruct1): Integer; override;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TPatchOp = (opDelete, opMissing, opDifferent);
|
|
||||||
|
|
||||||
PEntryStruct2 = ^TEntryStruct2;
|
|
||||||
|
|
||||||
TEntryStruct2 = packed record
|
|
||||||
Op: TPatchOp;
|
|
||||||
Filename: String[255];
|
|
||||||
Size: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
EntryStructCmp: TEntryStructComparer;
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
function TEntryStructComparer.Compare(const Left, Right: TEntryStruct1)
|
|
||||||
: Integer;
|
|
||||||
begin
|
|
||||||
Result := Integer(CompareValue(Left.Position, Right.Position));
|
|
||||||
end;
|
|
||||||
|
|
||||||
initialization
|
|
||||||
|
|
||||||
EntryStructCmp := TEntryStructComparer.Create;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
unit IOUtils;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
uses
|
|
||||||
WinAPI.Windows,
|
|
||||||
System.SysUtils, System.Classes, System.StrUtils, System.Types, System.Math,
|
|
||||||
System.Generics.Defaults, System.Generics.Collections;
|
|
||||||
|
|
||||||
const
|
|
||||||
XTOOL_IODEC = $314C5458;
|
|
||||||
XTOOL_PATCH = $324C5458;
|
|
||||||
XTOOL_MAPSUF2 = '_mapped.io';
|
|
||||||
XTOOL_MAPSUF2 = '_mapped.io';
|
|
||||||
|
|
||||||
type
|
|
||||||
PEntryStruct1 = ^TEntryStruct1;
|
|
||||||
|
|
||||||
TEntryStruct1 = packed record
|
|
||||||
Filename: String[255];
|
|
||||||
Position: Int64;
|
|
||||||
Size: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TEntryStructComparer = class(TComparer<TEntryStruct1>)
|
|
||||||
public
|
|
||||||
function Compare(const Left, Right: TEntryStruct1): Integer; override;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TPatchOp = (opDelete, opMissing, opDifferent);
|
|
||||||
|
|
||||||
PEntryStruct2 = ^TEntryStruct2;
|
|
||||||
|
|
||||||
TEntryStruct2 = packed record
|
|
||||||
Op: TPatchOp;
|
|
||||||
Filename: String[255];
|
|
||||||
Size: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
EntryStructCmp: TEntryStructComparer;
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
function TEntryStructComparer.Compare(const Left, Right: TEntryStruct1)
|
|
||||||
: Integer;
|
|
||||||
begin
|
|
||||||
Result := Integer(CompareValue(Left.Position, Right.Position));
|
|
||||||
end;
|
|
||||||
|
|
||||||
initialization
|
|
||||||
|
|
||||||
EntryStructCmp := TEntryStructComparer.Create;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
unit IOUtils;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
uses
|
|
||||||
WinAPI.Windows,
|
|
||||||
System.SysUtils, System.Classes, System.StrUtils, System.Types, System.Math,
|
|
||||||
System.Generics.Defaults, System.Generics.Collections;
|
|
||||||
|
|
||||||
const
|
|
||||||
XTOOL_IODEC = $314C5458;
|
|
||||||
XTOOL_PATCH = $324C5458;
|
|
||||||
XTOOL_MAPSUF1 = '-vm.tmp';
|
|
||||||
XTOOL_MAPSUF2 = '_mapped.io';
|
|
||||||
|
|
||||||
type
|
|
||||||
PEntryStruct1 = ^TEntryStruct1;
|
|
||||||
|
|
||||||
TEntryStruct1 = packed record
|
|
||||||
Filename: String[255];
|
|
||||||
Position: Int64;
|
|
||||||
Size: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TEntryStructComparer = class(TComparer<TEntryStruct1>)
|
|
||||||
public
|
|
||||||
function Compare(const Left, Right: TEntryStruct1): Integer; override;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TPatchOp = (opDelete, opMissing, opDifferent);
|
|
||||||
|
|
||||||
PEntryStruct2 = ^TEntryStruct2;
|
|
||||||
|
|
||||||
TEntryStruct2 = packed record
|
|
||||||
Op: TPatchOp;
|
|
||||||
Filename: String[255];
|
|
||||||
Size: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
EntryStructCmp: TEntryStructComparer;
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
function TEntryStructComparer.Compare(const Left, Right: TEntryStruct1)
|
|
||||||
: Integer;
|
|
||||||
begin
|
|
||||||
Result := Integer(CompareValue(Left.Position, Right.Position));
|
|
||||||
end;
|
|
||||||
|
|
||||||
initialization
|
|
||||||
|
|
||||||
EntryStructCmp := TEntryStructComparer.Create;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
unit IOUtils;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
uses
|
|
||||||
WinAPI.Windows,
|
|
||||||
System.SysUtils, System.Classes, System.StrUtils, System.Types, System.Math,
|
|
||||||
System.Generics.Defaults, System.Generics.Collections;
|
|
||||||
|
|
||||||
const
|
|
||||||
XTOOL_IODEC = $314C5458;
|
|
||||||
XTOOL_PATCH = $324C5458;
|
|
||||||
XTOOL_MAPSUF1 = '-vm.tmp';
|
|
||||||
XTOOL_MAPSUF2 = '_mapped.io';
|
|
||||||
XTOOL_MAPSUF3 = '-tmp';
|
|
||||||
|
|
||||||
type
|
|
||||||
PEntryStruct1 = ^TEntryStruct1;
|
|
||||||
|
|
||||||
TEntryStruct1 = packed record
|
|
||||||
Filename: String[255];
|
|
||||||
Position: Int64;
|
|
||||||
Size: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TEntryStructComparer = class(TComparer<TEntryStruct1>)
|
|
||||||
public
|
|
||||||
function Compare(const Left, Right: TEntryStruct1): Integer; override;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TPatchOp = (opDelete, opMissing, opDifferent);
|
|
||||||
|
|
||||||
PEntryStruct2 = ^TEntryStruct2;
|
|
||||||
|
|
||||||
TEntryStruct2 = packed record
|
|
||||||
Op: TPatchOp;
|
|
||||||
Filename: String[255];
|
|
||||||
Size: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
EntryStructCmp: TEntryStructComparer;
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
function TEntryStructComparer.Compare(const Left, Right: TEntryStruct1)
|
|
||||||
: Integer;
|
|
||||||
begin
|
|
||||||
Result := Integer(CompareValue(Left.Position, Right.Position));
|
|
||||||
end;
|
|
||||||
|
|
||||||
initialization
|
|
||||||
|
|
||||||
EntryStructCmp := TEntryStructComparer.Create;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
unit IOUtils;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
uses
|
|
||||||
WinAPI.Windows,
|
|
||||||
System.SysUtils, System.Classes, System.StrUtils, System.Types, System.Math,
|
|
||||||
System.Generics.Defaults, System.Generics.Collections;
|
|
||||||
|
|
||||||
const
|
|
||||||
XTOOL_IODEC = $314C5458;
|
|
||||||
XTOOL_PATCH = $324C5458;
|
|
||||||
XTOOL_MAPSUF1 = '-tmp';
|
|
||||||
XTOOL_MAPSUF2 = '_mapped.io';
|
|
||||||
|
|
||||||
type
|
|
||||||
PEntryStruct1 = ^TEntryStruct1;
|
|
||||||
|
|
||||||
TEntryStruct1 = packed record
|
|
||||||
Filename: String[255];
|
|
||||||
Position: Int64;
|
|
||||||
Size: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TEntryStructComparer = class(TComparer<TEntryStruct1>)
|
|
||||||
public
|
|
||||||
function Compare(const Left, Right: TEntryStruct1): Integer; override;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TPatchOp = (opDelete, opMissing, opDifferent);
|
|
||||||
|
|
||||||
PEntryStruct2 = ^TEntryStruct2;
|
|
||||||
|
|
||||||
TEntryStruct2 = packed record
|
|
||||||
Op: TPatchOp;
|
|
||||||
Filename: String[255];
|
|
||||||
Size: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
EntryStructCmp: TEntryStructComparer;
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
function TEntryStructComparer.Compare(const Left, Right: TEntryStruct1)
|
|
||||||
: Integer;
|
|
||||||
begin
|
|
||||||
Result := Integer(CompareValue(Left.Position, Right.Position));
|
|
||||||
end;
|
|
||||||
|
|
||||||
initialization
|
|
||||||
|
|
||||||
EntryStructCmp := TEntryStructComparer.Create;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
unit IOUtils;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
uses
|
|
||||||
WinAPI.Windows,
|
|
||||||
System.SysUtils, System.Classes, System.StrUtils, System.Types, System.Math,
|
|
||||||
System.Generics.Defaults, System.Generics.Collections;
|
|
||||||
|
|
||||||
const
|
|
||||||
XTOOL_IODEC = $314C5458;
|
|
||||||
XTOOL_PATCH = $324C5458;
|
|
||||||
XTOOL_MAPSUF1 = '-tmp';
|
|
||||||
XTOOL_MAPSUF2 = '_mapped.io';
|
|
||||||
|
|
||||||
type
|
|
||||||
PEntryStruct1 = ^TEntryStruct1;
|
|
||||||
|
|
||||||
TEntryStruct1 = packed record
|
|
||||||
Filename: String[255];
|
|
||||||
Position: Int64;
|
|
||||||
Size: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TEntryStructComparer = class(TComparer<TEntryStruct1>)
|
|
||||||
public
|
|
||||||
function Compare(const Left, Right: TEntryStruct1): Integer; override;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TPatchOp = (opDelete, opMissing, opDifferent);
|
|
||||||
|
|
||||||
PEntryStruct2 = ^TEntryStruct2;
|
|
||||||
|
|
||||||
TEntryStruct2 = packed record
|
|
||||||
Op: TPatchOp;
|
|
||||||
Filename: String[255];
|
|
||||||
Size: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
EntryStructCmp: TEntryStructComparer;
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
function TEntryStructComparer.Compare(const Left, Right: TEntryStruct1)
|
|
||||||
: Integer;
|
|
||||||
begin
|
|
||||||
Result := Integer(CompareValue(Left.Position, Right.Position));
|
|
||||||
end;
|
|
||||||
|
|
||||||
initialization
|
|
||||||
|
|
||||||
EntryStructCmp := TEntryStructComparer.Create;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
unit IOUtils;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
uses
|
|
||||||
WinAPI.Windows,
|
|
||||||
System.SysUtils, System.Classes, System.StrUtils, System.Types, System.Math,
|
|
||||||
System.Generics.Defaults, System.Generics.Collections;
|
|
||||||
|
|
||||||
const
|
|
||||||
XTOOL_IODEC = $314C5458;
|
|
||||||
XTOOL_PATCH = $324C5458;
|
|
||||||
XTOOL_MAPSUF1 = '-tmp';
|
|
||||||
XTOOL_MAPSUF2 = '_mapped.io';
|
|
||||||
|
|
||||||
type
|
|
||||||
PEntryStruct1 = ^TEntryStruct1;
|
|
||||||
|
|
||||||
TEntryStruct1 = packed record
|
|
||||||
Filename: String[255];
|
|
||||||
Position: Int64;
|
|
||||||
Size: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TEntryStructComparer = class(TComparer<TEntryStruct1>)
|
|
||||||
public
|
|
||||||
function Compare(const Left, Right: TEntryStruct1): Integer; override;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TPatchOp = (opDelete, opMissing, opDifferent);
|
|
||||||
|
|
||||||
PEntryStruct2 = ^TEntryStruct2;
|
|
||||||
|
|
||||||
TEntryStruct2 = packed record
|
|
||||||
Op: TPatchOp;
|
|
||||||
Filename: String[255];
|
|
||||||
Size: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
EntryStructCmp: TEntryStructComparer;
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
function TEntryStructComparer.Compare(const Left, Right: TEntryStruct1)
|
|
||||||
: Integer;
|
|
||||||
begin
|
|
||||||
Result := Integer(CompareValue(Left.Position, Right.Position));
|
|
||||||
end;
|
|
||||||
|
|
||||||
initialization
|
|
||||||
|
|
||||||
EntryStructCmp := TEntryStructComparer.Create;
|
|
||||||
|
|
||||||
end.
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
unit IOUtils;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
uses
|
|
||||||
WinAPI.Windows,
|
|
||||||
System.SysUtils, System.Classes, System.StrUtils, System.Types, System.Math,
|
|
||||||
System.Generics.Defaults, System.Generics.Collections;
|
|
||||||
|
|
||||||
const
|
|
||||||
XTOOL_IODEC = $314C5458;
|
|
||||||
XTOOL_PATCH = $324C5458;
|
|
||||||
XTOOL_MAPSUF1 = '-tmp';
|
|
||||||
XTOOL_MAPSUF2 = '_mapped.io';
|
|
||||||
XTOOL_MAPSUF3 = '.tmp';
|
|
||||||
|
|
||||||
type
|
|
||||||
PEntryStruct1 = ^TEntryStruct1;
|
|
||||||
|
|
||||||
TEntryStruct1 = packed record
|
|
||||||
Filename: String[255];
|
|
||||||
Position: Int64;
|
|
||||||
Size: Integer;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TEntryStructComparer = class(TComparer<TEntryStruct1>)
|
|
||||||
public
|
|
||||||
function Compare(const Left, Right: TEntryStruct1): Integer; override;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TPatchOp = (opDelete, opMissing, opDifferent);
|
|
||||||
|
|
||||||
PEntryStruct2 = ^TEntryStruct2;
|
|
||||||
|
|
||||||
TEntryStruct2 = packed record
|
|
||||||
Op: TPatchOp;
|
|
||||||
Filename: String[255];
|
|
||||||
Size: Int64;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var
|
|
||||||
EntryStructCmp: TEntryStructComparer;
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
function TEntryStructComparer.Compare(const Left, Right: TEntryStruct1)
|
|
||||||
: Integer;
|
|
||||||
begin
|
|
||||||
Result := Integer(CompareValue(Left.Position, Right.Position));
|
|
||||||
end;
|
|
||||||
|
|
||||||
initialization
|
|
||||||
|
|
||||||
EntryStructCmp := TEntryStructComparer.Create;
|
|
||||||
|
|
||||||
end.
|
|
||||||
Reference in New Issue
Block a user