FieldCodeGenerator.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. /*
  2. * Copyright (C) 2022 The V-Gears Team
  3. *
  4. * This file is part of V-Gears
  5. *
  6. * V-Gears is free software: you can redistribute it and/or modify it under
  7. * terms of the GNU General Public License as published by the Free Software
  8. * Foundation, version 3.0 (GPLv3) of the License.
  9. *
  10. * V-Gears is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. */
  15. #include <boost/algorithm/string/predicate.hpp>
  16. #include "decompiler/field/FieldCodeGenerator.h"
  17. #include "decompiler/field/FieldEngine.h"
  18. const std::string FF7::FunctionMetaData::DELIMITER("_");
  19. const std::string FF7::FunctionMetaData::START("start");
  20. const std::string FF7::FunctionMetaData::END("end");
  21. FF7::FunctionMetaData::FunctionMetaData(std::string meta_data){Parse(meta_data);}
  22. bool FF7::FunctionMetaData::IsStart() const{return start_;}
  23. bool FF7::FunctionMetaData::IsEnd() const{return end_;}
  24. std::string FF7::FunctionMetaData::GetEntityName(){return entity_name_;}
  25. int FF7::FunctionMetaData::GetCharacterId(){return character_id_;}
  26. void FF7::FunctionMetaData::Parse(std::string meta_data){
  27. std::deque<std::string> strs;
  28. boost::split(strs, meta_data, boost::is_any_of(DELIMITER), boost::token_compress_on);
  29. if (!strs.empty()){
  30. auto tmp = strs.front();
  31. strs.pop_front();
  32. ParseStart(tmp, strs);
  33. }
  34. }
  35. void FF7::FunctionMetaData::ParseStart(const std::string& item, std::deque<std::string>& strs){
  36. if (item == START){
  37. start_ = true;
  38. if (!strs.empty()){
  39. auto tmp = strs.front();
  40. strs.pop_front();
  41. ParseEnd(tmp, strs);
  42. }
  43. }
  44. else ParseEnd(item, strs);
  45. }
  46. void FF7::FunctionMetaData::ParseEnd(const std::string& item, std::deque<std::string>& strs){
  47. if (item == END){
  48. end_ = true;
  49. if (!strs.empty()){
  50. auto tmp = strs.front();
  51. strs.pop_front();
  52. ParseCharId(tmp, strs);
  53. }
  54. }
  55. else ParseCharId(item, strs);
  56. }
  57. void FF7::FunctionMetaData::ParseCharId(const std::string& item, std::deque<std::string>& strs){
  58. if (!item.empty()) character_id_ = std::stoi(item);
  59. if (!strs.empty()){
  60. auto tmp = strs.front();
  61. strs.pop_front();
  62. ParseEntity(tmp, strs);
  63. }
  64. }
  65. void FF7::FunctionMetaData::ParseEntity(const std::string& item, std::deque<std::string>& strs){
  66. entity_name_ = item;
  67. for (auto& part : strs)
  68. if (!part.empty()) entity_name_ += "_" + part;
  69. }
  70. void FF7::FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
  71. // TODO: Break into parts
  72. auto instruction = insts.begin();
  73. std::vector<std::pair<Function&, InstVec>> functions_with_bodies;
  74. for (
  75. auto function = engine_->_functions.begin();
  76. function != engine_->_functions.end();
  77. ++ function
  78. ){
  79. InstVec body;
  80. for (size_t i = 0; i < function->second.mNumInstructions; ++ i, ++ instruction)
  81. body.push_back(*instruction);
  82. functions_with_bodies.push_back(std::pair<Function&, InstVec> {function->second, body});
  83. }
  84. for (
  85. auto function = functions_with_bodies.begin();
  86. function != functions_with_bodies.end();
  87. ++ function
  88. ){
  89. OnBeforeStartFunction(function->first);
  90. auto signature = ConstructFuncSignature(function->first);
  91. AddOutputLine(signature, false, true);
  92. OnStartFunction(function->first);
  93. // Comment with original instructions.
  94. std::unordered_map<uint32, InstVec> labels;
  95. for (
  96. auto instruction = function->second.begin();
  97. instruction != function->second.end();
  98. ++ instruction
  99. ){
  100. if ((*instruction)->isCondJump() || (*instruction)->IsUncondJump()){
  101. auto targetAddr = (*instruction)->GetDestAddress();
  102. auto label = labels.find(targetAddr);
  103. if (label == labels.end()) labels.insert({targetAddr, InstVec()});
  104. labels[targetAddr].push_back(*instruction);
  105. }
  106. }
  107. // Implemented instructions.
  108. bool end_needed = false;
  109. for (
  110. auto instruction = function->second.begin();
  111. instruction != function->second.end();
  112. ++instruction
  113. ){
  114. auto label = labels.find((*instruction)->_address);
  115. if (label != labels.end()){
  116. bool needs_label = false;
  117. bool needs_new_line = false;
  118. for (auto origin = label->second.begin(); origin != label->second.end(); ++ origin){
  119. if ((*origin)->isCondJump()){
  120. AddOutputLine("end", true, false);
  121. needs_new_line = true;
  122. }
  123. else needs_label = true;
  124. }
  125. if (needs_new_line) AddOutputLine("");
  126. if (needs_label)
  127. AddOutputLine((boost::format("::label_0x%1$X::") % label->first).str());
  128. }
  129. ValueStack stack;
  130. (*instruction)->ProcessInst(function->first, stack, engine_, this);
  131. if (end_needed){
  132. AddOutputLine("end -- end if", true, false);
  133. end_needed = false;
  134. }
  135. if ((*instruction)->isCondJump()){
  136. AddOutputLine(
  137. (boost::format("if (%s) then") % stack.pop()->getString()).str(), false, true
  138. );
  139. // If the next instruction is the last in the function, mark the next pass to
  140. // add an 'end' after the instruction to close the if.
  141. if ((*(instruction + 1))->_address == (*(function->second.back()))._address)
  142. end_needed = true;
  143. }
  144. else if ((*instruction)->IsUncondJump()){
  145. // If destination address is outside the functions, turn goto into a return.
  146. if ((*instruction)->GetDestAddress() > function->first.mEndAddr){
  147. AddOutputLine(
  148. "-- Overflowed jump to "
  149. + (boost::format("0x%1$X") % (*instruction)->GetDestAddress()).str()
  150. + " (last address in function is "
  151. + (boost::format("0x%1$X)") % function->first.mEndAddr).str()
  152. );
  153. AddOutputLine("do return 0 end");
  154. }
  155. // Prevent backward jumps in the on_start script.
  156. else if (
  157. "on_start" == function->first._name
  158. && (*instruction)->GetDestAddress() <= (*instruction)->_address
  159. ){
  160. AddOutputLine("-- No infinite loops in the on_start script.");
  161. AddOutputLine((
  162. boost::format("-- goto label_0x%1$X") % (*instruction)->GetDestAddress()
  163. ).str());
  164. }
  165. else{
  166. AddOutputLine(
  167. (boost::format("goto label_0x%1$X") % (*instruction)->GetDestAddress()).str()
  168. );
  169. }
  170. }
  171. // Else, already output'd.
  172. }
  173. // Add missing return:
  174. if (
  175. "return 0" != lines_.at(lines_.size() - 1)._line
  176. && "do return 0 end" != lines_.at(lines_.size() - 1)._line
  177. ){
  178. AddOutputLine("do return 0 end", false, false);
  179. }
  180. OnEndFunction(function->first);
  181. }
  182. for (auto i = lines_.begin(); i != lines_.end(); ++i){
  183. if (i->_unindentBefore && indent_level_ > 0){
  184. indent_level_ --;
  185. }
  186. output_ << IndentString(i->_line) << std::endl;
  187. if (i->_indentAfter) indent_level_++;
  188. }
  189. }
  190. void FF7::FieldCodeGenerator::AddOutputLine(
  191. std::string line, bool unindent_before, bool indent_after
  192. ){lines_.push_back(CodeLine(line, unindent_before, indent_after));}
  193. float FF7::FieldCodeGenerator::GetScaleFactor() const
  194. {return static_cast<FieldEngine*>(engine_)->GetScaleFactor();}
  195. void FF7::FieldCodeGenerator::OnBeforeStartFunction(const Function& function){
  196. FunctionMetaData meta_data(function._metadata);
  197. if (meta_data.IsStart()){
  198. AddOutputLine("EntityContainer[\"" + meta_data.GetEntityName() + "\"] = {", false, true);
  199. if (meta_data.GetCharacterId() != -1)
  200. AddOutputLine(meta_data.GetEntityName() + " = nil,\n");
  201. }
  202. const auto comment = formatter_.FunctionComment(meta_data.GetEntityName(), function._name);
  203. if (!comment.empty()) AddOutputLine("-- " + comment);
  204. }
  205. void FF7::FieldCodeGenerator::OnStartFunction(const Function& func){
  206. AddOutputLine("--[[");
  207. for (const auto& inst : insts_){
  208. if (inst->_address >= func.mStartAddr && inst->_address <= func.mEndAddr){
  209. std::stringstream output;
  210. output << inst;
  211. AddOutputLine(output.str());
  212. }
  213. }
  214. AddOutputLine("]]\n");
  215. // TODO: If this hack is needed, maybe it can just be added to the "Direcor" entity.
  216. if (func._name == "on_start" || func._name == "init"){
  217. AddOutputLine("-- HACK ensure camera follows cloud, fix in engine properly later");
  218. AddOutputLine("background2d:autoscroll_to_entity(entity_manager:get_entity(\"Cloud\"))");
  219. }
  220. }
  221. void FF7::FieldCodeGenerator::OnEndFunction(const Function& function){
  222. // End function.
  223. AddOutputLine("end,", true, false);
  224. // End class?
  225. FunctionMetaData meta_data(function._metadata);
  226. if (meta_data.IsEnd()) AddOutputLine("}\n\n\n", true, false);
  227. else AddOutputLine("\n");
  228. }
  229. std::string FF7::FieldCodeGenerator::ConstructFuncSignature(const Function &function){
  230. // Generate name
  231. FunctionMetaData meta_data(function._metadata);
  232. return formatter_.FunctionName(meta_data.GetEntityName(), function._name) + " = function(self)";
  233. }
  234. bool FF7::FieldCodeGenerator::OutputOnlyRequiredLabels() const{return true;}
  235. SUDM::IScriptFormatter& FF7::FieldCodeGenerator::GetFormatter(){return formatter_;}
  236. const std::string FF7::FieldCodeGenerator::FormatInstructionNotImplemented(
  237. const std::string& entity, uint32 address, uint32 opcode
  238. ){
  239. return (
  240. boost::format(
  241. "-- log:log(\"In entity \\\"%1%\\\", address 0x%2$08x: "
  242. "instruction 0x%3$04x not implemented\")"
  243. ) % entity % address % opcode
  244. ).str();
  245. }
  246. const std::string FF7::FieldCodeGenerator::FormatInstructionNotImplemented(
  247. const std::string& entity, uint32 address, const Instruction& instruction
  248. ){
  249. std::stringstream parameterList;
  250. for (auto i = instruction._params.begin(); i != instruction._params.end(); ++ i){
  251. if (i != instruction._params.begin()) parameterList << ", ";
  252. parameterList << *i;
  253. }
  254. return(
  255. boost::format(
  256. "-- log:log(\"In entity \\\"%1%\\\", address 0x%2$08x: "
  257. "instruction %3%(%4%) not implemented\")"
  258. ) % entity % address % instruction._name % parameterList.str()
  259. ).str();
  260. }
  261. const std::string FF7::FieldCodeGenerator::FormatBool(uint32 value){
  262. return value == 0 ? "false" : "true";
  263. }
  264. const std::string FF7::FieldCodeGenerator::FormatInvertedBool(uint32 value){
  265. return value == 0 ? "true" : "false";
  266. }