yamlupgrade.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "yamlupgrade.hpp"
  4. using namespace rathena::tool_yamlupgrade;
  5. static bool upgrade_achievement_db(std::string file, const uint32 source_version);
  6. static bool upgrade_item_db(std::string file, const uint32 source_version);
  7. static bool upgrade_job_stats(std::string file, const uint32 source_version);
  8. static bool upgrade_status_db(std::string file, const uint32 source_version);
  9. static bool upgrade_map_drops_db(std::string file, const uint32 source_version);
  10. static bool upgrade_enchantgrade_db( std::string file, const uint32 source_version );
  11. static bool upgrade_item_group_db( std::string file, const uint32 source_version );
  12. template<typename Func>
  13. bool process(const std::string &type, uint32 version, const std::vector<std::string> &paths, const std::string &name, Func lambda) {
  14. for (const std::string &path : paths) {
  15. const std::string name_ext = name + ".yml";
  16. const std::string from = path + "/" + name_ext;
  17. const std::string to = path + "/" + name + "-upgrade.yml";
  18. inNode.reset();
  19. if (fileExists(from)) {
  20. inNode = YAML::LoadFile(from);
  21. uint32 source_version = getHeaderVersion(inNode);
  22. if (source_version >= version) {
  23. continue;
  24. }
  25. if (!askConfirmation("Found the file \"%s\", which requires an upgrade.\nDo you want to convert it now? (Y/N)\n", from.c_str())) {
  26. continue;
  27. }
  28. if (fileExists(to)) {
  29. if (!askConfirmation("The file \"%s\" already exists.\nDo you want to replace it? (Y/N)\n", to.c_str())) {
  30. continue;
  31. }
  32. }
  33. ShowNotice("Upgrade process has begun.\n");
  34. std::ofstream outFile;
  35. body.~Emitter();
  36. new (&body) YAML::Emitter();
  37. outFile.open(to);
  38. if (!outFile.is_open()) {
  39. ShowError("Can not open file \"%s\" for writing.\n", to.c_str());
  40. return false;
  41. }
  42. prepareHeader(outFile, type, version, name);
  43. if (inNode["Body"].IsDefined()) {
  44. prepareBody();
  45. if (!lambda(path, name_ext, source_version)) {
  46. outFile.close();
  47. return false;
  48. }
  49. finalizeBody();
  50. outFile << body.c_str();
  51. }
  52. prepareFooter(outFile);
  53. // Make sure there is an empty line at the end of the file for git
  54. outFile << "\n";
  55. outFile.close();
  56. }
  57. }
  58. return true;
  59. }
  60. bool YamlUpgradeTool::initialize( int32 argc, char* argv[] ){
  61. const std::string path_db = std::string(db_path);
  62. const std::string path_db_mode = path_db + "/" + DBPATH;
  63. const std::string path_db_import = path_db + "/" + DBIMPORT;
  64. // Loads required conversion constants
  65. if (fileExists(item_db.getDefaultLocation())) {
  66. item_db.load();
  67. } else {
  68. parse_item_constants_txt((path_db_mode + "item_db.txt").c_str());
  69. parse_item_constants_txt((path_db_import + "item_db.txt").c_str());
  70. }
  71. if (fileExists(mob_db.getDefaultLocation())) {
  72. mob_db.load();
  73. } else {
  74. sv_readdb(path_db_mode.c_str(), "mob_db.txt", ',', 31 + 2 * MAX_MVP_DROP + 2 * MAX_MOB_DROP, 31 + 2 * MAX_MVP_DROP + 2 * MAX_MOB_DROP, -1, &parse_mob_constants_txt, false);
  75. sv_readdb(path_db_import.c_str(), "mob_db.txt", ',', 31 + 2 * MAX_MVP_DROP + 2 * MAX_MOB_DROP, 31 + 2 * MAX_MVP_DROP + 2 * MAX_MOB_DROP, -1, &parse_mob_constants_txt, false);
  76. }
  77. if (fileExists(skill_db.getDefaultLocation())) {
  78. skill_db.load();
  79. } else {
  80. sv_readdb(path_db_mode.c_str(), "skill_db.txt", ',', 18, 18, -1, parse_skill_constants_txt, false);
  81. sv_readdb(path_db_import.c_str(), "skill_db.txt", ',', 18, 18, -1, parse_skill_constants_txt, false);
  82. }
  83. // Load constants
  84. #define export_constant_npc(a) export_constant(a)
  85. #include <map/script_constants.hpp>
  86. std::vector<std::string> root_paths = {
  87. path_db,
  88. path_db_mode,
  89. path_db_import
  90. };
  91. if (!process("ACHIEVEMENT_DB", 2, root_paths, "achievement_db", [](const std::string &path, const std::string &name_ext, uint32 source_version) -> bool {
  92. return upgrade_achievement_db(path + name_ext, source_version);
  93. })) {
  94. return false;
  95. }
  96. if (!process("ITEM_DB", 3, root_paths, "item_db", [](const std::string& path, const std::string& name_ext, uint32 source_version) -> bool {
  97. return upgrade_item_db(path + name_ext, source_version);
  98. })) {
  99. return false;
  100. }
  101. if (!process("JOB_STATS", 2, root_paths, "job_stats", [](const std::string& path, const std::string& name_ext, uint32 source_version) -> bool {
  102. return upgrade_job_stats(path + name_ext, source_version);
  103. })) {
  104. return false;
  105. }
  106. if (!process("STATUS_DB", 3, root_paths, "status", [](const std::string& path, const std::string& name_ext, uint32 source_version) -> bool {
  107. return upgrade_status_db(path + name_ext, source_version);
  108. })) {
  109. return false;
  110. }
  111. if (!process("MAP_DROP_DB", 2, root_paths, "map_drops", [](const std::string& path, const std::string& name_ext, uint32 source_version) -> bool {
  112. return upgrade_map_drops_db(path + name_ext, source_version);
  113. })) {
  114. return 0;
  115. }
  116. if( !process( "ENCHANTGRADE_DB", 3, root_paths, "enchantgrade", []( const std::string& path, const std::string& name_ext, uint32 source_version ) -> bool {
  117. return upgrade_enchantgrade_db( path + name_ext, source_version );
  118. } ) ){
  119. return false;
  120. }
  121. if( !process( "ITEM_GROUP_DB", 4, root_paths, "item_group_db", []( const std::string& path, const std::string& name_ext, uint32 source_version ) -> bool {
  122. return upgrade_item_group_db( path + name_ext, source_version );
  123. } ) ){
  124. return false;
  125. }
  126. return true;
  127. }
  128. // Implementation of the upgrade functions
  129. static bool upgrade_achievement_db(std::string file, const uint32 source_version) {
  130. size_t entries = 0;
  131. for (const auto &input : inNode["Body"]) {
  132. body << YAML::BeginMap;
  133. body << YAML::Key << "Id" << YAML::Value << input["ID"];
  134. std::string constant = input["Group"].as<std::string>();
  135. constant.erase(0, 3); // Remove "AG_"
  136. if (constant.compare("Chat") == 0) // Chat -> Chatting
  137. constant.insert(4, "ting");
  138. else if (constant.compare("Hear") == 0 || constant.compare("See") == 0)
  139. constant = "Chatting"; // Aegis treats these as general "Talk to NPC" achievements.
  140. else if (constant.compare("Refine") == 0) { // Refine -> Enchant
  141. constant.erase(0, 6);
  142. constant = "Enchant" + constant;
  143. }
  144. body << YAML::Key << "Group" << YAML::Value << name2Upper(constant);
  145. body << YAML::Key << "Name" << YAML::Value << input["Name"];
  146. if (input["Target"].IsDefined()) {
  147. body << YAML::Key << "Targets";
  148. body << YAML::BeginSeq;
  149. for (const auto &it : input["Target"]) {
  150. body << YAML::BeginMap;
  151. body << YAML::Key << "Id" << YAML::Value << it["Id"];
  152. if (it["MobID"].IsDefined()) {
  153. uint16 mob_id = it["MobID"].as<uint16>();
  154. std::string *mob_name = util::umap_find(aegis_mobnames, mob_id);
  155. if (mob_name == nullptr) {
  156. ShowWarning("mob_avail reading: Invalid mob-class %hu, Mob not read.\n", mob_id);
  157. return false;
  158. }
  159. body << YAML::Key << "Mob" << YAML::Value << *mob_name;
  160. }
  161. if (it["Count"].IsDefined() && it["Count"].as<int32>() > 1)
  162. body << YAML::Key << "Count" << YAML::Value << it["Count"];
  163. body << YAML::EndMap;
  164. }
  165. body << YAML::EndSeq;
  166. }
  167. if (input["Condition"].IsDefined())
  168. body << YAML::Key << "Condition" << YAML::Value << input["Condition"];
  169. if (input["Map"].IsDefined())
  170. body << YAML::Key << "Map" << YAML::Value << input["Map"];
  171. if (input["Dependent"].IsDefined()) {
  172. body << YAML::Key << "Dependents";
  173. body << YAML::BeginMap;
  174. for (const auto &it : input["Dependent"]) {
  175. body << YAML::Key << it["Id"] << YAML::Value << true;
  176. }
  177. body << YAML::EndMap;
  178. }
  179. /**
  180. * Example usage for adding label at specific version.
  181. if (source_version < ?) {
  182. body << YAML::Key << "CustomLabel" << YAML::Value << "Unique";
  183. }
  184. */
  185. if (input["Reward"].IsDefined()) {
  186. body << YAML::Key << "Rewards";
  187. body << YAML::BeginMap;
  188. if (input["Reward"]["ItemID"].IsDefined()) {
  189. t_itemid item_id = input["Reward"]["ItemID"].as<t_itemid>();
  190. std::string *item_name = util::umap_find(aegis_itemnames, item_id);
  191. if (item_name == nullptr) {
  192. ShowError("Reward item name for item ID %u is not known.\n", item_id);
  193. return false;
  194. }
  195. body << YAML::Key << "Item" << YAML::Value << *item_name;
  196. }
  197. if (input["Reward"]["Amount"].IsDefined() && input["Reward"]["Amount"].as<uint16>() > 1)
  198. body << YAML::Key << "Amount" << YAML::Value << input["Reward"]["Amount"];
  199. if (input["Reward"]["Script"].IsDefined())
  200. body << YAML::Key << "Script" << YAML::Value << input["Reward"]["Script"];
  201. if (input["Reward"]["TitleID"].IsDefined())
  202. body << YAML::Key << "TitleId" << YAML::Value << input["Reward"]["TitleID"];
  203. body << YAML::EndMap;
  204. }
  205. body << YAML::Key << "Score" << YAML::Value << input["Score"];
  206. body << YAML::EndMap;
  207. entries++;
  208. }
  209. ShowStatus("Done converting/upgrading '" CL_WHITE "%zu" CL_RESET "' achievements in '" CL_WHITE "%s" CL_RESET "'.\n", entries, file.c_str());
  210. return true;
  211. }
  212. static bool upgrade_item_db(std::string file, const uint32 source_version) {
  213. size_t entries = 0;
  214. for( auto input : inNode["Body"] ){
  215. // If under version 2
  216. if( source_version < 2 ){
  217. // Add armor level to all equipments
  218. if( input["Type"].IsDefined() && input["Type"].as<std::string>() == "Armor" ){
  219. input["ArmorLevel"] = 1;
  220. }
  221. }
  222. // TODO: this currently converts all scripts using Literal syntax to normal double quote strings
  223. body << input;
  224. entries++;
  225. }
  226. ShowStatus("Done converting/upgrading '" CL_WHITE "%zu" CL_RESET "' items in '" CL_WHITE "%s" CL_RESET "'.\n", entries, file.c_str());
  227. return true;
  228. }
  229. static bool upgrade_job_stats(std::string file, const uint32 source_version) {
  230. size_t entries = 0;
  231. for (auto input : inNode["Body"]) {
  232. // If under version 2
  233. if (source_version < 2) {
  234. // Field name changes
  235. if (input["HPFactor"].IsDefined()) {
  236. input["HpFactor"] = input["HPFactor"].as<uint32>();
  237. input.remove("HPFactor");
  238. }
  239. if (input["HpMultiplicator"].IsDefined()) {
  240. input["HpIncrease"] = input["HpMultiplicator"].as<uint32>();
  241. input.remove("HpMultiplicator");
  242. }
  243. if (input["HPMultiplicator"].IsDefined()) {
  244. input["HpIncrease"] = input["HPMultiplicator"].as<uint32>();
  245. input.remove("HPMultiplicator");
  246. }
  247. if (input["SpFactor"].IsDefined()) {
  248. input["SpIncrease"] = input["SpFactor"].as<uint32>();
  249. input.remove("SpFactor");
  250. }
  251. if (input["SPFactor"].IsDefined()) {
  252. input["SpIncrease"] = input["SPFactor"].as<uint32>();
  253. input.remove("SPFactor");
  254. }
  255. }
  256. body << input;
  257. entries++;
  258. }
  259. ShowStatus("Done converting/upgrading '" CL_WHITE "%zu" CL_RESET "' job stats in '" CL_WHITE "%s" CL_RESET "'.\n", entries, file.c_str());
  260. return true;
  261. }
  262. static bool upgrade_status_db(std::string file, const uint32 source_version) {
  263. size_t entries = 0;
  264. for (auto input : inNode["Body"]) {
  265. // If under version 3
  266. if (source_version < 3) {
  267. // Rename End to EndOnStart
  268. if (input["End"].IsDefined()) {
  269. input["EndOnStart"] = input["End"];
  270. input.remove("End");
  271. }
  272. }
  273. body << input;
  274. entries++;
  275. }
  276. ShowStatus("Done converting/upgrading '" CL_WHITE "%zu" CL_RESET "' statuses in '" CL_WHITE "%s" CL_RESET "'.\n", entries, file.c_str());
  277. return true;
  278. }
  279. static bool upgrade_map_drops_db(std::string file, const uint32 source_version) {
  280. size_t entries = 0;
  281. for( auto input : inNode["Body"] ){
  282. // If under version 2, adjust the rates from n/10000 to n/100000
  283. if( source_version < 2 ){
  284. if (input["GlobalDrops"].IsDefined()) {
  285. for( auto GlobalDrops : input["GlobalDrops"] ){
  286. if (GlobalDrops["Rate"].IsDefined()) {
  287. uint32 val = GlobalDrops["Rate"].as<uint32>() * 10;
  288. GlobalDrops["Rate"] = val;
  289. }
  290. }
  291. }
  292. if (input["SpecificDrops"].IsDefined()) {
  293. for( auto SpecificDrops : input["SpecificDrops"] ){
  294. if (SpecificDrops["Drops"].IsDefined()) {
  295. for( auto Drops : SpecificDrops["Drops"] ){
  296. if (Drops["Rate"].IsDefined()) {
  297. uint32 val = Drops["Rate"].as<uint32>() * 10;
  298. Drops["Rate"] = val;
  299. }
  300. }
  301. }
  302. }
  303. }
  304. }
  305. body << input;
  306. entries++;
  307. }
  308. ShowStatus("Done converting/upgrading '" CL_WHITE "%zu" CL_RESET "' rates in '" CL_WHITE "%s" CL_RESET "'.\n", entries, file.c_str());
  309. return true;
  310. }
  311. static bool upgrade_enchantgrade_db( std::string file, const uint32 source_version ){
  312. size_t entries = 0;
  313. for( auto input : inNode["Body"] ){
  314. // If under version 3
  315. if( source_version < 3 ){
  316. if( input["Levels"].IsDefined() ){
  317. for( auto levelNode : input["Levels"] ){
  318. if( levelNode["Grades"].IsDefined() ){
  319. for( auto gradeNode : levelNode["Grades"] ){
  320. // Convert Refine + Chance to a Chances array
  321. if( gradeNode["Refine"].IsDefined() && !gradeNode["Chance"].IsDefined() ){
  322. ShowError( "Cannot upgrade automatically, because Refine is specified, but Chance is missing" );
  323. return false;
  324. }
  325. if( gradeNode["Chance"].IsDefined() && !gradeNode["Refine"].IsDefined() ){
  326. ShowError( "Cannot upgrade automatically, because Chance is specified, but Refine is missing" );
  327. return false;
  328. }
  329. uint16 refine = gradeNode["Refine"].as<uint16>();
  330. uint16 chance = gradeNode["Chance"].as<uint16>();
  331. auto chancesNode = gradeNode["Chances"];
  332. for( int32 i = refine, j = 0; i <= MAX_REFINE; i++, j++ ){
  333. auto chanceNode = chancesNode[j];
  334. chanceNode["Refine"] = i;
  335. chanceNode["Chance"] = chance;
  336. }
  337. // Remove the existing Refine entry
  338. gradeNode.remove( "Refine" );
  339. // Remove the existing Chance entry
  340. gradeNode.remove( "Chance" );
  341. }
  342. }
  343. }
  344. }
  345. }
  346. body << input;
  347. entries++;
  348. }
  349. ShowStatus( "Done converting/upgrading '" CL_WHITE "%zu" CL_RESET "' entries in '" CL_WHITE "%s" CL_RESET "'.\n", entries, file.c_str() );
  350. return true;
  351. }
  352. static bool upgrade_item_group_db( std::string file, const uint32 source_version ){
  353. size_t entries = 0;
  354. for( const auto input : inNode["Body"] ){
  355. if( source_version < 4 ){
  356. body << YAML::BeginMap;
  357. body << YAML::Key << "Group" << YAML::Value << input["Group"];
  358. if( input["SubGroups"].IsDefined() ){
  359. body << YAML::Key << "SubGroups";
  360. body << YAML::BeginSeq;
  361. for (const auto &it : input["SubGroups"]) {
  362. body << YAML::BeginMap;
  363. if( !it["SubGroup"].IsDefined() ){
  364. ShowError( "Cannot upgrade automatically, SubGroup is missing." );
  365. return false;
  366. }
  367. body << YAML::Key << "SubGroup" << YAML::Value << it["SubGroup"];
  368. if (it["SubGroup"].as<uint16>() == 0)
  369. body << YAML::Key << "Algorithm" << YAML::Value << "All";
  370. else if (it["SubGroup"].as<uint16>() == 6)
  371. body << YAML::Key << "Algorithm" << YAML::Value << "Random";
  372. // else
  373. // body << YAML::Key << "Algorithm" << YAML::Value << "SharedPool";
  374. if( it["List"].IsDefined() )
  375. body << YAML::Key << "List";{
  376. body << YAML::BeginSeq;
  377. uint32 index = 0;
  378. for( auto ListNode : it["List"] ){
  379. if( !ListNode["Item"].IsDefined() ){
  380. ShowError( "Cannot upgrade automatically, Item is missing" );
  381. return false;
  382. }
  383. body << YAML::BeginMap;
  384. body << YAML::Key << "Index" << YAML::Value << index;
  385. body << YAML::Key << "Item" << YAML::Value << ListNode["Item"];
  386. if( ListNode["Rate"].IsDefined() )
  387. body << YAML::Key << "Rate" << YAML::Value << ListNode["Rate"];
  388. if( ListNode["Amount"].IsDefined() )
  389. body << YAML::Key << "Amount" << YAML::Value << ListNode["Amount"];
  390. if( ListNode["Duration"].IsDefined() )
  391. body << YAML::Key << "Duration" << YAML::Value << ListNode["Duration"];
  392. if( ListNode["Announced"].IsDefined() )
  393. body << YAML::Key << "Announced" << YAML::Value << ListNode["Announced"];
  394. if( ListNode["UniqueId"].IsDefined() )
  395. body << YAML::Key << "UniqueId" << YAML::Value << ListNode["UniqueId"];
  396. if( ListNode["Stacked"].IsDefined() )
  397. body << YAML::Key << "Stacked" << YAML::Value << ListNode["Stacked"];
  398. if( ListNode["Named"].IsDefined() )
  399. body << YAML::Key << "Named" << YAML::Value << ListNode["Named"];
  400. if( ListNode["Bound"].IsDefined() )
  401. body << YAML::Key << "Bound" << YAML::Value << ListNode["Bound"];
  402. if( ListNode["RandomOptionGroup"].IsDefined() )
  403. body << YAML::Key << "RandomOptionGroup" << YAML::Value << ListNode["RandomOptionGroup"];
  404. if( ListNode["RefineMinimum"].IsDefined() )
  405. body << YAML::Key << "RefineMinimum" << YAML::Value << ListNode["RefineMinimum"];
  406. if( ListNode["RefineMaximum"].IsDefined() )
  407. body << YAML::Key << "RefineMaximum" << YAML::Value << ListNode["RefineMaximum"];
  408. if( ListNode["Clear"].IsDefined() )
  409. body << YAML::Key << "Clear" << YAML::Value << ListNode["Clear"];
  410. index++;
  411. body << YAML::EndMap;
  412. }
  413. body << YAML::EndSeq;
  414. }
  415. if( it["Clear"].IsDefined() )
  416. body << YAML::Key << "Clear" << YAML::Value << it["Clear"];
  417. body << YAML::EndMap;
  418. }
  419. body << YAML::EndSeq;
  420. }
  421. body << YAML::EndMap;
  422. }
  423. entries++;
  424. }
  425. ShowStatus( "Done converting/upgrading '" CL_WHITE "%zu" CL_RESET "' entries in '" CL_WHITE "%s" CL_RESET "'.\n", entries, file.c_str() );
  426. return true;
  427. }
  428. int32 main( int32 argc, char *argv[] ){
  429. return main_core<YamlUpgradeTool>( argc, argv );
  430. }