Browse Source

CodeGenerator refactored.

Iñigo Valentin 3 năm trước cách đây
mục cha
commit
d70644d95a

+ 209 - 58
V-Gears-Installer/include/decompiler/CodeGenerator.h

@@ -51,50 +51,70 @@ enum ARGUMENT_ORDER{
 
 /**
  * Base class for code generators.
+ *
+ * This is to be overriden by each engine.
  */
 class CodeGenerator {
 
     public:
-        LuaLanguage& TargetLang()
-        {
-            assert(target_lang_);
-            return *target_lang_;
-        }
 
-        void writeFunctionCall(std::string functionName, std::string paramsFormat, const std::vector<ValuePtr>& params);
+        /**
+         * Constructor
+         *
+         * @param engine[in] The engine used for the script.
+         * @param output[out] The stream to output the code to.
+         * @param bin_order[in] Order of arguments for binary operators.
+         * @param call_order[in] Order of arguments for function calls.
+         */
+        CodeGenerator(
+          Engine *engine, std::ostream &output, ARGUMENT_ORDER bin_order, ARGUMENT_ORDER call_order
+        );
 
-        const ARGUMENT_ORDER _binOrder;  ///< Order of operands for binary operations.
-        const ARGUMENT_ORDER _callOrder; ///< Order of operands for call arguments.
-        ValueList _argList;        ///< Storage for lists of arguments to be built when processing function calls.
-        GroupPtr cur_group_;     ///< Pointer to the group currently being processed.
+        /**
+         * Destructor.
+         *
+         * Does nothing.
+         */
+        virtual ~CodeGenerator();
 
-        virtual ~CodeGenerator() { }
+        /**
+         * Retrieves the target language.
+         */
+        LuaLanguage& GetLanguage();
 
         /**
-         * Constructor for CodeGenerator.
+         * Writes a function call.
          *
-         * @param engine Pointer to the Engine used for the script.
-         * @param output The std::ostream to output the code to.
-         * @param binOrder Order of arguments for binary operators.
-         * @param callOrder Order of arguments for function calls.
+         * @param function_name[in] The name of the function.
+         * @param param_format[in] Characters indicating the parameter format.
+         * 'b' for boolean parameters, 'n' for integers (treated as unsigned)
+         * or 'f' for floats.
+         * @param params[in] The list of parameters.
          */
-        CodeGenerator(Engine *engine, std::ostream &output, ARGUMENT_ORDER binOrder, ARGUMENT_ORDER callOrder);
+        void WriteFunctionCall(
+          std::string function_name, std::string param_format, const std::vector<ValuePtr>& params
+        );
 
         /**
          * Generates code from the provided graph and outputs it to stdout.
          *
-         * @param g The annotated graph of the script.
+         * @param insts[in] The list of instructions.
+         * @param graph[in] The annotated graph of the script.
          */
-        virtual void Generate(InstVec& insts, const Graph &g);
+        virtual void Generate(InstVec& insts, const Graph &graph);
 
         /**
          * Adds a line of code to the current group.
          *
-         * @param s The line to add.
-         * @param unindentBefore Whether or not to remove an indentation level before the line. Defaults to false.
-         * @param indentAfter Whether or not to add an indentation level after the line. Defaults to false.
+         * @param line[in] The line to add.
+         * @param unindent_before[in] Whether or not to remove an indentation
+         * level before the line. Defaults to false.
+         * @param indent_after[in] Whether or not to add an indentation level
+         * after the line. Defaults to false.
          */
-        virtual void AddOutputLine(std::string s, bool unindentBefore = false, bool indentAfter = false);
+        virtual void AddOutputLine(
+          std::string line, bool unindent_before = false, bool indent_after = false
+        );
 
         /**
          * Writes a comment line indicating an unimplemented opcode.
@@ -103,83 +123,214 @@ class CodeGenerator {
          * @param class_name[in] The class where the instruction is. Unused.
          * @param instruction[in] The unimplemented instruction.
          */
-        void WriteTodo(std::string class_name, std::string instruction){
-            AddOutputLine("-- UNIMPLMENTED INSTRUCTION: \"" + instruction + "\")");
-        }
+        void WriteTodo(std::string class_name, std::string instruction);
 
         /**
          * Generate an assignment statement.
          *
-         * @param dst The variable being assigned to.
-         * @param src The value being assigned.
+         * @param dst[in] The variable being assigned to.
+         * @param src[in] The value being assigned.
          */
-        void writeAssignment(ValuePtr dst, ValuePtr src);
+        void WriteAssignment(ValuePtr dst, ValuePtr src);
 
         /**
          * Add an argument to the argument list.
          *
-         * @param p The argument to add.
+         * @param arg[in] The argument to add.
          */
-        void addArg(ValuePtr p);
+        void AddArg(ValuePtr arg);
 
         /**
          * Process a single character of metadata.
          *
-         * @param inst The instruction being processed.
-         * @param c The character signifying the action to be taken.
-         * @param pos The position at which c occurred in the metadata.
+         * @param inst[in] The instruction being processed. Unused.
+         * @param c[in] The character signifying the action to be taken. The
+         * only valid one is 'p'.
+         * @param pos[in] The position at which c occurred in the metadata.
+         * Unused.
          */
         virtual void ProcessSpecialMetadata(const InstPtr inst, char c, int pos);
 
+        /**
+         * Retrieves the argument list.
+         *
+         * @return The argument list.
+         */
+        virtual ValueList GetArgList();
+
+        /**
+         * Retrieves the order of operands for binary operations.
+         *
+         * @return The order of operands
+         */
+        virtual ARGUMENT_ORDER GetBinaryOrder();
+
     protected:
-        Engine *_engine;        ///< Pointer to the Engine used for the script.
-        std::ostream &_output;  ///< The std::ostream to output the code to.
-        ValueStack _stack;      ///< The stack currently being processed.
-        uint _indentLevel;      ///< Indentation level.
-        GraphVertex _curVertex; ///< Graph vertex currently being processed.
-        std::unique_ptr<LuaLanguage> target_lang_;
 
         /**
-         * Processes an instruction. Called by process() for each instruction.
-         * Call the base class implementation for opcodes you cannot handle yourself,
+         * Processes an instruction. Called by {@see Process()} for each
+         * instruction. Call the base class implementation for opcodes not
+         * handled by an implemented engine, or where the base class
+         * implementation is preferable.
+         *
+         * @param function[in] The function the instruction is is.
+         * @param inst[in] The instruction to process.
+         * @param insts[in] Every instruction in the function.
+         */
+        void ProcessInst(Function& function, InstVec& insts, const InstPtr inst);
+
+        /**
+         * Processes an unconditional jump instruction. Called by
+         * {@see ProcessInst()} for those instructions. Call the base class
+         * implementation for opcodes not handled by an implemented engine,
          * or where the base class implementation is preferable.
          *
-         * @param inst The instruction to process.
+         * @param function[in] The function the instruction is is.
+         * @param inst[in] The instruction to process.
+         * @param insts[in] Every instruction in the function.
+         */
+        void ProcessUncondJumpInst(Function& function, InstVec& insts, const InstPtr inst);
+
+        /**
+         * Processes a conditional jump instruction. Called by
+         * {@see ProcessInst()} for those instructions. Call the base class
+         * implementation for opcodes not handled by an implemented engine,
+         * or where the base class implementation is preferable.
+         *
+         * @param inst[in] The instruction to process.
          */
-        void ProcessInst(Function& func, InstVec& insts, const InstPtr inst);
-        void ProcessUncondJumpInst(Function& func, InstVec& insts, const InstPtr inst);
         void ProcessCondJumpInst(const InstPtr inst);
 
         /**
          * Indents a string according to the current indentation level.
          *
-         * @param s The string to indent.
+         * @param s[in] The string to indent.
          * @result The indented string.
          */
-        std::string indentString(std::string s);
+        std::string IndentString(std::string s);
 
         /**
          * Construct the signature for a function.
          *
-         * @param func Reference to the function to construct the signature for.
+         * @param function[in] Reference to the function to construct the
+         * signature for.
+         * @return For this base class, an empty string.
+         */
+        virtual std::string ConstructFuncSignature(const Function& function);
+
+        /**
+         * Adds lines to the script before a function.
+         *
+         * Called before writing a function start. For this base class, it
+         * does nothing.
+         *
+         * @param function[in] The function about to start.
+         */
+        virtual void OnBeforeStartFunction(const Function& function);
+
+        /**
+         * Adds lines to the script at the end a function.
+         *
+         * Called after writing a function. For this base class, it adds a
+         * closing bracer "}".
+         *
+         * @param function[in] The function about to end.
+         */
+        virtual void OnEndFunction(const Function& function);
+
+        /**
+         * Adds lines to the script before a function instructions.
+         *
+         * Called after writing a function start. For this base class, it
+         * does nothing.
+         *
+         * @param function[in] The function starting.
+         */
+        virtual void OnStartFunction(const Function& function);
+
+        /**
+         * Checks if only required labels are to be written.
+         *
+         * @return Always false.
+         */
+        virtual bool OutputOnlyRequiredLabels() const;
+
+        /**
+         * Generates a pass.
+         *
+         * @param insts[in] The list of instructions.
+         * @param graph[in] The code graph.
+         * @todo Understand and explain.
+         */
+        void GeneratePass(InstVec& insts, const Graph& g);
+
+        /**
+         * Indicates if a label is being processed.
+         */
+        bool is_label_pass_ = true;
+
+        /**
+         * The group currently being processed.
          */
-        virtual std::string ConstructFuncSignature(const Function& func);
-        virtual void OnBeforeStartFunction(const Function& func);
-        virtual void OnEndFunction(const Function& func);
-        virtual void OnStartFunction(const Function&) { }
-        virtual bool OutputOnlyRequiredLabels() const { return false; }
+        GroupPtr cur_group_;
 
-        void generatePass(InstVec& insts, const Graph& g);
-        bool mIsLabelPass = true;
+        /**
+         * The engine used for teh script.
+         */
+        Engine *engine_;
+
+        /**
+         * The stream to output the code to.
+         */
+        std::ostream &output_;
+
+        /**
+         * The stack currently being processed.
+         */
+        ValueStack stack_;
+
+        /**
+         * Current indentation level.
+         */
+        uint indent_level_;
+
+        /**
+         * Graph vertex currently being processed.
+         */
+        GraphVertex cur_vertex_;
+
+        /**
+         * The target language.
+         */
+        std::unique_ptr<LuaLanguage> target_lang_;
 
     private:
-        Graph _g;                  ///< The annotated graph of the script.
 
         /**
          * Processes a GraphVertex.
          *
-         * @param v The vertex to process.
+         * @param vertex[in] The vertex to process.
+         */
+        void Process(Function& function, InstVec& insts, GraphVertex vertex);
+
+        /**
+         * The annotated graph of the script.
+         */
+        Graph graph_;
+
+        /**
+         * Order of operands for binary operations.
+         */
+        const ARGUMENT_ORDER bin_order_;
+
+        /**
+         * Order of operands for call arguments.
+         */
+        const ARGUMENT_ORDER call_order_;
+
+        /**
+         * Lists of arguments to be built when processing function calls.
          */
-        void process(Function& func, InstVec& insts, GraphVertex v);
+        ValueList arg_list_;
 
 };

+ 1 - 1
V-Gears-Installer/include/decompiler/LuaLanguage.h

@@ -74,7 +74,7 @@ class LuaLanguage{
          *
          * Continue is not implemented in Lua.
          *
-         * @return An emopty string.
+         * @return A commented string.
          */
         virtual const std::string LoopContinue();
 

+ 288 - 363
V-Gears-Installer/src/decompiler/CodeGenerator.cpp

@@ -21,427 +21,352 @@
 #include "decompiler/CodeGenerator.h"
 #include "decompiler/LuaLanguage.h"
 
-#define GET(vertex)    (boost::get(boost::vertex_name, _g, vertex))
-#define GET_EDGE(edge) (boost::get(boost::edge_attribute, _g, edge))
+#define GET(vertex)    (boost::get(boost::vertex_name, graph_, vertex))
+#define GET_EDGE(edge) (boost::get(boost::edge_attribute, graph_, edge))
 
-void CodeGenerator::OnBeforeStartFunction(const Function&)
+CodeGenerator::CodeGenerator(
+  Engine *engine, std::ostream &output, ARGUMENT_ORDER bin_order, ARGUMENT_ORDER call_order
+): output_(output), bin_order_(bin_order), call_order_(call_order)
 {
-}
-
-void CodeGenerator::OnEndFunction(const Function &)
-{
-    AddOutputLine("}", true, false);
-}
-
-std::string CodeGenerator::ConstructFuncSignature(const Function &)
-{
-    return "";
-}
-
-std::string CodeGenerator::indentString(std::string s)
-{
-    std::stringstream stream;
-    stream << std::string(INDENT_SPACES * _indentLevel, ' ') << s;
-    return stream.str();
-}
-
-CodeGenerator::CodeGenerator(Engine *engine, std::ostream &output, ARGUMENT_ORDER binOrder, ARGUMENT_ORDER callOrder)
-   : _output(output),
-    _binOrder(binOrder),
-    _callOrder(callOrder)
-{
-    _engine = engine;
-    _indentLevel = 0;
+    engine_ = engine;
+    indent_level_ = 0;
     target_lang_ = std::make_unique<LuaLanguage>();
 }
 
-typedef std::pair<GraphVertex, ValueStack> DFSEntry;
-
-void CodeGenerator::generatePass(InstVec& insts, const Graph& g)
-{
-    _g = g;
-    for (FuncMap::iterator fn = _engine->_functions.begin(); fn != _engine->_functions.end(); ++fn)
-    {
-        while (!_stack.empty())
-        {
-            _stack.pop();
-        }
-        GraphVertex entryPoint = fn->second._v;
-        std::string funcSignature = ConstructFuncSignature(fn->second);
-
-        // Write the function start
-        bool printFuncSignature = !funcSignature.empty();
-        if (printFuncSignature)
-        {
-            cur_group_ = GET(entryPoint);
-            if (!(fn == _engine->_functions.begin()))
-            {
-                 AddOutputLine("");
-            }
-            OnBeforeStartFunction(fn->second);
-
-            AddOutputLine(funcSignature, false, true);
-
-            OnStartFunction(fn->second);
-        }
-
-        GroupPtr lastGroup = GET(entryPoint);
+CodeGenerator::~CodeGenerator(){}
 
-        // DFS from entry point to process each vertex
-        Stack<DFSEntry> dfsStack;
-        std::set<GraphVertex> seen;
-        dfsStack.push(DFSEntry(entryPoint, ValueStack()));
-        seen.insert(entryPoint);
-        while (!dfsStack.empty())
-        {
-            DFSEntry e = dfsStack.pop();
-            GroupPtr tmp = GET(e.first);
-            if ((*tmp->start_)->_address > (*lastGroup->start_)->_address)
-            {
-                lastGroup = tmp;
-            }
-            _stack = e.second;
-            GraphVertex v = e.first;
-            process(fn->second, insts, v);
-            OutEdgeRange r = boost::out_edges(v, _g);
-            for (OutEdgeIterator i = r.first; i != r.second; ++i)
-            {
-                GraphVertex target = boost::target(*i, _g);
-                if (seen.find(target) == seen.end())
-                {
-                    dfsStack.push(DFSEntry(target, _stack));
-                    seen.insert(target);
-                }
-            }
-        }
+LuaLanguage& CodeGenerator::GetLanguage(){
+    assert(target_lang_);
+    return *target_lang_;
+}
 
-        // Write the function end
-        if (printFuncSignature)
-        {
-            cur_group_ = lastGroup;
-            OnEndFunction(fn->second);
+void CodeGenerator::WriteFunctionCall(
+  std::string function_name, std::string param_format, const std::vector<ValuePtr>& params
+){
+    std::string func_call = function_name + target_lang_->FunctionCallBegin();
+    const char* format = param_format.c_str();
+    int param_index = 0;
+    while (*format){
+        bool skip_argument = false;
+        switch (*format){
+            case 'b': func_call += params[param_index]->getUnsigned() ? "true" : "false"; break;
+            case 'n': func_call += std::to_string(params[param_index]->getUnsigned()); break;
+            case 'f':
+                func_call += std::to_string(
+                  static_cast<float>(params[param_index]->getUnsigned()) / 30.0f
+                );
+                break;
+            case '_': skip_argument = true; break;// Ignore param
+            default: throw std::runtime_error("Unknown param type");
         }
-
-        // Print output
-        GroupPtr p = GET(entryPoint);
-        while (p != NULL)
-        {
-            for (auto it = p->_code.begin(); it != p->_code.end(); ++it)
-            {
-                if (it->_unindentBefore)
-                {
-                    assert(_indentLevel > 0);
-                    _indentLevel--;
-                }
-
-                if (OutputOnlyRequiredLabels())
-                {
-                    _output << indentString(it->_line) << std::endl;
-                }
-                else
-                {
-                    _output << boost::format("%08X: %s") % (*p->start_)->_address % indentString(it->_line) << std::endl;
-                }
-
-                if (it->_indentAfter)
-                {
-                    _indentLevel++;
-                }
-            }
-            p = p->_next;
+        param_index ++;
+        format ++;
+        if (*format) {
+            // There is another param.
+            if (!skip_argument) func_call += target_lang_->FunctionCallArgumentSeperator() + " ";
         }
     }
+    func_call += target_lang_->FunctionCallEnd();
+    AddOutputLine(func_call);
 }
 
-void CodeGenerator::Generate(InstVec& insts, const Graph &g)
-{
-    if (OutputOnlyRequiredLabels())
-    {
+void CodeGenerator::Generate(InstVec& insts, const Graph &graph){
+    if (OutputOnlyRequiredLabels()){
         // Call twice, once where no output is generated but instructions are
         // marked as "needs label", then a 2nd time to actually output the code
-        mIsLabelPass = true;
-        generatePass(insts, g);
+        is_label_pass_ = true;
+        GeneratePass(insts, graph);
     }
-    mIsLabelPass = false;
-    generatePass(insts, g);
+    is_label_pass_ = false;
+    GeneratePass(insts, graph);
 }
 
-void CodeGenerator::AddOutputLine(std::string s, bool unindentBefore, bool indentAfter)
-{
-    // We don't generate output in the labels pass, we just find instructions that
-    // require a label to be outputted
-    if (!mIsLabelPass)
-    {
-        cur_group_->_code.push_back(CodeLine(s, unindentBefore, indentAfter));
-    }
+void CodeGenerator::AddOutputLine(std::string line, bool unindent_before, bool indent_after){
+    // Don't generate output in the labels pass, just find
+    // instructions that require a label to be outputted.
+    if (!is_label_pass_) cur_group_->_code.push_back(CodeLine(line, unindent_before, indent_after));
 }
 
-void CodeGenerator::writeAssignment(ValuePtr dst, ValuePtr src) 
-{
+void CodeGenerator::WriteTodo(std::string class_name, std::string instruction){
+    AddOutputLine("-- UNIMPLMENTED INSTRUCTION: \"" + instruction + "\")");
+}
+
+void CodeGenerator::WriteAssignment(ValuePtr dst, ValuePtr src) {
     std::stringstream s;
     s << dst << " = " << src << target_lang_->LineTerminator();
     AddOutputLine(s.str());
 }
 
-void CodeGenerator::process(Function& func, InstVec& insts, GraphVertex v)
-{
-    _curVertex = v;
-    cur_group_ = GET(v);
+void CodeGenerator::AddArg(ValuePtr arg) {
+    if (call_order_ == FIFO_ARGUMENT_ORDER) arg_list_.push_front(arg);
+    else if (call_order_ == LIFO_ARGUMENT_ORDER) arg_list_.push_back(arg);
+}
 
-    // Check if we should add else start
-    if (cur_group_->_startElse)
-    {
-        AddOutputLine(target_lang_->EndBlock(LuaLanguage::TO_ELSE_BLOCK) + " " + target_lang_->Else() + " " + target_lang_->StartBlock(LuaLanguage::BEGIN_ELSE), true, true);
+void CodeGenerator::ProcessSpecialMetadata(const InstPtr inst, char c, int) {
+    switch (c){
+    case 'p': AddArg(stack_.pop()); break;
+    default:
+        std::cerr << boost::format("WARNING: Unknown character in metadata: %c\n") % c;
+        break;
     }
+}
 
-    // Check ingoing edges to see if we want to add any extra output
-    InEdgeRange ier = boost::in_edges(v, _g);
-    for (InEdgeIterator ie = ier.first; ie != ier.second; ++ie)
-    {
-        GraphVertex in = boost::source(*ie, _g);
-        GroupPtr inGroup = GET(in);
-
-        if (!boost::get(boost::edge_attribute, _g, *ie)._isJump || inGroup->_stackLevel == -1)
-        {
-            continue;
-        }
-
-        switch (inGroup->_type)
-        {
-        case kDoWhileCondGroupType:
-            AddOutputLine(target_lang_->DoLoopHeader(), false, true);
-            break;
-        case kIfCondGroupType:
-            if (!cur_group_->_startElse)
-            {
-                AddOutputLine(target_lang_->EndBlock(LuaLanguage::END_OF_IF), true, false);
-            }
-            break;
-        case kWhileCondGroupType:
-            AddOutputLine(target_lang_->EndBlock(LuaLanguage::END_OF_WHILE), true, false);
-            break;
-        default:
-            break;
-        }
-    }
+ValueList CodeGenerator::GetArgList(){return arg_list_;}
 
-    ConstInstIterator it = cur_group_->start_;
-    do 
-    {
-        // If we only want to write labels that targets of goto's then check if this is the pass
-        // after we've setup mLabelRequired on each instruction. If this is set then it needs a label
-        // so write one out.
-        if (OutputOnlyRequiredLabels() && !mIsLabelPass && (*it)->mLabelRequired)
-        {
-            AddOutputLine(target_lang_->Label((*it)->_address));
-        }
-        ProcessInst(func, insts, *it);
-    } while (it++ != cur_group_->end_);
+ARGUMENT_ORDER CodeGenerator::GetBinaryOrder(){return bin_order_;}
 
-    // Add else end if necessary
-    for (ElseEndIterator elseIt = cur_group_->_endElse.begin(); elseIt != cur_group_->_endElse.end(); ++elseIt)
-    {
-        if (!(*elseIt)->_coalescedElse)
-        {
-            AddOutputLine(target_lang_->EndBlock(LuaLanguage::END_OF_IF_ELSE_CHAIN), true, false);
-        }
-    }
+void CodeGenerator::ProcessInst(Function& function, InstVec& insts, const InstPtr inst){
+    inst->ProcessInst(function, stack_, engine_, this);
+    if (inst->isCondJump()) ProcessCondJumpInst(inst);
+    else if (inst->IsUncondJump()) ProcessUncondJumpInst(function, insts, inst);
 }
 
-void CodeGenerator::ProcessUncondJumpInst(Function& func, InstVec& insts, const InstPtr inst)
-{
-    switch (cur_group_->_type)
-    {
-    case kBreakGroupType:
-        AddOutputLine(target_lang_->LoopBreak());
-        break;
-    case kContinueGroupType:
-        AddOutputLine(target_lang_->LoopContinue());
-        break;
-    default: // Might be a goto
-    {
-        bool printJump = true;
-        OutEdgeRange jumpTargets = boost::out_edges(_curVertex, _g);
-        for (OutEdgeIterator target = jumpTargets.first; target != jumpTargets.second && printJump; ++target)
-        {
-            Group* next = cur_group_->_next;
-            if (next)
+void CodeGenerator::ProcessUncondJumpInst(Function& function, InstVec& insts, const InstPtr inst){
+    switch (cur_group_->_type){
+        case kBreakGroupType: AddOutputLine(target_lang_->LoopBreak()); break;
+        case kContinueGroupType: AddOutputLine(target_lang_->LoopContinue()); break;
+        default: // Might be a goto.
             {
-                // Don't output jump to next vertex
-                if (boost::target(*target, _g) == next->_vertex)
-                {
-                    printJump = false;
-                    break;
-                }
-
-                // Don't output jump if next vertex starts an else block
-                if (next->_startElse)
-                {
-                    printJump = false;
-                    break;
-                }
-
-                OutEdgeRange targetR = boost::out_edges(boost::target(*target, _g), _g);
-                for (OutEdgeIterator targetE = targetR.first; targetE != targetR.second; ++targetE)
-                {
-                    // Don't output jump to while loop that has jump to next vertex
-                    if (boost::target(*targetE, _g) == next->_vertex)
-                    {
-                        printJump = false;
+                bool print_jump = true;
+                OutEdgeRange jump_targets = boost::out_edges(cur_vertex_, graph_);
+                for (
+                  OutEdgeIterator target = jump_targets.first;
+                  target != jump_targets.second && print_jump;
+                  ++ target
+                ){
+                    Group* next = cur_group_->_next;
+                    if (next){
+                        // Don't output jump to next vertex.
+                        if (boost::target(*target, graph_) == next->_vertex){
+                            print_jump = false;
+                            break;
+                        }
+                        // Don't output jump if next vertex starts an else block.
+                        if (next->_startElse){
+                            print_jump = false;
+                            break;
+                        }
+                        OutEdgeRange target_range = boost::out_edges(
+                          boost::target(*target, graph_), graph_
+                        );
+                        for (
+                          OutEdgeIterator target_it = target_range.first;
+                          target_it != target_range.second;
+                          ++ target_it
+                        ){
+                            // Don't output jump to while loop that has jump to next vertex.
+                            if (boost::target(*target_it, graph_) == next->_vertex) print_jump = false;
+                        }
+                        if (print_jump){
+                            // Check if this instruction is the last instruction in the function
+                            // and its an uncond jump.
+                            if (
+                              cur_group_->_type == kDoWhileCondGroupType
+                              && inst->_address == function.mEndAddr
+                              && inst->IsUncondJump()
+                            ){
+                                print_jump = false;
+                                AddOutputLine(
+                                  target_lang_->DoLoopFooter(true) + "true"
+                                  + target_lang_->DoLoopFooter(false), true, false
+                                );
+                            }
+                        }
                     }
                 }
-
-                if (printJump)
-                {
-                    // Check if this instruction is the last instruction in the function
-                    // and its an uncond jump
-                    if (cur_group_->_type == kDoWhileCondGroupType && inst->_address == func.mEndAddr && inst->IsUncondJump())
-                    {
-                        printJump = false;
-                        AddOutputLine(target_lang_->DoLoopFooter(true) + "true" + target_lang_->DoLoopFooter(false), true, false);
+                if (print_jump){
+                    const uint32 dst_addr = inst->GetDestAddress();
+                    if (is_label_pass_){
+                        // Mark the goto target.
+                        for (auto& i : insts){
+                            if (i->_address == dst_addr){
+                                i->mLabelRequired = true;
+                                break;
+                            }
+                        }
                     }
+                    AddOutputLine(target_lang_->Goto(dst_addr));
                 }
             }
-        }
+        break;
+    }
+}
 
-        if (printJump)
-        {
-            const uint32 dstAddr = inst->GetDestAddress();
-            if (mIsLabelPass)
-            {
-                // Mark the goto target
-                for (auto& i : insts)
-                {
-                    if (i->_address == dstAddr)
-                    {
-                        i->mLabelRequired = true;
-                        break;
+void CodeGenerator::ProcessCondJumpInst(const InstPtr inst){
+    std::stringstream s;
+    switch (cur_group_->_type){
+        case kIfCondGroupType:
+            if (cur_group_->_startElse && cur_group_->_code.size() == 1){
+                OutEdgeRange oer = boost::out_edges(cur_vertex_, graph_);
+                bool coalesce_else = false;
+                for (OutEdgeIterator oe = oer.first; oe != oer.second; ++ oe){
+                    GroupPtr oGr = GET(boost::target(*oe, graph_))->_prev;
+                    if (
+                      std::find(oGr->_endElse.begin(), oGr->_endElse.end(), cur_group_.get())
+                      != oGr->_endElse.end()
+                    ){
+                        coalesce_else = true;
                     }
                 }
+                if (coalesce_else){
+                    cur_group_->_code.clear();
+                    cur_group_->_coalescedElse = true;
+                    s << target_lang_->EndBlock(LuaLanguage::TO_ELSE_BLOCK)
+                      << " " << target_lang_->Else() << " ";
+                }
             }
-            AddOutputLine(target_lang_->Goto(dstAddr));
-        }
-    }
-        break;
+            s << target_lang_->If(true) << stack_.pop()->negate() << target_lang_->If(false);
+            AddOutputLine(s.str(), cur_group_->_coalescedElse, true);
+            break;
+        case kWhileCondGroupType:
+            s << target_lang_->WhileHeader(true) << stack_.pop()->negate()
+              << target_lang_->WhileHeader(false) << " "
+              << target_lang_->StartBlock(LuaLanguage::BEGIN_WHILE);
+            AddOutputLine(s.str(), false, true);
+            break;
+        case kDoWhileCondGroupType:
+            s << target_lang_->EndBlock(LuaLanguage::END_WHILE) <<  " "
+              << target_lang_->WhileHeader(true) << stack_.pop()
+              << target_lang_->WhileHeader(false);
+            AddOutputLine(s.str(), true, false);
+            break;
+        default:
+            break;
     }
 }
 
-void CodeGenerator::writeFunctionCall(std::string functionName, std::string paramsFormat, const std::vector<ValuePtr>& params)
-{
-    std::string strFuncCall = functionName + target_lang_->FunctionCallBegin();
-    const char* str = paramsFormat.c_str();
-    int paramIndex = 0;
-    while (*str)
-    {
-        bool skipArgument = false;
-        switch (*str)
-        {
-        case 'b':
-            strFuncCall += params[paramIndex]->getUnsigned() ? "true" : "false";
-            break;
+std::string CodeGenerator::IndentString(std::string s){
+    std::stringstream stream;
+    stream << std::string(INDENT_SPACES * indent_level_, ' ') << s;
+    return stream.str();
+}
 
-        case 'n':
-            strFuncCall += std::to_string(params[paramIndex]->getUnsigned());
-            break;
+std::string CodeGenerator::ConstructFuncSignature(const Function& function){return "";}
 
-        case 'f':
-            strFuncCall += std::to_string(static_cast<float>(params[paramIndex]->getUnsigned()) / 30.0f);
-            break;
+void CodeGenerator::OnBeforeStartFunction(const Function& function){}
 
-        case '_': // Ignore param
-            skipArgument = true;
-            break;
+void CodeGenerator::OnEndFunction(const Function& function){AddOutputLine("}", true, false);}
 
-        default:
-            throw std::runtime_error("Unknown param type");
-            break;
+void CodeGenerator::OnStartFunction(const Function& function){}
+
+bool CodeGenerator::OutputOnlyRequiredLabels() const{return false;}
+
+typedef std::pair<GraphVertex, ValueStack> DFSEntry;
+
+void CodeGenerator::GeneratePass(InstVec& insts, const Graph& graph){
+    graph_ = graph;
+    for (
+      FuncMap::iterator fn = engine_->_functions.begin(); fn != engine_->_functions.end(); ++ fn
+    ){
+        while (!stack_.empty()) stack_.pop();
+        GraphVertex entry_point = fn->second._v;
+        std::string func_signature = ConstructFuncSignature(fn->second);
+        // Write the function start.
+        bool print_func_signature = !func_signature.empty();
+        if (print_func_signature){
+            cur_group_ = GET(entry_point);
+            if (!(fn == engine_->_functions.begin())) AddOutputLine("");
+            OnBeforeStartFunction(fn->second);
+            AddOutputLine(func_signature, false, true);
+            OnStartFunction(fn->second);
         }
-        paramIndex++;
-        str++;
-        if (*str)
-        {
-            // There is another param
-            if (!skipArgument)
-            {
-                strFuncCall += target_lang_->FunctionCallArgumentSeperator() + " ";
+        GroupPtr last_group = GET(entry_point);
+        // DFS from entry point to process each vertex.
+        Stack<DFSEntry> dfs_stack;
+        std::set<GraphVertex> seen;
+        dfs_stack.push(DFSEntry(entry_point, ValueStack()));
+        seen.insert(entry_point);
+        while (!dfs_stack.empty()){
+            DFSEntry e = dfs_stack.pop();
+            GroupPtr tmp = GET(e.first);
+            if ((*tmp->start_)->_address > (*last_group->start_)->_address) last_group = tmp;
+            stack_ = e.second;
+            GraphVertex v = e.first;
+            Process(fn->second, insts, v);
+            OutEdgeRange r = boost::out_edges(v, graph_);
+            for (OutEdgeIterator i = r.first; i != r.second; ++ i){
+                GraphVertex target = boost::target(*i, graph_);
+                if (seen.find(target) == seen.end()){
+                    dfs_stack.push(DFSEntry(target, stack_));
+                    seen.insert(target);
+                }
             }
         }
-    }
-    strFuncCall += target_lang_->FunctionCallEnd();
-    AddOutputLine(strFuncCall);
-}
-
-void CodeGenerator::ProcessCondJumpInst(const InstPtr inst)
-{
-    std::stringstream s;
-    switch (cur_group_->_type)
-    {
-    case kIfCondGroupType:
-        if (cur_group_->_startElse && cur_group_->_code.size() == 1)
-        {
-            OutEdgeRange oer = boost::out_edges(_curVertex, _g);
-            bool coalesceElse = false;
-            for (OutEdgeIterator oe = oer.first; oe != oer.second; ++oe)
-            {
-                GroupPtr oGr = GET(boost::target(*oe, _g))->_prev;
-                if (std::find(oGr->_endElse.begin(), oGr->_endElse.end(), cur_group_.get()) != oGr->_endElse.end())
-                {
-                    coalesceElse = true;
+        // Write the function end
+        if (print_func_signature){
+            cur_group_ = last_group;
+            OnEndFunction(fn->second);
+        }
+        // Print output.
+        GroupPtr p = GET(entry_point);
+        while (p != NULL){
+            for (auto it = p->_code.begin(); it != p->_code.end(); ++ it){
+                if (it->_unindentBefore){
+                    assert(indent_level_ > 0);
+                    indent_level_--;
                 }
+                if (OutputOnlyRequiredLabels()) output_ << IndentString(it->_line) << std::endl;
+                else{
+                    output_ << (
+                      boost::format("%08X: %s") % (*p->start_)->_address % IndentString(it->_line)
+                    ) << std::endl;
+                }
+                if (it->_indentAfter) indent_level_++;
             }
-            if (coalesceElse)
-            {
-                cur_group_->_code.clear();
-                cur_group_->_coalescedElse = true;
-                s << target_lang_->EndBlock(LuaLanguage::TO_ELSE_BLOCK) << " " << target_lang_->Else() << " ";
-            }
+            p = p->_next;
         }
-        s << target_lang_->If(true) << _stack.pop()->negate() << target_lang_->If(false);
-        AddOutputLine(s.str(), cur_group_->_coalescedElse, true);
-        break;
-    case kWhileCondGroupType:
-        s << target_lang_->WhileHeader(true) << _stack.pop()->negate() << target_lang_->WhileHeader(false) << " " << target_lang_->StartBlock(LuaLanguage::BEGIN_WHILE);
-        AddOutputLine(s.str(), false, true);
-        break;
-    case kDoWhileCondGroupType:
-        s << target_lang_->EndBlock(LuaLanguage::END_WHILE) <<  " " << target_lang_->WhileHeader(true) << _stack.pop() << target_lang_->WhileHeader(false);
-        AddOutputLine(s.str(), true, false);
-        break;
-    default:
-        break;
     }
 }
 
-void CodeGenerator::ProcessInst(Function& func, InstVec& insts, const InstPtr inst)
-{
-    inst->ProcessInst(func, _stack, _engine, this);
-    if (inst->isCondJump())
-    {
-        ProcessCondJumpInst(inst);
+void CodeGenerator::Process(Function& function, InstVec& insts, GraphVertex vertex){
+    cur_vertex_ = vertex;
+    cur_group_ = GET(vertex);
+
+    // Check if we should add else start
+    if (cur_group_->_startElse){
+        AddOutputLine(
+          target_lang_->EndBlock(LuaLanguage::TO_ELSE_BLOCK) + " " + target_lang_->Else()
+          + " " + target_lang_->StartBlock(LuaLanguage::BEGIN_ELSE), true, true
+        );
     }
-    else if (inst->IsUncondJump())
-    {
-        ProcessUncondJumpInst(func, insts, inst);
+    // Check ingoing edges to see if we want to add any extra output
+    InEdgeRange ier = boost::in_edges(vertex, graph_);
+    for (InEdgeIterator ie = ier.first; ie != ier.second; ++ ie){
+        GraphVertex in = boost::source(*ie, graph_);
+        GroupPtr in_group = GET(in);
+        if (!boost::get(boost::edge_attribute, graph_, *ie)._isJump || in_group->_stackLevel == -1)
+            continue;
+        switch (in_group->_type){
+            case kDoWhileCondGroupType:
+                AddOutputLine(target_lang_->DoLoopHeader(), false, true);
+                break;
+            case kIfCondGroupType:
+                if (!cur_group_->_startElse)
+                    AddOutputLine(target_lang_->EndBlock(LuaLanguage::END_OF_IF), true, false);
+                break;
+            case kWhileCondGroupType:
+                AddOutputLine(target_lang_->EndBlock(LuaLanguage::END_OF_WHILE), true, false);
+                break;
+            default:
+                break;
+        }
     }
-}
-
-void CodeGenerator::addArg(ValuePtr p) 
-{
-    if (_callOrder == FIFO_ARGUMENT_ORDER)
-        _argList.push_front(p);
-    else if (_callOrder == LIFO_ARGUMENT_ORDER)
-        _argList.push_back(p);
-}
-
-void CodeGenerator::ProcessSpecialMetadata(const InstPtr inst, char c, int) 
-{
-    switch (c) 
-    {
-    case 'p':
-        addArg(_stack.pop());
-        break;
-    default:
-        std::cerr << boost::format("WARNING: Unknown character in metadata: %c\n") % c;
-        break;
+    ConstInstIterator it = cur_group_->start_;
+    do{
+        // If we only want to write labels that targets of goto's then check
+        // if this is the pass after we've setup mLabelRequired on each
+        // instruction. If this is set then it needs a label so write one out.
+        if (OutputOnlyRequiredLabels() && !is_label_pass_ && (*it)->mLabelRequired)
+            AddOutputLine(target_lang_->Label((*it)->_address));
+        ProcessInst(function, insts, *it);
+    } while (it++ != cur_group_->end_);
+    // Add else end if necessary
+    for (
+      ElseEndIterator else_it = cur_group_->_endElse.begin();
+      else_it != cur_group_->_endElse.end();
+      ++ else_it
+    ){
+        if (!(*else_it)->_coalescedElse)
+            AddOutputLine(target_lang_->EndBlock(LuaLanguage::END_OF_IF_ELSE_CHAIN), true, false);
     }
 }

+ 8 - 8
V-Gears-Installer/src/decompiler/field/FieldCodeGenerator.cpp

@@ -88,8 +88,8 @@ void FF7::FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
     
     std::vector<std::pair<Function&, InstVec>> functions_with_bodies;
     for (
-      auto function = _engine->_functions.begin();
-      function != _engine->_functions.end();
+      auto function = engine_->_functions.begin();
+      function != engine_->_functions.end();
       ++ function
     ){
         InstVec body;
@@ -147,7 +147,7 @@ void FF7::FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
                     AddOutputLine((boost::format("::label_0x%1$X::") % label->first).str());
             }
             ValueStack stack;
-            (*instruction)->ProcessInst(function->first, stack, _engine, this);
+            (*instruction)->ProcessInst(function->first, stack, engine_, this);
             if (end_needed){
                 AddOutputLine("end -- end if", true, false);
                 end_needed = false;
@@ -201,11 +201,11 @@ void FF7::FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
     }
 
     for (auto i = lines_.begin(); i != lines_.end(); ++i){
-        if (i->_unindentBefore && _indentLevel > 0){
-            _indentLevel --;
+        if (i->_unindentBefore && indent_level_ > 0){
+            indent_level_ --;
         }
-        _output << indentString(i->_line) << std::endl;
-        if (i->_indentAfter) _indentLevel++;
+        output_ << IndentString(i->_line) << std::endl;
+        if (i->_indentAfter) indent_level_++;
     }
 }
 
@@ -214,7 +214,7 @@ void FF7::FieldCodeGenerator::AddOutputLine(
 ){lines_.push_back(CodeLine(line, unindent_before, indent_after));}
 
 float FF7::FieldCodeGenerator::GetScaleFactor() const
-{return static_cast<FieldEngine*>(_engine)->GetScaleFactor();}
+{return static_cast<FieldEngine*>(engine_)->GetScaleFactor();}
 
 void FF7::FieldCodeGenerator::OnBeforeStartFunction(const Function& function){
     FunctionMetaData meta_data(function._metadata);

+ 1 - 1
V-Gears-Installer/src/decompiler/field/instruction/FieldMathInstruction.cpp

@@ -71,7 +71,7 @@ void FF7::FieldMathInstruction::ProcessInst(
                   cg->GetFormatter(), dest_bank, dest_address
                 );
                 code_gen->AddOutputLine(
-                  source + " = " + source + " % " + dest + code_gen->TargetLang().LineTerminator()
+                  source + " = " + source + " % " + dest + code_gen->GetLanguage().LineTerminator()
                 );
             }
         break;

+ 4 - 4
V-Gears-Installer/src/decompiler/instruction.cpp

@@ -126,9 +126,9 @@ void BoolNegateStackInstruction::ProcessInst(Function&, ValueStack &stack, Engin
 void BinaryOpStackInstruction::ProcessInst(Function&, ValueStack &stack, Engine*, CodeGenerator *codeGen) {
 	ValuePtr op1 = stack.pop();
 	ValuePtr op2 = stack.pop();
-	if (codeGen->_binOrder == FIFO_ARGUMENT_ORDER)
+	if (codeGen->GetBinaryOrder() == FIFO_ARGUMENT_ORDER)
 		stack.push(new BinaryOpValue(op2, op1, _codeGenData));
-	else if (codeGen->_binOrder == LIFO_ARGUMENT_ORDER)
+	else if (codeGen->GetBinaryOrder() == LIFO_ARGUMENT_ORDER)
 		stack.push(new BinaryOpValue(op1, op2, _codeGenData));
 }
 
@@ -149,12 +149,12 @@ void UnaryOpPostfixStackInstruction::ProcessInst(Function& , ValueStack &stack,
 }
 
 void KernelCallStackInstruction::ProcessInst(Function&, ValueStack &stack, Engine*, CodeGenerator *codeGen) {
-	codeGen->_argList.clear();
+	codeGen->GetArgList().clear();
 	bool returnsValue = (_codeGenData.find("r") == 0);
 	std::string metadata = (!returnsValue ? _codeGenData : _codeGenData.substr(1));
 	for (size_t i = 0; i < metadata.length(); i++)
 		codeGen->ProcessSpecialMetadata(this, metadata[i], i);
-	stack.push(new CallValue(_name, codeGen->_argList));
+	stack.push(new CallValue(_name, codeGen->GetArgList()));
 	if (!returnsValue) {
 		std::stringstream stream;
 		stream << stack.pop() << ";";