library xtool.unity;
{$IF CompilerVersion < 23.0}
{$Message Fatal 'Compiler outdated'}
{$IFEND}
{$R *.res}
uses
System.SysUtils,
System.Classes,
System.StrUtils,
System.IOUtils,
MemoryModule in '..\base\MemoryModule.pas',
PluginUtils in '..\base\PluginUtils.pas',
LZ4DLL in '..\imports\LZ4DLL.pas';
const
STATUS_NONE = 0;
STATUS_PREDICTED = 1;
STATUS_PROCESSED = 2;
STATUS_VERIFIED = 3;
type
PEncodeSI = ^TEncodeSI;
TEncodeSI = record
ActualPosition, StorePosition: Cardinal;
OriginalSize, UnpackedSize: Cardinal;
InfoPosition, InfoSize: Cardinal;
Checksum: Cardinal;
ID, Codec: Byte;
Option: Byte;
Status: Byte;
Reserved: Cardinal;
end;
PDecodeSI = ^TDecodeSI;
TDecodeSI = record
Position: Cardinal;
Size: Cardinal;
ID, Codec: Byte;
Option: Byte;
end;
PFutureSI = ^TFutureSI;
TFutureSI = record
Position: Int64;
OriginalSize, UnpackedSize: Cardinal;
ID, Codec: Byte;
Option: Byte;
Status: Byte;
end;
var
XInfo: TArray<TArray<String>>;
function GetID(Method: String): Integer;
var
I, J: Integer;
begin
Result := 0;
for I := Low(XInfo) to High(XInfo) do
for J := Low(XInfo[I]) to High(XInfo[I]) do
if XInfo[I][J] = Method then
begin
Result := I;
break
end;
end;
function GetCodec(Method: String): Integer;
var
I, J: Integer;
begin
Result := 0;
for I := Low(XInfo) to High(XInfo) do
for J := Low(XInfo[I]) to High(XInfo[I]) do
if XInfo[I][J] = Method then
begin
Result := J;
break
end;
end;
type
TPluginCtx = record
end;
PPluginCtx = ^TPluginCtx;
const
CodecNames: TArray<String> = ['unity'];
UNITY_CODEC = 0;
var
CodecInit: TArray<Boolean>;
function XTool_Init(Methods: String; AInfo: TArray < TArray < String >> )
: PPluginCtx;
var
I: Integer;
B: Boolean;
begin
XInfo := AInfo;
SetLength(CodecInit, Length(CodecNames));
for I := Low(CodecNames) to High(CodecNames) do
begin
B := AnsiIndexText(CodecNames[I], DecodeStr(Methods, ',')) >= 0;
case I of
UNITY_CODEC:
CodecInit[I] := B and LZ4DLL.DLLLoaded;
end;
end;
end;
procedure XTool_Free(ctx: PPluginCtx);
begin
end;
function XTool_Codecs: TArray<String>;
begin
Result := CodecNames;
end;
procedure XTool_Scan1(ctx: PPluginCtx; MemInput: TMemoryStream; MemPos: Int64;
AvailSize, TotalSize: Cardinal; Output, Temp: TMemoryStream;
Info1, Info2: TInfoStore);
var
Ptr: PByte;
Pos: Cardinal;
I: Integer;
P1, P2: Integer;
Flags: Integer;
Count: Integer;
X, Y: Integer;
S: String;
OutPos: Int64;
StreamInfo1: TEncodeSI;
StreamInfo2: TFutureSI;
begin
if BoolArray(CodecInit, False) then
exit;
Ptr := MemInput.Memory;
Pos := 0;
while Pos < AvailSize do
begin
if CodecInit[UNITY_CODEC] and (PInteger(Ptr + Pos)^ = $74696E55) then
begin
P1 := GetStr((Ptr + Pos), 10, S) + 1; // ident (UnityFS)
if S = 'UnityFS' then
begin
Inc(P1, Integer.Size); // file version (6)
Inc(P1, GetStr((Ptr + Pos + P1), 10, S) + 1); // 5.x.x
Inc(P1, GetStr((Ptr + Pos + P1), 10, S) + 1); // engine version
Inc(P1, Int64.Size); // file size
X := EndianSwap(PInteger(Ptr + Pos + P1)^);
Inc(P1, Integer.Size); // compressed structure size
Y := EndianSwap(PInteger(Ptr + Pos + P1)^);
Inc(P1, Integer.Size); // decompressed structure size
Flags := EndianSwap(PInteger(Ptr + Pos + P1)^);
Inc(P1, Integer.Size); // flags
if Temp.Size < Y then
Temp.Size := Y;
// decompress the structure to get info
if (Flags and $3F) = 3 then
if LZ4_decompress_safe((Ptr + Pos + P1), Temp.Memory, X, Y) = Y then
begin
if Y > X then
begin
OutPos := Output.Position;
Output.WriteBuffer(Temp.Memory^, Y);
StreamInfo1.ActualPosition := Pos + P1;
StreamInfo1.StorePosition := OutPos;
StreamInfo1.OriginalSize := X;
StreamInfo1.UnpackedSize := Y;
StreamInfo1.InfoPosition := 0;
StreamInfo1.InfoSize := 0;
StreamInfo1.Checksum := crc32(0, (Ptr + Pos + P1), X);
StreamInfo1.ID := GetID('lz4hc');
StreamInfo1.Codec := GetCodec('lz4hc');
StreamInfo1.Option := 10;
StreamInfo1.Status := STATUS_PREDICTED;
Info1.Add(StreamInfo1);
end; // optional
Inc(P1, X);
P2 := 16;
Count := EndianSwap(PInteger(PByte(Temp.Memory) + P2)^);
Inc(P2, Integer.Size); // stream count
for I := 0 to Count - 1 do
begin
Y := EndianSwap(PInteger(PByte(Temp.Memory) + P2)^);
Inc(P2, Integer.Size); // decompressed stream size
X := EndianSwap(PInteger(PByte(Temp.Memory) + P2)^);
Inc(P2, Integer.Size); // compressed stream size
Inc(P2, SmallInt.Size); // flag
if Y > X then
begin
StreamInfo2.Position := MemPos + Pos + P1;
StreamInfo2.OriginalSize := X;
StreamInfo2.UnpackedSize := Y;
StreamInfo2.ID := GetID('lz4hc');
StreamInfo2.Codec := GetCodec('lz4hc');
StreamInfo2.Option := 10;
StreamInfo2.Status := STATUS_PREDICTED;
Info2.Add(StreamInfo2);
// lz4hc, level 10 (predicted)
end;
Inc(P1, X); // seek to next stream
end;
Inc(Pos, P1);
continue;
end;
end;
end;
Inc(Pos);
end;
end;
procedure XTool_Scan2(ctx: PPluginCtx; MemInput: TMemoryStream; MemPos: Int64;
AvailSize, TotalSize: Cardinal; Output, Temp: TMemoryStream;
Info1, Info2: TInfoStore);
begin
end;
procedure XTool_Process(ctx: PPluginCtx; MemInput1, MemInput2: TMemoryStream;
Temp: TMemoryStream; var StreamInfo: TEncodeSI);
begin
end;
procedure XTool_Restore(ctx: PPluginCtx; Output: TStream;
MemInput: TMemoryStream; Temp: TMemoryStream; var StreamInfo: TDecodeSI);
begin
end;
exports XTool_Init, XTool_Free, XTool_Codecs, XTool_Scan1, XTool_Scan2,
XTool_Process, XTool_Restore;
begin
end.