148 lines
5.1 KiB
C#
148 lines
5.1 KiB
C#
using System.Collections.Generic;
|
|
using System.IO;
|
|
using UnityEngine;
|
|
|
|
public class ScriptLoader
|
|
{
|
|
public Script Load(byte[] binaryData)
|
|
{
|
|
Script script = new Script();
|
|
using (BinaryReader reader = new BinaryReader(new MemoryStream(binaryData)))
|
|
{
|
|
uint subTableOffset = reader.ReadUInt32();
|
|
uint unk2 = reader.ReadUInt32();
|
|
uint subCount = reader.ReadUInt32();
|
|
|
|
uint cmdListOffset = reader.ReadUInt32();
|
|
uint paramsListOffset = reader.ReadUInt32();
|
|
uint textArrayOffset = reader.ReadUInt32();
|
|
uint endOfFileOffset = (uint)binaryData.Length; // Êîíåö ôàéëà
|
|
|
|
script.SubroutineTable = ParseSubTable(reader, subCount, subTableOffset);
|
|
|
|
reader.BaseStream.Position = cmdListOffset;
|
|
List<ScriptCommand> commandsWithoutParams = ParseCmdListWithoutParams(reader, paramsListOffset);
|
|
script.Commands = new List<ScriptCommand>(commandsWithoutParams.Count);
|
|
|
|
for (int i = 0; i < commandsWithoutParams.Count; i++)
|
|
{
|
|
ScriptCommand cmd = commandsWithoutParams[i];
|
|
uint currentParamsOffset = cmd.ParamsOffset;
|
|
uint nextParamsOffset;
|
|
|
|
if (i < commandsWithoutParams.Count - 1)
|
|
{
|
|
nextParamsOffset = commandsWithoutParams[i + 1].ParamsOffset;
|
|
}
|
|
else
|
|
{
|
|
nextParamsOffset = textArrayOffset;
|
|
}
|
|
|
|
|
|
int paramsLengthInBytes = (int)(nextParamsOffset - currentParamsOffset);
|
|
cmd.Params = ReadParamValues(reader, currentParamsOffset, paramsLengthInBytes);
|
|
script.Commands.Add(cmd);
|
|
}
|
|
script.TextData = ReadBytes(reader, textArrayOffset, endOfFileOffset);
|
|
}
|
|
return script;
|
|
}
|
|
|
|
private List<ScriptCommand> ParseCmdListWithoutParams(BinaryReader reader, uint paramsListOffset)
|
|
{
|
|
var commands = new List<ScriptCommand>();
|
|
while (reader.BaseStream.Position < paramsListOffset)
|
|
{
|
|
ScriptCommand cmd = new ScriptCommand
|
|
{
|
|
OpCode = reader.ReadUInt32() & 0xFFFF, //Cleaning to 16-bit command (May be some trash or what ???),
|
|
ParamsOffset = reader.ReadUInt32()
|
|
};
|
|
commands.Add(cmd);
|
|
}
|
|
return commands;
|
|
}
|
|
|
|
private int[] ReadParamValues(BinaryReader reader, uint startOffset, int lengthInBytes)
|
|
{
|
|
if (lengthInBytes <= 0) return new int[0];
|
|
if (lengthInBytes % 4 != 0)
|
|
{
|
|
Debug.LogWarning($"Parameter block length ({lengthInBytes}) is not a multiple of 4. Truncating.");
|
|
lengthInBytes = (lengthInBytes / 4) * 4; // Îêðóãëÿåì âíèç äî áëèæàéøåãî, êðàòíîãî 4
|
|
}
|
|
|
|
int paramCount = lengthInBytes / 4;
|
|
int[] parameters = new int[paramCount];
|
|
|
|
reader.BaseStream.Position = startOffset;
|
|
for (int i = 0; i < paramCount; i++)
|
|
{
|
|
parameters[i] = reader.ReadInt32();
|
|
}
|
|
|
|
return parameters;
|
|
}
|
|
|
|
private byte[] ReadBytes(BinaryReader reader, uint startAddr, uint endAddr)
|
|
{
|
|
if (startAddr >= endAddr)
|
|
return new byte[0];
|
|
|
|
int length = (int)(endAddr - startAddr);
|
|
reader.BaseStream.Position = startAddr;
|
|
return reader.ReadBytes(length);
|
|
}
|
|
|
|
private Dictionary<ushort, Subroutine> ParseSubTable(BinaryReader reader, uint subCount, long baseOffset)
|
|
{
|
|
var table = new Dictionary<ushort, Subroutine>();
|
|
|
|
for (ushort id = 0; id < subCount; id++)
|
|
{
|
|
long entryOffset = baseOffset + (id * 72); // 72 bytes on each subroutine header
|
|
reader.BaseStream.Position = entryOffset;
|
|
|
|
Subroutine sub = new Subroutine();
|
|
sub.Id = id;
|
|
|
|
// ×èòàåì èìÿ (null-terminated string)
|
|
List<byte> nameBytes = new List<byte>();
|
|
byte b;
|
|
while ((b = reader.ReadByte()) != 0)
|
|
{
|
|
nameBytes.Add(b);
|
|
}
|
|
sub.Name = System.Text.Encoding.UTF8.GetString(nameBytes.ToArray());
|
|
|
|
reader.BaseStream.Position = entryOffset + 64; //address always +64 byte offset from structure start
|
|
sub.Address = reader.ReadUInt32();
|
|
|
|
table.Add(id, sub);
|
|
}
|
|
return table;
|
|
}
|
|
}
|
|
|
|
public class Script
|
|
{
|
|
public Dictionary<ushort, Subroutine> SubroutineTable { get; set; }
|
|
public List<ScriptCommand> Commands { get; set; }
|
|
public byte[] TextData { get; set; } // textBlob
|
|
}
|
|
|
|
public class Subroutine
|
|
{
|
|
public ushort Id { get; set; }
|
|
public string Name { get; set; }
|
|
public uint Address { get; set; } // Àäðåñ ïåðâîé êîìàíäû ïîäïðîãðàììû â ñïèñêå Commands
|
|
}
|
|
|
|
public class ScriptCommand
|
|
{
|
|
public uint OpCode { get; set; }
|
|
public ushort executedStatus { get; set; } //For original engine compability (0 - not executed, 3 - parsed, may be other statuses - need to reverse)
|
|
public uint ParamsOffset { get; set; }
|
|
public int[] Params { get; set; } // command params array
|
|
} |