yamlupgrade.cpp 19 KB

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