269 lines
8.2 KiB
C#
269 lines
8.2 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 M12ControlCode : IControlCode, IComparable, IComparable<M12ControlCode>
|
|
{
|
|
public static IEnumerable<IControlCode> Codes { get; private set; }
|
|
|
|
public byte Identifier { get; set; }
|
|
public string Description { get; set; }
|
|
public bool IsEnd { get; set; }
|
|
public int Length { get; set; }
|
|
public bool IsVariableLength { get; set; }
|
|
public int ReferenceOffset { get; set; }
|
|
public bool HasReferences { get; set; }
|
|
public bool AbsoluteAddressing { get; set; }
|
|
public bool IsCompressedString { get { return false; } }
|
|
public bool SuppressNextEnd { get; set; }
|
|
|
|
static M12ControlCode()
|
|
{
|
|
Codes = JsonConvert.DeserializeObject<List<M12ControlCode>>(
|
|
Asset.ReadAllText("m12-codelist.json"));
|
|
}
|
|
|
|
public bool IsMatch(byte[] rom, int address)
|
|
{
|
|
return rom[address] == Identifier &&
|
|
rom[address + 1] == 0xFF;
|
|
}
|
|
|
|
public bool IsMatch(string[] codeStrings)
|
|
{
|
|
if (codeStrings == null || codeStrings.Length < 2)
|
|
return false;
|
|
|
|
byte value1 = Convert.ToByte(codeStrings[0], 16);
|
|
byte value2 = Convert.ToByte(codeStrings[1], 16);
|
|
|
|
return value1 == Identifier && value2 == 0xFF;
|
|
}
|
|
|
|
// Assumes the code has already been matched
|
|
public bool IsValid(string[] codeStrings)
|
|
{
|
|
if (codeStrings == null || codeStrings.Length < 2)
|
|
return false;
|
|
|
|
if (!HasReferences)
|
|
{
|
|
if (codeStrings.Length != Length)
|
|
return false;
|
|
|
|
// Check that each codestring is a byte
|
|
for (int i = 2; 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 3 code strings
|
|
if (codeStrings.Length < 3)
|
|
return false;
|
|
|
|
// Check bytes before references
|
|
for (int i = 2; i < ReferenceOffset; i++)
|
|
{
|
|
if (!Compiler.IsHexByte(codeStrings[i]))
|
|
return false;
|
|
}
|
|
|
|
// Check references
|
|
int numReferences;
|
|
if (!IsVariableLength)
|
|
{
|
|
numReferences = 1;
|
|
}
|
|
else
|
|
{
|
|
byte count;
|
|
|
|
if (!byte.TryParse(codeStrings[2], System.Globalization.NumberStyles.HexNumber,
|
|
null, out count))
|
|
return false;
|
|
|
|
numReferences = count;
|
|
}
|
|
|
|
for (int i = 0; i < numReferences; i++)
|
|
{
|
|
string reference = codeStrings[i + 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; 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
|
|
{
|
|
byte count = rom[address + 2];
|
|
return (count * 4) + 3;
|
|
}
|
|
}
|
|
|
|
private int GetReference(byte[] rom, int referenceAddress)
|
|
{
|
|
if (AbsoluteAddressing)
|
|
return rom.ReadGbaPointer(referenceAddress);
|
|
else
|
|
return rom.ReadInt(referenceAddress) + referenceAddress;
|
|
}
|
|
|
|
public IList<int> GetReferences(byte[] rom, int address)
|
|
{
|
|
if (!HasReferences)
|
|
return null;
|
|
|
|
var refs = new List<int>();
|
|
|
|
if (!IsVariableLength)
|
|
{
|
|
refs.Add(GetReference(rom, address + ReferenceOffset));
|
|
}
|
|
else
|
|
{
|
|
byte count = rom[address + 2];
|
|
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>();
|
|
|
|
codeStrings.Add(new CodeByte(Identifier));
|
|
codeStrings.Add(new CodeByte(0xFF));
|
|
|
|
int length = ComputeLength(rom, address);
|
|
|
|
if (!HasReferences)
|
|
{
|
|
// Direct copy
|
|
for (int i = 2; 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 = 2; 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];
|
|
|
|
if (!AbsoluteAddressing)
|
|
{
|
|
pointer -= referenceAddress;
|
|
}
|
|
else
|
|
{
|
|
pointer |= 0x8000000;
|
|
}
|
|
|
|
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)
|
|
{
|
|
throw new InvalidOperationException("Code is not a compressed string");
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return String.Format("[{0:X2} FF]: {1}", Identifier, Description);
|
|
}
|
|
|
|
public int CompareTo(M12ControlCode other)
|
|
{
|
|
return Identifier.CompareTo(other.Identifier);
|
|
}
|
|
|
|
public int CompareTo(object obj)
|
|
{
|
|
var other = obj as M12ControlCode;
|
|
if (other == null)
|
|
throw new Exception("Cannot compare!");
|
|
|
|
return CompareTo(other);
|
|
}
|
|
}
|
|
}
|