yaml.hpp 14 KB

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