BattleDataInstaller.cpp 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
  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 <fstream>
  20. #include <zlib.h>
  21. #include <tinyxml.h>
  22. #include <boost/filesystem.hpp>
  23. #include <OgreMesh.h>
  24. #include "BattleDataInstaller.h"
  25. #include "TexFile.h"
  26. #include "data/VGearsHRCFileManager.h"
  27. #include "data/VGearsAFileManager.h"
  28. #include "data/DaFile.h"
  29. #include "data/FF7Data.h"
  30. #include "common/VGearsStringUtil.h"
  31. #include "common/FinalFantasy7/FF7NameLookup.h"
  32. BattleDataInstaller::BattleDataInstaller(
  33. const std::string input_dir, const std::string output_dir, Ogre::ResourceGroupManager* res_mgr
  34. ):
  35. input_dir_(input_dir), output_dir_(output_dir),
  36. scene_bin_(input_dir + "data/battle/scene.bin"), total_scenes_(0), next_scene_(0),
  37. res_mgr_(res_mgr)
  38. {}
  39. BattleDataInstaller::~BattleDataInstaller(){}
  40. unsigned int BattleDataInstaller::InitializeScenes(){
  41. next_scene_ = 0;
  42. scenes_.clear();
  43. enemies_.clear();
  44. attacks_.clear();
  45. // Get all scenes from scene.bin.
  46. for (int i = 0; i < 32; i ++){
  47. File scene_bin_block(&scene_bin_, i * 0x2000, 0x2000);
  48. for (int o = 0; o < 10; o ++){
  49. scene_bin_block.SetOffset(4 * o);
  50. u32 start_offset = scene_bin_block.readU32LE() * 4;
  51. u32 end_offset = scene_bin_block.readU32LE() * 4;
  52. if (end_offset > start_offset + 0x2000) end_offset = start_offset + 0x2000;
  53. if (start_offset >= 0 && start_offset < 0x2000){
  54. File scene_bin_fragment(
  55. &scene_bin_block, start_offset, end_offset - start_offset - 1
  56. );
  57. File* extracted = ExtractGZipScene(scene_bin_fragment);
  58. if (extracted != NULL){
  59. scenes_uncompressed_.push_back(extracted);
  60. total_scenes_ ++;
  61. }
  62. }
  63. else break;
  64. }
  65. }
  66. return total_scenes_;
  67. }
  68. unsigned int BattleDataInstaller::InitializeModels(){
  69. next_model_to_process_ = 0;
  70. next_model_to_convert_ = 0;
  71. battle_lgp_files_.clear();
  72. battle_lgp_file_names_.clear();
  73. models_.clear();
  74. // Open battle.lgp
  75. File battle_lgp_file(input_dir_ + "data/battle/battle.lgp");
  76. // Also, open it as a LGP archive.
  77. VGears::LGPArchive battle_lgp(input_dir_ + "data/battle/battle.lgp", "LGP");
  78. battle_lgp.open(input_dir_ + "data/battle/battle.lgp", true);
  79. battle_lgp.load();
  80. VGears::LGPArchive::FileList file_list = battle_lgp.GetFiles();
  81. for (int i = 0; i < file_list.size(); i ++){
  82. VGears::LGPArchive::FileEntry f = file_list.at(i);
  83. if (f.data_offset + f.data_size <= battle_lgp_file.GetFileSize()){
  84. File b_lgp_file(&battle_lgp_file, f.data_offset, f.data_size);
  85. battle_lgp_files_.push_back(b_lgp_file);
  86. battle_lgp_file_names_.push_back(f.file_name);
  87. }
  88. }
  89. return battle_lgp_files_.size();
  90. }
  91. unsigned int BattleDataInstaller::ProcessScene(){
  92. if (next_scene_ >= scenes_uncompressed_.size()) return total_scenes_;
  93. BattleSceneFile scene(next_scene_, scenes_uncompressed_[next_scene_]);
  94. scenes_.push_back(scene);
  95. // Add new enemies to the list.
  96. std::vector<Enemy> enemies = scene.GetEnemies();
  97. for (int e = 0; e < enemies.size(); e ++){
  98. bool already_added = false;
  99. for (int l = 0; l < enemies_.size(); l ++){
  100. if (enemies_[l].id == enemies[e].id){
  101. already_added = true;
  102. break;
  103. }
  104. }
  105. if (already_added) continue;
  106. enemies_.push_back(enemies[e]);
  107. }
  108. // Add new attacks to the list.
  109. std::vector<Attack> attacks = scene.GetAttacks();
  110. for (int a = 0; a < attacks.size(); a ++){
  111. bool already_added = false;
  112. for (int l = 0; l < attacks_.size(); l ++){
  113. if (attacks_[l].id == attacks[a].id){
  114. already_added = true;
  115. break;
  116. }
  117. }
  118. if (already_added) continue;
  119. attacks_.push_back(attacks[a]);
  120. }
  121. // Add new formations to the list.
  122. std::vector<Formation> formations = scene.GetFormations();
  123. for (Formation f : formations){
  124. f.id = formations_.size(); // Assign the next ID.
  125. formations_.push_back(f);
  126. }
  127. next_scene_ ++;
  128. return next_scene_;
  129. }
  130. unsigned int BattleDataInstaller::ProcessModel(){
  131. if (next_model_to_process_ >= battle_lgp_files_.size()) return battle_lgp_files_.size();
  132. // TODO: Do something with the files.
  133. // A little explanation. Each file has a 4 letter name, for example: 1234
  134. // 12 is the model identifier.
  135. // 34 if the type of file:
  136. // - aa: Skeleton file (.hrc)
  137. // - ab: Unknown
  138. // - ac - al: Textures (.tex)
  139. // - am - cz: Polygon files (.p)
  140. // - da: Animations (.anim)
  141. std::string id = battle_lgp_file_names_[next_model_to_process_].substr(0, 2);
  142. /*if (id != "rt"){ // TODO DEBUG REMOVE
  143. next_model_to_process_ ++;
  144. return next_model_to_process_;
  145. }*/
  146. std::string type = battle_lgp_file_names_[next_model_to_process_].substr(2, 2);
  147. FF7Data::BattleModelInfo info = FF7Data::GetBattleModelInfo(id);
  148. if (type == "aa"){
  149. // HRC file.
  150. battle_lgp_files_[next_model_to_process_].WriteFile(
  151. output_dir_ + "temp/battle_models/"
  152. + battle_lgp_file_names_[next_model_to_process_] + ".hrcbin"
  153. );
  154. bool found = false;
  155. for (int m = 0; m < models_.size(); m ++){
  156. if (models_[m].id == id){
  157. models_[m].hrc = battle_lgp_file_names_[next_model_to_process_] + ".hrc";
  158. found = true;
  159. break;
  160. }
  161. }
  162. if (found == false){
  163. Model model;
  164. model.id = id;
  165. model.hrc = battle_lgp_file_names_[next_model_to_process_] + ".hrc";
  166. models_.push_back(model);
  167. }
  168. }
  169. else if (type == "ab"){
  170. battle_lgp_files_[next_model_to_process_].WriteFile(
  171. output_dir_ + "temp/battle_models/"
  172. + battle_lgp_file_names_[next_model_to_process_] + ".unknown"
  173. );
  174. } // Unknown and not needed.
  175. else if (type == "da"){
  176. // .a Animation file.
  177. battle_lgp_files_[next_model_to_process_].WriteFile(
  178. output_dir_ + "temp/battle_models/"
  179. + battle_lgp_file_names_[next_model_to_process_] + ".anim"
  180. );
  181. bool found = false;
  182. for (int m = 0; m < models_.size(); m ++){
  183. if (models_[m].id == id){
  184. models_[m].anim = battle_lgp_file_names_[next_model_to_process_] + ".anim";
  185. found = true;
  186. break;
  187. }
  188. }
  189. if (found == false){
  190. Model model;
  191. model.id = id;
  192. model.anim = battle_lgp_file_names_[next_model_to_process_] + ".anim";
  193. models_.push_back(model);
  194. }
  195. }
  196. else if (
  197. type == "ac" || type == "ad" || type == "ae" || type == "af" || type == "ag"
  198. || type == "ah"|| type == "ai" || type == "aj" || type == "ak" || type == "al"
  199. ){
  200. // .tex texture files. Save directly to their directory.
  201. battle_lgp_files_[next_model_to_process_].WriteFile(
  202. output_dir_ + "temp/battle_models/"
  203. + battle_lgp_file_names_[next_model_to_process_] + ".tex"
  204. );
  205. TexFile tex(battle_lgp_files_[next_model_to_process_]);
  206. std::string path = output_dir_ + "models/battle/";
  207. if (info.is_player){
  208. path += "characters/";
  209. // Add or update on character_data_
  210. bool found = false;
  211. for (auto data: character_data_){
  212. if (data.id == info.numeric_id){
  213. // Check if the model already exists for this character
  214. bool model_found = false;
  215. for (auto mod: data.models){
  216. if (mod.model == info.alphanumeric_id + "_" + info.name_normal + ".mesh"){
  217. model_found = true;
  218. break;
  219. }
  220. }
  221. if (model_found) break;
  222. CharacterModel model;
  223. model.name = info.name;
  224. model.model = info.alphanumeric_id + "_" + info.name_normal + ".mesh";
  225. data.models.push_back(model);
  226. character_data_.push_back(data);
  227. found = true;
  228. break;
  229. }
  230. }
  231. if (!found){
  232. CharacterData character_data;
  233. character_data.id = info.numeric_id;
  234. CharacterModel model;
  235. model.name = info.name;
  236. model.model = info.alphanumeric_id + "_" + info.name_normal + ".mesh";
  237. character_data.models.push_back(model);
  238. character_data_.push_back(character_data);
  239. }
  240. }
  241. else if (info.is_scene){
  242. SceneModel scene_data;
  243. scene_data.id = info.numeric_id;
  244. scene_data.model = info.alphanumeric_id + "_" + info.name_normal + ".mesh";
  245. scene_data.description = info.name;
  246. scene_data_.push_back(scene_data);
  247. path += "scenes/";
  248. }
  249. else path += "enemies/";
  250. path += (id + type + "_" + info.name_normal + ".png");
  251. tex.SavePng(path);
  252. res_mgr_->declareResource(
  253. id + type + "_" + info.name_normal + ".png", "Texture", "FFVIITextures"
  254. );
  255. bool found = false;
  256. for (int m = 0; m < models_.size(); m ++){
  257. if (models_[m].id == id){
  258. models_[m].tex.push_back(id + type+ "_" + info.name_normal + ".png");
  259. found = true;
  260. break;
  261. }
  262. }
  263. if (found == false){
  264. Model model;
  265. model.id = id;
  266. model.tex.push_back(id + type + "_" + info.name_normal + ".png");
  267. models_.push_back(model);
  268. }
  269. }
  270. else{
  271. // .p polygon file.
  272. battle_lgp_files_[next_model_to_process_].WriteFile(
  273. output_dir_ + "temp/battle_models/"
  274. + battle_lgp_file_names_[next_model_to_process_] + ".p"
  275. );
  276. bool found = false;
  277. for (int m = 0; m < models_.size(); m ++){
  278. if (models_[m].id == id){
  279. models_[m].p.push_back(battle_lgp_file_names_[next_model_to_process_] + ".p");
  280. found = true;
  281. break;
  282. }
  283. }
  284. if (found == false){
  285. Model model;
  286. model.id = id;
  287. model.p.push_back(battle_lgp_file_names_[next_model_to_process_] + ".p");
  288. models_.push_back(model);
  289. }
  290. }
  291. next_model_to_process_ ++;
  292. return next_model_to_process_;
  293. }
  294. unsigned int BattleDataInstaller::ConvertModelsInit(){
  295. next_model_to_convert_ = 0;
  296. return models_.size();
  297. }
  298. unsigned int BattleDataInstaller::ConvertModel(){
  299. if (next_model_to_convert_ >= models_.size()) return models_.size();
  300. Model model = models_[next_model_to_convert_];
  301. try{
  302. std::string path = "";
  303. FF7Data::BattleModelInfo info = FF7Data::GetBattleModelInfo(model.id);
  304. if (info.is_player) path += "characters/";
  305. else if (info.is_scene) path += "scenes/";
  306. else path += "enemies/";
  307. if (info.is_scene){
  308. DecompileSceneHrc(
  309. File(output_dir_ + "temp/battle_models/" + model.hrc + "bin"), model,
  310. output_dir_ + "temp/battle_models/" + model.id + "_" + info.name_normal + ".hrc",
  311. model.id + "_" + info.name_normal
  312. );
  313. }
  314. else{
  315. DecompileHrc(
  316. File(output_dir_ + "temp/battle_models/" + model.hrc + "bin"), model,
  317. output_dir_ + "temp/battle_models/" + model.id + "_" + info.name_normal + ".hrc",
  318. model.id + "_" + info.name_normal
  319. );
  320. }
  321. GenerateRsdFiles(model, output_dir_ + "temp/battle_models/");
  322. if ("" != model.anim){ // Skip for non-animated, ie battle backgrounds
  323. //std::cout << " GENERATE ANIMATIONS FOR " << model.id << std::endl;
  324. DaFile da(File(output_dir_ + "temp/battle_models/" + model.anim));
  325. std::vector<std::string> a_files = da.GenerateAFiles(
  326. model.id, output_dir_ + "temp/battle_models/"
  327. );
  328. for (std::string file_name : a_files) model.a.push_back(file_name);
  329. }
  330. // Reload the resources for the newly created hrc and rsd.
  331. // TODO: Could this be done with res_mgr_->declareResource() ? Probably faster
  332. res_mgr_->removeResourceLocation(output_dir_ + "temp/battle_models/", "FFVII");
  333. res_mgr_->addResourceLocation(
  334. output_dir_ + "temp/battle_models/", "FileSystem", "FFVII", true, true
  335. );
  336. Ogre::ResourcePtr hrc = VGears::HRCFileManager::GetSingleton().load(
  337. model.id + "_" + info.name_normal + ".hrc", "FFVII"
  338. );
  339. Ogre::String base_name;
  340. VGears::StringUtil::splitBase(model.hrc, base_name);
  341. auto mesh_name = model.id + "_" + info.name_normal + ".mesh";
  342. Ogre::MeshPtr mesh(Ogre::MeshManager::getSingleton().load(mesh_name, "FFVII"));
  343. Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
  344. for (std::string anim : model.a){
  345. VGears::AFileManager &afl_mgr(VGears::AFileManager::GetSingleton());
  346. Ogre::String a_base_name;
  347. VGears::StringUtil::splitBase(anim, a_base_name);
  348. VGears::AFilePtr a
  349. = afl_mgr.load(a_base_name + ".a", "FFVII").staticCast<VGears::AFile>();
  350. // Convert the FF7 name to a more readable name set in the meta data.
  351. VGears::StringUtil::splitBase(anim, base_name);
  352. a->AddTo(skeleton, VGears::NameLookup::Animation(base_name));
  353. }
  354. ExportMesh(path, mesh);
  355. }
  356. catch (const Ogre::Exception& ex){
  357. std::cerr << "[ERROR] Ogre exception converting battle model "
  358. << model.hrc <<": " << ex.what() << std::endl;
  359. }
  360. catch (const std::exception& ex){
  361. std::cerr << "[ERROR] Exception converting battle model "
  362. << model.hrc << ": " << ex.what() << std::endl;
  363. }
  364. next_model_to_convert_ ++;
  365. return next_model_to_convert_;
  366. }
  367. void BattleDataInstaller::WriteEnemies(){
  368. for (Enemy enemy : enemies_){
  369. TiXmlDocument xml;
  370. std::unique_ptr<TiXmlElement> container(new TiXmlElement("enemy"));
  371. container->SetAttribute("id", enemy.id);
  372. container->SetAttribute("model", enemy.model);
  373. container->SetAttribute("name", enemy.name);
  374. container->SetAttribute("level", enemy.level);
  375. container->SetAttribute("exp", enemy.exp);
  376. container->SetAttribute("ap", enemy.ap);
  377. container->SetAttribute("money", enemy.money);
  378. container->SetAttribute("morph", enemy.morph);
  379. container->SetDoubleAttribute("back_damage", enemy.back_damage);
  380. std::unique_ptr<TiXmlElement> stats(new TiXmlElement("stats"));
  381. std::unique_ptr<TiXmlElement> stat_str(new TiXmlElement("stat"));
  382. stat_str->SetAttribute("id", "str");
  383. stat_str->SetAttribute("value", enemy.str);
  384. stats->LinkEndChild(stat_str.release());
  385. std::unique_ptr<TiXmlElement> stat_mag(new TiXmlElement("stat"));
  386. stat_mag->SetAttribute("id", "mag");
  387. stat_mag->SetAttribute("value", enemy.mag);
  388. stats->LinkEndChild(stat_mag.release());
  389. std::unique_ptr<TiXmlElement> stat_def(new TiXmlElement("stat"));
  390. stat_def->SetAttribute("id", "def");
  391. stat_def->SetAttribute("value", enemy.def);
  392. stats->LinkEndChild(stat_def.release());
  393. std::unique_ptr<TiXmlElement> stat_mdef(new TiXmlElement("stat"));
  394. stat_mdef->SetAttribute("id", "mdef");
  395. stat_mdef->SetAttribute("value", enemy.mdef);
  396. stats->LinkEndChild(stat_mdef.release());
  397. std::unique_ptr<TiXmlElement> stat_spd(new TiXmlElement("stat"));
  398. stat_spd->SetAttribute("id", "spd");
  399. stat_spd->SetAttribute("value", enemy.spd);
  400. stats->LinkEndChild(stat_spd.release());
  401. std::unique_ptr<TiXmlElement> stat_lck(new TiXmlElement("stat"));
  402. stat_lck->SetAttribute("id", "lck");
  403. stat_lck->SetAttribute("value", enemy.lck);
  404. stats->LinkEndChild(stat_lck.release());
  405. std::unique_ptr<TiXmlElement> stat_eva(new TiXmlElement("stat"));
  406. stat_eva->SetAttribute("id", "eva");
  407. stat_eva->SetAttribute("value", enemy.eva);
  408. stats->LinkEndChild(stat_eva.release());
  409. container->LinkEndChild(stats.release());
  410. std::unique_ptr<TiXmlElement> elements(new TiXmlElement("elements"));
  411. for (Enemy::Element element : enemy.elements){
  412. std::unique_ptr<TiXmlElement> xml_element(new TiXmlElement("element"));
  413. xml_element->SetAttribute("id", element.id);
  414. xml_element->SetDoubleAttribute("factor", element.factor);
  415. elements->LinkEndChild(xml_element.release());
  416. }
  417. container->LinkEndChild(elements.release());
  418. std::unique_ptr<TiXmlElement> immunities(new TiXmlElement("immunities"));
  419. for (Enemy::Immunity immunity : enemy.immunities){
  420. std::unique_ptr<TiXmlElement> xml_immunity(new TiXmlElement("immunity"));
  421. xml_immunity->SetAttribute("status", immunity.status);
  422. xml_immunity->SetDoubleAttribute("rate", immunity.rate);
  423. immunities->LinkEndChild(xml_immunity.release());
  424. }
  425. container->LinkEndChild(immunities.release());
  426. std::unique_ptr<TiXmlElement> attacks(new TiXmlElement("attacks"));
  427. for (Enemy::Attack attack : enemy.attacks){
  428. std::unique_ptr<TiXmlElement> xml_attack(new TiXmlElement("attack"));
  429. xml_attack->SetAttribute("status", attack.id);
  430. xml_attack->SetAttribute("camera", attack.camera);
  431. attacks->LinkEndChild(xml_attack.release());
  432. }
  433. container->LinkEndChild(attacks.release());
  434. std::unique_ptr<TiXmlElement> manip_attacks(new TiXmlElement("manipulate"));
  435. manip_attacks->SetAttribute("manipulable", enemy.manipulate_attacks.size() > 0 ? 1 : 0);
  436. for (u16 attack : enemy.manipulate_attacks){
  437. std::unique_ptr<TiXmlElement> xml_manip_attack(new TiXmlElement("attack"));
  438. xml_manip_attack->SetAttribute("id", attack);
  439. manip_attacks->LinkEndChild(xml_manip_attack.release());
  440. }
  441. container->LinkEndChild(manip_attacks.release());
  442. std::unique_ptr<TiXmlElement> steals(new TiXmlElement("steal"));
  443. for (Enemy::Item item : enemy.steal){
  444. std::unique_ptr<TiXmlElement> xml_steal(new TiXmlElement("item"));
  445. xml_steal->SetAttribute("id", item.id);
  446. xml_steal->SetDoubleAttribute("rate", item.rate);
  447. steals->LinkEndChild(xml_steal.release());
  448. }
  449. container->LinkEndChild(steals.release());
  450. std::unique_ptr<TiXmlElement> drops(new TiXmlElement("drop"));
  451. for (Enemy::Item item : enemy.drop){
  452. std::unique_ptr<TiXmlElement> xml_drop(new TiXmlElement("item"));
  453. xml_drop->SetAttribute("id", item.id);
  454. xml_drop->SetDoubleAttribute("rate", item.rate);
  455. drops->LinkEndChild(xml_drop.release());
  456. }
  457. container->LinkEndChild(drops.release());
  458. std::unique_ptr<TiXmlElement> animations(new TiXmlElement("animations"));
  459. for (unsigned int animation : enemy.animations){
  460. std::unique_ptr<TiXmlElement> xml_animation(new TiXmlElement("animation"));
  461. xml_animation->SetAttribute("id", animation);
  462. animations->LinkEndChild(xml_animation.release());
  463. }
  464. container->LinkEndChild(animations.release());
  465. xml.LinkEndChild(container.release());
  466. std::string file_name = BuildEnemyFileName(enemy);
  467. xml.SaveFile(output_dir_ + file_name);
  468. enemy_map_[enemy.id] = file_name;
  469. }
  470. }
  471. void BattleDataInstaller::WriteAttacks(){
  472. for (Attack attack : attacks_){
  473. TiXmlDocument xml;
  474. std::unique_ptr<TiXmlElement> container(new TiXmlElement("Attack"));
  475. container->SetAttribute("id", attack.id);
  476. container->SetAttribute("name", attack.name);
  477. container->SetAttribute("mp", attack.mp);
  478. container->SetAttribute("reflectable", attack.reflectable);
  479. std::unique_ptr<TiXmlElement> damage(new TiXmlElement("Damage"));
  480. std::unique_ptr<TiXmlElement> damage_power(new TiXmlElement("Parameter"));
  481. damage_power->SetAttribute("id", "power");
  482. damage_power->SetAttribute("value", attack.damage.power);
  483. damage->LinkEndChild(damage_power.release());
  484. std::unique_ptr<TiXmlElement> damage_formula(new TiXmlElement("Parameter"));
  485. damage_formula->SetAttribute("id", "formula");
  486. damage_formula->SetAttribute("value", attack.damage.formula);
  487. damage->LinkEndChild(damage_formula.release());
  488. std::unique_ptr<TiXmlElement> damage_modifier(new TiXmlElement("Parameter"));
  489. damage_modifier->SetAttribute("id", "modifier");
  490. damage_modifier->SetAttribute("value", attack.damage.modifier);
  491. damage->LinkEndChild(damage_modifier.release());
  492. std::unique_ptr<TiXmlElement> damage_acc(new TiXmlElement("Parameter"));
  493. damage_acc->SetAttribute("id", "accuracy");
  494. damage_acc->SetAttribute("value", attack.damage.accuracy);
  495. damage->LinkEndChild(damage_acc.release());
  496. std::unique_ptr<TiXmlElement> damage_darkness(new TiXmlElement("Parameter"));
  497. damage_darkness->SetAttribute("id", "affected_by_darkness");
  498. damage_darkness->SetAttribute("value", attack.damage.affected_by_darkness);
  499. damage->LinkEndChild(damage_darkness.release());
  500. std::unique_ptr<TiXmlElement> damage_restore(new TiXmlElement("Parameter"));
  501. damage_restore->SetAttribute("id", "restore");
  502. damage_restore->SetAttribute("value", static_cast<int>(attack.damage.restore));
  503. damage->LinkEndChild(damage_restore.release());
  504. std::unique_ptr<TiXmlElement> damage_mp(new TiXmlElement("Parameter"));
  505. damage_mp->SetAttribute("id", "damage_mp");
  506. damage_mp->SetAttribute("value", attack.damage.damage_mp);
  507. damage->LinkEndChild(damage_mp.release());
  508. std::unique_ptr<TiXmlElement> damage_drain_hp(new TiXmlElement("Parameter"));
  509. damage_drain_hp->SetAttribute("id", "drain_hp");
  510. damage_drain_hp->SetAttribute("value", attack.damage.drain_hp);
  511. damage->LinkEndChild(damage_drain_hp.release());
  512. std::unique_ptr<TiXmlElement> damage_drain_mp(new TiXmlElement("Parameter"));
  513. damage_drain_mp->SetAttribute("id", "drain_mp");
  514. damage_drain_mp->SetAttribute("value", attack.damage.drain_mp);
  515. damage->LinkEndChild(damage_drain_mp.release());
  516. std::unique_ptr<TiXmlElement> damage_ignore(new TiXmlElement("Parameter"));
  517. damage_ignore->SetAttribute("id", "ignore_status_defense");
  518. damage_ignore->SetAttribute("value", attack.damage.ignore_status_defense);
  519. damage->LinkEndChild(damage_ignore.release());
  520. std::unique_ptr<TiXmlElement> damage_critical(new TiXmlElement("Parameter"));
  521. damage_critical->SetAttribute("id", "always_critical");
  522. damage_critical->SetAttribute("value", attack.damage.critical);
  523. damage->LinkEndChild(damage_critical.release());
  524. container->LinkEndChild(damage.release());
  525. std::unique_ptr<TiXmlElement> target_opts(new TiXmlElement("Target"));
  526. std::unique_ptr<TiXmlElement> target_se(new TiXmlElement("Parameter"));
  527. target_se->SetAttribute("id", "selection_enabled");
  528. target_se->SetAttribute("value", attack.target.selection_enabled);
  529. target_opts->LinkEndChild(target_se.release());
  530. std::unique_ptr<TiXmlElement> target_de(new TiXmlElement("Parameter"));
  531. target_de->SetAttribute("id", "default_enemy");
  532. target_de->SetAttribute("value", attack.target.default_enemy);
  533. target_opts->LinkEndChild(target_de.release());
  534. std::unique_ptr<TiXmlElement> target_dm(new TiXmlElement("Parameter"));
  535. target_dm->SetAttribute("id", "default_multiple");
  536. target_dm->SetAttribute("value", attack.target.default_multiple);
  537. target_opts->LinkEndChild(target_dm.release());
  538. std::unique_ptr<TiXmlElement> target_tm(new TiXmlElement("Parameter"));
  539. target_tm->SetAttribute("id", "toggle_multiple");
  540. target_tm->SetAttribute("value", attack.target.toggle_multiple);
  541. target_opts->LinkEndChild(target_tm.release());
  542. std::unique_ptr<TiXmlElement> target_fr(new TiXmlElement("Parameter"));
  543. target_fr->SetAttribute("id", "fixed_row");
  544. target_fr->SetAttribute("value", attack.target.fixed_row);
  545. target_opts->LinkEndChild(target_fr.release());
  546. std::unique_ptr<TiXmlElement> target_sr(new TiXmlElement("Parameter"));
  547. target_sr->SetAttribute("id", "short_range");
  548. target_sr->SetAttribute("value", attack.target.short_range);
  549. target_opts->LinkEndChild(target_sr.release());
  550. std::unique_ptr<TiXmlElement> target_ar(new TiXmlElement("Parameter"));
  551. target_ar->SetAttribute("id", "all_rows");
  552. target_ar->SetAttribute("value", attack.target.all_rows);
  553. target_opts->LinkEndChild(target_ar.release());
  554. std::unique_ptr<TiXmlElement> target_rd(new TiXmlElement("Parameter"));
  555. target_rd->SetAttribute("id", "random");
  556. target_rd->SetAttribute("value", attack.target.random);
  557. target_opts->LinkEndChild(target_rd.release());
  558. std::unique_ptr<TiXmlElement> target_od(new TiXmlElement("Parameter"));
  559. target_od->SetAttribute("id", "only_for_dead");
  560. target_od->SetAttribute("value", attack.target.only_for_dead);
  561. target_opts->LinkEndChild(target_od.release());
  562. std::unique_ptr<TiXmlElement> target_nr(new TiXmlElement("Parameter"));
  563. target_nr->SetAttribute("id", "no_retarget");
  564. target_nr->SetAttribute("value", attack.target.no_re_target);
  565. target_opts->LinkEndChild(target_nr.release());
  566. container->LinkEndChild(target_opts.release());
  567. std::unique_ptr<TiXmlElement> effects(new TiXmlElement("Effects"));
  568. for (Attack::StatusEffect effect : attack.status_effects){
  569. std::unique_ptr<TiXmlElement> xml_effect(new TiXmlElement("Effect"));
  570. xml_effect->SetAttribute("id", effect.id);
  571. xml_effect->SetDoubleAttribute("chance", effect.chance);
  572. xml_effect->SetAttribute("mode", GameData::STATUS_EFFECT_MODE::to_string(effect.type));
  573. effects->LinkEndChild(xml_effect.release());
  574. }
  575. container->LinkEndChild(effects.release());
  576. std::unique_ptr<TiXmlElement> elements(new TiXmlElement("Elements"));
  577. for (unsigned int element : attack.elements){
  578. std::unique_ptr<TiXmlElement> xml_element(new TiXmlElement("Element"));
  579. xml_element->SetAttribute("id", element);
  580. elements->LinkEndChild(xml_element.release());
  581. }
  582. container->LinkEndChild(elements.release());
  583. std::unique_ptr<TiXmlElement> anim(new TiXmlElement("Animation"));
  584. std::unique_ptr<TiXmlElement> anim_e(new TiXmlElement("Parameter"));
  585. anim_e->SetAttribute("id", "effect");
  586. anim_e->SetAttribute("value", attack.animation.effect);
  587. anim->LinkEndChild(anim_e.release());
  588. std::unique_ptr<TiXmlElement> anim_ie(new TiXmlElement("Parameter"));
  589. anim_ie->SetAttribute("id", "impact_effect");
  590. anim_ie->SetAttribute("value", attack.animation.impact_effect);
  591. anim->LinkEndChild(anim_ie.release());
  592. std::unique_ptr<TiXmlElement> anim_s(new TiXmlElement("Parameter"));
  593. anim_s->SetAttribute("id", "sound");
  594. anim_s->SetAttribute("value", attack.animation.sound);
  595. anim->LinkEndChild(anim_s.release());
  596. std::unique_ptr<TiXmlElement> anim_ha(new TiXmlElement("Parameter"));
  597. anim_ha->SetAttribute("id", "hurt_anim");
  598. anim_ha->SetAttribute("value", attack.animation.hurt_anim);
  599. anim->LinkEndChild(anim_ha.release());
  600. std::unique_ptr<TiXmlElement> anim_c(new TiXmlElement("Parameter"));
  601. anim_c->SetAttribute("id", "camera_single");
  602. anim_c->SetAttribute("value", attack.animation.camera_single);
  603. anim->LinkEndChild(anim_c.release());
  604. std::unique_ptr<TiXmlElement> anim_cm(new TiXmlElement("Parameter"));
  605. anim_cm->SetAttribute("id", "camera_multiple");
  606. anim_cm->SetAttribute("value", attack.animation.camera_multiple);
  607. anim->LinkEndChild(anim_cm.release());
  608. container->LinkEndChild(anim.release());
  609. xml.LinkEndChild(container.release());
  610. std::string file_name = BuildAttackFileName(attack);
  611. xml.SaveFile(output_dir_ + file_name);
  612. attack_map_[attack.id] = file_name;
  613. }
  614. }
  615. void BattleDataInstaller::WriteFormations(){
  616. for (Formation formation : formations_){
  617. TiXmlDocument xml;
  618. std::unique_ptr<TiXmlElement> container(new TiXmlElement("formation"));
  619. container->SetAttribute("id", formation.id);
  620. container->SetAttribute("next", formation.next_formation);
  621. container->SetDoubleAttribute("escape_difficulty", formation.escape_counter);
  622. container->SetAttribute("escapable", formation.escapable);
  623. container->SetAttribute("skip_victory_pose", formation.skip_victory_pose);
  624. container->SetAttribute("skip_spoils", formation.skip_spoils);
  625. container->SetAttribute("skip_victory_pose", formation.skip_victory_pose);
  626. container->SetAttribute("preemptive_disabled", formation.preemptive_disabled);
  627. container->SetAttribute("layout", static_cast<int>(formation.layout));
  628. std::unique_ptr<TiXmlElement> location(new TiXmlElement("location"));
  629. location->SetAttribute("id", formation.location);
  630. location->SetAttribute("name", formation.location_name);
  631. container->LinkEndChild(location.release());
  632. std::unique_ptr<TiXmlElement> enemies(new TiXmlElement("enemies"));
  633. for (Formation::Enemy enemy : formation.enemies){
  634. std::unique_ptr<TiXmlElement> enemy_xml(new TiXmlElement("enemy"));
  635. enemy_xml->SetAttribute("id", enemy.id);
  636. enemy_xml->SetAttribute("x", enemy.x);
  637. enemy_xml->SetAttribute("y", enemy.y);
  638. enemy_xml->SetAttribute("z", enemy.z);
  639. enemy_xml->SetAttribute("row", enemy.row);
  640. std::string cover = "";
  641. for (int c = 0; c < 5; c ++) cover += enemy.cover[c];
  642. enemy_xml->SetAttribute("cover", cover);
  643. enemy_xml->SetAttribute("visible", enemy.visible);
  644. enemy_xml->SetAttribute("direction", enemy.direction);
  645. enemy_xml->SetAttribute("targeteable", enemy.targeteable);
  646. enemy_xml->SetAttribute("main_script_active", enemy.main_script_active);
  647. enemies->LinkEndChild(enemy_xml.release());
  648. }
  649. container->LinkEndChild(enemies.release());
  650. std::unique_ptr<TiXmlElement> cameras(new TiXmlElement("camera"));
  651. cameras->SetAttribute("initial", formation.initial_camera_position);
  652. int c = 0;
  653. for (Formation::Camera camera : formation.camera_positions){
  654. std::unique_ptr<TiXmlElement> camera_xml(new TiXmlElement("position"));
  655. camera_xml->SetAttribute("id", c);
  656. camera_xml->SetAttribute("x", camera.x);
  657. camera_xml->SetAttribute("y", camera.y);
  658. camera_xml->SetAttribute("z", camera.z);
  659. camera_xml->SetAttribute("direction_x", camera.d_x);
  660. camera_xml->SetAttribute("direction_y", camera.d_y);
  661. camera_xml->SetAttribute("direction_z", camera.d_z);
  662. cameras->LinkEndChild(camera_xml.release());
  663. c ++;
  664. }
  665. container->LinkEndChild(cameras.release());
  666. std::unique_ptr<TiXmlElement> arena(new TiXmlElement("arena"));
  667. arena->SetAttribute("is_arena", formation.is_arena_battle);
  668. for (int next : formation.next_arena_formation_candidates){
  669. std::unique_ptr<TiXmlElement> next_xml(new TiXmlElement("next_candidate"));
  670. next_xml->SetAttribute("id", next);
  671. arena->LinkEndChild(next_xml.release());
  672. }
  673. container->LinkEndChild(arena.release());
  674. xml.LinkEndChild(container.release());
  675. std::string file_name = std::to_string(formation.id);
  676. while (file_name.length() < 4) file_name = "0" + file_name;
  677. file_name = "gamedata/formation/" + file_name + ".xml";
  678. xml.SaveFile(output_dir_ + file_name);
  679. formation_map_[formation.id] = file_name;
  680. }
  681. }
  682. void BattleDataInstaller::WriteCharacterData(){
  683. TiXmlDocument xml;
  684. std::unique_ptr<TiXmlElement> container(new TiXmlElement("characters"));
  685. for (CharacterData data : character_data_){
  686. std::unique_ptr<TiXmlElement> xml_character(new TiXmlElement("character"));
  687. xml_character->SetAttribute("id", data.id);
  688. for (CharacterModel model : data.models){
  689. std::unique_ptr<TiXmlElement> xml_model(new TiXmlElement("model"));
  690. xml_model->SetAttribute("name", model.name);
  691. xml_model->SetAttribute("file", model.model);
  692. xml_character->LinkEndChild(xml_model.release());
  693. }
  694. container->LinkEndChild(xml_character.release());
  695. }
  696. xml.LinkEndChild(container.release());
  697. xml.SaveFile(output_dir_ + "models/battle/characters.xml");
  698. }
  699. void BattleDataInstaller::WriteSceneData(){
  700. TiXmlDocument xml;
  701. std::unique_ptr<TiXmlElement> container(new TiXmlElement("scenes"));
  702. for (SceneModel data : scene_data_){
  703. std::unique_ptr<TiXmlElement> xml_scene(new TiXmlElement("scene"));
  704. xml_scene->SetAttribute("id", data.id);
  705. xml_scene->SetAttribute("file", data.model);
  706. xml_scene->SetAttribute("description", data.description);
  707. container->LinkEndChild(xml_scene.release());
  708. }
  709. xml.LinkEndChild(container.release());
  710. xml.SaveFile(output_dir_ + "models/battle/scenes.xml");
  711. }
  712. std::string BattleDataInstaller::BuildEnemyFileName(Enemy enemy){
  713. std::string file_name = "gamedata/enemy/";
  714. std::string id = std::to_string(enemy.id);
  715. while (id.size() < 4) id = "0" + id;
  716. file_name += id + ".xml";
  717. return file_name;
  718. }
  719. std::string BattleDataInstaller::BuildAttackFileName(Attack attack){
  720. std::string file_name = "gamedata/attack/";
  721. std::string id = std::to_string(attack.id);
  722. while (id.size() < 4) id = "0" + id;
  723. file_name += (id + "_");
  724. for (int n = 0; n < attack.name.size(); n ++){
  725. if (
  726. (attack.name[n] >= '0' && attack.name[n] <= '9')
  727. || (attack.name[n] >= 'a' && attack.name[n] <= 'z')
  728. || (attack.name[n] >= 'A' && attack.name[n] <= 'Z')
  729. ){
  730. file_name += attack.name[n];
  731. }
  732. }
  733. file_name += ".xml";
  734. return file_name;
  735. }
  736. File* BattleDataInstaller::ExtractGZipScene(File file){
  737. u32 offset = 0;
  738. u8* file_buffer = static_cast<u8*>(malloc(file.GetFileSize()));
  739. file.GetFileBuffer(file_buffer, 0, file.GetFileSize());
  740. u32 extract_size = 256 * 1024;
  741. u8* extract_buffer = static_cast<u8*>(malloc(extract_size));
  742. int ret;
  743. z_stream strm;
  744. strm.zalloc = Z_NULL; // Used to allocate the internal state.
  745. strm.zfree = Z_NULL; // Used to free the internal state.
  746. strm.opaque = Z_NULL; // Private data object passed to zalloc and zfree.
  747. strm.next_in = file_buffer + offset; //Next input byte.
  748. strm.avail_in = file.GetFileSize() - offset; // Number of bytes available at next_in.
  749. strm.next_out = extract_buffer; // Next output byte should be put there.
  750. strm.avail_out = extract_size; // Remaining free space at next_out.
  751. ret = inflateInit2(&strm, 15 + 32);
  752. if (ret != Z_OK){
  753. switch (ret){
  754. case Z_MEM_ERROR:
  755. inflateEnd(&strm);
  756. std::cout << "Warning: inflateInit2 - Z_MEM_ERROR in file "
  757. << file.GetFileName() << std::endl;
  758. break;
  759. case Z_STREAM_ERROR:
  760. inflateEnd(&strm);
  761. std::cout << "Warning: inflateInit2 - Z_STREAM_ERROR in file "
  762. << file.GetFileName() << std::endl;
  763. break;
  764. default:
  765. inflateEnd(&strm);
  766. std::cout << "Warning: inflateInit2 - unknown error in file "
  767. << file.GetFileName() << std::endl;
  768. }
  769. return NULL;
  770. }
  771. do{
  772. if (strm.next_out == NULL){
  773. inflateEnd(&strm);
  774. std::cout << "Warning: strm.next_out == NULL in file "
  775. << file.GetFileName() << std::endl;
  776. return NULL;
  777. }
  778. ret = inflate(&strm, Z_NO_FLUSH);
  779. assert(ret != Z_STREAM_ERROR);
  780. switch (ret){
  781. case Z_NEED_DICT:
  782. inflateEnd(&strm);
  783. std::cout << "Warning: inflate - Z_NEED_DICT in file "
  784. << file.GetFileName() << std::endl;
  785. return NULL;
  786. case Z_DATA_ERROR:
  787. inflateEnd(&strm);
  788. std::cout << "Warning: inflate - Z_DATA_ERROR in file "
  789. << file.GetFileName() << std::endl;
  790. return NULL;
  791. case Z_MEM_ERROR:
  792. inflateEnd(&strm);
  793. std::cout << "Warning: inflate - Z_MEM_ERROR in file "
  794. << file.GetFileName() << std::endl;
  795. return NULL;
  796. }
  797. if (ret != Z_STREAM_END){
  798. extract_buffer = static_cast<u8*>(realloc(extract_buffer, extract_size * 2));
  799. if (extract_buffer == NULL){
  800. inflateEnd(&strm);
  801. //std::cout << "Warning: extract_buffer == NULL in file "
  802. // << file.GetFileName() << std::endl;
  803. return NULL;
  804. }
  805. strm.next_out = static_cast<u8*>(extract_buffer + extract_size);
  806. strm.avail_out = extract_size;
  807. extract_size *= 2;
  808. }
  809. } while (ret != Z_STREAM_END);
  810. extract_size = extract_size - strm.avail_out;
  811. (void)inflateEnd(&strm);
  812. if (ret != Z_STREAM_END){
  813. std::cout << "Warning: ret != Z_STREAM_END in file " << file.GetFileName() << std::endl;
  814. return NULL;
  815. }
  816. File* out_file = new File(extract_buffer, 0, extract_size);
  817. return out_file;
  818. }
  819. void BattleDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPtr &mesh){
  820. VGears::String base_mesh_name;
  821. VGears::StringUtil::splitFull(mesh->getName(), base_mesh_name);
  822. Ogre::MeshSerializer mesh_serializer;
  823. Ogre::SkeletonPtr skeleton(mesh->getSkeleton());
  824. Ogre::SkeletonSerializer skeleton_serializer;
  825. skeleton_serializer.exportSkeleton(
  826. skeleton.get(), output_dir_ + "models/battle/" + outdir + base_mesh_name + ".skeleton"
  827. );
  828. mesh->setSkeletonName(outdir + base_mesh_name + ".skeleton");
  829. mesh_serializer.exportMesh(
  830. mesh.get(), output_dir_ + "models/battle/" + outdir + mesh->getName()
  831. );
  832. Ogre::MaterialSerializer mat_ser;
  833. std::set<std::string> textures;
  834. Ogre::Mesh::SubMeshList sub_meshes = mesh->getSubMeshes();
  835. for (Ogre::SubMesh* sub_mesh : sub_meshes){
  836. // Change material name to avoid conflicts with field model materials.
  837. sub_mesh->setMaterialName(sub_mesh->getMaterialName());
  838. Ogre::MaterialPtr mat(
  839. Ogre::MaterialManager::getSingleton().getByName(sub_mesh->getMaterialName())
  840. );
  841. if (mat == nullptr) continue;
  842. for (size_t techs = 0; techs < mat->getNumTechniques(); techs ++){
  843. Ogre::Technique* tech = mat->getTechnique(techs);
  844. if (tech == nullptr) continue;
  845. for (size_t pass_num = 0; pass_num < tech->getNumPasses(); pass_num ++){
  846. Ogre::Pass* pass = tech->getPass(pass_num);
  847. if (pass == nullptr) continue;
  848. for (
  849. size_t texture_unit_num = 0;
  850. texture_unit_num < pass->getNumTextureUnitStates();
  851. texture_unit_num ++
  852. ){
  853. Ogre::TextureUnitState* unit = pass->getTextureUnitState(texture_unit_num);
  854. if (unit && unit->getTextureName().empty() == false){
  855. // Ensure the output material script references png files, no tex files.
  856. Ogre::String base_name;
  857. VGears::StringUtil::splitBase(unit->getTextureName(), base_name);
  858. unit->setTextureName(outdir + base_name + ".png");
  859. textures.insert(unit->getTextureName());
  860. }
  861. }
  862. }
  863. }
  864. if (std::count(materials_.begin(), materials_.end(), sub_mesh->getMaterialName()) == 0){
  865. mat_ser.queueForExport(mat);
  866. materials_.push_back(sub_mesh->getMaterialName());
  867. }
  868. }
  869. mat_ser.exportQueued(
  870. output_dir_ + "models/battle/" + outdir + base_mesh_name + VGears::EXT_MATERIAL
  871. );
  872. }
  873. void BattleDataInstaller::GenerateRsdFiles(Model model, std::string path){
  874. std::sort(model.tex.begin(), model.tex.end());
  875. // For each p file, generate a rsd file.
  876. for (std::string p : model.p){
  877. std::string base_name = p.substr(0, 4);
  878. std::string content = "@RSD940102\n";
  879. content += "PLY=" + base_name + ".PLY\n";
  880. content += "MAT=" + base_name + ".MAT\n";
  881. content += "GRP=" + base_name + ".GRP\n";
  882. content += "NTEX=" + std::to_string(model.tex.size()) + "\n";
  883. for (int t = 0; t < model.tex.size(); t ++)
  884. content += "TEX[" + std::to_string(t) + "]=" + model.tex[t] + "\n";
  885. content += "\n";
  886. std::ofstream out(path + base_name + ".rsd");
  887. out << content;
  888. out.close();
  889. }
  890. }
  891. void BattleDataInstaller::DecompileHrc(
  892. File compiled, Model model, std::string path, std::string skeleton_name
  893. ){
  894. compiled.SetOffset(0);
  895. compiled.readU32LE();
  896. compiled.readU32LE();
  897. compiled.readU32LE();
  898. std::sort(model.p.begin(), model.p.end());
  899. std::string decompiled(":HEADER_BLOCK 2\n:SKELETON " + skeleton_name + "\n");
  900. int bones = compiled.readU32LE();
  901. compiled.SetOffset(compiled.GetFileSize() - (bones * 12));
  902. decompiled += ":BONES " + std::to_string(bones) + "\n\n";
  903. int p_file_index = 0;
  904. for (int b = 0; b < bones; b ++){
  905. decompiled += "bone" + std::to_string(b) + "\n";
  906. int parent = compiled.readU32LE();
  907. if (parent == 0xFFFFFFFF) decompiled += "root\n";
  908. else decompiled += "bone" + std::to_string(parent) + "\n";
  909. union {
  910. float f;
  911. unsigned long ul;
  912. unsigned char b[4];
  913. } u;
  914. u.b[0] = compiled.readU8();
  915. u.b[1] = compiled.readU8();
  916. u.b[2] = compiled.readU8();
  917. u.b[3] = compiled.readU8();
  918. decompiled += std::to_string(u.f * -1) + "\n";
  919. int offset = compiled.readU32LE();
  920. if (offset == 0) decompiled += "0\n\n";
  921. else {
  922. decompiled += std::to_string(offset) + " ";
  923. if (p_file_index < model.p.size()) decompiled += model.p[p_file_index].substr(0, 4);
  924. p_file_index ++;
  925. decompiled += "\n\n";
  926. }
  927. }
  928. std::ofstream out(path);
  929. out << decompiled;
  930. out.close();
  931. }
  932. void BattleDataInstaller::DecompileSceneHrc(
  933. File compiled, Model model, std::string path, std::string skeleton_name
  934. ){
  935. compiled.SetOffset(0);
  936. compiled.readU32LE(); // Skip 4 bytes
  937. compiled.readU32LE(); // Skip 4 bytes
  938. compiled.readU32LE(); // Skip 4 bytes
  939. compiled.readU32LE(); // Supposed to be the number of bones, but for scenes is always 0.
  940. std::sort(model.p.begin(), model.p.end());
  941. std::string decompiled(":HEADER_BLOCK 2\n:SKELETON " + skeleton_name + "\n");
  942. int bones = model.p.size();
  943. decompiled += ":BONES " + std::to_string(bones) + "\n\n";
  944. int p_file_index = 0;
  945. for (int b = 0; b < bones; b ++){
  946. decompiled += "bone" + std::to_string(b) + "\n";
  947. if (b == 0) decompiled += "root\n";
  948. else decompiled += ("bone" + std::to_string(b - 1) + "\n");
  949. decompiled += "100.000000\n";
  950. decompiled += "1 " + model.p[b].substr(0, 4) + "\n\n";
  951. }
  952. std::ofstream out(path);
  953. out << decompiled;
  954. out.close();
  955. }