Quellcode durchsuchen

Updated the CSV2YAML tool to utilize YAML Emitter (#4448)

* Processing performance increases by an order of magnitudes.
* Generalized the path vector names to be used for future conversion functions.
* Fixed new lines.
* Added header templates.
Thanks to @Lemongrass3110!
Aleos vor 5 Jahren
Ursprung
Commit
b8a12cca3e
5 geänderte Dateien mit 188 neuen und 108 gelöschten Zeilen
  1. 1 1
      .gitattributes
  2. 15 0
      doc/yaml/db/guild_skill_tree.yml
  3. 17 0
      doc/yaml/db/license.yml
  4. 34 0
      doc/yaml/db/pet_db.yml
  5. 121 107
      src/tool/csv2yaml.cpp

+ 1 - 1
.gitattributes

@@ -1,6 +1,6 @@
 *        text=auto
 *.cpp diff=cpp
-*.yml diff
+*.yml diff text eol=lf
 *.sln    merge=union
 *.vcproj merge=union
 *.vcxproj merge=union

+ 15 - 0
doc/yaml/db/guild_skill_tree.yml

@@ -0,0 +1,15 @@
+###########################################################################
+# Guild Skill Database
+###########################################################################
+#
+# Guild Skill Settings
+#
+###########################################################################
+# Id - Skill ID of the guild skill.
+###########################################################################
+# MaxLevel - Maximum level of the guild skill.
+###########################################################################
+# Required - A list of required skills for the skill to become available.
+#   Id: Skill ID of the required guild skill.
+#   Level: Level of the required guild skill.
+###########################################################################

+ 17 - 0
doc/yaml/db/license.yml

@@ -0,0 +1,17 @@
+# This file is a part of rAthena.
+#   Copyright(C) 2019 rAthena Development Team
+#   https://rathena.org - https://github.com/rathena
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#

+ 34 - 0
doc/yaml/db/pet_db.yml

@@ -0,0 +1,34 @@
+###########################################################################
+# Pet Database
+###########################################################################
+#
+# Pet Settings
+#
+###########################################################################
+# - Mob                      Monster that can be used as pet
+#   TameItem                 Pet Tame Item.
+#   EggItem                  Pet Egg Item.
+#   EquipItem                Pet Accessory Item. (Default: 0)
+#   FoodItem                 Pet Food Item. (Default: 0)
+#   Fullness                 The amount of hunger is decreased every [HungryDelay] seconds.
+#   HungryDelay              The amount of time in seconds it takes for hunger to decrease after feeding. (Default: 60)
+#   HungerIncrease           The amount of hunger that is increased every time the pet is fed (Default: 20)
+#   IntimacyStart            Amount of Intimacy the pet starts with. (Default: 250)
+#   IntimacyFed              Amount of Intimacy that is increased when fed. (Default: 50)
+#   IntimacyOverfed          Amount of Intimacy that is increased when over-fed. (Default: -100)
+#   IntimacyHungry           Amount of Intimacy that is increased when the pet is hungry. (Default: -5)
+#   IntimacyOwnerDie         Amount of Intimacy that is increased when the pet owner dies. (Default: -20)
+#   CaptureRate              Capture success rate. (10000 = 100%)
+#   SpecialPerformance       If a pet has a Special Performance. (Default: true)
+#   AttackRate               Rate of which the pet will attack [requires at least pet_support_min_friendly intimacy]. (10000 = 100%)
+#   RetaliateRate            Rate of which the pet will retaliate when master is being attacked [requires at least pet_support_min_friendly intimacy]. (10000 = 100%)
+#   ChangeTargetRate         Rate of which the pet will change its attack target. (10000 = 100%)
+#   AllowAutoFeed            Allows turning automatic pet feeding on. (Default: false)
+#   Script                   Bonus script to execute when the pet is alive. (Default: null)
+#   SupportScript            Bonus script to execute when pet_status_support is enabled. (Default: null)
+#   Evolution:               Pet evolution settings. (Optional) (Default: null)
+#     - Target               Mob this pet can evolve to.
+#       ItemRequirements:      Item requirements for evolving this pet.
+#         - Item               Self-explanatory
+#           Amount
+###########################################################################

+ 121 - 107
src/tool/csv2yaml.cpp

@@ -1,11 +1,12 @@
 // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
 // For more information, see LICENCE in the main folder
 
-#include <iostream>
 #include <fstream>
 #include <functional>
-#include <vector>
+#include <iostream>
+#include <locale>
 #include <unordered_map>
+#include <vector>
 
 #ifdef WIN32
 	#include <conio.h>
@@ -81,12 +82,9 @@ static bool parse_mob_constants( char* split[], int columns, int current );
 static bool parse_skill_constants( char* split[], int columns, int current );
 
 bool fileExists( const std::string& path );
-bool writeToFile( const YAML::Node& node, const std::string& path );
-void prepareHeader( YAML::Node& node, const std::string& type, uint32 version );
 bool askConfirmation( const char* fmt, ... );
 
-YAML::Node body;
-size_t counter;
+YAML::Emitter body;
 
 // Implement the function instead of including the original version by linking
 void script_set_constant_( const char* name, int value, const char* constant_name, bool isparameter, bool deprecated ){
@@ -106,6 +104,54 @@ const char* constant_lookup( int32 value, const char* prefix ){
 	return nullptr;
 }
 
+void copyFileIfExists( std::ofstream& file,const std::string& name, bool newLine ){
+	std::string path = "doc/yaml/db/" + name + ".yml";
+
+	if( fileExists( path ) ){
+		std::ifstream source( path, std::ios::binary );
+
+		std::istreambuf_iterator<char> begin_source( source );
+		std::istreambuf_iterator<char> end_source;
+		std::ostreambuf_iterator<char> begin_dest( file );
+		copy( begin_source, end_source, begin_dest );
+
+		source.close();
+
+		if( newLine ){
+			file << "\n";
+		}
+	}
+}
+
+void prepareHeader(std::ofstream &file, const std::string& type, uint32 version, const std::string& name) {
+	copyFileIfExists( file, "license", false );
+	copyFileIfExists( file, name, true );
+
+	YAML::Emitter header(file);
+
+	header << YAML::BeginMap;
+	header << YAML::Key << "Header";
+	header << YAML::BeginMap;
+	header << YAML::Key << "Type" << YAML::Value << type;
+	header << YAML::Key << "Version" << YAML::Value << version;
+	header << YAML::EndMap;
+	header << YAML::EndMap;
+
+	file << "\n";
+	file << "\n";
+}
+
+void prepareBody(void) {
+	body << YAML::BeginMap;
+	body << YAML::Key << "Body";
+	body << YAML::BeginSeq;
+}
+
+void finalizeBody(void) {
+	body << YAML::EndSeq;
+	body << YAML::EndMap;
+}
+
 template<typename Func>
 bool process( const std::string& type, uint32 version, const std::vector<std::string>& paths, const std::string& name, Func lambda ){
 	for( const std::string& path : paths ){
@@ -117,30 +163,35 @@ bool process( const std::string& type, uint32 version, const std::vector<std::st
 			if( !askConfirmation( "Found the file \"%s\", which requires migration to yml.\nDo you want to convert it now? (Y/N)\n", from.c_str() ) ){
 				continue;
 			}
-
-			YAML::Node root;
-
-			prepareHeader( root, type, version );
-			body.reset();
-			counter = 0;
 			
-			if( !lambda( path, name_ext ) ){
-				return false;
+			if (fileExists(to)) {
+				if (!askConfirmation("The file \"%s\" already exists.\nDo you want to replace it? (Y/N)\n", to.c_str())) {
+					continue;
+				}
 			}
 
-			root["Body"] = body;
+			std::ofstream out;
 
-			if( fileExists( to ) ){
-				if( !askConfirmation( "The file \"%s\" already exists.\nDo you want to replace it? (Y/N)\n", to.c_str() ) ){
-					continue;
-				}
+			out.open(to);
+
+			if (!out.is_open()) {
+				ShowError("Can not open file \"%s\" for writing.\n", to.c_str());
+				return false;
 			}
 
-			if( !writeToFile( root, to ) ){
-				ShowError( "Failed to write the converted yml data to \"%s\".\nAborting now...\n", to.c_str() );
+			prepareHeader(out, type, version, name);
+			prepareBody();
+
+			if( !lambda( path, name_ext ) ){
+				out.close();
 				return false;
 			}
 
+			finalizeBody();
+			out << body.c_str();
+			// Make sure there is an empty line at the end of the file for git
+			out << "\n";
+			out.close();
 			
 			// TODO: do you want to delete/rename?
 		}
@@ -165,23 +216,19 @@ int do_init( int argc, char** argv ){
 	// Load constants
 	#include "../map/script_constants.hpp"
 
-	std::vector<std::string> guild_skill_tree_paths = {
+	std::vector<std::string> root_paths = {
 		path_db,
+		path_db_mode,
 		path_db_import
 	};
 
-	if( !process( "GUILD_SKILL_TREE_DB", 1, guild_skill_tree_paths, "guild_skill_tree", []( const std::string& path, const std::string& name_ext ) -> bool {
+	if( !process( "GUILD_SKILL_TREE_DB", 1, root_paths, "guild_skill_tree", []( const std::string& path, const std::string& name_ext ) -> bool {
 		return sv_readdb( path.c_str(), name_ext.c_str(), ',', 2 + MAX_GUILD_SKILL_REQUIRE * 2, 2 + MAX_GUILD_SKILL_REQUIRE * 2, -1, &guild_read_guildskill_tree_db, false );
 	} ) ){
 		return 0;
 	}
 
-	std::vector<std::string> pet_paths = {
-		path_db_mode,
-		path_db_import
-	};
-
-	if( !process( "PET_DB", 1, pet_paths, "pet_db", []( const std::string& path, const std::string& name_ext ) -> bool {
+	if( !process( "PET_DB", 1, root_paths, "pet_db", []( const std::string& path, const std::string& name_ext ) -> bool {
 		return pet_read_db( ( path + name_ext ).c_str() );
 	} ) ){
 		return 0;
@@ -209,39 +256,6 @@ bool fileExists( const std::string& path ){
 	}
 }
 
-bool writeToFile( const YAML::Node& node, const std::string& path ){
-	std::ofstream out;
-
-	out.open( path );
-
-	if( !out.is_open() ){
-		ShowError( "Can not open file \"%s\" for writing.\n", path.c_str() );
-		return false;
-	}
-
-	out << node;
-
-	// Make sure there is an empty line at the end of the file for git
-#ifdef WIN32
-	out << "\r\n";
-#else
-	out << "\n";
-#endif
-
-	out.close();
-
-	return true;
-}
-
-void prepareHeader( YAML::Node& node, const std::string& type, uint32 version ){
-	YAML::Node header;
-
-	header["Type"] = type;
-	header["Version"] = version;
-
-	node["Header"] = header;
-}
-
 bool askConfirmation( const char* fmt, ... ){
 	va_list ap;
 
@@ -410,10 +424,7 @@ static bool parse_skill_constants( char* split[], int columns, int current ){
 // Copied and adjusted from guild.cpp
 // <skill id>,<max lv>,<req id1>,<req lv1>,<req id2>,<req lv2>,<req id3>,<req lv3>,<req id4>,<req lv4>,<req id5>,<req lv5>
 static bool guild_read_guildskill_tree_db( char* split[], int columns, int current ){
-	YAML::Node node;
-
 	uint16 skill_id = (uint16)atoi(split[0]);
-
 	std::string* name = util::umap_find( aegis_skillnames, skill_id );
 
 	if( name == nullptr ){
@@ -421,33 +432,39 @@ static bool guild_read_guildskill_tree_db( char* split[], int columns, int curre
 		return false;
 	}
 
-	node["Id"] = *name;
-	node["MaxLevel"] = (uint16)atoi(split[1]);
+	body << YAML::BeginMap;
+	body << YAML::Key << "Id" << YAML::Value << *name;
+	body << YAML::Key << "MaxLevel" << YAML::Value << atoi(split[1]);
 
-	for( int i = 0, j = 0; i < MAX_GUILD_SKILL_REQUIRE; i++ ){
-		uint16 required_skill_id = atoi( split[i * 2 + 2] );
-		uint16 required_skill_level = atoi( split[i * 2 + 3] );
+	if (atoi(split[2]) > 0) {
+		body << YAML::Key << "Required";
+		body << YAML::BeginSeq;
 
-		if( required_skill_id == 0 || required_skill_level == 0 ){
-			continue;
-		}
+		for (int i = 0, j = 0; i < MAX_GUILD_SKILL_REQUIRE; i++) {
+			uint16 required_skill_id = atoi(split[i * 2 + 2]);
+			uint16 required_skill_level = atoi(split[i * 2 + 3]);
 
-		std::string* required_name = util::umap_find( aegis_skillnames, required_skill_id );
+			if (required_skill_id == 0 || required_skill_level == 0) {
+				continue;
+			}
 
-		if( required_name == nullptr ){
-			ShowError( "Skill name for required skill id %hu is not known.\n", required_skill_id );
-			return false;
-		}
+			std::string* required_name = util::umap_find(aegis_skillnames, required_skill_id);
 
-		YAML::Node req;
+			if (required_name == nullptr) {
+				ShowError("Skill name for required skill id %hu is not known.\n", required_skill_id);
+				return false;
+			}
 
-		req["Id"] = *required_name;
-		req["Level"] = required_skill_level;
+			body << YAML::BeginMap;
+			body << YAML::Key << "Id" << YAML::Value << *required_name;
+			body << YAML::Key << "Level" << YAML::Value << required_skill_level;
+			body << YAML::EndMap;
+		}
 
-		node["Required"][j++] = req;
+		body << YAML::EndSeq;
 	}
 
-	body[counter++] = node;
+	body << YAML::EndMap;
 
 	return true;
 }
@@ -531,9 +548,8 @@ static size_t pet_read_db( const char* file ){
 			continue;
 		}
 
-		YAML::Node node;
-
-		node["Mob"] = *mob_name;
+		body << YAML::BeginMap;
+		body << YAML::Key << "Mob" << YAML::Value << *mob_name;
 
 		uint16 tame_item_id = (uint16)atoi( str[3] );
 
@@ -545,11 +561,10 @@ static size_t pet_read_db( const char* file ){
 				return false;
 			}
 
-			node["TameItem"] = *tame_item_name;
+			body << YAML::Key << "TameItem" << YAML::Value << *tame_item_name;
 		}
 
 		uint16 egg_item_id = (uint16)atoi( str[4] );
-
 		std::string* egg_item_name = util::umap_find( aegis_itemnames, egg_item_id );
 
 		if( egg_item_name == nullptr ){
@@ -557,7 +572,7 @@ static size_t pet_read_db( const char* file ){
 			return false;
 		}
 
-		node["EggItem"] = *egg_item_name;
+		body << YAML::Key << "EggItem" << YAML::Value << *egg_item_name;
 
 		uint16 equip_item_id = (uint16)atoi( str[5] );
 
@@ -569,7 +584,7 @@ static size_t pet_read_db( const char* file ){
 				return false;
 			}
 
-			node["EquipItem"] = *equip_item_name;
+			body << YAML::Key << "EquipItem" << YAML::Value << *equip_item_name;
 		}
 
 		uint16 food_item_id = (uint16)atoi( str[6] );
@@ -582,48 +597,47 @@ static size_t pet_read_db( const char* file ){
 				return false;
 			}
 
-			node["FoodItem"] = *food_item_name;
+			body << YAML::Key << "FoodItem" << YAML::Value << *food_item_name;
 		}
 
-		node["Fullness"] = atoi( str[7] );
+		body << YAML::Key << "Fullness" << YAML::Value << atoi(str[7]);
 		// Default: 60
 		if( atoi( str[8] ) != 60 ){
-			node["HungryDelay"] = atoi( str[8] );
+			body << YAML::Key << "HungryDelay" << YAML::Value << atoi(str[8]);
 		}
 		// Default: 250
 		if( atoi( str[11] ) != 250 ){
-			node["IntimacyStart"] = atoi( str[11] );
+			body << YAML::Key << "IntimacyStart" << YAML::Value << atoi(str[11]);
 		}
-		node["IntimacyFed"] = atoi( str[9] );
+		body << YAML::Key << "IntimacyFed" << YAML::Value << atoi(str[9]);
 		// Default: -100
 		if( atoi( str[10] ) != 100 ){
-			node["IntimacyOverfed"] = -atoi( str[10] );
+			body << YAML::Key << "IntimacyOverfed" << YAML::Value << -atoi(str[10]);
 		}
 		// pet_hungry_friendly_decrease battle_conf
-		//node["IntimacyHungry"] = -5;
+		//body << YAML::Key << "IntimacyHungry" << YAML::Value << -5;
 		// Default: -20
 		if( atoi( str[12] ) != 20 ){
-			node["IntimacyOwnerDie"] = -atoi( str[12] );
+			body << YAML::Key << "IntimacyOwnerDie" << YAML::Value << -atoi(str[12]);
 		}
-		node["CaptureRate"] = atoi( str[13] );
+		body << YAML::Key << "CaptureRate" << YAML::Value << atoi(str[13]);
 		// Default: true
 		if( atoi( str[15] ) == 0 ){
-			node["SpecialPerformance"] = false;
+			body << YAML::Key << "SpecialPerformance" << YAML::Value << "false";
 		}
-		node["AttackRate"] = atoi( str[17] );
-		node["RetaliateRate"] = atoi( str[18] );
-		node["ChangeTargetRate"] = atoi( str[19] );
+		body << YAML::Key << "AttackRate" << YAML::Value << atoi(str[17]);
+		body << YAML::Key << "RetaliateRate" << YAML::Value << atoi(str[18]);
+		body << YAML::Key << "ChangeTargetRate" << YAML::Value << atoi(str[19]);
 
 		if( *str[21] ){
-			node["Script"] = str[21];
+			body << YAML::Key << "Script" << YAML::Value << YAML::Literal << str[21];
 		}
 
 		if( *str[20] ){
-			node["SupportScript"] = str[20];
+			body << YAML::Key << "SupportScript" << YAML::Value << YAML::Literal << str[20];
 		}
 
-		body[counter++] = node;
-
+		body << YAML::EndMap;
 		entries++;
 	}