Mother2GbaTranslation/ScriptTool/ScriptTool/Program.cs

502 lines
20 KiB
C#
Raw Normal View History

2015-03-13 22:04:23 +00:00
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
{
2015-03-16 04:16:12 +00:00
// Options
static CommandOptions options;
// ROMs
static byte[] ebRom;
static byte[] m12Rom;
2015-03-13 22:04:23 +00:00
// Decompiler setup
2015-03-22 23:55:34 +00:00
static Decompiler m12Decompiler;
static Decompiler ebDecompiler;
static IDictionary<byte, string> m12CharLookup;
static IDictionary<byte, string> ebCharLookup;
2015-03-16 04:16:12 +00:00
// Compiler setup
2015-03-22 23:55:34 +00:00
static Compiler m12Compiler;
2015-03-16 04:16:12 +00:00
static StreamWriter IncludeFile;
2015-03-13 22:04:23 +00:00
static void Main(string[] args)
{
2015-03-16 04:16:12 +00:00
options = ParseCommandLine(args);
2015-03-13 22:04:23 +00:00
if (options == null)
{
Usage();
return;
}
2015-03-22 23:55:34 +00:00
m12CharLookup = JsonConvert.DeserializeObject<Dictionary<byte, string>>(File.ReadAllText("m12-char-lookup.json"));
ebCharLookup = JsonConvert.DeserializeObject<Dictionary<byte, string>>(File.ReadAllText("eb-char-lookup.json"));
2015-03-13 22:04:23 +00:00
if (options.Command == CommandType.Decompile)
{
2015-03-22 23:55:34 +00:00
// Set up decompilers
m12Decompiler = new Decompiler(M12ControlCode.Codes, m12CharLookup, (rom, address) => rom[address + 1] == 0xFF);
ebDecompiler = new Decompiler(EbControlCode.Codes, ebCharLookup, (rom, address) => rom[address] < 0x20);
2015-03-13 22:04:23 +00:00
// Load ROMs
2015-03-16 04:16:12 +00:00
ebRom = File.ReadAllBytes(options.EbRom);
m12Rom = File.ReadAllBytes(options.M12Rom);
2015-03-13 22:04:23 +00:00
// Decompile misc string tables
if (options.DoMiscText)
{
2015-03-16 04:16:12 +00:00
//DecompileEbMisc();
DecompileM12Misc();
2015-03-13 22:04:23 +00:00
}
// Decompile main string tables
if (options.DoMainText)
{
2015-03-22 23:55:34 +00:00
DecompileEb();
2015-03-16 04:16:12 +00:00
DecompileM12();
2015-03-13 22:04:23 +00:00
}
}
else if (options.Command == CommandType.Compile)
{
2015-03-22 23:55:34 +00:00
// Set up compilers
m12Compiler = new Compiler(M12ControlCode.Codes, (rom, address) => rom[address + 1] == 0xFF);
2015-03-16 04:16:12 +00:00
using (IncludeFile = File.CreateText(Path.Combine(options.WorkingDirectory, "m12-includes.asm")))
{
IncludeFile.WriteLine("arch gba.thumb");
// Compile main string tables
if (options.DoMainText)
{
CompileM12();
}
2015-03-16 04:16:12 +00:00
// Compile misc string tables
if (options.DoMiscText)
{
CompileM12Misc();
}
}
2015-03-13 22:04:23 +00:00
}
}
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
};
}
2015-03-22 23:55:34 +00:00
static void DecompileEb()
2015-03-13 22:04:23 +00:00
{
// Pull all string refs from the ROM
var allRefs = new List<Tuple<string, MainStringRef[]>>();
2015-03-22 23:55:34 +00:00
var tptTuple = EbTextTables.ReadTptRefs(ebRom);
allRefs.Add(Tuple.Create("eb-tpt-primary", tptTuple.Item1));
allRefs.Add(Tuple.Create("eb-tpt-secondary", tptTuple.Item2));
2015-03-13 22:04:23 +00:00
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
2015-03-22 23:55:34 +00:00
var allPointers = allRefs.SelectMany(rl => rl.Item2).Select(r => r.OldPointer);
ebDecompiler.LabelMap.AddRange(allPointers);
IList<int[]> textRanges = new List<int[]>();
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 });
var strings = new List<string>();
foreach (var range in textRanges)
{
ebDecompiler.ScanRange(ebRom, range[0], range[1]);
}
foreach (var range in textRanges)
{
strings.Add(ebDecompiler.DecompileRange(ebRom, range[0], range[1], true));
}
2015-03-13 22:04:23 +00:00
// Update labels for all refs and write to JSON
foreach (var refList in allRefs)
{
foreach (var stringRef in refList.Item2)
2015-03-22 23:55:34 +00:00
stringRef.Label = ebDecompiler.LabelMap.Labels[stringRef.OldPointer];
2015-03-13 22:04:23 +00:00
2015-03-22 23:55:34 +00:00
File.WriteAllText(Path.Combine(options.WorkingDirectory, refList.Item1 + ".json"),
JsonConvert.SerializeObject(refList.Item2, Formatting.Indented));
2015-03-13 22:04:23 +00:00
}
// Write the strings
2015-03-22 23:55:34 +00:00
File.WriteAllText(Path.Combine(options.WorkingDirectory, "eb-strings.txt"), String.Join(Environment.NewLine, strings));
2015-03-13 22:04:23 +00:00
}
2015-03-16 04:16:12 +00:00
static void DecompileM12()
2015-03-13 22:04:23 +00:00
{
// Pull all string refs from the ROM
var allRefs = new List<Tuple<string, MainStringRef[]>>();
2015-03-20 21:40:43 +00:00
var tptTuple = M12TextTables.ReadTptRefs(m12Rom);
allRefs.Add(Tuple.Create("m12-tpt-primary", tptTuple.Item1));
allRefs.Add(Tuple.Create("m12-tpt-secondary", tptTuple.Item2));
2015-03-16 18:44:47 +00:00
allRefs.Add(Tuple.Create("m12-psihelp", M12TextTables.ReadPsiHelpRefs(m12Rom)));
allRefs.Add(Tuple.Create("m12-battle-actions", M12TextTables.ReadBattleActionRefs(m12Rom)));
allRefs.Add(Tuple.Create("m12-itemhelp", M12TextTables.ReadItemHelpRefs(m12Rom)));
allRefs.Add(Tuple.Create("m12-movements", M12TextTables.ReadMovementRefs(m12Rom)));
allRefs.Add(Tuple.Create("m12-objects", M12TextTables.ReadObjectRefs(m12Rom)));
allRefs.Add(Tuple.Create("m12-phonelist", M12TextTables.ReadPhoneRefs(m12Rom)));
allRefs.Add(Tuple.Create("m12-unknown", M12TextTables.ReadUnknownRefs(m12Rom)));
allRefs.Add(Tuple.Create("m12-enemy-encounters", M12TextTables.ReadEnemyEncounters(m12Rom)));
allRefs.Add(Tuple.Create("m12-prayers", M12TextTables.ReadPrayerRefs(m12Rom)));
allRefs.Add(Tuple.Create("m12-asmrefs", M12TextTables.ReadAsmRefs(m12Rom)));
2015-03-13 22:04:23 +00:00
// Decompile
2015-03-16 04:16:12 +00:00
var allPointers = allRefs.SelectMany(rl => rl.Item2).Select(r => r.OldPointer);
m12Decompiler.LabelMap.AddRange(allPointers);
var strings = new List<string>();
m12Decompiler.ScanRange(m12Rom, 0x3697F, 0x8C4B0);
strings.Add(m12Decompiler.DecompileRange(m12Rom, 0x3697F, 0x8C4B0, true));
2015-03-13 22:04:23 +00:00
// Update labels for all refs and write to JSON
foreach (var refList in allRefs)
{
foreach (var stringRef in refList.Item2)
2015-03-16 04:16:12 +00:00
stringRef.Label = m12Decompiler.LabelMap.Labels[stringRef.OldPointer];
2015-03-13 22:04:23 +00:00
2015-03-16 04:16:12 +00:00
File.WriteAllText(Path.Combine(options.WorkingDirectory, refList.Item1 + ".json"),
JsonConvert.SerializeObject(refList.Item2, Formatting.Indented));
2015-03-13 22:04:23 +00:00
}
// Write the strings
2015-03-16 04:16:12 +00:00
File.WriteAllText(Path.Combine(options.WorkingDirectory, "m12-strings.txt"), String.Join(Environment.NewLine, strings));
2015-03-13 22:04:23 +00:00
}
2015-03-16 04:16:12 +00:00
static void DecompileM12Misc()
2015-03-13 22:04:23 +00:00
{
// Item names
var itemNames = M12TextTables.ReadItemNames(m12Rom);
2015-03-16 04:16:12 +00:00
DecompileM12MiscStringCollection("m12-itemnames", itemNames);
2015-03-13 22:04:23 +00:00
// Menu choices
var menuChoices = M12TextTables.ReadMenuChoices(m12Rom);
2015-03-16 04:16:12 +00:00
DecompileM12MiscStringCollection("m12-menuchoices", menuChoices);
2015-03-13 22:04:23 +00:00
// Misc text
var miscText = M12TextTables.ReadMiscText(m12Rom);
2015-03-16 04:16:12 +00:00
DecompileM12MiscStringCollection("m12-misctext", miscText);
2015-03-13 22:04:23 +00:00
2015-03-13 22:59:17 +00:00
// Dad
var dadText = M12TextTables.ReadDadText(m12Rom);
2015-03-16 04:16:12 +00:00
DecompileM12MiscStringCollection("m12-dadtext", dadText);
2015-03-13 22:59:17 +00:00
// PSI text
var psiText = M12TextTables.ReadPsiText(m12Rom);
2015-03-16 04:16:12 +00:00
DecompileM12MiscStringCollection("m12-psitext", psiText);
2015-03-13 22:59:17 +00:00
// Enemy names
var enemyNames = M12TextTables.ReadEnemyNames(m12Rom);
2015-03-16 04:16:12 +00:00
DecompileM12MiscStringCollection("m12-enemynames", enemyNames);
2015-03-13 22:59:17 +00:00
2015-03-13 22:04:23 +00:00
// PSI names
var psiNames = M12TextTables.ReadPsiNames(m12Rom);
2015-03-16 04:16:12 +00:00
DecompileFixedStringCollection(m12Decompiler, m12Rom, "m12-psinames", psiNames);
// PSI targets
var miscText2 = M12TextTables.ReadPsiTargets(m12Rom);
DecompileFixedStringCollection(m12Decompiler, m12Rom, "m12-psitargets", miscText2);
2015-03-13 22:04:23 +00:00
}
2015-03-16 04:16:12 +00:00
static void DecompileM12MiscStringCollection(string name, MiscStringCollection miscStringCollection)
2015-03-13 22:04:23 +00:00
{
// Decompile the strings
foreach (var miscStringRef in miscStringCollection.StringRefs)
{
2015-03-16 04:16:12 +00:00
string decompiledString;
if (miscStringRef.BasicMode)
{
decompiledString = m12Decompiler.ReadFFString(m12Rom, miscStringRef.OldPointer);
}
else
{
decompiledString = m12Decompiler.DecompileString(m12Rom, miscStringRef.OldPointer, false);
}
2015-03-13 22:04:23 +00:00
miscStringRef.Old =
2015-03-16 04:16:12 +00:00
miscStringRef.New = decompiledString;
2015-03-13 22:04:23 +00:00
}
// Write JSON
2015-03-16 04:16:12 +00:00
File.WriteAllText(Path.Combine(options.WorkingDirectory, name + ".json"),
JsonConvert.SerializeObject(miscStringCollection, Formatting.Indented));
2015-03-13 22:04:23 +00:00
}
2015-03-16 04:16:12 +00:00
static void DecompileFixedStringCollection(IDecompiler decompiler, byte[] rom, string name, FixedStringCollection fixedStringCollection)
2015-03-13 22:04:23 +00:00
{
// Decompile the strings
foreach (var fixedStringRef in fixedStringCollection.StringRefs)
{
fixedStringRef.Old =
fixedStringRef.New =
2015-03-16 04:16:12 +00:00
decompiler.DecompileRange(rom, fixedStringRef.OldPointer,
fixedStringRef.OldPointer + fixedStringCollection.EntryLength, false);
2015-03-13 22:04:23 +00:00
}
// Write JSON
2015-03-16 04:16:12 +00:00
File.WriteAllText(Path.Combine(options.WorkingDirectory, name + ".json"),
JsonConvert.SerializeObject(fixedStringCollection, Formatting.Indented));
2015-03-13 22:04:23 +00:00
}
static void CompileM12()
{
int baseAddress = 0xB80000;
int referenceAddress = baseAddress;
var buffer = new List<byte>();
// Get the strings
string m12Strings = File.ReadAllText(Path.Combine(options.WorkingDirectory, "m12-strings-english.txt"));
// Compile
2015-03-22 23:55:34 +00:00
m12Compiler.ScanString(m12Strings, ref referenceAddress, ebCharLookup, false);
referenceAddress = baseAddress;
2015-03-22 23:55:34 +00:00
m12Compiler.CompileString(m12Strings, buffer, ref referenceAddress, ebCharLookup);
File.WriteAllBytes(Path.Combine(options.WorkingDirectory, "m12-main-strings.bin"), buffer.ToArray());
// Update labels
string[] labelFiles = {
2015-03-22 23:55:34 +00:00
"m12-tpt-primary",
"m12-tpt-secondary",
"m12-psihelp",
"m12-battle-actions",
"m12-itemhelp",
"m12-movements",
"m12-objects",
"m12-phonelist",
"m12-unknown",
"m12-asmrefs",
"m12-enemy-encounters",
"m12-prayers"
};
using (var labelAsmFile = File.CreateText(Path.Combine(options.WorkingDirectory, "m12-main-strings.asm")))
{
labelAsmFile.WriteLine(String.Format("org ${0:X}; incbin m12-main-strings.bin", baseAddress | 0x8000000));
labelAsmFile.WriteLine();
foreach (var file in labelFiles)
{
var mainStringRefs = JsonConvert.DeserializeObject<MainStringRef[]>(File.ReadAllText(
Path.Combine(options.WorkingDirectory, file + ".json")));
foreach (var stringRef in mainStringRefs)
{
labelAsmFile.WriteLine(String.Format("org ${0:X}; dd ${1:X8}",
stringRef.PointerLocation | 0x8000000, m12Compiler.AddressMap[stringRef.Label] | 0x8000000));
}
}
}
IncludeFile.WriteLine("incsrc m12-main-strings.asm");
}
2015-03-16 04:16:12 +00:00
static void CompileM12Misc()
{
int referenceAddress = 0xB3C000;
// Item names
CompileM12MiscStringCollection("m12-itemnames", ref referenceAddress);
// Misc text
CompileM12MiscStringCollection("m12-misctext", ref referenceAddress);
// PSI text
CompileM12MiscStringCollection("m12-psitext", ref referenceAddress);
2015-03-16 18:44:47 +00:00
// Enemy names
CompileM12MiscStringCollection("m12-enemynames", ref referenceAddress);
// PSI names
var newPsiPointers = CompileM12FixedStringCollection("m12-psinames", ref referenceAddress);
// Fix pointers to specific PSI strings
int psiPointer = newPsiPointers[1];
int[] updateAddresses = {
0xC21AC,
0xC2364,
0xC2420,
0xC24DC,
0xD3998
};
IncludeFile.WriteLine();
IncludeFile.WriteLine("// Fix pointers to \"PSI \"");
foreach (var address in updateAddresses)
{
IncludeFile.WriteLine(String.Format("org ${0:X}; dd ${1:X8}",
address | 0x8000000, psiPointer | 0x8000000));
}
// PSI targets
CompileM12FixedStringCollection("m12-psitargets", ref referenceAddress);
2015-03-16 04:16:12 +00:00
}
static void CompileM12MiscStringCollection(string name, ref int referenceAddress)
{
int baseAddress = referenceAddress;
var buffer = new List<byte>();
// Read the JSON
MiscStringCollection stringCollection = JsonConvert.DeserializeObject<MiscStringCollection>(
File.ReadAllText(Path.Combine(options.WorkingDirectory, name + ".json")));
// Open the offset ASM file
using (var offsetFile = File.CreateText(Path.Combine(options.WorkingDirectory, name + ".asm")))
{
// Include the binfile
offsetFile.WriteLine(String.Format("org ${0:X}; incbin {1}.bin",
baseAddress | 0x8000000, name));
offsetFile.WriteLine();
// Compile all strings
foreach (var str in stringCollection.StringRefs.OrderBy(s => s.Index))
2015-03-16 04:16:12 +00:00
{
offsetFile.WriteLine(String.Format("org ${0:X}; dd ${1:X8}",
str.OffsetLocation | 0x8000000, referenceAddress - stringCollection.StringsLocation));
2015-03-22 23:55:34 +00:00
m12Compiler.CompileString(str.New, buffer, ref referenceAddress, ebCharLookup);
2015-03-16 04:16:12 +00:00
}
}
// Write the buffer
File.WriteAllBytes(Path.Combine(options.WorkingDirectory, name + ".bin"), buffer.ToArray());
// Add to the include file
IncludeFile.WriteLine("incsrc " + name + ".asm");
}
static IList<int> CompileM12FixedStringCollection(string name, ref int referenceAddress)
{
int baseAddress = referenceAddress;
var buffer = new List<byte>();
var newPointers = new List<int>();
// Read the JSON
FixedStringCollection stringCollection = JsonConvert.DeserializeObject<FixedStringCollection>(
File.ReadAllText(Path.Combine(options.WorkingDirectory, name + ".json")));
// Open the data ASM file
using (var offsetFile = File.CreateText(Path.Combine(options.WorkingDirectory, name + ".asm")))
{
// Include the binfile
offsetFile.WriteLine(String.Format("org ${0:X}; incbin {1}.bin",
baseAddress | 0x8000000, name));
offsetFile.WriteLine();
// Update table pointers
foreach (int tablePointer in stringCollection.TablePointers)
{
offsetFile.WriteLine(String.Format("org ${0:X}; dd ${1:X8}",
tablePointer | 0x8000000, baseAddress | 0x8000000));
}
// Compile all strings
foreach (var str in stringCollection.StringRefs.OrderBy(s => s.Index))
{
newPointers.Add(referenceAddress);
2015-03-22 23:55:34 +00:00
m12Compiler.CompileString(str.New, buffer, ref referenceAddress, ebCharLookup, stringCollection.EntryLength);
}
}
// Write the buffer
File.WriteAllBytes(Path.Combine(options.WorkingDirectory, name + ".bin"), buffer.ToArray());
// Add to the include file
IncludeFile.WriteLine("incsrc " + name + ".asm");
return newPointers;
}
2015-03-13 22:04:23 +00:00
}
}