ff7_field_engine.cpp 88 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969
  1. #include "decompiler/field/ff7_field_engine.h"
  2. #include <iostream>
  3. #include <sstream>
  4. #include <boost/format.hpp>
  5. #include "../../../include/decompiler/field/FieldCodeGenerator.h"
  6. #include "../../../include/decompiler/field/FieldDisassembler.h"
  7. #define GET(vertex) (boost::get(boost::vertex_name, g, vertex))
  8. /*
  9. OpCodes which need implementing (already done in DAT dumper)
  10. "BLINK"
  11. "XYI"
  12. "CMOVE"
  13. "MOVA"
  14. "TURA"
  15. "ANIMW"
  16. "FMOVE"
  17. "ANIME2"
  18. "ANIM_1"
  19. "CANIM1" ?
  20. "CANM_1"
  21. "TURN"
  22. "DIRA"
  23. "GETDIR"
  24. "GETAXY"
  25. "JUMP"
  26. "AXYZI"
  27. "LADER"
  28. "OFST"
  29. "TALKR"
  30. "ANIMB"
  31. "TURNW"
  32. */
  33. std::unique_ptr<Disassembler> FF7FieldEngine::getDisassembler(
  34. InstVec &insts, const std::vector<unsigned char>& raw_script_data
  35. ){
  36. auto ret = std::make_unique<FieldDisassembler>(mFormatter, this, insts, raw_script_data);
  37. scale_factor_ = ret->GetScaleFactor();
  38. return std::move(ret);
  39. }
  40. std::unique_ptr<Disassembler> FF7FieldEngine::getDisassembler(InstVec &insts){
  41. auto ret = std::make_unique<FieldDisassembler>(mFormatter, this, insts);
  42. scale_factor_ = ret->GetScaleFactor();
  43. return std::move(ret);
  44. }
  45. std::unique_ptr<CodeGenerator> FF7FieldEngine::getCodeGenerator(
  46. const InstVec& insts, std::ostream &output
  47. ){
  48. // The broken version:
  49. //return std::make_unique<FF7CodeGenerator>(this, insts, output);
  50. // The not-as-nice-but-at-least-it-works version:
  51. return std::make_unique<FieldCodeGenerator>(this, insts, output, mFormatter);
  52. }
  53. void FF7FieldEngine::postCFG(InstVec& insts, Graph g){
  54. /*
  55. // In FF7 some scripts ends with an infinite loop to "keep it alive"
  56. // in VGears this isn't required so we can remove them
  57. RemoveTrailingInfiniteLoops(insts, g);
  58. // This could generate bad code, but it always seems to follow that pattern that if the last
  59. // instruction is an uncond jump back into the script then it simply nests all of those blocks
  60. // in an infinite loop
  61. MarkInfiniteLoopGroups(insts, g);
  62. // Scripts end with a "return" this isn't required so strip them out
  63. RemoveExtraneousReturnStatements(insts, g);
  64. */
  65. }
  66. std::map<std::string, int> FF7FieldEngine::GetEntities() const{
  67. std::map<std::string, int> r;
  68. for (auto& f : _functions){
  69. const Function& func = f.second;
  70. FunctionMetaData meta(func._metadata);
  71. auto it = r.find(meta.GetEntityName());
  72. if (it != std::end(r)){
  73. // Try to find a function in this entity has that has a char id
  74. // don't overwrite a valid char id with a "blank" one
  75. if (it->second == -1) it->second = meta.GetCharacterId();
  76. }
  77. // TODO: Don't add lines here:
  78. else r[meta.GetEntityName()] = meta.GetCharacterId();
  79. }
  80. return r;
  81. }
  82. std::vector<SUDM::Field::FieldEntity> FF7FieldEngine::GetEntityList() const{
  83. std::vector<SUDM::Field::FieldEntity> entities;
  84. for (auto entity: mEntityIndexMap){
  85. if (entity.second.IsLine() == false){
  86. SUDM::Field::FieldEntity ent;
  87. ent.name = entity.second.Name();
  88. ent.index = entity.second.GetIndex();
  89. // Get character ID.
  90. ent.char_id = -1;
  91. std::map<std::string, int> r;
  92. //if (ent.name == "Cloud") std::cout << "CHAR_ID FOR CLOUD" << std::endl;
  93. for (auto& f : _functions){
  94. const Function& func = f.second;
  95. FunctionMetaData meta(func._metadata);
  96. if (meta.GetEntityName() == ent.name){
  97. ent.char_id = meta.GetCharacterId();
  98. break;
  99. }
  100. }
  101. entities.push_back(ent);
  102. }
  103. }
  104. return entities;
  105. }
  106. std::vector<SUDM::Field::Line> FF7FieldEngine::GetLineList() const{
  107. std::vector<SUDM::Field::Line> lines;
  108. for (auto entity: mEntityIndexMap){
  109. if (entity.second.IsLine() == true){
  110. SUDM::Field::Line line;
  111. line.name = entity.second.Name();
  112. line.point_a = entity.second.GetLinePointA();
  113. line.point_b = entity.second.GetLinePointB();
  114. lines.push_back(line);
  115. }
  116. }
  117. return lines;
  118. }
  119. void FF7FieldEngine::AddEntityFunction(
  120. const std::string& entity_name, size_t entity_index,
  121. const std::string& func_name, size_t func_index
  122. ){
  123. auto it = mEntityIndexMap.find(entity_index);
  124. if (it != std::end(mEntityIndexMap)) (*it).second.AddFunction(func_name, func_index);
  125. else{
  126. Entity e(entity_name, entity_index);
  127. e.AddFunction(func_name, func_index);
  128. mEntityIndexMap.insert(std::make_pair(entity_index, e));
  129. }
  130. }
  131. void FF7FieldEngine::MarkEntityAsLine(
  132. size_t entity_index, bool line, std::vector<float> point_a, std::vector<float> point_b
  133. ){
  134. auto it = mEntityIndexMap.find(entity_index);
  135. if (it != std::end(mEntityIndexMap)){
  136. (*it).second.MarkAsLine(line, point_a, point_b);
  137. }
  138. }
  139. bool FF7FieldEngine::EntityIsLine(size_t entity_index){
  140. auto it = mEntityIndexMap.find(entity_index);
  141. if (it != std::end(mEntityIndexMap)) return (*it).second.IsLine();
  142. return false;
  143. }
  144. void FF7FieldEngine::RemoveExtraneousReturnStatements(InstVec& insts, Graph g){
  145. for (auto& f : _functions){
  146. Function& func = f.second;
  147. for (auto it = insts.begin(); it != insts.end(); it ++){
  148. // Is it the last instruction in the function, and is it a return statement?
  149. if ((*it)->_address == func.mEndAddr){
  150. if ((*it)->_opcode == OPCODE::RET){
  151. // Set new end address to be before the NOP.
  152. func.mEndAddr = (*(it - 1))->_address;
  153. func.mNumInstructions --;
  154. Instruction* nop = new FF7NoOperationInstruction();
  155. nop->_opcode = OPCODE::NOP;
  156. nop->_address = (*it)->_address;
  157. (*it).reset(nop);
  158. break;
  159. }
  160. }
  161. }
  162. }
  163. }
  164. void FF7FieldEngine::RemoveTrailingInfiniteLoops(InstVec& insts, Graph g){
  165. for (auto& f : _functions){
  166. Function& func = f.second;
  167. for (auto it = insts.begin(); it != insts.end(); it ++){
  168. // Is it the last instruction in the function, a jump, and a jumping to itself?
  169. if ((*it)->_address == func.mEndAddr){
  170. if ((*it)->isJump() && (*it)->getDestAddress() == (*it)->_address){
  171. // Set new end address to be before the NOP
  172. func.mEndAddr = (*(it - 1))->_address;
  173. func.mNumInstructions--;
  174. Instruction* nop = new FF7NoOperationInstruction();
  175. nop->_opcode = OPCODE::NOP;
  176. nop->_address = (*it)->_address;
  177. (*it).reset(nop);
  178. break;
  179. }
  180. }
  181. }
  182. }
  183. }
  184. void FF7FieldEngine::MarkInfiniteLoopGroups(InstVec& insts, Graph g){
  185. for (auto& f : _functions){
  186. Function& func = f.second;
  187. for (auto it = insts.begin(); it != insts.end(); it ++){
  188. if ((*it)->_address == func.mEndAddr){
  189. // Note: This is a "best effort heuristic", so quite a few loops will
  190. // still end up as goto's. This could potentially generate invalid code too.
  191. if ((*it)->isUncondJump()){
  192. // Then assume its an infinite do { } while(true)
  193. // loop that wraps part of the script.
  194. VertexRange vr = boost::vertices(g);
  195. for (VertexIterator v = vr.first; v != vr.second; ++ v){
  196. GroupPtr gr = GET(*v);
  197. if ((*gr->_start)->_address == func.mEndAddr){
  198. // Then assume its an infinite do { } while(true) loop
  199. // that wraps part of the script.
  200. gr->_type = kDoWhileCondGroupType;
  201. }
  202. }
  203. }
  204. break;
  205. }
  206. }
  207. }
  208. }
  209. void FF7CondJumpInstruction::processInst(
  210. Function& function, ValueStack &stack, Engine* engine, CodeGenerator* code_gen
  211. ){
  212. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  213. std::string funcName;
  214. if (_opcode == OPCODE::IFKEYON) funcName = "entity_manager:is_key_on";
  215. else if (_opcode == OPCODE::IFKEYOFF) funcName = "entity_manager:is_key_off";
  216. else if (_opcode == OPCODE::IFKEY) funcName = "Key";
  217. else if (_opcode == OPCODE::IFMEMBQ) funcName = "IFMEMBQ";
  218. else if (_opcode == OPCODE::IFPRTYQ) funcName = "IFPRTYQ";
  219. // If condition is a function, add and stop.
  220. if (!funcName.empty()){
  221. uint32 param = _params[0]->getUnsigned();
  222. // Special cases. The first parameter of IFKEY, IFKEYON and IFKEYOFF
  223. // can be ORed to get the individual keys, but there are two invalid
  224. // ones: 512 and 1024. They must be XORed.
  225. if (
  226. _opcode == OPCODE::IFKEY
  227. || _opcode == OPCODE::IFKEYON
  228. || _opcode == OPCODE::IFKEYOFF
  229. ){
  230. if (param >= 1024) param = param ^ 1024;
  231. if (param >= 512) param = param ^ 512;
  232. }
  233. ValuePtr v = new UnqotedStringValue(funcName + "(" + std::to_string(param) + ")");
  234. stack.push(v);
  235. return;
  236. }
  237. std::string op;
  238. uint32 type = _params[4]->getUnsigned();
  239. const auto& source = FieldCodeGenerator::FormatValueOrVariable(
  240. cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned()
  241. );
  242. const auto& destination = FieldCodeGenerator::FormatValueOrVariable(
  243. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned()
  244. );
  245. switch (type){
  246. case 0: op = "=="; break;
  247. case 1: op = "~="; break;
  248. case 2: op = ">"; break;
  249. case 3: op = "<"; break;
  250. case 4: op = ">="; break;
  251. case 5: op = "<="; break;
  252. case 6: op = "&"; break;
  253. case 7: op = "^"; break;
  254. case 8: op = "|"; break;
  255. case 9:
  256. {
  257. op = "bit(" + _params[0]->getString() + ", " + _params[2]->getString()
  258. + ", " + destination + ") == 1";
  259. ValuePtr v = new UnqotedStringValue(op);
  260. stack.push(v);
  261. }
  262. return;
  263. case 0xA:
  264. {
  265. op = "bit(" + _params[0]->getString() + ", " + _params[2]->getString()
  266. + ", " + destination + ") == 0";
  267. ValuePtr v = new UnqotedStringValue(op);
  268. stack.push(v);
  269. }
  270. return;
  271. default: throw UnknownConditionalOperatorException(_address, type);
  272. }
  273. ValuePtr v = new BinaryOpValue(new VarValue(source), new VarValue(destination), op);
  274. stack.push(v);
  275. }
  276. uint32 FF7CondJumpInstruction::getDestAddress() const{
  277. uint32 params_size = 0;
  278. uint32 jump_param_index = 5;
  279. switch (_opcode){
  280. case OPCODE::IFUB: params_size = 5; break;
  281. case OPCODE::IFUBL: params_size = 5; break;
  282. case OPCODE::IFSW: params_size = 7; break;
  283. case OPCODE::IFSWL: params_size = 7; break;
  284. case OPCODE::IFUW: params_size = 7; break;
  285. case OPCODE::IFUWL: params_size = 7; break;
  286. case OPCODE::IFKEYON:
  287. case OPCODE::IFKEYOFF:
  288. case OPCODE::IFKEY:
  289. params_size = 3;
  290. jump_param_index = 1;
  291. break;
  292. case OPCODE::IFPRTYQ:
  293. case OPCODE::IFMEMBQ:
  294. params_size = 2;
  295. jump_param_index = 1;
  296. break;
  297. default: throw UnknownJumpTypeException(_address, _opcode);
  298. }
  299. return _address + _params[jump_param_index]->getUnsigned() + params_size;
  300. }
  301. std::ostream& FF7CondJumpInstruction::print(std::ostream &output) const{
  302. Instruction::print(output);
  303. output << " (False target address: 0x" << std::hex << getDestAddress() << std::dec << ")";
  304. return output;
  305. }
  306. bool FF7UncondJumpInstruction::isFuncCall() const{return _isCall;}
  307. bool FF7UncondJumpInstruction::isUncondJump() const{return !_isCall;}
  308. uint32 FF7UncondJumpInstruction::getDestAddress() const{
  309. if (
  310. static_cast<OPCODE>(_opcode) == OPCODE::JMPF
  311. || static_cast<OPCODE>(_opcode) == OPCODE::JMPFL){
  312. // Short or long forward jump.
  313. return _address + _params[0]->getUnsigned() + 1;
  314. }
  315. // Backwards jump, OPCODE::JMPB/L.
  316. return _address - _params[0]->getUnsigned();
  317. }
  318. std::ostream& FF7UncondJumpInstruction::print(std::ostream &output) const{
  319. Instruction::print(output);
  320. output << " (Jump target address: 0x" << std::hex << getDestAddress() << std::dec << ")";
  321. return output;
  322. }
  323. void FF7UncondJumpInstruction::processInst(
  324. Function& func, ValueStack&, Engine* engine, CodeGenerator* code_gen
  325. ){
  326. switch (_opcode){
  327. case OPCODE::JMPB:
  328. case OPCODE::JMPBL:
  329. // HACK: Hard loop will hang the game, insert a wait to yield control
  330. code_gen->AddOutputLine("-- Hack, yield control for possible inf loop");
  331. code_gen->AddOutputLine("script:wait(0)");
  332. break;
  333. }
  334. }
  335. static void WriteTodo(CodeGenerator *code_gen, std::string entity_name, std::string opCode){
  336. code_gen->AddOutputLine("-- Todo(\"" + opCode + "\")");
  337. }
  338. void FF7ControlFlowInstruction::processInst(
  339. Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen
  340. ){
  341. FF7FieldEngine& eng = static_cast<FF7FieldEngine&>(*engine);
  342. FunctionMetaData md(func._metadata);
  343. switch (_opcode){
  344. case OPCODE::RET:
  345. // A few notes in RET.
  346. // - Lua requires all functions to end with a return.
  347. // - Lua doesn's like returns if not followed by an end.
  348. // - Don't add a return statement to on_start functions.
  349. // Game 'init' and 'main' code is concatenated in V-Gears. If the
  350. // 'main' return is kept, the code of 'main' is never executed.
  351. // There are safeguards forfunctions without a return in the end,
  352. // so it's OK not to include it here.
  353. if (func._name != "on_start") code_gen->AddOutputLine("do return 0 end");
  354. break;
  355. case OPCODE::REQ: processREQ(code_gen, eng); break;
  356. case OPCODE::REQSW: processREQSW(code_gen, eng); break;
  357. case OPCODE::REQEW: processREQEW(code_gen, eng); break;
  358. case OPCODE::PREQ: WriteTodo(code_gen, md.GetEntityName(), "PREQ"); break;
  359. case OPCODE::PRQSW: WriteTodo(code_gen, md.GetEntityName(), "PRQSW"); break;
  360. case OPCODE::PRQEW: WriteTodo(code_gen, md.GetEntityName(), "PRQEW"); break;
  361. case OPCODE::RETTO: processRETTO(code_gen); break;
  362. case OPCODE::WAIT: processWAIT(code_gen); break;
  363. default:
  364. code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented(
  365. md.GetEntityName(), _address, _opcode
  366. ));
  367. }
  368. }
  369. void FF7ControlFlowInstruction::processREQ(
  370. CodeGenerator* code_gen, const FF7FieldEngine& engine
  371. ){
  372. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  373. const auto& entity = engine.EntityByIndex(_params[0]->getSigned());
  374. const auto& script_name = cg->GetFormatter().FunctionName(
  375. entity.Name(), entity.FunctionByIndex(_params[2]->getUnsigned())
  376. );
  377. auto priority = _params[1]->getUnsigned();
  378. code_gen->AddOutputLine((
  379. boost::format("script:request(Script.ENTITY, \"%1%\", \"%2%\", %3%)")
  380. % entity.Name() % script_name % priority
  381. ).str());
  382. }
  383. void FF7ControlFlowInstruction::processREQSW(
  384. CodeGenerator* code_gen, const FF7FieldEngine& engine
  385. ){
  386. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  387. const auto& entity = engine.EntityByIndex(_params[0]->getSigned());
  388. const auto& script_name = cg->GetFormatter().FunctionName(
  389. entity.Name(), entity.FunctionByIndex(_params[2]->getUnsigned())
  390. );
  391. auto priority = _params[1]->getUnsigned();
  392. code_gen->AddOutputLine((
  393. boost::format("script:request_start_sync(Script.ENTITY, \"%1%\", \"%2%\", %3%)")
  394. % entity.Name() % script_name % priority
  395. ).str());
  396. }
  397. void FF7ControlFlowInstruction::processREQEW(
  398. CodeGenerator* code_gen, const FF7FieldEngine& engine
  399. ){
  400. try{
  401. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  402. const auto& entity = engine.EntityByIndex(_params[0]->getSigned());
  403. const auto& script_name = cg->GetFormatter().FunctionName(
  404. entity.Name(), entity.FunctionByIndex(_params[2]->getUnsigned())
  405. );
  406. auto priority = _params[1]->getUnsigned();
  407. code_gen->AddOutputLine((
  408. boost::format("script:request_end_sync(Script.ENTITY, \"%1%\", \"%2%\", %3%)")
  409. % entity.Name() % script_name % priority
  410. ).str());
  411. }
  412. catch (const InternalDecompilerError&){
  413. code_gen->AddOutputLine((
  414. boost::format("-- ERROR call to non existing function index %1%")
  415. % _params[2]->getUnsigned()
  416. ).str());
  417. }
  418. }
  419. void FF7ControlFlowInstruction::processRETTO(CodeGenerator* code_gen){
  420. auto entity_index = _params[0]->getUnsigned();
  421. auto priority = _params[1]->getUnsigned();
  422. code_gen->AddOutputLine((
  423. boost::format("-- return_to(script_id_in_current_entity=%2%, priority=%1%)")
  424. % entity_index % priority
  425. ).str());
  426. }
  427. void FF7ControlFlowInstruction::processWAIT(CodeGenerator* code_gen){
  428. code_gen->AddOutputLine((
  429. boost::format("script:wait(%1%)") % (_params[0]->getUnsigned() / 30.0f)
  430. ).str());
  431. }
  432. void FF7ModuleInstruction::processInst(
  433. Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen
  434. ){
  435. FunctionMetaData md(func._metadata);
  436. switch (_opcode){
  437. case OPCODE::DSKCG: WriteTodo(code_gen, md.GetEntityName(), "DSKCG"); break;
  438. case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::ARROW:
  439. code_gen->AddOutputLine((
  440. boost::format("game:pointer_enable(%1%)")
  441. % (_params[0]->getUnsigned() ? "true" : "false")
  442. ).str());
  443. break;
  444. case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::PNAME:
  445. WriteTodo(code_gen, md.GetEntityName(), "PNAME");
  446. break;
  447. case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::GMSPD:
  448. WriteTodo(code_gen, md.GetEntityName(), "GMSPD");
  449. break;
  450. case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::SMSPD:
  451. WriteTodo(code_gen, md.GetEntityName(), "SMSPD");
  452. break;
  453. case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::FLMAT:
  454. code_gen->AddOutputLine("game:fill_materia()");
  455. break;
  456. case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::FLITM:
  457. WriteTodo(code_gen, md.GetEntityName(), "FLITM");
  458. break;
  459. case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::BTLCK:
  460. code_gen->AddOutputLine((
  461. boost::format("game:battle_enable(%1%)")
  462. % (_params[0]->getUnsigned() ? "true" : "false")
  463. ).str());
  464. break;
  465. case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::MVLCK:
  466. code_gen->AddOutputLine((
  467. boost::format("game:movie_enable(%1%)")
  468. % (_params[0]->getUnsigned() ? "true" : "false")
  469. ).str());
  470. break;
  471. case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::SPCNM:
  472. WriteTodo(code_gen, md.GetEntityName(), "SPCNM");
  473. break;
  474. case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::RSGLB:
  475. code_gen->AddOutputLine("game:global_reset()");
  476. break;
  477. case (OPCODE::SPECIAL << 8) | OPCODE_SPECIAL::CLITM:
  478. WriteTodo(code_gen, md.GetEntityName(), "CLITM");
  479. break;
  480. case OPCODE::MINIGAME: WriteTodo(code_gen, md.GetEntityName(), "MINIGAME"); break;
  481. case OPCODE::BTMD2: WriteTodo(code_gen, md.GetEntityName(), "BTMD2"); break;
  482. case OPCODE::BTRLD: WriteTodo(code_gen, md.GetEntityName(), "BTRLD"); break;
  483. case OPCODE::BTLTB: WriteTodo(code_gen, md.GetEntityName(), "BTLTB"); break;
  484. case OPCODE::MAPJUMP: processMAPJUMP(code_gen, func); break;
  485. case OPCODE::LSTMP: WriteTodo(code_gen, md.GetEntityName(), "LSTMP"); break;
  486. case OPCODE::BATTLE: processBATTLE(code_gen); break;
  487. case OPCODE::BTLON: processBTLON(code_gen); break;
  488. case OPCODE::BTLMD: WriteTodo(code_gen, md.GetEntityName(), "BTLMD"); break;
  489. case OPCODE::MPJPO:
  490. // Gateway function will do nothing if this is set to true
  491. code_gen->AddOutputLine(
  492. std::string("FFVII.Data.DisableGateways=")
  493. + (_params[0]->getUnsigned() ? "true" : "false")
  494. );
  495. break;
  496. // Prepare to change map, don't need to output anything for this.
  497. case OPCODE::PMJMP: code_gen->AddOutputLine("-- Prepare map change"); break;
  498. // Prepare to change map, don't need to output anything for this,
  499. // seems to be the same thing as PMJMP
  500. case OPCODE::PMJMP2: code_gen->AddOutputLine("-- Prepare map change 2"); break;
  501. case OPCODE::GAMEOVER: WriteTodo(code_gen, md.GetEntityName(), "GAMEOVER"); break;
  502. default:
  503. code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented(
  504. md.GetEntityName(), _address, _opcode
  505. ));
  506. }
  507. }
  508. void FF7ModuleInstruction::processBATTLE(CodeGenerator* code_gen){
  509. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  510. const auto& battle_id = FieldCodeGenerator::FormatValueOrVariable(
  511. cg->GetFormatter(), _params[0]->getUnsigned(), _params[1]->getUnsigned()
  512. );
  513. code_gen->AddOutputLine((boost::format("entity_manager:battle_run(%1%)") % battle_id).str());
  514. }
  515. void FF7ModuleInstruction::processBTLON(CodeGenerator* code_gen){
  516. code_gen->AddOutputLine((
  517. boost::format("entity_manager:random_encounters_on(%1%)")
  518. % FieldCodeGenerator::FormatInvertedBool(_params[0]->getUnsigned())
  519. ).str());
  520. }
  521. void FF7ModuleInstruction::processMAPJUMP(CodeGenerator* code_gen, Function& func){
  522. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  523. const auto target_map_id = _params[0]->getUnsigned();
  524. FunctionMetaData md(func._metadata);
  525. const std::string source_spawn_point_name
  526. = cg->GetFormatter().SpawnPointName(target_map_id, md.GetEntityName(), func._name, _address);
  527. cg->GetFormatter().AddSpawnPoint(
  528. target_map_id, md.GetEntityName(), func._name, _address,
  529. _params[1]->getSigned(), // X
  530. _params[2]->getSigned(), // Y
  531. _params[3]->getSigned(), // Walk mesh triangle ID
  532. _params[4]->getSigned() // Angle
  533. );
  534. const std::string target_map_name = cg->GetFormatter().MapName(target_map_id);
  535. code_gen->AddOutputLine(
  536. "load_field_map_request(\"" + target_map_name + "\", \"" + source_spawn_point_name + "\")"
  537. );
  538. }
  539. void FF7MathInstruction::processInst(
  540. Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen
  541. ){
  542. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  543. FunctionMetaData md(func._metadata);
  544. switch (_opcode){
  545. case OPCODE::PLUS_: processSaturatedPLUS(code_gen); break;
  546. case OPCODE::PLUS2_: processSaturatedPLUS2(code_gen); break;
  547. case OPCODE::MINUS_: processSaturatedMINUS(code_gen); break;
  548. case OPCODE::MINUS2_: processSaturatedMINUS2(code_gen); break;
  549. case OPCODE::INC_: processSaturatedINC(code_gen); break;
  550. case OPCODE::INC2_: processSaturatedINC2(code_gen); break;
  551. case OPCODE::DEC_: processSaturatedDEC(code_gen); break;
  552. case OPCODE::DEC2_: processSaturatedDEC2(code_gen); break;
  553. case OPCODE::RDMSD: processRDMSD(code_gen); break;
  554. case OPCODE::SETBYTE:
  555. case OPCODE::SETWORD:
  556. processSETBYTE_SETWORD(code_gen);
  557. break;
  558. case OPCODE::BITON: processBITON(code_gen); break;
  559. case OPCODE::BITOFF: processBITOFF(code_gen); break;
  560. case OPCODE::BITXOR: WriteTodo(code_gen, md.GetEntityName(), "BITXOR"); break;
  561. case OPCODE::PLUS:
  562. case OPCODE::PLUS2:
  563. processPLUSx_MINUSx(code_gen, "+");
  564. break;
  565. case OPCODE::MINUS:
  566. case OPCODE::MINUS2:
  567. processPLUSx_MINUSx(code_gen, "-");
  568. break;
  569. case OPCODE::MUL: WriteTodo(code_gen, md.GetEntityName(), "MUL"); break;
  570. case OPCODE::MUL2: WriteTodo(code_gen, md.GetEntityName(), "MUL2"); break;
  571. case OPCODE::DIV: WriteTodo(code_gen, md.GetEntityName(), "DIV"); break;
  572. case OPCODE::DIV2: WriteTodo(code_gen, md.GetEntityName(), "DIV2"); break;
  573. case OPCODE::MOD:
  574. {
  575. const uint32 source_bank = _params[0]->getUnsigned();
  576. const uint32 source_address_or_value = _params[2]->getUnsigned();
  577. auto source = FieldCodeGenerator::FormatValueOrVariable(
  578. cg->GetFormatter(), source_bank, source_address_or_value
  579. );
  580. const uint32 dest_bank = _params[1]->getUnsigned();
  581. const uint32 dest_address = _params[3]->getUnsigned();
  582. auto dest = FieldCodeGenerator::FormatValueOrVariable(
  583. cg->GetFormatter(), dest_bank, dest_address
  584. );
  585. code_gen->AddOutputLine(
  586. source + " = " + source + " % " + dest + code_gen->TargetLang().LineTerminator()
  587. );
  588. }
  589. break;
  590. case OPCODE::MOD2: WriteTodo(code_gen, md.GetEntityName(), "MOD2"); break;
  591. case OPCODE::AND: WriteTodo(code_gen, md.GetEntityName(), "AND"); break;
  592. case OPCODE::AND2: WriteTodo(code_gen, md.GetEntityName(), "AND2"); break;
  593. case OPCODE::OR: WriteTodo(code_gen, md.GetEntityName(), "OR"); break;
  594. case OPCODE::OR2: WriteTodo(code_gen, md.GetEntityName(), "OR2"); break;
  595. case OPCODE::XOR: WriteTodo(code_gen, md.GetEntityName(), "XOR"); break;
  596. case OPCODE::XOR2: WriteTodo(code_gen, md.GetEntityName(), "XOR2"); break;
  597. case OPCODE::INC:
  598. case OPCODE::INC2:
  599. processINCx_DECx(code_gen, "+");
  600. break;
  601. case OPCODE::DEC:
  602. case OPCODE::DEC2:
  603. processINCx_DECx(code_gen, "-");
  604. break;
  605. case OPCODE::RANDOM: processRANDOM(code_gen); break;
  606. case OPCODE::LBYTE: WriteTodo(code_gen, md.GetEntityName(), "LBYTE"); break;
  607. case OPCODE::HBYTE: WriteTodo(code_gen, md.GetEntityName(), "HBYTE"); break;
  608. case OPCODE::TWOBYTE: WriteTodo(code_gen, md.GetEntityName(), "2BYTE"); break;
  609. case OPCODE::SIN: WriteTodo(code_gen, md.GetEntityName(), "SIN"); break;
  610. case OPCODE::COS: WriteTodo(code_gen, md.GetEntityName(), "COS"); break;
  611. default:
  612. code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented(
  613. md.GetEntityName(), _address, _opcode
  614. ));
  615. }
  616. }
  617. void FF7MathInstruction::processSaturatedPLUS(CodeGenerator* code_gen){
  618. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  619. // TODO: Check for assignment to value.
  620. const auto& lhs = FieldCodeGenerator::FormatValueOrVariable(
  621. cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned()
  622. );
  623. const auto& rhs = FieldCodeGenerator::FormatValueOrVariable(
  624. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned()
  625. );
  626. // TODO: Respect destination bank sizes and negative wrap-around.
  627. code_gen->AddOutputLine((boost::format("%1% = %1% + %2%") % lhs % rhs).str());
  628. code_gen->AddOutputLine((boost::format("--if (%1% > 255); %1% = 255; end") % lhs).str());
  629. }
  630. void FF7MathInstruction::processSaturatedPLUS2(CodeGenerator* code_gen){
  631. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  632. // TODO: Check for assignment to value.
  633. const auto& lhs = FieldCodeGenerator::FormatValueOrVariable(
  634. cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned()
  635. );
  636. const auto& rhs = FieldCodeGenerator::FormatValueOrVariable(
  637. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned()
  638. );
  639. // TODO: Respect destination bank sizes and negative wrap-around.
  640. code_gen->AddOutputLine((boost::format("%1% = %1% + %2%") % lhs % rhs).str());
  641. code_gen->AddOutputLine((boost::format("--if (%1% > 32767); %1% = 32767; end") % lhs).str());
  642. }
  643. void FF7MathInstruction::processSaturatedMINUS(CodeGenerator* code_gen){
  644. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  645. // TODO: Check for assignment to value.
  646. const auto& lhs = FieldCodeGenerator::FormatValueOrVariable(
  647. cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned()
  648. );
  649. const auto& rhs = FieldCodeGenerator::FormatValueOrVariable(
  650. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned()
  651. );
  652. // TODO: Respect destination bank sizes and positive wrap-around.
  653. code_gen->AddOutputLine((boost::format("%1% = %1% - %2%") % lhs % rhs).str());
  654. code_gen->AddOutputLine((boost::format("--if (%1% < 0); %1% = 0; end") % lhs).str());
  655. }
  656. void FF7MathInstruction::processSaturatedMINUS2(CodeGenerator* code_gen){
  657. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  658. // TODO: Check for assignment to value.
  659. const auto& lhs = FieldCodeGenerator::FormatValueOrVariable(
  660. cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned()
  661. );
  662. const auto& rhs = FieldCodeGenerator::FormatValueOrVariable(
  663. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned()
  664. );
  665. // TODO: Respect destination bank sizes and positive wrap-around.
  666. code_gen->AddOutputLine((boost::format("%1% = %1% - %2%") % lhs % rhs).str());
  667. code_gen->AddOutputLine((boost::format("--if (%1% < 0); %1% = 0; end") % lhs).str());
  668. }
  669. void FF7MathInstruction::processSaturatedINC(CodeGenerator* code_gen){
  670. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  671. // TODO: Check for assignment to value.
  672. const auto& dest = FieldCodeGenerator::FormatValueOrVariable(
  673. cg->GetFormatter(), _params[0]->getUnsigned(), _params[1]->getUnsigned()
  674. );
  675. // TODO: Respect destination bank sizes and negative wrap-around.
  676. code_gen->AddOutputLine((boost::format("%1% = %1% + 1") % dest).str());
  677. code_gen->AddOutputLine((boost::format("--if (%1% > 255); %1% = 255; end") % dest).str());
  678. }
  679. void FF7MathInstruction::processSaturatedINC2(CodeGenerator* code_gen){
  680. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  681. // TODO: Check for assignment to value.
  682. const auto& dest = FieldCodeGenerator::FormatValueOrVariable(
  683. cg->GetFormatter(), _params[0]->getUnsigned(), _params[1]->getUnsigned()
  684. );
  685. // TODO: Respect destination bank sizes and negative wrap-around.
  686. code_gen->AddOutputLine((boost::format("%1% = %1% + 1") % dest).str());
  687. code_gen->AddOutputLine((boost::format("--if (%1% > 32767); %1% = 32767; end") % dest).str());
  688. }
  689. void FF7MathInstruction::processSaturatedDEC(CodeGenerator* code_gen){
  690. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  691. // TODO: Check for assignment to value.
  692. const auto& dest = FieldCodeGenerator::FormatValueOrVariable(
  693. cg->GetFormatter(), _params[0]->getUnsigned(), _params[1]->getUnsigned()
  694. );
  695. // TODO: Respect destination bank sizes and positive wrap-around.
  696. code_gen->AddOutputLine((boost::format("%1% = %1% - 1") % dest).str());
  697. code_gen->AddOutputLine((boost::format("--if (%1% < 0); %1% = 0; end") % dest).str());
  698. }
  699. void FF7MathInstruction::processSaturatedDEC2(CodeGenerator* code_gen){
  700. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  701. // TODO: Check for assignment to value.
  702. const auto& dest = FieldCodeGenerator::FormatValueOrVariable(
  703. cg->GetFormatter(), _params[0]->getUnsigned(), _params[1]->getUnsigned()
  704. );
  705. // TODO: Respect destination bank sizes and positive wrap-around.
  706. code_gen->AddOutputLine((boost::format("%1% = %1% - 1") % dest).str());
  707. code_gen->AddOutputLine((boost::format("--if (%1% < 0); %1% = 0; end") % dest).str());
  708. }
  709. void FF7MathInstruction::processRDMSD(CodeGenerator* code_gen){
  710. // TODO: we don't have os.time...
  711. // TODO: RNG emulation?
  712. code_gen->AddOutputLine("math.randomseed(os.time())");
  713. }
  714. void FF7MathInstruction::processSETBYTE_SETWORD(CodeGenerator* code_gen){
  715. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  716. // TODO: Check for assignment to value.
  717. const auto& destination = FieldCodeGenerator::FormatValueOrVariable(
  718. cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned()
  719. );
  720. const auto& source = FieldCodeGenerator::FormatValueOrVariable(
  721. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned()
  722. );
  723. // TODO: respect destination bank sizes (16-bit writes only affect low byte)
  724. code_gen->AddOutputLine((boost::format("%1% = %2%") % destination % source).str());
  725. }
  726. void FF7MathInstruction::processBITON(CodeGenerator* code_gen){
  727. code_gen->AddOutputLine((boost::format("bit_on(%1%, %2%, %3%)")
  728. % _params[0]->getUnsigned() % _params[2]->getUnsigned() % _params[3]->getUnsigned()
  729. ).str());
  730. }
  731. void FF7MathInstruction::processBITOFF(CodeGenerator* code_gen){
  732. code_gen->AddOutputLine((boost::format("bit_off(%1%, %2%, %3%)")
  733. % _params[0]->getUnsigned() % _params[2]->getUnsigned() % _params[3]->getUnsigned()
  734. ).str());
  735. }
  736. void FF7MathInstruction::processPLUSx_MINUSx(CodeGenerator* code_gen, const std::string& op){
  737. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  738. // TODO: Check for assignment to value.
  739. const auto& lhs = FieldCodeGenerator::FormatValueOrVariable(
  740. cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned()
  741. );
  742. const auto& rhs = FieldCodeGenerator::FormatValueOrVariable(
  743. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned()
  744. );
  745. // TODO: repect destination bank sizes and wraparound
  746. code_gen->AddOutputLine((boost::format("%1% = %1% %2% %3%") % lhs % op % rhs).str());
  747. }
  748. void FF7MathInstruction::processINCx_DECx(CodeGenerator* code_gen, const std::string& op){
  749. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  750. // TODO: Check for assignment to value.
  751. const auto& destination = FieldCodeGenerator::FormatValueOrVariable(
  752. cg->GetFormatter(), _params[0]->getUnsigned(), _params[1]->getUnsigned()
  753. );
  754. // TODO: repect destination bank sizes and wraparound
  755. code_gen->AddOutputLine((boost::format("%1% = %1% %2% 1") % destination % op).str());
  756. }
  757. void FF7MathInstruction::processRANDOM(CodeGenerator* code_gen){
  758. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  759. // TODO: Check for assignment to value.
  760. const auto& destination = FieldCodeGenerator::FormatValueOrVariable(
  761. cg->GetFormatter(), _params[0]->getUnsigned(), _params[1]->getUnsigned()
  762. );
  763. // TODO: Respect destination bank sizes (16-bit writes only affect low byte).
  764. // TODO: RNG emulation?
  765. code_gen->AddOutputLine((boost::format("%1% = math.random(0, 255)") % destination).str());
  766. }
  767. void FF7WindowInstruction::processInst(
  768. Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen
  769. ){
  770. FF7FieldEngine& eng = static_cast<FF7FieldEngine&>(*engine);
  771. FunctionMetaData md(func._metadata);
  772. switch (_opcode){
  773. case OPCODE::TUTOR: WriteTodo(code_gen, md.GetEntityName(), "TUTOR"); break;
  774. case OPCODE::WCLS: WriteTodo(code_gen, md.GetEntityName(), "WCLS"); break;
  775. case OPCODE::WSIZW: WriteTodo(code_gen, md.GetEntityName(), "WSIZW"); break;
  776. case OPCODE::WSPCL: WriteTodo(code_gen, md.GetEntityName(), "WSPCL"); break;
  777. case OPCODE::WNUMB: WriteTodo(code_gen, md.GetEntityName(), "WNUMB"); break;
  778. case OPCODE::STTIM: WriteTodo(code_gen, md.GetEntityName(), "STTIM"); break;
  779. case OPCODE::MESSAGE: processMESSAGE(code_gen, eng.ScriptName()); break;
  780. case OPCODE::MPARA: WriteTodo(code_gen, md.GetEntityName(), "MPARA"); break;
  781. case OPCODE::MPRA2: WriteTodo(code_gen, md.GetEntityName(), "MPRA2"); break;
  782. case OPCODE::MPNAM: processMPNAM(code_gen); break;
  783. case OPCODE::ASK: WriteTodo(code_gen, md.GetEntityName(), "ASK"); break;
  784. case OPCODE::MENU: WriteTodo(code_gen, md.GetEntityName(), "MENU"); break;
  785. case OPCODE::MENU2: processMENU2(code_gen); break;
  786. case OPCODE::WINDOW: processWINDOW(code_gen); break;
  787. case OPCODE::WMOVE: WriteTodo(code_gen, md.GetEntityName(), "WMOVE"); break;
  788. case OPCODE::WMODE: WriteTodo(code_gen, md.GetEntityName(), "WMODE"); break;
  789. case OPCODE::WREST: WriteTodo(code_gen, md.GetEntityName(), "WREST"); break;
  790. case OPCODE::WCLSE: processWCLSE(code_gen); break;
  791. case OPCODE::WROW: WriteTodo(code_gen, md.GetEntityName(), "WROW"); break;
  792. case OPCODE::GWCOL: WriteTodo(code_gen, md.GetEntityName(), "GWCOL"); break;
  793. case OPCODE::SWCOL: WriteTodo(code_gen, md.GetEntityName(), "SWCOL"); break;
  794. default:
  795. code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented(
  796. md.GetEntityName(), _address, _opcode
  797. ));
  798. }
  799. }
  800. void FF7WindowInstruction::processWINDOW(CodeGenerator* code_gen){
  801. // Init a new window. It won't be displayed until MESSAGE is used.
  802. auto windowId = _params[0]->getUnsigned();
  803. auto x = _params[1]->getUnsigned();
  804. auto y = _params[2]->getUnsigned();
  805. auto width = _params[3]->getUnsigned();
  806. auto height = _params[4]->getUnsigned();
  807. code_gen->AddOutputLine((
  808. boost::format("dialog:dialog_open(\"%1%\", %2%, %3%, %4%, %5%)")
  809. % windowId % x % y % width % height
  810. ).str());
  811. }
  812. void FF7WindowInstruction::processMESSAGE(
  813. CodeGenerator* code_gen, const std::string& script_name
  814. ){
  815. // Displays a dialog in the WINDOW that has previously been initialized to display this dialog.
  816. auto window_id = _params[0]->getUnsigned();
  817. auto dialog_id = _params[1]->getUnsigned();
  818. code_gen->AddOutputLine((
  819. boost::format("dialog:dialog_set_text(\"%1%\", \"%2%_%3%\")")
  820. % window_id % script_name % dialog_id
  821. ).str());
  822. code_gen->AddOutputLine(
  823. (boost::format("dialog:dialog_wait_for_close(\"%1%\")") % window_id).str()
  824. );
  825. }
  826. void FF7WindowInstruction::processWCLSE(CodeGenerator* code_gen){
  827. // Close a dialog.
  828. auto windowId = _params[0]->getUnsigned();
  829. code_gen->AddOutputLine((boost::format("dialog:dialog_close(\"%1%\")") % windowId).str());
  830. }
  831. void FF7WindowInstruction::processMPNAM(CodeGenerator* code_gen){
  832. code_gen->AddOutputLine(
  833. (boost::format("-- field:map_name(%1%)") % _params[0]->getUnsigned()).str()
  834. );
  835. }
  836. void FF7WindowInstruction::processMENU2(CodeGenerator* code_gen){
  837. code_gen->AddOutputLine((
  838. boost::format("-- field:menu_lock(%1%)")
  839. % FieldCodeGenerator::FormatBool(_params[0]->getUnsigned())
  840. ).str());
  841. }
  842. void FF7PartyInstruction::processInst(
  843. Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen
  844. ){
  845. FunctionMetaData md(func._metadata);
  846. switch (_opcode){
  847. case OPCODE::SPTYE: WriteTodo(code_gen, md.GetEntityName(), "SPTYE"); break;
  848. case OPCODE::GTPYE: WriteTodo(code_gen, md.GetEntityName(), "GTPYE"); break;
  849. case OPCODE::GOLDU: WriteTodo(code_gen, md.GetEntityName(), "GOLDU"); break;
  850. case OPCODE::GOLDD: WriteTodo(code_gen, md.GetEntityName(), "GOLDD"); break;
  851. case OPCODE::HMPMAX1: WriteTodo(code_gen, md.GetEntityName(), "HMPMAX1"); break;
  852. case OPCODE::HMPMAX2: WriteTodo(code_gen, md.GetEntityName(), "HMPMAX2"); break;
  853. case OPCODE::MHMMX: WriteTodo(code_gen, md.GetEntityName(), "MHMMX"); break;
  854. case OPCODE::HMPMAX3: WriteTodo(code_gen, md.GetEntityName(), "HMPMAX3"); break;
  855. case OPCODE::MPU: WriteTodo(code_gen, md.GetEntityName(), "MPU"); break;
  856. case OPCODE::MPD: WriteTodo(code_gen, md.GetEntityName(), "MPD"); break;
  857. case OPCODE::HPU: WriteTodo(code_gen, md.GetEntityName(), "HPU"); break;
  858. case OPCODE::HPD: WriteTodo(code_gen, md.GetEntityName(), "HPD"); break;
  859. case OPCODE::STITM: processSTITM(code_gen); break;
  860. case OPCODE::DLITM: WriteTodo(code_gen, md.GetEntityName(), "DLITM"); break;
  861. case OPCODE::CKITM: WriteTodo(code_gen, md.GetEntityName(), "CKITM"); break;
  862. case OPCODE::SMTRA: WriteTodo(code_gen, md.GetEntityName(), "SMTRA"); break;
  863. case OPCODE::DMTRA: WriteTodo(code_gen, md.GetEntityName(), "DMTRA"); break;
  864. case OPCODE::CMTRA: WriteTodo(code_gen, md.GetEntityName(), "CMTRA"); break;
  865. case OPCODE::GETPC: WriteTodo(code_gen, md.GetEntityName(), "GETPC"); break;
  866. case OPCODE::PRTYP: WriteTodo(code_gen, md.GetEntityName(), "PRTYP"); break;
  867. case OPCODE::PRTYM: WriteTodo(code_gen, md.GetEntityName(), "PRTYM"); break;
  868. case OPCODE::PRTYE: processPRTYE(code_gen); break;
  869. case OPCODE::MMBUD: WriteTodo(code_gen, md.GetEntityName(), "MMBUD"); break;
  870. case OPCODE::MMBLK: WriteTodo(code_gen, md.GetEntityName(), "MMBLK"); break;
  871. case OPCODE::MMBUK: WriteTodo(code_gen, md.GetEntityName(), "MMBUK"); break;
  872. case OPCODE::CHGLD: WriteTodo(code_gen, md.GetEntityName(), "CHGLD"); break;
  873. default:
  874. code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented(
  875. md.GetEntityName(), _address, _opcode
  876. ));
  877. }
  878. }
  879. void FF7PartyInstruction::processSTITM(CodeGenerator* code_gen){
  880. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  881. const auto& item_id = FieldCodeGenerator::FormatValueOrVariable(
  882. cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned()
  883. );
  884. const auto& amount = FieldCodeGenerator::FormatValueOrVariable(
  885. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned()
  886. );
  887. code_gen->AddOutputLine((boost::format("FFVII.add_item(%1%, %2%)") % item_id % amount).str());
  888. }
  889. void FF7PartyInstruction::processPRTYE(CodeGenerator* code_gen){
  890. FieldCodeGenerator* gc = static_cast<FieldCodeGenerator*>(code_gen);
  891. auto char_id_1 = gc->GetFormatter().CharName(_params[0]->getUnsigned());
  892. char_id_1 = (char_id_1 == "") ? "nil" : ("\"" + char_id_1 + "\"");
  893. auto char_id_2 = gc->GetFormatter().CharName(_params[1]->getUnsigned());
  894. char_id_2 = (char_id_2 == "") ? "nil" : ("\"" + char_id_2 + "\"");
  895. auto char_id_3 = gc->GetFormatter().CharName(_params[2]->getUnsigned());
  896. char_id_3 = (char_id_3 == "") ? "nil" : ("\"" + char_id_3 + "\"");
  897. code_gen->AddOutputLine((
  898. boost::format("FFVII.set_party(%1%, %2%, %3%)") % char_id_1 % char_id_2 % char_id_3
  899. ).str());
  900. }
  901. void FF7ModelInstruction::processInst(
  902. Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen
  903. ){
  904. FF7FieldEngine& eng = static_cast<FF7FieldEngine&>(*engine);
  905. FunctionMetaData md(func._metadata);
  906. switch (_opcode){
  907. case OPCODE::JOIN: processJOIN(code_gen); break;
  908. case OPCODE::SPLIT: processSPLIT(code_gen); break;
  909. case OPCODE::BLINK: WriteTodo(code_gen, md.GetEntityName(), "BLINK"); break;
  910. case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::EYETX:
  911. WriteTodo(code_gen, md.GetEntityName(), "EYETX");
  912. break;
  913. case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::TRNSP:
  914. WriteTodo(code_gen, md.GetEntityName(), "TRNSP");
  915. break;
  916. case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::AMBNT:
  917. WriteTodo(code_gen, md.GetEntityName(), "AMBNT");
  918. break;
  919. case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::Unknown03:
  920. WriteTodo(code_gen, md.GetEntityName(), "Unknown03");
  921. break;
  922. case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::Unknown04:
  923. WriteTodo(code_gen, md.GetEntityName(), "Unknown04");
  924. break;
  925. case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::Unknown05:
  926. WriteTodo(code_gen, md.GetEntityName(), "Unknown05");
  927. break;
  928. case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::LIGHT:
  929. WriteTodo(code_gen, md.GetEntityName(), "LIGHT");
  930. break;
  931. case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::Unknown07:
  932. WriteTodo(code_gen, md.GetEntityName(), "Unknown07");
  933. break;
  934. case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::Unknown08:
  935. WriteTodo(code_gen, md.GetEntityName(), "Unknown08");
  936. break;
  937. case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::Unknown09:
  938. WriteTodo(code_gen, md.GetEntityName(), "Unknown09");
  939. break;
  940. case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::SBOBJ:
  941. WriteTodo(code_gen, md.GetEntityName(), "SBOBJ");
  942. break;
  943. case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::Unknown0B:
  944. WriteTodo(code_gen, md.GetEntityName(), "Unknown0B");
  945. break;
  946. case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::Unknown0C:
  947. WriteTodo(code_gen, md.GetEntityName(), "Unknown0C");
  948. break;
  949. case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::SHINE:
  950. WriteTodo(code_gen, md.GetEntityName(), "SHINE");
  951. break;
  952. case (OPCODE::KAWAI << 8) | OPCODE_KAWAI::RESET:
  953. WriteTodo(code_gen, md.GetEntityName(), "RESET");
  954. break;
  955. case OPCODE::KAWIW: WriteTodo(code_gen, md.GetEntityName(), "KAWIW"); break;
  956. case OPCODE::PMOVA: WriteTodo(code_gen, md.GetEntityName(), "PMOVA"); break;
  957. case OPCODE::PDIRA: WriteTodo(code_gen, md.GetEntityName(), "PDIRA"); break;
  958. case OPCODE::PTURA: WriteTodo(code_gen, md.GetEntityName(), "PTURA"); break;
  959. case OPCODE::PGTDR: WriteTodo(code_gen, md.GetEntityName(), "PGTDR"); break;
  960. case OPCODE::PXYZI: WriteTodo(code_gen, md.GetEntityName(), "PXYZI"); break;
  961. case OPCODE::TLKON: processTLKON(code_gen, md.GetEntityName()); break;
  962. case OPCODE::PC: processPC(code_gen, md.GetEntityName()); break;
  963. case OPCODE::opCodeCHAR: processCHAR(code_gen, md.GetEntityName()); break;
  964. case OPCODE::DFANM: processDFANM(code_gen, md.GetEntityName(), md.GetCharacterId()); break;
  965. case OPCODE::ANIME1: processANIME1(code_gen, md.GetEntityName(), md.GetCharacterId()); break;
  966. case OPCODE::VISI: processVISI(code_gen, md.GetEntityName()); break;
  967. case OPCODE::XYZI: processXYZI(code_gen, md.GetEntityName()); break;
  968. case OPCODE::XYI: WriteTodo(code_gen, md.GetEntityName(), "XYI"); break;
  969. case OPCODE::XYZ: WriteTodo(code_gen, md.GetEntityName(), "XYZ"); break;
  970. case OPCODE::MOVE: processMOVE(code_gen, md.GetEntityName()); break;
  971. case OPCODE::CMOVE: WriteTodo(code_gen, md.GetEntityName(), "CMOVE"); break;
  972. case OPCODE::MOVA: WriteTodo(code_gen, md.GetEntityName(), "MOVA"); break;
  973. case OPCODE::TURA: WriteTodo(code_gen, md.GetEntityName(), "TURA"); break;
  974. case OPCODE::ANIMW: WriteTodo(code_gen, md.GetEntityName(), "ANIMW"); break;
  975. case OPCODE::FMOVE: WriteTodo(code_gen, md.GetEntityName(), "FMOVE"); break;
  976. case OPCODE::ANIME2: WriteTodo(code_gen, md.GetEntityName(), "ANIME2"); break;
  977. case OPCODE::ANIM_1: WriteTodo(code_gen, md.GetEntityName(), "ANIM_1"); break;
  978. case OPCODE::CANIM1: WriteTodo(code_gen, md.GetEntityName(), "CANIM1"); break;
  979. case OPCODE::CANM_1: WriteTodo(code_gen, md.GetEntityName(), "CANM_1"); break;
  980. case OPCODE::MSPED: processMSPED(code_gen, md.GetEntityName()); break;
  981. case OPCODE::DIR: processDIR(code_gen, md.GetEntityName()); break;
  982. case OPCODE::TURNGEN: processTURNGEN(code_gen, md.GetEntityName()); break;
  983. case OPCODE::TURN: WriteTodo(code_gen, md.GetEntityName(), "TURN"); break;
  984. case OPCODE::DIRA: WriteTodo(code_gen, md.GetEntityName(), "DIRA"); break;
  985. case OPCODE::GETDIR: WriteTodo(code_gen, md.GetEntityName(), "GETDIR"); break;
  986. case OPCODE::GETAXY: WriteTodo(code_gen, md.GetEntityName(), "GETAXY"); break;
  987. case OPCODE::GETAI: processGETAI(code_gen, eng); break;
  988. case OPCODE::ANIM_2: processANIM_2(code_gen, md.GetEntityName(), md.GetCharacterId()); break;
  989. case OPCODE::CANIM2: processCANIM2(code_gen, md.GetEntityName(), md.GetCharacterId()); break;
  990. case OPCODE::CANM_2: processCANM_2(code_gen, md.GetEntityName(), md.GetCharacterId()); break;
  991. case OPCODE::ASPED: WriteTodo(code_gen, md.GetEntityName(), "ASPED"); break;
  992. case OPCODE::CC: processCC(code_gen, eng); break;
  993. case OPCODE::JUMP: processJUMP(code_gen, md.GetEntityName()); break;
  994. case OPCODE::AXYZI: processAXYZI(code_gen); break;
  995. case OPCODE::LADER: processLADER(code_gen, md.GetEntityName()); break;
  996. case OPCODE::OFST: processOFST(code_gen, md.GetEntityName()); break;
  997. case OPCODE::OFSTW:
  998. code_gen->AddOutputLine("self." + md.GetEntityName() + ":offset_sync()");
  999. break;
  1000. case OPCODE::TALKR: WriteTodo(code_gen, md.GetEntityName(), "TALKR"); break;
  1001. case OPCODE::SLIDR: WriteTodo(code_gen, md.GetEntityName(), "SLIDR"); break;
  1002. case OPCODE::SOLID: processSOLID(code_gen, md.GetEntityName()); break;
  1003. case OPCODE::TLKR2: WriteTodo(code_gen, md.GetEntityName(), "TLKR2"); break;
  1004. case OPCODE::SLDR2: WriteTodo(code_gen, md.GetEntityName(), "SLDR2"); break;
  1005. case OPCODE::CCANM: WriteTodo(code_gen, md.GetEntityName(), "CCANM"); break;
  1006. case OPCODE::FCFIX: WriteTodo(code_gen, md.GetEntityName(), "FCFIX"); break;
  1007. case OPCODE::ANIMB: WriteTodo(code_gen, md.GetEntityName(), "ANIMB"); break;
  1008. case OPCODE::TURNW: WriteTodo(code_gen, md.GetEntityName(), "TURNW"); break;
  1009. default:
  1010. code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented(
  1011. md.GetEntityName(), _address, _opcode
  1012. ));
  1013. }
  1014. }
  1015. void FF7ModelInstruction::processJOIN(CodeGenerator* code_gen){
  1016. code_gen->AddOutputLine("join_party(" + std::to_string(_params[0]->getUnsigned()) + ")");
  1017. }
  1018. void FF7ModelInstruction::processSPLIT(CodeGenerator* code_gen){
  1019. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1020. const float scale = 128.0f * cg->GetScaleFactor();
  1021. const auto& ax = FieldCodeGenerator::FormatValueOrVariable(
  1022. cg->GetFormatter(), _params[0]->getUnsigned(), _params[6]->getSigned(),
  1023. FieldCodeGenerator::ValueType::Float, scale
  1024. );
  1025. const auto& ay = FieldCodeGenerator::FormatValueOrVariable(
  1026. cg->GetFormatter(), _params[1]->getUnsigned(), _params[7]->getSigned(),
  1027. FieldCodeGenerator::ValueType::Float, scale
  1028. );
  1029. const auto& ar = FieldCodeGenerator::FormatValueOrVariable(
  1030. cg->GetFormatter(), _params[2]->getUnsigned(), _params[8]->getSigned(),
  1031. FieldCodeGenerator::ValueType::Float, scale
  1032. );
  1033. const auto& bx = FieldCodeGenerator::FormatValueOrVariable(
  1034. cg->GetFormatter(), _params[3]->getUnsigned(), _params[9]->getSigned(),
  1035. FieldCodeGenerator::ValueType::Float, scale
  1036. );
  1037. const auto& by = FieldCodeGenerator::FormatValueOrVariable(
  1038. cg->GetFormatter(), _params[4]->getUnsigned(), _params[10]->getSigned(),
  1039. FieldCodeGenerator::ValueType::Float, scale
  1040. );
  1041. const auto& br = FieldCodeGenerator::FormatValueOrVariable(
  1042. cg->GetFormatter(), _params[5]->getUnsigned(), _params[11]->getSigned(),
  1043. FieldCodeGenerator::ValueType::Float, scale
  1044. );
  1045. const auto& speed = _params[12]->getUnsigned();
  1046. code_gen->AddOutputLine((
  1047. boost::format("split_party(%1%, %2%, %3%, %4%, %5%, %6%, %7%)")
  1048. % ax % ay % ar % bx % by % br % speed
  1049. ).str());
  1050. }
  1051. void FF7ModelInstruction::processTLKON(CodeGenerator* code_gen, const std::string& entity){
  1052. code_gen->AddOutputLine((
  1053. boost::format("self.%1%:set_talkable(%2%)")
  1054. % entity % FieldCodeGenerator::FormatInvertedBool(_params[0]->getUnsigned())
  1055. ).str());
  1056. }
  1057. void FF7ModelInstruction::processPC(CodeGenerator* code_gen, const std::string& entity){
  1058. code_gen->AddOutputLine(
  1059. (boost::format("set_entity_to_character(\"%1%\", \"%1%\")") % entity).str()
  1060. );
  1061. }
  1062. void FF7ModelInstruction::processCHAR(CodeGenerator* code_gen, const std::string& entity){
  1063. code_gen->AddOutputLine(
  1064. (boost::format("self.%1% = entity_manager:get_entity(\"%1%\")") % entity).str()
  1065. );
  1066. }
  1067. void FF7ModelInstruction::processDFANM(
  1068. CodeGenerator* code_gen, const std::string& entity, int char_id
  1069. ){
  1070. // ID will be fixed-up downstream.
  1071. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1072. auto animation_id = _params[0]->getUnsigned();
  1073. // TODO: check for zero.
  1074. auto speed = 1.0f / _params[1]->getUnsigned();
  1075. code_gen->AddOutputLine((
  1076. boost::format("self.%1%:set_default_animation(\"%2%\") -- speed %3%")
  1077. % entity % cg->GetFormatter().AnimationName(char_id, animation_id) % speed
  1078. ).str());
  1079. code_gen->AddOutputLine((
  1080. boost::format("self.%1%:play_animation(\"%2%\")")
  1081. % entity % cg->GetFormatter().AnimationName(char_id, animation_id)
  1082. ).str());
  1083. }
  1084. void FF7ModelInstruction::processANIME1(
  1085. CodeGenerator* code_gen, const std::string& entity, int char_id
  1086. ){
  1087. // ID will be fixed-up downstream.
  1088. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1089. auto animation_id = _params[0]->getUnsigned();
  1090. // TODO: check for zero.
  1091. auto speed = 1.0f / _params[1]->getUnsigned();
  1092. code_gen->AddOutputLine((
  1093. boost::format("self.%1%:play_animation(\"%2%\") -- speed %3%")
  1094. % entity % cg->GetFormatter().AnimationName(char_id, animation_id) % speed
  1095. ).str());
  1096. code_gen->AddOutputLine((boost::format("self.%1%:animation_sync()") % entity).str());
  1097. }
  1098. void FF7ModelInstruction::processVISI(CodeGenerator* code_gen, const std::string& entity){
  1099. code_gen->AddOutputLine((
  1100. boost::format("self.%1%:set_visible(%2%)")
  1101. % entity % FieldCodeGenerator::FormatBool(_params[0]->getUnsigned())
  1102. ).str());
  1103. }
  1104. void FF7ModelInstruction::processXYZI(CodeGenerator* code_gen, const std::string& entity){
  1105. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1106. const float scale = 128.0f * cg->GetScaleFactor();
  1107. const auto& x = FieldCodeGenerator::FormatValueOrVariable(
  1108. cg->GetFormatter(), _params[0]->getUnsigned(), _params[4]->getSigned(),
  1109. FieldCodeGenerator::ValueType::Float, scale
  1110. );
  1111. const auto& y = FieldCodeGenerator::FormatValueOrVariable(
  1112. cg->GetFormatter(), _params[1]->getUnsigned(), _params[5]->getSigned(),
  1113. FieldCodeGenerator::ValueType::Float, scale
  1114. );
  1115. const auto& z = FieldCodeGenerator::FormatValueOrVariable(
  1116. cg->GetFormatter(), _params[2]->getUnsigned(), _params[6]->getSigned(),
  1117. FieldCodeGenerator::ValueType::Float, scale
  1118. );
  1119. const auto& triangle_id = FieldCodeGenerator::FormatValueOrVariable(
  1120. cg->GetFormatter(), _params[3]->getUnsigned(), _params[7]->getUnsigned()
  1121. );
  1122. code_gen->AddOutputLine((
  1123. boost::format("self.%1%:set_position(%2%, %3%, %4%) -- triangle ID %5%")
  1124. % entity % x % y % z % triangle_id
  1125. ).str());
  1126. }
  1127. void FF7ModelInstruction::processMOVE(CodeGenerator* code_gen, const std::string& entity){
  1128. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1129. const float scale = 128.0f * cg->GetScaleFactor();
  1130. const auto& x = FieldCodeGenerator::FormatValueOrVariable(
  1131. cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getSigned(),
  1132. FieldCodeGenerator::ValueType::Float, scale
  1133. );
  1134. const auto& y = FieldCodeGenerator::FormatValueOrVariable(
  1135. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getSigned(),
  1136. FieldCodeGenerator::ValueType::Float, scale
  1137. );
  1138. code_gen->AddOutputLine(
  1139. (boost::format("self.%1%:move_to_position(%2%, %3%)") % entity % x % y).str()
  1140. );
  1141. code_gen->AddOutputLine((boost::format("self.%1%:move_sync()") % entity).str());
  1142. }
  1143. void FF7ModelInstruction::processMSPED(CodeGenerator* code_gen, const std::string& entity){
  1144. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1145. const float scale = 128.0f * cg->GetScaleFactor();
  1146. const auto& speed = FieldCodeGenerator::FormatValueOrVariable(
  1147. cg->GetFormatter(), _params[1]->getUnsigned(), _params[2]->getUnsigned(),
  1148. FieldCodeGenerator::ValueType::Float, 256.0f * scale / 30.0f
  1149. );
  1150. code_gen->AddOutputLine(
  1151. (boost::format("self.%1%:set_move_auto_speed(%2%)") % entity % speed).str()
  1152. );
  1153. }
  1154. void FF7ModelInstruction::processDIR(CodeGenerator* code_gen, const std::string& entity){
  1155. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1156. const auto& degrees = FieldCodeGenerator::FormatValueOrVariable(
  1157. cg->GetFormatter(), _params[0]->getUnsigned(), _params[1]->getUnsigned(),
  1158. FieldCodeGenerator::ValueType::Float, 256.0f / 360.0f
  1159. );
  1160. code_gen->AddOutputLine((boost::format("self.%1%:set_rotation(%2%)") % entity % degrees).str());
  1161. }
  1162. void FF7ModelInstruction::processTURNGEN(CodeGenerator* code_gen, const std::string& entity){
  1163. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1164. const auto& degrees = FieldCodeGenerator::FormatValueOrVariable(
  1165. cg->GetFormatter(), _params[1]->getUnsigned(), _params[2]->getUnsigned(),
  1166. FieldCodeGenerator::ValueType::Float, 256.0f / 360.0f
  1167. );
  1168. std::string direction;
  1169. switch (_params[3]->getUnsigned()){
  1170. case 0: direction = "Entity.CLOCKWISE"; break;
  1171. case 1: direction = "Entity.ANTICLOCKWISE"; break;
  1172. case 2: direction = "Entity.CLOSEST"; break;
  1173. // Default to closest:
  1174. default: direction = "Entity.CLOSEST"; break;
  1175. }
  1176. auto steps = _params[4]->getUnsigned();
  1177. std::string step_type;
  1178. switch (_params[5]->getUnsigned()){
  1179. case 1: step_type = "Entity.LINEAR"; break;
  1180. case 2: step_type = "Entity.SMOOTH"; break;
  1181. // Default to smooth
  1182. default: step_type = "Entity.SMOOTH"; break;
  1183. }
  1184. const float scaled_steps = static_cast<float>(steps) / 30.0f;
  1185. code_gen->AddOutputLine((
  1186. boost::format("self.%1%:turn_to_direction(%2%, %3%, %4%, %5%)")
  1187. % entity % degrees % direction % step_type % scaled_steps
  1188. ).str());
  1189. code_gen->AddOutputLine((boost::format("self.%1%:turn_sync()") % entity).str());
  1190. }
  1191. void FF7ModelInstruction::processGETAI(CodeGenerator* code_gen, const FF7FieldEngine& engine){
  1192. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1193. // TODO: check for assignment to literal.
  1194. const auto& variable = FieldCodeGenerator::FormatValueOrVariable(
  1195. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned()
  1196. );
  1197. const auto& entity = engine.EntityByIndex(_params[2]->getUnsigned());
  1198. code_gen->AddOutputLine(
  1199. (boost::format("%1% = entity_manager:get_entity(\"%2%\"):get_move_triangle_id()")
  1200. % variable % entity.Name()
  1201. ).str());
  1202. }
  1203. void FF7ModelInstruction::processANIM_2(
  1204. CodeGenerator* code_gen, const std::string& entity, int char_id
  1205. ){
  1206. // ID will be fixed-up downstream.
  1207. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1208. auto animation_id = _params[0]->getUnsigned();
  1209. // TODO: check for zero.
  1210. auto speed = 1.0f / _params[1]->getUnsigned();
  1211. code_gen->AddOutputLine((
  1212. boost::format("self.%1%:play_animation_stop(\"%2%\") -- speed %3%")
  1213. % entity % cg->GetFormatter().AnimationName(char_id, animation_id) % speed
  1214. ).str());
  1215. code_gen->AddOutputLine((boost::format("self.%1%:animation_sync()") % entity).str());
  1216. }
  1217. void FF7ModelInstruction::processCANIM2(
  1218. CodeGenerator* code_gen, const std::string& entity, int char_id
  1219. ){
  1220. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1221. auto animation_id = _params[0]->getUnsigned();
  1222. auto start_frame = _params[1]->getUnsigned() / 30.0f;
  1223. auto end_frame = _params[2]->getUnsigned() / 30.0f;
  1224. // TODO: check for zero.
  1225. auto speed = 1.0f / _params[3]->getUnsigned();
  1226. code_gen->AddOutputLine((
  1227. boost::format("self.%1%:play_animation(\"%2%\", %3%, %4%) -- speed %5%")
  1228. % entity % cg->GetFormatter().AnimationName(char_id, animation_id)
  1229. % start_frame % end_frame % speed
  1230. ).str());
  1231. code_gen->AddOutputLine((boost::format("self.%1%:animation_sync()") % entity).str());
  1232. }
  1233. void FF7ModelInstruction::processCANM_2(
  1234. CodeGenerator* code_gen, const std::string& entity, int char_id
  1235. ){
  1236. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1237. // ID will be fixed-up downstream.
  1238. auto animation_id = _params[0]->getUnsigned();
  1239. auto start_frame = _params[1]->getUnsigned() / 30.0f;
  1240. auto end_frame = _params[2]->getUnsigned() / 30.0f;
  1241. // TODO: check for zero.
  1242. auto speed = 1.0f / _params[3]->getUnsigned();
  1243. code_gen->AddOutputLine((
  1244. boost::format("self.%1%:play_animation_stop(\"%2%\", %3%, %4%) -- speed %5%")
  1245. % entity % cg->GetFormatter().AnimationName(char_id, animation_id)
  1246. % start_frame % end_frame % speed
  1247. ).str());
  1248. code_gen->AddOutputLine((boost::format("self.%1%:animation_sync()") % entity).str());
  1249. }
  1250. void FF7ModelInstruction::processCC(CodeGenerator* code_gen, const FF7FieldEngine& engine){
  1251. const auto& entity = engine.EntityByIndex(_params[0]->getUnsigned());
  1252. code_gen->AddOutputLine(
  1253. (boost::format("entity_manager:set_player_entity(\"%1%\")") % entity.Name()).str()
  1254. );
  1255. }
  1256. void FF7ModelInstruction::processJUMP(CodeGenerator* code_gen, const std::string& entity){
  1257. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1258. const float scale = 128.0f * cg->GetScaleFactor();
  1259. float x = std::stof(FieldCodeGenerator::FormatValueOrVariable(
  1260. cg->GetFormatter(), _params[0]->getUnsigned(), _params[4]->getSigned(),
  1261. FieldCodeGenerator::ValueType::Float, scale
  1262. ));
  1263. float y = std::stof(FieldCodeGenerator::FormatValueOrVariable(
  1264. cg->GetFormatter(), _params[1]->getUnsigned(), _params[5]->getSigned(),
  1265. FieldCodeGenerator::ValueType::Float, scale
  1266. ));
  1267. int i = atoi(FieldCodeGenerator::FormatValueOrVariable(
  1268. cg->GetFormatter(), _params[2]->getUnsigned(), _params[6]->getSigned()
  1269. ).c_str());
  1270. int steps = atoi(FieldCodeGenerator::FormatValueOrVariable(
  1271. cg->GetFormatter(), _params[3]->getUnsigned(), _params[7]->getSigned()
  1272. ).c_str());
  1273. //x *= 0.00781250273224;
  1274. //y *= 0.00781250273224;
  1275. // Always force reduce steps and bound it between 1 and 5.
  1276. steps /= 6;
  1277. if (steps < 1) steps = 1;
  1278. if (steps > 5) steps = 5;
  1279. // TODO: Z hardcoded as -1, handled in Entity::ScriptJumpToPosition.
  1280. // TODO: Hardcoded 0.5 in seconds. Calculate using distance.
  1281. code_gen->AddOutputLine((
  1282. boost::format("self.%1%:jump_to_position(%2%, %3%, -1, 0.5, %4%) -- %5% steps.")
  1283. % entity % x % y % i % steps
  1284. ).str());
  1285. code_gen->AddOutputLine((boost::format("self.%1%:jump_sync()") % entity).str());
  1286. }
  1287. void FF7ModelInstruction::processAXYZI(CodeGenerator* code_gen){
  1288. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1289. const float scale = 128.0f * cg->GetScaleFactor();
  1290. code_gen->AddOutputLine((
  1291. boost::format("axyzi(%1%, %2%, %3%, %4%, %5%, %6%, %7%, %8%, %9%, %10%)")
  1292. % _params[0]->getSigned() % _params[1]->getSigned() % _params[2]->getSigned()
  1293. % _params[3]->getSigned() % _params[4]->getSigned() % _params[5]->getSigned()
  1294. % _params[6]->getSigned() % _params[7]->getSigned() % _params[8]->getSigned()
  1295. % scale
  1296. ).str());
  1297. }
  1298. void FF7ModelInstruction::processLADER(CodeGenerator* code_gen, const std::string& entity){
  1299. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1300. const float scale = 128.0f * cg->GetScaleFactor();
  1301. const auto& x = FieldCodeGenerator::FormatValueOrVariable(
  1302. cg->GetFormatter(), _params[0]->getUnsigned(), _params[4]->getSigned(),
  1303. FieldCodeGenerator::ValueType::Float, scale
  1304. );
  1305. const auto& y = FieldCodeGenerator::FormatValueOrVariable(
  1306. cg->GetFormatter(), _params[1]->getUnsigned(), _params[5]->getSigned(),
  1307. FieldCodeGenerator::ValueType::Float, scale
  1308. );
  1309. const auto& z = FieldCodeGenerator::FormatValueOrVariable(
  1310. cg->GetFormatter(), _params[2]->getUnsigned(), _params[6]->getSigned(),
  1311. FieldCodeGenerator::ValueType::Float, scale
  1312. );
  1313. uint end_triangle = atoi(FieldCodeGenerator::FormatValueOrVariable(
  1314. cg->GetFormatter(), _params[3]->getUnsigned(), _params[7]->getUnsigned()
  1315. ).c_str());
  1316. uint keys = _params[8]->getUnsigned();
  1317. uint animation = _params[9]->getUnsigned();
  1318. //float orientation = _params[10]->getUnsigned() / (256.0f / 360.0f);
  1319. const auto& orientation = FieldCodeGenerator::FormatValueOrVariable(
  1320. cg->GetFormatter(), 0, _params[10]->getSigned(),
  1321. FieldCodeGenerator::ValueType::Float, 256.0f / 360.0f
  1322. );
  1323. uint speed = _params[11]->getUnsigned();
  1324. // TODO: Animation hardcoded as "btce".
  1325. // TODO: Orientation and speed not set.
  1326. code_gen->AddOutputLine((
  1327. boost::format(
  1328. "self.%1%:linear_to_position(%2%, %3%, %4%, %5%, \"Climb\", %6%, %7%) "
  1329. "-- Animation %8% -- Speed %9%"
  1330. ) % entity % x % y % z % keys % orientation % end_triangle % animation % speed
  1331. ).str());
  1332. code_gen->AddOutputLine((boost::format("self.%1%:linear_sync()") % entity).str());
  1333. }
  1334. void FF7ModelInstruction::processSOLID(CodeGenerator* code_gen, const std::string& entity){
  1335. code_gen->AddOutputLine((
  1336. boost::format("self.%1%:set_solid(%2%)")
  1337. % entity % FieldCodeGenerator::FormatInvertedBool(_params[0]->getUnsigned())
  1338. ).str());
  1339. }
  1340. void FF7ModelInstruction::processOFST(CodeGenerator* code_gen, const std::string& entity){
  1341. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1342. const float scale = 128.0f * cg->GetScaleFactor();
  1343. float x = std::stof(FieldCodeGenerator::FormatValueOrVariable(
  1344. cg->GetFormatter(), _params[0]->getUnsigned(), _params[5]->getSigned(),
  1345. FieldCodeGenerator::ValueType::Float, scale
  1346. ));
  1347. float y = std::stof(FieldCodeGenerator::FormatValueOrVariable(
  1348. cg->GetFormatter(), _params[1]->getUnsigned(),_params[6]->getSigned(),
  1349. FieldCodeGenerator::ValueType::Float, scale
  1350. ));
  1351. float z = std::stof(FieldCodeGenerator::FormatValueOrVariable(
  1352. cg->GetFormatter(), _params[2]->getUnsigned(), _params[7]->getSigned(),
  1353. FieldCodeGenerator::ValueType::Float, scale
  1354. ));
  1355. float speed = std::stof(FieldCodeGenerator::FormatValueOrVariable(
  1356. cg->GetFormatter(), _params[3]->getUnsigned(), _params[8]->getUnsigned()
  1357. ));
  1358. // Spatial coordinates need to be scaled down.
  1359. // TODO: This number is empirically deducted. Why this number?
  1360. //x *= 0.00390f;
  1361. //y *= 0.00390f;
  1362. //z *= 0.00390f;
  1363. // Speed needs to be scaled down by the frame rate.
  1364. speed /= 30.0f;
  1365. code_gen->AddOutputLine((
  1366. boost::format("self.%1%:offset_to_position(%2%, %3%, %4%, %5%, %6%)")
  1367. % entity % x % y % z
  1368. % (_params[4]->getUnsigned() ? "Entity.SMOOTH" : "Entity.LINEAR")% speed
  1369. ).str());
  1370. }
  1371. void FF7WalkmeshInstruction::processInst(
  1372. Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen
  1373. ){
  1374. FunctionMetaData md(func._metadata);
  1375. switch (_opcode){
  1376. case OPCODE::SLIP: WriteTodo(code_gen, md.GetEntityName(), "SLIP"); break;
  1377. case OPCODE::UC: processUC(code_gen); break;
  1378. case OPCODE::IDLCK:
  1379. // Triangle id, on or off
  1380. code_gen->AddOutputLine(
  1381. (boost::format("walkmesh:lock_walkmesh(%1%, %2%)")
  1382. % _params[0]->getUnsigned()
  1383. % FieldCodeGenerator::FormatBool(_params[1]->getUnsigned())).str());
  1384. break;
  1385. case OPCODE::LINE: processLINE(code_gen, md.GetEntityName()); break;
  1386. case OPCODE::LINON: WriteTodo(code_gen, md.GetEntityName(), "LINON"); break;
  1387. case OPCODE::SLINE: WriteTodo(code_gen, md.GetEntityName(), "SLINE"); break;
  1388. default:
  1389. code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented(
  1390. md.GetEntityName(), _address, _opcode
  1391. ));
  1392. }
  1393. }
  1394. void FF7WalkmeshInstruction::processUC(CodeGenerator* code_gen){
  1395. code_gen->AddOutputLine((
  1396. boost::format("entity_manager:player_lock(%1%)")
  1397. % FieldCodeGenerator::FormatBool(_params[0]->getUnsigned())
  1398. ).str());
  1399. }
  1400. void FF7WalkmeshInstruction::processLINE(CodeGenerator* code_gen, const std::string& entity){
  1401. float xa = _params[0]->getSigned();
  1402. float ya = _params[1]->getSigned();
  1403. float za = _params[2]->getSigned();
  1404. float xb = _params[3]->getSigned();
  1405. float yb = _params[4]->getSigned();
  1406. float zb = _params[5]->getSigned();
  1407. // Scale down. TODO: Why this number?
  1408. xa *= 0.00781249709639;
  1409. ya *= 0.00781249709639;
  1410. za *= 0.00781249709639;
  1411. xb *= 0.00781249709639;
  1412. yb *= 0.00781249709639;
  1413. zb *= 0.00781249709639;
  1414. // Just print as a comment.
  1415. code_gen->AddOutputLine(
  1416. "-- LINE (" + std::to_string(xa) + ", " + std::to_string(ya) + ", " + std::to_string(za)
  1417. + ")-(" + std::to_string(xb) + ", " +std::to_string(yb) + ", " + std::to_string(zb) + ")"
  1418. );
  1419. }
  1420. void FF7BackgroundInstruction::processInst(
  1421. Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen
  1422. ){
  1423. FunctionMetaData md(func._metadata);
  1424. switch (_opcode){
  1425. case OPCODE::BGPDH: WriteTodo(code_gen, md.GetEntityName(), "BGPDH");break;
  1426. case OPCODE::BGSCR: WriteTodo(code_gen, md.GetEntityName(), "BGSCR"); break;
  1427. case OPCODE::MPPAL: WriteTodo(code_gen, md.GetEntityName(), "MPPAL"); break;
  1428. case OPCODE::BGON: processBGON(code_gen); break;
  1429. case OPCODE::BGOFF: processBGOFF(code_gen); break;
  1430. case OPCODE::BGROL: WriteTodo(code_gen, md.GetEntityName(), "BGROL"); break;
  1431. case OPCODE::BGROL2: WriteTodo(code_gen, md.GetEntityName(), "BGROL2"); break;
  1432. case OPCODE::BGCLR: processBGCLR(code_gen); break;
  1433. case OPCODE::STPAL: processSTPAL(code_gen); break;
  1434. case OPCODE::LDPAL: processLDPAL(code_gen); break;
  1435. case OPCODE::CPPAL: processCPPAL(code_gen); break;
  1436. case OPCODE::RTPAL: WriteTodo(code_gen, md.GetEntityName(), "RTPAL"); break;
  1437. case OPCODE::ADPAL: processADPAL(code_gen); break;
  1438. case OPCODE::MPPAL2: processMPPAL2(code_gen); break;
  1439. case OPCODE::STPLS: processSTPLS(code_gen); break;
  1440. case OPCODE::LDPLS: processLDPLS(code_gen); break;
  1441. case OPCODE::CPPAL2: WriteTodo(code_gen, md.GetEntityName(), "CPPAL2"); break;
  1442. case OPCODE::ADPAL2: WriteTodo(code_gen, md.GetEntityName(), "ADPAL2"); break;
  1443. case OPCODE::RTPAL2: WriteTodo(code_gen, md.GetEntityName(), "RTPAL2"); break;
  1444. default:
  1445. code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented(
  1446. md.GetEntityName(), _address, _opcode
  1447. ));
  1448. }
  1449. }
  1450. void FF7BackgroundInstruction::processBGON(CodeGenerator* code_gen){
  1451. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1452. const auto& background_id = FieldCodeGenerator::FormatValueOrVariable(
  1453. cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned()
  1454. );
  1455. const auto& layer_id = FieldCodeGenerator::FormatValueOrVariable(
  1456. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned()
  1457. );
  1458. code_gen->AddOutputLine(
  1459. (boost::format("-- field:background_on(%1%, %2%)") % background_id % layer_id).str()
  1460. );
  1461. }
  1462. void FF7BackgroundInstruction::processBGOFF(CodeGenerator* code_gen){
  1463. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1464. const auto& background_id = FieldCodeGenerator::FormatValueOrVariable(
  1465. cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned()
  1466. );
  1467. const auto& layer_id = FieldCodeGenerator::FormatValueOrVariable(
  1468. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned()
  1469. );
  1470. code_gen->AddOutputLine(
  1471. (boost::format("-- field:background_off(%1%, %2%)") % background_id % layer_id).str()
  1472. );
  1473. }
  1474. void FF7BackgroundInstruction::processBGCLR(CodeGenerator* code_gen){
  1475. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1476. const auto& background_id = FieldCodeGenerator::FormatValueOrVariable(
  1477. cg->GetFormatter(), _params[1]->getUnsigned(), _params[2]->getUnsigned()
  1478. );
  1479. code_gen->AddOutputLine(
  1480. (boost::format("-- field:background_clear(%1%)") % background_id).str()
  1481. );
  1482. }
  1483. void FF7BackgroundInstruction::processSTPAL(CodeGenerator* code_gen){
  1484. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1485. const auto& source = FieldCodeGenerator::FormatValueOrVariable(
  1486. cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned()
  1487. );
  1488. const auto& destination = FieldCodeGenerator::FormatValueOrVariable(
  1489. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned()
  1490. );
  1491. auto num_entries = _params[4]->getUnsigned() + 1;
  1492. code_gen->AddOutputLine((
  1493. boost::format("-- store palette %1% to position %2%, start CLUT index 0, %3% entries")
  1494. % source % destination % num_entries
  1495. ).str());
  1496. }
  1497. void FF7BackgroundInstruction::processLDPAL(CodeGenerator* code_gen){
  1498. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1499. const auto& source = FieldCodeGenerator::FormatValueOrVariable(
  1500. cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned()
  1501. );
  1502. const auto& destination = FieldCodeGenerator::FormatValueOrVariable(
  1503. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned()
  1504. );
  1505. auto num_entries = _params[4]->getUnsigned() + 1;
  1506. code_gen->AddOutputLine((
  1507. boost::format("-- load palette %2% from position %1%, start CLUT index 0, %3% entries")
  1508. % source % destination % num_entries
  1509. ).str());
  1510. }
  1511. void FF7BackgroundInstruction::processCPPAL(CodeGenerator* code_gen){
  1512. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1513. const auto& source = FieldCodeGenerator::FormatValueOrVariable(
  1514. cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned()
  1515. );
  1516. const auto& destination = FieldCodeGenerator::FormatValueOrVariable(
  1517. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned()
  1518. );
  1519. auto num_entries = _params[4]->getUnsigned() + 1;
  1520. code_gen->AddOutputLine((
  1521. boost::format("-- copy palette %1% to palette %2%, %3% entries")
  1522. % source % destination % num_entries
  1523. ).str());
  1524. }
  1525. void FF7BackgroundInstruction::processADPAL(CodeGenerator* code_gen){
  1526. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1527. const auto& source = FieldCodeGenerator::FormatValueOrVariable(
  1528. cg->GetFormatter(), _params[0]->getUnsigned(), _params[6]->getUnsigned()
  1529. );
  1530. const auto& destination = FieldCodeGenerator::FormatValueOrVariable(
  1531. cg->GetFormatter(), _params[1]->getUnsigned(), _params[7]->getUnsigned()
  1532. );
  1533. const auto& r = FieldCodeGenerator::FormatValueOrVariable(
  1534. cg->GetFormatter(), _params[4]->getUnsigned(), _params[10]->getUnsigned()
  1535. );
  1536. const auto& g = FieldCodeGenerator::FormatValueOrVariable(
  1537. cg->GetFormatter(), _params[3]->getUnsigned(), _params[9]->getUnsigned()
  1538. );
  1539. const auto& b = FieldCodeGenerator::FormatValueOrVariable(
  1540. cg->GetFormatter(), _params[2]->getUnsigned(), _params[8]->getUnsigned()
  1541. );
  1542. auto num_entries = _params[11]->getUnsigned() + 1;
  1543. code_gen->AddOutputLine((
  1544. boost::format(
  1545. "-- add RGB(%3%, %4%, %5%) to %6% entries of palette stored at position %1%, "
  1546. "storing result in position %2%"
  1547. ) % source % destination % r % g % b % num_entries
  1548. ).str());
  1549. }
  1550. void FF7BackgroundInstruction::processMPPAL2(CodeGenerator* code_gen){
  1551. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1552. const auto& source = FieldCodeGenerator::FormatValueOrVariable(
  1553. cg->GetFormatter(), _params[0]->getUnsigned(), _params[6]->getUnsigned()
  1554. );
  1555. const auto& destination = FieldCodeGenerator::FormatValueOrVariable(
  1556. cg->GetFormatter(), _params[1]->getUnsigned(), _params[7]->getUnsigned()
  1557. );
  1558. const auto& r = FieldCodeGenerator::FormatValueOrVariable(
  1559. cg->GetFormatter(), _params[4]->getUnsigned(), _params[10]->getUnsigned()
  1560. );
  1561. const auto& g = FieldCodeGenerator::FormatValueOrVariable(
  1562. cg->GetFormatter(), _params[3]->getUnsigned(), _params[9]->getUnsigned()
  1563. );
  1564. const auto& b = FieldCodeGenerator::FormatValueOrVariable(
  1565. cg->GetFormatter(), _params[2]->getUnsigned(), _params[8]->getUnsigned()
  1566. );
  1567. auto num_entries = _params[11]->getUnsigned() + 1;
  1568. code_gen->AddOutputLine((
  1569. boost::format(
  1570. "-- multiply RGB(%3%, %4%, %5%) by %6% entries of palette stored at position %1%, "
  1571. "storing result in position %2%"
  1572. ) % source % destination % r % g % b % num_entries).str());
  1573. }
  1574. void FF7BackgroundInstruction::processSTPLS(CodeGenerator* code_gen){
  1575. auto source = _params[0]->getUnsigned();
  1576. auto destination = _params[1]->getUnsigned();
  1577. auto start_clut = _params[2]->getUnsigned();
  1578. auto num_entries = _params[3]->getUnsigned() + 1;
  1579. code_gen->AddOutputLine((
  1580. boost::format("-- store palette %1% to position %2%, start CLUT index %3%, %4% entries")
  1581. % source % destination % start_clut % num_entries
  1582. ).str());
  1583. }
  1584. void FF7BackgroundInstruction::processLDPLS(CodeGenerator* code_gen){
  1585. auto source = _params[0]->getUnsigned();
  1586. auto destination = _params[1]->getUnsigned();
  1587. auto startClut = _params[2]->getUnsigned();
  1588. auto num_entries = _params[3]->getUnsigned() + 1;
  1589. code_gen->AddOutputLine((
  1590. boost::format("-- load palette %2% from position %1%, start CLUT index %3%, %4% entries")
  1591. % source % destination % startClut % num_entries
  1592. ).str());
  1593. }
  1594. void FF7CameraInstruction::processInst(
  1595. Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen
  1596. ){
  1597. FunctionMetaData md(func._metadata);
  1598. switch (_opcode){
  1599. case OPCODE::NFADE: processNFADE(code_gen); break;
  1600. case OPCODE::SHAKE: WriteTodo(code_gen, md.GetEntityName(), "SHAKE"); break;
  1601. case OPCODE::SCRLO: WriteTodo(code_gen, md.GetEntityName(), "SCRLO"); break;
  1602. case OPCODE::SCRLC: WriteTodo(code_gen, md.GetEntityName(), "SCRLC"); break;
  1603. case OPCODE::SCR2D: processSCR2D(code_gen); break;
  1604. case OPCODE::SCRCC: WriteTodo(code_gen, md.GetEntityName(), "SCRCC"); break;
  1605. case OPCODE::SCR2DC: processSCR2DC(code_gen); break;
  1606. case OPCODE::SCRLW: WriteTodo(code_gen, md.GetEntityName(), "SCRLW"); break;
  1607. case OPCODE::SCR2DL: WriteTodo(code_gen, md.GetEntityName(), "SCR2DL"); break;
  1608. case OPCODE::VWOFT: WriteTodo(code_gen, md.GetEntityName(), "VWOFT"); break;
  1609. case OPCODE::FADE: processFADE(code_gen); break;
  1610. case OPCODE::FADEW: WriteTodo(code_gen, md.GetEntityName(), "FADEW"); break;
  1611. case OPCODE::SCRLP: WriteTodo(code_gen, md.GetEntityName(), "SCRLP"); break;
  1612. case OPCODE::MVCAM: WriteTodo(code_gen, md.GetEntityName(), "MVCAM"); break;
  1613. case OPCODE::SCRLA: WriteTodo(code_gen, md.GetEntityName(), "SCRLA"); break;
  1614. default:
  1615. code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented(
  1616. md.GetEntityName(), _address, _opcode
  1617. ));
  1618. }
  1619. }
  1620. void FF7CameraInstruction::processNFADE(CodeGenerator* code_gen){
  1621. // TODO: Not fully reversed.
  1622. auto raw_type = _params[4]->getUnsigned();
  1623. if (raw_type == 0){
  1624. code_gen->AddOutputLine("-- fade:clear()");
  1625. return;
  1626. }
  1627. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1628. const std::string type = raw_type == 12 ? "Fade.SUBTRACT" : "Fade.ADD";
  1629. const auto& r = FieldCodeGenerator::FormatValueOrVariable(
  1630. cg->GetFormatter(), _params[0]->getUnsigned(), _params[5]->getUnsigned()
  1631. );
  1632. const auto& g = FieldCodeGenerator::FormatValueOrVariable(
  1633. cg->GetFormatter(), _params[1]->getUnsigned(), _params[6]->getUnsigned()
  1634. );
  1635. const auto& b = FieldCodeGenerator::FormatValueOrVariable(
  1636. cg->GetFormatter(), _params[2]->getUnsigned(), _params[7]->getUnsigned()
  1637. );
  1638. const auto& unknown = FieldCodeGenerator::FormatValueOrVariable(
  1639. cg->GetFormatter(), _params[3]->getUnsigned(), _params[8]->getUnsigned()
  1640. );
  1641. code_gen->AddOutputLine(
  1642. (boost::format("-- fade:fade(%2%, %3%, %4%, %1%, %5%)") % type % r % g % b % unknown).str()
  1643. );
  1644. }
  1645. void FF7CameraInstruction::processSCR2D(CodeGenerator* code_gen){
  1646. // kUpScaler.
  1647. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1648. const auto& x = FieldCodeGenerator::FormatValueOrVariable(
  1649. cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getSigned()
  1650. );
  1651. const auto& y = FieldCodeGenerator::FormatValueOrVariable(
  1652. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getSigned()
  1653. );
  1654. code_gen->AddOutputLine((
  1655. boost::format("background2d:scroll_to_position(%1% * 3, %2% * 3, Background2D.NONE, 0)")
  1656. % x % y
  1657. ).str());
  1658. }
  1659. void FF7CameraInstruction::processSCR2DC(CodeGenerator* code_gen){
  1660. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1661. const auto& x = FieldCodeGenerator::FormatValueOrVariable(
  1662. cg->GetFormatter(), _params[0]->getUnsigned(), _params[4]->getSigned()
  1663. );
  1664. const auto& y = FieldCodeGenerator::FormatValueOrVariable(
  1665. cg->GetFormatter(), _params[1]->getUnsigned(), _params[5]->getSigned()
  1666. );
  1667. const auto& speed = FieldCodeGenerator::FormatValueOrVariable(
  1668. cg->GetFormatter(), _params[3]->getUnsigned(), _params[6]->getUnsigned(),
  1669. FieldCodeGenerator::ValueType::Float, 30.0f
  1670. );
  1671. code_gen->AddOutputLine((
  1672. boost::format("background2d:scroll_to_position(%1% * 3, %2% * 3, Background2D.SMOOTH, %3%)")
  1673. % x % y % speed
  1674. ).str());
  1675. }
  1676. void FF7CameraInstruction::processFADE(CodeGenerator* code_gen){
  1677. // TODO: not fully reversed
  1678. auto raw_type = _params[8]->getUnsigned();
  1679. std::string type;
  1680. switch (raw_type) {
  1681. case 1:
  1682. case 2:
  1683. case 7:
  1684. case 8:
  1685. type = "Fade.SUBTRACT";
  1686. break;
  1687. case 4: code_gen->AddOutputLine("-- fade:black()"); return;
  1688. default: type = "Fade.ADD"; break;
  1689. }
  1690. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1691. const auto& r = FieldCodeGenerator::FormatValueOrVariable(
  1692. cg->GetFormatter(), _params[0]->getUnsigned(), _params[4]->getUnsigned()
  1693. );
  1694. const auto& g = FieldCodeGenerator::FormatValueOrVariable(
  1695. cg->GetFormatter(), _params[1]->getUnsigned(), _params[5]->getUnsigned()
  1696. );
  1697. const auto& b = FieldCodeGenerator::FormatValueOrVariable(
  1698. cg->GetFormatter(), _params[2]->getUnsigned(), _params[6]->getUnsigned()
  1699. );
  1700. // TODO: needs to be divided by 30.0f?
  1701. auto speed = _params[7]->getUnsigned();
  1702. auto start = _params[9]->getUnsigned();
  1703. code_gen->AddOutputLine((
  1704. boost::format("-- fade:fade(%2%, %3%, %4%, %1%, %5%, %6%)") % type % r % g % b % speed % start
  1705. ).str());
  1706. }
  1707. void FF7AudioVideoInstruction::processInst(
  1708. Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen
  1709. ){
  1710. FunctionMetaData md(func._metadata);
  1711. switch (_opcode){
  1712. case OPCODE::BGMOVIE: WriteTodo(code_gen, md.GetEntityName(), "BGMOVIE"); break;
  1713. case OPCODE::AKAO2: processAKAO2(code_gen); break;
  1714. case OPCODE::MUSIC: processMUSIC(code_gen); break;
  1715. case OPCODE::SOUND: processSOUND(code_gen); break;
  1716. case OPCODE::AKAO: processAKAO(code_gen); break;
  1717. case OPCODE::MUSVT: WriteTodo(code_gen, md.GetEntityName(), "MUSVT"); break;
  1718. case OPCODE::MUSVM: WriteTodo(code_gen, md.GetEntityName(), "MUSVM"); break;
  1719. case OPCODE::MULCK: processMULCK(code_gen); break;
  1720. case OPCODE::BMUSC: WriteTodo(code_gen, md.GetEntityName(), "BMUSC"); break;
  1721. case OPCODE::CHMPH: WriteTodo(code_gen, md.GetEntityName(), "CHMPH"); break;
  1722. case OPCODE::PMVIE: processPMVIE(code_gen); break;
  1723. case OPCODE::MOVIE: processMOVIE(code_gen); break;
  1724. case OPCODE::MVIEF: processMVIEF(code_gen); break;
  1725. case OPCODE::FMUSC: WriteTodo(code_gen, md.GetEntityName(), "FMUSC"); break;
  1726. case OPCODE::CMUSC: WriteTodo(code_gen, md.GetEntityName(), "CMUSC"); break;
  1727. case OPCODE::CHMST: WriteTodo(code_gen, md.GetEntityName(), "CHMST"); break;
  1728. default:
  1729. code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented(
  1730. md.GetEntityName(), _address, _opcode
  1731. ));
  1732. }
  1733. }
  1734. void FF7AudioVideoInstruction::processAKAO2(CodeGenerator* code_gen){
  1735. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1736. const auto& param1 = FieldCodeGenerator::FormatValueOrVariable(
  1737. cg->GetFormatter(), _params[0]->getUnsigned(), _params[7]->getUnsigned()
  1738. );
  1739. const auto& param2 = FieldCodeGenerator::FormatValueOrVariable(
  1740. cg->GetFormatter(), _params[1]->getUnsigned(), _params[8]->getUnsigned()
  1741. );
  1742. const auto& param3 = FieldCodeGenerator::FormatValueOrVariable(
  1743. cg->GetFormatter(), _params[2]->getUnsigned(), _params[9]->getUnsigned()
  1744. );
  1745. const auto& param4 = FieldCodeGenerator::FormatValueOrVariable(
  1746. cg->GetFormatter(), _params[3]->getUnsigned(), _params[10]->getUnsigned()
  1747. );
  1748. const auto& param5 = FieldCodeGenerator::FormatValueOrVariable(
  1749. cg->GetFormatter(), _params[5]->getUnsigned(), _params[11]->getUnsigned()
  1750. );
  1751. auto op = _params[6]->getUnsigned();
  1752. code_gen->AddOutputLine((
  1753. boost::format("-- music:execute_akao(0x%6$02x, %1%, %2%, %3%, %4%, %5%)")
  1754. % param1 % param2 % param3 % param4 % param5 % op
  1755. ).str());
  1756. }
  1757. void FF7AudioVideoInstruction::processMUSIC(CodeGenerator* code_gen){
  1758. code_gen->AddOutputLine((
  1759. boost::format("-- music:execute_akao(0x10, pointer_to_field_AKAO_%1%)")
  1760. % _params[0]->getUnsigned()
  1761. ).str());
  1762. }
  1763. void FF7AudioVideoInstruction::processSOUND(CodeGenerator* code_gen){
  1764. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1765. const auto& soundId = FieldCodeGenerator::FormatValueOrVariable(
  1766. cg->GetFormatter(), _params[0]->getUnsigned(), _params[2]->getUnsigned()
  1767. );
  1768. const auto& panning = FieldCodeGenerator::FormatValueOrVariable(
  1769. cg->GetFormatter(), _params[1]->getUnsigned(), _params[3]->getUnsigned()
  1770. );
  1771. code_gen->AddOutputLine(
  1772. (boost::format("-- music:execute_akao(0x20, %1%, %2%)") % soundId % panning).str()
  1773. );
  1774. }
  1775. void FF7AudioVideoInstruction::processAKAO(CodeGenerator* code_gen){
  1776. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1777. const auto& param1 = FieldCodeGenerator::FormatValueOrVariable(
  1778. cg->GetFormatter(), _params[0]->getUnsigned(), _params[7]->getUnsigned()
  1779. );
  1780. const auto& param2 = FieldCodeGenerator::FormatValueOrVariable(
  1781. cg->GetFormatter(), _params[1]->getUnsigned(), _params[8]->getUnsigned()
  1782. );
  1783. const auto& param3 = FieldCodeGenerator::FormatValueOrVariable(
  1784. cg->GetFormatter(), _params[2]->getUnsigned(), _params[9]->getUnsigned()
  1785. );
  1786. const auto& param4 = FieldCodeGenerator::FormatValueOrVariable(
  1787. cg->GetFormatter(), _params[3]->getUnsigned(), _params[10]->getUnsigned()
  1788. );
  1789. const auto& param5 = FieldCodeGenerator::FormatValueOrVariable(
  1790. cg->GetFormatter(), _params[5]->getUnsigned(), _params[11]->getUnsigned()
  1791. );
  1792. auto op = _params[6]->getUnsigned();
  1793. code_gen->AddOutputLine((
  1794. boost::format("-- music:execute_akao(0x%6$02x, %1%, %2%, %3%, %4%, %5%)")
  1795. % param1 % param2 % param3 % param4 % param5 % op
  1796. ).str());
  1797. }
  1798. void FF7AudioVideoInstruction::processMULCK(CodeGenerator* code_gen){
  1799. code_gen->AddOutputLine((
  1800. boost::format("-- music:lock(%1%)")
  1801. % FieldCodeGenerator::FormatBool(_params[0]->getUnsigned())
  1802. ).str());
  1803. }
  1804. void FF7AudioVideoInstruction::processPMVIE(CodeGenerator* code_gen){
  1805. code_gen->AddOutputLine(
  1806. (boost::format("-- field:movie_set(%1%)") % _params[0]->getUnsigned()
  1807. ).str());
  1808. }
  1809. void FF7AudioVideoInstruction::processMOVIE(CodeGenerator* code_gen){
  1810. code_gen->AddOutputLine("-- field:play_movie()");
  1811. }
  1812. void FF7AudioVideoInstruction::processMVIEF(CodeGenerator* code_gen){
  1813. // TODO: Check for assignment to value.
  1814. FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
  1815. const auto& destination = FieldCodeGenerator::FormatValueOrVariable(
  1816. cg->GetFormatter(), _params[1]->getUnsigned(), _params[2]->getUnsigned());
  1817. code_gen->AddOutputLine(
  1818. (boost::format("-- %1% = field:get_movie_frame()") % destination).str()
  1819. );
  1820. }
  1821. void FF7UncategorizedInstruction::processInst(
  1822. Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen
  1823. ){
  1824. FunctionMetaData md(func._metadata);
  1825. switch (_opcode){
  1826. case OPCODE::MPDSP: WriteTodo(code_gen, md.GetEntityName(), "MPDSP"); break;
  1827. case OPCODE::SETX: WriteTodo(code_gen, md.GetEntityName(), "SETX"); break;
  1828. case OPCODE::GETX: WriteTodo(code_gen, md.GetEntityName(), "GETX"); break;
  1829. case OPCODE::SEARCHX: WriteTodo(code_gen, md.GetEntityName(), "SEARCHX"); break;
  1830. default:
  1831. code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented(
  1832. md.GetEntityName(), _address, _opcode)
  1833. );
  1834. }
  1835. }
  1836. void FF7NoOperationInstruction::processInst(
  1837. Function& func, ValueStack&, Engine* engine, CodeGenerator *code_gen
  1838. ){}