diff --git a/sbdump/main.cs b/sbdump/main.cs index a5f05d9..ffc88db 100644 --- a/sbdump/main.cs +++ b/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 Stack = new Stack(); + List DecompiledCode = new List(); + List IfStatements = new List(); + List JmpStatements = new List(); + 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 DecompCode, List Ifs, List 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(); diff --git a/sbdump/settings.cs b/sbdump/settings.cs index e06ab2e..4a6e257 100644 --- a/sbdump/settings.cs +++ b/sbdump/settings.cs @@ -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"); } } - } + } }