FieldCodeGenerator.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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 "FieldCodeGenerator.h"
  17. #include "FieldEngine.h"
  18. const std::string FunctionMetaData::DELIMITER("_");
  19. const std::string FunctionMetaData::START("start");
  20. const std::string FunctionMetaData::END("end");
  21. FunctionMetaData::FunctionMetaData(std::string meta_data){Parse(meta_data);}
  22. bool FunctionMetaData::IsStart() const{return start_;}
  23. bool FunctionMetaData::IsEnd() const{return end_;}
  24. std::string FunctionMetaData::GetEntityName(){return entity_name_;}
  25. int FunctionMetaData::GetCharacterId(){return character_id_;}
  26. void 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 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 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 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 FunctionMetaData::ParseEntity(const std::string& item, std::deque<std::string>& strs){
  66. entity_name_ = item;
  67. for (auto& part : strs) if (!part.empty()) entity_name_ += "_" + part;
  68. }
  69. void FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
  70. // TODO: Break into parts
  71. auto instruction = insts.begin();
  72. std::vector<std::pair<Function&, InstVec>> functions_with_bodies;
  73. for (auto function = engine_->functions.begin(); function != engine_->functions.end(); ++ function){
  74. InstVec body;
  75. for (size_t i = 0; i < function->second.num_instructions; ++ i, ++ instruction)
  76. body.push_back(*instruction);
  77. functions_with_bodies.push_back(std::pair<Function&, InstVec> {function->second, body});
  78. }
  79. for (auto function = functions_with_bodies.begin(); function != functions_with_bodies.end(); ++ function){
  80. OnBeforeStartFunction(function->first);
  81. auto signature = ConstructFuncSignature(function->first);
  82. AddOutputLine(signature, false, true);
  83. OnStartFunction(function->first);
  84. // Generate labels for jump instruction targets.
  85. std::unordered_map<uint32, InstVec> labels;
  86. for (auto instruction = function->second.begin(); instruction != function->second.end(); ++ instruction){
  87. if ((*instruction)->IsCondJump() || (*instruction)->IsUncondJump()){
  88. auto targetAddr = (*instruction)->GetDestAddress();
  89. auto label = labels.find(targetAddr);
  90. if (label == labels.end()) labels.insert({targetAddr, InstVec()});
  91. labels[targetAddr].push_back(*instruction);
  92. }
  93. }
  94. // Implemented instructions.
  95. // Some conditional jumps point to an address outside the function (i.e the starting
  96. // address of the next function) and the 'end' are not written to the Lua script. Keep
  97. // track of how many are needed and add them at the end of the function.
  98. int ends_needed = 0;
  99. for (auto instruction = function->second.begin(); instruction != function->second.end(); ++ instruction){
  100. auto label = labels.find((*instruction)->GetAddress());
  101. if (label != labels.end()){
  102. bool needs_label = false;
  103. bool needs_new_line = false;
  104. for (auto origin = label->second.begin(); origin != label->second.end(); ++ origin){
  105. if ((*origin)->IsCondJump()){
  106. ends_needed --;
  107. AddOutputLine("end", true, false);
  108. needs_new_line = true;
  109. }
  110. else needs_label = true;
  111. }
  112. if (needs_new_line) AddOutputLine("");
  113. if (needs_label) AddOutputLine((boost::format("::label_0x%1$X::") % label->first).str());
  114. }
  115. ValueStack stack;
  116. (*instruction)->ProcessInst(function->first, stack, engine_, this);
  117. //if (end_needed){
  118. // AddOutputLine("end -- end if", true, false);
  119. // end_needed = false;
  120. //}
  121. if ((*instruction)->IsCondJump()){
  122. ends_needed ++;
  123. AddOutputLine((boost::format("if (%s) then") % stack.Pop()->GetString()).str(), false, true);
  124. // If the next instruction is the last in the function, mark the next pass to
  125. // add an 'end' after the instruction to close the if.
  126. //if ((*(instruction + 1))->GetAddress() == (*(function->second.back())).GetAddress())
  127. // end_needed = true;
  128. }
  129. else if ((*instruction)->IsUncondJump()){
  130. // If destination address is outside the functions, turn goto into a return.
  131. if ((*instruction)->GetDestAddress() > function->first.end_addr){
  132. AddOutputLine(
  133. "-- Overflowed jump to " + (boost::format("0x%1$X") % (*instruction)->GetDestAddress()).str()
  134. + " (last address in function is " + (boost::format("0x%1$X)") % function->first.end_addr).str()
  135. );
  136. AddOutputLine("do return 0 end");
  137. }
  138. // Prevent backward jumps in the on_start script.
  139. else if (
  140. "on_start" == function->first.name && (*instruction)->GetDestAddress() <= (*instruction)->GetAddress()
  141. ){
  142. AddOutputLine("-- No infinite loops in the on_start script.");
  143. AddOutputLine((boost::format("-- goto label_0x%1$X") % (*instruction)->GetDestAddress()).str());
  144. }
  145. else AddOutputLine((boost::format("goto label_0x%1$X") % (*instruction)->GetDestAddress()).str());
  146. }
  147. // Else, already output'd.
  148. }
  149. for(; ends_needed > 0; ends_needed --) AddOutputLine("end -- clean-up", true, false);
  150. // Add missing return:
  151. if ("return 0" != lines_.at(lines_.size() - 1).line && "do return 0 end" != lines_.at(lines_.size() - 1).line)
  152. AddOutputLine("do return 0 end", false, false);
  153. OnEndFunction(function->first);
  154. }
  155. for (auto i = lines_.begin(); i != lines_.end(); ++i){
  156. if (i->unindent_before && indent_level_ > 0) indent_level_ --;
  157. output_ << IndentString(i->line) << std::endl;
  158. if (i->indent_after) indent_level_++;
  159. }
  160. }
  161. void FieldCodeGenerator::AddOutputLine(std::string line, bool unindent_before, bool indent_after){
  162. lines_.push_back(CodeLine(line, unindent_before, indent_after));
  163. }
  164. float FieldCodeGenerator::GetScaleFactor() const{return static_cast<FieldEngine*>(engine_)->GetScaleFactor();}
  165. void FieldCodeGenerator::OnBeforeStartFunction(const Function& function){
  166. FunctionMetaData meta_data(function.metadata);
  167. if (meta_data.IsStart()){
  168. AddOutputLine("EntityContainer[\"" + meta_data.GetEntityName() + "\"] = {", false, true);
  169. if (meta_data.GetCharacterId() != -1) AddOutputLine(meta_data.GetEntityName() + " = nil,\n");
  170. }
  171. const auto comment = formatter_.GetFunctionComment(meta_data.GetEntityName(), function.name);
  172. if (!comment.empty()) AddOutputLine("-- " + comment);
  173. }
  174. void FieldCodeGenerator::OnStartFunction(const Function& func){
  175. // Add some hacks for Director entity before the comments
  176. FunctionMetaData meta_data(func.metadata);
  177. if (meta_data.GetEntityName() == "Director" && func.name == "on_start"){
  178. // TODO: Don't hardcode Cloud, Use the one from the previous level,
  179. AddOutputLine("-- HACK: Set default playable entity an ensure the camera follows it.");
  180. AddOutputLine("entity_manager:set_player_entity(\"Cloud\")");
  181. AddOutputLine("background2d:autoscroll_to_entity(entity_manager:get_entity(\"Cloud\"))\n");
  182. }
  183. // For lines, exit if they are off.
  184. if (func.name == "on_approach" || func.name == "on_cross" || func.name == "on_near" || func.name == "on_leave"){
  185. AddOutputLine("if self.on ~= nill and self.on == false then", false, true);
  186. AddOutputLine("return 0");
  187. AddOutputLine("end", true, false);
  188. }
  189. AddOutputLine("--[[");
  190. for (const auto& inst : insts_){
  191. if (inst->GetAddress() >= func.start_addr && inst->GetAddress() <= func.end_addr){
  192. std::stringstream output;
  193. output << inst;
  194. AddOutputLine(output.str());
  195. }
  196. }
  197. AddOutputLine("]]\n");
  198. }
  199. void FieldCodeGenerator::OnEndFunction(const Function& function){
  200. // End function.
  201. AddOutputLine("end,", true, false);
  202. // End class?
  203. FunctionMetaData meta_data(function.metadata);
  204. if (meta_data.IsEnd()) AddOutputLine("}\n\n\n", true, false);
  205. else AddOutputLine("\n");
  206. }
  207. std::string FieldCodeGenerator::ConstructFuncSignature(const Function &function){
  208. // Generate name
  209. FunctionMetaData meta_data(function.metadata);
  210. return formatter_.GetFriendlyFunctionName(meta_data.GetEntityName(), function.name) + " = function(self)";
  211. }
  212. bool FieldCodeGenerator::OutputOnlyRequiredLabels() const{return true;}
  213. FieldScriptFormatter& FieldCodeGenerator::GetFormatter(){return formatter_;}
  214. const std::string FieldCodeGenerator::FormatInstructionNotImplemented(
  215. const std::string& entity, uint32 address, uint32 opcode
  216. ){
  217. return (
  218. boost::format(
  219. "-- log:log(\"In entity \\\"%1%\\\", address 0x%2$08x: instruction 0x%3$04x not implemented\")"
  220. ) % entity % address % opcode
  221. ).str();
  222. }
  223. const std::string FieldCodeGenerator::FormatInstructionNotImplemented(
  224. const std::string& entity, uint32 address, const Instruction& instruction
  225. ){
  226. std::stringstream parameterList;
  227. //for (auto i = instruction._params.begin(); i != instruction._params.end(); ++ i){
  228. for (auto i = instruction.GetParams().begin(); i != instruction.GetParams().end(); ++ i){
  229. //if (i != instruction._params.begin()) parameterList << ", ";
  230. if (i != instruction.GetParams().begin()) parameterList << ", ";
  231. parameterList << *i;
  232. }
  233. return(
  234. boost::format(
  235. "-- log:log(\"In entity \\\"%1%\\\", address 0x%2$08x: instruction %3%(%4%) not implemented\")"
  236. ) % entity % address % instruction.GetName() % parameterList.str()
  237. ).str();
  238. }
  239. const std::string FieldCodeGenerator::FormatBool(uint32 value){return value == 0 ? "false" : "true";}
  240. const std::string FieldCodeGenerator::FormatInvertedBool(uint32 value){return value == 0 ? "true" : "false";}