Files
P2SinNext/Assets/Code/ScriptLoader.cs
2025-10-19 19:04:50 +05:00

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
}