ssc/sbdump/main.cs
arookas a4e0af193e Updated arookas library
Now it uses the public repository, making syncing updates much easier.
2016-02-12 22:53:18 -05:00

279 lines
8.3 KiB
C#

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;
static uint sTextOffset, sDataOffset, sDynsymOffset;
static int sDataCount, sDynsymCount, sBssCount;
const string cTitle = "sbdump v0.1 arookas";
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) {
#if !DEBUG
try {
#endif
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();
}
if (sSettings.OutputDynsym) {
WriteDynsym();
}
if (sSettings.OutputBss) {
WriteBss();
}
Console.WriteLine("Closing output file...");
}
Console.WriteLine("Closing input file...");
}
Console.WriteLine("Done.");
#if !DEBUG
}
catch (Exception e) {
Console.WriteLine(e.Message);
return 1;
}
#endif
return 0;
}
static void ReadCommandLine(string[] args) {
Console.WriteLine("Reading command line...");
sSettings = new CommandLineSettings(new aCommandLine(args));
}
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();
sDynsymOffset = sReader.Read32();
sDynsymCount = sReader.ReadS32();
sBssCount = sReader.ReadS32();
}
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);
sWriter.WriteLine("# .dynsym offset : {0:X8}", sDynsymOffset);
sWriter.WriteLine("# .dynsym count : {0}", sDynsymCount);
sWriter.WriteLine("# .bss count : {0}", sBssCount);
sWriter.WriteLine();
}
static void WriteText() {
Console.WriteLine("Outputting .text...");
sWriter.WriteLine(".text");
WriteText(0u);
var symbols = new Symbol[sDynsymCount];
for (var i = 0; i < sDynsymCount; ++i) {
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);
do {
var pos = sReader.Position - sTextOffset;
command = sReader.Read8();
sWriter.Write(" {0:X8} {1}", pos, sCommandNames[command]);
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;
case 0x04: WriteVar(); break;
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;
case 0x22: WriteJmp(ofs); break;
case 0x23: WriteJmp(ofs); break;
}
sWriter.WriteLine();
} while (command != 0x21 && command != 0x27);
sWriter.WriteLine();
sReader.Back();
}
static void WriteVar() {
var display = sReader.ReadS32();
var data = sReader.ReadS32();
sWriter.Write(" {0} {1}", display, data);
switch (display) {
case 0: sWriter.Write(" # {0}", FetchSymbolName(FetchSymbol(i => i.Type == SymbolType.Variable && i.Data == data))); break;
case 1: sWriter.Write(" # local{0}", data); break;
}
}
static uint WriteJmp(uint ofs) {
var dest = sReader.Read32();
var symbol = FetchSymbol(i => i.Data == ofs);
if (ofs > 0 && symbol != null) {
var name = FetchSymbolName(symbol);
sWriter.Write(" {0} + ${1:X4} # ${2:X8}", name, dest - ofs, dest);
}
else {
sWriter.Write(" ${0:X8}", dest);
}
return dest;
}
static void WriteData() {
Console.WriteLine("Outputting .data...");
sWriter.WriteLine(".data");
sReader.Goto(sDataOffset);
for (int i = 0; i < sDataCount; ++i) {
var ofs = sReader.Read32();
var data = FetchDataValue(ofs);
sWriter.WriteLine(" .string \"{0}\"", data);
}
sWriter.WriteLine();
}
static void WriteDynsym() {
Console.WriteLine("Outputting .dynsym...");
sWriter.WriteLine(".dynsym");
sReader.Goto(sDynsymOffset);
for (int i = 0; i < sDynsymCount; ++i) {
var symbol = new Symbol(sReader);
var name = FetchSymbolName(symbol);
sWriter.WriteLine(" .{0} {1}", sSymbolTypes[(int)symbol.Type], name);
}
sWriter.WriteLine();
}
static void WriteBss() {
Console.WriteLine("Outputting .bss...");
sWriter.WriteLine(".bss");
for (int i = 0; i < sBssCount; ++i) {
var symbol = FetchSymbol(j => j.Type == SymbolType.Variable && j.Data == i);
if (symbol != null) {
sWriter.WriteLine(" .var {0}", FetchSymbolName(symbol));
}
else {
sWriter.WriteLine(" .var");
}
}
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) {
return "null";
}
return FetchDataValue(FetchData(i));
}
static string FetchDataValue(uint ofs) {
sReader.Keep();
sReader.Goto(sDataOffset + (4 * sDataCount) + ofs);
var data = sReader.ReadString<aZSTR>();
sReader.Back();
return data;
}
static Symbol FetchSymbol(int i) {
sReader.Keep();
sReader.Goto(sDynsymOffset + (20 * i));
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();
sReader.Goto(sDynsymOffset);
for (int i = 0; i < sDynsymCount; ++i) {
var symbol = new Symbol(sReader);
if (predicate(symbol)) {
found = symbol;
break;
}
}
sReader.Back();
return found;
}
static string FetchSymbolName(Symbol symbol) {
sReader.Keep();
sReader.Goto(sDynsymOffset + (20 * sDynsymCount) + symbol.StringOffset);
var name = sReader.ReadString<aZSTR>();
sReader.Back();
return name;
}
}
}