xtool/precompressor/PrecompSearch.pas

269 lines
7.5 KiB
ObjectPascal

unit PrecompSearch;
interface
uses
Utils, SynCommons, SynCrypto,
PrecompUtils,
WinAPI.Windows,
System.SysUtils, System.Classes, System.StrUtils,
System.Types, System.Math, System.IOUtils;
const
XTOOL_DB = $42445458;
var
Codec: TPrecompressor;
implementation
type
PEntryStruct = ^TEntryStruct;
TEntryStruct = record
Position: Int64;
OldSize, NewSize: Integer;
end;
PSearchStruct = ^TSearchStruct;
TSearchStruct = record
Name: String;
SearchInt: Int64;
HashSize: Integer;
HashDigest: TMD5Digest;
Codec: String;
Resource: Integer;
EntryList: TArray<TEntryStruct>;
end;
var
SearchList: TStringDynArray;
SearchInfo: TArray<TArray<TArray<Integer>>>;
SearchCount: TArray<TArray<Integer>>;
CodecSearch: TArray<TArray<TSearchStruct>>;
CodecAvailable, CodecEnabled: TArray<Boolean>;
function SearchInit(Command: PChar; Count: Integer;
Funcs: PPrecompFuncs): Boolean;
var
I: Integer;
X, Y, Z: Integer;
S: String;
begin
Result := True;
for X := Low(CodecAvailable) to High(CodecAvailable) do
begin
CodecAvailable[X] := True;
CodecEnabled[X] := False;
end;
for X := Low(CodecSearch) to High(CodecSearch) do
for Y := Low(CodecSearch[X]) to High(CodecSearch[X]) do
CodecSearch[X, Y].Resource := RegisterResources(CodecSearch[X, Y].Codec);
X := 0;
while Funcs^.GetCodec(Command, X, False) <> '' do
begin
S := Funcs^.GetCodec(Command, X, False);
for Y := Low(Codec.Names) to High(Codec.Names) do
if CompareText(S, Codec.Names[Y]) = 0 then
begin
CodecEnabled[Y] := True;
break;
end;
Inc(X);
end;
I := 0;
for X := Low(CodecEnabled) to High(CodecEnabled) do
if CodecEnabled[X] then
begin
SetLength(SearchInfo[X], $10000);
SetLength(SearchCount[X], $10000);
for Z := Low(SearchInfo[X]) to High(SearchInfo[X]) do
begin
SearchCount[X, Z] := 0;
for Y := Low(CodecSearch[X]) to High(CodecSearch[X]) do
begin
LongRec(I).Words[0] := Int64Rec(CodecSearch[X, Y].SearchInt).Words[0];
if Z = I then
begin
Inc(SearchCount[X, Z]);
Insert(Y, SearchInfo[X, Z], Length(SearchInfo[X, Z]));
end;
AddMethod(PrecompGetCodec(PChar(CodecSearch[X, Y].Codec), 0, False));
end;
end;
end;
end;
procedure SearchFree(Funcs: PPrecompFuncs);
var
X, Y, Z: Integer;
begin
for X := Low(CodecEnabled) to High(CodecEnabled) do
if CodecEnabled[X] then
begin
for Y := Low(SearchInfo[X]) to High(SearchInfo[X]) do
SetLength(SearchInfo[X, Y], 0);
SetLength(SearchInfo[X], 0);
SetLength(SearchCount[X], 0);
end;
end;
function SearchParse(Command: PChar; Option: PInteger;
Funcs: PPrecompFuncs): Boolean;
begin
Result := False;
end;
procedure SearchScan1(Instance, Depth: Integer; Input: PByte;
Size, SizeEx: NativeInt; Output: _PrecompOutput; Add: _PrecompAdd;
Funcs: PPrecompFuncs);
var
I: Integer;
J: Word;
X, Y, Z: Integer;
Pos, LSize: NativeInt;
SI: _StrInfo1;
SS: PSearchStruct;
MD5: TMD5;
Digest: TMD5Digest;
MD5Checked: Boolean;
begin
for I := Low(CodecSearch) to High(CodecSearch) do
if CodecEnabled[I] then
begin
Pos := 0;
LSize := Size - Pred(Int64.Size);
while Pos < LSize do
begin
J := PWord(Input + Pos)^;
if (SearchCount[I, J] > 0) and
(CodecSearch[I, 0].HashSize <= (SizeEx - Pos)) then
begin
MD5Checked := False;
for X := 0 to SearchCount[I, J] - 1 do
begin
if (PInt64(Input + Pos)^ = CodecSearch[I, SearchInfo[I, J, X]]
.SearchInt) then
begin
if not MD5Checked then
begin
MD5.Full(Input + Pos, CodecSearch[I, 0].HashSize, Digest);
MD5Checked := True;
end;
// fix this
if CompareMem(@CodecSearch[I, SearchInfo[I, J, X]].HashDigest[0],
@Digest[0], sizeof(TMD5Digest)) then
begin
SS := @CodecSearch[I, SearchInfo[I, J, X]];
Output(Instance, nil, -1);
for Y := Low(SS^.EntryList) to High(SS^.EntryList) do
begin
SI.Position := Pos + SS^.EntryList[Y].Position;
SI.OldSize := SS^.EntryList[Y].OldSize;
SI.NewSize := SS^.EntryList[Y].NewSize;
SI.Option := 0;
SI.Resource := SS^.Resource;
if System.Pos(SPrecompSep2, SS^.Codec) > 0 then
SI.Status := TStreamStatus.Predicted
else
SI.Status := TStreamStatus.None;
Add(Instance, @SI, PChar(SS^.Codec), nil);
end;
end;
end;
end;
end;
Inc(Pos);
end;
end;
end;
function SearchScan2(Instance, Depth: Integer; Input: Pointer; Size: NativeInt;
StreamInfo: PStrInfo2; Output: _PrecompOutput; Funcs: PPrecompFuncs): Boolean;
begin
Result := False;
end;
function SearchProcess(Instance, Depth: Integer; OldInput, NewInput: Pointer;
StreamInfo: PStrInfo2; Output: _PrecompOutput; Funcs: PPrecompFuncs): Boolean;
begin
Result := False;
end;
function SearchRestore(Instance, Depth: Integer; Input, InputExt: Pointer;
StreamInfo: _StrInfo3; Output: _PrecompOutput; Funcs: PPrecompFuncs): Boolean;
begin
Result := False;
end;
var
I, J, K: Integer;
S: String;
Bytes: TBytes;
FStream: TFileStream;
I32: Integer;
SearchStruct: PSearchStruct;
initialization
SearchList := TDirectory.GetFiles(ExtractFilePath(Utils.GetModuleName), '*.xtl',
TSearchOption.soTopDirectoryOnly);
for I := Low(SearchList) to High(SearchList) do
begin
FStream := TFileStream.Create(SearchList[I], fmShareDenyNone);
try
if FStream.Size >= 4 then
begin
FStream.ReadBuffer(I32, I32.Size);
if (I32 = XTOOL_DB) then
begin
if FStream.Position < FStream.Size then
begin
J := Length(CodecSearch);
SetLength(CodecSearch, Succ(J));
S := ChangeFileExt(ExtractFileName(SearchList[I]), '');
Insert(S, Codec.Names, Length(Codec.Names));
end;
while FStream.Position < FStream.Size do
begin
New(SearchStruct);
SearchStruct^.Name := S;
FStream.ReadBuffer(SearchStruct^.SearchInt,
SearchStruct^.SearchInt.Size);
FStream.ReadBuffer(SearchStruct^.HashSize,
SearchStruct^.HashSize.Size);
FStream.ReadBuffer(SearchStruct^.HashDigest, sizeof(THash128));
FStream.ReadBuffer(I32, I32.Size);
SetLength(Bytes, I32);
FStream.ReadBuffer(Bytes[0], I32);
SearchStruct^.Codec := StringOf(Bytes);
Insert(SearchStruct^, CodecSearch[J], Length(CodecSearch[J]));
FStream.ReadBuffer(I32, I32.Size);
K := Pred(Length(CodecSearch[J]));
SetLength(CodecSearch[J, K].EntryList, I32);
FStream.ReadBuffer(CodecSearch[J, K].EntryList[0],
I32 * sizeof(TEntryStruct));
end;
end;
end;
finally
FStream.Free;
end;
end;
Codec.Initialised := False;
Codec.Init := @SearchInit;
Codec.Free := @SearchFree;
Codec.Parse := @SearchParse;
Codec.Scan1 := @SearchScan1;
Codec.Scan2 := @SearchScan2;
Codec.Process := @SearchProcess;
Codec.Restore := @SearchRestore;
SetLength(SearchInfo, Length(CodecSearch));
SetLength(SearchCount, Length(CodecSearch));
SetLength(CodecAvailable, Length(CodecSearch));
SetLength(CodecEnabled, Length(CodecSearch));
end.