2015-12-31 12:27:03 +09:00
|
|
|
|
using arookas.IO.Binary;
|
|
|
|
|
using System;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
|
|
|
|
namespace arookas {
|
|
|
|
|
class sbdump {
|
|
|
|
|
static CommandLineSettings sSettings;
|
|
|
|
|
static aBinaryReader sReader;
|
|
|
|
|
static TextWriter sWriter;
|
2016-03-02 16:11:40 +09:00
|
|
|
|
static uint sTextOffset, sDataOffset, sSymOffset;
|
|
|
|
|
static int sDataCount, sSymCount, sVarCount;
|
2015-12-31 12:27:03 +09:00
|
|
|
|
|
2016-03-02 16:01:52 +09:00
|
|
|
|
const string cTitle = "sbdump arookas";
|
2015-12-31 12:27:03 +09:00
|
|
|
|
static readonly string[] sSymbolTypes = { "builtin", "function", "var", };
|
|
|
|
|
static readonly string[] sCommandNames = {
|
|
|
|
|
"int", "flt", "str", "adr", "var", "nop", "inc", "dec",
|
|
|
|
|
"add", "sub", "mul", "div", "mod", "ass", "eq", "ne",
|
|
|
|
|
"gt", "lt", "ge", "le", "neg", "not", "and", "or",
|
|
|
|
|
"band", "bor", "shl", "shr", "call", "func", "mkfr", "mkds",
|
|
|
|
|
"ret", "ret0", "jne", "jmp", "pop", "int0", "int1", "end",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int Main(string[] args) {
|
2016-02-01 14:28:03 +09:00
|
|
|
|
#if !DEBUG
|
2015-12-31 12:27:03 +09:00
|
|
|
|
try {
|
2016-02-01 14:28:03 +09:00
|
|
|
|
#endif
|
2015-12-31 12:27:03 +09:00
|
|
|
|
Console.WriteLine(cTitle);
|
|
|
|
|
ReadCommandLine(args);
|
|
|
|
|
Console.WriteLine("Opening input file...");
|
|
|
|
|
using (var sb = File.OpenRead(sSettings.Input)) {
|
|
|
|
|
CreateReader(sb);
|
|
|
|
|
Console.WriteLine("Creating output file...");
|
|
|
|
|
using (sWriter = File.CreateText(sSettings.Output)) {
|
|
|
|
|
ReadHeader();
|
|
|
|
|
WritePreamble();
|
|
|
|
|
if (sSettings.OutputHeader) {
|
|
|
|
|
WriteHeader();
|
|
|
|
|
}
|
|
|
|
|
if (sSettings.OutputText) {
|
|
|
|
|
WriteText();
|
|
|
|
|
}
|
|
|
|
|
if (sSettings.OutputData) {
|
|
|
|
|
WriteData();
|
|
|
|
|
}
|
2016-03-02 16:02:53 +09:00
|
|
|
|
if (sSettings.OutputSym) {
|
|
|
|
|
WriteSym();
|
2015-12-31 12:27:03 +09:00
|
|
|
|
}
|
2016-03-02 16:44:47 +09:00
|
|
|
|
if (sSettings.OutputVars) {
|
|
|
|
|
WriteVars();
|
2015-12-31 12:27:03 +09:00
|
|
|
|
}
|
|
|
|
|
Console.WriteLine("Closing output file...");
|
|
|
|
|
}
|
|
|
|
|
Console.WriteLine("Closing input file...");
|
|
|
|
|
}
|
|
|
|
|
Console.WriteLine("Done.");
|
2016-02-01 14:28:03 +09:00
|
|
|
|
#if !DEBUG
|
2015-12-31 12:27:03 +09:00
|
|
|
|
}
|
|
|
|
|
catch (Exception e) {
|
|
|
|
|
Console.WriteLine(e.Message);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2016-02-01 14:28:03 +09:00
|
|
|
|
#endif
|
|
|
|
|
return 0;
|
2015-12-31 12:27:03 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ReadCommandLine(string[] args) {
|
|
|
|
|
Console.WriteLine("Reading command line...");
|
2016-02-13 12:53:18 +09:00
|
|
|
|
sSettings = new CommandLineSettings(new aCommandLine(args));
|
2015-12-31 12:27:03 +09:00
|
|
|
|
}
|
|
|
|
|
static void CreateReader(Stream stream) {
|
|
|
|
|
Console.WriteLine("Creating binary reader...");
|
|
|
|
|
sReader = new aBinaryReader(stream, Endianness.Big, Encoding.GetEncoding(932));
|
|
|
|
|
}
|
|
|
|
|
static void WritePreamble() {
|
|
|
|
|
Console.WriteLine("Writing preamble...");
|
|
|
|
|
sWriter.WriteLine("# {0}", cTitle);
|
|
|
|
|
sWriter.WriteLine("# {0}", DateTime.Now);
|
|
|
|
|
sWriter.WriteLine("# {0}", Path.GetFileName(sSettings.Input));
|
|
|
|
|
sWriter.WriteLine();
|
|
|
|
|
}
|
|
|
|
|
static void ReadHeader() {
|
|
|
|
|
Console.WriteLine("Reading header...");
|
|
|
|
|
if (sReader.Read32() != 0x53504342u) { // 'SPCB'
|
|
|
|
|
throw new Exception("Invalid magic.");
|
|
|
|
|
}
|
|
|
|
|
sTextOffset = sReader.Read32();
|
|
|
|
|
sDataOffset = sReader.Read32();
|
|
|
|
|
sDataCount = sReader.ReadS32();
|
2016-03-02 16:11:40 +09:00
|
|
|
|
sSymOffset = sReader.Read32();
|
|
|
|
|
sSymCount = sReader.ReadS32();
|
|
|
|
|
sVarCount = sReader.ReadS32();
|
2015-12-31 12:27:03 +09:00
|
|
|
|
}
|
|
|
|
|
static void WriteHeader() {
|
|
|
|
|
Console.WriteLine("Outputting header...");
|
|
|
|
|
sWriter.WriteLine("# Header information");
|
|
|
|
|
sWriter.WriteLine("# .text offset : {0:X8}", sTextOffset);
|
|
|
|
|
sWriter.WriteLine("# .data offset : {0:X8}", sDataOffset);
|
|
|
|
|
sWriter.WriteLine("# .data count : {0}", sDataCount);
|
2016-03-02 16:11:40 +09:00
|
|
|
|
sWriter.WriteLine("# .sym offset : {0:X8}", sSymOffset);
|
|
|
|
|
sWriter.WriteLine("# .sym count : {0}", sSymCount);
|
|
|
|
|
sWriter.WriteLine("# var count : {0}", sVarCount);
|
2015-12-31 12:27:03 +09:00
|
|
|
|
sWriter.WriteLine();
|
|
|
|
|
}
|
|
|
|
|
static void WriteText() {
|
|
|
|
|
Console.WriteLine("Outputting .text...");
|
|
|
|
|
sWriter.WriteLine(".text");
|
|
|
|
|
WriteText(0u);
|
2016-03-02 16:11:40 +09:00
|
|
|
|
var symbols = new Symbol[sSymCount];
|
|
|
|
|
for (var i = 0; i < sSymCount; ++i) {
|
2015-12-31 12:27:03 +09:00
|
|
|
|
symbols[i] = FetchSymbol(i);
|
|
|
|
|
}
|
|
|
|
|
foreach (var symbol in symbols.Where(i => i.Type == SymbolType.Function).OrderBy(i => i.Data)) {
|
|
|
|
|
sWriter.WriteLine("{0}: ", FetchSymbolName(symbol));
|
|
|
|
|
WriteText(symbol.Data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
static void WriteText(uint ofs) {
|
|
|
|
|
byte command;
|
|
|
|
|
sReader.Keep();
|
|
|
|
|
sReader.Goto(sTextOffset + ofs);
|
2016-03-02 16:46:13 +09:00
|
|
|
|
var maxofs = 0u;
|
2015-12-31 12:27:03 +09:00
|
|
|
|
do {
|
|
|
|
|
var pos = sReader.Position - sTextOffset;
|
|
|
|
|
command = sReader.Read8();
|
|
|
|
|
sWriter.Write(" {0:X8} {1}", pos, sCommandNames[command]);
|
2016-03-02 16:46:13 +09:00
|
|
|
|
var nextofs = 0u;
|
2015-12-31 12:27:03 +09:00
|
|
|
|
switch (command) {
|
|
|
|
|
case 0x00: sWriter.Write(" {0}", sReader.ReadS32()); break;
|
|
|
|
|
case 0x01: sWriter.Write(" {0}", sReader.ReadF32()); break;
|
|
|
|
|
case 0x02: {
|
|
|
|
|
var data = sReader.ReadS32();
|
|
|
|
|
var value = FetchDataValue(data);
|
|
|
|
|
sWriter.Write(" {0} # \"{1}\"", data, value);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 0x03: sWriter.Write(" ${0:X8}", sReader.Read32()); break;
|
2016-01-23 15:15:50 +09:00
|
|
|
|
case 0x04: WriteVar(); break;
|
2015-12-31 12:27:03 +09:00
|
|
|
|
case 0x06: WriteVar(); break;
|
|
|
|
|
case 0x07: WriteVar(); break;
|
|
|
|
|
case 0x0D: {
|
|
|
|
|
sReader.Read8(); // TSpcInterp skips this byte
|
|
|
|
|
WriteVar();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 0x1C: {
|
|
|
|
|
var dest = sReader.Read32();
|
|
|
|
|
var args = sReader.ReadS32();
|
|
|
|
|
var symbol = FetchSymbol(i => i.Data == dest);
|
|
|
|
|
if (symbol != null) {
|
|
|
|
|
sWriter.Write(" {0}, {1}", FetchSymbolName(symbol), args);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
sWriter.Write(" ${0:X8}, {1}", dest, args);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 0x1D: sWriter.Write(" {0}, {1}", FetchSymbolName(FetchSymbol(sReader.ReadS32())), sReader.ReadS32()); break;
|
|
|
|
|
case 0x1E: sWriter.Write(" {0}", sReader.ReadS32()); break;
|
|
|
|
|
case 0x1F: sWriter.Write(" {0}", sReader.ReadS32()); break;
|
2016-03-02 16:46:13 +09:00
|
|
|
|
case 0x22: nextofs = WriteJmp(ofs); break;
|
|
|
|
|
case 0x23: nextofs = WriteJmp(ofs); break;
|
2015-12-31 12:27:03 +09:00
|
|
|
|
}
|
|
|
|
|
sWriter.WriteLine();
|
2016-03-02 16:46:13 +09:00
|
|
|
|
if (nextofs > maxofs) {
|
|
|
|
|
maxofs = nextofs;
|
|
|
|
|
}
|
|
|
|
|
} while (!IsReturnCommand(command) || sReader.Position <= sTextOffset + maxofs);
|
2015-12-31 12:27:03 +09:00
|
|
|
|
sWriter.WriteLine();
|
|
|
|
|
sReader.Back();
|
|
|
|
|
}
|
|
|
|
|
static void WriteVar() {
|
|
|
|
|
var display = sReader.ReadS32();
|
2016-01-23 14:58:56 +09:00
|
|
|
|
var data = sReader.ReadS32();
|
|
|
|
|
sWriter.Write(" {0} {1}", display, data);
|
2015-12-31 12:27:03 +09:00
|
|
|
|
switch (display) {
|
2016-01-23 14:58:56 +09:00
|
|
|
|
case 0: sWriter.Write(" # {0}", FetchSymbolName(FetchSymbol(i => i.Type == SymbolType.Variable && i.Data == data))); break;
|
|
|
|
|
case 1: sWriter.Write(" # local{0}", data); break;
|
2015-12-31 12:27:03 +09:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-01 18:41:06 +09:00
|
|
|
|
static uint WriteJmp(uint ofs) {
|
2015-12-31 12:27:03 +09:00
|
|
|
|
var dest = sReader.Read32();
|
|
|
|
|
var symbol = FetchSymbol(i => i.Data == ofs);
|
|
|
|
|
if (ofs > 0 && symbol != null) {
|
|
|
|
|
var name = FetchSymbolName(symbol);
|
2016-01-23 15:36:27 +09:00
|
|
|
|
sWriter.Write(" {0} + ${1:X4} # ${2:X8}", name, dest - ofs, dest);
|
2015-12-31 12:27:03 +09:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
sWriter.Write(" ${0:X8}", dest);
|
|
|
|
|
}
|
2016-02-01 18:41:06 +09:00
|
|
|
|
return dest;
|
2015-12-31 12:27:03 +09:00
|
|
|
|
}
|
|
|
|
|
static void WriteData() {
|
|
|
|
|
Console.WriteLine("Outputting .data...");
|
|
|
|
|
sWriter.WriteLine(".data");
|
|
|
|
|
sReader.Goto(sDataOffset);
|
2016-03-02 16:45:06 +09:00
|
|
|
|
for (var i = 0; i < sDataCount; ++i) {
|
2015-12-31 12:27:03 +09:00
|
|
|
|
var ofs = sReader.Read32();
|
|
|
|
|
var data = FetchDataValue(ofs);
|
|
|
|
|
sWriter.WriteLine(" .string \"{0}\"", data);
|
|
|
|
|
}
|
|
|
|
|
sWriter.WriteLine();
|
|
|
|
|
}
|
2016-03-02 16:02:53 +09:00
|
|
|
|
static void WriteSym() {
|
|
|
|
|
Console.WriteLine("Outputting .sym...");
|
|
|
|
|
sWriter.WriteLine(".sym");
|
2016-03-02 16:11:40 +09:00
|
|
|
|
sReader.Goto(sSymOffset);
|
2016-03-02 16:45:06 +09:00
|
|
|
|
for (var i = 0; i < sSymCount; ++i) {
|
2015-12-31 12:27:03 +09:00
|
|
|
|
var symbol = new Symbol(sReader);
|
|
|
|
|
var name = FetchSymbolName(symbol);
|
|
|
|
|
sWriter.WriteLine(" .{0} {1}", sSymbolTypes[(int)symbol.Type], name);
|
|
|
|
|
}
|
|
|
|
|
sWriter.WriteLine();
|
|
|
|
|
}
|
2016-03-02 16:44:47 +09:00
|
|
|
|
static void WriteVars() {
|
|
|
|
|
Console.WriteLine("Outputting variables...");
|
|
|
|
|
sWriter.WriteLine("# variables:");
|
2016-03-02 16:45:06 +09:00
|
|
|
|
for (var i = 0; i < sVarCount; ++i) {
|
2015-12-31 12:27:03 +09:00
|
|
|
|
var symbol = FetchSymbol(j => j.Type == SymbolType.Variable && j.Data == i);
|
|
|
|
|
if (symbol != null) {
|
2016-03-02 16:44:47 +09:00
|
|
|
|
sWriter.WriteLine("# {0}", FetchSymbolName(symbol));
|
2015-12-31 12:27:03 +09:00
|
|
|
|
}
|
|
|
|
|
else {
|
2016-03-02 16:44:47 +09:00
|
|
|
|
sWriter.WriteLine("# (NULL)");
|
2015-12-31 12:27:03 +09:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sWriter.WriteLine();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint FetchData(int i) {
|
|
|
|
|
sReader.Keep();
|
|
|
|
|
sReader.Goto(sDataOffset + (4 * i));
|
|
|
|
|
var data = sReader.Read32();
|
|
|
|
|
sReader.Back();
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
static string FetchDataValue(int i) {
|
|
|
|
|
if (i < 0 || i >= sDataCount) {
|
2016-03-02 16:44:47 +09:00
|
|
|
|
return "(NULL)";
|
2015-12-31 12:27:03 +09:00
|
|
|
|
}
|
|
|
|
|
return FetchDataValue(FetchData(i));
|
|
|
|
|
}
|
|
|
|
|
static string FetchDataValue(uint ofs) {
|
|
|
|
|
sReader.Keep();
|
|
|
|
|
sReader.Goto(sDataOffset + (4 * sDataCount) + ofs);
|
2016-02-13 12:53:18 +09:00
|
|
|
|
var data = sReader.ReadString<aZSTR>();
|
2015-12-31 12:27:03 +09:00
|
|
|
|
sReader.Back();
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
static Symbol FetchSymbol(int i) {
|
|
|
|
|
sReader.Keep();
|
2016-03-02 16:11:40 +09:00
|
|
|
|
sReader.Goto(sSymOffset + (20 * i));
|
2015-12-31 12:27:03 +09:00
|
|
|
|
var symbol = new Symbol(sReader);
|
|
|
|
|
sReader.Back();
|
|
|
|
|
return symbol;
|
|
|
|
|
}
|
|
|
|
|
static Symbol FetchSymbol(Predicate<Symbol> predicate) {
|
|
|
|
|
if (predicate == null) {
|
|
|
|
|
throw new ArgumentNullException("predicate");
|
|
|
|
|
}
|
|
|
|
|
Symbol found = null;
|
|
|
|
|
sReader.Keep();
|
2016-03-02 16:11:40 +09:00
|
|
|
|
sReader.Goto(sSymOffset);
|
2016-03-02 16:45:06 +09:00
|
|
|
|
for (var i = 0; i < sSymCount; ++i) {
|
2015-12-31 12:27:03 +09:00
|
|
|
|
var symbol = new Symbol(sReader);
|
|
|
|
|
if (predicate(symbol)) {
|
|
|
|
|
found = symbol;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sReader.Back();
|
|
|
|
|
return found;
|
|
|
|
|
}
|
|
|
|
|
static string FetchSymbolName(Symbol symbol) {
|
|
|
|
|
sReader.Keep();
|
2016-03-02 16:11:40 +09:00
|
|
|
|
sReader.Goto(sSymOffset + (20 * sSymCount) + symbol.StringOffset);
|
2016-02-13 12:53:18 +09:00
|
|
|
|
var name = sReader.ReadString<aZSTR>();
|
2015-12-31 12:27:03 +09:00
|
|
|
|
sReader.Back();
|
|
|
|
|
return name;
|
|
|
|
|
}
|
2016-03-02 16:46:13 +09:00
|
|
|
|
|
|
|
|
|
static bool IsReturnCommand(byte cmd) {
|
|
|
|
|
return (
|
|
|
|
|
cmd == 0x20 || // ret
|
|
|
|
|
cmd == 0x21 || // ret0
|
|
|
|
|
cmd == 0x27 // end
|
|
|
|
|
);
|
|
|
|
|
}
|
2015-12-31 12:27:03 +09:00
|
|
|
|
}
|
|
|
|
|
}
|