diff --git a/ScriptTool/ScriptTool.sln b/ScriptTool/ScriptTool.sln new file mode 100644 index 0000000..783d62f --- /dev/null +++ b/ScriptTool/ScriptTool.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptTool", "ScriptTool\ScriptTool.csproj", "{CA5C95AE-2CD2-47E3-B5AE-FE6136808AC4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CA5C95AE-2CD2-47E3-B5AE-FE6136808AC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA5C95AE-2CD2-47E3-B5AE-FE6136808AC4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA5C95AE-2CD2-47E3-B5AE-FE6136808AC4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CA5C95AE-2CD2-47E3-B5AE-FE6136808AC4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ScriptTool/ScriptTool/App.config b/ScriptTool/ScriptTool/App.config new file mode 100644 index 0000000..9c05822 --- /dev/null +++ b/ScriptTool/ScriptTool/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/ScriptTool/ScriptTool/CommandOptions.cs b/ScriptTool/ScriptTool/CommandOptions.cs new file mode 100644 index 0000000..21cecda --- /dev/null +++ b/ScriptTool/ScriptTool/CommandOptions.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ScriptTool +{ + class CommandOptions + { + public string WorkingDirectory { get; set; } + public CommandType Command { get; set; } + public bool DoMiscText { get; set; } + public bool DoMainText { get; set; } + public string EbRom { get; set; } + public string M12Rom { get; set; } + } + + enum CommandType + { + Compile, + Decompile + } +} diff --git a/ScriptTool/ScriptTool/ControlCode.cs b/ScriptTool/ScriptTool/ControlCode.cs new file mode 100644 index 0000000..febd911 --- /dev/null +++ b/ScriptTool/ScriptTool/ControlCode.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; + +namespace ScriptTool +{ + class ControlCode + { + public IList Identifier { get; private set; } + public bool End { get; set; } + public bool Multiple { get; set; } + public int Length { get; set; } + public string Description { get; set; } + const string HexChars = "0123456789ABCDEFabcdef"; + + public ControlCode() + { + Identifier = new List(); + } + + public bool Match(byte[] rom, int address) + { + for (int i = 0; i < Identifier.Count; i++) + if (rom[address + i] != Identifier[i]) + return false; + return true; + } + + public bool BeginsWith(params byte[] bytes) + { + if (bytes.Length > Identifier.Count) + return false; + + var first = Identifier.Take(bytes.Length); + if (!first.SequenceEqual(bytes)) + return false; + + return true; + } + + public override string ToString() + { + return Description; + } + + public static IList LoadEbControlCodes(string path) + { + var codeList = new List(); + string[] lines = File.ReadAllLines(path); + + foreach (var line in lines) + { + var code = new ControlCode(); + string def = line.Substring(0, line.IndexOf(',')); + string desc = line.Substring(line.IndexOf(',') + 1); + code.Description = desc.Substring(1, desc.Length - 3); + + while (true) + { + if (def.StartsWith("!")) + { + code.End = true; + def = def.Substring(1); + continue; + } + + if (def.StartsWith("*")) + { + code.Multiple = true; + def = def.Substring(1); + continue; + } + + break; + } + + string[] defs = def.Split(' '); + for (int i = 0; i < defs.Length; i++) + { + if (!HexChars.Contains(defs[i][0])) + break; + + code.Identifier.Add(byte.Parse(defs[i], System.Globalization.NumberStyles.HexNumber)); + } + + if (code.Multiple) + code.Length = -1; + else + code.Length = defs.Length; + + codeList.Add(code); + } + + return codeList; + } + + public static IList LoadM12ControlCodes(string path) + { + var codeList = new List(); + string[] lines = File.ReadAllLines(path); + + foreach (var line in lines) + { + var code = new ControlCode(); + string def = line.Substring(0, line.IndexOf(',')); + string desc = line.Substring(line.IndexOf(',') + 1); + code.Description = desc; + + while (true) + { + if (def.StartsWith("!")) + { + code.End = true; + def = def.Substring(1); + continue; + } + + if (def.StartsWith("*")) + { + code.Multiple = true; + def = def.Substring(1); + continue; + } + + break; + } + + string[] defs = def.Split(' '); + code.Identifier.Add(byte.Parse(defs[0], System.Globalization.NumberStyles.HexNumber)); + code.Identifier.Add(0xFF); + + if (code.Multiple) + code.Length = -1; + else + code.Length = defs.Length; + + codeList.Add(code); + } + + return codeList; + } + } +} diff --git a/ScriptTool/ScriptTool/DecompileContext.cs b/ScriptTool/ScriptTool/DecompileContext.cs new file mode 100644 index 0000000..46bf7f2 --- /dev/null +++ b/ScriptTool/ScriptTool/DecompileContext.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ScriptTool +{ + class DecompileContext + { + public LabelMap LabelMap { get; private set; } + public IList Strings { get; private set; } + + public DecompileContext() + { + LabelMap = new LabelMap(); + Strings = new List(); + } + } +} diff --git a/ScriptTool/ScriptTool/EbTextDecompiler.cs b/ScriptTool/ScriptTool/EbTextDecompiler.cs new file mode 100644 index 0000000..fc74972 --- /dev/null +++ b/ScriptTool/ScriptTool/EbTextDecompiler.cs @@ -0,0 +1,324 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using Newtonsoft.Json; + +namespace ScriptTool +{ + class EbTextDecompiler + { + private IList _controlCodes; + public IList ControlCodes + { + get { return _controlCodes; } + set + { + // Grab the jump table codes + foreach (var code in value) + { + if (code.BeginsWith(9)) + jumpTableReturn = code; + + if (code.BeginsWith(0x1f, 0xc0)) + jumpTableNoReturn = code; + } + + _controlCodes = value; + } + } + + private ControlCode jumpTableReturn; + private ControlCode jumpTableNoReturn; + + private const string charMapEb = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZØØØØØ`abcdefghijklmnopqrstuvwxyz{|}~\\"; + + private static string[][] compressedStringsEb; + private static IList textRanges = new List(); + + static EbTextDecompiler() + { + // Load compressed strings + compressedStringsEb = new string[3][]; + string[] stringsFromFile = File.ReadAllLines(@"eb-compressed-strings.txt"); + compressedStringsEb[0] = stringsFromFile.Take(0x100).ToArray(); + compressedStringsEb[1] = stringsFromFile.Skip(0x100).Take(0x100).ToArray(); + compressedStringsEb[2] = stringsFromFile.Skip(0x200).Take(0x100).ToArray(); + + // Load text ranges + textRanges.Add(new int[] { 0x50000, 0x5FFEC }); + textRanges.Add(new int[] { 0x60000, 0x6FFE3 }); + textRanges.Add(new int[] { 0x70000, 0x7FF40 }); + textRanges.Add(new int[] { 0x80000, 0x8BC2D }); + textRanges.Add(new int[] { 0x8D9ED, 0x8FFF3 }); + textRanges.Add(new int[] { 0x90000, 0x9FF2F }); + textRanges.Add(new int[] { 0x2F4E20, 0x2FA460 }); + } + + public void Decompile(byte[] rom, int[] addresses, DecompileContext context) + { + if (ControlCodes == null) + throw new Exception("Codelist is null"); + + // First pass -- define labels + foreach (var address in addresses) + context.LabelMap.Append(address); + + foreach(var range in textRanges) + ScanAt(rom, range[0], range[1], context, ScanMode.FirstPass); + + // Second pass -- decompile the strings + foreach (var range in textRanges) + ScanAt(rom, range[0], range[1], context, ScanMode.SecondPass); + } + + private void ScanAt(byte[] rom, int startAddress, int endAddress, DecompileContext context, ScanMode mode, bool newLines = true) + { + bool ended = false; + bool foundMatch = false; + bool prev1902 = false; + + var sb = new StringBuilder(); + + int address = startAddress; + + if (address == 0) + { + throw new Exception("Null pointer"); + } + + if (mode == ScanMode.FirstPass) + { + context.LabelMap.Append(address); + } + + while (!ended) + { + // Check for label definition + if (mode == ScanMode.SecondPass && context.LabelMap.Labels.ContainsKey(address)) + { + sb.Append("^" + context.LabelMap[address] + "^"); + } + + // Check for control codes + + // No codes begin with a value above 0x1F, so check for that first + if (rom[address] < 0x20) + { + // Loop through each control code until we find a match + foundMatch = false; + foreach (var code in ControlCodes) + { + if (code.Match(rom, address)) + { + foundMatch = true; + + if (mode == ScanMode.SecondPass && !IsCompressedCode(code)) + { + // Output the start of the code block + sb.Append('['); + + // Output the identifier bytes + sb.Append(String.Join(" ", code.Identifier.Select(b => b.ToString("X2")).ToArray())); + } + + // Skip the identifier bytes + address += code.Identifier.Count; + + // Found a match -- check if it's variable-length + if (code.Multiple) + { + if (code.BeginsWith(9) || + code.BeginsWith(0x1F, 0xC0)) + { + int count = rom[address++]; + + if (mode == ScanMode.SecondPass) + { + sb.Append(' '); + sb.Append(count.ToString("X2")); + } + + for (int i = 0; i < count; i++) + { + int jump = rom.ReadSnesPointer(address); + address += 4; + + context.LabelMap.Append(jump); + + if (mode == ScanMode.SecondPass) + { + sb.Append(" _"); + sb.Append(context.LabelMap[jump]); + sb.Append('_'); + } + } + } + } + else + { + // Check if it references any other addresses -- scan those too + if (code.BeginsWith(8) || + code.BeginsWith(0xA) || + code.BeginsWith(0x1B, 0x02) || + code.BeginsWith(0x1B, 0x03) || + code.BeginsWith(0x1F, 0x63)) + { + // Single address at next byte + int jump = rom.ReadSnesPointer(address); + + context.LabelMap.Append(jump); + + if (mode == ScanMode.SecondPass) + { + sb.Append(" _"); + sb.Append(context.LabelMap[jump]); + sb.Append('_'); + } + } + + else if (code.BeginsWith(0x1F, 0x66) || + code.BeginsWith(6)) + { + // Skip two bytes; single address afterwards + int jump = rom.ReadSnesPointer(address + 2); + + context.LabelMap.Append(jump); + + if (mode == ScanMode.SecondPass) + { + sb.Append(' '); + sb.Append(rom[address].ToString("X2")); + sb.Append(' '); + sb.Append(rom[address + 1].ToString("X2")); + + sb.Append(" _"); + sb.Append(context.LabelMap[jump]); + sb.Append('_'); + } + } + + else if ( + code.BeginsWith(0x1F, 0x18) || + code.BeginsWith(0x1F, 0x19)) + { + // Check + } + + else if (mode == ScanMode.SecondPass && + (code.BeginsWith(0x15) || + code.BeginsWith(0x16) || + code.BeginsWith(0x17))) + { + // Check for compressed codes + int bank = code.Identifier[0] - 0x15; + int index = rom[address]; + sb.Append(compressedStringsEb[bank][index]); + } + + else + { + // Regular control code -- output the rest of the bytes + if (mode == ScanMode.SecondPass) + { + for (int i = 0; i < (code.Length - code.Identifier.Count); i++) + { + sb.Append(' '); + sb.Append(rom[address + i].ToString("X2")); + } + } + } + + // Skip the rest of the bytes + address += code.Length - code.Identifier.Count; + } + + if (mode == ScanMode.SecondPass && !IsCompressedCode(code)) + { + // Output the end of the code block + sb.Append(']'); + } + + // End the block if necessary + if (address >= endAddress) + { + ended = true; + } + + // Insert a newline after each end code for readibility + if (mode == ScanMode.SecondPass && code.End && !prev1902) + { + sb.AppendLine(); + /*sb.Append('('); + sb.Append(address.ToString("X")); + sb.Append(')');*/ + } + + // Check if we're in a menu string + if (code.BeginsWith(0x19, 0x02)) + prev1902 = true; + else + prev1902 = false; + + break; + } + } + + if (!foundMatch) + { + // Bad! + throw new Exception("Found unknown control code"); + } + } + else + { + // It's not a control code -- just skip it + if (mode == ScanMode.SecondPass) + sb.Append(CharLookup(rom[address])); + + address++; + } + } + + if (mode == ScanMode.SecondPass) + context.Strings.Add(sb.ToString()); + + return; + } + + private string CharLookup(byte value) + { + if (value >= 0x8B && value <= 0x8F) + { + // Greek letters -- output hex literals instead + return "[" + value.ToString("X2") + "]"; + } + else if (value == 0xAF) + { + // Musical note -- output \ instead of the garbage char that normally would get outputted + return "\\"; + } + else + { + return charMapEb[value - 0x50].ToString(); + } + } + + private bool IsCompressedCode(ControlCode code) + { + if (code.Identifier[0] == 0x15 || code.Identifier[0] == 0x16 || + code.Identifier[0] == 0x17) + return true; + + return false; + } + + private enum ScanMode + { + FirstPass, + SecondPass + } + } +} diff --git a/ScriptTool/ScriptTool/EbTextTables.cs b/ScriptTool/ScriptTool/EbTextTables.cs new file mode 100644 index 0000000..13a0e72 --- /dev/null +++ b/ScriptTool/ScriptTool/EbTextTables.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ScriptTool +{ + class EbTextTables + { + public static MainStringRef[] ReadTptRefs(byte[] rom) + { + var refs = new List(); + + int address = 0xF8985; + int entries = 1584; + + for (int i = 0; i < entries; i++) + { + int firstPointer = rom.ReadSnesPointer(address + 9); + if (firstPointer != 0) + refs.Add(new MainStringRef { Index = i, PointerLocation = address + 9, OldPointer = firstPointer }); + + byte type = rom[address]; + if (type != 2) + { + int secondPointer = rom.ReadSnesPointer(address + 13); + if (secondPointer != 0) + refs.Add(new MainStringRef { Index = i, PointerLocation = address + 13, OldPointer = secondPointer }); + } + + address += 17; + } + + return refs.ToArray(); + } + + public static MainStringRef[] ReadBattleActionRefs(byte[] rom) + { + var refs = new List(); + int address = 0x157B68; + for (int i = 0; i < 318; i++) + { + int pointer = rom.ReadSnesPointer(address + 4); + if (pointer != 0) + refs.Add(new MainStringRef { Index = i, PointerLocation = address + 4, OldPointer = pointer }); + address += 12; + } + return refs.ToArray(); + } + + public static MainStringRef[] ReadPrayerRefs(byte[] rom) + { + var refs = new List(); + int address = 0x4A309; + for (int i = 0; i < 10; i++) + { + int pointer = rom.ReadSnesPointer(address); + if (pointer != 0) + refs.Add(new MainStringRef { Index = i, PointerLocation = address, OldPointer = pointer }); + address += 4; + } + return refs.ToArray(); + } + + public static MainStringRef[] ReadItemHelpRefs(byte[] rom) + { + var refs = new List(); + int address = 0x155000; + for (int i = 0; i < 254; i++) + { + int pointer = rom.ReadSnesPointer(address + 0x23); + if (pointer != 0) + refs.Add(new MainStringRef { Index = i, PointerLocation = address + 0x23, OldPointer = pointer }); + address += 39; + } + return refs.ToArray(); + } + + public static MainStringRef[] ReadPsiHelpRefs(byte[] rom) + { + var refs = new List(); + int address = 0x158A50; + for (int i = 0; i < 53; i++) + { + int pointer = rom.ReadSnesPointer(address + 11); + if (pointer != 0) + refs.Add(new MainStringRef { Index = i, PointerLocation = address + 11, OldPointer = pointer }); + address += 15; + } + return refs.ToArray(); + } + + public static MainStringRef[] ReadPhoneRefs(byte[] rom) + { + var refs = new List(); + int address = 0x157AAE; + for (int i = 0; i < 6; i++) + { + int pointer = rom.ReadSnesPointer(address + 0x1B); + if (pointer != 0) + refs.Add(new MainStringRef { Index = i, PointerLocation = address + 0x1B, OldPointer = pointer }); + address += 31; + } + return refs.ToArray(); + } + + public static MainStringRef[] ReadEnemyTextRefs(byte[] rom) + { + var refs = new List(); + int address = 0x159589; + for (int i = 0; i < 231; i++) + { + int pointer = rom.ReadSnesPointer(address + 0x2D); + if (pointer != 0) + refs.Add(new MainStringRef { Index = i, PointerLocation = address + 0x2D, OldPointer = pointer }); + + pointer = rom.ReadSnesPointer(address + 0x31); + if (pointer != 0) + refs.Add(new MainStringRef { Index = i, PointerLocation = address + 0x31, OldPointer = pointer }); + + address += 94; + } + return refs.ToArray(); + } + } +} diff --git a/ScriptTool/ScriptTool/Extensions.cs b/ScriptTool/ScriptTool/Extensions.cs new file mode 100644 index 0000000..e228831 --- /dev/null +++ b/ScriptTool/ScriptTool/Extensions.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ScriptTool +{ + public static class Extensions + { + public static int ReadInt(this byte[] rom, int address) + { + int value = rom[address] | + (rom[address + 1] << 8) | + (rom[address + 2] << 16) | + (rom[address + 3] << 24); + return value; + } + + public static int ReadSnesPointer(this byte[] rom, int address) + { + int offset = rom.ReadInt(address); + if (offset == 0) return 0; + return offset - 0xC00000; + } + + public static int ReadGbaPointer(this byte[] rom, int address) + { + int offset = rom.ReadInt(address); + if (offset == 0) return 0; + return offset & 0x1FFFFFF; + } + } +} diff --git a/ScriptTool/ScriptTool/FixedStringCollection.cs b/ScriptTool/ScriptTool/FixedStringCollection.cs new file mode 100644 index 0000000..9d05591 --- /dev/null +++ b/ScriptTool/ScriptTool/FixedStringCollection.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace ScriptTool +{ + class FixedStringCollection + { + [JsonConverter(typeof(JsonHexConverter))] + public int StringsLocation { get; set; } + + public int NumEntries { get; set; } + public int EntryLength { get; set; } + public IList StringRefs { get; set; } + } +} diff --git a/ScriptTool/ScriptTool/FixedStringRef.cs b/ScriptTool/ScriptTool/FixedStringRef.cs new file mode 100644 index 0000000..8d19873 --- /dev/null +++ b/ScriptTool/ScriptTool/FixedStringRef.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace ScriptTool +{ + public class FixedStringRef + { + [JsonConverter(typeof(JsonHexConverter))] + public int Index { get; set; } + + [JsonConverter(typeof(JsonHexConverter))] + public int OldPointer { get; set; } + + public string Old { get; set; } + public string New { get; set; } + } +} diff --git a/ScriptTool/ScriptTool/JsonHexConverter.cs b/ScriptTool/ScriptTool/JsonHexConverter.cs new file mode 100644 index 0000000..2a01031 --- /dev/null +++ b/ScriptTool/ScriptTool/JsonHexConverter.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace ScriptTool +{ + public class JsonHexConverter : JsonConverter + { + private static readonly Type[] allowedTypes = { + typeof(int), + typeof(uint), + typeof(byte), + typeof(sbyte), + typeof(short), + typeof(ushort), + typeof(long), + typeof(ulong) + }; + + public override bool CanConvert(Type objectType) + { + return allowedTypes.Contains(objectType); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Integer) + { + var hex = serializer.Deserialize(reader, objectType); + return hex; + } + throw new Exception("Unexpected token"); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteRawValue(String.Format("0x{0:X}", value)); + } + } +} diff --git a/ScriptTool/ScriptTool/LabelMap.cs b/ScriptTool/ScriptTool/LabelMap.cs new file mode 100644 index 0000000..1243b1f --- /dev/null +++ b/ScriptTool/ScriptTool/LabelMap.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ScriptTool +{ + class LabelMap + { + public IDictionary Labels { get; private set; } + private int counter = 0; + + public LabelMap() + { + Labels = new Dictionary(); + } + + public string this[int address] + { + get + { + return Labels[address]; + } + set + { + Labels[address] = value; + } + } + + public void Append(int address) + { + if (!Labels.ContainsKey(address)) + { + string newLabel = String.Concat("L", counter.ToString()); + Labels.Add(address, newLabel); + counter++; + } + } + + public void Reset() + { + counter = 0; + } + + public void Clear() + { + Reset(); + Labels.Clear(); + } + } +} diff --git a/ScriptTool/ScriptTool/M12TextDecompiler.cs b/ScriptTool/ScriptTool/M12TextDecompiler.cs new file mode 100644 index 0000000..efc5dfd --- /dev/null +++ b/ScriptTool/ScriptTool/M12TextDecompiler.cs @@ -0,0 +1,306 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using Newtonsoft.Json; + +namespace ScriptTool +{ + class M12TextDecompiler + { + public IList ControlCodes { get; set; } + private static string[] charMap; + private static IList textRanges = new List(); + private static DecompileContext staticContext = new DecompileContext(); + + static M12TextDecompiler() + { + // Load strings + charMap = File.ReadAllLines("m12-text-table.txt"); + + // Load text ranges + textRanges.Add(new int[] { 0x3697F, 0x8C4B0 }); + } + + public void Decompile(byte[] rom, int[] addresses, DecompileContext context) + { + if (ControlCodes == null) + throw new Exception("Codelist is null"); + + // First pass -- define labels + foreach (var address in addresses) + context.LabelMap.Append(address); + + foreach(var range in textRanges) + ScanAt(rom, range[0], range[1], context, ScanMode.FirstPass, false, false, false); + + // Second pass -- decompile the strings + foreach (var range in textRanges) + ScanAt(rom, range[0], range[1], context, ScanMode.SecondPass, false, false, true); + } + + public string ReadString(byte[] rom, int address, int endAddress, bool basicMode) + { + return ScanAt(rom, address, endAddress, null, ScanMode.ReadOnce, true, basicMode, false); + } + + private string ScanAt(byte[] rom, int startAddress, int endAddress, DecompileContext context, + ScanMode mode, bool stopOnEnd, bool basicMode, bool newLines) + { + bool ended = false; + bool foundMatch = false; + var sb = new StringBuilder(); + + int address = startAddress; + + if (address == 0) + { + throw new Exception("Null pointer"); + } + + if (mode == ScanMode.FirstPass) + { + context.LabelMap.Append(address); + } + + while (!ended) + { + // Check for label definition + if (mode == ScanMode.SecondPass && context.LabelMap.Labels.ContainsKey(address)) + { + sb.Append("^" + context.LabelMap[address] + "^"); + } + + // Check for control codes (unless it's in basic mode) + + // No codes begin with a value above 0x1F, so check for that first + if (rom[address + 1] == 0xFF && (!basicMode || (basicMode && (rom[address] == 0)))) + { + // Loop through each control code until we find a match + foundMatch = false; + foreach (var code in ControlCodes) + { + if (code.Match(rom, address)) + { + foundMatch = true; + + if (mode == ScanMode.SecondPass || mode == ScanMode.ReadOnce) + { + // Output the start of the code block + sb.Append('['); + + // Output the identifier bytes + sb.Append(String.Join(" ", code.Identifier.Select(b => b.ToString("X2")).ToArray())); + } + + // Skip the identifier bytes + address += code.Identifier.Count; + + // Found a match -- check if it's variable-length + if (code.Multiple) + { + if (code.BeginsWith(0x95) || + code.BeginsWith(0xBD)) + { + int count = rom[address++]; + + if (mode == ScanMode.SecondPass || mode == ScanMode.ReadOnce) + { + sb.Append(' '); + sb.Append(count.ToString("X2")); + } + + for (int i = 0; i < count; i++) + { + int jump = rom.ReadInt(address); + jump += address; + + address += 4; + + context.LabelMap.Append(jump); + + if (mode == ScanMode.SecondPass || mode == ScanMode.ReadOnce) + { + sb.Append(" _"); + sb.Append(context.LabelMap[jump]); + sb.Append('_'); + } + } + } + } + else + { + // Check if it references any other addresses -- scan those too + if (code.BeginsWith(0x04) || + code.BeginsWith(0x05) || + code.BeginsWith(0x80) || + code.BeginsWith(0x81) || + code.BeginsWith(0x82) || + code.BeginsWith(0x86)) + { + // Single relative address at next byte + int jump = rom.ReadInt(address); + jump += address; + + context.LabelMap.Append(jump); + + if (mode == ScanMode.SecondPass || mode == ScanMode.ReadOnce) + { + sb.Append(" _"); + sb.Append(context.LabelMap[jump]); + sb.Append('_'); + } + } + + else if (code.BeginsWith(0x1C)) + { + // Skip two bytes; single relative address afterwards + int jump = rom.ReadInt(address + 2); + jump += address + 2; + + context.LabelMap.Append(jump); + + if (mode == ScanMode.SecondPass || mode == ScanMode.ReadOnce) + { + sb.Append(' '); + sb.Append(rom[address].ToString("X2")); + sb.Append(' '); + sb.Append(rom[address + 1].ToString("X2")); + + sb.Append(" _"); + sb.Append(context.LabelMap[jump]); + sb.Append('_'); + } + } + + else if (code.BeginsWith(0x9D)) + { + // Skip two bytes; single absolute address afterwards + int jump = rom.ReadGbaPointer(address + 2); + + context.LabelMap.Append(jump); + + if (mode == ScanMode.SecondPass || mode == ScanMode.ReadOnce) + { + sb.Append(' '); + sb.Append(rom[address].ToString("X2")); + sb.Append(' '); + sb.Append(rom[address + 1].ToString("X2")); + + sb.Append(" _"); + sb.Append(context.LabelMap[jump]); + sb.Append('_'); + } + } + + else if (code.BeginsWith(0xA2)) + { + // Single absolute address at next byte + int jump = rom.ReadGbaPointer(address); + + context.LabelMap.Append(jump); + + if (mode == ScanMode.SecondPass || mode == ScanMode.ReadOnce) + { + sb.Append(" _"); + sb.Append(context.LabelMap[jump]); + sb.Append('_'); + } + } + + else + { + // Regular control code -- output the rest of the bytes + if (mode == ScanMode.SecondPass || mode == ScanMode.ReadOnce) + { + for (int i = 0; i < (code.Length - code.Identifier.Count); i++) + { + sb.Append(' '); + sb.Append(rom[address + i].ToString("X2")); + } + } + } + + // Skip the rest of the bytes + address += code.Length - code.Identifier.Count; + } + + if (mode == ScanMode.SecondPass || mode == ScanMode.ReadOnce) + { + // Output the end of the code block + sb.Append(']'); + } + + // End the block if necessary + if (stopOnEnd && code.End) + { + ended = true; + } + + // Insert a newline after each end code for readibility + if (newLines && (mode == ScanMode.SecondPass || mode == ScanMode.ReadOnce) && code.End) + { + sb.AppendLine(); + /*sb.Append('('); + sb.Append(address.ToString("X")); + sb.Append(')');*/ + } + + break; + } + } + + if (!foundMatch) + { + // Bad! + throw new Exception("Found unknown control code"); + } + } + else + { + // It's not a control code -- just skip it + if (mode == ScanMode.SecondPass || mode == ScanMode.ReadOnce) + { + if (!basicMode || (basicMode && (rom[address] != 0xFF))) + sb.Append(CharLookup(rom[address])); + } + + address++; + } + + if ((endAddress != -1) && (address >= endAddress)) + ended = true; + } + + if (mode == ScanMode.SecondPass) + context.Strings.Add(sb.ToString()); + + else if (mode == ScanMode.ReadOnce) + return sb.ToString(); + + return null; + } + + private string CharLookup(byte value) + { + if ((value >= 83 && value <= 95) || + (value >= 180 && value <= 191) || + value == 255) + { + // Invalid + throw new Exception("Invalid character"); + } + + return charMap[value]; + } + + private enum ScanMode + { + FirstPass, + SecondPass, + ReadOnce + } + } +} diff --git a/ScriptTool/ScriptTool/M12TextTables.cs b/ScriptTool/ScriptTool/M12TextTables.cs new file mode 100644 index 0000000..9331e98 --- /dev/null +++ b/ScriptTool/ScriptTool/M12TextTables.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ScriptTool +{ + class M12TextTables + { + private static int[] StupidIndices = + { + 0xB9, 0xBF, 0xC0, 0x164, 0x169, + 0x16D, 0x16F, 0x171, 0x1B0, 0x1B1, + 0x1B3, 0x1C1, 0x233, 0x239, 0x247, + 0x286, 0x2AD, 0x2AE, 0x2AF, 0x2E7, + 0x30A, 0x318, 0x3E1, 0x458, 0x45D, + 0x48C, 0x48D, 0x514, 0x515, 0x516, + 0x57D + }; + + public static MainStringRef[] ReadTptRefs(byte[] rom) + { + var refs = new List(); + + int address = 0x8EB14; + int entries = 1584; + + for (int i = 0; i < entries; i++) + { + if (StupidIndices.Contains(i)) + { + int firstPointer = rom.ReadGbaPointer(address + 9); + if (firstPointer != 0) + refs.Add(new MainStringRef { Index = i, PointerLocation = address + 9, OldPointer = firstPointer }); + + byte type = rom[address]; + if (type != 2) + { + int secondPointer = rom.ReadGbaPointer(address + 13); + if (secondPointer != 0) + refs.Add(new MainStringRef { Index = i, PointerLocation = address + 13, OldPointer = secondPointer }); + } + } + else + { + int firstPointer = rom.ReadGbaPointer(address + 12); + if (firstPointer != 0) + refs.Add(new MainStringRef { Index = i, PointerLocation = address + 12, OldPointer = firstPointer }); + + byte type = rom[address]; + if (type != 2) + { + int secondPointer = rom.ReadGbaPointer(address + 16); + if (secondPointer != 0) + refs.Add(new MainStringRef { Index = i, PointerLocation = address + 16, OldPointer = secondPointer }); + } + } + + address += 20; + } + + return refs.ToArray(); + } + + /*public static StringRef[] ReadBattleActionRefs(byte[] rom) + { + var refs = new List(); + int address = 0x157B68; + for (int i = 0; i < 318; i++) + { + int pointer = rom.ReadPointer(address + 4); + if (pointer != 0) + refs.Add(new StringRef { Index = i, PointerLocation = address + 4, OldPointer = pointer }); + address += 12; + } + return refs.ToArray(); + } + + public static StringRef[] ReadPrayerRefs(byte[] rom) + { + var refs = new List(); + int address = 0x4A309; + for (int i = 0; i < 10; i++) + { + int pointer = rom.ReadPointer(address); + if (pointer != 0) + refs.Add(new StringRef { Index = i, PointerLocation = address, OldPointer = pointer }); + address += 4; + } + return refs.ToArray(); + } + + public static StringRef[] ReadItemHelpRefs(byte[] rom) + { + var refs = new List(); + int address = 0x155000; + for (int i = 0; i < 254; i++) + { + int pointer = rom.ReadPointer(address + 0x23); + if (pointer != 0) + refs.Add(new StringRef { Index = i, PointerLocation = address + 0x23, OldPointer = pointer }); + address += 39; + } + return refs.ToArray(); + } + + public static StringRef[] ReadPsiHelpRefs(byte[] rom) + { + var refs = new List(); + int address = 0x158A50; + for (int i = 0; i < 53; i++) + { + int pointer = rom.ReadPointer(address + 11); + if (pointer != 0) + refs.Add(new StringRef { Index = i, PointerLocation = address + 11, OldPointer = pointer }); + address += 15; + } + return refs.ToArray(); + } + + public static StringRef[] ReadPhoneRefs(byte[] rom) + { + var refs = new List(); + int address = 0x157AAE; + for (int i = 0; i < 6; i++) + { + int pointer = rom.ReadPointer(address + 0x1B); + if (pointer != 0) + refs.Add(new StringRef { Index = i, PointerLocation = address + 0x1B, OldPointer = pointer }); + address += 31; + } + return refs.ToArray(); + } + + public static StringRef[] ReadEnemyTextRefs(byte[] rom) + { + var refs = new List(); + int address = 0x159589; + for (int i = 0; i < 231; i++) + { + int pointer = rom.ReadPointer(address + 0x2D); + if (pointer != 0) + refs.Add(new StringRef { Index = i, PointerLocation = address + 0x2D, OldPointer = pointer }); + + pointer = rom.ReadPointer(address + 0x31); + if (pointer != 0) + refs.Add(new StringRef { Index = i, PointerLocation = address + 0x31, OldPointer = pointer }); + + address += 94; + } + return refs.ToArray(); + }*/ + + public static MiscStringCollection ReadPointerTable(byte[] rom, int tableAddress, int stringsAddress) + { + var refs = new List(); + int entries = rom.ReadInt(tableAddress); + + int currentTableAddress = tableAddress; + currentTableAddress += 4; + + for (int i = 0; i < entries; i++) + { + int offset = rom.ReadInt(currentTableAddress); + refs.Add(new MiscStringRef + { + OffsetLocation = currentTableAddress, + OldPointer = offset + stringsAddress, + Index = i + }); + currentTableAddress += 4; + } + + return new MiscStringCollection + { + OffsetTableLocation = tableAddress, + StringsLocation = stringsAddress, + StringRefs = refs + }; + } + + public static FixedStringCollection ReadFixedStringTable(byte[] rom, int stringsAddress, int numEntries, int entryLength) + { + var refs = new List(); + int currentStringAddress = stringsAddress; + + for(int i=0;i StringRefs { get; set; } + } +} diff --git a/ScriptTool/ScriptTool/MiscStringRef.cs b/ScriptTool/ScriptTool/MiscStringRef.cs new file mode 100644 index 0000000..b495558 --- /dev/null +++ b/ScriptTool/ScriptTool/MiscStringRef.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace ScriptTool +{ + public class MiscStringRef + { + [JsonConverter(typeof(JsonHexConverter))] + public int Index { get; set; } + + [JsonConverter(typeof(JsonHexConverter))] + public int OffsetLocation { get; set; } + + [JsonConverter(typeof(JsonHexConverter))] + public int OldPointer { get; set; } + + public bool BasicMode { get; set; } + public string Old { get; set; } + public string New { get; set; } + + public bool ShouldSerializeBasicMode() + { + return BasicMode; + } + } +} diff --git a/ScriptTool/ScriptTool/Program.cs b/ScriptTool/ScriptTool/Program.cs new file mode 100644 index 0000000..39aaf2a --- /dev/null +++ b/ScriptTool/ScriptTool/Program.cs @@ -0,0 +1,250 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using Newtonsoft.Json; + +namespace ScriptTool +{ + class Program + { + static IList EbControlCodes = new List(); + static IList M12ControlCodes = new List(); + + // Decompiler setup + static EbTextDecompiler ebDecompiler; + static M12TextDecompiler m12Decompiler; + + static void Main(string[] args) + { + CommandOptions options = ParseCommandLine(args); + if (options == null) + { + Usage(); + return; + } + + LoadControlCodes(); + + if (options.Command == CommandType.Decompile) + { + ebDecompiler = new EbTextDecompiler() { ControlCodes = EbControlCodes }; + m12Decompiler = new M12TextDecompiler() { ControlCodes = M12ControlCodes }; + + // Load ROMs + byte[] ebRom = File.ReadAllBytes(options.EbRom); + byte[] m12Rom = File.ReadAllBytes(options.M12Rom); + + // Decompile misc string tables + if (options.DoMiscText) + { + DecompileEbMisc(ebRom, options.WorkingDirectory); + DecompileM12Misc(m12Rom, options.WorkingDirectory); + } + + // Decompile main string tables + if (options.DoMainText) + { + DecompileEb(ebRom, options.WorkingDirectory); + DecompileM12(m12Rom, options.WorkingDirectory); + } + } + else if (options.Command == CommandType.Compile) + { + // TBD + } + } + + static void Usage() + { + Console.WriteLine("Usage:"); + Console.WriteLine(" ScriptTool.exe [-decompile or -compile] [-misc] [-main] workingdirectory [ebrom m12rom]");; + } + + static CommandOptions ParseCommandLine(string[] args) + { + var argList = new List(args); + + // Check for decompile switch + CommandType command; + if (argList.Contains("-decompile") && !argList.Contains("-compile")) + { + command = CommandType.Decompile; + argList.Remove("-decompile"); + } + else if (argList.Contains("-compile") && !argList.Contains("-decompile")) + { + command = CommandType.Compile; + argList.Remove("-compile"); + } + else + { + return null; + } + + // Check for main and misc flags + bool doMain = false; + bool doMisc = false; + if (argList.Contains("-main")) + { + doMain = true; + argList.Remove("-main"); + } + if (argList.Contains("-misc")) + { + doMisc = true; + argList.Remove("-misc"); + } + + // Check for working directory + if (argList.Count < 1) + return null; + + string working = argList[0]; + if (!Directory.Exists(working)) + return null; + + // Check for ROM paths + string ebRom = null; + string m12Rom = null; + if (command == CommandType.Decompile && argList.Count == 3) + { + ebRom = argList[1]; + m12Rom = argList[2]; + if (!File.Exists(ebRom) || !File.Exists(m12Rom)) + return null; + } + + return new CommandOptions + { + WorkingDirectory = working, + EbRom = ebRom, + M12Rom = m12Rom, + Command = command, + DoMainText = doMain, + DoMiscText = doMisc + }; + } + + static void LoadControlCodes() + { + EbControlCodes = ControlCode.LoadEbControlCodes("eb-codelist.txt"); + M12ControlCodes = ControlCode.LoadM12ControlCodes("m12-codelist.txt"); + } + + static void DecompileEb(byte[] ebRom, string workingDirectory) + { + var context = new DecompileContext(); + + // Pull all string refs from the ROM + var allRefs = new List>(); + allRefs.Add(Tuple.Create("eb-tpt", EbTextTables.ReadTptRefs(ebRom))); + allRefs.Add(Tuple.Create("eb-battle-actions", EbTextTables.ReadBattleActionRefs(ebRom))); + allRefs.Add(Tuple.Create("eb-prayers", EbTextTables.ReadPrayerRefs(ebRom))); + allRefs.Add(Tuple.Create("eb-item-help", EbTextTables.ReadItemHelpRefs(ebRom))); + allRefs.Add(Tuple.Create("eb-psi-help", EbTextTables.ReadPsiHelpRefs(ebRom))); + allRefs.Add(Tuple.Create("eb-phone", EbTextTables.ReadPhoneRefs(ebRom))); + allRefs.Add(Tuple.Create("eb-enemy-encounters", EbTextTables.ReadEnemyTextRefs(ebRom))); + + // Decompile + var allPointers = allRefs.SelectMany(rl => rl.Item2).Select(r => r.OldPointer).ToArray(); + ebDecompiler.Decompile(ebRom, allPointers, context); + + // Update labels for all refs and write to JSON + foreach (var refList in allRefs) + { + foreach (var stringRef in refList.Item2) + stringRef.Label = context.LabelMap[stringRef.OldPointer]; + + File.WriteAllText(Path.Combine(workingDirectory, refList.Item1 + ".json"), JsonConvert.SerializeObject(refList.Item2, Formatting.Indented)); + } + + // Write the strings + File.WriteAllText(Path.Combine(workingDirectory, "eb-strings.txt"), String.Join(Environment.NewLine, context.Strings)); + } + + static void DecompileEbMisc(byte[] ebRom, string workingDirectory) + { + + } + + static void DecompileM12(byte[] m12Rom, string workingDirectory) + { + + var context = new DecompileContext(); + + // Pull all string refs from the ROM + var allRefs = new List>(); + allRefs.Add(Tuple.Create("m12-tpt", M12TextTables.ReadTptRefs(m12Rom))); + + // Decompile + var allPointers = allRefs.SelectMany(rl => rl.Item2).Select(r => r.OldPointer).ToArray(); + m12Decompiler.Decompile(m12Rom, allPointers, context); + + // Update labels for all refs and write to JSON + foreach (var refList in allRefs) + { + foreach (var stringRef in refList.Item2) + stringRef.Label = context.LabelMap[stringRef.OldPointer]; + + File.WriteAllText(Path.Combine(workingDirectory, refList.Item1 + ".json"), JsonConvert.SerializeObject(refList.Item2, Formatting.Indented)); + } + + // Write the strings + File.WriteAllText(Path.Combine(workingDirectory, "m12-strings.txt"), String.Join(Environment.NewLine, context.Strings)); + } + + static void DecompileM12Misc(byte[] m12Rom, string workingDirectory) + { + // Item names + var itemNames = M12TextTables.ReadItemNames(m12Rom); + DecompileM12MiscStringCollection(m12Rom, workingDirectory, "m12-itemnames", itemNames); + + // Menu choices + var menuChoices = M12TextTables.ReadMenuChoices(m12Rom); + DecompileM12MiscStringCollection(m12Rom, workingDirectory, "m12-menuchoices", menuChoices); + + // Misc text + var miscText = M12TextTables.ReadMiscText(m12Rom); + DecompileM12MiscStringCollection(m12Rom, workingDirectory, "m12-misctext", miscText); + + // PSI names + var psiNames = M12TextTables.ReadPsiNames(m12Rom); + DecompileM12FixedStringCollection(m12Rom, workingDirectory, "m12-psinames", psiNames); + } + + static void DecompileM12MiscStringCollection(byte[] rom, string workingDirectory, string name, MiscStringCollection miscStringCollection) + { + // Decompile the strings + foreach (var miscStringRef in miscStringCollection.StringRefs) + { + miscStringRef.Old = + miscStringRef.New = + m12Decompiler.ReadString(rom, miscStringRef.OldPointer, -1, miscStringRef.BasicMode); + } + + // Write JSON + File.WriteAllText(Path.Combine(workingDirectory, name + ".json"), JsonConvert.SerializeObject(miscStringCollection, Formatting.Indented)); + } + + static void DecompileM12FixedStringCollection(byte[] rom, string workingDirectory, string name, FixedStringCollection fixedStringCollection) + { + // Decompile the strings + foreach (var fixedStringRef in fixedStringCollection.StringRefs) + { + fixedStringRef.Old = + fixedStringRef.New = + m12Decompiler.ReadString(rom, fixedStringRef.OldPointer, + fixedStringRef.OldPointer + fixedStringCollection.EntryLength, + false); + } + + // Write JSON + File.WriteAllText(Path.Combine(workingDirectory, name + ".json"), JsonConvert.SerializeObject(fixedStringCollection, Formatting.Indented)); + } + + //static void CompileM12Misc() + } +} diff --git a/ScriptTool/ScriptTool/Properties/AssemblyInfo.cs b/ScriptTool/ScriptTool/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0e43008 --- /dev/null +++ b/ScriptTool/ScriptTool/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ScriptTool")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ScriptTool")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("04d88ce9-e4a3-457b-9f74-959ddf16f81d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ScriptTool/ScriptTool/ScriptTool.csproj b/ScriptTool/ScriptTool/ScriptTool.csproj new file mode 100644 index 0000000..245f603 --- /dev/null +++ b/ScriptTool/ScriptTool/ScriptTool.csproj @@ -0,0 +1,93 @@ + + + + + Debug + AnyCPU + {CA5C95AE-2CD2-47E3-B5AE-FE6136808AC4} + Exe + Properties + ScriptTool + ScriptTool + v4.5.1 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + + + \ No newline at end of file diff --git a/ScriptTool/ScriptTool/eb-codelist.txt b/ScriptTool/ScriptTool/eb-codelist.txt new file mode 100644 index 0000000..a1fc584 --- /dev/null +++ b/ScriptTool/ScriptTool/eb-codelist.txt @@ -0,0 +1,216 @@ +00,"Line break"; +01,"Start on Blank Line"; +!02,"Signal end of block or string"; +03,"Wait with prompt, brief pause in battle"; +04 XX XX,"Toggle flag $XXXX on"; +05 XX XX,"Toggle flag $XXXX off"; +06 XX XX YY YY YY YY,"If flag $XXXX is set, jump to address $YYYYYYYY"; +07 XX XX,"Set binary flag with the status of flag $XXXX"; +08 XX XX XX XX,"Parse text at $XXXXXXXX and continue here"; +*09,"Multiple-entry pointer table (Jump to address)"; +!0A XX XX XX XX,"Jump to text at address $XXXXXXXX"; +0B XX,"Set binary flag to true if number in memory == $XX"; +0C XX,"Set binary flag to true if number in memory != $XX"; +0D XX,"Copy from memory $XX into argumentative memory"; +0E XX,"Input $XX into a memory bank"; +0F,"Secondary memory +1"; +10 XX,"Pause for ~0.02 sec. * $XX"; +11,"Treats text displayed by [1C (07|0C) XX] as a menu"; +12,"Clear current line of text"; +13,"Wait without prompt"; +14,"Wait with prompt in battle"; +18 00,"Closes most recently used text window"; +18 01 XX,"Open window"; +18 02,"UNKNOWN"; +18 03 XX,"Change parsing focus to window $XX"; +18 04,"Close all windows"; +18 05 XX YY,"Display text $XX pixels over and $YY lines down"; +18 06,"Clear contents from current window"; +18 07 XX XX XX XX YY,"Check for $XX inequality to memory"; +18 08 XX,"UNKNOWN"; +18 09 XX,"UNKNOWN"; +18 0A,"Open wallet window and display current cash on hand"; +18 0D XX YY,"Display Status window for Char$XX YY-01-parameter, 01,02 specific, 02 ?"; +19 02,"Load a text string into memory (end with [02])" +19 04,"UNKNOWN"; +19 05 XX YY ZZ,"Inflict ailment YY YY on character $XX"; +19 10 XX,"Return the character number of the person in party position $XX" +19 11 XX,"Return One Letter from a Character's Name (see lexicon.txt)"; +19 14,"UNKNOWN"; +19 16 XX YY,"Return Byte YY of Character XX Status"; +19 18 XX,"UNKNOWN"; +19 19 XX YY,"Sets $YY(01-0e) in item menu of Char$XX into working memory"; +19 1A XX,"UNKNOWN"; +19 1B XX,"UNKNOWN"; +19 1C XX YY,"UNKNOWN"; +19 1D XX YY,"UNKNOWN"; +19 1E,"UNKNOWN"; +19 1F,"UNKNOWN"; +19 20,"UNKNOWN"; +19 21 XX,"UNKNOWN"; +19 22 XX XX YY YY,"Store to memory the direction from character $XX to object $YY"; +19 23 XX XX YY YY ZZ,"Store to memory the direction from TPT entry $XX to object $YY (ZZ unknown)"; +19 24 XX XX YY YY ZZ,"Store to memory the direction from generated sprite $XX to object $YY (ZZ=00)"; +19 25 XX,"UNKNOWN"; +19 26 XX,"UNKNOWN"; +19 27 XX,"UNKNOWN"; +19 28 XX,"UNKNOWN"; +1A 00,"UNKNOWN INCOMPLETE"; +1A 01 WW WW WW WW XX XX XX XX YY YY YY YY ZZ ZZ ZZ ZZ,"Current Party Member Selection Menu"; +1A 04,"UNKNOWN INCOMPLETE"; +1A 05 XX YY,"Display the inventory of character YY in window XX"; +1A 06 XX,"Displays shop window $XX"; +1A 07,"Related to Escargo Express stored goods window"; +1A 08,"UNKNOWN INCOMPLETE"; +1A 09,"UNKNOWN INCOMPLETE"; +1A 0A,"Open non-working phone window"; +1A 0B,"Copy WRAM to Active Memory"; +1B 00,"Copy all memory to storage"; +1B 01,"Copy all storage to memory"; +1B 02 XX XX XX XX,"If binary flag is set 0, jump to $XXXXXXXX"; +1B 03 XX XX XX XX,"If binary flag is set 1, jump to $XXXXXXXX"; +1B 04,"Swap working and argumentary memory"; +1B 05,"Copy Active Memory to WRAM"; +1B 06,"Copy WRAM to Active Memory"; +1C 00 XX,"Text and window background color effects"; +1C 01 XX,"Display statistical data value $XX"; +1C 02 XX,"Display character name $XX"; +1C 03 XX,"Display Text Character XX; 00 as argument"; +1C 04,"Open party HP/PP status windows"; +1C 05 XX,"Display item name $XX"; +1C 06 XX,"Display teleport destination name $XX"; +1C 07 XX,"Display $XX strings centered horizontally (see [19 02])"; +1C 08 XX,"Display text graphic $XX"; +1C 09 XX,"UNKNOWN"; +1C 0A XX XX XX XX,"Displays $XXXXXXXX in decimal"; +1C 0B XX XX XX XX,"Displays $XXXXXXXX in decimal as money"; +1C 0C XX,"Display $XX strings vertically (see [19 02])"; +1C 0D,"Display action user name"; +1C 0E,"Display action target name"; +1C 0F,"UNKNOWN INCOMPLETE;IN PROGRESS (Relates to Level-Up stats editor)"; +1C 11 XX,"UNKNOWN"; +1C 12 XX,"Print PSI name $XX"; +1C 13 XX YY,"Display XX Amimation (YY is usually 00) See Animation code on forum"; +1C 14 XX,"Say $XX pronoun"; +1C 15 XX,"Say $XX pronoun"; +1D 00 XX XX,"Add item $YY to character $XX's inventory and return $YY if successful"; +1D 01 XX YY,"Remove item $YY from character $XX's inventory"; +1D 02 XX,"Set binary flag true if character $XX does not have free space in their inventory"; +1D 03 XX,"Set binary flag true if character $XX has free space in their inventory"; +1D 04 XX YY,"Sets binary flag true if char $XX does not have item $YY"; +1D 05 XX YY,"Sets binary flag true if char $XX has item $YY"; +1D 06 XX XX XX XX,"Add $XX dollars to ATM balance"; +1D 07 XX XX XX XX,"Remove $XX dollars from ATM balance"; +1D 08 XX XX,"Add $XX dollars to wallet balance"; +1D 09 XX XX,"Remove $XX dollars from wallet balance" +1D 0A XX,"Return price of item"; +1D 0B XX,"Get one half of item $XX"; +1D 0C XX XX,"UNKNOWN"; +1D 0D XX YY YY,"Set binary flag true if character $XX has ailment YY YY"; +1D 0E XX YY,"Add item $YY to character $XX's inventory and return number of items held"; +1D 0F XX YY,"UNKNOWN"; +1D 10 XX XX,"UNKNOWN"; +1D 11 XX XX,"UNKNOWN"; +1D 12 XX XX,"UNKNOWN"; +1D 13 XX XX,"UNKNOWN"; +1D 14 XX XX XX XX,"Sets binary flag true if the player currently holds $XX in the wallet"; +1D 15 XX XX,"Multiply $XXXX by the number of characters in the party, and store in Working Memory"; +1D 17 XX XX XX XX,"Sets binary flag true if the player currently holds $XX in the ATM"; +1D 18 XX,"UNKNOWN"; +1D 19 XX,"Sets binary flag true if there are $XX party members"; +1D 20,"Check same user/target"; +1D 21 XX,"Generate random number from 0 to $XX"; +1D 22,"Set binary flag if current sector is Exit mouse-compatible"; +1D 23 XX,"UNKNOWN"; +1D 24 XX,"May relate to returning cash earned since last phone call to dad"; +1E 00 XX YY,"Character $XX recovers HP by YY% of max"; +1E 01 XX YY,"Character $XX loses HP by YY% of max"; +1E 02 XX YY,"Character $XX recovers $YY HP"; +1E 03 XX YY,"Character $XX loses $YY HP"; +1E 04 XX YY,"Character $XX recovers PP by YY% of max"; +1E 05 XX YY,"Character $XX loses PP by YY% of max"; +1E 06 XX YY,"Character $XX recovers $YY PP"; +1E 07 XX YY,"Character $XX loses $YY PP"; +1E 08 XX YY,"Character $XX becomes level $YY"; +1E 09 XX YY YY YY,"Character $XX gains $YYYYYY EXP"; +1E 0A XX YY,"Boost IQ in character $XX by $YY"; +1E 0B XX YY,"Boost Guts in character $XX by $YY"; +1E 0C XX YY,"Boost Speed in character $XX by $YY"; +1E 0D XX YY,"Boost Vitality in character $XX by $YY"; +1E 0E XX YY,"Boost Luck in character $XX by $YY"; +1F 00 XX YY,"Play music track $YY ($XX is unused)"; +1F 01 00,"Stops music"; +1F 02 XX,"Play sound effect $XX"; +1F 03,"Return music setting to sector default"; +1F 04 XX,"Toggles sound for text printing (?)"; +1F 05,"Disallow Sector Boundaries to Change Music"; +1F 06,"Allow Sector Boundaries to Change Music"; +1F 07 XX,"Apply audio effect $XX"; +1F 11 XX,"Add character $XX to party"; +1F 12 XX,"Remove character $XX from party"; +1F 13 XX YY,"Turn character $XX to direction $YY"; +1F 14 XX,"UNKNOWN"; +1F 15 XX XX YY YY ZZ,"Generate sprite $XXXX with movement pattern $YYYY using style $ZZ"; +1F 16 XX XX YY,"Turn TPT entry $XXXX to direction $YY"; +1F 17 XX XX YY YY ZZ,"Generate TPT entry $XXXX with movement pattern $YYYY using style $ZZ"; +1F 18 XX XX XX XX XX XX XX,"UNKNOWN"; +1F 19 XX XX XX XX XX XX XX,"UNKNOWN"; +1F 1A XX XX YY,"Generate sprite $YY near TPT entry $XXXX"; +1F 1B XX XX,"Delete 1F 1A sprite near TPT entry $XXXX"; +1F 1C XX YY,"Generate sprite $YY near character $XX"; +1F 1D XX,"Delete 1F 1C sprite near character $XX"; +1F 1E XX XX YY,"Make TPT entry $XXXX disappear via style $YY"; +1F 1F XX XX YY,"Delete 1F 15-generated sprite $XXXX via style $YY"; +1F 20 XX YY,"PSI Teleport to destination $XX using method $YY"; +1F 21 XX,"Teleport to 0x15EDAB teleport coordinate entry $XX"; +1F 23 XX XX,"Trigger battle with 0x10D74C enemy group entry $XXXX"; +1F 30,"Change to normal font"; +1F 31,"Change to Mr. Saturn font"; +1F 41 XX,"Call special event $XX"; +1F 50,"Disable controller input"; +1F 51,"Re-enable controller input"; +1F 52 XX,"Number selector with XX digits"; +1F 60 XX,"UNKNOWN"; +1F 61,"Simultaneously commences all prepared movements"; +1F 62 XX,"UNKNOWN"; +1F 63 XX XX XX XX,"Jump to $XXXXXXXX after the next screen refresh"; +1F 64,"Purge all NPCs from party"; +1F 65,"Purge first NPC from party"; +1F 66 XX YY ZZ ZZ ZZ ZZ,"Activate hotspot $XX with address $ZZZZZZZZ"; +1F 67 XX,"Deactivate hotspot $XX"; +1F 68,"Store current coordinates into memory"; +1F 69,"Teleport to coordinates stored by 1F 68"; +1F 71 XX YY,"Character $XX realizes special PSI $YY"; +1F 81 XX XX,"Check If Character Can Use Item"; +1F 83 XX XX,"Equip character XX with his or her YYth item"; +1F 90,"UNKNOWN"; +1F A0,"TPT entry being spoken to/checked faces downwards"; +1F A1,"Change Direction of Current TPT Entry to Down"; +1F A2,"UNKNOWN"; +1F B0,"Save the game in the slot selected at the beginning"; +*1F C0,"Multiple-entry pointer table (Reference address)"; +1F D0 XX,"Attempt to Fix Items"; +1F D1,"Returns a numeric value based on the proximity of a magic truffle TPT entry"; +1F D2 XX,"Summon photographer to location $XX"; +1F D3 XX,"Trigger timed event $XX from the 0x15F845 table"; +1F E1 XX YY ZZ,"Change map palette to that of tileset $XX, pallet $YY at speed $ZZ"; +1F E4 XX XX YY,"Change direction of 1F 15-generated sprite $XXXX to $YY"; +1F E5 XX,"Lock player movement"; +1F E6 XX XX,"Delay appearance of TPT entry $XXXX until end of text parsing"; +1F E7 XX XX,"UNKNOWN"; +1F E8 XX,"Restrict player movement if camera is focused on other sprite (XX is usually FF)"; +1F E9 XX XX,"Restrict player movement until end of text block (use 1F 61 after)"; +1F EA XX XX,"UNKNOWN"; +1F EB XX YY,"Make character $XX disappear via style $YY"; +1F EC XX YY,"Make character $XX appear via style $YY"; +1F ED,"Your party will be teleported (in a glitchy way) on top of some sprite that is nearby when the text block is over if the camera switched to another sprite in the block of text"; +1F EE XX XX,"UNKNOWN"; +1F EF XX XX,"Focuses the camera to where [1f 15]-generated sprite xxxx is but doesn't follow it"; +1F F0,"Activate bicycle"; +1F F1 XX XX YY YY,"Apply movement pattern $YYYY to TPT entry $XXXX"; +1F F2 XX XX YY YY,"Apply movement pattern $YYYY to sprite $XXXX"; +1F F3 XX XX YY,"Generate sprite $YY near sprite $XXXX"; +1F F4 XX XX,"Delete 1F F3 sprite near sprite $XXXX"; +15 XX,"Use compressed string $XX from bank 0"; +16 XX,"Use compressed string $XX from bank 1"; +17 XX,"Use compressed string $XX from bank 2"; \ No newline at end of file diff --git a/ScriptTool/ScriptTool/eb-compressed-strings.txt b/ScriptTool/ScriptTool/eb-compressed-strings.txt new file mode 100644 index 0000000..74d6a81 --- /dev/null +++ b/ScriptTool/ScriptTool/eb-compressed-strings.txt @@ -0,0 +1,768 @@ + + in the + that +... + and + this + to the + about + just + of the + something + going to + to + you have + your + for + you're +@You + really + don't +@The +e the +e you +... + the + will + ...Brick Road +ing + some + +@Do you want to + like +ou don't have + is + you + you + anything else + the + you want to + for the + friend + at the +ould you like + from + would +he Runaway Five + with + want to +@If you + you don't +s the +ed to +e... + something +t the +@... +@Please +'s + of your +@It's +@Thank you +@I + here. + in +@Do you + I'll + have +e of +d you +@I'm +me to +@I don't +@Well, +@This is +ed the +@You're + for a + anything +ing + of + you should + I + from the +s... + it's + time +e to +ed +e of the + to you +n't you + again + for you. +other +ation + little +ing to + can't + much + someone + on the + looks like + don't you + very +the + can +you + that you + it + you want +ou can't + able to + already + give you +understand + + my + you can + that + what +here's + there +n the +@What +Thank you + I can't +one +@The + thought + not +You should +ou know + has + back + of +ve been + I'm + there + with you +@I heard + in + here + Fourside +I wonder + to + could + think + out + good + the + You + too much +ome back +t... + here +thing +come +ly +ent + strong + money. +@I'll +an +ou must + are you + with the + on your +too many + you. +know + to be + around + if you +@Are you +ome again +e and + more +think +e your +@Don't +nd the +t to +rea + he + me + strange + for you +ight + a + be +ther +all + cannot +here is +You have +Monotoli + was +ll you +hat +, but + stuff +eep + it + didn't + like th +ll right + should + over +@Oh, + hear + every +I'm not + about t + zombies + damage +his is +all + some +@It + attack + right +ally +orry +ess +er +@That's +carry it +ake +t was +, the +n you +sn't + help +king +ear +ing the + It's +very + talking +ou've +that's + to me +@Hello + enemy + you are + by + IBNT +ight +on't +est +ick + power +s that +s are + call +t is + is +age +, and + great +@Thanks + people +ter +not +ill +ness +@This + into + ha + I can +t your + before + things +tion + for +be + this +Happy + You +out +enough + I'm +@You + go +for + all th + though +ing you +e you +ring +one +get +t you +@I'm s + the s +e's +port +d... + out +@What + get +I have +looking +n this + of my + have a +ent + do +d of +ting +ncrease + Twoson +through +s your +ou'll + place + right + Onett + + our + too +ater + be +ain +ing. +take +e is +the + please + do you +need +use + now. + got + +and +ning + sta + the t +I was +y the +his +appe +con + ho +hat's +Threed +get +s of +inter +talk + man +day +ove + ha ha +inally +monkey +s and +ate +see +town +side +y to +ever +equip + than + who +long +care +room +e are +ard +end +, you + the b +I know +think +s you +time +ment + so +! +I've +ell + wa +ious +reat +live +ange + we +ble +@..... +ecover + only +thing +er... +Mr. +ough + now + I +wor +to +ed. + I +'re + give +ing a +return +better +ince + well + one +still +ying +me... +sta +ust +per +lease +ling +point +ect +ast +pretty +Giygas +ecause +member +carry + used +elieve + money +pres +way + di + even +ound +s to +ting +ted +oing + and +ine + T +sure + on +@...I +while +@but +@We +ice +se +ive + rea + com +est +have + but +away +here + must + but + want +ready +I'm +carr +my +ay... + are +@Hey, +world +ing a + happ + seem + his +nder + se +ant +item +@But + sho +in a +made +night +et's + like + The +che + de +ful +hat +and +self +ould +@No +car + tr + good + stor +ombie +@Oh +@It +s a +vent +ant + so +olla +ree +Your +make + work +power + home +also +ance +@How +h... +@If +ple +buy +e, +n't +oke +n't +more +Good +t a + all +take +round + when + name +being +attle +ite +ack +y... +@Whe +with +al +have + look +t of +aster + a lo + feel +here +count +monst +now +ark +ous +'ll + bus +head + any +t you +erson +after +ummer + hard +ful +ever + kid + a b +kay +tra +pla +ook +ome +eat +@Wel + off +turn +I am +oney + any +ave +any +enem + con + me +red +en + bo + re +ell +sell +what +next +ure + bu +pro + on +res +es + lo +our +use +iste +ood + no +ope +ock +row +@He +how + may + as +stor + are +ost +mean + We + He +@An +it +in +ound +one. +come +blue +way, + or + mo +. +each +some +@(Th +otel +hand +came + ca +ame +@Hey +een +spec +red +ing! + ... +hose +ind +ice +ver + W +min +et +on +ace +er. +are +nter +o... +were +help +old + it. +hear +stop +look +com +@Do + her + pro +chin +ble +e a +girl +luck + B +act +ike +down +part + see + use +uch +from +just +llow +@Her + at +@So, + ye +ser +ying +ring +eave +big +ried + was + say + su +her +it. +Man +kin + up +ers +@Why +p... +cour + him +agic +any +fin +eal +he +ide +oor +ity +got +ens +ish +ive + sa +oon +fee +s a +nce + D +uck +ass +man +a p +@(I +@Wh + gu + ( +bus +los +ts + ba + S + if +@Ha + ma +unn +ay +ner +@A +or +stu +ust +kya +shi +pos + A +int + le +ary +ven + ch +own + hu +@My +@So + sp + sh +wan +ool + Y +old +eas +, I +iki +@Ky +@Ye +spi diff --git a/ScriptTool/ScriptTool/m12-codelist.txt b/ScriptTool/ScriptTool/m12-codelist.txt new file mode 100644 index 0000000..59258a0 --- /dev/null +++ b/ScriptTool/ScriptTool/m12-codelist.txt @@ -0,0 +1,198 @@ +!00 FF,End (02) +01 FF,New line (00) +02 FF,Prompt + new line (03 00) +03 FF XX XX,Display menu string set {0:X} +04 FF XX XX XX XX,Jump to offset {0:X} if first choice selected +05 FF XX XX XX XX,Jump to offset {0:X} if second choice selected +08 FF XX XX,Set flag {0:X} (04) +09 FF XX XX,Unset flag {0:X} (05) +0C FF,Party leader's name +0D FF,Ness' name (1C 02 01 or 1C 01 08) +0E FF,Paula's name (1C 02 02) +0F FF,Jeff's name (1C 02 03) +10 FF,Poo's name (1C 02 04) +11 FF,Favorite food (1C 01 04) +12 FF,Favorite thing (1C 01 05) +14 FF XX XX,Teleport to entry {0:X} (1F 21) +15 FF,King's name (1C 02 07) +!18 FF XX XX,Unknown +1A FF XX XX,Display string {0:X}? (Appears after 90 FF) +1B FF XX XX,Pause for {0:X} frames (10) +1C FF XX XX YY YY YY YY,If flag {0:X} is set, jump to offset {1:X} and return (06) +1D FF,Wait without prompt (13) +1E FF XX XX,Return one letter from a character's name (19 11) +1F FF XX XX,Display text character {0:X} (1C 03) +20 FF,Display SMAAASH!! graphics (1C 08 01) +21 FF,Display YOU WIN! graphics (1C 08 02) +22 FF,Unknown +23 FF,Display cash in ATM (1C 01 07) +24 FF,Display character 1 defense (1C 01 12) +25 FF,Display character 2 defense (1C 01 28) +26 FF,Display character 3 defense (1C 01 3E) +27 FF,Display character 4 defense (1C 01 54) +28 FF,Display character 1 offense (1C 01 11) +29 FF,Display character 2 offense (1C 01 27) +2A FF,Display character 3 offense (1C 01 3D) +2B FF,Display character 4 offense (1C 01 53) +2D FF,Display PSI name? (1C 12 00) +2E FF,Show main text window (18 01 01) +5F FF XX,Set the current rendering location to {0:X} (custom code) +60 FF XX,Add {0:X} pixels to the current rendering location (custom code) +61 FF XX XX,Toggles sound for text printing (?) (1F 04) +62 FF,Display inventory? (?) +63 FF,Display cash on hand (1C 01 06) +64 FF,Call special event (value pushed with FC FF?) (1F 41) +65 FF,Purge all NPCs from party (1F 64) +66 FF,Unknown (19 1E) +67 FF,Unknown (19 28) +68 FF,Clear main text window (sometimes used in place of 18 01 01) +69 FF,Open party HP/PP status windows (1C 04) +6A FF XX XX,Set binary flag if number in memory != {0:X} (0C) +6B FF XX XX,Apply audio effect {0:X} (1F 07) +6C FF XX XX YY YY,Sets binary flag true if character {0:X} does not have item {1:X} (1D 04) +6D FF XX XX YY YY,Remove item {1:X} from character {0:X}'s inventory (1D 01) +6E FF XX XX,Deactivate hotspot {0:X} (1F 67) +6F FF XX XX YY YY,Boost vitality in character {0:X} by {1:X} (1E 0D, values match) +70 FF XX XX YY YY,Boost speed in character {0:X} by {1:X} (1E 0C, values match) +71 FF XX XX YY YY,Boost guts in character {0:X} by {1:X} (1E 0B, values match) +72 FF XX XX YY YY,Boost IQ in character {0:X} by {1:X} (1E 0A, values match) +73 FF XX XX YY YY,Boost luck in character {0:X} by {1:X} (1E 0E, values match) +74 FF,Stop music (1F 01 00) +75 FF,Unknown (?) +76 FF XX XX,Show window flavor menu? Argument seems to tell it which string to start with (so a value of 1 will skip the first flavor and show the remaining 6) +77 FF XX XX,Unknown (1D 18, values match) +78 FF XX XX YY YY,Unknown (19 1D, values match) +79 FF XX XX YY YY,Unknown (19 1C, values match) +7A FF XX XX,Unknown (19 1A, values match) +7B FF,Change to Mr. Saturn font (1F 31) +7C FF,Change to normal font (1F 30) +7D FF,Activate bicycle (1F F0) +7E FF XX XX,Unknown (19 26) +7F FF,Save the game in the slot selected at the beginning (1F B0) +!80 FF XX XX XX XX,Jump to offset {0:X} and don't return (0A) +81 FF XX XX XX XX,If binary flag is set, jump to offset {0:X} and return (1B 03) +82 FF XX XX XX XX,If binary flag is unset, jump to offset {0:X} and return (1B 02) +83 FF XX XX,Set binary flag to status of flag {0:X} (07) +84 FF XX XX YY YY ZZ ZZ,Generate TPT entry {0:X} with movement pattern {1:X} using style {2:X} (1F 17) +85 FF XX XX YY YY,Make TPT entry {0:X} disappear via style {1:X} (1F 1E) +86 FF XX XX XX XX,Jump to offset {0:X} and return (08) +87 FF,Swap working and argumentary memory (1B 04) +88 FF,Copy all memory to storage (1B 00) +89 FF,Copy all storage to memory (1B 01) +8A FF,Copy active memory to working memory (1B 05) +8B FF,Copy working memory to active memory (1B 06) +8C FF,Unknown (1F A2) +8D FF XX XX,Load character number in party position {0:X} (19 10) +8E FF,TPT entry being spoken to/checked faces downwards (1F A0) +8F FF,Change direction of current TPT entry to down (1F A1) +90 FF XX,Load character/item name {0:X} into memory (?) +91 FF XX,Set binary flag if character {0:X} has free space in their inventory (1D 03) +92 FF XX YY YY,Set binary flag true if character {0:X} has ailment {1:X} (corresponds exactly to 1D 0D XX YY YY) +93 FF XX YY,Add item {1:X} to character {0:X}'s inventory and return number of items held (1D 0E) +94 FF XX,Display shop window {0:X} (1A 06) +*95 FF,Jump table with return (09) +96 FF XX,Set binary flag if there are {0:X} party members (1D 19) +97 FF XX,Set binary flag if character {0:X} does not have free space in their inventory (1D 02) +98 FF XX,Return price of item {0:X} (1D 0A) +99 FF XX XX XX XX,Sets binary flag if the player currently holds {0:X} in the wallet (1D 14) +9A FF XX XX,Set binary flag if number in memory == {0:X} (0B) +9B FF XX XX XX XX,Remove {0:X} dollars from wallet (1D 09) +9C FF,Open wallet window and display current cash on hand (18 0A) +9D FF XX YY ZZ ZZ ZZ ZZ,Activate hotspot {0:X} with address ${2:X} (1F 66) +9E FF,Simultaneously commences all prepared movements (1F 61) +9F FF,Display action user name (1C 0D) +A0 FF XX XX YY YY ZZ ZZ,Generate sprite {0:X} with movement pattern {1:X} using style {2:X} (1F 15) +A1 FF XX XX YY YY,Apply movement pattern {1:X} to TPT entry {0:X} (1F F1) +A2 FF XX XX XX XX,After finishing this text block, close all other windows, jump to offset {0:X} and don't return (?) +A3 FF XX XX,Restrict player movement until end of text block (1F E9, use 9E FF after) +A4 FF XX XX,Delay appearance of TPT entry {0:X} until end of text parsing (1F E6) +A5 FF XX XX YY YY,Delete A0 FF-generated sprite {0:X} via style {1:X} (1F 1F) +A6 FF XX XX YY YY,Apply movement pattern {1:X} to sprite {0:X} (1F F2) +A7 FF XX XX,Trigger battle with enemy group entry {0:X} (1F 23) +A8 FF XX,Copy from memory {0:X} into argumentative memory (0D) +A9 FF XX XX,Unknown (1F EA, values don't match) +AA FF XX XX,Unknown (1F E7, values don't match) +AB FF XX YY,Copies item {1:X} from character {0:X}'s inventory into working memory (19 19) +AC FF,Unknown (1C 0F) +AD FF,Display action target name (1C 0E) +AE FF XX,Add character {0:X} to party (1F 11) +AF FF XX,Remove character {0:X} from party (1F 12) +B0 FF XX XX YY YY,Generate sprite {1:X} near TPT entry {0:X} (1F 1A) +B1 FF XX XX YY YY,Generate sprite {1:X} near sprite {0:X} (1F F3) +B2 FF XX XX YY YY,Generate sprite {1:X} near character {0:X} (1F 1C) +B3 FF XX XX,Delete B0 FF sprite near TPT entry {0:X} (1F 1B) +B4 FF XX XX,Delete B1 FF sprite near sprite {0:X} (1F F4) +B5 FF XX XX,Delete B2 FF sprite near character {0:X} (1F 1D) +B6 FF XX YY,Display the inventory of character {1:X} in window {0:X} (1A 05, values match) +B7 FF XX,Return half-price of item {0:X} (1D 0B) +B8 FF XX,Load {0:X} into memory as a decimal string (related to 1C 0A) +B9 FF XX XX YY YY,Unknown (1D 0F) +BA FF XX XX XX XX,Add {0:X} dollars to wallet (1D 08) +BB FF XX,Multiply {0:X} by the number of characters in the party, and store in working memory (1D 15) +BC FF XX XX,Input {0:X} into memory bank (0E) +*BD FF,Jump table with return (1F C0) +BE FF XX XX YY YY,Set binary flag if character {0:X} has item {1:X} (1D 05) +BF FF,Unknown (19 20) +C0 FF,Increment secondary memory (0F) +C1 FF,Unknown (19 1F) +C2 FF,Unknown, appears before 1A FF 05 00 (1C 11 69) +C3 FF XX XX YY YY,Store to memory the direction from character {0:X} to object {1:X} (19 22) +C4 FF XX YY,Play music {1:X} (1F 00) +C5 FF,Return music to sector default (1F 03) +C6 FF,Close all windows (18 04) +C7 FF,Close most recently-used window (18 00) +C8 FF XX XX,Number selector with {0:X} digits (1F 52) +C9 FF XX XX,Focuses the camera to where A0 FF-generated sprite {0:X} is but doesn't follow it (1F EF) +CA FF,Your party will be teleported (in a glitchy way) on top of some sprite that is nearby when the text block is over if the camera switched to another sprite in the block of text (1F ED) +CB FF XX XX YY YY,Make character {0:X} disappear via style {1:X} (1F EB) +CC FF XX XX YY YY,Make character {0:X} appear via style {1:X} (1F EC) +CD FF XX XX YY YY ZZ ZZ,Change map palette to that of tileset {0:X}, palette {1:X} at speed {2:X} (1F E1) +CE FF XX XX YY YY,Turn TPT entry {0:X} to direction {1:X} (1F 16) +CF FF XX XX,Trigger timed event {0:X} (1F D3) +D0 FF XX XX XX XX,Sets binary flag true if the player currently holds {0:X} in the ATM (1D 17) +D1 FF XX XX XX XX,Add {0:X} dollars to ATM balance (1D 06) +D3 FF XX XX,Remove {0:X} dollars from ATM balance? (1D 07) +D4 FF XX XX,Play sound effect {0:X} (1F 02) +D5 FF XX XX,Unknown (1D 23, value matches) +D6 FF XX XX YY YY,Unknown (1D 11, value matches) +D7 FF XX XX YY YY,Equip character {0:X} with their {1:X}'th item (1F 83) +D8 FF XX XX YY YY,Turn character {0:X} to direction {1:X} (1F 13) +D9 FF XX XX,Unknown (1F 14 XX) +DA FF XX XX YY YY,Unknown (occurs after A0 FF and DC FF) +DB FF XX XX YY YY ZZ ZZ,Store to memory the direction from TPT entry {0:X} to object {1:X} (ZZ unknown) (19 23) +DC FF XX XX YY YY ZZ ZZ,Unknown (sandwiched between DA FF codes?) +DD FF XX XX XX XX YY YY YY YY,Check for {0:X} inequality to memory (18 07) +DE FF,Unknown (?) +DF FF,Open non-working phone window (1A 0A) +E0 FF XX XX,May relate to returning cash earned since last phone call to dad (1D 24, value matches) +E1 FF XX XX,Unknown (19 18, value matches) +E2 FF XX XX,Summon photographer to location {0:X} (1F D2) +E3 FF,Unknown (19 21 00) +E4 FF,Unknown (19 25 00) +E5 FF,Check same user/target (1D 20) +E6 FF XX XX,Unknown (1F EE, value matches) +E7 FF XX XX,Show Mach Pizza window? Different arguments yield different menu strings (02 = full A menu, 03 = A menu sans PSI, and more) +E8 FF XX XX YY YY,Character {0:X} realizes special PSI {1:X} (1F 71) +E9 FF XX XX YY YY YY YY,Character {0:X} gains {1:X} EXP (1E 09) +EA FF XX XX,Lock player movement (1F E5, value matches) +EB FF XX XX,Restrict player movement if camera is focused on other sprite (XX is usually FF) (1F E8) +EC FF,Display party select menu? (1A 01) +ED FF XX XX YY YY,Return byte {1:X} of character {0:X} status (19 16, values match) +EE FF XX XX YY YY ZZ ZZ,Inflict ailment {1:X} on character {0:X} (ZZ might possibly be the value to set? 0 for off, 1 for on) (YY and ZZ flipped w.r.t EB) (19 05) +EF FF XX XX YY YY,PSI Teleport to destination {0:X} using method {1:X} (1F 20, values match) +F0 FF XX XX YY YY,Display animation {0:X} (YY is usually 00) (1C 13) +F1 FF,Show Escargo Express window? ([18 02][1A 07][18 03 0D][18 00][18 03 01][01]) +F2 FF XX XX XX XX,Unknown (1D 13, values match) +F3 FF XX XX XX XX,Unknown (1D 12, values match) +F4 FF XX XX YY YY,Unknown (1D 0C, values match) +F5 FF,Unknown (19 14) +F6 FF XX XX,Generate random number from 0 to {0:X} (1D 21) +F7 FF XX XX YY YY,Character {0:X} recovers HP by {1:X} of max (1E 00) +F8 FF XX XX YY YY,Character {0:X} recovers PP by {1:X} of max (1E 04) +F9 FF XX XX YY YY,Character {0:X} recovers HP by {1:X} of max (not sure of the difference with F7 FF) (1E 00) +FA FF XX XX YY YY,Character {0:X} recovers PP by {1:X} of max (not sure of the difference with F8 FF) (1E 04) +FB FF XX XX,Attempt to fix items (1F D0) +FC FF XX XX,Unknown; load some kind of value into memory? Observed: [FC FF 06 00] [64 FF] == [1F 41 06] +FD FF,Set binary flag if current sector is Exit mouse-compatible (1D 22) +FE FF,Store current coordinates into memory (1F 68) +FF FF,Returns a numeric value based on the proximity of a magic truffle TPT entry (1F D1) \ No newline at end of file diff --git a/ScriptTool/ScriptTool/m12-text-table.txt b/ScriptTool/ScriptTool/m12-text-table.txt new file mode 100644 index 0000000..8182fd1 --- /dev/null +++ b/ScriptTool/ScriptTool/m12-text-table.txt @@ -0,0 +1,256 @@ +ぁ +あ +ぃ +い +ぅ +う +ぇ +え +ぉ +お +か +が +き +ぎ +く +ぐ +け +げ +こ +ご +さ +ざ +し +じ +す +ず +せ +ぜ +そ +ぞ +た +だ +ち +ぢ +っ +つ +づ +て +で +と +ど +な +に +ぬ +ね +の +は +ば +ぱ +ひ +び +ぴ +ふ +ぶ +ぷ +へ +べ +ぺ +ほ +ぼ +ぽ +ま +み +む +め +も +ゃ +や +ゅ +ゆ +ょ +よ +ら +り +る +れ +ろ + +わ + + +を +ん +INVALID +INVALID +INVALID +INVALID +INVALID +INVALID +INVALID +INVALID +INVALID +INVALID +INVALID +INVALID +INVALID +ァ +ア +ィ +イ +ゥ +ウ +ェ +エ +ォ +オ +カ +ガ +キ +ギ +ク +グ +ケ +ゲ +コ +ゴ +サ +ザ +シ +ジ +ス +ズ +セ +ゼ +ソ +ゾ +タ +ダ +チ +ヂ +ッ +ツ +ヅ +テ +デ +ト +ド +ナ +ニ +ヌ +ネ +ノ +ハ +バ +パ +ヒ +ビ +ピ +フ +ブ +プ +ヘ +ベ +ペ +ホ +ボ +ポ +マ +ミ +. +ム +メ +モ +ャ +ヤ +ュ +ユ +ョ +ヨ +ラ +リ +ル +レ +ロ + +ワ + + +ヲ +ン +INVALID +INVALID +INVALID +INVALID +INVALID +INVALID +INVALID +INVALID +INVALID +INVALID +INVALID +INVALID +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +α +β +γ +Σ +Ω + +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +# +! +? +/ +: +ー +~ +· +·· +。 +“ +” +( +) +♪ +◯ +$ +" +° +◆ +(e) +CONTROL CODE \ No newline at end of file diff --git a/ScriptTool/ScriptTool/packages.config b/ScriptTool/ScriptTool/packages.config new file mode 100644 index 0000000..af70bc8 --- /dev/null +++ b/ScriptTool/ScriptTool/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file