Added ScriptTool

This commit is contained in:
jberbrec 2015-03-13 18:04:23 -04:00
parent acfe5ddb8e
commit 52900e9c50
25 changed files with 3272 additions and 0 deletions

22
ScriptTool/ScriptTool.sln Normal file
View File

@ -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

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
</startup>
</configuration>

View File

@ -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
}
}

View File

@ -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<byte> 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<byte>();
}
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<ControlCode> LoadEbControlCodes(string path)
{
var codeList = new List<ControlCode>();
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<ControlCode> LoadM12ControlCodes(string path)
{
var codeList = new List<ControlCode>();
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;
}
}
}

View File

@ -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<string> Strings { get; private set; }
public DecompileContext()
{
LabelMap = new LabelMap();
Strings = new List<string>();
}
}
}

View File

@ -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<ControlCode> _controlCodes;
public IList<ControlCode> 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<int[]> textRanges = new List<int[]>();
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
}
}
}

View File

@ -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<MainStringRef>();
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<MainStringRef>();
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<MainStringRef>();
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<MainStringRef>();
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<MainStringRef>();
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<MainStringRef>();
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<MainStringRef>();
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();
}
}
}

View File

@ -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;
}
}
}

View File

@ -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<FixedStringRef> StringRefs { get; set; }
}
}

View File

@ -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; }
}
}

View File

@ -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));
}
}
}

View File

@ -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<int, string> Labels { get; private set; }
private int counter = 0;
public LabelMap()
{
Labels = new Dictionary<int, string>();
}
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();
}
}
}

View File

@ -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<ControlCode> ControlCodes { get; set; }
private static string[] charMap;
private static IList<int[]> textRanges = new List<int[]>();
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
}
}
}

View File

@ -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<MainStringRef>();
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<StringRef>();
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<StringRef>();
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<StringRef>();
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<StringRef>();
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<StringRef>();
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<StringRef>();
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<MiscStringRef>();
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<FixedStringRef>();
int currentStringAddress = stringsAddress;
for(int i=0;i<numEntries;i++)
{
refs.Add(new FixedStringRef
{
OldPointer = currentStringAddress,
Index = i
});
currentStringAddress += entryLength;
}
return new FixedStringCollection
{
StringsLocation = stringsAddress,
NumEntries = numEntries,
EntryLength = entryLength,
StringRefs = refs
};
}
public static MiscStringCollection ReadItemNames(byte[] rom)
{
return ReadPointerTable(rom, 0xB1AF94, 0xB1A694);
}
public static MiscStringCollection ReadMenuChoices(byte[] rom)
{
return ReadPointerTable(rom, 0xB19A64, 0xB198B4);
}
public static MiscStringCollection ReadMiscText(byte[] rom)
{
var miscStrings = ReadPointerTable(rom, 0xB17EE4, 0xB17424);
// Flag basic mode refs
for (int i = 0x4A; i <= 0x55; i++)
miscStrings.StringRefs[i].BasicMode = true;
return miscStrings;
}
public static FixedStringCollection ReadPsiNames(byte[] rom)
{
return ReadFixedStringTable(rom, 0xB1B916, 0x12, 0xD);
}
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace ScriptTool
{
public class MainStringRef
{
[JsonConverter(typeof(JsonHexConverter))]
public int Index { get; set; }
[JsonConverter(typeof(JsonHexConverter))]
public int PointerLocation { get; set; }
[JsonConverter(typeof(JsonHexConverter))]
public int OldPointer { get; set; }
public string Label { get; set; }
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace ScriptTool
{
class MiscStringCollection
{
[JsonConverter(typeof(JsonHexConverter))]
public int OffsetTableLocation { get; set; }
[JsonConverter(typeof(JsonHexConverter))]
public int StringsLocation { get; set; }
public IList<MiscStringRef> StringRefs { get; set; }
}
}

View File

@ -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;
}
}
}

View File

@ -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<ControlCode> EbControlCodes = new List<ControlCode>();
static IList<ControlCode> M12ControlCodes = new List<ControlCode>();
// 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<string>(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<Tuple<string, MainStringRef[]>>();
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<Tuple<string, MainStringRef[]>>();
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()
}
}

View File

@ -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")]

View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{CA5C95AE-2CD2-47E3-B5AE-FE6136808AC4}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ScriptTool</RootNamespace>
<AssemblyName>ScriptTool</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="CommandOptions.cs" />
<Compile Include="ControlCode.cs" />
<Compile Include="DecompileContext.cs" />
<Compile Include="FixedStringCollection.cs" />
<Compile Include="M12TextTables.cs" />
<Compile Include="M12TextDecompiler.cs" />
<Compile Include="EbTextDecompiler.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="JsonHexConverter.cs" />
<Compile Include="LabelMap.cs" />
<Compile Include="MiscStringCollection.cs" />
<Compile Include="FixedStringRef.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="EbTextTables.cs" />
<Compile Include="MiscStringRef.cs" />
<Compile Include="MainStringRef.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="eb-codelist.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="eb-compressed-strings.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="m12-codelist.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="m12-text-table.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -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";

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="net451" />
</packages>