khanat-opennel-code/code/ryzom/tools/skill_extractor/skill_extractor.cpp
2017-03-15 17:43:49 +02:00

746 lines
22 KiB
C++

// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "nel/misc/types_nl.h"
#include "nel/misc/file.h"
#include "nel/misc/config_file.h"
#include "string.h"
#include <vector>
#include <string>
using namespace std;
using namespace NLMISC;
// define macro for outputone code line
#define outLine( s )\
{\
out = string(s);\
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );\
}\
// set of code
static set<string> Codes;
struct CSkill
{
string SkillName;
string NormalizedSkillName;
CSkill *ParentSkillPtr;
string ParentSkill;
uint16 MaxValue;
string Code;
uint8 StageType;
string MainCategory;
string SecondaryCategory;
vector<CSkill*> Children;
CSkill() : ParentSkillPtr(NULL),MaxValue(0),StageType(0)
{}
CSkill(const CSkill &skill)
{
*this = skill;
}
CSkill &operator=(const CSkill &skill)
{
SkillName = skill.SkillName;
NormalizedSkillName = skill.NormalizedSkillName;
ParentSkillPtr = skill.ParentSkillPtr;
ParentSkill = skill.ParentSkill;
MaxValue = skill.MaxValue;
Code = skill.Code;
StageType = skill.StageType;
MainCategory = skill.MainCategory;
SecondaryCategory = skill.SecondaryCategory;
Children = skill.Children;
return *this;
}
void skillName(string name)
{
SkillName = name;
/* // fast correction to avoid strange cases when names end with a ' '
sint i = SkillName.size();
for ( ; i > 0; i-- )
{
if ( SkillName[i-1] != ' ' )
break;
}
SkillName.resize( i );
uint idxSpace;
char c[2];
c[0] = SkillName.substr( 0, 1).c_str()[0] - 32;
c[1] = 0;
NormalizedSkillName = string( c ) + SkillName.substr( 1 );
while( ( idxSpace = NormalizedSkillName.find(" ") ) != string::npos )
{
string skillNameTmp = NormalizedSkillName.substr( 0, idxSpace );
if( idxSpace < ( SkillName.size() - 1 ) )
{
c[0] = (*NormalizedSkillName.substr( idxSpace + 1, 1 ).c_str());
if( c[0] >= 'a' ) c[0] -= 32;
skillNameTmp = skillNameTmp + string( c ) + NormalizedSkillName.substr( idxSpace + 2 );
}
NormalizedSkillName = skillNameTmp;
}
*/
NormalizedSkillName = SkillName;
}
void buildCode()
{
if (ParentSkillPtr != NULL)
Code = ParentSkillPtr->Code + Code;
Codes.insert( Code );
for (uint i = 0 ; i < Children.size() ; ++i)
Children[i]->buildCode();
}
void writeInSheet(COFile &fo)
{
/*
<STRUCT Name="AccurateBleedingShot">
<ATOM Name="Skill" Value="acurate bleeding shot "/>
<ATOM Name="SkillCode" Value="none"/>
<ATOM Name="MaxSkillValue" Value="50"/>
<ARRAY Name="ChildSkills">
<ATOM Name="AccurateBreathlessShot" Value="acurate breathless shot"/>
<ATOM Name="AnimalSlideSlip" Value="animal slideslip"/>
</ARRAY>
</STRUCT>
*/
string out;
out = string(" <STRUCT Name=\"")+ NormalizedSkillName + string("\">\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
out = string(" <ATOM Name=\"Skill\" Value=\"")+ SkillName + string("\"/>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
out = string(" <ATOM Name=\"SkillCode\" Value=\"")+ Code + string("\"/>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
out = string(" <ATOM Name=\"MaxSkillValue\" Value=\"")+ toString(MaxValue) + string("\"/>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
out = string(" <ATOM Name=\"Type of Stage\" Value=\"")+ toString(StageType) + string("\"/>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
if (ParentSkillPtr != NULL)
{
out = string(" <ATOM Name=\"ParentSkill\" Value=\"")+ ParentSkill + string("\"/>\n");
}
else
{
out = string(" <ATOM Name=\"ParentSkill\" Value=\"\"/>\n");
}
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
if( !Children.empty())
{
out = string(" <ARRAY Name=\"ChildSkills\">\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
for (uint i = 0 ; i < Children.size() ; ++i)
{
out = string(" <ATOM Name=\"") + Children[i]->NormalizedSkillName + string("\" Value=\"")+ Children[i]->SkillName + string("\"/>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
}
out = string(" </ARRAY>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
}
out = string(" </STRUCT>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
for (uint i = 0 ; i < Children.size() ; ++i)
Children[i]->writeInSheet(fo);
}
};
struct CSkillTree
{
void buildCode()
{
for (uint i = 0 ; i < RootSkills.size() ; ++i)
RootSkills[i]->buildCode();
}
vector<CSkill*> RootSkills;
};
static CSkillTree SkillTree;
static map< string, CSkill> SkillNameToStruct;
char separators[] = ";,";
//-----------------------------------------------
// main
//
//-----------------------------------------------
sint main( sint argc, char ** argv )
{
/////////////////////////////////////////////////////////////////////////////////////
// Somes working variables
char buffer[4096];
// vector contained selector category
vector< string > selector;
/////////////////////////////////////////////////////////////////////////////////////
// Check number of arguments
if( argc < 4 )
{
printf("Create a file .typ for george contained a subset of skills\n\n");
printf("SKILL_EXTRACTOR <output file> <input file skills> <[<selector> ...]\n");
printf(" param 1 : create tree (yes) or no (no)\n");
printf(" if output file have .typ extension, generate .typ george file format\n");
printf(" if output file have .dfn extension, generate .dfn george file format\n");
printf(" param 3 is sheet2 of SkillCategory.xls exported in csv format\n");
printf(" Selector is category present in input file, selectors begins by '+' for or operation and by '.' for and operation\n");
printf(" the first selector must be or operation (begin by '+' character)\n");
return 1;
}
// parse the config file
CConfigFile configFile;
try
{
configFile.load( "skill_extractor.cfg" );
}
catch(const Exception &e )
{
nlwarning("<CShopTypeManager::initShopBase> skill_extractor.cfg %s",e.what());
return 1;
}
//get the csv file
CConfigFile::CVar * cfgVar = configFile.getVarPtr("CsvDir");
if (!cfgVar)
{
printf("var 'CsvDir' not found in the skill_extractor.cfg");
return 1;
}
const string& CSVDir = cfgVar->asString() + string("/");
//get the path for the generated source files
cfgVar = configFile.getVarPtr("SrcDir");
if (!cfgVar)
{
printf("var 'SrcDir' not found in the skill_extractor.cfg");
return 1;
}
const string& srcDir = cfgVar->asString() + string("/");
//get the path for the generated source files
cfgVar = configFile.getVarPtr("PdsDir");
if (!cfgVar)
{
printf("var 'PdsDir' not found in the skill_extractor.cfg");
return 1;
}
const string& pdsDir = cfgVar->asString() + string("/");
//get the path for the generated dfn
cfgVar = configFile.getVarPtr("DfnDir");
if (!cfgVar)
{
printf("var 'DfnDir' not found in the skill_extractor.cfg");
return 1;
}
const string& dfnDir = cfgVar->asString() + string("/");
//get the path for the generated skill tree
cfgVar = configFile.getVarPtr("SkillTreeDir");
if (!cfgVar)
{
printf("var 'DfnDir' not found in the skill_extractor.cfg");
return 1;
}
const string& treeDir = cfgVar->asString() + string("/");
/////////////////////////////////////////////////////////////////////////////////////
// Export .typ and .dfn file
// open skill file
CIFile f;
if( ! f.open( CSVDir + string( argv[3] ) ) )
{
nlwarning( "File %s open failed", argv[3] );
return 1;
}
// read all input file
uint col;
string skillName;
char * ptr;
map< string, CSkill>::const_iterator itSkillStruct;
while( ! f.eof() )
{
f.getline( buffer, 4096 );
col = 0;
ptr = strtok( buffer, separators );
CSkill skill;
while( ptr && string( ptr ) != string(" ") )
{
switch(col)
{
case 0: // skill name
{
skillName = strupr( string( ptr ) );
vector< string > emptyVectorOfString;
skill.skillName(skillName);
}
break;
case 1: // code
skill.Code = toUpper(string( ptr ));
break;
case 2: // parent skill
skill.ParentSkill = toUpper(string( ptr ));
break;
case 3: // max skill value
NLMISC::fromString(std::string(ptr), skill.MaxValue);
break;
case 4: // stage type
NLMISC::fromString(std::string(ptr), skill.StageType);
break;
case 5: // main category
skill.MainCategory = string( ptr );
break;
case 6: // secondary category
skill.SecondaryCategory = string( ptr );
break;
default: // error ?
break;
};
++col;
ptr = strtok( 0, separators );
}
if ( !skill.SkillName.empty())
{
// insert skill in the Map
//pair< map< string, CSkill>::const_iterator, bool> skillInsert = SkillNameToStruct.insert( make_pair(skill.SkillName, skill) );
//nlinfo("Insert skill %s, parent %s", skill.SkillName.c_str(), skill.ParentSkill.c_str() );
SkillNameToStruct.insert( make_pair(skill.SkillName, skill) );
}
}
f.close();
// create the tree
map< string, CSkill>::iterator itSkill;
map< string, CSkill>::iterator itSkillEnd = SkillNameToStruct.end();
uint count = 0;
for ( itSkill = SkillNameToStruct.begin() ; itSkill != itSkillEnd ; ++itSkill )
{
++count;
if ( (*itSkill).second.ParentSkill == string("NONE") || (*itSkill).second.ParentSkill.empty() )
{
SkillTree.RootSkills.push_back(&((*itSkill).second));
}
else
{
map< string, CSkill>::iterator its = SkillNameToStruct.find((*itSkill).second.ParentSkill);
if (its == itSkillEnd)
{
nlwarning("ERROR : cannot find the parent skill %s for skill %s (skill %u)", (*itSkill).second.ParentSkill.c_str(), (*itSkill).second.SkillName.c_str(), count );
nlstop;
}
(*itSkill).second.ParentSkillPtr = &((*its).second);
(*its).second.Children.push_back(&((*itSkill).second));
}
}
SkillTree.buildCode();
COFile fo;
// create the skill tree if first param == yes
if ( string( argv[1] ) == string("yes") )
{
if( ! fo.open( treeDir + string("skills.skill_tree"), false ) )
{
nlwarning(" Can't open file skills.skill_tree for writing");
return 1;
}
string out("<?xml version=\"1.0\"?>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
out = string("<FORM Version=\"0.2\" State=\"modified\">\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
out = string(" <STRUCT>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
out = string(" <ARRAY Name=\"SkillData\">\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
for ( vector<CSkill*>::const_iterator itTree = SkillTree.RootSkills.begin() ; itTree != SkillTree.RootSkills.end() ; ++itTree)
{
(*itTree)->writeInSheet(fo);
}
out = string(" </ARRAY>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
out = string(" </STRUCT>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
out = string("</FORM>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
fo.close();
// create the code .typ
if( ! fo.open( string("_skillsCode.typ"), false ) )
{
nlwarning(" Can't open file _skillsCode.typ for writing");
return 1;
}
out = string("<TYPE Type=\"String\" UI=\"NonEditableCombo\" Default=\"None\" Version=\"0.1\" State=\"modified\">\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
out = string(" <DEFINITION Label=\"unknown\" Value=\"unknown\"/>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
set<string>::const_iterator itCode;
for ( itCode = Codes.begin() ; itCode != Codes.end() ; ++itCode )
{
out = string(" <DEFINITION Label=\"") + (*itCode) + string("\" Value=\"") + (*itCode) + string("\"/>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
}
out = string("</TYPE>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
fo.close();
}
// read category in command line
for( sint i = 4; i < argc; ++i )
{
selector.push_back( string( argv[ i ] ) );
}
// generate a file containing skills and associated Code
if( ! fo.open( string( "skill_codes.txt" ) ) )
{
nlwarning(" Can't open file %s for writing", "skill_codes.txt" );
return 1;
}
for ( itSkill = SkillNameToStruct.begin() ; itSkill != itSkillEnd ; ++itSkill)
{
string out;
string space;
if ( (*itSkill).second.NormalizedSkillName.size() < 50)
space.resize( 50 - (*itSkill).second.NormalizedSkillName.size(), ' ' );
outLine((*itSkill).second.NormalizedSkillName + space + string("\t") + (*itSkill).second.Code + string("\n") );
}
// generate .typ or .dfn file
if( ! fo.open( dfnDir + string( argv[2] ), false ) )
{
nlwarning(" Can't open file %s for writing", argv[2] );
return 1;
}
// output header of .typ or .dfn file
string out("<?xml version=\"1.0\"?>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
if( string( argv[2] ).find(".typ") != string::npos )
{
out = string("<TYPE Type=\"String\" UI=\"NonEditableCombo\" Default=\"unknown\" Version=\"0.1\" State=\"modified\">\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
out = string(" <DEFINITION Label=\"unknown\" Value=\"unknown\"/>\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
}
else
{
out = string("<DFN Version=\"0.0\" State=\"modified\">\n");
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
}
// parse all skills to export selected ones
for ( itSkillStruct = SkillNameToStruct.begin() ; itSkillStruct != SkillNameToStruct.end() ; ++itSkillStruct )
{
bool selected = false;
bool found = false;
const CSkill &skill = (*itSkillStruct).second;
for( vector< string >::iterator its = selector.begin(); its != selector.end(); ++its )
{
found = false;
if ( skill.MainCategory == (*its).substr(1) )
{
found = true;
}
else if ( skill.SecondaryCategory == (*its).substr(1) )
{
found = true;
}
if( found )
{
if( (*its).substr( 0, 1) == string("+") ) // or operation
{
selected = true;
}
else if( (*its).substr( 0, 1) == string(".") ) // and operation
{
selected &= true;
}
}
else if( (*its).substr( 0, 1) == string(".") )
{
selected = false;
}
}
if( selected )
{
if( string( argv[2] ).find(".typ") != string::npos )
{
out = string(" <DEFINITION Label=\"") + skill.SkillName + string("\" Value=\"") + skill.NormalizedSkillName + string("\"/>\n");
}
else
{
out = string(" <ELEMENT Name=\"") + skill.NormalizedSkillName + string("\" Type=\"Type\" Filename=\"creature_stat.typ\"/>\n");
}
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
}
}
if( string( argv[2] ).find(".typ") != string::npos )
{
out = string("</TYPE>\n");
}
else
{
out = string("</DFN>\n");
}
fo.serialBuffer( (uint8 *) const_cast< char * >(out.c_str()), (uint)out.size() );
fo.close();
/////////////////////////////////////////////////////////////////////////////////////
// Generate skill.cpp and skill.h code file
// output begin skill.h file
if( ! fo.open( srcDir + string( "skills.h" ) ) )
{
nlwarning(" Can't open file %s for writing", "skills.h" );
return 1;
}
// write header of header file
outLine("/** \\file skills.h\n");
outLine(" * skills enumeration: generated by skill extractor program\n");
outLine(" *\n");
outLine(" */\n");
outLine("\n");
outLine("#ifndef RY_SKILLS_H\n");
outLine("#define RY_SKILLS_H\n");
outLine("\n");
outLine("#include \"nel/misc/types_nl.h\"\n");
outLine("\n");
outLine("#include <string.h>\n");
outLine("\n");
outLine( string("// NbSkills in enum : ") + toString( SkillNameToStruct.size() ) + string(" Report this in database.xml \n\n") );
outLine("namespace SKILLS\n");
outLine("{\n");
outLine(" enum ESkills\n");
outLine(" {\n");
itSkill = SkillNameToStruct.begin();
if (itSkill != itSkillEnd)
{
outLine(string(" ") + (*itSkill).second.NormalizedSkillName + string(" = 0,\n") );
for ( ++itSkill; itSkill != itSkillEnd ; ++itSkill)
{
outLine(string(" ") + (*itSkill).second.NormalizedSkillName + string(",\n") );
}
}
// output end skill enum and skill type enum and skill api
outLine("\n");
outLine(" NUM_SKILLS,\n");
outLine(" unknown,\n");
outLine(" };\n");
outLine("\n");
// output all skills
/* for( it = skillsAndSelector.begin(); it != skillsAndSelector.end(); ++it )
{
uint idxSpace;
out = (*it).first;
while( ( idxSpace = out.find(" ") ) != string::npos )
{
string tmp = out.substr( 0, idxSpace );
if( idxSpace < ( out.size() - 1 ) )
{
tmp = tmp + string("_") + out.substr( idxSpace + 1 );
}
out = tmp;
}
outLine( string(" ") + out + string(",\n") );
}
// output end skill enum and skill type enum and skill api
outLine("\n");
outLine(" NUM_SKILLS,\n");
outLine(" unknown\n");
outLine(" };\n");
outLine("\n");
outLine(" enum ESkillType\n");
outLine(" {\n");
outLine(" skill,\n");
outLine(" specialized_skill,\n");
outLine(" training_characteristic,\n");
outLine(" training_resist,\n");
outLine(" training_score,\n");
outLine("\n");
outLine(" unknown_skill_type\n");
outLine(" };\n");
outLine("\n");
*/
outLine(" /**\n");
outLine(" * get the right skill enum from the input string\n");
outLine(" * \\param str the input string\n");
outLine(" * \\return the ESkills associated to this string (Unknown if the string cannot be interpreted)\n");
outLine(" */\n");
outLine(" ESkills toSkill ( const std::string &str );\n");
outLine("\n");
outLine(" /**\n");
outLine(" * get the right skill string from the gived enum\n");
outLine(" * \\param skill the skill to convert\n");
outLine(" * \\return the string associated to this enum number (Unknown if the enum number not exist)\n");
outLine(" */\n");
outLine(" const std::string& toString( uint16 skill );\n");
outLine("\n");
outLine(" /**\n");
outLine(" * get the skill category name\n");
outLine(" * \\param s is the enum number\n");
outLine(" * \\return the string name of skill type (Unknown if the enum number not exist)\n");
outLine(" */\n");
outLine(" const std::string& getSkillCategoryName( uint16 s );\n");
outLine("\n");
outLine("}; // SKILLS\n");
outLine("\n");
outLine("#endif // RY_SKILLS_H\n");
outLine("/* End of skills.h */\n");
/////////////////////////////////////////////////////////////////////////////////////
// begin output skill.cpp file
if( ! fo.open( srcDir + string( "skills.cpp" ) ) )
{
nlwarning(" Can't open file skills.cpp for writing");
return 1;
}
outLine("/** \\file skills.cpp\n");
outLine(" * \n");
outLine(" */\n\n");
outLine("#include \"stdpch.h\"\n");
outLine("\n");
outLine("#include \"nel/misc/debug.h\"\n");
outLine("#include \"skills.h\"\n");
outLine("#include \"nel/misc/string_conversion.h\"\n");
outLine("\n");
outLine("using namespace std;\n");
outLine("using namespace NLMISC;\n");
outLine("\n");
outLine("namespace SKILLS\n");
outLine("{\n");
outLine("\n");
outLine("static string UnknownString(\"Unknown\");\n");
outLine("\n");
outLine("\tNL_BEGIN_STRING_CONVERSION_TABLE (ESkills)\n");
// parser all skills and init the conversion map
for ( itSkill = SkillNameToStruct.begin() ; itSkill != itSkillEnd ; ++itSkill)
{
outLine (string ("\t NL_STRING_CONVERSION_TABLE_ENTRY(") + (*itSkill).second.NormalizedSkillName + string(")\n") );
}
//outLine (string (" { \"unknown\", unknown },\n" ) );
outLine (string("\t NL_STRING_CONVERSION_TABLE_ENTRY(unknown)\n") );
outLine("\tNL_END_STRING_CONVERSION_TABLE(ESkills, SkillsConversion, unknown)\n");
outLine("\n");
outLine("\n");
outLine("\tESkills toSkill( const std::string &str )\n");
outLine("\t{\n");
outLine("\t return SkillsConversion.fromString(str);\n");
outLine("\t}\n");
outLine("\n");
outLine("\tconst std::string& toString( uint16 skill )\n");
outLine("\t{\n");
outLine("\t return SkillsConversion.toString((ESkills)skill);\n");
outLine("\t}\n");
outLine("\n");
outLine("\n");
outLine("\tconst std::string& getSkillCategoryName( uint16 s )\n");
outLine("\t{\n");
/*outLine(" if( s < sizeof(SkillCategoryStrings)/sizeof(SkillCategoryStrings[0]) )\n");
outLine(" {\n");
outLine(" return SkillCategoryStrings[ s ];\n");
outLine(" }\n");
outLine(" else return UnknownString;\n");
*/
outLine("\t return UnknownString;\n");
outLine("\t}\n");
outLine("\n");
outLine("}; // SKILLS\n");
fo.close();
/////////////////////////////////////////////////////////////////////////////////////
// Generate skills.pds script file
if( ! fo.open( pdsDir + string( "skills.pds" ) ) )
{
nlwarning(" Can't open file %s for writing", "skills.pds" );
return 1;
}
outLine( string("// NbSkills in enum : ") + toString( SkillNameToStruct.size() ) + string(" Report this in database.xml \n\n") );
outLine("file \"skills.h\"\n");
outLine("{\n");
outLine("\tenum TSkill\n");
outLine("\t{\n");
outLine("\t\tBeginSkill\n");
outLine("\t\t{\n");
itSkill = SkillNameToStruct.begin();
if (itSkill != itSkillEnd)
{
outLine(string("\t\t\t") + (*itSkill).second.NormalizedSkillName);
for ( ++itSkill; itSkill != itSkillEnd ; ++itSkill)
{
outLine(",\n");
outLine(string("\t\t\t") + (*itSkill).second.NormalizedSkillName);
}
}
outLine("\n\t\t}\n");
outLine("\t} EndSkill\n");
outLine("}\n");
fo.close();
nlinfo("job finish");
return EXIT_SUCCESS;
}