csv2yaml.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include <iostream>
  4. #include <fstream>
  5. #include <functional>
  6. #include <vector>
  7. #include <unordered_map>
  8. #ifdef WIN32
  9. #include <conio.h>
  10. #else
  11. #include <termios.h>
  12. #include <unistd.h>
  13. #include <stdio.h>
  14. #endif
  15. #include <yaml-cpp/yaml.h>
  16. #include "../common/cbasetypes.hpp"
  17. #include "../common/core.hpp"
  18. #include "../common/malloc.hpp"
  19. #include "../common/mmo.hpp"
  20. #include "../common/nullpo.hpp"
  21. #include "../common/showmsg.hpp"
  22. #include "../common/strlib.hpp"
  23. #include "../common/utilities.hpp"
  24. #ifdef WIN32
  25. #include "../common/winapi.hpp"
  26. #endif
  27. // Only for constants - do not use functions of it or linking will fail
  28. #include "../map/achievement.hpp"
  29. #include "../map/battle.hpp"
  30. #include "../map/battleground.hpp"
  31. #include "../map/channel.hpp"
  32. #include "../map/chat.hpp"
  33. #include "../map/date.hpp"
  34. #include "../map/instance.hpp"
  35. #include "../map/mercenary.hpp"
  36. #include "../map/mob.hpp"
  37. #include "../map/npc.hpp"
  38. #include "../map/pc.hpp"
  39. #include "../map/pet.hpp"
  40. #include "../map/script.hpp"
  41. #include "../map/storage.hpp"
  42. #include "../map/quest.hpp"
  43. using namespace rathena;
  44. #ifndef WIN32
  45. int getch( void ){
  46. struct termios oldattr, newattr;
  47. int ch;
  48. tcgetattr( STDIN_FILENO, &oldattr );
  49. newattr = oldattr;
  50. newattr.c_lflag &= ~( ICANON | ECHO );
  51. tcsetattr( STDIN_FILENO, TCSANOW, &newattr );
  52. ch = getchar();
  53. tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );
  54. return ch;
  55. }
  56. #endif
  57. // Required constant and structure definitions
  58. #define MAX_GUILD_SKILL_REQUIRE 5
  59. // Forward declaration of conversion functions
  60. static bool guild_read_guildskill_tree_db( char* split[], int columns, int current );
  61. static size_t pet_read_db( const char* file );
  62. // Constants for conversion
  63. std::unordered_map<uint16, std::string> aegis_itemnames;
  64. std::unordered_map<uint16, std::string> aegis_mobnames;
  65. std::unordered_map<uint16, std::string> aegis_skillnames;
  66. std::unordered_map<const char*, int32> constants;
  67. // Forward declaration of constant loading functions
  68. static bool parse_item_constants( const char* path );
  69. static bool parse_mob_constants( char* split[], int columns, int current );
  70. static bool parse_skill_constants( char* split[], int columns, int current );
  71. bool fileExists( const std::string& path );
  72. bool writeToFile( const YAML::Node& node, const std::string& path );
  73. void prepareHeader( YAML::Node& node, const std::string& type, uint32 version );
  74. bool askConfirmation( const char* fmt, ... );
  75. YAML::Node body;
  76. size_t counter;
  77. // Implement the function instead of including the original version by linking
  78. void script_set_constant_( const char* name, int value, const char* constant_name, bool isparameter, bool deprecated ){
  79. constants[name] = value;
  80. }
  81. const char* constant_lookup( int32 value, const char* prefix ){
  82. nullpo_retr( nullptr, prefix );
  83. for( auto const& pair : constants ){
  84. // Same prefix group and same value
  85. if( strncasecmp( pair.first, prefix, strlen( prefix ) ) == 0 && pair.second == value ){
  86. return pair.first;
  87. }
  88. }
  89. return nullptr;
  90. }
  91. template<typename Func>
  92. bool process( const std::string& type, uint32 version, const std::vector<std::string>& paths, const std::string& name, Func lambda ){
  93. for( const std::string& path : paths ){
  94. const std::string name_ext = name + ".txt";
  95. const std::string from = path + "/" + name_ext;
  96. const std::string to = path + "/" + name + ".yml";
  97. if( fileExists( from ) ){
  98. if( !askConfirmation( "Found the file \"%s\", which requires migration to yml.\nDo you want to convert it now? (Y/N)\n", from.c_str() ) ){
  99. continue;
  100. }
  101. YAML::Node root;
  102. prepareHeader( root, type, version );
  103. body.reset();
  104. counter = 0;
  105. if( !lambda( path, name_ext ) ){
  106. return false;
  107. }
  108. root["Body"] = body;
  109. if( fileExists( to ) ){
  110. if( !askConfirmation( "The file \"%s\" already exists.\nDo you want to replace it? (Y/N)\n", to.c_str() ) ){
  111. continue;
  112. }
  113. }
  114. if( !writeToFile( root, to ) ){
  115. ShowError( "Failed to write the converted yml data to \"%s\".\nAborting now...\n", to.c_str() );
  116. return false;
  117. }
  118. // TODO: do you want to delete/rename?
  119. }
  120. }
  121. return true;
  122. }
  123. int do_init( int argc, char** argv ){
  124. const std::string path_db = std::string( db_path );
  125. const std::string path_db_mode = path_db + "/" + DBPATH;
  126. const std::string path_db_import = path_db + "/" + DBIMPORT;
  127. // Loads required conversion constants
  128. parse_item_constants( ( path_db_mode + "/item_db.txt" ).c_str() );
  129. parse_item_constants( ( path_db_import + "/item_db.txt" ).c_str() );
  130. 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, false );
  131. 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, false );
  132. sv_readdb( path_db_mode.c_str(), "skill_db.txt", ',', 18, 18, -1, parse_skill_constants, false );
  133. sv_readdb( path_db_import.c_str(), "skill_db.txt", ',', 18, 18, -1, parse_skill_constants, false );
  134. // Load constants
  135. #include "../map/script_constants.hpp"
  136. std::vector<std::string> guild_skill_tree_paths = {
  137. path_db,
  138. path_db_import
  139. };
  140. if( !process( "GUILD_SKILL_TREE_DB", 1, guild_skill_tree_paths, "guild_skill_tree", []( const std::string& path, const std::string& name_ext ) -> bool {
  141. return sv_readdb( path.c_str(), name_ext.c_str(), ',', 2 + MAX_GUILD_SKILL_REQUIRE * 2, 2 + MAX_GUILD_SKILL_REQUIRE * 2, -1, &guild_read_guildskill_tree_db, false );
  142. } ) ){
  143. return 0;
  144. }
  145. std::vector<std::string> pet_paths = {
  146. path_db_mode,
  147. path_db_import
  148. };
  149. if( !process( "PET_DB", 1, pet_paths, "pet_db", []( const std::string& path, const std::string& name_ext ) -> bool {
  150. return pet_read_db( ( path + name_ext ).c_str() );
  151. } ) ){
  152. return 0;
  153. }
  154. // TODO: add implementations ;-)
  155. return 0;
  156. }
  157. void do_final(void){
  158. }
  159. bool fileExists( const std::string& path ){
  160. std::ifstream in;
  161. in.open( path );
  162. if( in.is_open() ){
  163. in.close();
  164. return true;
  165. }else{
  166. return false;
  167. }
  168. }
  169. bool writeToFile( const YAML::Node& node, const std::string& path ){
  170. std::ofstream out;
  171. out.open( path );
  172. if( !out.is_open() ){
  173. ShowError( "Can not open file \"%s\" for writing.\n", path.c_str() );
  174. return false;
  175. }
  176. out << node;
  177. // Make sure there is an empty line at the end of the file for git
  178. #ifdef WIN32
  179. out << "\r\n";
  180. #else
  181. out << "\n";
  182. #endif
  183. out.close();
  184. return true;
  185. }
  186. void prepareHeader( YAML::Node& node, const std::string& type, uint32 version ){
  187. YAML::Node header;
  188. header["Type"] = type;
  189. header["Version"] = version;
  190. node["Header"] = header;
  191. }
  192. bool askConfirmation( const char* fmt, ... ){
  193. va_list ap;
  194. va_start( ap, fmt );
  195. _vShowMessage( MSG_NONE, fmt, ap );
  196. va_end( ap );
  197. char c = getch();
  198. if( c == 'Y' || c == 'y' ){
  199. return true;
  200. }else{
  201. return false;
  202. }
  203. }
  204. // Constant loading functions
  205. static bool parse_item_constants( const char* path ){
  206. uint32 lines = 0, count = 0;
  207. char line[1024];
  208. FILE* fp;
  209. fp = fopen(path, "r");
  210. if (fp == NULL) {
  211. ShowWarning("itemdb_readdb: File not found \"%s\", skipping.\n", path);
  212. return false;
  213. }
  214. // process rows one by one
  215. while (fgets(line, sizeof(line), fp))
  216. {
  217. char *str[32], *p;
  218. int i;
  219. lines++;
  220. if (line[0] == '/' && line[1] == '/')
  221. continue;
  222. memset(str, 0, sizeof(str));
  223. p = strstr(line, "//");
  224. if (p != nullptr) {
  225. *p = '\0';
  226. }
  227. p = line;
  228. while (ISSPACE(*p))
  229. ++p;
  230. if (*p == '\0')
  231. continue;// empty line
  232. for (i = 0; i < 19; ++i)
  233. {
  234. str[i] = p;
  235. p = strchr(p, ',');
  236. if (p == NULL)
  237. break;// comma not found
  238. *p = '\0';
  239. ++p;
  240. }
  241. if (p == NULL)
  242. {
  243. ShowError("itemdb_readdb: Insufficient columns in line %d of \"%s\" (item with id %d), skipping.\n", lines, path, atoi(str[0]));
  244. continue;
  245. }
  246. // Script
  247. if (*p != '{')
  248. {
  249. ShowError("itemdb_readdb: Invalid format (Script column) in line %d of \"%s\" (item with id %d), skipping.\n", lines, path, atoi(str[0]));
  250. continue;
  251. }
  252. str[19] = p + 1;
  253. p = strstr(p + 1, "},");
  254. if (p == NULL)
  255. {
  256. ShowError("itemdb_readdb: Invalid format (Script column) in line %d of \"%s\" (item with id %d), skipping.\n", lines, path, atoi(str[0]));
  257. continue;
  258. }
  259. *p = '\0';
  260. p += 2;
  261. // OnEquip_Script
  262. if (*p != '{')
  263. {
  264. ShowError("itemdb_readdb: Invalid format (OnEquip_Script column) in line %d of \"%s\" (item with id %d), skipping.\n", lines, path, atoi(str[0]));
  265. continue;
  266. }
  267. str[20] = p + 1;
  268. p = strstr(p + 1, "},");
  269. if (p == NULL)
  270. {
  271. ShowError("itemdb_readdb: Invalid format (OnEquip_Script column) in line %d of \"%s\" (item with id %d), skipping.\n", lines, path, atoi(str[0]));
  272. continue;
  273. }
  274. *p = '\0';
  275. p += 2;
  276. // OnUnequip_Script (last column)
  277. if (*p != '{')
  278. {
  279. ShowError("itemdb_readdb: Invalid format (OnUnequip_Script column) in line %d of \"%s\" (item with id %d), skipping.\n", lines, path, atoi(str[0]));
  280. continue;
  281. }
  282. str[21] = p;
  283. p = &str[21][strlen(str[21]) - 2];
  284. if (*p != '}') {
  285. /* lets count to ensure it's not something silly e.g. a extra space at line ending */
  286. int lcurly = 0, rcurly = 0;
  287. for (size_t v = 0; v < strlen(str[21]); v++) {
  288. if (str[21][v] == '{')
  289. lcurly++;
  290. else if (str[21][v] == '}') {
  291. rcurly++;
  292. p = &str[21][v];
  293. }
  294. }
  295. if (lcurly != rcurly) {
  296. ShowError("itemdb_readdb: Mismatching curly braces in line %d of \"%s\" (item with id %d), skipping.\n", lines, path, atoi(str[0]));
  297. continue;
  298. }
  299. }
  300. str[21] = str[21] + 1; //skip the first left curly
  301. *p = '\0'; //null the last right curly
  302. uint16 item_id = atoi( str[0] );
  303. char* name = trim( str[1] );
  304. aegis_itemnames[item_id] = std::string(name);
  305. count++;
  306. }
  307. fclose(fp);
  308. ShowStatus("Done reading '" CL_WHITE "%u" CL_RESET "' entries in '" CL_WHITE "%s" CL_RESET "'.\n", count, path);
  309. return true;
  310. }
  311. static bool parse_mob_constants( char* split[], int columns, int current ){
  312. uint16 mob_id = atoi( split[0] );
  313. char* name = trim( split[1] );
  314. aegis_mobnames[mob_id] = std::string( name );
  315. return true;
  316. }
  317. static bool parse_skill_constants( char* split[], int columns, int current ){
  318. uint16 skill_id = atoi( split[0] );
  319. char* name = trim( split[16] );
  320. aegis_skillnames[skill_id] = std::string( name );
  321. return true;
  322. }
  323. // Implementation of the conversion functions
  324. // Copied and adjusted from guild.cpp
  325. // <skill id>,<max lv>,<req id1>,<req lv1>,<req id2>,<req lv2>,<req id3>,<req lv3>,<req id4>,<req lv4>,<req id5>,<req lv5>
  326. static bool guild_read_guildskill_tree_db( char* split[], int columns, int current ){
  327. YAML::Node node;
  328. uint16 skill_id = (uint16)atoi(split[0]);
  329. std::string* name = util::umap_find( aegis_skillnames, skill_id );
  330. if( name == nullptr ){
  331. ShowError( "Skill name for skill id %hu is not known.\n", skill_id );
  332. return false;
  333. }
  334. node["Id"] = *name;
  335. node["MaxLevel"] = (uint16)atoi(split[1]);
  336. for( int i = 0, j = 0; i < MAX_GUILD_SKILL_REQUIRE; i++ ){
  337. uint16 required_skill_id = atoi( split[i * 2 + 2] );
  338. uint16 required_skill_level = atoi( split[i * 2 + 3] );
  339. if( required_skill_id == 0 || required_skill_level == 0 ){
  340. continue;
  341. }
  342. std::string* required_name = util::umap_find( aegis_skillnames, required_skill_id );
  343. if( required_name == nullptr ){
  344. ShowError( "Skill name for required skill id %hu is not known.\n", required_skill_id );
  345. return false;
  346. }
  347. YAML::Node req;
  348. req["Id"] = *required_name;
  349. req["Level"] = required_skill_level;
  350. node["Required"][j++] = req;
  351. }
  352. body[counter++] = node;
  353. return true;
  354. }
  355. // Copied and adjusted from pet.cpp
  356. static size_t pet_read_db( const char* file ){
  357. FILE* fp = fopen( file, "r" );
  358. if( fp == nullptr ){
  359. ShowError( "can't read %s\n", file );
  360. return 0;
  361. }
  362. int lines = 0;
  363. size_t entries = 0;
  364. char line[1024];
  365. while( fgets( line, sizeof(line), fp ) ) {
  366. char *str[22], *p;
  367. unsigned k;
  368. lines++;
  369. if(line[0] == '/' && line[1] == '/')
  370. continue;
  371. memset(str, 0, sizeof(str));
  372. p = line;
  373. while( ISSPACE(*p) )
  374. ++p;
  375. if( *p == '\0' )
  376. continue; // empty line
  377. for( k = 0; k < 20; ++k ) {
  378. str[k] = p;
  379. p = strchr(p,',');
  380. if( p == NULL )
  381. break; // comma not found
  382. *p = '\0';
  383. ++p;
  384. }
  385. if( p == NULL ) {
  386. ShowError("read_petdb: Insufficient columns in line %d, skipping.\n", lines);
  387. continue;
  388. }
  389. // Pet Script
  390. if( *p != '{' ) {
  391. ShowError("read_petdb: Invalid format (Pet Script column) in line %d, skipping.\n", lines);
  392. continue;
  393. }
  394. str[20] = p;
  395. p = strstr(p+1,"},");
  396. if( p == NULL ) {
  397. ShowError("read_petdb: Invalid format (Pet Script column) in line %d, skipping.\n", lines);
  398. continue;
  399. }
  400. p[1] = '\0';
  401. p += 2;
  402. // Equip Script
  403. if( *p != '{' ) {
  404. ShowError("read_petdb: Invalid format (Equip Script column) in line %d, skipping.\n", lines);
  405. continue;
  406. }
  407. str[21] = p;
  408. uint16 mob_id = atoi( str[0] );
  409. std::string* mob_name = util::umap_find( aegis_mobnames, mob_id );
  410. if( mob_name == nullptr ){
  411. ShowWarning( "pet_db reading: Invalid mob-class %hu, pet not read.\n", mob_id );
  412. continue;
  413. }
  414. YAML::Node node;
  415. node["Mob"] = *mob_name;
  416. uint16 tame_item_id = (uint16)atoi( str[3] );
  417. if( tame_item_id > 0 ){
  418. std::string* tame_item_name = util::umap_find( aegis_itemnames, tame_item_id );
  419. if( tame_item_name == nullptr ){
  420. ShowError( "Item name for item id %hu is not known.\n", tame_item_id );
  421. return false;
  422. }
  423. node["TameItem"] = *tame_item_name;
  424. }
  425. uint16 egg_item_id = (uint16)atoi( str[4] );
  426. std::string* egg_item_name = util::umap_find( aegis_itemnames, egg_item_id );
  427. if( egg_item_name == nullptr ){
  428. ShowError( "Item name for item id %hu is not known.\n", egg_item_id );
  429. return false;
  430. }
  431. node["EggItem"] = *egg_item_name;
  432. uint16 equip_item_id = (uint16)atoi( str[5] );
  433. if( equip_item_id > 0 ){
  434. std::string* equip_item_name = util::umap_find( aegis_itemnames, equip_item_id );
  435. if( equip_item_name == nullptr ){
  436. ShowError( "Item name for item id %hu is not known.\n", equip_item_id );
  437. return false;
  438. }
  439. node["EquipItem"] = *equip_item_name;
  440. }
  441. uint16 food_item_id = (uint16)atoi( str[6] );
  442. if( food_item_id > 0 ){
  443. std::string* food_item_name = util::umap_find( aegis_itemnames, food_item_id );
  444. if( food_item_name == nullptr ){
  445. ShowError( "Item name for item id %hu is not known.\n", food_item_id );
  446. return false;
  447. }
  448. node["FoodItem"] = *food_item_name;
  449. }
  450. node["Fullness"] = atoi( str[7] );
  451. // Default: 60
  452. if( atoi( str[8] ) != 60 ){
  453. node["HungryDelay"] = atoi( str[8] );
  454. }
  455. // Default: 250
  456. if( atoi( str[11] ) != 250 ){
  457. node["IntimacyStart"] = atoi( str[11] );
  458. }
  459. node["IntimacyFed"] = atoi( str[9] );
  460. // Default: -100
  461. if( atoi( str[10] ) != 100 ){
  462. node["IntimacyOverfed"] = -atoi( str[10] );
  463. }
  464. // pet_hungry_friendly_decrease battle_conf
  465. //node["IntimacyHungry"] = -5;
  466. // Default: -20
  467. if( atoi( str[12] ) != 20 ){
  468. node["IntimacyOwnerDie"] = -atoi( str[12] );
  469. }
  470. node["CaptureRate"] = atoi( str[13] );
  471. // Default: true
  472. if( atoi( str[15] ) == 0 ){
  473. node["SpecialPerformance"] = false;
  474. }
  475. node["AttackRate"] = atoi( str[17] );
  476. node["RetaliateRate"] = atoi( str[18] );
  477. node["ChangeTargetRate"] = atoi( str[19] );
  478. if( *str[21] ){
  479. node["Script"] = str[21];
  480. }
  481. if( *str[20] ){
  482. node["SupportScript"] = str[20];
  483. }
  484. body[counter++] = node;
  485. entries++;
  486. }
  487. fclose(fp);
  488. ShowStatus("Done reading '" CL_WHITE "%d" CL_RESET "' pets in '" CL_WHITE "%s" CL_RESET "'.\n", entries, file );
  489. return entries;
  490. }