/*
* V-Gears
* Copyright (C) 2022 V-Gears Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#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, size_t index):
mName(name), index_(index), is_line_(false)
{}
/**
* Retrieves the entity name.
*
* @return The entity name.
*/
std::string Name() const{return mName;}
/**
* Retrieves the entity index.
*
* The index is the one at which appears in the original
* game script.
*
* @return The entity index.
*/
size_t GetIndex() const{return index_;}
/**
* 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. If the entity is a line,
* the name will be overridden.
* @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;
}
/**
* Marks the entity as a line.
*
* @param line[in] True to mark the entity as a line,
* false to unmark it.
* @param point_a[in] First point of the line. Can be null
* if line is false.
* @param point_b[in] Second point of the line. Can be
* null if line is false.
*/
void MarkAsLine(
bool line, std::vector point_a, std::vector point_b
){
is_line_ = line;
point_a_.clear();
point_b_.clear();
if (line){
if (point_a.size() >= 3 && point_b.size() >= 3){
point_a_.push_back(point_a[0]);
point_a_.push_back(point_a[1]);
point_a_.push_back(point_a[2]);
point_b_.push_back(point_b[0]);
point_b_.push_back(point_b[1]);
point_b_.push_back(point_b[2]);
}
// TODO: Notify on else.
}
// TODO: These are not getting to the final script.
// Maybe this can be removed?
AddFunction("on_enter_line", 1);
AddFunction("on_move_to_line", 2);
AddFunction("on_cross_line", 3);
AddFunction("on_leave_line", 4);
}
/**
* Checks if the entity is a line.
*
* Note that an entity is not considered to be a line
* until a function has been found containing the opcode
* LINE and {@see MarkAsLine} has been called.
*
* @return true if the entity is a line.
*/
bool IsLine(){return is_line_;}
/**
* Retrieves the first point of the line entity.
*
* If the entity is not a line, the behavior is undefined.
*
* @return The first point of the line entity.
*/
std::vector GetLinePointA(){return point_a_;}
/**
* Retrieves the second point of the line entity.
*
* If the entity is not a line, the behavior is undefined.
*
* @return The second point of the line entity.
*/
std::vector GetLinePointB(){return point_b_;}
private:
/**
* Entity name.
*/
std::string mName;
/**
* Entity index.
*/
size_t index_;
/**
* Function list.
* @todo What is a function here? An Opcode?
*/
std::map< size_t, std::string > mFunctions;
/**
* Indicates if the entity is a line.
*/
bool is_line_;
/**
* The first point of a line entity.
*
* If the entity is not a line, it may not be initializer.
*/
std::vector point_a_;
/**
* The second point of a line entity.
*
* If the entity is not a line, it may not be initializer.
*/
std::vector point_b_;
};
/**
* Retrieves the dissasembler.
*
* @param insts[in] List of instructions.
* @param raw_script_data[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& raw_script_data
) 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;
/**
* Retrieves all non-line entities in the map.
*
* @return A list of non-line entities.
*/
std::vector GetEntityList() const;
/**
* Retrieves all line entities in the map.
*
* @param A list of line entities.
*/
std::vector GetLineList() const;
/**
* Retrieves all entities in the map.
*
* @return A map of entities, with the name and index.
*/
std::map GetEntityIndexMap() const{return mEntityIndexMap;}
/**
* Adds a function to an entity.
*
* @param entity_name Name of the entity.
* @param entity_index Index of the entity.
* @param func_name Name of the function.
* @param func_index Index of the function.
*/
void AddEntityFunction(
const std::string& entity_name, size_t entity_index,
const std::string& func_name, size_t func_index
);
/**
* Marks an entity as a line.
*
* @param entity_index Index of the entity.
* @param line[in] True to mark the entity as a line, false to
* unmark it.
* @param point_a[in] First point of the line. Can be null if line
* is false.
* @param point_b[in] Second point of the line. Can be null if
* line is false.
*/
void MarkEntityAsLine(
size_t entity_index, bool line,
std::vector point_a, std::vector point_b
);
/**
* Checks if an entity has been marked as a line.
*
* @param entity_index[in] Index of the entity to check.
* @return True if the entity is a line. False if it isn't, or if
* there is no such entity.
*/
bool EntityIsLine(size_t entity_index);
/**
* 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. Unused.
*/
void RemoveExtraneousReturnStatements(InstVec& insts, Graph g);
/**
* Removes trailing infinite loops.
*
* In FF7 some scripts ends with an infinite loop to keep it
* alive. in VGears 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 VGears 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:
// TODO: Make private and add accessors.
/**
* 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 code_gen[in] Code generator.
* @todo Func and engine are unused?
* @todo Understand and document properly.
*/
virtual void processInst(
Function& func, ValueStack &stack, Engine *engine, CodeGenerator *code_gen
) 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 a conditional jump instruction.
*
* Checks if the condition is a function or a comparison, and
* adds the function to the stack.
*
* @param function[in] Function to process. Unused.
* @param stack[out] Function stack. The Instruction will be added
* here.
* @param engine[in] Engine. Unused.
* @param code_gen[in] Code generator.
*/
virtual void processInst(
Function& function, ValueStack &stack, Engine *engine, CodeGenerator *code_gen
) 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 code_gen[in] Code generator.
*/
virtual void processInst(
Function& func, ValueStack &stack, Engine *engine, CodeGenerator *code_gen
) 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* code_gen, 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* code_gen, 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* code_gen, 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* code_gen);
/**
* 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* code_gen);
};
/**
* 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. Unused.
* @param code_gen[in] Code generator.
*/
virtual void processInst(
Function& func, ValueStack &stack, Engine *engine, CodeGenerator *code_gen
) override;
private:
/**
* Processes a BATTLE opcode.
*
* 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* code_gen);
/**
* Processes a BTLON opcode.
*
* 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* code_gen);
/**
* Processes a MAPJUMP opcode.
*
* 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 behavior 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* code_gen, Function& func);
};
/**
* A math instruction.
*/
class FF7MathInstruction : public StoreInstruction{
public:
/**
* Processes the instruction.
*
* @param func[in] Function to process.
* @param stack[out] Function stack.
* @param engine[in] Engine. Unused.
* @param code_gen[in] Code generator.
*/
virtual void processInst(
Function& func, ValueStack &stack, Engine *engine, CodeGenerator *code_gen
) override;
private:
void processSaturatedPLUS(CodeGenerator* code_gen);
void processSaturatedPLUS2(CodeGenerator* code_gen);
void processSaturatedMINUS(CodeGenerator* code_gen);
void processSaturatedMINUS2(CodeGenerator* code_gen);
void processSaturatedINC(CodeGenerator* code_gen);
void processSaturatedINC2(CodeGenerator* code_gen);
void processSaturatedDEC(CodeGenerator* code_gen);
void processSaturatedDEC2(CodeGenerator* code_gen);
void processRDMSD(CodeGenerator* code_gen);
void processSETBYTE_SETWORD(CodeGenerator* code_gen);
/**
* Processes a BITON opcode.
*
* Opcode: 0x82
* Short name: BITON
* Long name: Set Bit
*
* Memory layout (4 bytes)
* |0x82|D/S|A|B|
*
* Arguments
* - const Bit[4] D: Destination bank.
* - const Bit[4] S: Source bank.
* - const UByte A: Destination address.
* - const UByte Bit: The number of the bit to turn on.
*
* Sets the nth bit in the "A" location, where n is a number
* between 0-7 supplied in B. A value of zero in B will set the
* least significant bit. If the Source Bank is 0 then the bit to
* be set is taken from "Bit". If the Source Bank is an 8 bit
* bank, then the bit is the address in that bank where the
* operand is.
*
* @param code_gen[in|out] Code generator. Output lines are
* appended to it.
*/
void processBITON(CodeGenerator* code_gen);
/**
* Processes a BITON opcode.
*
* Opcode: 0x83
* Short name: BITOFF
* Long name: Reset Bit
*
* Memory layout (4 bytes)
* |0x83|D/S|A|B|
*
* Arguments
* - const Bit[4] D: Destination bank.
* - const Bit[4] S: Source bank.
* - const UByte A: Destination address.
* - const UByte Bit: The number of the bit to turn off.
*
* Sets the nth bit in the "A" location, where n is a number
* between 0-7 supplied in B. A value of zero in B will reset the
* least significant bit. If the Source Bank is 0 then the bit to
* be set is taken from "Bit". If the Source Bank is an 8 bit
* bank, then the bit is the address in that bank where the
* operand is.
*
* @param code_gen[in|out] Code generator. Output lines are
* appended to it.
*/
void processBITOFF(CodeGenerator* code_gen);
void processPLUSx_MINUSx(CodeGenerator* code_gen, const std::string& op);
void processINCx_DECx(CodeGenerator* code_gen, const std::string& op);
void processRANDOM(CodeGenerator* code_gen);
};
/**
* A window instruction.
*/
class FF7WindowInstruction : public KernelCallInstruction{
public:
/**
* Processes the instruction.
*
* @param func[in] Function to process.
* @param stack[out] Function stack.
* @param engine[in] Engine.
* @param code_gen[in] Code generator.
*/
virtual void processInst(
Function& func, ValueStack &stack, Engine *engine, CodeGenerator *code_gen
) override;
private:
void processMESSAGE(CodeGenerator* code_gen, const std::string& script_name);
void processMPNAM(CodeGenerator* code_gen);
void processMENU2(CodeGenerator* code_gen);
void processWINDOW(CodeGenerator* code_gen);
void processWCLSE(CodeGenerator* code_gen);
};
/**
* A party instruction
*/
class FF7PartyInstruction : public KernelCallInstruction{
public:
/**
* Processes the instruction.
*
* @param func[in] Function to process.
* @param stack[out] Function stack.
* @param engine[in] Engine. Unused
* @param code_gen[in] Code generator.
*/
virtual void processInst(
Function& func, ValueStack &stack, Engine *engine, CodeGenerator *code_gen
) override;
private:
void processSTITM(CodeGenerator* code_gen);
void processPRTYE(CodeGenerator* code_gen);
};
/**
* A model instruction.
*/
class FF7ModelInstruction : public KernelCallInstruction{
public:
/**
* Processes the instruction.
*
* @param func[in] Function to process.
* @param stack[out] Function stack.
* @param engine[in] Engine.
* @param code_gen[in] Code generator.
*/
virtual void processInst(
Function& func, ValueStack &stack, Engine *engine, CodeGenerator *code_gen
) override;
private:
/**
* Processes a JOIN opcode.
*
* Opcode: 0x08
* Short name: JOIN
* Long name: Party Field Join
* Memory layout (2 bytes)
* |0x08|S|
*
* Arguments
* - const UByte S: Speed that the characters join back together.
*
* Causes seperated party characters that have previously been
* SPLIT onto the field, to be joined back together again; that
* is, only the party leader becomes visible on the field. This
* should be called if a previous SPLIT has completed (the party
* members have finished speaking, or performing their actions,
* for example). As with SPLIT, the speed of the join is
* specified, from a scale of 1 (almost instant) to FF (very slow
* walk), and must be non-zero. In contrast to most MOVE related
* op codes, the speed is this setting is actually the total
* number of frames required. Depending on the distance from the
* player character and the number of frames required, the entity
* plays a run or walk animation. Also, all characters take the
* same time irrespective of distance. Calling JOIN without having
* previously SPLIT the characters will cause the party members to
* appear at the walkmesh origin and attempt to JOIN from there.
* This is not normally the required behaviour and should be
* avoided.
*
* @param code_gen The code generator.
*/
void processJOIN(CodeGenerator* code_gen);
/**
* Processes a SPLIT opcode.
*
* Opcode: 0x09
* Short name: SPLIT
* Long name: Party Field Split
*
* Memory layout (15 bytes)
* |0x20|B1/B2|B3/B4|B5/B6|XA|XA|YA|YA|DA|XB|XB|YB|YB|DB|S|
*
* Arguments
* - const Bit[4] B1: Bank for XA, or zero if XA is specified as a
* literal value.
* - const Bit[4] B2: Bank for YA, or zero if YA is specified as a
* literal value.
* - const Bit[4] B3: Bank for DA, or zero if DA is specified as a
* literal value.
* - const Bit[4] B4: Bank for XB, or zero if XB is specified as a
* literal value.
* - const Bit[4] B5: Bank for YB, or zero if YB is specified as a
* literal value.
* - const Bit[4] B6: Bank for DB, or zero if DB is specified as a
* literal value.
* - const Short XA: X-coordinate of the second character in the
* party after the split, or address for the value if B1 is
* non-zero.
* - const Short YA: Y-coordinate of the second character in the
* party after the split, or address for the value if B2 is
* non-zero.
* - const UByte DA: Direction the second character faces after the
* split, or address for the value if B3 is non-zero.
* - const Short XB: X-coordinate of the third character in the
* party after the split, or address for the value if B4 is
* non-zero.
* - const Short YB: Y-coordinate of the third character in the
* party after the split, or address for the value if B5 is
* non-zero.
* - const UByte DB: Direction the third character faces after the
* split, or address for the value if B6 is non-zero.
* - const UByte S: Speed that the characters split.
*
* Causes the common 'split effect' whereby the second and third
* characters in the current party 'come out' from the party
* leader. That is, they become visible in the field, starting
* from the center of the party leader, and move out to the
* coordinates specified in the argument list. This is commonly
* used when the other characters in the current party have an
* action or dialog to perform and must be individually visible in
* the field. As well as specifying final coordinates for the two
* other party characters, the directions each character faces
* after the split are specified as a byte, using the common
* direction values found throughout the game. Speed is also given
* and is used to specify the rate at which the characters leave
* the party leader, using a scale from 1 (almost instant) to FF
* (extremely slow walk); this must be non-zero. In contrast to
* most MOVE related op codes, the speed is this setting is
* actually the total number of frames required. Depending on the
* distance from the player character and the number of frames
* required, the entity plays a run or walk animation. Also, all
* characters take the same time irrespective of distance.
*
* @param code_gen The code generator.
*/
void processSPLIT(CodeGenerator* code_gen);
void processTLKON(CodeGenerator* code_gen, const std::string& entity);
void processPC(CodeGenerator* code_gen, const std::string& entity);
void processCHAR(CodeGenerator* code_gen, const std::string& entity);
void processDFANM(CodeGenerator* code_gen, const std::string& entity, int char_id);
void processANIME1(CodeGenerator* code_gen, const std::string& entity, int char_id);
void processVISI(CodeGenerator* code_gen, const std::string& entity);
void processXYZI(CodeGenerator* code_gen, const std::string& entity);
void processMOVE(CodeGenerator* code_gen, const std::string& entity);
void processMSPED(CodeGenerator* code_gen, const std::string& entity);
void processDIR(CodeGenerator* code_gen, const std::string& entity);
void processTURNGEN(CodeGenerator* code_gen, const std::string& entity);
void processGETAI(CodeGenerator* code_gen, const FF7FieldEngine& engine);
void processANIM_2(CodeGenerator* code_gen, const std::string& entity, int char_id);
void processCANIM2(CodeGenerator* code_gen, const std::string& entity, int char_id);
void processCANM_2(CodeGenerator* code_gen, const std::string& entity, int char_id);
void processCC(CodeGenerator* code_gen, const FF7FieldEngine& engine);
/**
* Processes a JUMP opcode.
*
* Opcode: 0xC0
* Short name: JUMP
* Long name: Jump
*
* Memory layout (7 bytes)
* |0xC2|B1/B2|B3/B4|X|Y|I|Steps|
*
* Arguments
* - const Bit[4] B1: Bank to retrieve X-coordinate, or zero if
* specifying X as a literal value.
* - const Bit[4] B2: Bank to retrieve Y-coordinate, or zero if
* specifying Y as a literal value.
* - const Bit[4] B3: Bank to retrieve triangle ID, or zero if
* specifying Z as a literal value.
* - const Bit[4] B4: Bank to retrieve jump height, or zero if
* specifying H as a literal value.
* - const Short X: X-coordinate of the target to jump to, or
* lower byte specifying address if B1 is non-zero.
* - const Short Y: Y-coordinate of the target to jump to, or
* lower byte specifying address if B2 is non-zero.
* - const Short I: Triangle ID of the target to jump to, or
* lower byte specifying address if B3 is non-zero.
* - const UShort Steps: Steps in jump. Must be non-zero if a
* literal value. Alternatively, lower byte specifies address if
* B4 is non-zero.
*
* Causes the character to jump to the specified point and
* triangle ID, with the jump curve peaking at a height which is
* increased by using a larger value for the H argument. In
* addition, the larger the number, the longer the jump will take
* to complete. A "normal" value is around 0x15, 0x01 is fast and
* instantaneous; the argument must not be zero or the game will
* crash. Whilst this is an unsigned two-byte number, a large
* value (beyond around 0x60) will not only cause a vast jump
* height, but also cause the screen to scroll erratically (the
* larger the number, the more erratic).
* Main update function go through all entity with JUMP state and
* if stage is 0 it calculates final Z point according to triangle
* id. It sets current coords as start coords. The main thing this
* function does is set B coefficient for later calculation. It
* defines as follows:
* B = (Z_final - Z_start) / steps - steps * 1.45;
* Then it set current step to 0 and stage to 1. On next update
* other part of function works. It's calculate real position.
* First it increment current step number. Then it calculate X and
* Y. They change linear so nothing interesting here. The Z
* calculation is as follows:
* Z_current = - step^2 * 1.45 + step * B + Z_start;
* If current substep equal number of steps then we set current
* triangle to final triangle and set stage to 2. Which finalizes
* the routine on next opcode call. Neither animation nor sound is
* specified in this opcode. An animation is played by using an
* animation opcode such as DFANM, and a SOUND played, before the
* jump.
*
* @param code_gen[in] The code generator.
* @param entity[in] The name of the entity.
*/
void processJUMP(CodeGenerator* code_gen, const std::string& entity);
/**
* Processes a AXYZI opcode
*
* Opcode: 0xC1
* Short name: AXYZI
* Long name: Entity Get Position
*
* Memory layout (8 bytes)
* |0xC1|B1/B2|B3/B4|A|X|Y|Z|I|
*
* Arguments
* - const Bit[4] B1: Bank to store X.
* - const Bit[4] B2: Bank to store Y.
* - const Bit[4] B3: Bank to store Z.
* - const Bit[4] B4: Bank to store I.
* - const UByte A: Entity ID whose field object will have its
* position retrieved from.
* - const UByte X: Address to store the X-coordinate.
* - const UByte Y: Address to store the Y-coordinate.
* - const UByte Z: Address to store the Z-coordinate.
* - const UByte I: Address to store the ID of the walkmesh
* triangle the object is standing on.
*
* Retrieves the coordinates of the field object that the entity,
* whose ID specified in A, is associated with. This opcode uses
* an entity ID, not a field object offset; as such, if an entity
* ID is given that does not have a field object, this opcode will
* store zero in each of the four address specified.
*/
void processAXYZI(CodeGenerator* code_gen);
/**
* Processes a LADER opcode.
*
* Opcode: 0xC2
* Short name: LADER
* Long name: Ladder
*
* Memory layout (15 bytes)
* |0xC2|B1/B2|B3/B4|X|X|Y|Y|Z|Z|I|I|K|A|D|S|
*
* Arguments
* - const Bit[4] B1: Bank to retrieve X-coordinate, or zero if X
* is specified as a literal value.
* - const Bit[4] B2: Bank to retrieve Y-coordinate, or zero if Y
* is specified as a literal value.
* - const Bit[4] B3: Bank to retrieve Z-coordinate, or zero if Z
* is specified as a literal value.
* - const Bit[4] B4: Bank to retrieve ID, or zero if I is
* specified as a literal value.
* - const Short X: X-coordinate of the end of the ladder, or
* address to find X-coordinate if B1 is non-zero.
* - const Short Y: Y-coordinate of the end of the ladder, or
* address to find Y-coordinate if B2 is non-zero.
* - const Short Z: Z-coordinate of the end of the ladder, or
* address to find Z-coordinate if B3 is non-zero.
* - const UShort I: ID of the walkmesh triangle found at the end
* of the ladder, or address to find ID if B4 is non-zero.
* - const UByte K: The keys used to move the character on the
* ladder.
* - const UByte A: Animation ID for the field object's movement
* animation.
* - const UByte D: Direction the character faces when climbing
* the ladder.
* - const UByte S: Speed of the animation whilst climbing the
* ladder.
*
* Causes the character to climb a ladder; that is, switching from
* standard walkmesh movement, to climbing along a line connecting
* two points on the walkmesh. If B1, B2, B3 or B4 is non-zero,
* then the value for that particular component is taken from
* memory using the corresponding bank and address specified,
* rather than as a literal value. Both retrieved values and
* literals can be used for different components. If using X, Y, Z
* or I as addresses, the lower byte should hold the address
* whilst the higher byte should be zero. The coordinates specify
* the end-point of the ladder; the current position of the
* character is used as the start point. The ID of the walkmesh
* triangle must be specified; this is the triangle the character
* will step onto after reaching the end point of the ladder. The
* K value specifies the keys used to move the character across
* the ladder; keys outside the range found in the table will
* cause unpredictable behaviour. The animation ID specifies an
* offset into the field object's animation list; this animation
* is played at the speed specified by S whilst the character
* climbs. Finally, the D argument is a direction value in the
* game's standard direction format, which orients the character
* on the ladder. This opcode is used as part of the character's
* entity, rather than in a seperate entity, as with a LINE. A
* LINE is used to set the start point of the ladder on the
* walkmesh. When this LINE is crossed by the player, a script in
* the LINE then uses a PREQ (or one of its variants), calling the
* script in the party leader that defines the LADER, causing the
* character to switch to 'climbing mode'. To set up a two-way
* ladder, two LINEs are used at either end, with different values
* for the LADER arguments, such as differing end points. If this
* opcode is used as part of a non-playable character entity, the
* NPC object will automatically climb from the start to the end
* point without need for player interaction.
*
* @param code_gen[in] The code generator.
* @param entity[in] The name of the entity.
*/
void processLADER(CodeGenerator* code_gen, const std::string& entity);
void processSOLID(CodeGenerator* code_gen, const std::string& entity);
/**
* Processes an OFST opcode.
*
* Opcode: 0xC3
* Short name: OFST
* Long name: Offset Object
*
* Memory layout (8 bytes)
* |0xC3|B1/B2|B3/B4|T|X|Y|Z|S|
*
* Arguments:
* - const Bit[4] B1: Bank to retrieve X offset, or zero if X is
* specified as a literal.
* - const Bit[4] B2: Bank to retrieve Y offset, or zero if Y is
* specified as a literal.
* - const Bit[4] B3: Bank to retrieve Z offset, or zero if Z is
* specified as a literal.
* - const Bit[4] B4: Bank to retrieve speed, or zero if S is
* specified as a literal.
* - const UByte T: Type of movement.
* - const Short X: X offset amount, relative to current position,
* or address to find X offset, if B1 is non-zero.
* - const Short Y: Y offset amount, relative to current position,
* or address to find Y offset, if B2 is non-zero.
* - const Short Z: Z offset amount, relative to current position,
* or address to find Z offset, if B3 is non-zero.
* - const UShort S: Speed of the offset movement, if type is
* non-zero, or address to find speed, if B4 is non-zero.
*
* Offsets the field object, belonging to the entity whose script
* this opcode resides in, by a certain amount. After being
* offset, the character continues to be constrained in movement
* as defined by the walkmesh's shape, but at a certain distance
* away from the normal walkmesh position. Other field objects are
* unaffected, and their position or movements are maintained on
* the walkmesh's original position. If B1, B2, B3 or B4 is
* non-zero, then the value for that particular component is taken
* from memory using the corresponding bank and address specified,
* rather than as a literal value. Both retrieved values and
* literals can be used for different components. If using T, X, Y
* or S as addresses, the lower byte should hold the address
* whilst the higher byte should be zero. The amount to offset is
* specified relative to the current position. If Type is
* specified, the object moves gradually from its current point to
* the offset position; this can be used to simulate movements
* such as elevators. Any type outside the range in the table will
* cause the offset not to occur. If the object is set to move
* gradually, then the speed of offset can be set; the greater the
* number, the slower the object moves to its target offset.
* Script execution may also be halted until the gradual offset
* has been completed. For this, see OFSTW.
*
* @param codegen[in] The code generator.
* @param entity[in] The entity name.
*/
void processOFST(CodeGenerator* codegen, const std::string& entity);
};
/**
* A walkmesh instruction.
*/
class FF7WalkmeshInstruction : public KernelCallInstruction{
public:
/**
* Processes the instruction.
*
* @param func[in] Function to process.
* @param stack[out] Function stack.
* @param engine[in] Engine. Unused.
* @param code_gen[in] Code generator.
*/
virtual void processInst(
Function& func, ValueStack &stack, Engine *engine, CodeGenerator *code_gen
) override;
private:
void processUC(CodeGenerator* code_gen);
/**
* Processes a LINE opcode.
*
* Opcode: 0xD0
* Short name: LINE
* Long name: Line definition
*
* Memory layout (7 bytes)
* |0xD0|XA|YA|ZA|XB|YB|ZB|
*
* Arguments:
* - const Short XA: X-coordinate of the first point of the line.
* - const Short YA: Y-coordinate of the first point of the line.
* - const Short ZA: Z-coordinate of the first point of the line.
* - const Short XB: X-coordinate of the second point of the line.
* - const Short YB: Y-coordinate of the second point of the line.
* - const Short ZB: Z-coordinate of the second point of the line.
*
* Defines a line on the walkmesh that, when crossed by a playable
* character, causes one of the entity's scripts to be executed.
* These are similar to the triggers in Section 8. All the lines
* in the current field can be turned on or off by using the LINON
* opcode.
*
* There are generally 6 scripts (other than the init and main) if
* the entity is a LINE.
* - script index 2 -> S1 - [OK].
* - script index 3 -> S2 - Move.
* - script index 4 -> S3 - Move.
* - script index 5 -> S4 - Go.
* - script index 6 -> S5 - Go 1x.
* - script index 7 -> S6 - Go away.
*
* @param code_gen The code generator.
* @param entity[in] The entity name.
*/
void processLINE(CodeGenerator* code_gen, const std::string& entity);
};
/**
* A background instruction.
*/
class FF7BackgroundInstruction : public KernelCallInstruction{
public:
/**
* Processes the instruction.
*
* @param func[in] Function to process.
* @param stack[out] Function stack.
* @param engine[in] Engine. Unused
* @param code_gen[in] Code generator.
*/
virtual void processInst(
Function& func, ValueStack &stack, Engine *engine, CodeGenerator *code_gen
) override;
private:
void processBGON(CodeGenerator* code_gen);
void processBGOFF(CodeGenerator* code_gen);
void processBGCLR(CodeGenerator* code_gen);
void processSTPAL(CodeGenerator* code_gen);
void processLDPAL(CodeGenerator* code_gen);
void processCPPAL(CodeGenerator* code_gen);
void processADPAL(CodeGenerator* code_gen);
void processMPPAL2(CodeGenerator* code_gen);
void processSTPLS(CodeGenerator* code_gen);
void processLDPLS(CodeGenerator* code_gen);
};
/**
* A camera instruction.
*/
class FF7CameraInstruction : public KernelCallInstruction{
public:
/**
* Processes the instruction.
*
* @param func[in] Function to process.
* @param stack[out] Function stack.
* @param engine[in] Engine. Unused
* @param code_gen[in] Code generator.
*/
virtual void processInst(
Function& func, ValueStack &stack, Engine *engine, CodeGenerator *code_gen
) override;
private:
void processNFADE(CodeGenerator* code_gen);
void processSCR2D(CodeGenerator* code_gen);
void processSCR2DC(CodeGenerator* code_gen);
void processFADE(CodeGenerator* code_gen);
};
/**
* An audio or video (or both) instruction.
*/
class FF7AudioVideoInstruction : public KernelCallInstruction{
public:
/**
* Processes the instruction.
*
* @param func[in] Function to process.
* @param stack[out] Function stack.
* @param engine[in] Engine. Unused
* @param code_gen[in] Code generator.
*/
virtual void processInst(
Function& func, ValueStack &stack, Engine *engine, CodeGenerator *code_gen
) override;
private:
void processAKAO2(CodeGenerator* code_gen);
void processMUSIC(CodeGenerator* code_gen);
void processSOUND(CodeGenerator* code_gen);
void processAKAO(CodeGenerator* code_gen);
void processMULCK(CodeGenerator* code_gen);
void processPMVIE(CodeGenerator* code_gen);
void processMOVIE(CodeGenerator* code_gen);
void processMVIEF(CodeGenerator* code_gen);
};
/**
* An instructions that doesn't fall in any other category.
*/
class FF7UncategorizedInstruction : public KernelCallInstruction{
public:
/**
* Processes the instruction.
*
* @param func[in] Function to process.
* @param stack[out] Function stack.
* @param engine[in] Engine. Unused.
* @param code_gen[in] Code generator.
*/
virtual void processInst(
Function& func, ValueStack &stack, Engine *engine, CodeGenerator *code_gen
) override;
};
/**
* An instruction that does nothing.
*/
class FF7NoOperationInstruction : public Instruction{
public:
/**
* Generates a instruction that does nothing.
*
* @return The generated instruction.
*/
static InstPtr Create(){return new FF7NoOperationInstruction();}
/**
* Processes the instruction.
*
* It doesn't do anything.
*
* @param func[in] Function to process. Unused.
* @param stack[out] Function stack. Unused.
* @param engine[in] Engine. Unused.
* @param code_gen[in] Code generator. Unused.
*/
virtual void processInst(
Function& func, ValueStack &stack, Engine *engine, CodeGenerator *code_gen
) override;
};
}