Mother2GbaTranslation/tools/ScriptTool/EbControlCode.cs

324 lines
9.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Newtonsoft.Json;
namespace ScriptTool
{
public class EbControlCode : IControlCode, IComparable, IComparable<EbControlCode>
{
public static IEnumerable<IControlCode> Codes { get; private set; }
private static string[][] compressedStrings;
public IList<byte> Identifier { get; set; }
public string Description { get; set; }
public bool IsEnd { get; set; }
public bool IsCompressedString { get; set; }
public int Length { get; set; }
public bool IsVariableLength { get; set; }
public int ReferenceOffset { get; set; }
public int CountOffset { get; set; }
public bool HasReferences { get; set; }
public bool AbsoluteAddressing { get { return true; } }
public bool SuppressNextEnd { get; set; }
static EbControlCode()
{
// Load compressed strings
compressedStrings = new string[3][];
string[] stringsFromFile = Asset.ReadAllLines("eb-compressed-strings.txt");
compressedStrings[0] = stringsFromFile.Take(0x100).ToArray();
compressedStrings[1] = stringsFromFile.Skip(0x100).Take(0x100).ToArray();
compressedStrings[2] = stringsFromFile.Skip(0x200).Take(0x100).ToArray();
// Load codes
Codes = JsonConvert.DeserializeObject<List<EbControlCode>>(
Asset.ReadAllText("eb-codelist.json"));
}
public bool IsMatch(byte[] rom, int address)
{
for (int i = 0; i < Identifier.Count; i++)
if (rom[address + i] != Identifier[i])
return false;
return true;
}
public bool IsMatch(string[] codeStrings)
{
if (codeStrings == null || codeStrings.Length < Identifier.Count)
return false;
for (int i = 0; i < Identifier.Count; i++)
if (Convert.ToByte(codeStrings[i], 16) != Identifier[i])
return false;
return true;
}
public bool IsValid(string[] codeStrings)
{
if (codeStrings == null || codeStrings.Length < 1)
return false;
if (!HasReferences)
{
if (codeStrings.Length != Length)
return false;
// Check that each codestring is a byte
for (int i = Identifier.Count; i < codeStrings.Length; i++)
{
if (!Compiler.IsHexByte(codeStrings[i]))
return false;
}
}
else
{
// If there's at least one reference, then there must be at least 5 code strings
if (codeStrings.Length < 5)
return false;
// Check bytes before references
for (int i = Identifier.Count; i < ReferenceOffset; i++)
{
if (!Compiler.IsHexByte(codeStrings[i]))
return false;
}
// Check references
int numReferences;
if (!IsVariableLength)
{
numReferences = (Length - ReferenceOffset) / 4;
}
else
{
byte count;
if (!byte.TryParse(codeStrings[CountOffset], System.Globalization.NumberStyles.HexNumber,
null, out count))
return false;
numReferences = count;
}
for (int i = 0; i < numReferences; i++)
{
string reference = codeStrings[(i * 4) + ReferenceOffset];
if (reference.Length < 3)
return false;
if (!(reference[0] == '_' && reference[reference.Length - 1] == '_'))
return false;
}
// Check bytes after references
for (int i = ReferenceOffset + (numReferences * 4); i < codeStrings.Length; i++)
{
if (!Compiler.IsHexByte(codeStrings[i]))
return false;
}
}
return true;
}
public int ComputeLength(byte[] rom, int address)
{
if (!IsVariableLength)
{
return Length;
}
else
{
int count = rom[address + CountOffset];
return (count * 4) + 1 + Identifier.Count;
}
}
private int GetReference(byte[] rom, int referenceAddress)
{
return rom.ReadSnesPointer(referenceAddress);
}
public IList<int> GetReferences(byte[] rom, int address)
{
if (!HasReferences)
return null;
var refs = new List<int>();
if (!IsVariableLength)
{
for (int i = ReferenceOffset; i < Length; i += 4)
refs.Add(GetReference(rom, address + i));
}
else
{
int count = rom[address + CountOffset];
for (int i = 0; i < count; i++)
{
refs.Add(GetReference(rom, address + ReferenceOffset + (i * 4)));
}
}
return refs;
}
public IList<CodeString> GetCodeStrings(byte[] rom, int address)
{
var codeStrings = new List<CodeString>();
foreach (var b in Identifier)
codeStrings.Add(new CodeByte(b));
int length = ComputeLength(rom, address);
if (!HasReferences)
{
// Direct copy
for (int i = Identifier.Count; i < length; i++)
{
codeStrings.Add(new CodeByte(rom[address + i]));
}
}
else
{
// Get references
var references = GetReferences(rom, address);
// Copy bytes before reference
for (int i = Identifier.Count; i < ReferenceOffset; i++)
{
codeStrings.Add(new CodeByte(rom[address + i]));
}
// Copy references
foreach (var reference in references)
{
codeStrings.Add(new CodeReference(reference));
}
// Copy bytes after reference
for (int i = ReferenceOffset + (references.Count * 4); i < length; i++)
{
codeStrings.Add(new CodeByte(rom[address + i]));
}
}
return codeStrings;
}
public void Compile(string[] codeStrings, IList<byte> buffer, ref int referenceAddress, IDictionary<string, int> addressMap)
{
foreach (var codeString in codeStrings)
{
if (codeString[0] == '_')
{
if (codeString[codeString.Length - 1] != '_')
throw new Exception("Reference has no closing underscore");
if (codeString.Length <= 2)
throw new Exception("Reference is empty");
string label = codeString.Substring(1, codeString.Length - 2);
int pointer = addressMap[label] + 0xC00000;
if (buffer != null)
buffer.AddInt(pointer);
referenceAddress += 4;
}
else
{
byte value = Convert.ToByte(codeString, 16);
buffer.Add(value);
referenceAddress++;
}
}
}
public string GetCompressedString(byte[] rom, int address)
{
return compressedStrings[rom[address] - 0x15][rom[address + 1]];
}
public override string ToString()
{
return String.Format("[{0}]: {1}", String.Join(" ", Identifier.Select(b => b.ToString("X2")).ToArray()), Description);
}
#region JSON
public bool ShouldSerializeIsEnd()
{
return IsEnd;
}
public bool ShouldSerializeIsCompressedString()
{
return IsCompressedString;
}
public bool ShouldSerializeIsVariableLength()
{
return IsVariableLength;
}
public bool ShouldSerializeHasReferences()
{
return HasReferences;
}
public bool ShouldSerializeAbsoluteAddressing()
{
return AbsoluteAddressing;
}
public bool ShouldSerializeReferenceOffset()
{
return HasReferences;
}
public bool ShouldSerializeCountOffset()
{
return IsVariableLength;
}
public bool ShouldSerializeSuppressNextEnd()
{
return SuppressNextEnd;
}
#endregion
public int CompareTo(EbControlCode other)
{
int numToCheck = Math.Min(Identifier.Count, other.Identifier.Count);
for (int i = 0; i < numToCheck; i++)
{
if (Identifier[i] != other.Identifier[i])
{
return Identifier[i].CompareTo(other.Identifier[i]);
}
}
return 0;
}
public int CompareTo(object obj)
{
var other = obj as EbControlCode;
if (other == null)
throw new Exception("Cannot compare!");
return CompareTo(other);
}
}
}