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 ControlCodes { get; set; } public IDictionary CharLookup { get; set; } public LabelMap LabelMap { get; set; } public Func ControlCodePredicate { get; set; } public Decompiler(IEnumerable controlCodes, IDictionary charLookup, Func 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 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 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]; } } }