FieldEngine.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. * V-Gears
  3. * Copyright (C) 2022 V-Gears Team
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program 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. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <iostream>
  19. #include <sstream>
  20. #include <boost/format.hpp>
  21. #include "decompiler/field/FieldEngine.h"
  22. #include "decompiler/field/FieldCodeGenerator.h"
  23. #include "decompiler/field/FieldDisassembler.h"
  24. #include "decompiler/field/instruction/FieldNoOperationInstruction.h"
  25. /**
  26. * @todo Understand and document
  27. */
  28. #define GET(vertex) (boost::get(boost::vertex_name, graph, vertex))
  29. /*
  30. * TODO: OpCodes which need implementing.
  31. *
  32. * BLINK
  33. * XYI
  34. * CMOVE
  35. * MOVA
  36. * TURA
  37. * ANIMW
  38. * FMOVE
  39. * ANIME2
  40. * ANIM_1
  41. * CANIM1 ?
  42. * CANM_1
  43. * TURN
  44. * DIRA
  45. * GETDIR
  46. * GETAXY
  47. * TALKR
  48. * ANIMB
  49. * TURNW
  50. */
  51. FieldEngine::Entity::Entity(const std::string& name, size_t index):
  52. name_(name), index_(index), is_line_(false){}
  53. std::string FieldEngine::Entity::GetName() const{return name_;}
  54. size_t FieldEngine::Entity::GetIndex() const{return index_;}
  55. std::string FieldEngine::Entity::FunctionByIndex(size_t index) const{
  56. auto it = functions_.find(index);
  57. if (it == std::end(functions_)) throw DecompilerException();
  58. return it->second;
  59. }
  60. void FieldEngine::Entity::AddFunction(const std::string& name, size_t index){
  61. functions_[index] = name;
  62. }
  63. void FieldEngine::Entity::MarkAsLine(
  64. bool line, std::vector<float> point_a, std::vector<float> point_b
  65. ){
  66. is_line_ = line;
  67. point_a_.clear();
  68. point_b_.clear();
  69. if (line){
  70. if (point_a.size() >= 3 && point_b.size() >= 3){
  71. point_a_.push_back(point_a[0]);
  72. point_a_.push_back(point_a[1]);
  73. point_a_.push_back(point_a[2]);
  74. point_b_.push_back(point_b[0]);
  75. point_b_.push_back(point_b[1]);
  76. point_b_.push_back(point_b[2]);
  77. }
  78. // TODO: Notify on else.
  79. }
  80. // TODO: These are not getting to the final script.
  81. // Maybe this can be removed?
  82. AddFunction("on_approach", 1);
  83. AddFunction("on_cross", 2);
  84. AddFunction("on_near", 3);
  85. AddFunction("on_leave", 4);
  86. }
  87. bool FieldEngine::Entity::IsLine(){return is_line_;}
  88. std::vector<float> FieldEngine::Entity::GetLinePointA(){return point_a_;}
  89. std::vector<float> FieldEngine::Entity::GetLinePointB(){return point_b_;}
  90. FieldEngine::FieldEngine(FieldScriptFormatter& formatter, std::string script_name) :
  91. formatter_(formatter), script_name_(script_name), scale_factor_(1.0f)
  92. {SetOutputStackEffect(false);}
  93. std::unique_ptr<Disassembler> FieldEngine::GetDisassembler(
  94. InstVec &insts, const std::vector<unsigned char>& raw_script_data
  95. ){
  96. auto ret = std::make_unique<FieldDisassembler>(formatter_, this, insts, raw_script_data);
  97. scale_factor_ = ret->GetScaleFactor();
  98. return std::move(ret);
  99. }
  100. std::unique_ptr<Disassembler> FieldEngine::GetDisassembler(InstVec &insts){
  101. auto ret = std::make_unique<FieldDisassembler>(formatter_, this, insts);
  102. scale_factor_ = ret->GetScaleFactor();
  103. return std::move(ret);
  104. }
  105. std::unique_ptr<CodeGenerator> FieldEngine::GetCodeGenerator(
  106. const InstVec& insts, std::ostream &output
  107. ){
  108. // The broken version:
  109. //return std::make_unique<FieldCodeGenerator>(this, insts, output);
  110. // The not-as-nice-but-at-least-it-works version:
  111. return std::make_unique<FieldCodeGenerator>(this, insts, output, formatter_);
  112. }
  113. void FieldEngine::PostCFG(InstVec& insts, Graph graph){
  114. // In FF7 some scripts ends with an infinite loop to "keep it alive"
  115. // in V-Gears this isn't required so they can be removed.
  116. //RemoveTrailingInfiniteLoops(insts, graph);
  117. // This could generate bad code, but it always seems to follow that pattern that if the last
  118. // instruction is an uncond jump back into the script then it simply nests all of those blocks
  119. // in an infinite loop.
  120. //MarkInfiniteLoopGroups(insts, graph);
  121. // Scripts end with a "return". This isn't required so strip them out.
  122. //RemoveExtraneousReturnStatements(insts, graph);
  123. }
  124. bool FieldEngine::UsePureGrouping() const{return false;}
  125. std::map<std::string, int> FieldEngine::GetEntities() const{
  126. std::map<std::string, int> r;
  127. for (auto& f : functions){
  128. const Function& func = f.second;
  129. FunctionMetaData meta(func.metadata);
  130. auto it = r.find(meta.GetEntityName());
  131. if (it != std::end(r)){
  132. // Try to find a function in this entity has that has a char id.
  133. // don't overwrite a valid char id with a "blank" one.
  134. if (it->second == -1) it->second = meta.GetCharacterId();
  135. }
  136. // TODO: Don't add line entities here:
  137. else r[meta.GetEntityName()] = meta.GetCharacterId();
  138. }
  139. return r;
  140. }
  141. std::vector<FieldDecompiler::FieldEntity> FieldEngine::GetEntityList() const{
  142. std::vector<FieldDecompiler::FieldEntity> entities;
  143. for (auto entity: entity_index_map_){
  144. if (entity.second.IsLine() == false){
  145. FieldDecompiler::FieldEntity ent;
  146. ent.name = entity.second.GetName();
  147. ent.index = entity.second.GetIndex();
  148. // Get character ID.
  149. ent.char_id = -1;
  150. std::map<std::string, int> r;
  151. for (auto& f : functions){
  152. const Function& func = f.second;
  153. FunctionMetaData meta(func.metadata);
  154. if (meta.GetEntityName() == ent.name){
  155. ent.char_id = meta.GetCharacterId();
  156. break;
  157. }
  158. }
  159. entities.push_back(ent);
  160. }
  161. }
  162. return entities;
  163. }
  164. std::vector<FieldDecompiler::Line> FieldEngine::GetLineList() const{
  165. std::vector<FieldDecompiler::Line> lines;
  166. for (auto entity: entity_index_map_){
  167. if (entity.second.IsLine() == true){
  168. FieldDecompiler::Line line;
  169. line.name = entity.second.GetName();
  170. line.point_a = entity.second.GetLinePointA();
  171. line.point_b = entity.second.GetLinePointB();
  172. lines.push_back(line);
  173. }
  174. }
  175. return lines;
  176. }
  177. void FieldEngine::AddEntityFunction(
  178. const std::string& entity_name, size_t entity_index,
  179. const std::string& func_name, size_t func_index
  180. ){
  181. auto it = entity_index_map_.find(entity_index);
  182. if (it != std::end(entity_index_map_)) (*it).second.AddFunction(func_name, func_index);
  183. else{
  184. Entity e(entity_name, entity_index);
  185. e.AddFunction(func_name, func_index);
  186. entity_index_map_.insert(std::make_pair(entity_index, e));
  187. }
  188. }
  189. void FieldEngine::MarkEntityAsLine(
  190. size_t entity_index, bool line, std::vector<float> point_a, std::vector<float> point_b
  191. ){
  192. auto it = entity_index_map_.find(entity_index);
  193. if (it != std::end(entity_index_map_)){
  194. (*it).second.MarkAsLine(line, point_a, point_b);
  195. }
  196. }
  197. bool FieldEngine::EntityIsLine(size_t entity_index){
  198. auto it = entity_index_map_.find(entity_index);
  199. if (it != std::end(entity_index_map_)) return (*it).second.IsLine();
  200. return false;
  201. }
  202. const FieldEngine::Entity& FieldEngine::EntityByIndex(size_t index) const{
  203. auto it = entity_index_map_.find(index);
  204. if (it == std::end(entity_index_map_)) throw DecompilerException();
  205. return it->second;
  206. }
  207. float FieldEngine::GetScaleFactor() const {return scale_factor_;}
  208. const std::string& FieldEngine::GetScriptName() const {return script_name_;}
  209. void FieldEngine::RemoveExtraneousReturnStatements(InstVec& insts, Graph graph){
  210. for (auto& f : functions){
  211. Function& func = f.second;
  212. for (auto it = insts.begin(); it != insts.end(); it ++){
  213. // Is it the last instruction in the function, and is it a return statement?
  214. if ((*it)->GetAddress() == func.end_addr){
  215. if ((*it)->GetOpcode() == OPCODES::RET){
  216. // Set new end address to be before the NOP.
  217. func.end_addr = (*(it - 1))->GetAddress();
  218. func.num_instructions --;
  219. Instruction* nop = new FieldNoOperationInstruction();
  220. nop->SetOpcode(OPCODES::NOP);
  221. nop->SetAddress((*it)->GetAddress());
  222. (*it).reset(nop);
  223. break;
  224. }
  225. }
  226. }
  227. }
  228. }
  229. void FieldEngine::RemoveTrailingInfiniteLoops(InstVec& insts, Graph graph){
  230. for (auto& f : functions){
  231. Function& func = f.second;
  232. for (auto it = insts.begin(); it != insts.end(); it ++){
  233. // Is it the last instruction in the function, a jump, and a jumping to itself?
  234. if ((*it)->GetAddress() == func.end_addr){
  235. if ((*it)->IsJump() && (*it)->GetDestAddress() == (*it)->GetAddress()){
  236. // Set new end address to be before the NOP
  237. func.end_addr = (*(it - 1))->GetAddress();
  238. func.num_instructions --;
  239. Instruction* nop = new FieldNoOperationInstruction();
  240. nop->SetOpcode(OPCODES::NOP);
  241. nop->SetAddress((*it)->GetAddress());
  242. (*it).reset(nop);
  243. break;
  244. }
  245. }
  246. }
  247. }
  248. }
  249. void FieldEngine::MarkInfiniteLoopGroups(InstVec& insts, Graph graph){
  250. for (auto& f : functions){
  251. Function& func = f.second;
  252. for (auto it = insts.begin(); it != insts.end(); it ++){
  253. if ((*it)->GetAddress() == func.end_addr){
  254. // Note: This is a "best effort heuristic", so quite a few loops will
  255. // still end up as goto's. This could potentially generate invalid code too.
  256. if ((*it)->IsUncondJump()){
  257. // Then assume its an infinite do { } while(true)
  258. // loop that wraps part of the script.
  259. VertexRange vr = boost::vertices(graph);
  260. for (VertexIterator v = vr.first; v != vr.second; ++ v){
  261. GroupPtr gr = GET(*v);
  262. if ((*gr->start)->GetAddress() == func.end_addr){
  263. // Then assume its an infinite do { } while(true) loop
  264. // that wraps part of the script.
  265. gr->type = GROUP_TYPE_DO_WHILE;
  266. }
  267. }
  268. }
  269. break;
  270. }
  271. }
  272. }
  273. }