yaml.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #ifndef YAML_HPP
  4. #define YAML_HPP
  5. #include <fstream>
  6. #include <functional>
  7. #include <iostream>
  8. #include <map>
  9. #include <unordered_map>
  10. #include <vector>
  11. #ifdef WIN32
  12. #include <conio.h>
  13. #else
  14. #include <termios.h>
  15. #include <unistd.h>
  16. #include <stdio.h>
  17. #endif
  18. #include <yaml-cpp/yaml.h>
  19. #include <ryml_std.hpp>
  20. #include <ryml.hpp>
  21. #include "../common/cbasetypes.hpp"
  22. #include "../common/core.hpp"
  23. #include "../common/malloc.hpp"
  24. #include "../common/mmo.hpp"
  25. #include "../common/nullpo.hpp"
  26. #include "../common/showmsg.hpp"
  27. #include "../common/strlib.hpp"
  28. #include "../common/utilities.hpp"
  29. #include "../common/utils.hpp"
  30. #ifdef WIN32
  31. #include "../common/winapi.hpp"
  32. #endif
  33. // Only for constants - do not use functions of it or linking will fail
  34. #define ONLY_CONSTANTS
  35. #include "../map/achievement.hpp"
  36. #include "../map/battle.hpp"
  37. #include "../map/battleground.hpp"
  38. #include "../map/cashshop.hpp"
  39. #include "../map/channel.hpp"
  40. #include "../map/chat.hpp"
  41. #include "../map/date.hpp"
  42. #include "../map/instance.hpp"
  43. #include "../map/elemental.hpp"
  44. #include "../map/mercenary.hpp"
  45. #include "../map/mob.hpp"
  46. #include "../map/npc.hpp"
  47. #include "../map/pc.hpp"
  48. #include "../map/pet.hpp"
  49. #include "../map/quest.hpp"
  50. #include "../map/script.hpp"
  51. #include "../map/skill.hpp"
  52. #include "../map/storage.hpp"
  53. using namespace rathena;
  54. /// Uncomment this line to enable the ability for the conversion tools to automatically convert
  55. /// all files with no user interaction, whether it be from CSV to YAML or YAML to SQL.
  56. //#define CONVERT_ALL
  57. #ifndef WIN32
  58. int getch(void) {
  59. struct termios oldattr, newattr;
  60. int ch;
  61. tcgetattr(STDIN_FILENO, &oldattr);
  62. newattr = oldattr;
  63. newattr.c_lflag &= ~(ICANON | ECHO);
  64. tcsetattr(STDIN_FILENO, TCSANOW, &newattr);
  65. ch = getchar();
  66. tcsetattr(STDIN_FILENO, TCSANOW, &oldattr);
  67. return ch;
  68. }
  69. #endif
  70. YAML::Node inNode;
  71. YAML::Emitter body;
  72. // Constants for conversion
  73. std::unordered_map<t_itemid, std::string> aegis_itemnames;
  74. std::unordered_map<uint32, t_itemid> aegis_itemviewid;
  75. std::unordered_map<uint16, std::string> aegis_mobnames;
  76. std::unordered_map<uint16, std::string> aegis_skillnames;
  77. std::unordered_map<const char *, int64> constants;
  78. // Implement the function instead of including the original version by linking
  79. void script_set_constant_(const char *name, int64 value, const char *constant_name, bool isparameter, bool deprecated) {
  80. if (!deprecated)
  81. constants[name] = value;
  82. }
  83. const char *constant_lookup(int32 value, const char *prefix) {
  84. nullpo_retr(nullptr, prefix);
  85. for (auto const &pair : constants) {
  86. // Same prefix group and same value
  87. if (strncasecmp(pair.first, prefix, strlen(prefix)) == 0 && pair.second == value) {
  88. return pair.first;
  89. }
  90. }
  91. return nullptr;
  92. }
  93. int64 constant_lookup_int(const char *constant) {
  94. nullpo_retr(-100, constant);
  95. for (auto const &pair : constants) {
  96. if (strlen(pair.first) == strlen(constant) && strncasecmp(pair.first, constant, strlen(constant)) == 0) {
  97. return pair.second;
  98. }
  99. }
  100. return -100;
  101. }
  102. /**
  103. * Determines if a file exists.
  104. * @param path: File to check for
  105. * @return True if file exists or false otherwise
  106. */
  107. bool fileExists(const std::string &path) {
  108. std::ifstream in;
  109. in.open(path);
  110. if (in.is_open()) {
  111. in.close();
  112. return true;
  113. } else {
  114. return false;
  115. }
  116. }
  117. /**
  118. * Prompt for confirmation.
  119. * @param fmt: Message to print
  120. * @param va_arg: Any arguments needed for message
  121. * @return True on yes or false otherwise
  122. */
  123. bool askConfirmation(const char *fmt, ...) {
  124. va_list ap;
  125. va_start(ap, fmt);
  126. _vShowMessage(MSG_NONE, fmt, ap);
  127. va_end(ap);
  128. char c = getch();
  129. if (c == 'Y' || c == 'y') {
  130. return true;
  131. } else {
  132. return false;
  133. }
  134. }
  135. /**
  136. * Returns a YAML header version
  137. * @param node: YAML node
  138. * @return Version number
  139. */
  140. uint32 getHeaderVersion(YAML::Node &node) {
  141. return node["Header"]["Version"].as<uint32>();
  142. }
  143. /**
  144. * Copy a file if it exists.
  145. * @param file: File stream
  146. * @param name: File name
  147. * @param newLine: Append new line at end of copy
  148. */
  149. void copyFileIfExists(std::ofstream &file, const std::string &name, bool newLine) {
  150. std::string path = "doc/yaml/db/" + name + ".yml";
  151. if (fileExists(path)) {
  152. std::ifstream source(path, std::ios::binary);
  153. std::istreambuf_iterator<char> begin_source(source);
  154. std::istreambuf_iterator<char> end_source;
  155. std::ostreambuf_iterator<char> begin_dest(file);
  156. copy(begin_source, end_source, begin_dest);
  157. source.close();
  158. if (newLine) {
  159. file << "\n";
  160. }
  161. }
  162. }
  163. /**
  164. * Prepares header for output.
  165. * @param file: File stream
  166. * @param type: Database type
  167. * @param version: Database version
  168. * @param name: File name
  169. */
  170. void prepareHeader(std::ofstream &file, const std::string &type, uint32 version, const std::string &name) {
  171. copyFileIfExists(file, "license", false);
  172. copyFileIfExists(file, name, true);
  173. YAML::Emitter header(file);
  174. header << YAML::BeginMap;
  175. header << YAML::Key << "Header";
  176. header << YAML::BeginMap;
  177. header << YAML::Key << "Type" << YAML::Value << type;
  178. header << YAML::Key << "Version" << YAML::Value << version;
  179. header << YAML::EndMap;
  180. header << YAML::EndMap;
  181. file << "\n";
  182. file << "\n";
  183. }
  184. /**
  185. * Prepares footer for output.
  186. * @param file: File stream
  187. */
  188. void prepareFooter(std::ostream &file) {
  189. if (!inNode["Footer"].IsDefined())
  190. return;
  191. if (inNode["Body"].IsDefined()) {
  192. file << "\n";
  193. file << "\n";
  194. }
  195. YAML::Emitter footer(file);
  196. footer << YAML::BeginMap;
  197. footer << YAML::Key << "Footer";
  198. footer << YAML::BeginMap;
  199. footer << YAML::Key << "Imports";
  200. footer << YAML::BeginSeq;
  201. for (const YAML::Node &import : inNode["Footer"]["Imports"]) {
  202. footer << YAML::BeginMap;
  203. footer << YAML::Key << "Path" << YAML::Value << import["Path"];
  204. if (import["Mode"].IsDefined())
  205. footer << YAML::Key << "Mode" << YAML::Value << import["Mode"];
  206. footer << YAML::EndMap;
  207. }
  208. footer << YAML::EndSeq;
  209. footer << YAML::EndMap;
  210. footer << YAML::EndMap;
  211. }
  212. /**
  213. * Prepares body for output.
  214. */
  215. void prepareBody(void) {
  216. body << YAML::BeginMap;
  217. body << YAML::Key << "Body";
  218. body << YAML::BeginSeq;
  219. }
  220. /**
  221. * Finalizes body's output.
  222. */
  223. void finalizeBody(void) {
  224. body << YAML::EndSeq;
  225. body << YAML::EndMap;
  226. }
  227. /**
  228. * Split the string with ':' as separator and put each value for a skilllv
  229. * @param str: String to split
  230. * @param val: Array to store value into
  231. * @param max: Max array size (Default: MAX_SKILL_LEVEL)
  232. * @return 0:error, x:number of value assign (max value)
  233. */
  234. int skill_split_atoi(char *str, int *val, int max = MAX_SKILL_LEVEL) {
  235. int i;
  236. for (i = 0; i < max; i++) {
  237. if (!str)
  238. break;
  239. val[i] = atoi(str);
  240. str = strchr(str, ':');
  241. if (str)
  242. *str++ = 0;
  243. }
  244. if (i == 0) // No data found.
  245. return 0;
  246. if (i == 1) // Single value, have the whole range have the same value.
  247. return 1;
  248. return i;
  249. }
  250. /**
  251. * Split string to int by constant value (const.yml) or atoi()
  252. * @param *str: String input
  253. * @param *val: Temporary storage
  254. * @param *delim: Delimiter (for multiple value support)
  255. * @param min_value: Minimum value. If the splitted value is less or equal than this, will be skipped
  256. * @param max: Maximum number that can be allocated
  257. * @return count: Number of success
  258. */
  259. uint8 skill_split_atoi2(char *str, int64 *val, const char *delim, int min_value, uint16 max) {
  260. uint8 i = 0;
  261. char *p = strtok(str, delim);
  262. while (p != NULL) {
  263. int64 n = min_value;
  264. trim(p);
  265. if (ISDIGIT(p[0])) // If using numeric
  266. n = atoi(p);
  267. else {
  268. n = constant_lookup_int(p);
  269. p = strtok(NULL, delim);
  270. }
  271. if (n > min_value) {
  272. val[i] = n;
  273. i++;
  274. if (i >= max)
  275. break;
  276. }
  277. p = strtok(NULL, delim);
  278. }
  279. return i;
  280. }
  281. /**
  282. * Split string to int
  283. * @param str: String input
  284. * @param val1: Temporary storage to first value
  285. * @param val2: Temporary storage to second value
  286. */
  287. static void itemdb_re_split_atoi(char* str, int* val1, int* val2) {
  288. int i, val[2];
  289. for (i = 0; i < 2; i++) {
  290. if (!str)
  291. break;
  292. val[i] = atoi(str);
  293. str = strchr(str, ':');
  294. if (str)
  295. *str++ = 0;
  296. }
  297. if (i == 0) {
  298. *val1 = *val2 = 0;
  299. return; // no data found
  300. }
  301. if (i == 1) { // Single Value
  302. *val1 = val[0];
  303. *val2 = 0;
  304. return;
  305. }
  306. // We assume we have 2 values.
  307. *val1 = val[0];
  308. *val2 = val[1];
  309. return;
  310. }
  311. /**
  312. * Determine if array contains level specific data
  313. * @param arr: Array to check
  314. * @return True if level specific or false for same for all levels
  315. */
  316. static bool isMultiLevel(int arr[]) {
  317. uint8 count = 0;
  318. for (uint8 i = 0; i < MAX_SKILL_LEVEL; i++) {
  319. if (arr[i] != 0)
  320. count++;
  321. }
  322. return (count < 2 ? false : true);
  323. }
  324. /**
  325. * Converts a string to upper case characters based on specific cases.
  326. * @param name: String to convert
  327. * @return Converted string
  328. */
  329. std::string name2Upper(std::string name) {
  330. util::tolower( name );
  331. name[0] = toupper(name[0]);
  332. for (size_t i = 0; i < name.size(); i++) {
  333. if (name[i - 1] == '_' || (name[i - 2] == '1' && name[i - 1] == 'h') || (name[i - 2] == '2' && name[i - 1] == 'h'))
  334. name[i] = toupper(name[i]);
  335. }
  336. return name;
  337. }
  338. // Constant loading functions
  339. static bool parse_item_constants_txt(const char *path) {
  340. uint32 lines = 0, count = 0;
  341. char line[1024];
  342. FILE *fp;
  343. fp = fopen(path, "r");
  344. if (fp == NULL) {
  345. ShowWarning("itemdb_readdb: File not found \"%s\", skipping.\n", path);
  346. return false;
  347. }
  348. // process rows one by one
  349. while (fgets(line, sizeof(line), fp))
  350. {
  351. char *str[32], *p;
  352. int i;
  353. lines++;
  354. if (line[0] == '/' && line[1] == '/')
  355. continue;
  356. memset(str, 0, sizeof(str));
  357. p = strstr(line, "//");
  358. if (p != nullptr) {
  359. *p = '\0';
  360. }
  361. p = line;
  362. while (ISSPACE(*p))
  363. ++p;
  364. if (*p == '\0')
  365. continue;// empty line
  366. for (i = 0; i < 19; ++i)
  367. {
  368. str[i] = p;
  369. p = strchr(p, ',');
  370. if (p == NULL)
  371. break;// comma not found
  372. *p = '\0';
  373. ++p;
  374. }
  375. t_itemid item_id = strtoul(str[0], nullptr, 10);
  376. if (p == NULL)
  377. {
  378. ShowError("itemdb_readdb: Insufficient columns in line %d of \"%s\" (item with id %u), skipping.\n", lines, path, item_id);
  379. continue;
  380. }
  381. // Script
  382. if (*p != '{')
  383. {
  384. ShowError("itemdb_readdb: Invalid format (Script column) in line %d of \"%s\" (item with id %u), skipping.\n", lines, path, item_id);
  385. continue;
  386. }
  387. str[19] = p + 1;
  388. p = strstr(p + 1, "},");
  389. if (p == NULL)
  390. {
  391. ShowError("itemdb_readdb: Invalid format (Script column) in line %d of \"%s\" (item with id %u), skipping.\n", lines, path, item_id);
  392. continue;
  393. }
  394. *p = '\0';
  395. p += 2;
  396. // OnEquip_Script
  397. if (*p != '{')
  398. {
  399. ShowError("itemdb_readdb: Invalid format (OnEquip_Script column) in line %d of \"%s\" (item with id %u), skipping.\n", lines, path, item_id);
  400. continue;
  401. }
  402. str[20] = p + 1;
  403. p = strstr(p + 1, "},");
  404. if (p == NULL)
  405. {
  406. ShowError("itemdb_readdb: Invalid format (OnEquip_Script column) in line %d of \"%s\" (item with id %u), skipping.\n", lines, path, item_id);
  407. continue;
  408. }
  409. *p = '\0';
  410. p += 2;
  411. // OnUnequip_Script (last column)
  412. if (*p != '{')
  413. {
  414. ShowError("itemdb_readdb: Invalid format (OnUnequip_Script column) in line %d of \"%s\" (item with id %u), skipping.\n", lines, path, item_id);
  415. continue;
  416. }
  417. str[21] = p;
  418. p = &str[21][strlen(str[21]) - 2];
  419. if (*p != '}') {
  420. /* lets count to ensure it's not something silly e.g. a extra space at line ending */
  421. int lcurly = 0, rcurly = 0;
  422. for (size_t v = 0; v < strlen(str[21]); v++) {
  423. if (str[21][v] == '{')
  424. lcurly++;
  425. else if (str[21][v] == '}') {
  426. rcurly++;
  427. p = &str[21][v];
  428. }
  429. }
  430. if (lcurly != rcurly) {
  431. ShowError("itemdb_readdb: Mismatching curly braces in line %d of \"%s\" (item with id %u), skipping.\n", lines, path, item_id);
  432. continue;
  433. }
  434. }
  435. str[21] = str[21] + 1; //skip the first left curly
  436. *p = '\0'; //null the last right curly
  437. uint32 view_id = strtoul(str[18], nullptr, 10);
  438. char *name = trim(str[1]);
  439. aegis_itemnames[item_id] = std::string(name);
  440. if (atoi(str[14]) & (EQP_HELM | EQP_COSTUME_HELM) && util::umap_find(aegis_itemviewid, view_id) == nullptr)
  441. aegis_itemviewid[view_id] = item_id;
  442. count++;
  443. }
  444. fclose(fp);
  445. ShowStatus("Done reading '" CL_WHITE "%u" CL_RESET "' entries in '" CL_WHITE "%s" CL_RESET "'.\n", count, path);
  446. return true;
  447. }
  448. const std::string ItemDatabase::getDefaultLocation() {
  449. return std::string(db_path) + "/item_db.yml";
  450. }
  451. uint64 ItemDatabase::parseBodyNode(const ryml::NodeRef& node) {
  452. t_itemid nameid;
  453. if (!this->asUInt32(node, "Id", nameid))
  454. return 0;
  455. if (this->nodeExists(node, "AegisName")) {
  456. std::string name;
  457. if (!this->asString(node, "AegisName", name))
  458. return 0;
  459. aegis_itemnames[nameid] = name;
  460. }
  461. if (this->nodeExists(node, "View")) {
  462. uint32 look;
  463. if (!this->asUInt32(node, "View", look))
  464. return 0;
  465. if (look > 0) {
  466. if (this->nodeExists(node, "Locations")) {
  467. const ryml::NodeRef& locationNode = node["Locations"];
  468. static std::vector<std::string> locations = {
  469. "Head_Low",
  470. "Head_Mid",
  471. "Head_Top",
  472. "Costume_Head_Low",
  473. "Costume_Head_Mid",
  474. "Costume_Head_Top"
  475. };
  476. for (std::string& location : locations) {
  477. if (this->nodeExists(locationNode, location)) {
  478. bool active;
  479. if (!this->asBool(locationNode, location, active))
  480. return 0;
  481. aegis_itemviewid[look] = nameid;
  482. break;
  483. }
  484. }
  485. }
  486. }
  487. }
  488. return 1;
  489. }
  490. void ItemDatabase::loadingFinished() {
  491. }
  492. ItemDatabase item_db;
  493. static bool parse_mob_constants_txt(char *split[], int columns, int current) {
  494. uint16 mob_id = atoi(split[0]);
  495. char *name = trim(split[1]);
  496. aegis_mobnames[mob_id] = std::string(name);
  497. return true;
  498. }
  499. static bool parse_skill_constants_txt(char *split[], int columns, int current) {
  500. uint16 skill_id = atoi(split[0]);
  501. char *name = trim(split[16]);
  502. aegis_skillnames[skill_id] = std::string(name);
  503. return true;
  504. }
  505. const std::string SkillDatabase::getDefaultLocation() {
  506. return std::string(db_path) + "/skill_db.yml";
  507. }
  508. uint64 SkillDatabase::parseBodyNode(const ryml::NodeRef& node) {
  509. t_itemid nameid;
  510. if (!this->asUInt32(node, "Id", nameid))
  511. return 0;
  512. if (this->nodeExists(node, "Name")) {
  513. std::string name;
  514. if (!this->asString(node, "Name", name))
  515. return 0;
  516. aegis_skillnames[nameid] = name;
  517. }
  518. return 1;
  519. }
  520. void SkillDatabase::clear() {
  521. TypesafeCachedYamlDatabase::clear();
  522. }
  523. void SkillDatabase::loadingFinished(){
  524. }
  525. SkillDatabase skill_db;
  526. const std::string MobDatabase::getDefaultLocation(){
  527. return std::string( db_path ) + "/mob_db.yml";
  528. }
  529. uint64 MobDatabase::parseBodyNode(const ryml::NodeRef& node) {
  530. uint16 mob_id;
  531. if (!this->asUInt16(node, "Id", mob_id))
  532. return 0;
  533. if (this->nodeExists(node, "AegisName")) {
  534. std::string name;
  535. if (!this->asString(node, "AegisName", name))
  536. return 0;
  537. aegis_mobnames[mob_id] = name;
  538. }
  539. return 1;
  540. }
  541. void MobDatabase::loadingFinished() {};
  542. MobDatabase mob_db;
  543. #endif /* YAML_HPP */