#include "decompiler/field/ff7_field_engine.h" #include #include #include #include "../../../include/decompiler/field/FieldCodeGenerator.h" #include "../../../include/decompiler/field/FieldDisassembler.h" #define GET(vertex) (boost::get(boost::vertex_name, g, vertex)) /* OpCodes which need implementing (already done in DAT dumper) "BLINK" "XYI" "CMOVE" "MOVA" "TURA" "ANIMW" "FMOVE" "ANIME2" "ANIM_1" "CANIM1" ? "CANM_1" "TURN" "DIRA" "GETDIR" "GETAXY" "JUMP" "AXYZI" "LADER" "OFST" "TALKR" "ANIMB" "TURNW" */ std::unique_ptr FF7FieldEngine::getDisassembler( InstVec &insts, const std::vector& raw_script_data ){ auto ret = std::make_unique(mFormatter, this, insts, raw_script_data); scale_factor_ = ret->GetScaleFactor(); return std::move(ret); } std::unique_ptr FF7FieldEngine::getDisassembler(InstVec &insts){ auto ret = std::make_unique(mFormatter, this, insts); scale_factor_ = ret->GetScaleFactor(); return std::move(ret); } std::unique_ptr FF7FieldEngine::getCodeGenerator( const InstVec& insts, std::ostream &output ){ // The broken version: //return std::make_unique(this, insts, output); // The not-as-nice-but-at-least-it-works version: return std::make_unique(this, insts, output, mFormatter); } void FF7FieldEngine::postCFG(InstVec& insts, Graph g){ /* // In FF7 some scripts ends with an infinite loop to "keep it alive" // in VGears this isn't required so we can remove them RemoveTrailingInfiniteLoops(insts, g); // This could generate bad code, but it always seems to follow that pattern that if the last // instruction is an uncond jump back into the script then it simply nests all of those blocks // in an infinite loop MarkInfiniteLoopGroups(insts, g); // Scripts end with a "return" this isn't required so strip them out RemoveExtraneousReturnStatements(insts, g); */ } std::map FF7FieldEngine::GetEntities() const{ std::map r; for (auto& f : _functions){ const Function& func = f.second; FunctionMetaData meta(func._metadata); auto it = r.find(meta.GetEntityName()); if (it != std::end(r)){ // Try to find a function in this entity has that has a char id // don't overwrite a valid char id with a "blank" one if (it->second == -1) it->second = meta.GetCharacterId(); } // TODO: Don't add lines here: else r[meta.GetEntityName()] = meta.GetCharacterId(); } return r; } std::vector FF7FieldEngine::GetEntityList() const{ std::vector entities; for (auto entity: mEntityIndexMap){ if (entity.second.IsLine() == false){ SUDM::Field::FieldEntity ent; ent.name = entity.second.Name(); ent.index = entity.second.GetIndex(); // Get character ID. ent.char_id = -1; std::map r; //if (ent.name == "Cloud") std::cout << "CHAR_ID FOR CLOUD" << std::endl; for (auto& f : _functions){ const Function& func = f.second; FunctionMetaData meta(func._metadata); if (meta.GetEntityName() == ent.name){ ent.char_id = meta.GetCharacterId(); break; } } entities.push_back(ent); } } return entities; } std::vector FF7FieldEngine::GetLineList() const{ std::vector lines; for (auto entity: mEntityIndexMap){ if (entity.second.IsLine() == true){ SUDM::Field::Line line; line.name = entity.second.Name(); line.point_a = entity.second.GetLinePointA(); line.point_b = entity.second.GetLinePointB(); lines.push_back(line); } } return lines; } void FF7FieldEngine::AddEntityFunction( const std::string& entity_name, size_t entity_index, const std::string& func_name, size_t func_index ){ auto it = mEntityIndexMap.find(entity_index); if (it != std::end(mEntityIndexMap)) (*it).second.AddFunction(func_name, func_index); else{ Entity e(entity_name, entity_index); e.AddFunction(func_name, func_index); mEntityIndexMap.insert(std::make_pair(entity_index, e)); } } void FF7FieldEngine::MarkEntityAsLine( size_t entity_index, bool line, std::vector point_a, std::vector point_b ){ auto it = mEntityIndexMap.find(entity_index); if (it != std::end(mEntityIndexMap)){ (*it).second.MarkAsLine(line, point_a, point_b); } } bool FF7FieldEngine::EntityIsLine(size_t entity_index){ auto it = mEntityIndexMap.find(entity_index); if (it != std::end(mEntityIndexMap)) return (*it).second.IsLine(); return false; } void FF7FieldEngine::RemoveExtraneousReturnStatements(InstVec& insts, Graph g){ for (auto& f : _functions){ Function& func = f.second; for (auto it = insts.begin(); it != insts.end(); it ++){ // Is it the last instruction in the function, and is it a return statement? if ((*it)->_address == func.mEndAddr){ if ((*it)->_opcode == OPCODE::RET){ // Set new end address to be before the NOP. func.mEndAddr = (*(it - 1))->_address; func.mNumInstructions --; Instruction* nop = new FF7NoOperationInstruction(); nop->_opcode = OPCODE::NOP; nop->_address = (*it)->_address; (*it).reset(nop); break; } } } } } void FF7FieldEngine::RemoveTrailingInfiniteLoops(InstVec& insts, Graph g){ for (auto& f : _functions){ Function& func = f.second; for (auto it = insts.begin(); it != insts.end(); it ++){ // Is it the last instruction in the function, a jump, and a jumping to itself? if ((*it)->_address == func.mEndAddr){ if ((*it)->isJump() && (*it)->getDestAddress() == (*it)->_address){ // Set new end address to be before the NOP func.mEndAddr = (*(it - 1))->_address; func.mNumInstructions--; Instruction* nop = new FF7NoOperationInstruction(); nop->_opcode = OPCODE::NOP; nop->_address = (*it)->_address; (*it).reset(nop); break; } } } } } void FF7FieldEngine::MarkInfiniteLoopGroups(InstVec& insts, Graph g){ for (auto& f : _functions){ Function& func = f.second; for (auto it = insts.begin(); it != insts.end(); it ++){ if ((*it)->_address == func.mEndAddr){ // Note: This is a "best effort heuristic", so quite a few loops will // still end up as goto's. This could potentially generate invalid code too. if ((*it)->isUncondJump()){ // Then assume its an infinite do { } while(true) // loop that wraps part of the script. VertexRange vr = boost::vertices(g); for (VertexIterator v = vr.first; v != vr.second; ++ v){ GroupPtr gr = GET(*v); if ((*gr->_start)->_address == func.mEndAddr){ // Then assume its an infinite do { } while(true) loop // that wraps part of the script. gr->_type = kDoWhileCondGroupType; } } } break; } } } } void FF7CondJumpInstruction::processInst( Function& function, ValueStack &stack, Engine* engine, CodeGenerator* code_gen ){ FieldCodeGenerator* cg = static_cast(code_gen); std::string funcName; if (_opcode == OPCODE::IFKEYON) funcName = "entity_manager:is_key_on"; else if (_opcode == OPCODE::IFKEYOFF) funcName = "entity_manager:is_key_off"; else if (_opcode == OPCODE::IFKEY) funcName = "Key"; else if (_opcode == OPCODE::IFMEMBQ) funcName = "IFMEMBQ"; else if (_opcode == OPCODE::IFPRTYQ) funcName = "IFPRTYQ"; // If condition is a function, add and stop. if (!funcName.empty()){ uint32 param = _params[0]->getUnsigned(); // Special cases. The first parameter of IFKEY, IFKEYON and IFKEYOFF // can be ORed to get the individual keys, but there are two invalid // ones: 512 and 1024. They must be XORed. if ( _opcode == OPCODE::IFKEY || _opcode == OPCODE::IFKEYON || _opcode == OPCODE::IFKEYOFF ){ if (param >= 1024) param = param ^ 1024; if (param >= 512) param = param ^ 512; } ValuePtr v = new UnqotedStringValue(funcName + "(" + std::to_string(param) + ")"); stack.push(v); return; } std::string op; uint32 type = _params[4]->getUnsigned(); const auto& source = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned() ); const auto& destination = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned() ); switch (type){ case 0: op = "=="; break; case 1: op = "~="; break; case 2: op = ">"; break; case 3: op = "<"; break; case 4: op = ">="; break; case 5: op = "<="; break; case 6: op = "&"; break; case 7: op = "^"; break; case 8: op = "|"; break; case 9: { op = "bit(" + _params[0]->getString() + ", " + _params[2]->getString() + ", " + destination + ") == 1"; ValuePtr v = new UnqotedStringValue(op); stack.push(v); } return; case 0xA: { op = "bit(" + _params[0]->getString() + ", " + _params[2]->getString() + ", " + destination + ") == 0"; ValuePtr v = new UnqotedStringValue(op); stack.push(v); } return; default: throw UnknownConditionalOperatorException(_address, type); } ValuePtr v = new BinaryOpValue(new VarValue(source), new VarValue(destination), op); stack.push(v); } uint32 FF7CondJumpInstruction::getDestAddress() const{ uint32 params_size = 0; uint32 jump_param_index = 5; switch (_opcode){ case OPCODE::IFUB: params_size = 5; break; case OPCODE::IFUBL: params_size = 5; break; case OPCODE::IFSW: params_size = 7; break; case OPCODE::IFSWL: params_size = 7; break; case OPCODE::IFUW: params_size = 7; break; case OPCODE::IFUWL: params_size = 7; break; case OPCODE::IFKEYON: case OPCODE::IFKEYOFF: case OPCODE::IFKEY: params_size = 3; jump_param_index = 1; break; case OPCODE::IFPRTYQ: case OPCODE::IFMEMBQ: params_size = 2; jump_param_index = 1; break; default: throw UnknownJumpTypeException(_address, _opcode); } return _address + _params[jump_param_index]->getUnsigned() + params_size; } std::ostream& FF7CondJumpInstruction::print(std::ostream &output) const{ Instruction::print(output); output << " (False target address: 0x" << std::hex << getDestAddress() << std::dec << ")"; return output; } bool FF7UncondJumpInstruction::isFuncCall() const{return _isCall;} bool FF7UncondJumpInstruction::isUncondJump() const{return !_isCall;} uint32 FF7UncondJumpInstruction::getDestAddress() const{ if ( static_cast(_opcode) == OPCODE::JMPF || static_cast(_opcode) == OPCODE::JMPFL){ // Short or long forward jump. return _address + _params[0]->getUnsigned() + 1; } // Backwards jump, OPCODE::JMPB/L. return _address - _params[0]->getUnsigned(); } std::ostream& FF7UncondJumpInstruction::print(std::ostream &output) const{ Instruction::print(output); output << " (Jump target address: 0x" << std::hex << getDestAddress() << std::dec << ")"; return output; } void FF7UncondJumpInstruction::processInst( Function& func, ValueStack&, Engine* engine, CodeGenerator* code_gen ){ switch (_opcode){ case OPCODE::JMPB: case OPCODE::JMPBL: // HACK: Hard loop will hang the game, insert a wait to yield control code_gen->AddOutputLine("-- Hack, yield control for possible inf loop"); code_gen->AddOutputLine("script:wait(0)"); break; } } static void WriteTodo(CodeGenerator *code_gen, std::string entity_name, std::string opCode){ code_gen->AddOutputLine("-- Todo(\"" + opCode + "\")"); } void FF7ControlFlowInstruction::processInst( Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen ){ FF7FieldEngine& eng = static_cast(*engine); FunctionMetaData md(func._metadata); switch (_opcode){ case OPCODE::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 OPCODE::REQ: processREQ(code_gen, eng); break; case OPCODE::REQSW: processREQSW(code_gen, eng); break; case OPCODE::REQEW: processREQEW(code_gen, eng); break; case OPCODE::PREQ: WriteTodo(code_gen, md.GetEntityName(), "PREQ"); break; case OPCODE::PRQSW: WriteTodo(code_gen, md.GetEntityName(), "PRQSW"); break; case OPCODE::PRQEW: WriteTodo(code_gen, md.GetEntityName(), "PRQEW"); break; case OPCODE::RETTO: processRETTO(code_gen); break; case OPCODE::WAIT: processWAIT(code_gen); break; default: code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented( md.GetEntityName(), _address, _opcode )); } } void FF7ControlFlowInstruction::processREQ( CodeGenerator* code_gen, const FF7FieldEngine& engine ){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& entity = engine.EntityByIndex(_params[0]->getSigned()); const auto& script_name = cg->GetFormatter().FunctionName( entity.Name(), entity.FunctionByIndex(_params[2]->getUnsigned()) ); auto priority = _params[1]->getUnsigned(); code_gen->AddOutputLine(( boost::format("script:request(Script.ENTITY, \"%1%\", \"%2%\", %3%)") % entity.Name() % script_name % priority ).str()); } void FF7ControlFlowInstruction::processREQSW( CodeGenerator* code_gen, const FF7FieldEngine& engine ){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& entity = engine.EntityByIndex(_params[0]->getSigned()); const auto& script_name = cg->GetFormatter().FunctionName( entity.Name(), 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.Name() % script_name % priority ).str()); } void FF7ControlFlowInstruction::processREQEW( CodeGenerator* code_gen, const FF7FieldEngine& engine ){ try{ FieldCodeGenerator* cg = static_cast(code_gen); const auto& entity = engine.EntityByIndex(_params[0]->getSigned()); const auto& script_name = cg->GetFormatter().FunctionName( entity.Name(), 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.Name() % 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 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 FF7ControlFlowInstruction::processWAIT(CodeGenerator* code_gen){ code_gen->AddOutputLine(( boost::format("script:wait(%1%)") % (_params[0]->getUnsigned() / 30.0f) ).str()); } void FF7ModuleInstruction::processInst( Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen ){ FunctionMetaData md(func._metadata); switch (_opcode){ case OPCODE::DSKCG: WriteTodo(code_gen, md.GetEntityName(), "DSKCG"); break; case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::ARROW: code_gen->AddOutputLine(( boost::format("game:pointer_enable(%1%)") % (_params[0]->getUnsigned() ? "true" : "false") ).str()); break; case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::PNAME: WriteTodo(code_gen, md.GetEntityName(), "PNAME"); break; case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::GMSPD: WriteTodo(code_gen, md.GetEntityName(), "GMSPD"); break; case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::SMSPD: WriteTodo(code_gen, md.GetEntityName(), "SMSPD"); break; case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::FLMAT: code_gen->AddOutputLine("game:fill_materia()"); break; case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::FLITM: WriteTodo(code_gen, md.GetEntityName(), "FLITM"); break; case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::BTLCK: code_gen->AddOutputLine(( boost::format("game:battle_enable(%1%)") % (_params[0]->getUnsigned() ? "true" : "false") ).str()); break; case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::MVLCK: code_gen->AddOutputLine(( boost::format("game:movie_enable(%1%)") % (_params[0]->getUnsigned() ? "true" : "false") ).str()); break; case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::SPCNM: WriteTodo(code_gen, md.GetEntityName(), "SPCNM"); break; case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::RSGLB: code_gen->AddOutputLine("game:global_reset()"); break; case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::CLITM: WriteTodo(code_gen, md.GetEntityName(), "CLITM"); break; case OPCODE::MINIGAME: WriteTodo(code_gen, md.GetEntityName(), "MINIGAME"); break; case OPCODE::BTMD2: WriteTodo(code_gen, md.GetEntityName(), "BTMD2"); break; case OPCODE::BTRLD: WriteTodo(code_gen, md.GetEntityName(), "BTRLD"); break; case OPCODE::BTLTB: WriteTodo(code_gen, md.GetEntityName(), "BTLTB"); break; case OPCODE::MAPJUMP: processMAPJUMP(code_gen, func); break; case OPCODE::LSTMP: WriteTodo(code_gen, md.GetEntityName(), "LSTMP"); break; case OPCODE::BATTLE: processBATTLE(code_gen); break; case OPCODE::BTLON: processBTLON(code_gen); break; case OPCODE::BTLMD: WriteTodo(code_gen, md.GetEntityName(), "BTLMD"); break; case OPCODE::MPJPO: // Gateway function will do nothing if this is set to true code_gen->AddOutputLine( std::string("FFVII.Data.DisableGateways=") + (_params[0]->getUnsigned() ? "true" : "false") ); break; // Prepare to change map, don't need to output anything for this. case OPCODE::PMJMP: code_gen->AddOutputLine("-- Prepare map change"); break; // Prepare to change map, don't need to output anything for this, // seems to be the same thing as PMJMP case OPCODE::PMJMP2: code_gen->AddOutputLine("-- Prepare map change 2"); break; case OPCODE::GAMEOVER: WriteTodo(code_gen, md.GetEntityName(), "GAMEOVER"); break; default: code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented( md.GetEntityName(), _address, _opcode )); } } void FF7ModuleInstruction::processBATTLE(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& battle_id = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[1]->getUnsigned() ); code_gen->AddOutputLine((boost::format("entity_manager:battle_run(%1%)") % battle_id).str()); } void FF7ModuleInstruction::processBTLON(CodeGenerator* code_gen){ code_gen->AddOutputLine(( boost::format("entity_manager:random_encounters_on(%1%)") % FieldCodeGenerator::FormatInvertedBool(_params[0]->getUnsigned()) ).str()); } void FF7ModuleInstruction::processMAPJUMP(CodeGenerator* code_gen, Function& func){ FieldCodeGenerator* cg = static_cast(code_gen); const auto target_map_id = _params[0]->getUnsigned(); FunctionMetaData md(func._metadata); const std::string source_spawn_point_name = cg->GetFormatter().SpawnPointName(target_map_id, md.GetEntityName(), func._name, _address); cg->GetFormatter().AddSpawnPoint( target_map_id, md.GetEntityName(), func._name, _address, _params[1]->getSigned(), // X _params[2]->getSigned(), // Y _params[3]->getSigned(), // Walk mesh triangle ID _params[4]->getSigned() // Angle ); const std::string target_map_name = cg->GetFormatter().MapName(target_map_id); code_gen->AddOutputLine( "load_field_map_request(\"" + target_map_name + "\", \"" + source_spawn_point_name + "\")" ); } void FF7MathInstruction::processInst( Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen ){ FieldCodeGenerator* cg = static_cast(code_gen); FunctionMetaData md(func._metadata); switch (_opcode){ case OPCODE::PLUS_: processSaturatedPLUS(code_gen); break; case OPCODE::PLUS2_: processSaturatedPLUS2(code_gen); break; case OPCODE::MINUS_: processSaturatedMINUS(code_gen); break; case OPCODE::MINUS2_: processSaturatedMINUS2(code_gen); break; case OPCODE::INC_: processSaturatedINC(code_gen); break; case OPCODE::INC2_: processSaturatedINC2(code_gen); break; case OPCODE::DEC_: processSaturatedDEC(code_gen); break; case OPCODE::DEC2_: processSaturatedDEC2(code_gen); break; case OPCODE::RDMSD: processRDMSD(code_gen); break; case OPCODE::SETBYTE: case OPCODE::SETWORD: processSETBYTE_SETWORD(code_gen); break; case OPCODE::BITON: processBITON(code_gen); break; case OPCODE::BITOFF: processBITOFF(code_gen); break; case OPCODE::BITXOR: WriteTodo(code_gen, md.GetEntityName(), "BITXOR"); break; case OPCODE::PLUS: case OPCODE::PLUS2: processPLUSx_MINUSx(code_gen, "+"); break; case OPCODE::MINUS: case OPCODE::MINUS2: processPLUSx_MINUSx(code_gen, "-"); break; case OPCODE::MUL: WriteTodo(code_gen, md.GetEntityName(), "MUL"); break; case OPCODE::MUL2: WriteTodo(code_gen, md.GetEntityName(), "MUL2"); break; case OPCODE::DIV: WriteTodo(code_gen, md.GetEntityName(), "DIV"); break; case OPCODE::DIV2: WriteTodo(code_gen, md.GetEntityName(), "DIV2"); break; case OPCODE::MOD: { const uint32 source_bank = _params[0]->getUnsigned(); const uint32 source_address_or_value = _params[2]->getUnsigned(); auto source = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), source_bank, source_address_or_value ); const uint32 dest_bank = _params[1]->getUnsigned(); const uint32 dest_address = _params[3]->getUnsigned(); auto dest = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), dest_bank, dest_address ); code_gen->AddOutputLine( source + " = " + source + " % " + dest + code_gen->TargetLang().LineTerminator() ); } break; case OPCODE::MOD2: WriteTodo(code_gen, md.GetEntityName(), "MOD2"); break; case OPCODE::AND: WriteTodo(code_gen, md.GetEntityName(), "AND"); break; case OPCODE::AND2: WriteTodo(code_gen, md.GetEntityName(), "AND2"); break; case OPCODE::OR: WriteTodo(code_gen, md.GetEntityName(), "OR"); break; case OPCODE::OR2: WriteTodo(code_gen, md.GetEntityName(), "OR2"); break; case OPCODE::XOR: WriteTodo(code_gen, md.GetEntityName(), "XOR"); break; case OPCODE::XOR2: WriteTodo(code_gen, md.GetEntityName(), "XOR2"); break; case OPCODE::INC: case OPCODE::INC2: processINCx_DECx(code_gen, "+"); break; case OPCODE::DEC: case OPCODE::DEC2: processINCx_DECx(code_gen, "-"); break; case OPCODE::RANDOM: processRANDOM(code_gen); break; case OPCODE::LBYTE: WriteTodo(code_gen, md.GetEntityName(), "LBYTE"); break; case OPCODE::HBYTE: WriteTodo(code_gen, md.GetEntityName(), "HBYTE"); break; case OPCODE::TWOBYTE: WriteTodo(code_gen, md.GetEntityName(), "2BYTE"); break; case OPCODE::SIN: WriteTodo(code_gen, md.GetEntityName(), "SIN"); break; case OPCODE::COS: WriteTodo(code_gen, md.GetEntityName(), "COS"); break; default: code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented( md.GetEntityName(), _address, _opcode )); } } void FF7MathInstruction::processSaturatedPLUS(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); // TODO: Check for assignment to value. const auto& lhs = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned() ); const auto& rhs = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned() ); // TODO: Respect destination bank sizes and negative wrap-around. code_gen->AddOutputLine((boost::format("%1% = %1% + %2%") % lhs % rhs).str()); code_gen->AddOutputLine((boost::format("--if (%1% > 255); %1% = 255; end") % lhs).str()); } void FF7MathInstruction::processSaturatedPLUS2(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); // TODO: Check for assignment to value. const auto& lhs = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned() ); const auto& rhs = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned() ); // TODO: Respect destination bank sizes and negative wrap-around. code_gen->AddOutputLine((boost::format("%1% = %1% + %2%") % lhs % rhs).str()); code_gen->AddOutputLine((boost::format("--if (%1% > 32767); %1% = 32767; end") % lhs).str()); } void FF7MathInstruction::processSaturatedMINUS(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); // TODO: Check for assignment to value. const auto& lhs = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned() ); const auto& rhs = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned() ); // TODO: Respect destination bank sizes and positive wrap-around. code_gen->AddOutputLine((boost::format("%1% = %1% - %2%") % lhs % rhs).str()); code_gen->AddOutputLine((boost::format("--if (%1% < 0); %1% = 0; end") % lhs).str()); } void FF7MathInstruction::processSaturatedMINUS2(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); // TODO: Check for assignment to value. const auto& lhs = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned() ); const auto& rhs = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned() ); // TODO: Respect destination bank sizes and positive wrap-around. code_gen->AddOutputLine((boost::format("%1% = %1% - %2%") % lhs % rhs).str()); code_gen->AddOutputLine((boost::format("--if (%1% < 0); %1% = 0; end") % lhs).str()); } void FF7MathInstruction::processSaturatedINC(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); // TODO: Check for assignment to value. const auto& dest = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[1]->getUnsigned() ); // TODO: Respect destination bank sizes and negative wrap-around. code_gen->AddOutputLine((boost::format("%1% = %1% + 1") % dest).str()); code_gen->AddOutputLine((boost::format("--if (%1% > 255); %1% = 255; end") % dest).str()); } void FF7MathInstruction::processSaturatedINC2(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); // TODO: Check for assignment to value. const auto& dest = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[1]->getUnsigned() ); // TODO: Respect destination bank sizes and negative wrap-around. code_gen->AddOutputLine((boost::format("%1% = %1% + 1") % dest).str()); code_gen->AddOutputLine((boost::format("--if (%1% > 32767); %1% = 32767; end") % dest).str()); } void FF7MathInstruction::processSaturatedDEC(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); // TODO: Check for assignment to value. const auto& dest = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[1]->getUnsigned() ); // TODO: Respect destination bank sizes and positive wrap-around. code_gen->AddOutputLine((boost::format("%1% = %1% - 1") % dest).str()); code_gen->AddOutputLine((boost::format("--if (%1% < 0); %1% = 0; end") % dest).str()); } void FF7MathInstruction::processSaturatedDEC2(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); // TODO: Check for assignment to value. const auto& dest = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[1]->getUnsigned() ); // TODO: Respect destination bank sizes and positive wrap-around. code_gen->AddOutputLine((boost::format("%1% = %1% - 1") % dest).str()); code_gen->AddOutputLine((boost::format("--if (%1% < 0); %1% = 0; end") % dest).str()); } void FF7MathInstruction::processRDMSD(CodeGenerator* code_gen){ // TODO: we don't have os.time... // TODO: RNG emulation? code_gen->AddOutputLine("math.randomseed(os.time())"); } void FF7MathInstruction::processSETBYTE_SETWORD(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); // TODO: Check for assignment to value. const auto& destination = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned() ); const auto& source = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned() ); // TODO: respect destination bank sizes (16-bit writes only affect low byte) code_gen->AddOutputLine((boost::format("%1% = %2%") % destination % source).str()); } void FF7MathInstruction::processBITON(CodeGenerator* code_gen){ code_gen->AddOutputLine((boost::format("bit_on(%1%, %2%, %3%)") % _params[0]->getUnsigned() % _params[2]->getUnsigned() % _params[3]->getUnsigned() ).str()); } void FF7MathInstruction::processBITOFF(CodeGenerator* code_gen){ code_gen->AddOutputLine((boost::format("bit_off(%1%, %2%, %3%)") % _params[0]->getUnsigned() % _params[2]->getUnsigned() % _params[3]->getUnsigned() ).str()); } void FF7MathInstruction::processPLUSx_MINUSx(CodeGenerator* code_gen, const std::string& op){ FieldCodeGenerator* cg = static_cast(code_gen); // TODO: Check for assignment to value. const auto& lhs = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned() ); const auto& rhs = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned() ); // TODO: repect destination bank sizes and wraparound code_gen->AddOutputLine((boost::format("%1% = %1% %2% %3%") % lhs % op % rhs).str()); } void FF7MathInstruction::processINCx_DECx(CodeGenerator* code_gen, const std::string& op){ FieldCodeGenerator* cg = static_cast(code_gen); // TODO: Check for assignment to value. const auto& destination = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[1]->getUnsigned() ); // TODO: repect destination bank sizes and wraparound code_gen->AddOutputLine((boost::format("%1% = %1% %2% 1") % destination % op).str()); } void FF7MathInstruction::processRANDOM(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); // TODO: Check for assignment to value. const auto& destination = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[1]->getUnsigned() ); // TODO: Respect destination bank sizes (16-bit writes only affect low byte). // TODO: RNG emulation? code_gen->AddOutputLine((boost::format("%1% = math.random(0, 255)") % destination).str()); } void FF7WindowInstruction::processInst( Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen ){ FF7FieldEngine& eng = static_cast(*engine); FunctionMetaData md(func._metadata); switch (_opcode){ case OPCODE::TUTOR: WriteTodo(code_gen, md.GetEntityName(), "TUTOR"); break; case OPCODE::WCLS: WriteTodo(code_gen, md.GetEntityName(), "WCLS"); break; case OPCODE::WSIZW: WriteTodo(code_gen, md.GetEntityName(), "WSIZW"); break; case OPCODE::WSPCL: WriteTodo(code_gen, md.GetEntityName(), "WSPCL"); break; case OPCODE::WNUMB: WriteTodo(code_gen, md.GetEntityName(), "WNUMB"); break; case OPCODE::STTIM: WriteTodo(code_gen, md.GetEntityName(), "STTIM"); break; case OPCODE::MESSAGE: processMESSAGE(code_gen, eng.ScriptName()); break; case OPCODE::MPARA: WriteTodo(code_gen, md.GetEntityName(), "MPARA"); break; case OPCODE::MPRA2: WriteTodo(code_gen, md.GetEntityName(), "MPRA2"); break; case OPCODE::MPNAM: processMPNAM(code_gen); break; case OPCODE::ASK: WriteTodo(code_gen, md.GetEntityName(), "ASK"); break; case OPCODE::MENU: WriteTodo(code_gen, md.GetEntityName(), "MENU"); break; case OPCODE::MENU2: processMENU2(code_gen); break; case OPCODE::WINDOW: processWINDOW(code_gen); break; case OPCODE::WMOVE: WriteTodo(code_gen, md.GetEntityName(), "WMOVE"); break; case OPCODE::WMODE: WriteTodo(code_gen, md.GetEntityName(), "WMODE"); break; case OPCODE::WREST: WriteTodo(code_gen, md.GetEntityName(), "WREST"); break; case OPCODE::WCLSE: processWCLSE(code_gen); break; case OPCODE::WROW: WriteTodo(code_gen, md.GetEntityName(), "WROW"); break; case OPCODE::GWCOL: WriteTodo(code_gen, md.GetEntityName(), "GWCOL"); break; case OPCODE::SWCOL: WriteTodo(code_gen, md.GetEntityName(), "SWCOL"); break; default: code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented( md.GetEntityName(), _address, _opcode )); } } void FF7WindowInstruction::processWINDOW(CodeGenerator* code_gen){ // Init a new window. It won't be displayed until MESSAGE is used. auto windowId = _params[0]->getUnsigned(); auto x = _params[1]->getUnsigned(); auto y = _params[2]->getUnsigned(); auto width = _params[3]->getUnsigned(); auto height = _params[4]->getUnsigned(); code_gen->AddOutputLine(( boost::format("dialog:dialog_open(\"%1%\", %2%, %3%, %4%, %5%)") % windowId % x % y % width % height ).str()); } void FF7WindowInstruction::processMESSAGE( CodeGenerator* code_gen, const std::string& script_name ){ // Displays a dialog in the WINDOW that has previously been initialized to display this dialog. auto window_id = _params[0]->getUnsigned(); auto dialog_id = _params[1]->getUnsigned(); code_gen->AddOutputLine(( boost::format("dialog:dialog_set_text(\"%1%\", \"%2%_%3%\")") % window_id % script_name % dialog_id ).str()); code_gen->AddOutputLine( (boost::format("dialog:dialog_wait_for_close(\"%1%\")") % window_id).str() ); } void FF7WindowInstruction::processWCLSE(CodeGenerator* code_gen){ // Close a dialog. auto windowId = _params[0]->getUnsigned(); code_gen->AddOutputLine((boost::format("dialog:dialog_close(\"%1%\")") % windowId).str()); } void FF7WindowInstruction::processMPNAM(CodeGenerator* code_gen){ code_gen->AddOutputLine( (boost::format("-- field:map_name(%1%)") % _params[0]->getUnsigned()).str() ); } void FF7WindowInstruction::processMENU2(CodeGenerator* code_gen){ code_gen->AddOutputLine(( boost::format("-- field:menu_lock(%1%)") % FieldCodeGenerator::FormatBool(_params[0]->getUnsigned()) ).str()); } void FF7PartyInstruction::processInst( Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen ){ FunctionMetaData md(func._metadata); switch (_opcode){ case OPCODE::SPTYE: WriteTodo(code_gen, md.GetEntityName(), "SPTYE"); break; case OPCODE::GTPYE: WriteTodo(code_gen, md.GetEntityName(), "GTPYE"); break; case OPCODE::GOLDU: WriteTodo(code_gen, md.GetEntityName(), "GOLDU"); break; case OPCODE::GOLDD: WriteTodo(code_gen, md.GetEntityName(), "GOLDD"); break; case OPCODE::HMPMAX1: WriteTodo(code_gen, md.GetEntityName(), "HMPMAX1"); break; case OPCODE::HMPMAX2: WriteTodo(code_gen, md.GetEntityName(), "HMPMAX2"); break; case OPCODE::MHMMX: WriteTodo(code_gen, md.GetEntityName(), "MHMMX"); break; case OPCODE::HMPMAX3: WriteTodo(code_gen, md.GetEntityName(), "HMPMAX3"); break; case OPCODE::MPU: WriteTodo(code_gen, md.GetEntityName(), "MPU"); break; case OPCODE::MPD: WriteTodo(code_gen, md.GetEntityName(), "MPD"); break; case OPCODE::HPU: WriteTodo(code_gen, md.GetEntityName(), "HPU"); break; case OPCODE::HPD: WriteTodo(code_gen, md.GetEntityName(), "HPD"); break; case OPCODE::STITM: processSTITM(code_gen); break; case OPCODE::DLITM: WriteTodo(code_gen, md.GetEntityName(), "DLITM"); break; case OPCODE::CKITM: WriteTodo(code_gen, md.GetEntityName(), "CKITM"); break; case OPCODE::SMTRA: WriteTodo(code_gen, md.GetEntityName(), "SMTRA"); break; case OPCODE::DMTRA: WriteTodo(code_gen, md.GetEntityName(), "DMTRA"); break; case OPCODE::CMTRA: WriteTodo(code_gen, md.GetEntityName(), "CMTRA"); break; case OPCODE::GETPC: WriteTodo(code_gen, md.GetEntityName(), "GETPC"); break; case OPCODE::PRTYP: WriteTodo(code_gen, md.GetEntityName(), "PRTYP"); break; case OPCODE::PRTYM: WriteTodo(code_gen, md.GetEntityName(), "PRTYM"); break; case OPCODE::PRTYE: processPRTYE(code_gen); break; case OPCODE::MMBUD: WriteTodo(code_gen, md.GetEntityName(), "MMBUD"); break; case OPCODE::MMBLK: WriteTodo(code_gen, md.GetEntityName(), "MMBLK"); break; case OPCODE::MMBUK: WriteTodo(code_gen, md.GetEntityName(), "MMBUK"); break; case OPCODE::CHGLD: WriteTodo(code_gen, md.GetEntityName(), "CHGLD"); break; default: code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented( md.GetEntityName(), _address, _opcode )); } } void FF7PartyInstruction::processSTITM(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& item_id = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned() ); const auto& amount = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned() ); code_gen->AddOutputLine((boost::format("FFVII.add_item(%1%, %2%)") % item_id % amount).str()); } void FF7PartyInstruction::processPRTYE(CodeGenerator* code_gen){ FieldCodeGenerator* gc = static_cast(code_gen); auto char_id_1 = gc->GetFormatter().CharName(_params[0]->getUnsigned()); char_id_1 = (char_id_1 == "") ? "nil" : ("\"" + char_id_1 + "\""); auto char_id_2 = gc->GetFormatter().CharName(_params[1]->getUnsigned()); char_id_2 = (char_id_2 == "") ? "nil" : ("\"" + char_id_2 + "\""); auto char_id_3 = gc->GetFormatter().CharName(_params[2]->getUnsigned()); char_id_3 = (char_id_3 == "") ? "nil" : ("\"" + char_id_3 + "\""); code_gen->AddOutputLine(( boost::format("FFVII.set_party(%1%, %2%, %3%)") % char_id_1 % char_id_2 % char_id_3 ).str()); } void FF7ModelInstruction::processInst( Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen ){ FF7FieldEngine& eng = static_cast(*engine); FunctionMetaData md(func._metadata); switch (_opcode){ case OPCODE::JOIN: processJOIN(code_gen); break; case OPCODE::SPLIT: processSPLIT(code_gen); break; case OPCODE::BLINK: WriteTodo(code_gen, md.GetEntityName(), "BLINK"); break; case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::EYETX: WriteTodo(code_gen, md.GetEntityName(), "EYETX"); break; case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::TRNSP: WriteTodo(code_gen, md.GetEntityName(), "TRNSP"); break; case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::AMBNT: WriteTodo(code_gen, md.GetEntityName(), "AMBNT"); break; case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::Unknown03: WriteTodo(code_gen, md.GetEntityName(), "Unknown03"); break; case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::Unknown04: WriteTodo(code_gen, md.GetEntityName(), "Unknown04"); break; case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::Unknown05: WriteTodo(code_gen, md.GetEntityName(), "Unknown05"); break; case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::LIGHT: WriteTodo(code_gen, md.GetEntityName(), "LIGHT"); break; case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::Unknown07: WriteTodo(code_gen, md.GetEntityName(), "Unknown07"); break; case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::Unknown08: WriteTodo(code_gen, md.GetEntityName(), "Unknown08"); break; case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::Unknown09: WriteTodo(code_gen, md.GetEntityName(), "Unknown09"); break; case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::SBOBJ: WriteTodo(code_gen, md.GetEntityName(), "SBOBJ"); break; case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::Unknown0B: WriteTodo(code_gen, md.GetEntityName(), "Unknown0B"); break; case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::Unknown0C: WriteTodo(code_gen, md.GetEntityName(), "Unknown0C"); break; case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::SHINE: WriteTodo(code_gen, md.GetEntityName(), "SHINE"); break; case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::RESET: WriteTodo(code_gen, md.GetEntityName(), "RESET"); break; case OPCODE::KAWIW: WriteTodo(code_gen, md.GetEntityName(), "KAWIW"); break; case OPCODE::PMOVA: WriteTodo(code_gen, md.GetEntityName(), "PMOVA"); break; case OPCODE::PDIRA: WriteTodo(code_gen, md.GetEntityName(), "PDIRA"); break; case OPCODE::PTURA: WriteTodo(code_gen, md.GetEntityName(), "PTURA"); break; case OPCODE::PGTDR: WriteTodo(code_gen, md.GetEntityName(), "PGTDR"); break; case OPCODE::PXYZI: WriteTodo(code_gen, md.GetEntityName(), "PXYZI"); break; case OPCODE::TLKON: processTLKON(code_gen, md.GetEntityName()); break; case OPCODE::PC: processPC(code_gen, md.GetEntityName()); break; case OPCODE::opCodeCHAR: processCHAR(code_gen, md.GetEntityName()); break; case OPCODE::DFANM: processDFANM(code_gen, md.GetEntityName(), md.GetCharacterId()); break; case OPCODE::ANIME1: processANIME1(code_gen, md.GetEntityName(), md.GetCharacterId()); break; case OPCODE::VISI: processVISI(code_gen, md.GetEntityName()); break; case OPCODE::XYZI: processXYZI(code_gen, md.GetEntityName()); break; case OPCODE::XYI: WriteTodo(code_gen, md.GetEntityName(), "XYI"); break; case OPCODE::XYZ: WriteTodo(code_gen, md.GetEntityName(), "XYZ"); break; case OPCODE::MOVE: processMOVE(code_gen, md.GetEntityName()); break; case OPCODE::CMOVE: WriteTodo(code_gen, md.GetEntityName(), "CMOVE"); break; case OPCODE::MOVA: WriteTodo(code_gen, md.GetEntityName(), "MOVA"); break; case OPCODE::TURA: WriteTodo(code_gen, md.GetEntityName(), "TURA"); break; case OPCODE::ANIMW: WriteTodo(code_gen, md.GetEntityName(), "ANIMW"); break; case OPCODE::FMOVE: WriteTodo(code_gen, md.GetEntityName(), "FMOVE"); break; case OPCODE::ANIME2: WriteTodo(code_gen, md.GetEntityName(), "ANIME2"); break; case OPCODE::ANIM_1: WriteTodo(code_gen, md.GetEntityName(), "ANIM_1"); break; case OPCODE::CANIM1: WriteTodo(code_gen, md.GetEntityName(), "CANIM1"); break; case OPCODE::CANM_1: WriteTodo(code_gen, md.GetEntityName(), "CANM_1"); break; case OPCODE::MSPED: processMSPED(code_gen, md.GetEntityName()); break; case OPCODE::DIR: processDIR(code_gen, md.GetEntityName()); break; case OPCODE::TURNGEN: processTURNGEN(code_gen, md.GetEntityName()); break; case OPCODE::TURN: WriteTodo(code_gen, md.GetEntityName(), "TURN"); break; case OPCODE::DIRA: WriteTodo(code_gen, md.GetEntityName(), "DIRA"); break; case OPCODE::GETDIR: WriteTodo(code_gen, md.GetEntityName(), "GETDIR"); break; case OPCODE::GETAXY: WriteTodo(code_gen, md.GetEntityName(), "GETAXY"); break; case OPCODE::GETAI: processGETAI(code_gen, eng); break; case OPCODE::ANIM_2: processANIM_2(code_gen, md.GetEntityName(), md.GetCharacterId()); break; case OPCODE::CANIM2: processCANIM2(code_gen, md.GetEntityName(), md.GetCharacterId()); break; case OPCODE::CANM_2: processCANM_2(code_gen, md.GetEntityName(), md.GetCharacterId()); break; case OPCODE::ASPED: WriteTodo(code_gen, md.GetEntityName(), "ASPED"); break; case OPCODE::CC: processCC(code_gen, eng); break; case OPCODE::JUMP: processJUMP(code_gen, md.GetEntityName()); break; case OPCODE::AXYZI: processAXYZI(code_gen); break; case OPCODE::LADER: processLADER(code_gen, md.GetEntityName()); break; case OPCODE::OFST: processOFST(code_gen, md.GetEntityName()); break; case OPCODE::OFSTW: code_gen->AddOutputLine("self." + md.GetEntityName() + ":offset_sync()"); break; case OPCODE::TALKR: WriteTodo(code_gen, md.GetEntityName(), "TALKR"); break; case OPCODE::SLIDR: WriteTodo(code_gen, md.GetEntityName(), "SLIDR"); break; case OPCODE::SOLID: processSOLID(code_gen, md.GetEntityName()); break; case OPCODE::TLKR2: WriteTodo(code_gen, md.GetEntityName(), "TLKR2"); break; case OPCODE::SLDR2: WriteTodo(code_gen, md.GetEntityName(), "SLDR2"); break; case OPCODE::CCANM: WriteTodo(code_gen, md.GetEntityName(), "CCANM"); break; case OPCODE::FCFIX: WriteTodo(code_gen, md.GetEntityName(), "FCFIX"); break; case OPCODE::ANIMB: WriteTodo(code_gen, md.GetEntityName(), "ANIMB"); break; case OPCODE::TURNW: WriteTodo(code_gen, md.GetEntityName(), "TURNW"); break; default: code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented( md.GetEntityName(), _address, _opcode )); } } void FF7ModelInstruction::processJOIN(CodeGenerator* code_gen){ code_gen->AddOutputLine("join_party(" + std::to_string(_params[0]->getUnsigned()) + ")"); } void FF7ModelInstruction::processSPLIT(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); const float scale = 128.0f * cg->GetScaleFactor(); const auto& ax = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[6]->getSigned(), FieldCodeGenerator::ValueType::Float, scale ); const auto& ay = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[7]->getSigned(), FieldCodeGenerator::ValueType::Float, scale ); const auto& ar = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[2]->getUnsigned(), _params[8]->getSigned(), FieldCodeGenerator::ValueType::Float, scale ); const auto& bx = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[3]->getUnsigned(), _params[9]->getSigned(), FieldCodeGenerator::ValueType::Float, scale ); const auto& by = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[4]->getUnsigned(), _params[10]->getSigned(), FieldCodeGenerator::ValueType::Float, scale ); const auto& br = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[5]->getUnsigned(), _params[11]->getSigned(), FieldCodeGenerator::ValueType::Float, scale ); const auto& speed = _params[12]->getUnsigned(); code_gen->AddOutputLine(( boost::format("split_party(%1%, %2%, %3%, %4%, %5%, %6%, %7%)") % ax % ay % ar % bx % by % br % speed ).str()); } void FF7ModelInstruction::processTLKON(CodeGenerator* code_gen, const std::string& entity){ code_gen->AddOutputLine(( boost::format("self.%1%:set_talkable(%2%)") % entity % FieldCodeGenerator::FormatInvertedBool(_params[0]->getUnsigned()) ).str()); } void FF7ModelInstruction::processPC(CodeGenerator* code_gen, const std::string& entity){ code_gen->AddOutputLine( (boost::format("set_entity_to_character(\"%1%\", \"%1%\")") % entity).str() ); } void FF7ModelInstruction::processCHAR(CodeGenerator* code_gen, const std::string& entity){ code_gen->AddOutputLine( (boost::format("self.%1% = entity_manager:get_entity(\"%1%\")") % entity).str() ); } void FF7ModelInstruction::processDFANM( CodeGenerator* code_gen, const std::string& entity, int char_id ){ // ID will be fixed-up downstream. FieldCodeGenerator* cg = static_cast(code_gen); auto animation_id = _params[0]->getUnsigned(); // TODO: check for zero. auto speed = 1.0f / _params[1]->getUnsigned(); code_gen->AddOutputLine(( boost::format("self.%1%:set_default_animation(\"%2%\") -- speed %3%") % entity % cg->GetFormatter().AnimationName(char_id, animation_id) % speed ).str()); code_gen->AddOutputLine(( boost::format("self.%1%:play_animation(\"%2%\")") % entity % cg->GetFormatter().AnimationName(char_id, animation_id) ).str()); } void FF7ModelInstruction::processANIME1( CodeGenerator* code_gen, const std::string& entity, int char_id ){ // ID will be fixed-up downstream. FieldCodeGenerator* cg = static_cast(code_gen); auto animation_id = _params[0]->getUnsigned(); // TODO: check for zero. auto speed = 1.0f / _params[1]->getUnsigned(); code_gen->AddOutputLine(( boost::format("self.%1%:play_animation(\"%2%\") -- speed %3%") % entity % cg->GetFormatter().AnimationName(char_id, animation_id) % speed ).str()); code_gen->AddOutputLine((boost::format("self.%1%:animation_sync()") % entity).str()); } void FF7ModelInstruction::processVISI(CodeGenerator* code_gen, const std::string& entity){ code_gen->AddOutputLine(( boost::format("self.%1%:set_visible(%2%)") % entity % FieldCodeGenerator::FormatBool(_params[0]->getUnsigned()) ).str()); } void FF7ModelInstruction::processXYZI(CodeGenerator* code_gen, const std::string& entity){ FieldCodeGenerator* cg = static_cast(code_gen); const float scale = 128.0f * cg->GetScaleFactor(); const auto& x = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[4]->getSigned(), FieldCodeGenerator::ValueType::Float, scale ); const auto& y = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[5]->getSigned(), FieldCodeGenerator::ValueType::Float, scale ); const auto& z = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[2]->getUnsigned(), _params[6]->getSigned(), FieldCodeGenerator::ValueType::Float, scale ); const auto& triangle_id = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[3]->getUnsigned(), _params[7]->getUnsigned() ); code_gen->AddOutputLine(( boost::format("self.%1%:set_position(%2%, %3%, %4%) -- triangle ID %5%") % entity % x % y % z % triangle_id ).str()); } void FF7ModelInstruction::processMOVE(CodeGenerator* code_gen, const std::string& entity){ FieldCodeGenerator* cg = static_cast(code_gen); const float scale = 128.0f * cg->GetScaleFactor(); const auto& x = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getSigned(), FieldCodeGenerator::ValueType::Float, scale ); const auto& y = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getSigned(), FieldCodeGenerator::ValueType::Float, scale ); code_gen->AddOutputLine( (boost::format("self.%1%:move_to_position(%2%, %3%)") % entity % x % y).str() ); code_gen->AddOutputLine((boost::format("self.%1%:move_sync()") % entity).str()); } void FF7ModelInstruction::processMSPED(CodeGenerator* code_gen, const std::string& entity){ FieldCodeGenerator* cg = static_cast(code_gen); const float scale = 128.0f * cg->GetScaleFactor(); const auto& speed = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[2]->getUnsigned(), FieldCodeGenerator::ValueType::Float, 256.0f * scale / 30.0f ); code_gen->AddOutputLine( (boost::format("self.%1%:set_move_auto_speed(%2%)") % entity % speed).str() ); } void FF7ModelInstruction::processDIR(CodeGenerator* code_gen, const std::string& entity){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& degrees = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[1]->getUnsigned(), FieldCodeGenerator::ValueType::Float, 256.0f / 360.0f ); code_gen->AddOutputLine((boost::format("self.%1%:set_rotation(%2%)") % entity % degrees).str()); } void FF7ModelInstruction::processTURNGEN(CodeGenerator* code_gen, const std::string& entity){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& degrees = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[2]->getUnsigned(), FieldCodeGenerator::ValueType::Float, 256.0f / 360.0f ); std::string direction; switch (_params[3]->getUnsigned()){ case 0: direction = "Entity.CLOCKWISE"; break; case 1: direction = "Entity.ANTICLOCKWISE"; break; case 2: direction = "Entity.CLOSEST"; break; // Default to closest: default: direction = "Entity.CLOSEST"; break; } auto steps = _params[4]->getUnsigned(); std::string step_type; switch (_params[5]->getUnsigned()){ case 1: step_type = "Entity.LINEAR"; break; case 2: step_type = "Entity.SMOOTH"; break; // Default to smooth default: step_type = "Entity.SMOOTH"; break; } const float scaled_steps = static_cast(steps) / 30.0f; code_gen->AddOutputLine(( boost::format("self.%1%:turn_to_direction(%2%, %3%, %4%, %5%)") % entity % degrees % direction % step_type % scaled_steps ).str()); code_gen->AddOutputLine((boost::format("self.%1%:turn_sync()") % entity).str()); } void FF7ModelInstruction::processGETAI(CodeGenerator* code_gen, const FF7FieldEngine& engine){ FieldCodeGenerator* cg = static_cast(code_gen); // TODO: check for assignment to literal. const auto& variable = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned() ); const auto& entity = engine.EntityByIndex(_params[2]->getUnsigned()); code_gen->AddOutputLine( (boost::format("%1% = entity_manager:get_entity(\"%2%\"):get_move_triangle_id()") % variable % entity.Name() ).str()); } void FF7ModelInstruction::processANIM_2( CodeGenerator* code_gen, const std::string& entity, int char_id ){ // ID will be fixed-up downstream. FieldCodeGenerator* cg = static_cast(code_gen); auto animation_id = _params[0]->getUnsigned(); // TODO: check for zero. auto speed = 1.0f / _params[1]->getUnsigned(); code_gen->AddOutputLine(( boost::format("self.%1%:play_animation_stop(\"%2%\") -- speed %3%") % entity % cg->GetFormatter().AnimationName(char_id, animation_id) % speed ).str()); code_gen->AddOutputLine((boost::format("self.%1%:animation_sync()") % entity).str()); } void FF7ModelInstruction::processCANIM2( CodeGenerator* code_gen, const std::string& entity, int char_id ){ FieldCodeGenerator* cg = static_cast(code_gen); auto animation_id = _params[0]->getUnsigned(); auto start_frame = _params[1]->getUnsigned() / 30.0f; auto end_frame = _params[2]->getUnsigned() / 30.0f; // TODO: check for zero. auto speed = 1.0f / _params[3]->getUnsigned(); code_gen->AddOutputLine(( boost::format("self.%1%:play_animation(\"%2%\", %3%, %4%) -- speed %5%") % entity % cg->GetFormatter().AnimationName(char_id, animation_id) % start_frame % end_frame % speed ).str()); code_gen->AddOutputLine((boost::format("self.%1%:animation_sync()") % entity).str()); } void FF7ModelInstruction::processCANM_2( CodeGenerator* code_gen, const std::string& entity, int char_id ){ FieldCodeGenerator* cg = static_cast(code_gen); // ID will be fixed-up downstream. auto animation_id = _params[0]->getUnsigned(); auto start_frame = _params[1]->getUnsigned() / 30.0f; auto end_frame = _params[2]->getUnsigned() / 30.0f; // TODO: check for zero. auto speed = 1.0f / _params[3]->getUnsigned(); code_gen->AddOutputLine(( boost::format("self.%1%:play_animation_stop(\"%2%\", %3%, %4%) -- speed %5%") % entity % cg->GetFormatter().AnimationName(char_id, animation_id) % start_frame % end_frame % speed ).str()); code_gen->AddOutputLine((boost::format("self.%1%:animation_sync()") % entity).str()); } void FF7ModelInstruction::processCC(CodeGenerator* code_gen, const FF7FieldEngine& engine){ const auto& entity = engine.EntityByIndex(_params[0]->getUnsigned()); code_gen->AddOutputLine( (boost::format("entity_manager:set_player_entity(\"%1%\")") % entity.Name()).str() ); } void FF7ModelInstruction::processJUMP(CodeGenerator* code_gen, const std::string& entity){ FieldCodeGenerator* cg = static_cast(code_gen); const float scale = 128.0f * cg->GetScaleFactor(); float x = std::stof(FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[4]->getSigned(), FieldCodeGenerator::ValueType::Float, scale )); float y = std::stof(FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[5]->getSigned(), FieldCodeGenerator::ValueType::Float, scale )); int i = atoi(FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[2]->getUnsigned(), _params[6]->getSigned() ).c_str()); int steps = atoi(FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[3]->getUnsigned(), _params[7]->getSigned() ).c_str()); //x *= 0.00781250273224; //y *= 0.00781250273224; // Always force reduce steps and bound it between 1 and 5. steps /= 6; if (steps < 1) steps = 1; if (steps > 5) steps = 5; // TODO: Z hardcoded as -1, handled in Entity::ScriptJumpToPosition. // TODO: Hardcoded 0.5 in seconds. Calculate using distance. code_gen->AddOutputLine(( boost::format("self.%1%:jump_to_position(%2%, %3%, -1, 0.5, %4%) -- %5% steps.") % entity % x % y % i % steps ).str()); code_gen->AddOutputLine((boost::format("self.%1%:jump_sync()") % entity).str()); } void FF7ModelInstruction::processAXYZI(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); const float scale = 128.0f * cg->GetScaleFactor(); code_gen->AddOutputLine(( boost::format("axyzi(%1%, %2%, %3%, %4%, %5%, %6%, %7%, %8%, %9%, %10%)") % _params[0]->getSigned() % _params[1]->getSigned() % _params[2]->getSigned() % _params[3]->getSigned() % _params[4]->getSigned() % _params[5]->getSigned() % _params[6]->getSigned() % _params[7]->getSigned() % _params[8]->getSigned() % scale ).str()); } void FF7ModelInstruction::processLADER(CodeGenerator* code_gen, const std::string& entity){ FieldCodeGenerator* cg = static_cast(code_gen); const float scale = 128.0f * cg->GetScaleFactor(); const auto& x = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[4]->getSigned(), FieldCodeGenerator::ValueType::Float, scale ); const auto& y = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[5]->getSigned(), FieldCodeGenerator::ValueType::Float, scale ); const auto& z = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[2]->getUnsigned(), _params[6]->getSigned(), FieldCodeGenerator::ValueType::Float, scale ); uint end_triangle = atoi(FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[3]->getUnsigned(), _params[7]->getUnsigned() ).c_str()); uint keys = _params[8]->getUnsigned(); uint animation = _params[9]->getUnsigned(); //float orientation = _params[10]->getUnsigned() / (256.0f / 360.0f); const auto& orientation = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), 0, _params[10]->getSigned(), FieldCodeGenerator::ValueType::Float, 256.0f / 360.0f ); uint speed = _params[11]->getUnsigned(); // TODO: Animation hardcoded as "btce". // TODO: Orientation and speed not set. code_gen->AddOutputLine(( boost::format( "self.%1%:linear_to_position(%2%, %3%, %4%, %5%, \"Climb\", %6%, %7%) " "-- Animation %8% -- Speed %9%" ) % entity % x % y % z % keys % orientation % end_triangle % animation % speed ).str()); code_gen->AddOutputLine((boost::format("self.%1%:linear_sync()") % entity).str()); } void FF7ModelInstruction::processSOLID(CodeGenerator* code_gen, const std::string& entity){ code_gen->AddOutputLine(( boost::format("self.%1%:set_solid(%2%)") % entity % FieldCodeGenerator::FormatInvertedBool(_params[0]->getUnsigned()) ).str()); } void FF7ModelInstruction::processOFST(CodeGenerator* code_gen, const std::string& entity){ FieldCodeGenerator* cg = static_cast(code_gen); const float scale = 128.0f * cg->GetScaleFactor(); float x = std::stof(FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[5]->getSigned(), FieldCodeGenerator::ValueType::Float, scale )); float y = std::stof(FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(),_params[6]->getSigned(), FieldCodeGenerator::ValueType::Float, scale )); float z = std::stof(FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[2]->getUnsigned(), _params[7]->getSigned(), FieldCodeGenerator::ValueType::Float, scale )); float speed = std::stof(FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[3]->getUnsigned(), _params[8]->getUnsigned() )); // Spatial coordinates need to be scaled down. // TODO: This number is empirically deducted. Why this number? //x *= 0.00390f; //y *= 0.00390f; //z *= 0.00390f; // Speed needs to be scaled down by the frame rate. speed /= 30.0f; code_gen->AddOutputLine(( boost::format("self.%1%:offset_to_position(%2%, %3%, %4%, %5%, %6%)") % entity % x % y % z % (_params[4]->getUnsigned() ? "Entity.SMOOTH" : "Entity.LINEAR")% speed ).str()); } void FF7WalkmeshInstruction::processInst( Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen ){ FunctionMetaData md(func._metadata); switch (_opcode){ case OPCODE::SLIP: WriteTodo(code_gen, md.GetEntityName(), "SLIP"); break; case OPCODE::UC: processUC(code_gen); break; case OPCODE::IDLCK: // Triangle id, on or off code_gen->AddOutputLine( (boost::format("walkmesh:lock_walkmesh(%1%, %2%)") % _params[0]->getUnsigned() % FieldCodeGenerator::FormatBool(_params[1]->getUnsigned())).str()); break; case OPCODE::LINE: processLINE(code_gen, md.GetEntityName()); break; case OPCODE::LINON: WriteTodo(code_gen, md.GetEntityName(), "LINON"); break; case OPCODE::SLINE: WriteTodo(code_gen, md.GetEntityName(), "SLINE"); break; default: code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented( md.GetEntityName(), _address, _opcode )); } } void FF7WalkmeshInstruction::processUC(CodeGenerator* code_gen){ code_gen->AddOutputLine(( boost::format("entity_manager:player_lock(%1%)") % FieldCodeGenerator::FormatBool(_params[0]->getUnsigned()) ).str()); } void FF7WalkmeshInstruction::processLINE(CodeGenerator* code_gen, const std::string& entity){ float xa = _params[0]->getSigned(); float ya = _params[1]->getSigned(); float za = _params[2]->getSigned(); float xb = _params[3]->getSigned(); float yb = _params[4]->getSigned(); float zb = _params[5]->getSigned(); // Scale down. TODO: Why this number? xa *= 0.00781249709639; ya *= 0.00781249709639; za *= 0.00781249709639; xb *= 0.00781249709639; yb *= 0.00781249709639; zb *= 0.00781249709639; // Just print as a comment. code_gen->AddOutputLine( "-- LINE (" + std::to_string(xa) + ", " + std::to_string(ya) + ", " + std::to_string(za) + ")-(" + std::to_string(xb) + ", " +std::to_string(yb) + ", " + std::to_string(zb) + ")" ); } void FF7BackgroundInstruction::processInst( Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen ){ FunctionMetaData md(func._metadata); switch (_opcode){ case OPCODE::BGPDH: WriteTodo(code_gen, md.GetEntityName(), "BGPDH");break; case OPCODE::BGSCR: WriteTodo(code_gen, md.GetEntityName(), "BGSCR"); break; case OPCODE::MPPAL: WriteTodo(code_gen, md.GetEntityName(), "MPPAL"); break; case OPCODE::BGON: processBGON(code_gen); break; case OPCODE::BGOFF: processBGOFF(code_gen); break; case OPCODE::BGROL: WriteTodo(code_gen, md.GetEntityName(), "BGROL"); break; case OPCODE::BGROL2: WriteTodo(code_gen, md.GetEntityName(), "BGROL2"); break; case OPCODE::BGCLR: processBGCLR(code_gen); break; case OPCODE::STPAL: processSTPAL(code_gen); break; case OPCODE::LDPAL: processLDPAL(code_gen); break; case OPCODE::CPPAL: processCPPAL(code_gen); break; case OPCODE::RTPAL: WriteTodo(code_gen, md.GetEntityName(), "RTPAL"); break; case OPCODE::ADPAL: processADPAL(code_gen); break; case OPCODE::MPPAL2: processMPPAL2(code_gen); break; case OPCODE::STPLS: processSTPLS(code_gen); break; case OPCODE::LDPLS: processLDPLS(code_gen); break; case OPCODE::CPPAL2: WriteTodo(code_gen, md.GetEntityName(), "CPPAL2"); break; case OPCODE::ADPAL2: WriteTodo(code_gen, md.GetEntityName(), "ADPAL2"); break; case OPCODE::RTPAL2: WriteTodo(code_gen, md.GetEntityName(), "RTPAL2"); break; default: code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented( md.GetEntityName(), _address, _opcode )); } } void FF7BackgroundInstruction::processBGON(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& background_id = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned() ); const auto& layer_id = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned() ); code_gen->AddOutputLine( (boost::format("-- field:background_on(%1%, %2%)") % background_id % layer_id).str() ); } void FF7BackgroundInstruction::processBGOFF(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& background_id = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned() ); const auto& layer_id = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned() ); code_gen->AddOutputLine( (boost::format("-- field:background_off(%1%, %2%)") % background_id % layer_id).str() ); } void FF7BackgroundInstruction::processBGCLR(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& background_id = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[2]->getUnsigned() ); code_gen->AddOutputLine( (boost::format("-- field:background_clear(%1%)") % background_id).str() ); } void FF7BackgroundInstruction::processSTPAL(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& source = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned() ); const auto& destination = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned() ); auto num_entries = _params[4]->getUnsigned() + 1; code_gen->AddOutputLine(( boost::format("-- store palette %1% to position %2%, start CLUT index 0, %3% entries") % source % destination % num_entries ).str()); } void FF7BackgroundInstruction::processLDPAL(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& source = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned() ); const auto& destination = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned() ); auto num_entries = _params[4]->getUnsigned() + 1; code_gen->AddOutputLine(( boost::format("-- load palette %2% from position %1%, start CLUT index 0, %3% entries") % source % destination % num_entries ).str()); } void FF7BackgroundInstruction::processCPPAL(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& source = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned() ); const auto& destination = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned() ); auto num_entries = _params[4]->getUnsigned() + 1; code_gen->AddOutputLine(( boost::format("-- copy palette %1% to palette %2%, %3% entries") % source % destination % num_entries ).str()); } void FF7BackgroundInstruction::processADPAL(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& source = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[6]->getUnsigned() ); const auto& destination = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[7]->getUnsigned() ); const auto& r = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[4]->getUnsigned(), _params[10]->getUnsigned() ); const auto& g = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[3]->getUnsigned(), _params[9]->getUnsigned() ); const auto& b = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[2]->getUnsigned(), _params[8]->getUnsigned() ); auto num_entries = _params[11]->getUnsigned() + 1; code_gen->AddOutputLine(( boost::format( "-- add RGB(%3%, %4%, %5%) to %6% entries of palette stored at position %1%, " "storing result in position %2%" ) % source % destination % r % g % b % num_entries ).str()); } void FF7BackgroundInstruction::processMPPAL2(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& source = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[6]->getUnsigned() ); const auto& destination = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[7]->getUnsigned() ); const auto& r = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[4]->getUnsigned(), _params[10]->getUnsigned() ); const auto& g = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[3]->getUnsigned(), _params[9]->getUnsigned() ); const auto& b = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[2]->getUnsigned(), _params[8]->getUnsigned() ); auto num_entries = _params[11]->getUnsigned() + 1; code_gen->AddOutputLine(( boost::format( "-- multiply RGB(%3%, %4%, %5%) by %6% entries of palette stored at position %1%, " "storing result in position %2%" ) % source % destination % r % g % b % num_entries).str()); } void FF7BackgroundInstruction::processSTPLS(CodeGenerator* code_gen){ auto source = _params[0]->getUnsigned(); auto destination = _params[1]->getUnsigned(); auto start_clut = _params[2]->getUnsigned(); auto num_entries = _params[3]->getUnsigned() + 1; code_gen->AddOutputLine(( boost::format("-- store palette %1% to position %2%, start CLUT index %3%, %4% entries") % source % destination % start_clut % num_entries ).str()); } void FF7BackgroundInstruction::processLDPLS(CodeGenerator* code_gen){ auto source = _params[0]->getUnsigned(); auto destination = _params[1]->getUnsigned(); auto startClut = _params[2]->getUnsigned(); auto num_entries = _params[3]->getUnsigned() + 1; code_gen->AddOutputLine(( boost::format("-- load palette %2% from position %1%, start CLUT index %3%, %4% entries") % source % destination % startClut % num_entries ).str()); } void FF7CameraInstruction::processInst( Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen ){ FunctionMetaData md(func._metadata); switch (_opcode){ case OPCODE::NFADE: processNFADE(code_gen); break; case OPCODE::SHAKE: WriteTodo(code_gen, md.GetEntityName(), "SHAKE"); break; case OPCODE::SCRLO: WriteTodo(code_gen, md.GetEntityName(), "SCRLO"); break; case OPCODE::SCRLC: WriteTodo(code_gen, md.GetEntityName(), "SCRLC"); break; case OPCODE::SCR2D: processSCR2D(code_gen); break; case OPCODE::SCRCC: WriteTodo(code_gen, md.GetEntityName(), "SCRCC"); break; case OPCODE::SCR2DC: processSCR2DC(code_gen); break; case OPCODE::SCRLW: WriteTodo(code_gen, md.GetEntityName(), "SCRLW"); break; case OPCODE::SCR2DL: WriteTodo(code_gen, md.GetEntityName(), "SCR2DL"); break; case OPCODE::VWOFT: WriteTodo(code_gen, md.GetEntityName(), "VWOFT"); break; case OPCODE::FADE: processFADE(code_gen); break; case OPCODE::FADEW: WriteTodo(code_gen, md.GetEntityName(), "FADEW"); break; case OPCODE::SCRLP: WriteTodo(code_gen, md.GetEntityName(), "SCRLP"); break; case OPCODE::MVCAM: WriteTodo(code_gen, md.GetEntityName(), "MVCAM"); break; case OPCODE::SCRLA: WriteTodo(code_gen, md.GetEntityName(), "SCRLA"); break; default: code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented( md.GetEntityName(), _address, _opcode )); } } void FF7CameraInstruction::processNFADE(CodeGenerator* code_gen){ // TODO: Not fully reversed. auto raw_type = _params[4]->getUnsigned(); if (raw_type == 0){ code_gen->AddOutputLine("-- fade:clear()"); return; } FieldCodeGenerator* cg = static_cast(code_gen); const std::string type = raw_type == 12 ? "Fade.SUBTRACT" : "Fade.ADD"; const auto& r = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[5]->getUnsigned() ); const auto& g = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[6]->getUnsigned() ); const auto& b = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[2]->getUnsigned(), _params[7]->getUnsigned() ); const auto& unknown = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[3]->getUnsigned(), _params[8]->getUnsigned() ); code_gen->AddOutputLine( (boost::format("-- fade:fade(%2%, %3%, %4%, %1%, %5%)") % type % r % g % b % unknown).str() ); } void FF7CameraInstruction::processSCR2D(CodeGenerator* code_gen){ // kUpScaler. FieldCodeGenerator* cg = static_cast(code_gen); const auto& x = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getSigned() ); const auto& y = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getSigned() ); code_gen->AddOutputLine(( boost::format("background2d:scroll_to_position(%1% * 3, %2% * 3, Background2D.NONE, 0)") % x % y ).str()); } void FF7CameraInstruction::processSCR2DC(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& x = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[4]->getSigned() ); const auto& y = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[5]->getSigned() ); const auto& speed = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[3]->getUnsigned(), _params[6]->getUnsigned(), FieldCodeGenerator::ValueType::Float, 30.0f ); code_gen->AddOutputLine(( boost::format("background2d:scroll_to_position(%1% * 3, %2% * 3, Background2D.SMOOTH, %3%)") % x % y % speed ).str()); } void FF7CameraInstruction::processFADE(CodeGenerator* code_gen){ // TODO: not fully reversed auto raw_type = _params[8]->getUnsigned(); std::string type; switch (raw_type) { case 1: case 2: case 7: case 8: type = "Fade.SUBTRACT"; break; case 4: code_gen->AddOutputLine("-- fade:black()"); return; default: type = "Fade.ADD"; break; } FieldCodeGenerator* cg = static_cast(code_gen); const auto& r = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[4]->getUnsigned() ); const auto& g = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[5]->getUnsigned() ); const auto& b = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[2]->getUnsigned(), _params[6]->getUnsigned() ); // TODO: needs to be divided by 30.0f? auto speed = _params[7]->getUnsigned(); auto start = _params[9]->getUnsigned(); code_gen->AddOutputLine(( boost::format("-- fade:fade(%2%, %3%, %4%, %1%, %5%, %6%)") % type % r % g % b % speed % start ).str()); } void FF7AudioVideoInstruction::processInst( Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen ){ FunctionMetaData md(func._metadata); switch (_opcode){ case OPCODE::BGMOVIE: WriteTodo(code_gen, md.GetEntityName(), "BGMOVIE"); break; case OPCODE::AKAO2: processAKAO2(code_gen); break; case OPCODE::MUSIC: processMUSIC(code_gen); break; case OPCODE::SOUND: processSOUND(code_gen); break; case OPCODE::AKAO: processAKAO(code_gen); break; case OPCODE::MUSVT: WriteTodo(code_gen, md.GetEntityName(), "MUSVT"); break; case OPCODE::MUSVM: WriteTodo(code_gen, md.GetEntityName(), "MUSVM"); break; case OPCODE::MULCK: processMULCK(code_gen); break; case OPCODE::BMUSC: WriteTodo(code_gen, md.GetEntityName(), "BMUSC"); break; case OPCODE::CHMPH: WriteTodo(code_gen, md.GetEntityName(), "CHMPH"); break; case OPCODE::PMVIE: processPMVIE(code_gen); break; case OPCODE::MOVIE: processMOVIE(code_gen); break; case OPCODE::MVIEF: processMVIEF(code_gen); break; case OPCODE::FMUSC: WriteTodo(code_gen, md.GetEntityName(), "FMUSC"); break; case OPCODE::CMUSC: WriteTodo(code_gen, md.GetEntityName(), "CMUSC"); break; case OPCODE::CHMST: WriteTodo(code_gen, md.GetEntityName(), "CHMST"); break; default: code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented( md.GetEntityName(), _address, _opcode )); } } void FF7AudioVideoInstruction::processAKAO2(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& param1 = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[7]->getUnsigned() ); const auto& param2 = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[8]->getUnsigned() ); const auto& param3 = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[2]->getUnsigned(), _params[9]->getUnsigned() ); const auto& param4 = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[3]->getUnsigned(), _params[10]->getUnsigned() ); const auto& param5 = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[5]->getUnsigned(), _params[11]->getUnsigned() ); auto op = _params[6]->getUnsigned(); code_gen->AddOutputLine(( boost::format("-- music:execute_akao(0x%6$02x, %1%, %2%, %3%, %4%, %5%)") % param1 % param2 % param3 % param4 % param5 % op ).str()); } void FF7AudioVideoInstruction::processMUSIC(CodeGenerator* code_gen){ code_gen->AddOutputLine(( boost::format("-- music:execute_akao(0x10, pointer_to_field_AKAO_%1%)") % _params[0]->getUnsigned() ).str()); } void FF7AudioVideoInstruction::processSOUND(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& soundId = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned() ); const auto& panning = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned() ); code_gen->AddOutputLine( (boost::format("-- music:execute_akao(0x20, %1%, %2%)") % soundId % panning).str() ); } void FF7AudioVideoInstruction::processAKAO(CodeGenerator* code_gen){ FieldCodeGenerator* cg = static_cast(code_gen); const auto& param1 = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[0]->getUnsigned(), _params[7]->getUnsigned() ); const auto& param2 = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[8]->getUnsigned() ); const auto& param3 = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[2]->getUnsigned(), _params[9]->getUnsigned() ); const auto& param4 = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[3]->getUnsigned(), _params[10]->getUnsigned() ); const auto& param5 = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[5]->getUnsigned(), _params[11]->getUnsigned() ); auto op = _params[6]->getUnsigned(); code_gen->AddOutputLine(( boost::format("-- music:execute_akao(0x%6$02x, %1%, %2%, %3%, %4%, %5%)") % param1 % param2 % param3 % param4 % param5 % op ).str()); } void FF7AudioVideoInstruction::processMULCK(CodeGenerator* code_gen){ code_gen->AddOutputLine(( boost::format("-- music:lock(%1%)") % FieldCodeGenerator::FormatBool(_params[0]->getUnsigned()) ).str()); } void FF7AudioVideoInstruction::processPMVIE(CodeGenerator* code_gen){ code_gen->AddOutputLine( (boost::format("-- field:movie_set(%1%)") % _params[0]->getUnsigned() ).str()); } void FF7AudioVideoInstruction::processMOVIE(CodeGenerator* code_gen){ code_gen->AddOutputLine("-- field:play_movie()"); } void FF7AudioVideoInstruction::processMVIEF(CodeGenerator* code_gen){ // TODO: Check for assignment to value. FieldCodeGenerator* cg = static_cast(code_gen); const auto& destination = FieldCodeGenerator::FormatValueOrVariable( cg->GetFormatter(), _params[1]->getUnsigned(), _params[2]->getUnsigned()); code_gen->AddOutputLine( (boost::format("-- %1% = field:get_movie_frame()") % destination).str() ); } void FF7UncategorizedInstruction::processInst( Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen ){ FunctionMetaData md(func._metadata); switch (_opcode){ case OPCODE::MPDSP: WriteTodo(code_gen, md.GetEntityName(), "MPDSP"); break; case OPCODE::SETX: WriteTodo(code_gen, md.GetEntityName(), "SETX"); break; case OPCODE::GETX: WriteTodo(code_gen, md.GetEntityName(), "GETX"); break; case OPCODE::SEARCHX: WriteTodo(code_gen, md.GetEntityName(), "SEARCHX"); break; default: code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented( md.GetEntityName(), _address, _opcode) ); } } void FF7NoOperationInstruction::processInst( Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen ){}