Added flow control analysis to decompiler

This commit is contained in:
Augs 2019-12-03 17:01:59 +00:00
parent 9b37f0273a
commit c0cf50c6f7
5 changed files with 247 additions and 127 deletions

172
sbdump/CodeGraph.cs Normal file
View 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
View 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
View 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,
}
}

View file

@ -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) {

View file

@ -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.");