yaml.hpp 14 KB

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