Mother2GbaTranslation/tools/ScriptTool/Decompiler.cs

194 lines
6.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace ScriptTool
{
class Decompiler : IDecompiler
{
public IEnumerable<IControlCode> ControlCodes { get; set; }
public IDictionary<byte, string> CharLookup { get; set; }
public LabelMap LabelMap { get; set; }
public Func<byte[], int, bool> ControlCodePredicate { get; set; }
public Decompiler(IEnumerable<IControlCode> controlCodes, IDictionary<byte, string> charLookup,
Func<byte[], int, bool> controlCodePredicate)
{
ControlCodes = controlCodes;
CharLookup = charLookup;
ControlCodePredicate = controlCodePredicate;
LabelMap = new LabelMap();
}
public void ScanRange(byte[] rom, int startAddress, int endAddress)
{
if (rom == null)
throw new ArgumentNullException();
int address = startAddress;
while (address < endAddress)
{
if (ControlCodePredicate(rom, address))
{
IControlCode code = ControlCodes.FirstOrDefault(c => c.IsMatch(rom, address));
if (code == null)
throw new Exception("Control code not found");
IList<int> references = code.GetReferences(rom, address);
if (references != null)
LabelMap.AddRange(references);
address += code.ComputeLength(rom, address);
}
else
{
address++;
}
}
}
private string FilterCodeString(CodeString codeString)
{
var codeByte = codeString as CodeByte;
if (codeByte != null)
return codeByte.Value.ToString("X2");
int address = ((CodeReference)codeString).Address;
string label;
if (!LabelMap.Labels.TryGetValue(address, out label))
{
label = "????";
}
return "_" + label + "_";
}
public string DecompileRange(byte[] rom, int startAddress, int endAddress, bool newLines)
{
if (rom == null)
throw new ArgumentNullException();
var builder = new StringBuilder();
bool readUntilEnd = (endAddress == -1);
bool ended = false;
bool suppressNextEnd = false;
int address = startAddress;
while (!ended)
{
if (LabelMap.Labels.ContainsKey(address))
{
builder.Append('^');
builder.Append(LabelMap.Labels[address]);
builder.Append('^');
}
if (ControlCodePredicate(rom, address))
{
IControlCode code = ControlCodes.FirstOrDefault(c => c.IsMatch(rom, address));
if (code == null)
throw new Exception("Control code not found");
// Check if it's compressed text
if (code.IsCompressedString)
{
builder.Append(code.GetCompressedString(rom, address));
}
else
{
IList<CodeString> codeStrings = code.GetCodeStrings(rom, address);
var filtered = codeStrings.Select(cs => FilterCodeString(cs)).ToArray();
builder.Append(String.Format("[{0}]", String.Join(" ", filtered)));
if (newLines && code.IsEnd && !suppressNextEnd)
{
builder.AppendLine();
}
}
address += code.ComputeLength(rom, address);
/*if (newLines && code.IsEnd && !suppressNextEnd)
{
builder.Append("(" + address.ToString("X") + ")");
}*/
if (readUntilEnd && code.IsEnd)
ended = true;
if (code.IsEnd)
{
suppressNextEnd = false;
}
else if (code.SuppressNextEnd == true)
{
suppressNextEnd = true;
}
}
else
{
string str = GetChar(rom, address++);
builder.Append(str);
}
if (!readUntilEnd && address >= endAddress)
ended = true;
}
return builder.ToString();
}
public string DecompileString(byte[] rom, int address, bool newLines)
{
return DecompileRange(rom, address, -1, newLines);
}
public string ReadFFString(byte[] rom, int address)
{
var builder = new StringBuilder();
bool ended = false;
while (!ended)
{
if (rom[address] == 0 && rom[address + 1] == 0xFF)
{
builder.Append("[00 FF]");
ended = true;
address += 2;
}
else if (rom[address] != 0xFF)
{
builder.Append(GetChar(rom, address++));
}
else
{
address++;
}
}
return builder.ToString();
}
public string GetChar(byte[] rom, int address)
{
byte value = rom[address];
if (!CharLookup.ContainsKey(value))
{
// Invalid
throw new Exception("Invalid character 0x" + value.ToString("X2") + " at address 0x" + address.ToString("X7"));
}
return CharLookup[value];
}
}
}