database.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "database.hpp"
  4. #include "showmsg.hpp"
  5. bool YamlDatabase::nodeExists( const YAML::Node& node, const std::string& name ){
  6. try{
  7. if( node[name] ){
  8. return true;
  9. }else{
  10. return false;
  11. }
  12. // TODO: catch( ... ) instead?
  13. }catch( const YAML::InvalidNode& ){
  14. return false;
  15. }catch( const YAML::BadSubscript& ){
  16. return false;
  17. }
  18. }
  19. bool YamlDatabase::nodesExist( const YAML::Node& node, std::initializer_list<const std::string> names ){
  20. bool missing = false;
  21. for( const std::string& name : names ){
  22. if( !this->nodeExists( node, name ) ){
  23. ShowError( "Missing mandatory node \"%s\".\n", name.c_str() );
  24. missing = true;
  25. }
  26. }
  27. if( missing ){
  28. this->invalidWarning( node, "At least one mandatory node did not exist.\n" );
  29. return false;
  30. }
  31. return true;
  32. }
  33. bool YamlDatabase::verifyCompatibility( const YAML::Node& rootNode ){
  34. if( !this->nodeExists( rootNode, "Header" ) ){
  35. ShowError( "No database \"Header\" was found.\n" );
  36. return false;
  37. }
  38. const YAML::Node& headerNode = rootNode["Header"];
  39. if( !this->nodeExists( headerNode, "Type" ) ){
  40. ShowError( "No database \"Type\" was found.\n" );
  41. return false;
  42. }
  43. const YAML::Node& typeNode = headerNode["Type"];
  44. const std::string& tmpType = typeNode.as<std::string>();
  45. if( tmpType != this->type ){
  46. ShowError( "Database type mismatch: %s != %s.\n", this->type.c_str(), tmpType.c_str() );
  47. return false;
  48. }
  49. uint16 tmpVersion;
  50. if( !this->asUInt16( headerNode, "Version", tmpVersion ) ){
  51. ShowError("Invalid header \"Version\" type for %s database.\n", this->type.c_str());
  52. return false;
  53. }
  54. if( tmpVersion != this->version ){
  55. if( tmpVersion > this->version ){
  56. ShowError( "Database version %hu is not supported. Maximum version is: %hu\n", tmpVersion, this->version );
  57. return false;
  58. }else if( tmpVersion >= this->minimumVersion ){
  59. ShowWarning( "Database version %hu is outdated and should be updated. Current version is: %hu\n", tmpVersion, this->minimumVersion );
  60. }else{
  61. ShowError( "Database version %hu is not supported anymore. Minimum version is: %hu\n", tmpVersion, this->minimumVersion );
  62. return false;
  63. }
  64. }
  65. return true;
  66. }
  67. bool YamlDatabase::load(){
  68. return this->load( this->getDefaultLocation() );
  69. }
  70. bool YamlDatabase::reload(){
  71. this->clear();
  72. return this->load();
  73. }
  74. bool YamlDatabase::load(const std::string& path) {
  75. YAML::Node rootNode;
  76. try {
  77. ShowStatus( "Loading '" CL_WHITE "%s" CL_RESET "'..." CL_CLL "\r", path.c_str() );
  78. rootNode = YAML::LoadFile(path);
  79. }
  80. catch(YAML::Exception &e) {
  81. ShowError("Failed to read %s database file from '" CL_WHITE "%s" CL_RESET "'.\n", this->type.c_str(), path.c_str());
  82. ShowError("%s (Line %d: Column %d)\n", e.msg.c_str(), e.mark.line, e.mark.column);
  83. return false;
  84. }
  85. // Required here already for header error reporting
  86. this->currentFile = path;
  87. if (!this->verifyCompatibility(rootNode)){
  88. ShowError("Failed to verify compatibility with %s database file from '" CL_WHITE "%s" CL_RESET "'.\n", this->type.c_str(), this->currentFile.c_str());
  89. return false;
  90. }
  91. const YAML::Node& header = rootNode["Header"];
  92. if( this->nodeExists( header, "Clear" ) ){
  93. bool clear;
  94. if( this->asBool( header, "Clear", clear ) && clear ){
  95. this->clear();
  96. }
  97. }
  98. this->parse( rootNode );
  99. this->parseImports( rootNode );
  100. this->loadingFinished();
  101. return true;
  102. }
  103. void YamlDatabase::loadingFinished(){
  104. // Does nothing by default, just for hooking
  105. }
  106. void YamlDatabase::parse( const YAML::Node& rootNode ){
  107. uint64 count = 0;
  108. if( this->nodeExists( rootNode, "Body" ) ){
  109. const YAML::Node& bodyNode = rootNode["Body"];
  110. size_t childNodesCount = bodyNode.size();
  111. size_t childNodesProgressed = 0;
  112. const char* fileName = this->currentFile.c_str();
  113. for( const YAML::Node &node : bodyNode ){
  114. count += this->parseBodyNode( node );
  115. ShowStatus( "Loading [%" PRIdPTR "/%" PRIdPTR "] entries from '" CL_WHITE "%s" CL_RESET "'" CL_CLL "\r", ++childNodesProgressed, childNodesCount, fileName );
  116. }
  117. ShowStatus( "Done reading '" CL_WHITE "%" PRIu64 CL_RESET "' entries in '" CL_WHITE "%s" CL_RESET "'" CL_CLL "\n", count, fileName );
  118. }
  119. }
  120. void YamlDatabase::parseImports( const YAML::Node& rootNode ){
  121. if( this->nodeExists( rootNode, "Footer" ) ){
  122. const YAML::Node& footerNode = rootNode["Footer"];
  123. if( this->nodeExists( footerNode, "Imports") ){
  124. for( const YAML::Node& node : footerNode["Imports"] ){
  125. std::string importFile;
  126. if( !this->asString( node, "Path", importFile ) ){
  127. continue;
  128. }
  129. if( this->nodeExists( node, "Mode" ) ){
  130. std::string mode;
  131. if( !this->asString( node, "Mode", mode ) ){
  132. continue;
  133. }
  134. #ifdef RENEWAL
  135. std::string compiledMode = "Renewal";
  136. #else
  137. std::string compiledMode = "Prerenewal";
  138. #endif
  139. if( compiledMode != mode ){
  140. // Skip this import
  141. continue;
  142. }
  143. }
  144. this->load( importFile );
  145. }
  146. }
  147. }
  148. }
  149. template <typename R> bool YamlDatabase::asType( const YAML::Node& node, const std::string& name, R& out ){
  150. if( this->nodeExists( node, name ) ){
  151. const YAML::Node& dataNode = node[name];
  152. try {
  153. R value = dataNode.as<R>();
  154. out = value;
  155. return true;
  156. }catch( const YAML::BadConversion& ){
  157. this->invalidWarning( dataNode, "Unable to parse \"%s\".\n", name.c_str() );
  158. return false;
  159. }
  160. }else{
  161. this->invalidWarning( node, "Missing node \"%s\".\n", name.c_str() );
  162. return false;
  163. }
  164. }
  165. bool YamlDatabase::asBool(const YAML::Node &node, const std::string &name, bool &out) {
  166. return asType<bool>(node, name, out);
  167. }
  168. bool YamlDatabase::asInt16( const YAML::Node& node, const std::string& name, int16& out ){
  169. return asType<int16>( node, name, out);
  170. }
  171. bool YamlDatabase::asUInt16(const YAML::Node& node, const std::string& name, uint16& out) {
  172. return asType<uint16>(node, name, out);
  173. }
  174. bool YamlDatabase::asInt32(const YAML::Node &node, const std::string &name, int32 &out) {
  175. return asType<int32>(node, name, out);
  176. }
  177. bool YamlDatabase::asUInt32(const YAML::Node &node, const std::string &name, uint32 &out) {
  178. return asType<uint32>(node, name, out);
  179. }
  180. bool YamlDatabase::asInt64(const YAML::Node &node, const std::string &name, int64 &out) {
  181. return asType<int64>(node, name, out);
  182. }
  183. bool YamlDatabase::asUInt64(const YAML::Node &node, const std::string &name, uint64 &out) {
  184. return asType<uint64>(node, name, out);
  185. }
  186. bool YamlDatabase::asFloat(const YAML::Node &node, const std::string &name, float &out) {
  187. return asType<float>(node, name, out);
  188. }
  189. bool YamlDatabase::asDouble(const YAML::Node &node, const std::string &name, double &out) {
  190. return asType<double>(node, name, out);
  191. }
  192. bool YamlDatabase::asString(const YAML::Node &node, const std::string &name, std::string &out) {
  193. return asType<std::string>(node, name, out);
  194. }
  195. bool YamlDatabase::asUInt16Rate( const YAML::Node& node, const std::string& name, uint16& out, uint16 maximum ){
  196. if( this->asUInt16( node, name, out ) ){
  197. if( out > maximum ){
  198. this->invalidWarning( node[name], "Node \"%s\" with value %" PRIu16 " exceeds maximum of %" PRIu16 ".\n", name.c_str(), out, maximum );
  199. return false;
  200. }else if( out == 0 ){
  201. this->invalidWarning( node[name], "Node \"%s\" needs to be at least 1.\n", name.c_str() );
  202. return false;
  203. }else{
  204. return true;
  205. }
  206. }else{
  207. return false;
  208. }
  209. }
  210. bool YamlDatabase::asUInt32Rate( const YAML::Node& node, const std::string& name, uint32& out, uint32 maximum ){
  211. if( this->asUInt32( node, name, out ) ){
  212. if( out > maximum ){
  213. this->invalidWarning( node[name], "Node \"%s\" with value %" PRIu32 " exceeds maximum of %" PRIu32 ".\n", name.c_str(), out, maximum );
  214. return false;
  215. }else if( out == 0 ){
  216. this->invalidWarning( node[name], "Node \"%s\" needs to be at least 1.\n", name.c_str() );
  217. return false;
  218. }else{
  219. return true;
  220. }
  221. }else{
  222. return false;
  223. }
  224. }
  225. void YamlDatabase::invalidWarning( const YAML::Node &node, const char* fmt, ... ){
  226. va_list ap;
  227. va_start(ap, fmt);
  228. // Remove any remaining garbage of a previous loading line
  229. ShowMessage( CL_CLL );
  230. // Print the actual error
  231. _vShowMessage( MSG_ERROR, fmt, ap );
  232. va_end(ap);
  233. ShowError( "Occurred in file '" CL_WHITE "%s" CL_RESET "' on line %d and column %d.\n", this->currentFile.c_str(), node.Mark().line + 1, node.Mark().column );
  234. #ifdef DEBUG
  235. YAML::Emitter out;
  236. out << node;
  237. ShowMessage( "%s\n", out.c_str() );
  238. #endif
  239. }
  240. std::string YamlDatabase::getCurrentFile(){
  241. return this->currentFile;
  242. }