/* * Q-Gears * Copyright (C) 2022 Q-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 . */ #pragma once #include "decompiler/decompiler_engine.h" #include #include #include "sudm.h" namespace FF7{ /** * Represents the FF7 Field engine. */ class FF7FieldEngine : public Engine{ public: /** * FF7FieldEngine constructor. * * Generates a FF7FieldEngine. * * @param formatter[in] The formatter to be used by the engine. * @param scriptName The script name. */ FF7FieldEngine( SUDM::IScriptFormatter& formatter, std::string scriptName ) : mFormatter(formatter), mScriptName(scriptName){ setOutputStackEffect(false); } /** * Destructor. */ FF7FieldEngine(const FF7FieldEngine&) = delete; /** * Destructor. */ FF7FieldEngine& operator = (const FF7FieldEngine&) = delete; /** * Represents an entity. * * An entity can be almost anything in a field map: the playable * character, an NPC, an item, a line... */ class Entity{ public: /** * Entity constructor. * * It doesn't initialize any of the fields. */ Entity() = default; /** * Entity constructor. * * Instantiates an entity with a name. * * @param name[in] Entity name. */ Entity(const std::string& name): mName(name){} /** * Retrieves the entity name. * * @return The entity name */ std::string Name() const{ return mName; } /** * Retrieves a function. * * Retrieves the name of a function from it's index. * * @param index[in] Function index. * @return Function name. * @throws InternalDecompilerError if there is no function * with the specified index. * @todo What is a function here? An Opcode? */ std::string FunctionByIndex(size_t index) const{ auto it = mFunctions.find(index); if (it == std::end(mFunctions)){ throw InternalDecompilerError(); } return it->second; } /** * Adds a function to the engine. * * Must be added by name and index. * * @param name[in] Function name. * @param index[in] Function index. * @todo What is a function here? An Opcode? */ void AddFunction(const std::string& name, size_t index){ mFunctions[index] = name; } private: /** * Entity name. */ std::string mName; /** * Function list. * @todo What is a function here? An Opcode? */ std::map< size_t, std::string > mFunctions; }; /** * Retrieves the dissasembler. * * @param insts[in] List of instructions. * @param rawScriptData[in] Script data, raw format. * @return Pointer to the dissasembler. * @todo Understand and document properly. */ virtual std::unique_ptr getDisassembler( InstVec &insts, const std::vector& rawScriptData ) override; /** * Retrieves the dissasembler. * * @param insts[in] List of instructions. * @return Pointer to the dissasembler. * @todo Understand and document properly. */ virtual std::unique_ptr getDisassembler( InstVec &insts ) override; /** * Retrieves the code generator. * * @param insts[in] List of instructions. * @param output[in] Pointer to the output (file, stream...). * @return Pointer to the generator. * @todo Understand and document properly. */ virtual std::unique_ptr getCodeGenerator( const InstVec& insts, std::ostream &output ) override; /** * Postprocessing actions to apply to the scripts. * * @param insts[in] Instruction list. * @param g[in] Code graph. * @todo Understand and document properly. * @todo What is the graph used to? */ virtual void postCFG(InstVec &insts, Graph g) override; /** * Indicates if instructions are purely grouped. * * @return True if instructions are purely grouped. * @todo What is pure grouping? */ virtual bool usePureGrouping() const override{return false;} /** * Retrieves all entities in the map. * * @return A map of entities, with the name and index. */ std::map GetEntities() const; /** * Adds a function to an entity. * * @param entityName Name of the entity. * @param entityIndex Index of the entity. * @param functionName Name of the function. * @param functionIndex Index of the function. */ void AddEntityFunction( const std::string& entityName, size_t entityIndex, const std::string& funcName, size_t funcIndex ); /** * Retrieves an entity. * * @param index[in] Index of the entity to retrieve. * @throws InternalDecompilerError if there is no entity at the * specified index. */ const Entity& EntityByIndex(size_t index) const{ auto it = mEntityIndexMap.find(index); if (it == std::end(mEntityIndexMap)){ throw InternalDecompilerError(); } return it->second; } /** * Retrieves the scale factor for the map. * * @return Map scale factor. */ float ScaleFactor() const {return mScaleFactor;} /** * Retrieves the script name. * * @return The script name. */ const std::string& ScriptName() const { return mScriptName; } private: /** * Removes extraneous return statements. * * Usefull for scripts that only contain one one return * statement. * * @param insts[in|out] List of instructions to proccess. Extraneous * return statements will be deleted from the instructions. * @param g[in] Code graph. * @todo What is the graph used to? */ void RemoveExtraneousReturnStatements(InstVec& insts, Graph g); /** * Removes trailing infinite loops. * * In FF7 some scripts ends with an infinite loop to keep it alive. * in QGears this isn't required, and can cause infinite loops, so * they can be removed. * * @param insts[in|out] List of instructions to proccess. Trailing * infinite loops will be deleted from the instructions. * @param g[in] Code graph. * @todo What is the graph used to? */ void RemoveTrailingInfiniteLoops(InstVec& insts, Graph g); /** * Tries to detect scripts with trailing infinite loops. * * In FF7 some scripts ends with an infinite loop to keep it alive. * in QGears this isn't required, and can cause infinite loops, so * they can be removed. This function marks them, so they can be * deleted with @{see FF7FieldEngine::RemoveTrailingInfiniteLoops} */ void MarkInfiniteLoopGroups(InstVec& insts, Graph g); /** * The script formatter. */ SUDM::IScriptFormatter& mFormatter; /** * The entity index map for the field. */ std::map mEntityIndexMap; /** * The map scale factor. */ float mScaleFactor = 1.0f; /** * The script name. */ std::string mScriptName; }; /** * An unconditional map jump instruction. */ class FF7UncondJumpInstruction : public UncondJumpInstruction{ public: /** * Whether or not this is really a call to a script function. */ bool _isCall; /** * Constructor. */ FF7UncondJumpInstruction() : _isCall(false) {} /** * Indicates if the instruction is a function call. * * @return true if the instruction is a function call, false if not. */ virtual bool isFuncCall() const; /** * Indicates if the instruction is an unconditional jump. * * @return true if the instruction is an unconditional jump, false * if it's not. */ virtual bool isUncondJump() const; /** * Retrieves the destination address of the jump. * * @return The offset (number of bytes) to jump from the beginning * of the instruction. */ virtual uint32 getDestAddress() const; /** * Processes the instruction. * * @param func[in] Function to process. * @param stack[out] Function stack. * @param engine[in] Engine. * @param codeGen[in] Code generator. * @todo Func and engine are unused? * @todo Understand and document properly. */ virtual void processInst( Function& func, ValueStack &stack, Engine *engine, CodeGenerator *codeGen ) override; /** * Prints the instruction * * @param output The stram to print the instruction to. * @todo Understand and document properly. */ virtual std::ostream& print(std::ostream &output) const override; }; /** * A conditional map jump instruction. */ class FF7CondJumpInstruction : public CondJumpInstruction{ public: /** * Processes the instruction. * * @param func[in] Function to process. * @param stack[out] Function stack. * @param engine[in] Engine. * @param codeGen[in] Code generator. * @todo Func and engine are unused? * @todo Understand and document properly. */ virtual void processInst( Function& func, ValueStack &stack, Engine *engine, CodeGenerator *codeGen ) override; /** * Retrieves the destination address of the jump. * * @return The offset (number of bytes) to jump from the beginning * of the instruction. */ virtual uint32 getDestAddress() const override; /** * Prints the instruction * * @param output The stram to print the instruction to. * @todo Understand and document properly. */ virtual std::ostream& print(std::ostream &output) const override; }; /** * A script flow control instruction. */ class FF7ControlFlowInstruction : public KernelCallInstruction{ public: /** * Create a FF7ControlFlowInstruction. * * @return Pointer to the newly created instruction. */ static InstPtr Create(){return new FF7ControlFlowInstruction();} /** * Processes the instruction. * * @param func[in] Function to process. * @param stack[out] Function stack. * @param engine[in] Engine. * @param codeGen[in] Code generator. * @todo Func and engine are unused? * @todo Understand and document properly. */ virtual void processInst( Function& func, ValueStack &stack, Engine *engine, CodeGenerator *codeGen ) override; private: /** * Processes a REQ command. * * Opcode: 0x01 * Short name: REQ * Long name: Request remote execution (asynchronous, * non-guaranteed) * * Memory layout * 0x01 * E * P/F * * Arguments * * const UByte E: The ID of the target entity. * const Bit[3] P: The priority at which we want to execute the * remote script (high 3 bits of byte). * const Bit[5] F: The ID of the specific member function of E to be * executed (low 5 bits of byte). * * Requests that a remote entity executes one of its member * functions at a specified priority. The request is asynchronous * and returns immediately without waiting for the remote execution * to start or finish. If the specified priority is already busy * executing, the request will fail silently. * * @param codegen[in|out] Code generator. Output lines are appended * to it. * @param engine[in] The engine instance to fetch entities. */ void processREQ( CodeGenerator* codeGen, const FF7FieldEngine& engine ); /** * Processes a REQSW command. * * Opcode: 0x02 * Short name: REQSW * Long name: Request remote execution (asynchronous execution, * guaranteed) * * Memory layout * 0x02 * E * P/F * * Arguments * * const UByte E: The ID of the target entity. * const Bit[3] P: The priority at which we want to execute the * remote script (high 3 bits of byte). * const Bit[5] F: The ID of the specific member function of E to be * executed (low 5 bits of byte). * * Requests that a remote entity executes one of its member * functions at a specified priority. If the specified priority is * already busy executing, the request will block until it becomes * available and only then return. The remote execution is still * carried out asynchronously, with no notification of completion. * * @param codegen[in|out] Code generator. Output lines are appended * to it. * @param engine[in] The engine instance to fetch entities. */ void processREQSW( CodeGenerator* codeGen, const FF7FieldEngine& engine ); /** * Processes a REQEW command. * * Opcode: 0x03 * Short name: REQEW * Long name: Request remote execution (synchronous, guaranteed) * * Memory layout * 0x03 * E * P/F * * Arguments * * const UByte E: The ID of the target entity. * const Bit[3] P: The priority at which we want to execute the * remote script (high 3 bits of byte). * const Bit[5] F: The ID of the specific member function of E to be * executed (low 5 bits of byte). * * Requests that a remote entity executes one of its member * functions at a specified priority. The request will block until * remote execution has finished before returning. * * @param codegen[in|out] Code generator. Output lines are appended * to it. * @param engine[in] The engine instance to fetch entities. */ void processREQEW( CodeGenerator* codeGen, const FF7FieldEngine& engine ); /** * Processes a RETTO command. * * Opcode: 0x07 * Short name: RETTO * Long name: Return To * * Memory layout * 0x07 * P/F * * Arguments * const Bit[3] P: The priority at which we want to execute the * remote script (high 3 bits of byte). * const Bit[5] F: The ID of the specific member function of the * current entity to be executed to (low 5 bits * of byte). * * Stops the active script loop for this entity and also any script * loops (except the main) that are queuing to be executed after the * current script. This is essentially the same as adding a RET onto * each of the active / queued scripts next execution position and * returning the current op index to index for each script. Then the * script control is passed to the script F within the current * entity with the priority P. * * @param codegen[in|out] Code generator. Output lines are appended * to it. */ void processRETTO(CodeGenerator* codeGen); /** * Processes a WAIT command. * * Opcode: 0x24 * Short name: WAIT * Long name: Wait * * Memory layout * 0x24 * A * * Arguments * const UShort A: Amount (number of frames) to wait. * * Pauses current script execution for a specific amount of time. * Rather than a specific time value in milliseconds/seconds, * the amount specifies the number of frames that must be drawn * before execution resumes. Since the game runs at 30fps, * WAIT(0x1E) (or WAIT(30) in decimal) will pause script execution * for 1 second, WAIT(0x96) will pause for 5 seconds, and so on. * * @param codegen[in|out] Code generator. Output lines are appended * to it. */ void processWAIT(CodeGenerator* codeGen); }; /** * A module instruction. */ class FF7ModuleInstruction : public KernelCallInstruction{ public: /** * Processes the instruction. * * @param func[in] Function to process. * @param stack[out] Function stack. * @param engine[in] Engine. * @param codeGen[in] Code generator. * @todo Func and engine are unused? * @todo Understand and document properly. */ virtual void processInst( Function& func, ValueStack &stack, Engine *engine, CodeGenerator *codeGen ) override; private: /** * Processes a BATTLE command. * * Opcode: 0x70 * Short name: BATTLE * Long name: Start battle * * Memory layout * 0x70 * B * N * N * * Arguments * const UByte B: Bank (16-bit) to retrieve the address of the * battle ID, or zero if it is given as a literal * value. * const UWord N: Battle ID, or address to find ID if B is * non-zero. * * This launches the battle module with whatever battle number is * used in the argument, or the value retrieved from memory location * N if B is non-zero. Battle 1, 2, and 999 (0x03E7) are debug * battles. * * @param codegen[in|out] Code generator. Output lines are appended * to it. */ void processBATTLE(CodeGenerator* codeGen); /** * Processes a BTLON command. * * Opcode: 0x71 * Short name: BTLON * Long name: Battle switch * * Memory layout * 0x71 * S * * Arguments * const UByte S: Switch battles on/off (0/1, respectively). * * Turns random encounters on or off for this field. Note that if a * field does not have any Encounter Data set in its field file, * battles will not occur regardless of the argument passed with * this opcode. * * @param codegen[in|out] Code generator. Output lines are appended * to it. */ void processBTLON(CodeGenerator* codeGen); /** * Processes a MAPJUMP command. * * Opcode: 0x60 * Short name: BTLON * Long name: Change Field * * Memory layout * 0x60 * I * I * X * X * Y * Y * Z * Z * D * * Arguments * const UShort I: Field ID of the map to jump to. * const Short X: X-coordinate of the player on the next field. * const Short Y: Y-coordinate of the player on the next field. * const Short Z: Z-coordinate of the player on the next field. * const UByte D: Direction the character will be facing on the * next field, in the standard game format. * * Switches fields to the one indicated by I, and places the * character at the coordinates and direction specified. This is an * alternative to using a gateway, and can complement their usage as * it allows for more than 12 gateways by simulating their behaviour * through a LINE which, when crossed, executes a MAPJUMP. * * @param codegen[in|out] Code generator. Output lines are appended * to it. * @param func[in] Function * @todo What is func for? */ void processMAPJUMP(CodeGenerator* codeGen, Function& func); }; class FF7MathInstruction : public StoreInstruction { public: virtual void processInst(Function& func, ValueStack &stack, Engine *engine, CodeGenerator *codeGen) override; private: void processSaturatedPLUS(CodeGenerator* codeGen); void processSaturatedPLUS2(CodeGenerator* codeGen); void processSaturatedMINUS(CodeGenerator* codeGen); void processSaturatedMINUS2(CodeGenerator* codeGen); void processSaturatedINC(CodeGenerator* codeGen); void processSaturatedINC2(CodeGenerator* codeGen); void processSaturatedDEC(CodeGenerator* codeGen); void processSaturatedDEC2(CodeGenerator* codeGen); void processRDMSD(CodeGenerator* codeGen); void processSETBYTE_SETWORD(CodeGenerator* codeGen); void processBITON(CodeGenerator* codeGen); void processPLUSx_MINUSx(CodeGenerator* codeGen, const std::string& op); void processINCx_DECx(CodeGenerator* codeGen, const std::string& op); void processRANDOM(CodeGenerator* codeGen); }; class FF7WindowInstruction : public KernelCallInstruction { public: virtual void processInst(Function& func, ValueStack &stack, Engine *engine, CodeGenerator *codeGen) override; private: void processMESSAGE(CodeGenerator* codeGen, const std::string& scriptName); void processMPNAM(CodeGenerator* codeGen); void processMENU2(CodeGenerator* codeGen); void processWINDOW(CodeGenerator* codeGen); void processWCLSE(CodeGenerator* codeGen); }; class FF7PartyInstruction : public KernelCallInstruction { public: virtual void processInst(Function& func, ValueStack &stack, Engine *engine, CodeGenerator *codeGen) override; private: void processSTITM(CodeGenerator* codeGen); void processPRTYE(CodeGenerator* codeGen); }; class FF7ModelInstruction : public KernelCallInstruction { public: virtual void processInst(Function& func, ValueStack &stack, Engine *engine, CodeGenerator *codeGen) override; private: void processTLKON(CodeGenerator* codeGen, const std::string& entity); void processPC(CodeGenerator* codeGen, const std::string& entity); void processCHAR(CodeGenerator* codeGen, const std::string& entity); void processDFANM(CodeGenerator* codeGen, const std::string& entity, int charId); void processANIME1(CodeGenerator* codeGen, const std::string& entity, int charId); void processVISI(CodeGenerator* codeGen, const std::string& entity); void processXYZI(CodeGenerator* codeGen, const std::string& entity); void processMOVE(CodeGenerator* codeGen, const std::string& entity); void processMSPED(CodeGenerator* codeGen, const std::string& entity); void processDIR(CodeGenerator* codeGen, const std::string& entity); void processTURNGEN(CodeGenerator* codeGen, const std::string& entity); void processGETAI(CodeGenerator* codeGen, const FF7FieldEngine& engine); void processANIM_2(CodeGenerator* codeGen, const std::string& entity, int charId); void processCANIM2(CodeGenerator* codeGen, const std::string& entity, int charId); void processCANM_2(CodeGenerator* codeGen, const std::string& entity, int charId); void processCC(CodeGenerator* codeGen, const FF7FieldEngine& engine); void processSOLID(CodeGenerator* codeGen, const std::string& entity); }; class FF7WalkmeshInstruction : public KernelCallInstruction { public: virtual void processInst(Function& func, ValueStack &stack, Engine *engine, CodeGenerator *codeGen) override; private: void processUC(CodeGenerator* codeGen); }; class FF7BackgroundInstruction : public KernelCallInstruction { public: virtual void processInst(Function& func, ValueStack &stack, Engine *engine, CodeGenerator *codeGen) override; private: void processBGON(CodeGenerator* codeGen); void processBGOFF(CodeGenerator* codeGen); void processBGCLR(CodeGenerator* codeGen); void processSTPAL(CodeGenerator* codeGen); void processLDPAL(CodeGenerator* codeGen); void processCPPAL(CodeGenerator* codeGen); void processADPAL(CodeGenerator* codeGen); void processMPPAL2(CodeGenerator* codeGen); void processSTPLS(CodeGenerator* codeGen); void processLDPLS(CodeGenerator* codeGen); }; class FF7CameraInstruction : public KernelCallInstruction { public: virtual void processInst(Function& func, ValueStack &stack, Engine *engine, CodeGenerator *codeGen) override; private: void processNFADE(CodeGenerator* codeGen); void processSCR2D(CodeGenerator* codeGen); void processSCR2DC(CodeGenerator* codeGen); void processFADE(CodeGenerator* codeGen); }; class FF7AudioVideoInstruction : public KernelCallInstruction { public: virtual void processInst(Function& func, ValueStack &stack, Engine *engine, CodeGenerator *codeGen) override; private: void processAKAO2(CodeGenerator* codeGen); void processMUSIC(CodeGenerator* codeGen); void processSOUND(CodeGenerator* codeGen); void processAKAO(CodeGenerator* codeGen); void processMULCK(CodeGenerator* codeGen); void processPMVIE(CodeGenerator* codeGen); void processMOVIE(CodeGenerator* codeGen); void processMVIEF(CodeGenerator* codeGen); }; class FF7UncategorizedInstruction : public KernelCallInstruction { public: virtual void processInst(Function& func, ValueStack &stack, Engine *engine, CodeGenerator *codeGen) override; }; class FF7NoOperationInstruction : public Instruction { public: static InstPtr Create() { return new FF7NoOperationInstruction(); } virtual void processInst(Function& func, ValueStack &stack, Engine *engine, CodeGenerator *codeGen) override; }; }