ff7_field_disassembler.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. #include <vector>
  2. #include "ff7_field_disassembler.h"
  3. #include "ff7_field_engine.h"
  4. #include "decompiler_engine.h"
  5. #include <boost/format.hpp>
  6. #include "lzs.h"
  7. #include "make_unique.h"
  8. #include <boost/algorithm/string/split.hpp>
  9. #include <boost/algorithm/string.hpp>
  10. #include "ff7_field_codegen.h"
  11. FF7::FF7Disassembler::FF7Disassembler(SUDM::IScriptFormatter& formatter, FF7FieldEngine* engine, InstVec& insts, const std::vector<unsigned char>& rawScriptData)
  12. : SimpleDisassembler(insts),
  13. mEngine(engine),
  14. mFormatter(formatter)
  15. {
  16. mbFromRaw = true;
  17. kSectionPointersSize = 0; // If loading a raw section then we don't have a "sections header" to skip
  18. auto dataCopy = rawScriptData;
  19. mStream = std::make_unique<BinaryReader>(std::move(dataCopy));
  20. ReadHeader();
  21. }
  22. FF7::FF7Disassembler::FF7Disassembler(SUDM::IScriptFormatter& formatter, FF7FieldEngine *engine, InstVec &insts)
  23. : SimpleDisassembler(insts),
  24. mEngine(engine),
  25. mFormatter(formatter)
  26. {
  27. }
  28. FF7::FF7Disassembler::~FF7Disassembler()
  29. {
  30. }
  31. void FF7::FF7Disassembler::ReadHeader()
  32. {
  33. if (!mbFromRaw)
  34. {
  35. // First read the file section pointers
  36. for (int i = 0; i < kNumSections; i++)
  37. {
  38. mSections[i] = mStream->ReadU32();
  39. }
  40. // Now fix up from PSX RAM pointers to simple file offsets
  41. const uint32 basePtr = mSections[0];
  42. for (int i = 0; i < kNumSections; i++)
  43. {
  44. mSections[i] = (mSections[i] - basePtr) + kSectionPointersSize;
  45. }
  46. // Now seek to the script section
  47. mStream->Seek(mSections[eScript]);
  48. }
  49. // Read the script header
  50. mHeader.Read(*mStream);
  51. mScaleFactor = static_cast<float>(mHeader.mScale) / 512.0f;
  52. }
  53. void FF7::FF7Disassembler::open(const char *filename)
  54. {
  55. // Read all of the file, decompress it, then stuff it into a stream
  56. mStream = std::make_unique<BinaryReader>(Lzs::Decompress(BinaryReader::ReadAll(filename)));
  57. ReadHeader();
  58. }
  59. uint32 FF7::FF7Disassembler::GetEndOfScriptOffset(uint16 curEntryPoint, size_t entityIndex, size_t scriptIndex)
  60. {
  61. uint16 nextEntryPoint = curEntryPoint;
  62. do
  63. {
  64. if (scriptIndex + 1 >= 32)
  65. {
  66. if (entityIndex + 1 >= mHeader.mEntityScripts.size())
  67. {
  68. // This is the very last script, so use the end of its data which is the offset to strings
  69. return mHeader.mOffsetToStrings;
  70. }
  71. else
  72. {
  73. // Wrap around to the next entity
  74. entityIndex++;
  75. scriptIndex = 0;
  76. }
  77. }
  78. else
  79. {
  80. // Get the next script in the same entity
  81. scriptIndex++;
  82. }
  83. nextEntryPoint = mHeader.mEntityScripts[entityIndex][scriptIndex];
  84. } while (nextEntryPoint == curEntryPoint);
  85. return nextEntryPoint;
  86. }
  87. std::unique_ptr<Function> FF7::FF7Disassembler::StartFunction(size_t scriptIndex)
  88. {
  89. auto func = std::make_unique<Function>();
  90. func->_retVal = false;
  91. func->_args = 0;
  92. func->_name = "script_" + std::to_string(scriptIndex);
  93. func->mStartAddr = _address;
  94. return func;
  95. }
  96. std::unique_ptr<Function> FF7::FF7Disassembler::StartLineFunction(size_t script_index){
  97. auto func = std::make_unique<Function>();
  98. func->_retVal = false;
  99. func->_args = 0;
  100. switch (script_index){
  101. case 0: func->_name = "on_enter_line"; break;
  102. case 1: func->_name = "on_move_to_line"; break;
  103. case 4: func->_name = "on_cross_line"; break;
  104. case 5: func->_name = "on_leave_line"; break;
  105. default: func->_name = "script_" + std::to_string(script_index);
  106. }
  107. func->mStartAddr = _address;
  108. return func;
  109. }
  110. struct ScriptInfo
  111. {
  112. uint16 mEntryPoint;
  113. uint32 mNextEntryPoint;
  114. size_t mIndex;
  115. };
  116. void FF7::FF7Disassembler::doDisassemble()
  117. {
  118. // Loop through the scripts for each entity
  119. for (size_t entityNumber = 0; entityNumber < mHeader.mEntityScripts.size(); entityNumber++)
  120. {
  121. std::string strOriginalName = mHeader.mFieldEntityNames[entityNumber].data();
  122. if (strOriginalName.empty())
  123. {
  124. // If the entity name was blank in the file then use a consistent generated name
  125. strOriginalName = "entity_" + std::to_string(entityNumber);
  126. }
  127. const std::string entityName = mFormatter.EntityName(strOriginalName);
  128. // Only parse each script one
  129. std::set<uint16> parsedScripts;
  130. std::vector<ScriptInfo> scriptInfo;
  131. // Collect the scripts to parse
  132. for (size_t scriptIndex = 0; scriptIndex < mHeader.mEntityScripts[entityNumber].size(); scriptIndex++)
  133. {
  134. uint16 scriptEntryPoint = mHeader.mEntityScripts[entityNumber][scriptIndex];
  135. if (parsedScripts.find(scriptEntryPoint) != std::end(parsedScripts))
  136. {
  137. // If we've already parsed this scripts entry point, then don't do it again as it means
  138. // two scripts have the same entry which only seems to be true for "empty" scripts
  139. continue;
  140. }
  141. parsedScripts.insert(scriptEntryPoint);
  142. const uint32 nextScriptEntryPoint = GetEndOfScriptOffset(scriptEntryPoint, entityNumber, scriptIndex);
  143. const uint32 scriptSize = nextScriptEntryPoint - scriptEntryPoint;
  144. if (scriptSize > 0)
  145. {
  146. ScriptInfo info = { scriptEntryPoint, nextScriptEntryPoint, scriptIndex };
  147. scriptInfo.push_back(info);
  148. }
  149. }
  150. for (auto it = scriptInfo.begin(); it != scriptInfo.end(); it++)
  151. {
  152. const bool isStart = it == scriptInfo.begin();
  153. const bool isEnd = it == (--scriptInfo.end());
  154. DisassembleIndivdualScript(entityName, entityNumber, it->mIndex, it->mEntryPoint, it->mNextEntryPoint, isStart, isEnd);
  155. }
  156. }
  157. }
  158. static int FindId(uint32 startAddr, uint32 endAddr, const InstVec& insts)
  159. {
  160. for (const InstPtr& instruction : insts)
  161. {
  162. if (instruction->_address >= startAddr && instruction->_address <= endAddr)
  163. {
  164. if (instruction->_opcode == FF7::eOpcodes::opCodeCHAR)
  165. {
  166. return instruction->_params[0]->getSigned();
  167. }
  168. }
  169. }
  170. return -1;
  171. }
  172. void FF7::FF7Disassembler::AddFunc(
  173. std::string entity_name, size_t entity_index, size_t script_index, uint32 next_script_entry_point,
  174. const bool is_start, bool is_end, bool to_return_only, std::string func_name
  175. ){
  176. bool is_line = false;
  177. std::vector<float> point_a = {0, 0, 0};
  178. std::vector<float> point_b = {0, 0, 0};
  179. const auto SCRIPT_ENTRY_POINT = mStream->Position();
  180. // Read each block of opcodes up to a return.
  181. const size_t old_num_instructions = _insts.size();
  182. std::unique_ptr<Function> func;
  183. // Initialize the function.
  184. if (mEngine->EntityIsLine(entity_index)) func = StartLineFunction(script_index);
  185. else func = StartFunction(script_index);
  186. // Read.
  187. if (to_return_only){
  188. // Read opcodes to the end or bail at the first return.
  189. is_line = ReadOpCodesToPositionOrReturn(
  190. next_script_entry_point + kSectionPointersSize, point_a, point_b
  191. );
  192. auto stream_pos = mStream->Position();
  193. const size_t endPos = next_script_entry_point + kSectionPointersSize;
  194. // Can't be the end if there is more data:
  195. if (stream_pos != endPos) is_end = false;
  196. }
  197. else{
  198. // Keep going till we have all of the script, i.e if we bail at a
  199. // return then call again till we have everything
  200. while (mStream->Position() != next_script_entry_point + kSectionPointersSize){
  201. is_line = ReadOpCodesToPositionOrReturn(
  202. next_script_entry_point + kSectionPointersSize, point_a, point_b
  203. );
  204. }
  205. }
  206. // Read metadata, mark start and/or end.
  207. std::string meta_data;
  208. if (is_start && is_end) meta_data = "start_end_";
  209. else if (is_start) meta_data = "start_";
  210. else if (is_end) meta_data = "end_";
  211. const size_t new_num_instructions = _insts.size();
  212. func->mNumInstructions = new_num_instructions - old_num_instructions;
  213. func->mEndAddr = _insts.back()->_address;
  214. if (!func_name.empty()) func->_name = func_name;
  215. // TODO: Remove and test. Should be applied in StartLineFunction
  216. // TODO: I dont know which one is OK. Check.
  217. if (mEngine->EntityIsLine(entity_index)){
  218. switch (script_index){
  219. case 1:
  220. case 2:
  221. func->_name = "on_enter_line"; break;
  222. case 3: func->_name = "on_move_to_line"; break;
  223. case 4: func->_name = "on_cross_line"; break;
  224. case 5: func->_name = "on_leave_line"; break;
  225. }
  226. }
  227. int id = FindId(func->mStartAddr, func->mEndAddr, _insts);
  228. // If there is no ID check if there was an ID for this entity in any of
  229. // its other functions and use that instead.
  230. if (id == -1){
  231. for (auto& func : mEngine->_functions){
  232. FunctionMetaData func_meta_data(func.second._metadata);
  233. if (func_meta_data.EntityName() == entity_name && func_meta_data.CharacterId() != -1){
  234. id = func_meta_data.CharacterId();
  235. break;
  236. }
  237. }
  238. }
  239. meta_data += std::to_string(id) + "_" + entity_name;
  240. func->_metadata = meta_data;
  241. mEngine->_functions[SCRIPT_ENTRY_POINT] = *func;
  242. mEngine->AddEntityFunction(entity_name, entity_index, func->_name, script_index);
  243. // If the entity is a line, mark it as so.
  244. if (is_line) mEngine->MarkEntityAsLine(entity_index, true, point_a, point_b);
  245. }
  246. void FF7::FF7Disassembler::DisassembleIndivdualScript(std::string entityName,
  247. size_t entityIndex,
  248. size_t scriptIndex,
  249. size_t scriptEntryPoint,
  250. uint32 nextScriptEntryPoint,
  251. bool isStart,
  252. bool isEnd)
  253. {
  254. scriptEntryPoint += kSectionPointersSize;
  255. mStream->Seek(scriptEntryPoint);
  256. _addressBase = mStream->Position();
  257. _address = _addressBase;
  258. // "Normal" script
  259. if (scriptIndex > 0)
  260. {
  261. if (scriptIndex == 1)
  262. {
  263. AddFunc(entityName, entityIndex, scriptIndex, nextScriptEntryPoint, isStart, isEnd, false, "on_interact");
  264. }
  265. else
  266. {
  267. AddFunc(entityName, entityIndex, scriptIndex, nextScriptEntryPoint, isStart, isEnd, false, "");
  268. }
  269. }
  270. else
  271. {
  272. const size_t endPos = nextScriptEntryPoint + kSectionPointersSize;
  273. // Read the init script, which means stop at the first return
  274. AddFunc(entityName, entityIndex, scriptIndex, nextScriptEntryPoint, isStart, isEnd, true, "on_start");
  275. // Not at the end of this script? Then the remaining data is the "main" script
  276. auto streamPos = mStream->Position();
  277. if (streamPos != endPos)
  278. {
  279. // The "main" script can have more than one return statement
  280. AddFunc(entityName, entityIndex, scriptIndex, nextScriptEntryPoint, false, isEnd, false, "on_update");
  281. streamPos = mStream->Position();
  282. // But should end exactly on the end pos
  283. if (streamPos != endPos)
  284. {
  285. throw InternalDecompilerError();
  286. }
  287. }
  288. }
  289. }
  290. const FF7::TInstructRecord kOpcodes[] =
  291. {
  292. // Flow
  293. { 1, FF7::eOpcodes::RET, "RET", "", FF7::FF7ControlFlowInstruction::Create },
  294. { 1, FF7::eOpcodes::REQ, "REQ", "BU", FF7::FF7ControlFlowInstruction::Create },
  295. { 1, FF7::eOpcodes::REQSW, "REQSW", "BU", FF7::FF7ControlFlowInstruction::Create },
  296. { 1, FF7::eOpcodes::REQEW, "REQEW", "BU", FF7::FF7ControlFlowInstruction::Create },
  297. { 1, FF7::eOpcodes::NOP, "NOP", "", FF7::FF7NoOperationInstruction::Create },
  298. { 1, FF7::eOpcodes::IFUB, "IFUB", "NNBBBL", FF7::FF7NoOperationInstruction::Create }
  299. };
  300. std::map<std::string, const FF7::TInstructRecord*> FF7::FieldInstructions()
  301. {
  302. // Convert the array to a map that we can query on by mnemonic
  303. std::map<std::string, const TInstructRecord*> mnemonicToInstructionRecords;
  304. for (size_t i = 0; i < boost::size(kOpcodes); i++)
  305. {
  306. mnemonicToInstructionRecords[kOpcodes[i].mMnemonic] = &kOpcodes[i];
  307. }
  308. return mnemonicToInstructionRecords;
  309. }
  310. bool FF7::FF7Disassembler::ReadOpCodesToPositionOrReturn(
  311. size_t end_pos, std::vector<float>& point_a, std::vector<float>& point_b
  312. ){
  313. bool is_line = false;
  314. std::vector<unsigned int> exitAddrs;
  315. while (mStream->Position() < end_pos){
  316. uint8 opcode = mStream->ReadU8();
  317. uint32 full_opcode = 0;
  318. std::string opcodePrefix;
  319. switch (opcode){
  320. // Flow
  321. OPCODE(eOpcodes::IFUB, "IFUB", FF7CondJumpInstruction, 0, "NBBBB");
  322. OPCODE(eOpcodes::RET, "RET", FF7ControlFlowInstruction, 0, "");
  323. OPCODE(eOpcodes::REQ, "REQ", FF7ControlFlowInstruction, 0, "BU");
  324. OPCODE(eOpcodes::REQSW, "REQSW", FF7ControlFlowInstruction, 0, "BU");
  325. OPCODE(eOpcodes::REQEW, "REQEW", FF7ControlFlowInstruction, 0, "BU");
  326. OPCODE(eOpcodes::PREQ, "PREQ", FF7ControlFlowInstruction, 0, "BU");
  327. OPCODE(eOpcodes::PRQSW, "PRQSW", FF7ControlFlowInstruction, 0, "BU");
  328. OPCODE(eOpcodes::PRQEW, "PRQEW", FF7ControlFlowInstruction, 0, "BU");
  329. OPCODE(eOpcodes::RETTO, "RETTO", FF7ControlFlowInstruction, 0, "U");
  330. OPCODE(eOpcodes::JMPF, "JMPF", FF7UncondJumpInstruction, 0, "B");
  331. OPCODE(eOpcodes::JMPFL, "JMPFL", FF7UncondJumpInstruction, 0, "w");
  332. OPCODE(eOpcodes::JMPB, "JMPB", FF7UncondJumpInstruction, 0, "B");
  333. OPCODE(eOpcodes::JMPBL, "JMPBL", FF7UncondJumpInstruction, 0, "w");
  334. OPCODE(eOpcodes::IFUBL, "IFUBL", FF7CondJumpInstruction, 0, "NBBBw");
  335. OPCODE(eOpcodes::IFSW, "IFSW", FF7CondJumpInstruction, 0, "NwwBB");
  336. OPCODE(eOpcodes::IFSWL, "IFSWL", FF7CondJumpInstruction, 0, "NwwBw");
  337. OPCODE(eOpcodes::IFUW, "IFUW", FF7CondJumpInstruction, 0, "NwwBB");
  338. OPCODE(eOpcodes::IFUWL, "IFUWL", FF7CondJumpInstruction, 0, "NwwBw");
  339. OPCODE(eOpcodes::WAIT, "WAIT", FF7ControlFlowInstruction, 0, "w");
  340. OPCODE(eOpcodes::IFKEY, "IFKEY", FF7CondJumpInstruction, 0, "wB");
  341. OPCODE(eOpcodes::IFKEYON, "IFKEYON", FF7CondJumpInstruction, 0, "wB");
  342. OPCODE(eOpcodes::IFKEYOFF, "IFKEYOFF", FF7CondJumpInstruction, 0, "wB");
  343. OPCODE(eOpcodes::NOP, "NOP", FF7NoOperationInstruction, 0, "");
  344. OPCODE(eOpcodes::IFPRTYQ, "IFPRTYQ", FF7CondJumpInstruction, 0, "BB");
  345. OPCODE(eOpcodes::IFMEMBQ, "IFMEMBQ", FF7CondJumpInstruction, 0, "BB");
  346. // Module
  347. OPCODE(eOpcodes::DSKCG, "DSKCG", FF7ModuleInstruction, 0, "B");
  348. START_SUBOPCODE(eOpcodes::SPECIAL)
  349. OPCODE(eSpecialOpcodes::ARROW, "ARROW", FF7ModuleInstruction, 0, "B");
  350. OPCODE(eSpecialOpcodes::PNAME, "PNAME", FF7ModuleInstruction, 0, "B");
  351. OPCODE(eSpecialOpcodes::GMSPD, "GMSPD", FF7ModuleInstruction, 0, "B");
  352. OPCODE(eSpecialOpcodes::SMSPD, "SMSPD", FF7ModuleInstruction, 0, "BB");
  353. OPCODE(eSpecialOpcodes::FLMAT, "FLMAT", FF7ModuleInstruction, 0, "");
  354. OPCODE(eSpecialOpcodes::FLITM, "FLITM", FF7ModuleInstruction, 0, "");
  355. OPCODE(eSpecialOpcodes::BTLCK, "BTLCK", FF7ModuleInstruction, 0, "B");
  356. OPCODE(eSpecialOpcodes::MVLCK, "MVLCK", FF7ModuleInstruction, 0, "B");
  357. OPCODE(eSpecialOpcodes::SPCNM, "SPCNM", FF7ModuleInstruction, 0, "BB");
  358. OPCODE(eSpecialOpcodes::RSGLB, "RSGLB", FF7ModuleInstruction, 0, "");
  359. OPCODE(eSpecialOpcodes::CLITM, "CLITM", FF7ModuleInstruction, 0, "");
  360. END_SUBOPCODE
  361. OPCODE(eOpcodes::MINIGAME, "MINIGAME", FF7ModuleInstruction, 0, "wsswBB");
  362. OPCODE(eOpcodes::BTMD2, "BTMD2", FF7ModuleInstruction, 0, "d");
  363. OPCODE(eOpcodes::BTRLD, "BTRLD", FF7ModuleInstruction, 0, "NB");
  364. OPCODE(eOpcodes::BTLTB, "BTLTB", FF7ModuleInstruction, 0, "B");
  365. OPCODE(eOpcodes::MAPJUMP, "MAPJUMP", FF7ModuleInstruction, 0, "wsswB");
  366. OPCODE(eOpcodes::LSTMP, "LSTMP", FF7ModuleInstruction, 0, "NB");
  367. OPCODE(eOpcodes::BATTLE, "BATTLE", FF7ModuleInstruction, 0, "Nw");
  368. OPCODE(eOpcodes::BTLON, "BTLON", FF7ModuleInstruction, 0, "B");
  369. OPCODE(eOpcodes::BTLMD, "BTLMD", FF7ModuleInstruction, 0, "w");
  370. OPCODE(eOpcodes::MPJPO, "MPJPO", FF7ModuleInstruction, 0, "B");
  371. OPCODE(eOpcodes::PMJMP, "PMJMP", FF7ModuleInstruction, 0, "w");
  372. OPCODE(eOpcodes::PMJMP2, "PMJMP2", FF7ModuleInstruction, 0, "");
  373. OPCODE(eOpcodes::GAMEOVER, "GAMEOVER", FF7ModuleInstruction, 0, "");
  374. // Math
  375. OPCODE(eOpcodes::PLUS_, "PLUS!", FF7MathInstruction, 0, "NBB");
  376. OPCODE(eOpcodes::PLUS2_, "PLUS2!", FF7MathInstruction, 0, "NBw");
  377. OPCODE(eOpcodes::MINUS_, "MINUS!", FF7MathInstruction, 0, "NBB");
  378. OPCODE(eOpcodes::MINUS2_, "MINUS2!", FF7MathInstruction, 0, "NBw");
  379. OPCODE(eOpcodes::INC_, "INC!", FF7MathInstruction, 0, "BB");
  380. OPCODE(eOpcodes::INC2_, "INC2!", FF7MathInstruction, 0, "BB");
  381. OPCODE(eOpcodes::DEC_, "DEC!", FF7MathInstruction, 0, "BB");
  382. OPCODE(eOpcodes::DEC2_, "DEC2!", FF7MathInstruction, 0, "BB");
  383. OPCODE(eOpcodes::RDMSD, "RDMSD", FF7MathInstruction, 0, "NB");
  384. OPCODE(eOpcodes::SETBYTE, "SETBYTE", FF7MathInstruction, 0, "NBB");
  385. OPCODE(eOpcodes::SETWORD, "SETWORD", FF7MathInstruction, 0, "NBw");
  386. OPCODE(eOpcodes::BITON, "BITON", FF7MathInstruction, 0, "NBB");
  387. OPCODE(eOpcodes::BITOFF, "BITOFF", FF7MathInstruction, 0, "NBB");
  388. OPCODE(eOpcodes::BITXOR, "BITXOR", FF7MathInstruction, 0, "NBB");
  389. OPCODE(eOpcodes::PLUS, "PLUS", FF7MathInstruction, 0, "NBB");
  390. OPCODE(eOpcodes::PLUS2, "PLUS2", FF7MathInstruction, 0, "NBw");
  391. OPCODE(eOpcodes::MINUS, "MINUS", FF7MathInstruction, 0, "NBB");
  392. OPCODE(eOpcodes::MINUS2, "MINUS2", FF7MathInstruction, 0, "NBw");
  393. OPCODE(eOpcodes::MUL, "MUL", FF7MathInstruction, 0, "NBB");
  394. OPCODE(eOpcodes::MUL2, "MUL2", FF7MathInstruction, 0, "NBw");
  395. OPCODE(eOpcodes::DIV, "DIV", FF7MathInstruction, 0, "NBB");
  396. OPCODE(eOpcodes::DIV2, "DIV2", FF7MathInstruction, 0, "NBw");
  397. OPCODE(eOpcodes::MOD, "MOD", FF7MathInstruction, 0, "NBB");
  398. OPCODE(eOpcodes::MOD2, "MOD2", FF7MathInstruction, 0, "NBw");
  399. OPCODE(eOpcodes::AND, "AND", FF7MathInstruction, 0, "NBB");
  400. OPCODE(eOpcodes::AND2, "AND2", FF7MathInstruction, 0, "NBw");
  401. OPCODE(eOpcodes::OR, "OR", FF7MathInstruction, 0, "NBB");
  402. OPCODE(eOpcodes::OR2, "OR2", FF7MathInstruction, 0, "NBw");
  403. OPCODE(eOpcodes::XOR, "XOR", FF7MathInstruction, 0, "NBB");
  404. OPCODE(eOpcodes::XOR2, "XOR2", FF7MathInstruction, 0, "NBw");
  405. OPCODE(eOpcodes::INC, "INC", FF7MathInstruction, 0, "BB");
  406. OPCODE(eOpcodes::INC2, "INC2", FF7MathInstruction, 0, "BB");
  407. OPCODE(eOpcodes::DEC, "DEC", FF7MathInstruction, 0, "BB");
  408. OPCODE(eOpcodes::DEC2, "DEC2", FF7MathInstruction, 0, "BB");
  409. OPCODE(eOpcodes::RANDOM, "RANDOM", FF7MathInstruction, 0, "BB");
  410. OPCODE(eOpcodes::LBYTE, "LBYTE", FF7MathInstruction, 0, "NBB");
  411. OPCODE(eOpcodes::HBYTE, "HBYTE", FF7MathInstruction, 0, "NBw");
  412. OPCODE(eOpcodes::TWOBYTE, "2BYTE", FF7MathInstruction, 0, "NNBBB");
  413. OPCODE(eOpcodes::SIN, "SIN", FF7MathInstruction, 0, "NNwwwB");
  414. OPCODE(eOpcodes::COS, "COS", FF7MathInstruction, 0, "NNwwwB");
  415. // Window
  416. OPCODE(eOpcodes::TUTOR, "TUTOR", FF7WindowInstruction, 0, "B");
  417. OPCODE(eOpcodes::WCLS, "WCLS", FF7WindowInstruction, 0, "B");
  418. OPCODE(eOpcodes::WSIZW, "WSIZW", FF7WindowInstruction, 0, "Bwwww");
  419. OPCODE(eOpcodes::WSPCL, "WSPCL", FF7WindowInstruction, 0, "BBBB");
  420. OPCODE(eOpcodes::WNUMB, "WNUMB", FF7WindowInstruction, 0, "NBwwB"); // NBdB when N == 0
  421. OPCODE(eOpcodes::STTIM, "STTIM", FF7WindowInstruction, 0, "NNBBB");
  422. OPCODE(eOpcodes::MESSAGE, "MESSAGE", FF7WindowInstruction, 0, "BB");
  423. OPCODE(eOpcodes::MPARA, "MPARA", FF7WindowInstruction, 0, "NBBB");
  424. OPCODE(eOpcodes::MPRA2, "MPRA2", FF7WindowInstruction, 0, "NBBw");
  425. OPCODE(eOpcodes::MPNAM, "MPNAM", FF7WindowInstruction, 0, "B");
  426. OPCODE(eOpcodes::ASK, "ASK", FF7WindowInstruction, 0, "NBBBBB");
  427. OPCODE(eOpcodes::MENU, "MENU", FF7WindowInstruction, 0, "NBB");
  428. OPCODE(eOpcodes::MENU2, "MENU2", FF7WindowInstruction, 0, "B");
  429. OPCODE(eOpcodes::WINDOW, "WINDOW", FF7WindowInstruction, 0, "Bwwww");
  430. OPCODE(eOpcodes::WMOVE, "WMOVE", FF7WindowInstruction, 0, "Bss");
  431. OPCODE(eOpcodes::WMODE, "WMODE", FF7WindowInstruction, 0, "BBB");
  432. OPCODE(eOpcodes::WREST, "WREST", FF7WindowInstruction, 0, "B");
  433. OPCODE(eOpcodes::WCLSE, "WCLSE", FF7WindowInstruction, 0, "B");
  434. OPCODE(eOpcodes::WROW, "WROW", FF7WindowInstruction, 0, "BB");
  435. OPCODE(eOpcodes::GWCOL, "GWCOL", FF7WindowInstruction, 0, "NNBBBB");
  436. OPCODE(eOpcodes::SWCOL, "SWCOL", FF7WindowInstruction, 0, "NNBBBB");
  437. // Party
  438. OPCODE(eOpcodes::SPTYE, "SPTYE", FF7PartyInstruction, 0, "NNBBB");
  439. OPCODE(eOpcodes::GTPYE, "GTPYE", FF7PartyInstruction, 0, "NNBBB");
  440. OPCODE(eOpcodes::GOLDU, "GOLDU", FF7PartyInstruction, 0, "Nww"); // Nd when N == 0
  441. OPCODE(eOpcodes::GOLDD, "GOLDD", FF7PartyInstruction, 0, "Nww"); // Nd when N == 0
  442. OPCODE(eOpcodes::CHGLD, "CHGLD", FF7PartyInstruction, 0, "NBB");
  443. OPCODE(eOpcodes::HMPMAX1, "HMPMAX1", FF7PartyInstruction, 0, "");
  444. OPCODE(eOpcodes::HMPMAX2, "HMPMAX2", FF7PartyInstruction, 0, "");
  445. OPCODE(eOpcodes::MHMMX, "MHMMX", FF7PartyInstruction, 0, "");
  446. OPCODE(eOpcodes::HMPMAX3, "HMPMAX3", FF7PartyInstruction, 0, "");
  447. OPCODE(eOpcodes::MPU, "MPU", FF7PartyInstruction, 0, "NBw");
  448. OPCODE(eOpcodes::MPD, "MPD", FF7PartyInstruction, 0, "NBw");
  449. OPCODE(eOpcodes::HPU, "HPU", FF7PartyInstruction, 0, "NBw");
  450. OPCODE(eOpcodes::HPD, "HPD", FF7PartyInstruction, 0, "NBw");
  451. OPCODE(eOpcodes::STITM, "STITM", FF7PartyInstruction, 0, "NwB");
  452. OPCODE(eOpcodes::DLITM, "DLITM", FF7PartyInstruction, 0, "NwB");
  453. OPCODE(eOpcodes::CKITM, "CKITM", FF7PartyInstruction, 0, "NwB");
  454. OPCODE(eOpcodes::SMTRA, "SMTRA", FF7PartyInstruction, 0, "NNBBBB");
  455. OPCODE(eOpcodes::DMTRA, "DMTRA", FF7PartyInstruction, 0, "NNBBBBB");
  456. OPCODE(eOpcodes::CMTRA, "CMTRA", FF7PartyInstruction, 0, "NNNBBBBBB");
  457. OPCODE(eOpcodes::GETPC, "GETPC", FF7PartyInstruction, 0, "NBB");
  458. OPCODE(eOpcodes::PRTYP, "PRTYP", FF7PartyInstruction, 0, "B");
  459. OPCODE(eOpcodes::PRTYM, "PRTYM", FF7PartyInstruction, 0, "B");
  460. OPCODE(eOpcodes::PRTYE, "PRTYE", FF7PartyInstruction, 0, "BBB");
  461. OPCODE(eOpcodes::MMBUD, "MMBUD", FF7PartyInstruction, 0, "BB");
  462. OPCODE(eOpcodes::MMBLK, "MMBLK", FF7PartyInstruction, 0, "B");
  463. OPCODE(eOpcodes::MMBUK, "MMBUK", FF7PartyInstruction, 0, "B");
  464. // Model
  465. OPCODE(eOpcodes::JOIN, "JOIN", FF7ModelInstruction, 0, "B");
  466. OPCODE(eOpcodes::SPLIT, "SPLIT", FF7ModelInstruction, 0, "NNNssBssBB");
  467. OPCODE(eOpcodes::BLINK, "BLINK", FF7ModelInstruction, 0, "B");
  468. OPCODE_BASE(eOpcodes::KAWAI)
  469. {
  470. int length = this->mStream->ReadU8();
  471. assert(length >= 3);
  472. std::ostringstream paramStream;
  473. for (int i = 3; i < length; ++i)
  474. {
  475. paramStream << "B";
  476. }
  477. auto parameters = paramStream.str();
  478. opcode = this->mStream->ReadU8();
  479. switch (opcode)
  480. {
  481. OPCODE(
  482. eKawaiOpcodes::EYETX, "EYETX", FF7ModelInstruction, 0, parameters.c_str()
  483. ); // was BBBB
  484. OPCODE(
  485. eKawaiOpcodes::TRNSP, "TRNSP", FF7ModelInstruction, 0, parameters.c_str()
  486. ); // was B
  487. OPCODE(
  488. eKawaiOpcodes::AMBNT, "AMBNT", FF7ModelInstruction, 0, parameters.c_str()
  489. ); // was BBBBBBB
  490. OPCODE(
  491. eKawaiOpcodes::Unknown03, "Unknown03",
  492. FF7ModelInstruction, 0, parameters.c_str()
  493. );
  494. OPCODE(
  495. eKawaiOpcodes::Unknown04, "Unknown04",
  496. FF7ModelInstruction, 0, parameters.c_str()
  497. );
  498. OPCODE(
  499. eKawaiOpcodes::Unknown05, "Unknown05",
  500. FF7ModelInstruction, 0, parameters.c_str()
  501. );
  502. OPCODE(
  503. eKawaiOpcodes::LIGHT, "LIGHT",FF7ModelInstruction, 0, parameters.c_str()
  504. );
  505. OPCODE(
  506. eKawaiOpcodes::Unknown07, "Unknown07",
  507. FF7ModelInstruction, 0, parameters.c_str()
  508. );
  509. OPCODE(
  510. eKawaiOpcodes::Unknown08, "Unknown08",
  511. FF7ModelInstruction, 0, parameters.c_str()
  512. );
  513. OPCODE(
  514. eKawaiOpcodes::Unknown09, "Unknown09",
  515. FF7ModelInstruction, 0, parameters.c_str()
  516. );
  517. OPCODE(
  518. eKawaiOpcodes::SBOBJ, "SBOBJ",
  519. FF7ModelInstruction, 0, parameters.c_str()
  520. );
  521. OPCODE(
  522. eKawaiOpcodes::Unknown0B, "Unknown0B",
  523. FF7ModelInstruction, 0, parameters.c_str()
  524. );
  525. OPCODE(
  526. eKawaiOpcodes::Unknown0C, "Unknown0C",
  527. FF7ModelInstruction, 0, parameters.c_str()
  528. );
  529. OPCODE(
  530. eKawaiOpcodes::SHINE, "SHINE", FF7ModelInstruction, 0, parameters.c_str()
  531. );
  532. OPCODE(
  533. eKawaiOpcodes::RESET, "RESET", FF7ModelInstruction, 0, parameters.c_str()
  534. );
  535. default:
  536. throw UnknownSubOpcodeException(this->_address, opcode);
  537. }
  538. INC_ADDR;
  539. INC_ADDR;
  540. }
  541. OPCODE_END
  542. OPCODE(eOpcodes::KAWIW, "KAWIW", FF7ModelInstruction, 0, "");
  543. OPCODE(eOpcodes::PMOVA, "PMOVA", FF7ModelInstruction, 0, "B");
  544. OPCODE(eOpcodes::PDIRA, "PDIRA", FF7ModelInstruction, 0, "B");
  545. OPCODE(eOpcodes::PTURA, "PTURA", FF7ModelInstruction, 0, "BBB");
  546. OPCODE(eOpcodes::PGTDR, "PGTDR", FF7ModelInstruction, 0, "NBB");
  547. OPCODE(eOpcodes::PXYZI, "PXYZI", FF7ModelInstruction, 0, "NNBBBBB");
  548. OPCODE(eOpcodes::TLKON, "TLKON", FF7ModelInstruction, 0, "B");
  549. OPCODE(eOpcodes::PC, "PC", FF7ModelInstruction, 0, "B");
  550. OPCODE(eOpcodes::opCodeCHAR, "CHAR", FF7ModelInstruction, 0, "B");
  551. OPCODE(eOpcodes::DFANM, "DFANM", FF7ModelInstruction, 0, "BB");
  552. OPCODE(eOpcodes::ANIME1, "ANIME1", FF7ModelInstruction, 0, "BB");
  553. OPCODE(eOpcodes::VISI, "VISI", FF7ModelInstruction, 0, "B");
  554. OPCODE(eOpcodes::XYZI, "XYZI", FF7ModelInstruction, 0, "NNsssw");
  555. OPCODE(eOpcodes::XYI, "XYI", FF7ModelInstruction, 0, "NNssw");
  556. OPCODE(eOpcodes::XYZ, "XYZ", FF7ModelInstruction, 0, "NNsss");
  557. OPCODE(eOpcodes::MOVE, "MOVE", FF7ModelInstruction, 0, "Nss");
  558. OPCODE(eOpcodes::CMOVE, "CMOVE", FF7ModelInstruction, 0, "Nss");
  559. OPCODE(eOpcodes::MOVA, "MOVA", FF7ModelInstruction, 0, "B");
  560. OPCODE(eOpcodes::TURA, "TURA", FF7ModelInstruction, 0, "BBB");
  561. OPCODE(eOpcodes::ANIMW, "ANIMW", FF7ModelInstruction, 0, "");
  562. OPCODE(eOpcodes::FMOVE, "FMOVE", FF7ModelInstruction, 0, "Nss");
  563. OPCODE(eOpcodes::ANIME2, "ANIME2", FF7ModelInstruction, 0, "BB");
  564. OPCODE(eOpcodes::ANIM_1, "ANIM!1", FF7ModelInstruction, 0, "BB");
  565. OPCODE(eOpcodes::CANIM1, "CANIM1", FF7ModelInstruction, 0, "BBBB");
  566. OPCODE(eOpcodes::CANM_1, "CANM!1", FF7ModelInstruction, 0, "BBBB");
  567. OPCODE(eOpcodes::MSPED, "MSPED", FF7ModelInstruction, 0, "Nw");
  568. OPCODE(eOpcodes::DIR, "DIR", FF7ModelInstruction, 0, "BB");
  569. OPCODE(eOpcodes::TURNGEN, "TURNGEN", FF7ModelInstruction, 0, "NBBBB");
  570. OPCODE(eOpcodes::TURN, "TURN", FF7ModelInstruction, 0, "NBBBB");
  571. OPCODE(eOpcodes::DIRA, "DIRA", FF7ModelInstruction, 0, "B");
  572. OPCODE(eOpcodes::GETDIR, "GETDIR", FF7ModelInstruction, 0, "NBB");
  573. OPCODE(eOpcodes::GETAXY, "GETAXY", FF7ModelInstruction, 0, "NBBB");
  574. OPCODE(eOpcodes::GETAI, "GETAI", FF7ModelInstruction, 0, "NBB");
  575. OPCODE(eOpcodes::ANIM_2, "ANIM!2", FF7ModelInstruction, 0, "BB");
  576. OPCODE(eOpcodes::CANIM2, "CANIM2", FF7ModelInstruction, 0, "BBBB");
  577. OPCODE(eOpcodes::CANM_2, "CANM!2", FF7ModelInstruction, 0, "BBBB");
  578. OPCODE(eOpcodes::ASPED, "ASPED", FF7ModelInstruction, 0, "Nw");
  579. OPCODE(eOpcodes::CC, "CC", FF7ModelInstruction, 0, "B");
  580. OPCODE(eOpcodes::JUMP, "JUMP", FF7ModelInstruction, 0, "NNssww");
  581. OPCODE(eOpcodes::AXYZI, "AXYZI", FF7ModelInstruction, 0, "NNBBBBB");
  582. OPCODE(eOpcodes::LADER, "LADER", FF7ModelInstruction, 0, "NNssswBBBB");
  583. OPCODE(eOpcodes::OFST, "OFST", FF7ModelInstruction, 0, "NNBsssw");
  584. OPCODE(eOpcodes::OFSTW, "OFSTW", FF7ModelInstruction, 0, "");
  585. OPCODE(eOpcodes::TALKR, "TALKR", FF7ModelInstruction, 0, "NB");
  586. OPCODE(eOpcodes::SLIDR, "SLIDR", FF7ModelInstruction, 0, "NB");
  587. OPCODE(eOpcodes::SOLID, "SOLID", FF7ModelInstruction, 0, "B");
  588. OPCODE(eOpcodes::TLKR2, "TLKR2", FF7ModelInstruction, 0, "Nw");
  589. OPCODE(eOpcodes::SLDR2, "SLDR2", FF7ModelInstruction, 0, "Nw");
  590. OPCODE(eOpcodes::CCANM, "CCANM", FF7ModelInstruction, 0, "BBB");
  591. OPCODE(eOpcodes::FCFIX, "FCFIX", FF7ModelInstruction, 0, "B");
  592. OPCODE(eOpcodes::ANIMB, "ANIMB", FF7ModelInstruction, 0, "");
  593. OPCODE(eOpcodes::TURNW, "TURNW", FF7ModelInstruction, 0, "");
  594. // Walkmesh
  595. OPCODE(eOpcodes::SLIP, "SLIP", FF7WalkmeshInstruction, 0, "B");
  596. OPCODE(eOpcodes::UC, "UC", FF7WalkmeshInstruction, 0, "B");
  597. OPCODE(eOpcodes::IDLCK, "IDLCK", FF7WalkmeshInstruction, 0, "wB");
  598. OPCODE(eOpcodes::LINON, "LINON", FF7WalkmeshInstruction, 0, "B");
  599. OPCODE(eOpcodes::SLINE, "SLINE", FF7WalkmeshInstruction, 0, "NNNssssss");
  600. //OPCODE(eOpcodes::LINE, "LINE", FF7WalkmeshInstruction, 0, "ssssss");
  601. // LINE is done like this to save opcode params to the function out params.
  602. case eOpcodes::LINE:
  603. is_line = true;
  604. full_opcode = (full_opcode << 8) + eOpcodes::LINE;
  605. this->_insts.push_back(new FF7WalkmeshInstruction());
  606. (this->_insts.back())->_opcode = full_opcode;
  607. (this->_insts.back())->_address = this->_address;
  608. (this->_insts.back())->_stackChange = 0;
  609. (this->_insts.back())->_name = opcodePrefix + std::string("LINE");
  610. (this->_insts.back())->_codeGenData = "";
  611. this->readParams((this->_insts.back()), "ssssss");
  612. point_a[0] = this->_insts.back()->_params[0]->getSigned();
  613. point_a[1] = this->_insts.back()->_params[1]->getSigned();
  614. point_a[2] = this->_insts.back()->_params[2]->getSigned();
  615. point_b[0] = this->_insts.back()->_params[3]->getSigned();
  616. point_b[1] = this->_insts.back()->_params[4]->getSigned();
  617. point_b[2] = this->_insts.back()->_params[5]->getSigned();
  618. break;
  619. // Backgnd
  620. OPCODE(eOpcodes::BGPDH, "BGPDH", FF7BackgroundInstruction, 0, "NBs");
  621. OPCODE(eOpcodes::BGSCR, "BGSCR", FF7BackgroundInstruction, 0, "NBss");
  622. OPCODE(eOpcodes::MPPAL, "MPPAL", FF7BackgroundInstruction, 0, "NNNBBBBBBB");
  623. OPCODE(eOpcodes::BGON, "BGON", FF7BackgroundInstruction, 0, "NBB");
  624. OPCODE(eOpcodes::BGOFF, "BGOFF", FF7BackgroundInstruction, 0, "NBB");
  625. OPCODE(eOpcodes::BGROL, "BGROL", FF7BackgroundInstruction, 0, "NB");
  626. OPCODE(eOpcodes::BGROL2, "BGROL2", FF7BackgroundInstruction, 0, "NB");
  627. OPCODE(eOpcodes::BGCLR, "BGCLR", FF7BackgroundInstruction, 0, "NB");
  628. OPCODE(eOpcodes::STPAL, "STPAL", FF7BackgroundInstruction, 0, "NBBB");
  629. OPCODE(eOpcodes::LDPAL, "LDPAL", FF7BackgroundInstruction, 0, "NBBB");
  630. OPCODE(eOpcodes::CPPAL, "CPPAL", FF7BackgroundInstruction, 0, "NBBB");
  631. OPCODE(eOpcodes::RTPAL, "RTPAL", FF7BackgroundInstruction, 0, "NNBBBB");
  632. OPCODE(eOpcodes::ADPAL, "ADPAL", FF7BackgroundInstruction, 0, "NNNBBBBBB");
  633. OPCODE(eOpcodes::MPPAL2, "MPPAL2", FF7BackgroundInstruction, 0, "NNNBBBBBB");
  634. OPCODE(eOpcodes::STPLS, "STPLS", FF7BackgroundInstruction, 0, "BBBB");
  635. OPCODE(eOpcodes::LDPLS, "LDPLS", FF7BackgroundInstruction, 0, "BBBB");
  636. OPCODE(eOpcodes::CPPAL2, "CPPAL2", FF7BackgroundInstruction, 0, "BBBBBBB");
  637. OPCODE(eOpcodes::RTPAL2, "RTPAL2", FF7BackgroundInstruction, 0, "BBBBBBB");
  638. OPCODE(eOpcodes::ADPAL2, "ADPAL2", FF7BackgroundInstruction, 0, "BBBBBBBBBB");
  639. // Camera
  640. OPCODE(eOpcodes::NFADE, "NFADE", FF7CameraInstruction, 0, "NNBBBBBB");
  641. OPCODE(eOpcodes::SHAKE, "SHAKE", FF7CameraInstruction, 0, "BBBBBBB");
  642. OPCODE(eOpcodes::SCRLO, "SCRLO", FF7CameraInstruction, 0, "B");
  643. OPCODE(eOpcodes::SCRLC, "SCRLC", FF7CameraInstruction, 0, "BBBB");
  644. OPCODE(eOpcodes::SCRLA, "SCRLA", FF7CameraInstruction, 0, "NwBB");
  645. OPCODE(eOpcodes::SCR2D, "SCR2D", FF7CameraInstruction, 0, "Nss");
  646. OPCODE(eOpcodes::SCRCC, "SCRCC", FF7CameraInstruction, 0, "");
  647. OPCODE(eOpcodes::SCR2DC, "SCR2DC", FF7CameraInstruction, 0, "NNssw");
  648. OPCODE(eOpcodes::SCRLW, "SCRLW", FF7CameraInstruction, 0, "");
  649. OPCODE(eOpcodes::SCR2DL, "SCR2DL", FF7CameraInstruction, 0, "NNssw");
  650. OPCODE(eOpcodes::VWOFT, "VWOFT", FF7CameraInstruction, 0, "NssB");
  651. OPCODE(eOpcodes::FADE, "FADE", FF7CameraInstruction, 0, "NNBBBBBB");
  652. OPCODE(eOpcodes::FADEW, "FADEW", FF7CameraInstruction, 0, "");
  653. OPCODE(eOpcodes::SCRLP, "SCRLP", FF7CameraInstruction, 0, "NwBB");
  654. OPCODE(eOpcodes::MVCAM, "MVCAM", FF7CameraInstruction, 0, "B");
  655. // AV
  656. OPCODE(eOpcodes::BGMOVIE, "BGMOVIE", FF7AudioVideoInstruction, 0, "B");
  657. OPCODE(eOpcodes::AKAO2, "AKAO2", FF7AudioVideoInstruction, 0, "NNNBwwwww");
  658. OPCODE(eOpcodes::MUSIC, "MUSIC", FF7AudioVideoInstruction, 0, "B");
  659. OPCODE(eOpcodes::SOUND, "SOUND", FF7AudioVideoInstruction, 0, "NwB");
  660. OPCODE(eOpcodes::AKAO, "AKAO", FF7AudioVideoInstruction, 0, "NNNBBwwww");
  661. OPCODE(eOpcodes::MUSVT, "MUSVT", FF7AudioVideoInstruction, 0, "B");
  662. OPCODE(eOpcodes::MUSVM, "MUSVM", FF7AudioVideoInstruction, 0, "B");
  663. OPCODE(eOpcodes::MULCK, "MULCK", FF7AudioVideoInstruction, 0, "B");
  664. OPCODE(eOpcodes::BMUSC, "BMUSC", FF7AudioVideoInstruction, 0, "B");
  665. OPCODE(eOpcodes::CHMPH, "CHMPH", FF7AudioVideoInstruction, 0, "BBB");
  666. OPCODE(eOpcodes::PMVIE, "PMVIE", FF7AudioVideoInstruction, 0, "B");
  667. OPCODE(eOpcodes::MOVIE, "MOVIE", FF7AudioVideoInstruction, 0, "");
  668. OPCODE(eOpcodes::MVIEF, "MVIEF", FF7AudioVideoInstruction, 0, "NB");
  669. OPCODE(eOpcodes::FMUSC, "FMUSC", FF7AudioVideoInstruction, 0, "B");
  670. OPCODE(eOpcodes::CMUSC, "CMUSC", FF7AudioVideoInstruction, 0, "BBBBBBB");
  671. OPCODE(eOpcodes::CHMST, "CHMST", FF7AudioVideoInstruction, 0, "NB");
  672. // Uncat
  673. OPCODE(eOpcodes::MPDSP, "MPDSP", FF7UncategorizedInstruction, 0, "B");
  674. OPCODE(eOpcodes::SETX, "SETX", FF7UncategorizedInstruction, 0, "BBBBBB");
  675. OPCODE(eOpcodes::GETX, "GETX", FF7UncategorizedInstruction, 0, "BBBBBB");
  676. OPCODE(eOpcodes::SEARCHX, "SEARCHX", FF7UncategorizedInstruction, 0, "BBBBBBBBBB");
  677. default:
  678. throw UnknownOpcodeException(this->_address, opcode);
  679. }
  680. INC_ADDR;
  681. // Is it within an "if" statement tracking?
  682. InstPtr i = this->_insts.back();
  683. if (i->isCondJump()) exitAddrs.push_back(i->getDestAddress());
  684. if (!exitAddrs.empty())
  685. if (i->_address == exitAddrs.back()) exitAddrs.pop_back();
  686. // Only bail if its the first RET that isn't within an "if" block.
  687. if (full_opcode == eOpcodes::RET && exitAddrs.empty()) return is_line;
  688. }
  689. return is_line;
  690. }