ff7_field_codegen.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. #include "ff7_field_codegen.h"
  2. #include "ff7_field_engine.h"
  3. #include <boost/algorithm/string/predicate.hpp>
  4. void FF7::FF7CodeGenerator::onBeforeStartFunction(const Function& func)
  5. {
  6. // Start class
  7. FunctionMetaData metaData(func._metadata);
  8. if (metaData.IsStart())
  9. {
  10. addOutputLine("EntityContainer[\"" + metaData.EntityName() + "\"] = {", false, true);
  11. if (metaData.CharacterId() != -1)
  12. {
  13. addOutputLine(metaData.EntityName() + " = nil,");
  14. }
  15. addOutputLine("");
  16. }
  17. }
  18. void FF7::FF7CodeGenerator::onStartFunction(const Function& func)
  19. {
  20. addOutputLine("--[[");
  21. for (const auto& inst : mInsts)
  22. {
  23. if (inst->_address >= func.mStartAddr && inst->_address <= func.mEndAddr)
  24. {
  25. std::stringstream output;
  26. output << inst;
  27. addOutputLine(output.str());
  28. }
  29. }
  30. addOutputLine("]]\n");
  31. }
  32. void FF7::FF7CodeGenerator::onEndFunction(const Function& func)
  33. {
  34. // End function
  35. addOutputLine("end,", true, false);
  36. // End class
  37. FunctionMetaData metaData(func._metadata);
  38. if (metaData.IsEnd())
  39. {
  40. addOutputLine("}", true, false);
  41. }
  42. }
  43. std::string FF7::FF7CodeGenerator::constructFuncSignature(const Function &func)
  44. {
  45. // Generate name
  46. return func._name + " = function(self)";
  47. }
  48. void FF7::FF7SimpleCodeGenerator::generate(InstVec& insts, const Graph& /*g*/){
  49. // TODO: Break into parts
  50. auto instruction = insts.begin();
  51. std::vector<std::pair<Function&, InstVec>> functions_with_bodies;
  52. for (
  53. auto function = _engine->_functions.begin();
  54. function != _engine->_functions.end();
  55. ++ function
  56. ){
  57. InstVec body;
  58. for (size_t i = 0; i < function->second.mNumInstructions; ++i, ++instruction)
  59. body.push_back(*instruction);
  60. functions_with_bodies.push_back(std::pair<Function&, InstVec> {function->second, body});
  61. }
  62. for (
  63. auto function = functions_with_bodies.begin();
  64. function != functions_with_bodies.end();
  65. ++ function
  66. ){
  67. onBeforeStartFunction(function->first);
  68. auto signature = constructFuncSignature(function->first);
  69. addOutputLine(signature, false, true);
  70. onStartFunction(function->first);
  71. // Comment with original instructions.
  72. std::unordered_map<uint32, InstVec> labels;
  73. for (
  74. auto instruction = function->second.begin();
  75. instruction != function->second.end();
  76. ++ instruction
  77. ){
  78. if ((*instruction)->isCondJump() || (*instruction)->isUncondJump()){
  79. auto targetAddr = (*instruction)->getDestAddress();
  80. auto label = labels.find(targetAddr);
  81. if (label == labels.end()) labels.insert({targetAddr, InstVec()});
  82. labels[targetAddr].push_back(*instruction);
  83. }
  84. }
  85. // Implemented instructions.
  86. bool end_needed = false;
  87. for (
  88. auto instruction = function->second.begin();
  89. instruction != function->second.end();
  90. ++instruction
  91. ){
  92. auto label = labels.find((*instruction)->_address);
  93. if (label != labels.end()){
  94. bool needs_label = false;
  95. bool needs_new_line = false;
  96. for (auto origin = label->second.begin(); origin != label->second.end(); ++ origin){
  97. if ((*origin)->isCondJump()){
  98. addOutputLine("end", true, false);
  99. needs_new_line = true;
  100. }
  101. else needs_label = true;
  102. }
  103. if (needs_new_line) addOutputLine("");
  104. if (needs_label)
  105. addOutputLine((boost::format("::label_0x%1$X::") % label->first).str());
  106. }
  107. ValueStack stack;
  108. (*instruction)->processInst(function->first, stack, _engine, this);
  109. if (end_needed){
  110. addOutputLine("end -- end if", true, false);
  111. end_needed = false;
  112. }
  113. if ((*instruction)->isCondJump()){
  114. addOutputLine(
  115. (boost::format("if (%s) then") % stack.pop()->getString()).str(), false, true
  116. );
  117. // If the next instruction is the last in the function, mark the next pass to
  118. // add an 'end' after the instruction to clode the if.
  119. if ((*(instruction + 1))->_address == (*(function->second.back()))._address)
  120. end_needed = true;
  121. }
  122. else if ((*instruction)->isUncondJump()){
  123. // If destination address is outside the functions, turn goto into a return.
  124. if ((*instruction)->getDestAddress() > function->first.mEndAddr){
  125. addOutputLine(
  126. "-- Overflowed jump to "
  127. + (boost::format("0x%1$X") % (*instruction)->getDestAddress()).str()
  128. + " (last address in function is "
  129. + (boost::format("0x%1$X)") % function->first.mEndAddr).str()
  130. );
  131. addOutputLine("do return 0 end");
  132. }
  133. // Prevent backward jumps in the on_start script.
  134. else if (
  135. "on_start" == function->first._name
  136. && (*instruction)->getDestAddress() <= (*instruction)->_address
  137. ){
  138. addOutputLine("-- No infinite loops in the on_start script.");
  139. addOutputLine((
  140. boost::format("-- goto label_0x%1$X") % (*instruction)->getDestAddress()
  141. ).str());
  142. }
  143. else{
  144. addOutputLine(
  145. (boost::format("goto label_0x%1$X") % (*instruction)->getDestAddress()).str()
  146. );
  147. }
  148. }
  149. // Else, already output'd.
  150. }
  151. // Add missing return:
  152. if (
  153. "return 0" != mLines.at(mLines.size() - 1)._line
  154. && "do return 0 end" != mLines.at(mLines.size() - 1)._line
  155. ){
  156. addOutputLine("do return 0 end", false, false);
  157. }
  158. onEndFunction(function->first);
  159. }
  160. for (auto i = mLines.begin(); i != mLines.end(); ++i){
  161. if (i->_unindentBefore && _indentLevel > 0){
  162. _indentLevel --;
  163. }
  164. _output << indentString(i->_line) << std::endl;
  165. if (i->_indentAfter) _indentLevel++;
  166. }
  167. }
  168. void FF7::FF7SimpleCodeGenerator::addOutputLine(std::string s, bool unindentBefore, bool indentAfter)
  169. {
  170. mLines.push_back(CodeLine(s, unindentBefore, indentAfter));
  171. }
  172. float FF7::FF7SimpleCodeGenerator::ScaleFactor() const
  173. {
  174. return static_cast<FF7::FF7FieldEngine*>(_engine)->ScaleFactor();
  175. }
  176. void FF7::FF7SimpleCodeGenerator::onBeforeStartFunction(const Function& func)
  177. {
  178. // Start class
  179. FunctionMetaData metaData(func._metadata);
  180. if (metaData.IsStart())
  181. {
  182. addOutputLine("EntityContainer[\"" + metaData.EntityName() + "\"] = {", false, true);
  183. if (metaData.CharacterId() != -1)
  184. {
  185. addOutputLine(metaData.EntityName() + " = nil,\n");
  186. }
  187. }
  188. const auto comment = mFormatter.FunctionComment(metaData.EntityName(), func._name);
  189. if (!comment.empty())
  190. {
  191. addOutputLine("-- " + comment);
  192. }
  193. }
  194. void FF7::FF7SimpleCodeGenerator::onStartFunction(const Function& func)
  195. {
  196. addOutputLine("--[[");
  197. for (const auto& inst : mInsts)
  198. {
  199. if (inst->_address >= func.mStartAddr && inst->_address <= func.mEndAddr)
  200. {
  201. std::stringstream output;
  202. output << inst;
  203. addOutputLine(output.str());
  204. }
  205. }
  206. addOutputLine("]]\n");
  207. if (func._name == "on_start" || func._name == "init")
  208. {
  209. addOutputLine("-- HACK ensure camera follows cloud, fix in engine properly later");
  210. addOutputLine("background2d:autoscroll_to_entity(entity_manager:get_entity(\"Cloud\"))");
  211. }
  212. }
  213. void FF7::FF7SimpleCodeGenerator::onEndFunction(const Function& func)
  214. {
  215. // End function
  216. addOutputLine("end,", true, false);
  217. // End class
  218. FunctionMetaData metaData(func._metadata);
  219. if (metaData.IsEnd())
  220. {
  221. addOutputLine("}\n\n\n", true, false);
  222. }
  223. else
  224. {
  225. addOutputLine("\n");
  226. }
  227. }
  228. std::string FF7::FF7SimpleCodeGenerator::constructFuncSignature(const Function &func)
  229. {
  230. // Generate name
  231. FunctionMetaData metaData(func._metadata);
  232. return mFormatter.FunctionName(metaData.EntityName(), func._name) + " = function(self)";
  233. }
  234. const std::string FF7::FF7CodeGeneratorHelpers::FormatInstructionNotImplemented(const std::string& entity, uint32 address, uint32 opcode)
  235. {
  236. return (boost::format("-- log:log(\"In entity \\\"%1%\\\", address 0x%2$08x: instruction 0x%3$04x not implemented\")") % entity % address % opcode).str();
  237. }
  238. const std::string FF7::FF7CodeGeneratorHelpers::FormatInstructionNotImplemented(const std::string& entity, uint32 address, const Instruction& instruction)
  239. {
  240. std::stringstream parameterList;
  241. for (auto i = instruction._params.begin(); i != instruction._params.end(); ++i)
  242. {
  243. if (i != instruction._params.begin())
  244. {
  245. parameterList << ", ";
  246. }
  247. parameterList << *i;
  248. }
  249. return (boost::format("-- log:log(\"In entity \\\"%1%\\\", address 0x%2$08x: instruction %3%(%4%) not implemented\")") % entity % address % instruction._name % parameterList.str()).str();
  250. }
  251. const std::string FF7::FF7CodeGeneratorHelpers::FormatBool(uint32 value)
  252. {
  253. return value == 0 ? "false" : "true";
  254. }
  255. const std::string FF7::FF7CodeGeneratorHelpers::FormatInvertedBool(uint32 value)
  256. {
  257. return value == 0 ? "true" : "false";
  258. }