/* * V-Gears * Copyright (C) 2022 V-Gears Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "decompiler/field/instruction/FieldControlFlowInstruction.h" #include "decompiler/field/FieldEngine.h" #include "decompiler/field/FieldCodeGenerator.h" #include "decompiler/field/FieldDisassembler.h" void FF7::FF7ControlFlowInstruction::ProcessInst( Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen ){ FF7::FieldEngine& eng = static_cast(*engine); FunctionMetaData md(func.metadata); switch (opcode_){ case OPCODES::RET: // A few notes in RET. // - Lua requires all functions to end with a return. // - Lua doesn's like returns if not followed by an end. // - Don't add a return statement to on_start functions. // Game 'init' and 'main' code is concatenated in V-Gears. If the // 'main' return is kept, the code of 'main' is never executed. // There are safeguards forfunctions without a return in the end, // so it's OK not to include it here. if (func.name != "on_start") code_gen->AddOutputLine("do return 0 end"); break; case OPCODES::REQ: ProcessREQ(code_gen, eng); break; case OPCODES::REQSW: ProcessREQSW(code_gen, eng); break; case OPCODES::REQEW: ProcessREQEW(code_gen, eng); break; case OPCODES::PREQ: code_gen->WriteTodo(md.GetEntityName(), "PREQ"); break; case OPCODES::PRQSW: code_gen->WriteTodo(md.GetEntityName(), "PRQSW"); break; case OPCODES::PRQEW: code_gen->WriteTodo(md.GetEntityName(), "PRQEW"); break; case OPCODES::RETTO: ProcessRETTO(code_gen); break; case OPCODES::WAIT: ProcessWAIT(code_gen); break; default: code_gen->AddOutputLine(FF7::FieldCodeGenerator::FormatInstructionNotImplemented( md.GetEntityName(), address_, opcode_ )); } } void FF7::FF7ControlFlowInstruction::ProcessREQ( CodeGenerator* code_gen, const FieldEngine& engine ){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& entity = engine.EntityByIndex(params_[0]->getSigned()); const auto& script_name = cg->GetFormatter().FunctionName( entity.GetName(), entity.FunctionByIndex(params_[2]->getUnsigned()) ); auto priority = params_[1]->getUnsigned(); code_gen->AddOutputLine(( boost::format("script:request(Script.ENTITY, \"%1%\", \"%2%\", %3%)") % entity.GetName() % script_name % priority ).str()); } void FF7::FF7ControlFlowInstruction::ProcessREQSW( CodeGenerator* code_gen, const FieldEngine& engine ){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& entity = engine.EntityByIndex(params_[0]->getSigned()); const auto& script_name = cg->GetFormatter().FunctionName( entity.GetName(), entity.FunctionByIndex(params_[2]->getUnsigned()) ); auto priority = params_[1]->getUnsigned(); code_gen->AddOutputLine(( boost::format("script:request_start_sync(Script.ENTITY, \"%1%\", \"%2%\", %3%)") % entity.GetName() % script_name % priority ).str()); } void FF7::FF7ControlFlowInstruction::ProcessREQEW( CodeGenerator* code_gen, const FieldEngine& engine ){ try{ FieldCodeGenerator* cg = static_cast(code_gen); const auto& entity = engine.EntityByIndex(params_[0]->getSigned()); const auto& script_name = cg->GetFormatter().FunctionName( entity.GetName(), entity.FunctionByIndex(params_[2]->getUnsigned()) ); auto priority = params_[1]->getUnsigned(); code_gen->AddOutputLine(( boost::format("script:request_end_sync(Script.ENTITY, \"%1%\", \"%2%\", %3%)") % entity.GetName() % script_name % priority ).str()); } catch (const InternalDecompilerError&){ code_gen->AddOutputLine(( boost::format("-- ERROR call to non existing function index %1%") % params_[2]->getUnsigned() ).str()); } } void FF7::FF7ControlFlowInstruction::ProcessRETTO(CodeGenerator* code_gen){ auto entity_index = params_[0]->getUnsigned(); auto priority = params_[1]->getUnsigned(); code_gen->AddOutputLine(( boost::format("-- return_to(script_id_in_current_entity=%2%, priority=%1%)") % entity_index % priority ).str()); } void FF7::FF7ControlFlowInstruction::ProcessWAIT(CodeGenerator* code_gen){ code_gen->AddOutputLine(( boost::format("script:wait(%1%)") % (params_[0]->getUnsigned() / 30.0f) ).str()); }