ff7_field_engine.cpp 89 KB

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