First version of decompiler option
This commit is contained in:
parent
33fb3886de
commit
9b37f0273a
2 changed files with 501 additions and 38 deletions
525
sbdump/main.cs
525
sbdump/main.cs
|
@ -3,6 +3,7 @@ using System;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace arookas {
|
||||
class sbdump {
|
||||
|
@ -22,38 +23,76 @@ namespace arookas {
|
|||
"ret", "ret0", "jne", "jmp", "pop", "int0", "int1", "end",
|
||||
};
|
||||
|
||||
static int Main(string[] args) {
|
||||
public struct CodeLine
|
||||
{
|
||||
public string Text;
|
||||
public long Address;
|
||||
|
||||
|
||||
public CodeLine(string txt, long addr)
|
||||
{
|
||||
Text = txt;
|
||||
Address = addr;
|
||||
}
|
||||
}
|
||||
|
||||
public struct JumpStatement
|
||||
{
|
||||
public long StartAddress;
|
||||
public long EndAddress;
|
||||
public JumpStatement(long addr1, long addr2)
|
||||
{
|
||||
StartAddress = addr1;
|
||||
EndAddress = addr2;
|
||||
}
|
||||
}
|
||||
|
||||
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.OutputSym) {
|
||||
WriteSym();
|
||||
}
|
||||
if (sSettings.OutputVars) {
|
||||
WriteVars();
|
||||
}
|
||||
Console.WriteLine("Closing output file...");
|
||||
}
|
||||
Console.WriteLine("Closing input file...");
|
||||
}
|
||||
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();
|
||||
if (sSettings.OutputSun)
|
||||
{
|
||||
WriteSun();
|
||||
}
|
||||
else
|
||||
{
|
||||
WritePreamble();
|
||||
if (sSettings.OutputHeader)
|
||||
{
|
||||
WriteHeader();
|
||||
}
|
||||
if (sSettings.OutputText)
|
||||
{
|
||||
WriteText();
|
||||
}
|
||||
if (sSettings.OutputData)
|
||||
{
|
||||
WriteData();
|
||||
}
|
||||
if (sSettings.OutputSym)
|
||||
{
|
||||
WriteSym();
|
||||
}
|
||||
if (sSettings.OutputVars)
|
||||
{
|
||||
WriteVars();
|
||||
}
|
||||
}
|
||||
Console.WriteLine("Closing output file...");
|
||||
}
|
||||
Console.WriteLine("Closing input file...");
|
||||
}
|
||||
Console.WriteLine("Done.");
|
||||
#if !DEBUG
|
||||
}
|
||||
|
@ -65,7 +104,8 @@ namespace arookas {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ReadCommandLine(string[] args) {
|
||||
static void ReadCommandLine(string[] args)
|
||||
{
|
||||
Console.WriteLine("Reading command line...");
|
||||
sSettings = new CommandLineSettings(new aCommandLine(args));
|
||||
}
|
||||
|
@ -92,7 +132,8 @@ namespace arookas {
|
|||
sSymCount = sReader.ReadS32();
|
||||
sVarCount = sReader.ReadS32();
|
||||
}
|
||||
static void WriteHeader() {
|
||||
static void WriteHeader()
|
||||
{
|
||||
Console.WriteLine("Outputting header...");
|
||||
sWriter.WriteLine("# Header information");
|
||||
sWriter.WriteLine("# .text offset : {0:X8}", sTextOffset);
|
||||
|
@ -103,15 +144,19 @@ namespace arookas {
|
|||
sWriter.WriteLine("# var count : {0}", sVarCount);
|
||||
sWriter.WriteLine();
|
||||
}
|
||||
static void WriteText() {
|
||||
static void WriteText()
|
||||
{
|
||||
Console.WriteLine("Outputting .text...");
|
||||
sWriter.WriteLine(".text");
|
||||
WriteText(0u);
|
||||
var symbols = new Symbol[sSymCount];
|
||||
for (var i = 0; i < sSymCount; ++i) {
|
||||
for (var i = 0; i < sSymCount; ++i)
|
||||
{
|
||||
symbols[i] = FetchSymbol(i);
|
||||
}
|
||||
foreach (var symbol in symbols.Where(i => i.Type == SymbolType.Function).OrderBy(i => i.Data)) {
|
||||
|
||||
foreach (var symbol in symbols.Where(i => i.Type == SymbolType.Function).OrderBy(i => i.Data))
|
||||
{
|
||||
sWriter.WriteLine("{0}: ", FetchSymbolName(symbol));
|
||||
WriteText(symbol.Data);
|
||||
}
|
||||
|
@ -228,7 +273,417 @@ namespace arookas {
|
|||
sWriter.WriteLine();
|
||||
}
|
||||
|
||||
static uint FetchData(int i) {
|
||||
static void WriteSun()
|
||||
{
|
||||
Console.WriteLine("Decompiling sb file...");
|
||||
DecompFunction(0u);//Decompile main part.
|
||||
var symbols = new Symbol[sSymCount];
|
||||
for (var i = 0; i < sSymCount; ++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));
|
||||
DecompFunction(symbol.Data);
|
||||
}
|
||||
}
|
||||
|
||||
static void DecompFunction(uint ofs)
|
||||
{
|
||||
byte command;
|
||||
sReader.Keep();
|
||||
sReader.Goto(sTextOffset + ofs);
|
||||
var maxofs = 0u;
|
||||
Stack<string> Stack = new Stack<string>();
|
||||
List<CodeLine> DecompiledCode = new List<CodeLine>();
|
||||
List<JumpStatement> IfStatements = new List<JumpStatement>();
|
||||
List<JumpStatement> JmpStatements = new List<JumpStatement>();
|
||||
do
|
||||
{
|
||||
var pos = sReader.Position - sTextOffset;
|
||||
command = sReader.Read8();
|
||||
var nextofs = 0u;
|
||||
switch (command)
|
||||
{
|
||||
case 0x00: //int
|
||||
{
|
||||
Stack.Push(sReader.ReadS32().ToString());
|
||||
break;
|
||||
}
|
||||
case 0x01: //flt
|
||||
{
|
||||
Stack.Push(sReader.ReadF32().ToString());
|
||||
break;
|
||||
}
|
||||
case 0x02: //str
|
||||
{
|
||||
|
||||
var data = sReader.ReadS32();
|
||||
var value = FetchDataValue(data);
|
||||
Stack.Push("\"" + value +"\"");
|
||||
break;
|
||||
}
|
||||
case 0x03: //adr
|
||||
{
|
||||
Stack.Push(sReader.ReadS32().ToString("X8"));
|
||||
break;
|
||||
}
|
||||
case 0x04: //var
|
||||
{
|
||||
var display = sReader.ReadS32();
|
||||
var data = sReader.ReadS32();
|
||||
switch (display)
|
||||
{
|
||||
case 0: Stack.Push(FetchSymbolName(FetchSymbol(i => i.Type == SymbolType.Variable && i.Data == data))); break;
|
||||
case 1: Stack.Push("local" + data.ToString()); break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x06: //inc
|
||||
{
|
||||
string Op = Stack.Pop();
|
||||
Stack.Push("(" + Op + "+1)");
|
||||
break;
|
||||
}
|
||||
case 0x07: //dec
|
||||
{
|
||||
string Op = Stack.Pop();
|
||||
Stack.Push("(" + Op + "-1)");
|
||||
break;
|
||||
}
|
||||
case 0x08: //add
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " + " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x09: //sub
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " - " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x0A: //mul
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " * " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x0B: //div
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " / " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x0C: //mod
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " % " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x0D: //ass
|
||||
{
|
||||
sReader.Read8(); //Ignore this byte
|
||||
var display = sReader.ReadS32();
|
||||
var data = sReader.ReadS32();
|
||||
string VariableName = "";
|
||||
switch (display)
|
||||
{
|
||||
case 0: VariableName = FetchSymbolName(FetchSymbol(i => i.Type == SymbolType.Variable && i.Data == data)); break;
|
||||
case 1: VariableName = "local" + data.ToString(); break;
|
||||
}
|
||||
CodeLine NewLine = new CodeLine(VariableName + " = " + Stack.Pop() + ";", pos);
|
||||
DecompiledCode.Add(NewLine);
|
||||
break;
|
||||
}
|
||||
case 0x0E: //eq
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " == " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x0F: //ne
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " != " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x10: //gt
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " > " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x11: //lt
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " < " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x12: //ge
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " >= " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x13: //le
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " <= " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x14: //neg
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
Stack.Push("-(" + Op1 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x15: //not
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
Stack.Push("!(" + Op1 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x16: //and
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " && " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x17: //or
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " || " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x18: //band
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " & " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x19: //bor
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " ^ " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x1A: //shl
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " << " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x1B: //shr
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " >> " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x1C: //call
|
||||
{
|
||||
var dest = sReader.Read32();
|
||||
var args = sReader.ReadS32();
|
||||
var symbol = FetchSymbol(i => i.Data == dest);
|
||||
string FuncName = "";
|
||||
if (symbol != null)
|
||||
{
|
||||
FuncName = FetchSymbolName(symbol);
|
||||
}
|
||||
else
|
||||
{
|
||||
FuncName = dest.ToString("X8");
|
||||
}
|
||||
string FuncInput = "";
|
||||
for(int i = 0; i < args; i++)
|
||||
{
|
||||
string Op = Stack.Pop();
|
||||
if (i != 0)
|
||||
FuncInput = Op + "," + FuncInput;
|
||||
else
|
||||
FuncInput = Op;
|
||||
}
|
||||
Stack.Push(FuncName + "(" + FuncInput + ")");
|
||||
break;
|
||||
}
|
||||
case 0x1D: //func
|
||||
{
|
||||
string FuncName = FetchSymbolName(FetchSymbol(sReader.ReadS32()));
|
||||
var args = sReader.ReadS32();
|
||||
string FuncInput = "";
|
||||
for (int i = 0; i < args; i++)
|
||||
{
|
||||
string Op = Stack.Pop();
|
||||
if (i != 0)
|
||||
FuncInput = Op + "," + FuncInput;
|
||||
else
|
||||
FuncInput = Op;
|
||||
}
|
||||
Stack.Push(FuncName + "(" + FuncInput + ")");
|
||||
break;
|
||||
}
|
||||
case 0x1E: sReader.ReadS32(); break;
|
||||
case 0x1F: sReader.ReadS32(); break;
|
||||
case 0x20: //ret
|
||||
{
|
||||
string Op = Stack.Pop();
|
||||
CodeLine NewLine = new CodeLine("return " + Op + ";", pos);
|
||||
DecompiledCode.Add(NewLine);
|
||||
break;
|
||||
}
|
||||
case 0x21: //ret0
|
||||
{
|
||||
CodeLine NewLine = new CodeLine("return 0;", pos);
|
||||
DecompiledCode.Add(NewLine);
|
||||
break;
|
||||
}
|
||||
case 0x22: //jne
|
||||
{
|
||||
var dest = sReader.Read32();
|
||||
nextofs = dest;
|
||||
string Op = Stack.Pop();
|
||||
CodeLine NewLine = new CodeLine("if(" + Op + "){",pos);
|
||||
IfStatements.Add(new JumpStatement(pos, dest));
|
||||
DecompiledCode.Add(NewLine);
|
||||
break;
|
||||
}
|
||||
case 0x23: //jmp
|
||||
{
|
||||
var dest = sReader.Read32();
|
||||
nextofs = dest;
|
||||
JmpStatements.Add(new JumpStatement(pos, dest));
|
||||
break;
|
||||
}
|
||||
case 0x24: //pop
|
||||
{
|
||||
string Op = Stack.Pop();
|
||||
CodeLine NewLine = new CodeLine(Op + ";", pos);
|
||||
DecompiledCode.Add(NewLine);
|
||||
break;
|
||||
}
|
||||
case 0x25: //int0
|
||||
{
|
||||
Stack.Push("0");
|
||||
break;
|
||||
}
|
||||
case 0x26: //int1
|
||||
{
|
||||
Stack.Push("1");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nextofs > maxofs)
|
||||
{
|
||||
maxofs = nextofs;
|
||||
}
|
||||
} while (!IsReturnCommand(command) || sReader.Position <= sTextOffset + maxofs);
|
||||
|
||||
WriteCode(DecompiledCode, IfStatements, JmpStatements);
|
||||
|
||||
sWriter.WriteLine();
|
||||
|
||||
sReader.Back();
|
||||
}
|
||||
|
||||
static void WriteCode(List<CodeLine> DecompCode, List<JumpStatement> Ifs, List<JumpStatement> Jumps)
|
||||
{
|
||||
|
||||
//Close if statements
|
||||
foreach (JumpStatement IfStatement in Ifs)
|
||||
{
|
||||
CodeLine NewLine;
|
||||
for (int i = DecompCode.Count-1; i > 0; i--)
|
||||
{
|
||||
if (IfStatement.EndAddress < DecompCode[i].Address)//close address
|
||||
{
|
||||
continue;
|
||||
}
|
||||
NewLine = new CodeLine("}", IfStatement.EndAddress);
|
||||
DecompCode.Insert(i + 1, NewLine);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//ImplementBreaks
|
||||
for (int i = 0; i < DecompCode.Count; i++)
|
||||
{
|
||||
if (i < DecompCode.Count - 1)
|
||||
{
|
||||
foreach (JumpStatement Jump in Jumps)
|
||||
{
|
||||
bool AddLabel = false;
|
||||
bool AddBreak = false;
|
||||
CodeLine Label = new CodeLine("", 0);
|
||||
CodeLine Break = new CodeLine("", 0);
|
||||
if (Jump.EndAddress >= DecompCode[i].Address && Jump.EndAddress < DecompCode[i + 1].Address)//add label
|
||||
{
|
||||
Label = new CodeLine(Jump.EndAddress.ToString("X8") + ":", Jump.EndAddress);
|
||||
AddLabel = true;
|
||||
}
|
||||
if (Jump.StartAddress >= DecompCode[i].Address && Jump.StartAddress < DecompCode[i + 1].Address)//add break
|
||||
{
|
||||
Break = new CodeLine("break " + Jump.EndAddress.ToString("X8") + ";", Jump.StartAddress);
|
||||
AddBreak = true;
|
||||
}
|
||||
if (AddLabel)
|
||||
{
|
||||
DecompCode.Insert(i + 1, Label);
|
||||
i++;
|
||||
}
|
||||
if (AddBreak)
|
||||
{
|
||||
DecompCode.Insert(i + 1, Break);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Indent = 0;
|
||||
//Indentation
|
||||
for (int i = 0; i < DecompCode.Count; i++)
|
||||
{
|
||||
string IndentedString = new String(' ', Indent * 4) + DecompCode[i].Text;
|
||||
if (DecompCode[i].Text.StartsWith("if"))
|
||||
{
|
||||
Indent++;
|
||||
}
|
||||
else if (DecompCode[i].Text.StartsWith("}"))
|
||||
{
|
||||
Indent = System.Math.Max(0, Indent - 1);
|
||||
IndentedString = new String(' ', Indent * 4) + DecompCode[i].Text;
|
||||
}
|
||||
DecompCode[i] = new CodeLine(IndentedString, DecompCode[i].Address);
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < DecompCode.Count; i++)
|
||||
{
|
||||
sWriter.WriteLine(DecompCode[i].Text);
|
||||
}
|
||||
}
|
||||
|
||||
static uint FetchData(int i) {
|
||||
sReader.Keep();
|
||||
sReader.Goto(sDataOffset + (4 * i));
|
||||
var data = sReader.Read32();
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
using System.IO;
|
||||
|
||||
namespace arookas {
|
||||
class CommandLineSettings {
|
||||
class CommandLineSettings
|
||||
{
|
||||
string mInput, mOutput;
|
||||
bool mOutputHeader, mOutputText, mOutputData, mOutputSym, mOutputVars;
|
||||
bool mOutputHeader, mOutputText, mOutputData, mOutputSym, mOutputVars, mDecomp;
|
||||
|
||||
public string Input { get { return mInput; } }
|
||||
public string Output { get { return mOutput; } }
|
||||
|
@ -13,11 +14,13 @@ namespace arookas {
|
|||
public bool OutputData { get { return mOutputData; } }
|
||||
public bool OutputSym { get { return mOutputSym; } }
|
||||
public bool OutputVars { get { return mOutputVars; } }
|
||||
public bool OutputSun { get { return mDecomp; } }
|
||||
|
||||
public CommandLineSettings(aCommandLine cmd) {
|
||||
if (cmd == null) {
|
||||
throw new ArgumentNullException("cmd");
|
||||
}
|
||||
mDecomp = false;
|
||||
foreach (var param in cmd) {
|
||||
switch (param.Name) {
|
||||
case "-in": mInput = param[0]; continue;
|
||||
|
@ -32,8 +35,13 @@ namespace arookas {
|
|||
case "-s": mOutputSym = false; continue;
|
||||
case "-V": mOutputVars = true; continue;
|
||||
case "-v": mOutputVars = false; continue;
|
||||
case "-sun": mDecomp = true; continue;
|
||||
}
|
||||
}
|
||||
///Debug///
|
||||
//mInput = "C:\\Users\\August\\Downloads\\Sunshine ROM hacking\\ssc\\Decompile\\talkevent.sb";
|
||||
//mDecomp = true;
|
||||
///Debug///
|
||||
if (mInput == null) {
|
||||
throw new Exception("Missing input file setting.");
|
||||
}
|
||||
|
@ -41,5 +49,5 @@ namespace arookas {
|
|||
mOutput = Path.ChangeExtension(mInput, ".txt");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue