yaml.hpp 15 KB

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