Added flow control analysis to decompiler
This commit is contained in:
parent
9b37f0273a
commit
c0cf50c6f7
5 changed files with 247 additions and 127 deletions
172
sbdump/CodeGraph.cs
Normal file
172
sbdump/CodeGraph.cs
Normal file
|
@ -0,0 +1,172 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace arookas
|
||||
{
|
||||
class CodeGraph
|
||||
{
|
||||
public List<CodeVertex> Graph;
|
||||
|
||||
public CodeGraph()
|
||||
{
|
||||
Graph = new List<CodeVertex>();
|
||||
}
|
||||
|
||||
public void AddVertex(CodeVertex NewVertex)//Adds a vertex
|
||||
{
|
||||
Graph.Add(NewVertex);
|
||||
}
|
||||
|
||||
|
||||
private int IndexFromAddr(long Address)
|
||||
{
|
||||
for (int i = 0; i < Graph.Count; i++)
|
||||
{
|
||||
if (Graph[i].Addr < Address)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
return -1;//-1 indicates end of the code
|
||||
}
|
||||
|
||||
public string OutputCode(int IndentL)
|
||||
{
|
||||
for (int i = 0; i < Graph.Count; i++)
|
||||
{
|
||||
if(Graph[i].Type == VertexType.Branch)
|
||||
{
|
||||
int BranchIndex = IndexFromAddr(Graph[i].BranchTo);
|
||||
if(BranchIndex == -1)
|
||||
{
|
||||
Graph[i].Code = "break " + Graph[i].BranchTo.ToString() + ";";//Branch to label
|
||||
long EndAddr = Graph[Graph.Count - 1].Addr;
|
||||
CodeVertex Label = new CodeVertex(VertexType.Label, -1, Graph[i].BranchTo.ToString() + ":", EndAddr+1);
|
||||
Graph.Add(Label);
|
||||
}
|
||||
else
|
||||
{
|
||||
VertexType DestType = Graph[BranchIndex].Type;
|
||||
|
||||
if (DestType == VertexType.CodeBlock || DestType == VertexType.Branch)
|
||||
{
|
||||
Graph[i].Code = "break " + Graph[i].BranchTo.ToString() + ";";//Branch to label
|
||||
CodeVertex NewLine = new CodeVertex(VertexType.Label, -1, Graph[i].BranchTo.ToString() + ":",Graph[i].BranchTo);
|
||||
Graph.Insert(BranchIndex, NewLine);
|
||||
if(BranchIndex <= i)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if(DestType == VertexType.Label)
|
||||
{
|
||||
string LabelName = Graph[BranchIndex].Code.Replace(':',' ');
|
||||
Graph[i].Code = "break " + LabelName + ";";//Branch to label
|
||||
}
|
||||
else if(DestType == VertexType.ConditionalBranch)
|
||||
{
|
||||
//While Loop Detected
|
||||
Graph[BranchIndex].Code = "while(" + Graph[BranchIndex].Code + ")" + Environment.NewLine + "{";//Put while loop
|
||||
int ClosingIndex = IndexFromAddr(Graph[BranchIndex].BranchTo);
|
||||
if (ClosingIndex == -1)
|
||||
{
|
||||
CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock, -1, "}", Graph[BranchIndex].BranchTo);
|
||||
Graph.Add(NewLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock, -1, "}", Graph[BranchIndex].BranchTo);
|
||||
Graph.Insert(ClosingIndex, NewLine);
|
||||
if (ClosingIndex <= i)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < Graph.Count; i++)
|
||||
{
|
||||
if (Graph[i].Type == VertexType.ConditionalBranch && !Graph[i].Code.StartsWith("while"))
|
||||
{
|
||||
if (Graph[i].Addr < Graph[i].BranchTo)
|
||||
{
|
||||
//If Statement Detected
|
||||
Graph[i].Code = "if(" + Graph[i].Code + ")" + Environment.NewLine + "{";//Put if
|
||||
int ClosingIndex = IndexFromAddr(Graph[i].BranchTo);
|
||||
if (ClosingIndex == -1)
|
||||
{
|
||||
CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock, -1, "}", Graph[i].BranchTo);
|
||||
Graph.Add(NewLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock, -1, "}", Graph[i].BranchTo);
|
||||
Graph.Insert(ClosingIndex, NewLine);
|
||||
if (ClosingIndex <= i)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Do loop detected
|
||||
Graph[i].Code = "} while(" + Graph[i].Code + ")";//Put do loop
|
||||
int ClosingIndex = IndexFromAddr(Graph[i].BranchTo);
|
||||
CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock, -1, "do" + Environment.NewLine + "{", Graph[i].BranchTo);
|
||||
Graph.Insert(ClosingIndex, NewLine);
|
||||
if (ClosingIndex <= i)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string DecompiledCode = "";
|
||||
for (int i = 0; i < Graph.Count; i++)
|
||||
{
|
||||
DecompiledCode = DecompiledCode + Environment.NewLine + Graph[i].Code;
|
||||
}
|
||||
|
||||
string[] lines = DecompiledCode.Split(new[] { Environment.NewLine },StringSplitOptions.None);
|
||||
DecompiledCode = "";
|
||||
|
||||
int IndentLevel = IndentL;
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
if (lines[i] == "")
|
||||
continue;
|
||||
string IndentedString = new String(' ', IndentLevel * 4) + lines[i];
|
||||
if (lines[i].Contains("{"))
|
||||
{
|
||||
IndentLevel++;
|
||||
}
|
||||
else if (lines[i].Contains("}"))
|
||||
{
|
||||
IndentLevel = System.Math.Max(0, IndentLevel - 1);
|
||||
IndentedString = new String(' ', IndentLevel * 4) + lines[i];
|
||||
}
|
||||
if(DecompiledCode == "")
|
||||
{
|
||||
DecompiledCode = IndentedString;
|
||||
}
|
||||
else
|
||||
{
|
||||
DecompiledCode = DecompiledCode + Environment.NewLine + IndentedString;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return DecompiledCode;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
24
sbdump/CodeVertex.cs
Normal file
24
sbdump/CodeVertex.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace arookas
|
||||
{
|
||||
class CodeVertex
|
||||
{
|
||||
public VertexType Type;
|
||||
public long BranchTo = -1;
|
||||
public string Code;
|
||||
public long Addr;
|
||||
|
||||
public CodeVertex(VertexType Typ, long BrTo,string Cde,long Adr)
|
||||
{
|
||||
Type = Typ;
|
||||
BranchTo = BrTo;
|
||||
Code = Cde;
|
||||
Addr = Adr;
|
||||
}
|
||||
}
|
||||
}
|
16
sbdump/Enums.cs
Normal file
16
sbdump/Enums.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace arookas
|
||||
{
|
||||
enum VertexType
|
||||
{
|
||||
CodeBlock = 0,
|
||||
Branch = 1,
|
||||
ConditionalBranch = 2,
|
||||
Label = 3,
|
||||
}
|
||||
}
|
158
sbdump/main.cs
158
sbdump/main.cs
|
@ -23,30 +23,6 @@ namespace arookas {
|
|||
"ret", "ret0", "jne", "jmp", "pop", "int0", "int1", "end",
|
||||
};
|
||||
|
||||
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 {
|
||||
|
@ -276,7 +252,7 @@ namespace arookas {
|
|||
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)
|
||||
{
|
||||
|
@ -285,21 +261,25 @@ namespace arookas {
|
|||
|
||||
foreach (var symbol in symbols.Where(i => i.Type == SymbolType.Function).OrderBy(i => i.Data))
|
||||
{
|
||||
sWriter.WriteLine("{0}: ", FetchSymbolName(symbol));
|
||||
DecompFunction(symbol.Data);
|
||||
}
|
||||
sWriter.WriteLine();
|
||||
sWriter.WriteLine("function "+ FetchSymbolName(symbol)+ "(...)" + Environment.NewLine+ "{");
|
||||
DecompFunction(symbol.Data, 1);
|
||||
sWriter.WriteLine("}");
|
||||
sWriter.WriteLine();
|
||||
}
|
||||
|
||||
DecompFunction(0u, 0);//Decompile main part.
|
||||
|
||||
}
|
||||
|
||||
static void DecompFunction(uint ofs)
|
||||
static void DecompFunction(uint ofs, int IndentL)
|
||||
{
|
||||
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>();
|
||||
CodeGraph FuncCodeGraph = new CodeGraph();
|
||||
do
|
||||
{
|
||||
var pos = sReader.Position - sTextOffset;
|
||||
|
@ -355,8 +335,8 @@ namespace arookas {
|
|||
}
|
||||
case 0x08: //add
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
string Op1 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " + " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
|
@ -369,8 +349,8 @@ namespace arookas {
|
|||
}
|
||||
case 0x0A: //mul
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
string Op1 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " * " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
|
@ -399,21 +379,21 @@ namespace arookas {
|
|||
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);
|
||||
CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock , -1, VariableName + " = " + Stack.Pop() + ";", pos);
|
||||
FuncCodeGraph.AddVertex(NewLine);
|
||||
break;
|
||||
}
|
||||
case 0x0E: //eq
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
string Op1 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " == " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
case 0x0F: //ne
|
||||
{
|
||||
string Op1 = Stack.Pop();
|
||||
string Op2 = Stack.Pop();
|
||||
string Op1 = Stack.Pop();
|
||||
Stack.Push("(" + Op1 + " != " + Op2 + ")");
|
||||
break;
|
||||
}
|
||||
|
@ -546,14 +526,15 @@ namespace arookas {
|
|||
case 0x20: //ret
|
||||
{
|
||||
string Op = Stack.Pop();
|
||||
CodeLine NewLine = new CodeLine("return " + Op + ";", pos);
|
||||
DecompiledCode.Add(NewLine);
|
||||
CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock , -1, "return " + Op + ";", pos);
|
||||
FuncCodeGraph.AddVertex(NewLine);
|
||||
|
||||
break;
|
||||
}
|
||||
case 0x21: //ret0
|
||||
{
|
||||
CodeLine NewLine = new CodeLine("return 0;", pos);
|
||||
DecompiledCode.Add(NewLine);
|
||||
CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock, -1, "return 0;", pos);
|
||||
FuncCodeGraph.AddVertex(NewLine);
|
||||
break;
|
||||
}
|
||||
case 0x22: //jne
|
||||
|
@ -561,23 +542,24 @@ namespace arookas {
|
|||
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);
|
||||
CodeVertex NewLine = new CodeVertex(VertexType.ConditionalBranch, dest, Op, pos);
|
||||
FuncCodeGraph.AddVertex(NewLine);
|
||||
|
||||
break;
|
||||
}
|
||||
case 0x23: //jmp
|
||||
{
|
||||
var dest = sReader.Read32();
|
||||
nextofs = dest;
|
||||
JmpStatements.Add(new JumpStatement(pos, dest));
|
||||
CodeVertex NewLine = new CodeVertex(VertexType.Branch, dest, "", pos);
|
||||
FuncCodeGraph.AddVertex(NewLine);
|
||||
break;
|
||||
}
|
||||
case 0x24: //pop
|
||||
{
|
||||
string Op = Stack.Pop();
|
||||
CodeLine NewLine = new CodeLine(Op + ";", pos);
|
||||
DecompiledCode.Add(NewLine);
|
||||
CodeVertex NewLine = new CodeVertex(VertexType.CodeBlock, -1, Op + ";", pos);
|
||||
FuncCodeGraph.AddVertex(NewLine);
|
||||
break;
|
||||
}
|
||||
case 0x25: //int0
|
||||
|
@ -597,90 +579,16 @@ namespace arookas {
|
|||
}
|
||||
} while (!IsReturnCommand(command) || sReader.Position <= sTextOffset + maxofs);
|
||||
|
||||
WriteCode(DecompiledCode, IfStatements, JmpStatements);
|
||||
WriteCode(FuncCodeGraph, IndentL);
|
||||
|
||||
sWriter.WriteLine();
|
||||
|
||||
sReader.Back();
|
||||
}
|
||||
|
||||
static void WriteCode(List<CodeLine> DecompCode, List<JumpStatement> Ifs, List<JumpStatement> Jumps)
|
||||
static void WriteCode(CodeGraph FuncCodeGraph, int Indent)
|
||||
{
|
||||
|
||||
//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);
|
||||
}
|
||||
sWriter.Write(FuncCodeGraph.OutputCode(Indent));
|
||||
}
|
||||
|
||||
static uint FetchData(int i) {
|
||||
|
|
|
@ -39,8 +39,8 @@ namespace arookas {
|
|||
}
|
||||
}
|
||||
///Debug///
|
||||
//mInput = "C:\\Users\\August\\Downloads\\Sunshine ROM hacking\\ssc\\Decompile\\talkevent.sb";
|
||||
//mDecomp = true;
|
||||
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.");
|
||||
|
|
Loading…
Reference in a new issue