/* * NOTE: * This is a simple ad-hoc decompiler intended strictly for debugging purposes. * Any pull requests or commits that attempt to turn this into a decompiler for * the creating a GameMaker project are strictly prohibited and will be immediately * declined. */ public static string Decompile(List <Instruction> _instructionList) { string _decomOutput = ""; Stack <dynamic> _decomStack = new Stack <dynamic>(); foreach (Instruction _instructionGet in _instructionList) { switch (_instructionGet.Opcode) { case LOpcode.b: case LOpcode.bt: case LOpcode.bf: { Instructions.Branch _instructionBranch = _instructionGet as Instructions.Branch; _decomOutput += "// branch->" + _instructionBranch.Jump + "\n"; break; } case LOpcode.pushi: { Instructions.PushImmediate _instructionPushImmediate = _instructionGet as Instructions.PushImmediate; _decomStack.Push(_instructionPushImmediate.Value.ToString()); break; } case LOpcode.push: { Instructions.Push _instructionPush = _instructionGet as Instructions.Push; switch (_instructionPush.Type) { case LArgumentType.Variable: { _decomStack.Push(_instructionPush.Variable.Name); break; } case LArgumentType.String: { _decomStack.Push("\"" + _instructionPush.Value.String + "\""); break; } default: { _decomStack.Push(_instructionPush.Value.Number.ToString()); break; } } break; } case LOpcode.pop: { Instructions.Pop _instructionPop = _instructionGet as Instructions.Pop; switch (_instructionPop.Variable.Scope) { case LVariableScope.Global: { _decomOutput += "global."; break; } case LVariableScope.Instance: { _decomOutput += "self."; break; } case LVariableScope.Local: { _decomOutput += "local."; break; } case LVariableScope.Static: { _decomOutput += "static."; break; } } _decomOutput += _instructionPop.Variable.Name + " = "; _decomOutput += _decomStack.Pop().ToString() + ";\n"; break; } case LOpcode.call: { Instructions.Call _instructionCall = _instructionGet as Instructions.Call; _decomOutput += _instructionCall.FunctionName + "("; for (int i = 0; i < _instructionCall.Count; i++) { _decomOutput += _decomStack.Pop().ToString(); if (i < _instructionCall.Count - 1) { _decomOutput += ", "; } } _decomOutput += ");\n"; break; } case LOpcode.exit: { _decomOutput += "exit;\n"; break; } } } return(_decomOutput); }
public void Parse(Game _game) { // Parse all instructions while (this.Reader.BaseStream.Position < this.Reader.BaseStream.Length) { this.BranchTable[this.Reader.BaseStream.Position] = this.Instructions.Count; Int32 _instructionGet = this.Reader.ReadInt32(); LOpcode _opcodeGet = Instruction.GetOpcode(_instructionGet); if (Instruction.Ignore.Contains(_opcodeGet) == false) { if (Instruction.Mapping.ContainsKey(_opcodeGet) == true) { this.Instructions.Add(Instruction.Mapping[_opcodeGet](_instructionGet, _game, this, this.Reader)); } else { throw new Exception(String.Format("Could not find instruction mapping for {0} at {1} in {2}", _opcodeGet, this.Reader.BaseStream.Position, this.Name)); } } } // Map out branching & environments for (int i = 0; i < this.Instructions.Count; i++) { switch (this.Instructions[i].Opcode) { case LOpcode.b: case LOpcode.bt: case LOpcode.bf: { Instructions.Branch _instructionBranch = this.Instructions[i] as Instructions.Branch; if (this.BranchTable.ContainsKey(_instructionBranch.Offset) == true) { _instructionBranch.Jump = this.BranchTable[_instructionBranch.Offset] - 1; } else { _instructionBranch.Jump = this.Instructions.Count; } break; } case LOpcode.pushenv: { Instructions.PushEnvironment _instructionEnv = this.Instructions[i] as Instructions.PushEnvironment; if (this.BranchTable.ContainsKey(_instructionEnv.Offset) == true) { _instructionEnv.Jump = this.BranchTable[_instructionEnv.Offset]; } else { _instructionEnv.Jump = this.Instructions.Count; } break; } case LOpcode.popenv: { Instructions.PopEnvironment _instructionEnv = this.Instructions[i] as Instructions.PopEnvironment; if (this.BranchTable.ContainsKey(_instructionEnv.Offset) == true) { _instructionEnv.Jump = this.BranchTable[_instructionEnv.Offset]; } else { _instructionEnv.Jump = this.Instructions.Count; } break; } } } // Check for arrays for (int i = 0; i < this.Instructions.Count; i++) { switch (this.Instructions[i].Opcode) { case LOpcode.setowner: { // Find next pop instruction for (int j = i; j < this.Instructions.Count; j++) { if (this.Instructions[j].Opcode == LOpcode.pop) { this.Instructions.RemoveAt(--i); // push.i <id> break; } else if (this.Instructions[j].Opcode == LOpcode.call) { Instructions.Call _instructionGet = this.Instructions[j] as Instructions.Call; if (_instructionGet.FunctionName == "@@NewGMLArray@@") { this.Instructions.RemoveAt(--i); break; } } } break; } } } #if (!DEBUG) // Print out finalized bytecode string _bytecodeOutput = this.Name + "\n"; for (int i = 0; i < this.Instructions.Count; i++) { _bytecodeOutput += String.Format("[{1}] - {0}", this.Instructions[i].Opcode, this.Instructions[i].Raw.ToString("X")); switch (this.Instructions[i].Opcode) { case LOpcode.b: { Instructions.Branch _instructionGet = this.Instructions[i] as Instructions.Branch; _bytecodeOutput += String.Format("(Goto={0}:{1})", this.Instructions[_instructionGet.Jump].Opcode, _instructionGet.Jump); break; } case LOpcode.call: { Instructions.Call _instructionGet = this.Instructions[i] as Instructions.Call; _bytecodeOutput += String.Format("(Function={0})", _instructionGet.FunctionName); break; } case LOpcode.pop: { Instructions.Pop _instructionGet = this.Instructions[i] as Instructions.Pop; _bytecodeOutput += String.Format("(Variable={0}, Scope={1})", _instructionGet.Variable.Name, _instructionGet.Data); break; } case LOpcode.push: { Instructions.Push _instructionGet = this.Instructions[i] as Instructions.Push; switch (_instructionGet.Type) { case LArgumentType.Variable: { _bytecodeOutput += String.Format("(Variable={0})", _instructionGet.Variable.Name); break; } default: { _bytecodeOutput += String.Format("(Value={0})", _instructionGet.Value.Value); break; } } break; } case LOpcode.pushb: { Instructions.PushBuiltin _instructionGet = this.Instructions[i] as Instructions.PushBuiltin; _bytecodeOutput += String.Format("(Variable={0})", _instructionGet.Variable); break; } case LOpcode.pushi: { Instructions.PushImmediate _instructionGet = this.Instructions[i] as Instructions.PushImmediate; _bytecodeOutput += String.Format("(Value={0})", _instructionGet.Value.Value); break; } } _bytecodeOutput += "\n"; } Console.WriteLine(_bytecodeOutput); // Print out decompiled bytecode try { Console.WriteLine("{0}:\n---\n{1}\n---", this.Name, Runner.Debug.Decompiler.Decompile(this.Instructions)); } catch (Exception e) { Console.WriteLine("Failed to decompile {0}: {1}", this.Name, e.Message); } #endif }