// 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 "moulinette.h"
#include "utils.h"
#include "nel/misc/algo.h"

TRMItem currentDocItem;
CRMData SortableData;


// Preloaded Files
CSString	FamilyTypContent;
CSString	GroupTypContent;
CSString	WKContent;


// Assign a new raw mat to a creature
void AssignerMP( const CSString& creatureName, const CSString& materialName )
{
	// we check if a creature is degenerated or not
	if ( ( creatureName.c_str()[3] != 'c' ) && ( creatureName.c_str()[3] != 'd' ) 
		&& ( creatureName.c_str()[3] != 'f' ) && ( creatureName.c_str()[3] != 'j' )
		&& ( creatureName.c_str()[3] != 'l' ) && ( creatureName.c_str()[3] != 'p' ) )
	{
	}
	else
	{
		// lecture du fichier d'assignement
		CSString fileName = toString( "%s//_%s_mp.creature", RAW_MATERIAL_ASSIGN.c_str(), creatureName.c_str() ); 
		CSString data;

		// create file if not exists
		if(!CFile::fileExists(fileName))
		{
			CSString	str;
			str = "<?xml version=\"1.0\"?>\r\n";
			str+= "<FORM Version=\"0.0\" State=\"modified\">\r\n";
			str+= "  <STRUCT>\r\n";
			str+= "    <STRUCT Name=\"Harvest\">\r\n";
			str+= "    </STRUCT>\r\n";
			str+= "  </STRUCT>\r\n";
			str+= "  <STRUCT/>\r\n";
			str+= "  <STRUCT/>\r\n";
			str+= "  <STRUCT/>\r\n";
			str+= "  <STRUCT/>\r\n";
			str+= "</FORM>\r\n";
			str.writeToFile( fileName );
		}

		// lecture
		data.readFromFile( fileName );

		if ( !data.contains( materialName.c_str() ) )
		{	
			// look for first number of unused raw mat
			CSString str = data;
			int nb= 0;
			while ( str.contains( "Name=\"MP" ) )
			{
				str = str.splitFrom( "Name=\"MP" );
				nb = str.firstWord().atoi();
			}

			// insert new raw mat
			str = "      <STRUCT Name=\"MP";
			str += toString( "%d\">\r\n        <ATOM Name=\"AssociatedItem\"", nb+1 );
			str += toString( " Value=\"%s\"/>\r\n      </STRUCT>\r\n    </STRUCT>\r\n  </STRUCT>\r\n", materialName.c_str() );
			
			data = data.replace( "    </STRUCT>\r\n  </STRUCT>\r\n", str.c_str() );
			data.writeToFile( fileName );
		}
	}
}


// save stats for each craft part
void LoadCraftParts()
{
	CSString data, ligne, info;
	data.readFromFile( ITEM_MP_PARAM_DFN );
	int index;

	printf( "-- LOADING CRAFT PARTS --\n" );
	do 
	{
		data = data.splitFrom( "Name=\"" );
		int index;
		index = data.c_str()[0] - 'A';
		craftParts[index].Desc = data.splitTo( "\"" );

	} while ( data.c_str()[0] != 'Z' );

	if ( ! CFile::fileExists( "rm_item_parts.csv" ) )
	{
		nlError( "rm_item_parts.csv not found\n");
		exit( 1 );
	}
	
	data.readFromFile( "rm_item_parts.csv" );
	
	while ( !data.empty() )
	{
		ligne = data.splitTo( "\n", true );
		
		// look for line concerning our craft part
		info = ligne.splitTo( ";", true );
		if ( !info.empty() )
		{
			index = info.c_str()[0] - 'A';

			for ( int i=0; i<6; i++ )
				ligne.splitTo( ";", true );

			// browse each characteristics
			for ( int i=0; i<NumMPStats; i++ )
			{
				info = ligne.splitTo( ";", true ).left(1);

				// if the cell is not empty, this characteristic is generated by craft part
				if ( ( info == "C" ) || ( info == "D" ) || ( info == "X" ) )
					craftParts[index].Carac[i] = true;
				else
					craftParts[index].Carac[i] = false;
			}
		}
	}
}


// Save creatures filenames in bestiary
void LoadCreatureFiles()
{
	printf( "-- REGISTERING CREATURE FILES --\n" );
	CSString inputSheetPath = LEVEL_DESIGN_PATH + "leveldesign\\Game_elem\\Creature\\Fauna\\bestiary";
	CPath::addSearchPath( inputSheetPath, true, false );

	vector<string> files;
	// on parcours les fichiers dans le repertoire
	CPath::getPathContent ( inputSheetPath, true, false, true, files );

	for (uint32 i=0; i<files.size(); ++i)
	{
		string filename = files[i];
		string filebase = CFile::getFilenameWithoutExtension( filename );
		creatureFiles[ filebase ] = filename;
	}
}


// Save items levels list to generate for each creature 
void InitCreatureMP()
{
	CSString data, ligneN, ligneM;
	CSString nom, code;
	map<string,string>::const_iterator it;

	if ( ! CFile::fileExists( "creature_models.csv" ) )
	{
		nlError( "creature_models.csv not found\n");
		exit( 1 );
	}
		
	data.readFromFile( "creature_models.csv" );

	while ( !data.empty() )
	{
		ligneN = data.splitTo( "\n", true );
		ligneM = data.splitTo( "\n", true );
		
		// check if line is valid
		if ( !ligneN.splitTo( ";", true ).empty() )
		{
			ligneM.splitTo( ";", true );

			// move until data
			ligneN.splitTo( ";", true );
			ligneM.splitTo( ";", true );
			ligneN.splitTo( ";", true );
			ligneM.splitTo( ";", true );

			while ( !ligneN.empty() )
			{
				ListeCreatureMP listeCreatureMP;
				
				if ( ligneN.contains( ";" ) )
				{
					code = ligneN.splitTo( ";", true );
					nom = ligneM.splitTo( ";", true );
				}
				else
				{
					code = ligneN.firstWord();
					nom = ligneM.firstWord();
					ligneN = "";
				}

				// save items to generate for each new creature name found
				for ( char eco='a'; eco<='z'; eco++ )
				{
					for ( int level=0; level<10; level++ )
					{
						for ( int creatureLevel=1; creatureLevel<=8; creatureLevel++ )
						{
							CSString fileName = toString( "c%s%c%c%d", code.toLower().c_str(), eco, 
								                          'a' + level, creatureLevel );
							// look for corresponding creature
							it = creatureFiles.find( fileName );

							if ( it != creatureFiles.end() )
							{			
								// if yes, this item should be generated
								CreatureMPItem* creatureMP = new CreatureMPItem;
								creatureMP->creatureLevel = creatureLevel;
								creatureMP->eco = eco;
								creatureMP->itemLevel = level;
								creatureMP->codeCreature = code;
								creatureMP->creatureFileName = fileName;
								listeCreatureMP.push_back( creatureMP );
							}	
						}
					}
				}

				itemsAGenerer[ nom ] = listeCreatureMP;
			}
		}
	}
}


// Return quality maximum foran item level
int GetMaxQuality( int level )
{
	if ( level == 0 )
		return 50;

	return ( level * 50 );
}


// Return energy maximum for an item level
int GetStatEnergy( int level )
{
	if ( level == 0 )
		return 20;
	
	return ( 20 + ( level-1 ) * 15 );
}


// Return item class name depending on its characteristics
void GetItemClass( int level, bool mission, bool creature, CSString& outClassName )
{
	static CSString missionClasses[] = { "Plain", "Average", "Prime", "Select", 
		                                 "Superb", "Magnificient" };

	static CSString nonMissionClasses[] = { "", "Basic", "Fine", "Choice", 
		                                    "Excellent", "Supreme" };

	if ( mission )
		outClassName = missionClasses[ level ];
	else if ( creature )
		outClassName = nonMissionClasses[ level+1 ];
	else
		outClassName = nonMissionClasses[ level ];
}


// Retourne la couleur de l'item
void GetItemColor( int color, char eco, int level, CSString& outStr )
{
	CSString commonColors [4] = { "Beige", "Green", "Turquoise", "Violet" };
	CSString rareColors [2] = { "Red", "Blue" };
	CSString primeRootColors [2] = { "White", "Black" };
	
	if ( eco == 'p' )
		outStr = primeRootColors[ color % 2 ];
	else if ( ( eco != 'c' ) && ( level >= 2 ) )
		outStr = rareColors[ color % 2 ];
	else if ( ( eco == 'c' ) && ( level >= 3 ) )
		outStr = rareColors[ color % 2 ];
	else
		outStr = commonColors[ color % 4 ];
} 


bool endsWith( const CSString& s, const CSString& substring )
{
	return (s.right( (uint)substring.size() ) == substring);
}


// Generate item names
void GenerateItemNames( const CSString& nomMP, char eco, int level, bool mission, bool creature, CSString& outStr )
{
	CSString itemClass, prefix, singularWithNoPrefix;
	CSString ia = "a";

	GetItemClass( level, mission, creature, itemClass );
	currentDocItem.push( DtStatQuality, itemClass );

	if ( !mission )
	{
		if ( ( creature && ( level == 3 ) ) || ( !creature && ( level == 4 ) ) )
			ia = "an";
	}

	singularWithNoPrefix = itemClass + " ";

	// selection de l'eco-systeme
	switch ( eco )
	{
	case 'c' :
		break;

	case 'd' :
		singularWithNoPrefix += "Desert ";
		break;

	case 'f' :
		singularWithNoPrefix += "Forest ";
		break;

	case 'j' :
		singularWithNoPrefix += "Jungle ";
		break;

	case 'l' :
		singularWithNoPrefix += "Lake ";
		break;

	case 'p' :
		singularWithNoPrefix += "Prime Root ";
		break;
	}

	singularWithNoPrefix += nomMP;

	// Contenant
	if ( endsWith( nomMP, "Wood" ) )
		prefix = "Bundle";
	else if ( endsWith( nomMP, "Bark" ) || endsWith( nomMP, "Moss" ) || endsWith( nomMP, "Sawdust" ) ||
			  endsWith( nomMP, "Straw" ) || endsWith( nomMP, "Dust" ) || endsWith( nomMP, "Soil" ) ||
			  endsWith( nomMP, "Cereal" ) )
		prefix = "Handful";
	else if ( endsWith( nomMP, "Resin" ) || endsWith( nomMP, "Wax" ) )
		prefix = "Portion";
	else if ( endsWith( nomMP, "Whiskers" ) || endsWith( nomMP, "Hairs" ) )
		prefix = "Tuft";
	else if ( endsWith( nomMP, "Silk" ) )
		prefix = "Ball";
	else if ( endsWith( nomMP, "Sap" ) || endsWith( nomMP, "Residue" ) || endsWith( nomMP, "Honey" ) ||
			  endsWith( nomMP, "Blood" ) )
		prefix = "Phial";
	else if ( endsWith( nomMP, "Fruit" ) )
		prefix = "Piece";
	else if ( endsWith( nomMP, "Flesh" ) )
		prefix = "Morsel";
	else if ( endsWith( nomMP, "Saliva" ) )
		prefix = "Sample";
	else if ( endsWith( nomMP, "Pollen" ) || endsWith( nomMP, "Fiber" ) || endsWith( nomMP, "Amber" ) ||
			  endsWith( nomMP, "Leather" ) || endsWith( nomMP, "Oil" ) )
		ia = "some";
	else if ( endsWith( nomMP, "Pelvis" ) || endsWith( nomMP, "Eye" ) || endsWith( nomMP, "Spine" ) ||
			  endsWith( nomMP, "Hoof" ) || endsWith( nomMP, "Mandible" ) || endsWith( nomMP, "Claw") ||
			  endsWith( nomMP, "Tail" ) || endsWith( nomMP, "Trunk" ) || endsWith( nomMP, "Shell" ) ||
			  endsWith( nomMP, "Sting" ) || endsWith( nomMP, "Skin" ) || endsWith( nomMP, "Beak" ) ||
			  endsWith( nomMP, "Wing" ) || endsWith( nomMP, "Horn" ) || endsWith( nomMP, "Rostrum" ) ||
			  endsWith( nomMP, "Skull" ) || endsWith( nomMP, "Pistil" ) )
		prefix = "Fragment"; // number-limited creature objects

	if ( ! prefix.empty() )
	{
		outStr = prefix + " of " + singularWithNoPrefix;
		ia = "a";
	}
	else
		outStr = singularWithNoPrefix;

	CSString singular = outStr;
	currentDocItem.push( DtTitle, outStr );

	// A, The
	outStr += "\t" + ia + "\tthe\t";

	// Plural
	if ( prefix.empty() )
		outStr += singular + "s";
	else
		outStr += prefix + "s" + " of " + singularWithNoPrefix;

	outStr += "\t\tthe";
}


// Return number of specified family
int GetNumeroMP( const CSString& nomMP )
{
	CSString result;
	char buffer[100];
	char buffer2[100];
	int res;

	// *** Get the family number, and add it to faimly.typ if not already done
	// look for raw mat existence in item_mp_family.typ file
	sprintf( buffer, "%s\" Value=\"", nomMP.c_str() );
	result = FamilyTypContent.splitFrom( buffer );

	// if yes, return raw mat number
	if ( !result.empty() )
		res = result.splitTo( "\"" ).atoi();
	else
	{
		// else, generate a new number:
		// get last raw mat number (the max)
		result = FamilyTypContent.splitTo( "<LOG>" ).right(10);
		result.splitTo( "\"", true );
		result = result.splitTo( "\"" );

		// increase by 1 to get the unused number
		res = result.atoi() + 1;

		// add new raw mat in file item_mp_family.typ
		sprintf( buffer, "  <DEFINITION Label=\"%s\" Value=\"%d\"/>\n<LOG>", nomMP.c_str(), res );
		FamilyTypContent= FamilyTypContent.replace( "<LOG>", buffer );
		FamilyTypContent.writeToFile( ITEM_MP_FAMILY_TYP );
	}

	// *** Add the text in wk.uxt (if not done)
	// Exist in wk.uxt ???
	sprintf( buffer, "mpfam%d\t", res );
	sprintf( buffer2, "mpfam%d ", res );
	// if not found
	if ( !WKContent.contains(buffer) && !WKContent.contains(buffer2) )
	{
		// add it at end
		sprintf( buffer, "mpfam%d\t\t\t[%s]\n\r\nmpgroup0", res, nomMP.c_str() );
		WKContent= WKContent.replace( "\r\nmpgroup0", buffer );
		WKContent.writeToFile( WK_UXT );
	}

	return res;
}


// Return number from specified group
int GetNumeroGroupe( const CSString& groupe )
{
	CSString result;
	char buffer[100];
	char buffer2[100];
	int res;

	// *** Get the group number, and add it to group.typ if not already done
	// look for group existence in item_mp_group.typ file
	sprintf( buffer, "%s\" Value=\"", groupe.c_str() );
	result = GroupTypContent.splitFrom( buffer );

	// if yes, return its group number
	if ( !result.empty() )
		res = result.splitTo( "\"" ).atoi();
	else
	{
		// else, generate a new number :
		// get the last group number (the max)
		result = GroupTypContent.splitTo( "<LOG>" ).right(10);
		result.splitTo( "\"", true );
		result = result.splitTo( "\"" );

		// increase by 1 to get the unused number
		res = result.atoi() + 1;

		// add new raw mat in file item_mp_group.typ
		sprintf( buffer, "<DEFINITION Label=\"%s\" Value=\"%d\"/>\n<LOG>", groupe.c_str(), res );
		GroupTypContent= GroupTypContent.replace( "<LOG>", buffer );
		GroupTypContent.writeToFile( ITEM_MP_GROUPE_TYP );
	}


	// *** Add the text in wk.uxt (if not done)
	// Exist in wk.uxt ???
	sprintf( buffer, "mpgroup%d\t", res );
	sprintf( buffer2, "mpgroup%d ", res );
	// if not found
	if ( !WKContent.contains(buffer) && !WKContent.contains(buffer2) )
	{
		// add it at end
		sprintf( buffer, "mpgroup%d\t\t\t[%s]\n\r\nmpSource", res, groupe.c_str() );
		WKContent= WKContent.replace( "\r\nmpSource", buffer );
		WKContent.writeToFile( WK_UXT );
	}

	return res;
}


// Generate parent item for a raw mat
void CreateParentSItem( int numMP, 
					    const CSString& nomMP,
						const CSString& groupe,
						bool dropOrSell,
						const CSString& icon,
						const CSString& overlay )
{
	CSString output;
	CSString outputFileName;

	// output filename
	outputFileName = toString( "%s_parent\\_m%04d.sitem", MP_DIRECTORY.c_str(), numMP );

	// xml header
	output = "<?xml version=\"1.0\"?>\n<FORM Version=\"0.0\" State=\"modified\">\n";
	
	// basics
	output += "  <STRUCT>\n    <STRUCT Name=\"basics\">\n";
	output += "      <ATOM Name=\"Drop or Sell\" Value=\"";

	if ( !dropOrSell )
		// mission items can't be sold
		output += "false\"/>\n";
	else
		// the others, yes
		output += "true\"/>\n";

	output += "      <ATOM Name=\"Bulk\" Value=\"0.5\"/>\n    </STRUCT>\n";

	// raw mat
	output += "    <STRUCT Name=\"mp\">\n";
	output += "      <ATOM Name=\"Family\" Value=\"";
	output += nomMP;
	output += "\"/>\n      <ATOM Name=\"Group\" Value=\"";
	output += groupe;
	output += "\"/>\n    </STRUCT>\n";

	// 3d
	output += "    <STRUCT Name=\"3d\">\n";
	
	if ( !icon.empty() )
	{
		output += "      <ATOM Name=\"icon\" Value=\"";
		output += icon;
		output += "\"/>\n";
	}

	if ( !overlay.empty() )
	{
		output += "      <ATOM Name=\"text overlay\" Value=\"";
		output += overlay;
		output += "\"/>\n";
	}

	output += "    </STRUCT>\n  </STRUCT>\n";

	// end of file
	output += "  <STRUCT/>\n  <STRUCT/>\n  <STRUCT/>\n  <STRUCT/>\n</FORM>\n";

	// final write
	output.writeToFile( outputFileName );

}


// Fill information related to craft with an item
void FillCraftData( char craft, int eco, int level, bool creature, int bestStat, 
				    int worstStat1, int worstStat2, CSString& outStr )
{
	CSString data;
	char buf[10];
	int index;
	static CSString carac[] = { "Durability", "Weight", "SapLoad", "DMG", "Speed",
			                    "Range", "DodgeModifier", "ParryModifier", 
					            "AdversaryDodgeModifier", "AdversaryParryModifier",
						        "ProtectionFactor", "MaxSlashingProtection",
						        "MaxBluntProtection", "MaxPiercingProtection",
						        "ElementalCastingTimeFactor", "ElementalPowerFactor",
						        "OffensiveAfflictionCastingTimeFactor", 
						        "OffensiveAfflictionPowerFactor", "HealCastingTimeFactor",
						        "HealPowerFactor", "DefensiveAfflictionCastingTimeFactor",
						        "DefensiveAfflictionPowerFactor",
								"AcidProtection",
								"ColdProtection",
								"FireProtection",
								"RotProtection",
								"ShockWaveProtection",
								"PoisonProtection",
								"ElectricityProtection",
								"DesertResistance",
								"ForestResistance",
								"LacustreResistance",
								"JungleResistance",
								"PrimaryRootResistance",
							};
	nlctassert((sizeof(carac)/sizeof(carac[0]))==NumMPStats);

	static int mediumStatsByStatQuality[] = { 20, 35, 50, 65, 80 };
	int stat, remaining, nbToRaise, ajout;
	int stats[NumMPStats];

	index = craft - 'A';

	outStr = "        <STRUCT Name=\"";
	outStr += craftParts[index].Desc;
	outStr += "\">\n";

	nbToRaise = 0;
	remaining = 0;
	ajout = 0;

	currentDocItem.push( DtCraftSlotName, craftParts[index].Desc.splitFrom( "(" ).splitTo( ")" ) );

	// save stats for each characteristic
	for ( int i=0; i<NumMPStats; i++ )
	{
		if ( craftParts[index].Carac[i]  )
		{
			if ( !creature )
				stat = mediumStatsByStatQuality[ level-1 ];
			else
				stat = mediumStatsByStatQuality[ level ];
			
			// manage weak/strong points of a raw mat
			if ( i == bestStat )
			{
				if ( worstStat2 == -1 )
					stat += 20;
				else
					stat += 40;

				if ( stat > 100 )
				{
					// if a stat exceeds 100, other are increased
					remaining = stat - 100;
					stat = 100;
				}
			}
			else if ( ( i == worstStat1 ) || ( i == worstStat2 ) )
			{
				stat -= 20;

				// minimum durability
				if ( ( i == 0 ) && ( stat < 1 ) )
					stat = 1;
			}
			else
				nbToRaise++;

			stats[i] = stat;
		}
		else 
			stats[i] = -1;
	}

	if ( nbToRaise != 0 )
		ajout = remaining/nbToRaise;
	
	// add information for each characteristic
	for ( int i=0; i<NumMPStats; i++ )
	{
		if ( stats[i] != -1 )
		{
			if ( ( i != bestStat ) && ( i != worstStat1 ) && ( i != worstStat2 ) )
				stats[i] += ajout;
		
			outStr += "          <ATOM Name=\"";
			outStr += carac[i];
			outStr += "\" Value=\"";
			
			sprintf( buf, "%d", stats[i] );

			outStr += buf;
			outStr += "\"/>\n";
		}
	}

	// CraftCivSpec depending on ecosystem
	outStr += "          <ATOM Name=\"CraftCivSpec\" Value=\"";
	CSString craftCiv;

	switch ( eco )
	{
	case 'c' :
		outStr += "common";
		craftCiv = "All";
		break;

	case 'd' :
		outStr += "fyros";
		craftCiv = "Fyros";
		break;

	case 'f' :
		outStr += "matis";
		craftCiv = "Matis";
		break;

	case 'j' :
		outStr += "zorai";
		craftCiv = "Zorai";
		break;

	case 'l' :
		outStr += "tryker";
		craftCiv = "Tryker";
		break;

	case 'p' :
		outStr += "common";
		craftCiv = "All";
		break;
	}

	currentDocItem.push( DtCraftCivSpec, craftCiv );

	outStr += "\"/>\n        </STRUCT>\n";
}


// Create item sheets
void CreateSheet( int numMP, const CSString& nomMP,
				  const CSString& code, char eco,  
				  int level, const MPCraftStats& craftStats,
				  bool specialItem = false, int variation = 1 )
{
	CSString output, directory, itemName, craftInfo, color;
	CSString outputFileName, ecoStr;
	char chaineNum[5];
	bool creature = ( code != "dxa" ) && ( code != "cxx" ) && ( code != "ixx" );

	// Creation du nom de fichier
	sprintf( chaineNum, "%04d", numMP );

	switch ( eco )
	{
	case 'd' :
		directory = "desert";
		ecoStr = "Desert";
		break;

	case 'f' :
		directory = "forest";
		ecoStr = "Forest";
		break;

	case 'j' :
		directory = "jungle";
		ecoStr = "Jungle";
		break;

	case 'l' :
		directory = "lacustre";
		ecoStr = "Lacustre";
		break;

	case 'p' :
		directory = "prime_roots";
		ecoStr = "PrimeRoots";
		break;

	default :
		directory = "common";
		ecoStr = "Common";
		eco = 'c';
		break;
	}

	if ( ( eco == 'c' ) && creature && ( craftStats.Craft.empty() ) )
		return;

	outputFileName = toString( "m%04d%s%c%c%02d.sitem", numMP, code.c_str(), eco, 
		                     'a' + level, variation );

	if ( craftStats.Craft.empty() )
	{
		CSString levelZone = toString( "%c", 'a' + level );
		currentDocItem.push( DtLevelZone, levelZone.toUpper() );
	}
	else
		currentDocItem.push( DtLevelZone, "-" );
	
	// fill sheets information
	output = "<?xml version=\"1.0\"?>\n<FORM Version=\"0.0\" State=\"modified\">\n";
	output += "  <PARENT Filename=\"_m";
	output += eco;
	output += ".sitem\"/>\n  <PARENT Filename=\"_m";
	output += chaineNum;
	output += ".sitem\"/>\n";

	// if a creature code, add it its parent
	if ( creature )
	{
		output += "  <PARENT Filename=\"_m"; 
		output += code;
		output += ".sitem\"/>\n";
		creature = true;
	}

	output += "  <STRUCT>\n";


    output += "    <STRUCT Name=\"mp\">\n";
	output += "      <ATOM Name=\"MpColor\" Value=\"";
	
	// mission materials always Beige
	if ( craftStats.Craft.empty() )
	{
		output += "Beige\"/>\n";
		if(craftStats.UsedAsCraftRequirement)
			output += "      <ATOM Name=\"UsedAsCraftRequirement\" Value=\"true\"/>\n";
	}
	else
	{
		// get the color
		GetItemColor( craftStats.color, eco, level, color );
		output += color;
		output += "\"/>\n";

		currentDocItem.push( DtColor, color );

		// add craft data
		output += "      <STRUCT Name=\"MpParam\">\n";
		for ( uint i=0; i<craftStats.Craft.size(); i++ )
		{
			int bestStat, worstStat1, worstStat2;

			if ( i == 0 )
			{
				bestStat = craftStats.bestStatA;
				worstStat1 = craftStats.worstStatA1;
				worstStat2 = craftStats.worstStatA2;
			}
			else if ( i == 1 )
			{
				bestStat = craftStats.bestStatB;
				worstStat1 = craftStats.worstStatB1;
				worstStat2 = craftStats.worstStatB2;
			}
			else
			{
				bestStat = -1;
				worstStat1 = -1;
				worstStat2 = -1;
			}

			currentDocItem.push( DtProp, toString( "%c", craftStats.Craft.c_str()[i] ).c_str() );
			
			FillCraftData( craftStats.Craft.c_str()[i], eco, level, creature, bestStat, 
					   worstStat1, worstStat2, craftInfo );
			output += craftInfo;
		}

		output += "      </STRUCT>\n";
	}

	output += "      <ATOM Name=\"MaxQuality\" Value=\"";
	if ( creature || specialItem || ( code == "cxx" ) )
	{
		CSString maxQuality = toString( "%d", 250 ); 
		output += maxQuality;
		currentDocItem.push( DtMaxLevel, maxQuality );
	}
	else
	{
		CSString maxQuality = toString( "%d", GetMaxQuality( level ) );
		output += maxQuality;
		currentDocItem.push( DtMaxLevel, maxQuality );
	}

	output += "\"/>\n      <ATOM Name=\"StatEnergy\" Value=\"";

	CSString statEnergy;
	if ( ( variation == 2 ) && ( numMP == 695 ) ) // cas particulier pour le kitin trophy (beurk)
		statEnergy = "0";
	else if ( !creature || ( craftStats.Craft.empty() ) )
		statEnergy = toString( "%d", GetStatEnergy( level ) );
	else if ( variation < 2 )
		statEnergy = toString( "%d", GetStatEnergy( level + 1 ) );
		
	output += statEnergy;
	currentDocItem.push( DtAverageEnergy, statEnergy );

	output += "\"/>\n    </STRUCT>\n  </STRUCT>\n  <STRUCT/>\n  <STRUCT/>\n  <STRUCT/>\n  <STRUCT/>\n</FORM>\n";

	output.writeToFile( toString( "%s%s\\%s", MP_DIRECTORY.c_str(), directory.c_str(), outputFileName.c_str() ) );

	// Generate names
	if ( !specialItem )
	{
		outputFileName = toString( "m%04d%s%c%c%02d", numMP, code.c_str(), eco, 'a' + level, variation );
		output = outputFileName;

		GenerateItemNames( nomMP, eco, level, ( craftStats.Craft.empty() ), creature, itemName );
		output += "\t" + itemName;
		itemNames.insert( output );
	}

	currentDocItem.push( DtEcosystem, ecoStr );
	currentDocItem.push( DtName, outputFileName );

	SortableData.updateItemAppend( currentDocItem, DtName );
	currentDocItem.reset( DtName );
	currentDocItem.reset( DtEcosystem );
	currentDocItem.reset( DtMaxLevel );
	currentDocItem.reset( DtAverageEnergy );
	currentDocItem.reset( DtTitle );
	currentDocItem.reset( DtLevelZone );
	currentDocItem.reset( DtStatQuality );
	currentDocItem.reset( DtCraftSlotName );
	currentDocItem.reset( DtCraftCivSpec );
	currentDocItem.reset( DtColor );
}


// Generate deposits items for harvested raw mats
void GenerateDepositItems( int numMP, const CSString& nomMP, const MPCraftStats& craftStats, const CSString& loc )
{
	CSString code;

	if ( loc.left(1) == "I" )
		code = "ixx";
	else if ( loc.left(1) == "D" )
		code = "dxa";
	else
		code = "cxx";

	// pas de craft = items de mission
	if ( craftStats.Craft.empty() )
	{
		if ( loc != "G" )
			CreateSheet( numMP, nomMP, code, 'c', 0, craftStats );
		
		for ( int i=1; i<6; i++ )
		{
			CreateSheet( numMP, nomMP, code, 'c', i, craftStats );
		}
	}
	else
	{
		// 2 items in common
		CreateSheet( numMP, nomMP, code, 'c', 1, craftStats );
		CreateSheet( numMP, nomMP, code, 'c', 2, craftStats );

		// 3 items per zone
		for ( int i=0; i<3; i++ )
		{
			CreateSheet( numMP, nomMP, code, 'd', 3+i, craftStats );
			CreateSheet( numMP, nomMP, code, 'f', 3+i, craftStats );
			CreateSheet( numMP, nomMP, code, 'j', 3+i, craftStats );
			CreateSheet( numMP, nomMP, code, 'l', 3+i, craftStats );
			CreateSheet( numMP, nomMP, code, 'p', 3+i, craftStats );
		}
	}
}


// Generate creatures items for a looted raw mat
void GenerateCreatureItems( int numMP, CSString& nomMP, const MPCraftStats& craftStats )
{
	map<CSString, ListeCreatureMP>::const_iterator itLCMP;
	int quality;
	static int statQuality[] = { 0, 1, 0, 1, 3, 6, 4, 2 };

	// Get items levels to generate for the creature
	itLCMP = itemsAGenerer.find( nomMP.firstWord() );

	if ( itLCMP != itemsAGenerer.end() )
	{
		ListeCreatureMP::const_iterator itMP = (*itLCMP).second.begin();

		// for each level of an item to generate
		while ( itMP != (*itLCMP).second.end() )
		{
			// save its stats
			char eco = (*itMP)->eco;
			int creatureLevel = (*itMP)->creatureLevel;
			int itemLevel = (*itMP)->itemLevel;
			CSString creatureFileName = "c";
			creatureFileName += (*itMP)->codeCreature.toLower();
			
			if ( !craftStats.Craft.empty() )
			{
				quality = statQuality[creatureLevel-1];
				if ( quality != 6 )
				{						
					if ( quality < 2 )
					{
						CreateSheet( numMP, nomMP, creatureFileName, 'c', quality, craftStats );

						AssignerMP( (*itMP)->creatureFileName, 
							toString( "m%04d%s%c%c01.sitem", numMP, creatureFileName.c_str(), 'c', 'a' + quality ) );
					}
					else
					{
						CreateSheet( numMP, nomMP, creatureFileName, eco, quality, craftStats );

						AssignerMP( (*itMP)->creatureFileName, 
							toString( "m%04d%s%c%c01.sitem", numMP, creatureFileName.c_str(), eco, 'a' + quality ) );
					}

					currentDocItem.push( DtCreature, (*itMP)->creatureFileName );
				}
			}
			else
			{
				// pas de MP de mission pour les boss
				if ( creatureLevel < 5 )
				{
					CreateSheet( numMP, nomMP, creatureFileName, eco, itemLevel, craftStats ); 

					AssignerMP( (*itMP)->creatureFileName, 
							toString( "m%04d%s%c%c01.sitem", numMP, creatureFileName.c_str(), eco, 'a' + itemLevel ) );

					currentDocItem.push( DtCreature, (*itMP)->creatureFileName );
				}
			}

			itMP++;
		}
	}
}


// Generate special item
void GenerateSpecialItem( int numMP, const CSString& nomMP, const MPCraftStats& craftStats, 
						  const CSString& loc,CSString& itemData, int variation )
{
	CSString info, code, name;
	info = itemData.splitTo( "/", true ).toLower();

	if ( loc.left(1) == "I" )
		code = "ixx";
	else if ( loc.left(1) == "D" )
		code = "dxa";
	else
		code = "cxx";

	CreateSheet( numMP, nomMP, code, info.c_str()[0], info.c_str()[1]-'a', craftStats, true, variation );

	name = toString( "m%04d%s%s%02d\t", numMP, code.c_str(), info.c_str(), variation ); 
	name += itemData.splitTo( "/", true ); // singular
	name += "\t";
	name += itemData.splitTo( "/", true ); // undefined article
	name += "\t";
	name += itemData.splitTo( "/", true ); // defined article
	name += "\t";
	name += itemData.splitTo( "/", true ); // plural
	name += "\t\t";
	name += itemData.splitTo( "/", true ); // plural article

	itemNames.insert( name );
}


// Special Attrib parsing. craftStats must be good for extraInfo init
void	parseSpecialAttributes(const CSString &specialAttributes, MPCraftStats &craftStats, CExtraInfo &extraInfo)
{
	// evaluate DropOrSell according to CraftStats: can DropOrSell if it is a MP Craft
	extraInfo.DropOrSell= craftStats.Craft != "";

	// parse attributes
	vector<string> strArray;
	splitString(specialAttributes, "-", strArray);
	for(uint i=0;i<strArray.size();i++)
	{
		if(nlstricmp(strArray[i], "R")==0)
			craftStats.UsedAsCraftRequirement= true;
		if(nlstricmp(strArray[i], "D0")==0)
			extraInfo.DropOrSell= false;
		if(nlstricmp(strArray[i], "D1")==0)
			extraInfo.DropOrSell= true;
	}
}


// New raw mat to process
void NewMP( CSString& ligne )
{
	CSString nomMP, groupe, loc, icon, overlay, special, stat, specialAttributes;
	MPCraftStats craftStats;
	CExtraInfo	extraInfo;
	int numMP;
	bool specialOnly = false;
	CSortedStringSet specialNames;

	// nouveau nom de famille
	nomMP = ligne.splitTo( ";", true );
	if ( nomMP.empty() )
	{
		// cette ligne ne contient pas d'info
		return;
	}
	
	// get information
	groupe = ligne.splitTo( ";", true );
	craftStats.Craft = ligne.splitTo( ";", true );
	specialAttributes= ligne.splitTo( ";" , true );
	parseSpecialAttributes(specialAttributes, craftStats, extraInfo);
	ligne.splitTo( ";" , true );
	loc = ligne.splitTo( ";" , true );
	icon = ligne.splitTo( ";", true );
	ligne.splitTo( ";", true );
	ligne.splitTo( ";", true );
	ligne.splitTo( ";", true );

	stat = ligne.splitTo( ";", true );
	if ( !stat.firstWord().empty() )
		craftStats.bestStatA = stat.atoi();
	else
		craftStats.bestStatA = -1;

	stat = ligne.splitTo( ";", true );
	if ( !stat.firstWord().empty() )
		craftStats.worstStatA1 = stat.atoi();
	else
		craftStats.worstStatA1 = -1;

	stat = ligne.splitTo( ";", true );
	if ( !stat.firstWord().empty() )
		craftStats.worstStatA2 = stat.atoi();
	else
		craftStats.worstStatA2 = -1;

	stat = ligne.splitTo( ";", true );
	if ( !stat.firstWord().empty() )
		craftStats.bestStatB = stat.atoi();
	else
		craftStats.bestStatB = -1;

	stat = ligne.splitTo( ";", true );
	if ( !stat.firstWord().empty() )
		craftStats.worstStatB1 = stat.atoi();
	else
		craftStats.worstStatB1 = -1;

	stat = ligne.splitTo( ";", true );
	if ( !stat.firstWord().empty() )
		craftStats.worstStatB2 = stat.atoi();
	else
		craftStats.worstStatB2 = -1;

	stat = ligne.splitTo( ";", true );
	craftStats.color = stat.firstWord().atoi();

	stat = ligne.splitTo( ";", true );
	specialOnly = stat.firstWord().contains( "x" );
	
	// cas particuliers
	while ( !ligne.empty() )
	{
		if ( !ligne.contains( ";" ) ) 
		{
			special = ligne;
			if ( !special.firstWord().empty() )
				specialNames.insert( special );
			ligne = "";
		}
		else
		{
			special = ligne.splitTo( ";", true );
			if ( !special.empty() )
				specialNames.insert( special );
		}
	}


	currentDocItem.push( DtRMFamily, nomMP );
	currentDocItem.push( DtGroup, groupe );
	
	// get raw mat number
	numMP = GetNumeroMP( nomMP );
	printf( "    Processing Family %d : %s\n", numMP, nomMP.c_str() );

	GetNumeroGroupe( groupe );


	// Add the MPFamily into the list
	if(numMP>=(sint)MPFamilies.size())
		MPFamilies.resize(numMP+1);
	MPFamilies[numMP].Name= nomMP;
	MPFamilies[numMP].Icon= icon;

	
	// raw mats found in deposits or goo
	if ( loc.left(1) != "C" )
	{
		if ( !specialOnly )
		{
			// Generate items
			GenerateDepositItems( numMP, nomMP, craftStats, loc );
		}
		
		// on enregistre les items se trouvant dans les deposits
		if ( loc.left(1) == "D" )
		{
			CSString output;
			output.writeToFile( toString( "%s%s_%d.mp", DEPOSIT_MPS.c_str(), nomMP.toLower().replace( " ", "_" ).c_str(), numMP ) );
		}

		overlay = nomMP.firstWord().toUpper().left(6);
	}
	// looted raw mats
	else
	{
		GenerateCreatureItems( numMP, nomMP, craftStats );
	}


	// special items
	CSString codeSpecial, nouveauCode;
	int variation = 1;
	CSortedStringSet::const_iterator it = specialNames.begin();

	while ( it != specialNames.end() )
	{
		CSString name = (*it);
		
		nouveauCode = name.left(2).toLower();

		if ( nouveauCode == codeSpecial )
			variation++;
		else
			variation = 1;

		GenerateSpecialItem( numMP, nomMP, craftStats, loc, name, variation );
		codeSpecial = nouveauCode;
		it++;
	}
		
		
	// Create parent sheet for raw mat
	CreateParentSItem( numMP, nomMP, groupe, extraInfo.DropOrSell, icon, overlay );

	currentDocItem.reset( DtRMFamily );
	currentDocItem.reset( DtGroup );
	currentDocItem.reset( DtProp );
	currentDocItem.reset( DtCreature );
}


// Generate Primitive Necklace (special object)
void CreatePrimitiveNecklace()
{
	CSString output;
	
	output = "<?xml version=\"1.0\"?>\n";
	output += "<FORM Version=\"0.0\" State=\"modified\">\n";
	output += "  <PARENT Filename=\"_mc.sitem\"/>\n";
	output += "  <PARENT Filename=\"_m0696.sitem\"/>\n";
	output += "  <STRUCT>\n    <STRUCT Name=\"mp\">\n";
	output += "      <ATOM Name=\"MpColor\" Value=\"Beige\"/>\n";
	output += "      <ATOM Name=\"MaxQuality\" Value=\"250\"/>\n";
	output += "      <ATOM Name=\"StatEnergy\" Value=\"0\"/>\n";
	output += "    </STRUCT>\n  </STRUCT>\n  <STRUCT/>\n  <STRUCT/>\n";
	output += "  <STRUCT/>\n  <STRUCT/>\n</FORM>\n";
	output.writeToFile( toString( "%scommon\\m0696ixxcc01.sitem", MP_DIRECTORY.c_str() ) );
	
	itemNames.insert( "m0696ixxcc01	Primitive Necklace	a	the	Primitive Necklaces		the" );

}


// Save names
void ItemNamesSave()
{
	printf( "-- SAVING ITEM NAMES --\n");
	CSString data, output;

	FILE* file;
	file = fopen( ITEM_WORDS_WK.c_str(), "rb" );

	char c;
	fread( &c, 1, 1, file );
	while ( !feof( file ) )
	{
		data += toString( "%c", c );
		fread( &c, 1, 1, file );
	}

	fclose( file );

	data.splitTo( "i", true );
	output = "i";
	output += data.splitTo( "prospector", true );

	CSortedStringSet::const_iterator it = itemNames.begin();

	while ( it != itemNames.end() )
	{
		if ( !output.contains( (*it).left(5).c_str() ) )
		{
			output += (*it);
			output += "\r\n";
		}
		it++;
	}

	output += "p";

	output += data;

	output.writeToFile( ITEM_WORDS_WK.c_str() );
}


// Load Customized Properties defined in raw_material_generation.cfg
void LoadCustomizedProperties()
{
	CSString data, name, prop, val;
	
	printf( "-- REGISTERING CUSTOMIZED PROPERTIES --\n" );
	
	data.readFromFile( "raw_material_generation.cfg" );
	data = data.splitFrom( "{\r\n\t" );
	CPath::addSearchPath( MP_DIRECTORY, true, false );

	while ( data.contains( "};" ) )
	{
		name = data.splitTo( ",", true ).replace( "\"", "" );
		prop = data.splitTo( ",", true ).replace( "\"", "" ).replace( " ", "" );

		val = data.splitTo( ",", true ).replace( "\"", "" ).replace( " ", "" );
		if ( val.contains( "\r\n" ) )
			val = val.splitTo( "\r\n", true );

		TRMItem item;
		item.push( DtName, name );
		item.push( DtCustomizedProperties, prop );
		SortableData.updateItemAppend( item, DtName );
		
		data.splitTo( "\t", true );

		CSString fileName, str, output;
		fileName = CPath::lookup( name, false, false, true );
		
		// check if file exists
		if ( !fileName.empty() )
		{
			CSString zone = prop.splitTo( ".", true );
			str.readFromFile( fileName );

			if ( !str.contains( zone.c_str() ) )
			{
				output = "<STRUCT>\n    <STRUCT Name=\"";
				output += toString( "%s\">\n      <ATOM Name=\"", zone.c_str() );
				output += toString( "%s\" Value=\"%s\"/>\n    </STRUCT>", prop.c_str(), val.c_str() );

				// check if property is not already inserted
				if ( !str.contains( output.c_str() ) )
				{
					str = str.replace( "<STRUCT>", output.c_str() );
					str.writeToFile( fileName );
				}
			}
			else
			{
				output = toString( "    <STRUCT Name=\"%s\">\n", zone.c_str() );
				output += toString( "      <ATOM Name=\"" );
				output += toString( "%s\" Value=\"%s\"/>\n", prop.c_str(), val.c_str() );

				// check if property is not already inserted
				if ( !str.contains( toString( "%s\" Value=\"%s\"/>\n", prop.c_str(), val.c_str() ).c_str() ) )
				{
					str = str.replace( toString( "    <STRUCT Name=\"%s\">\n", zone.c_str() ).c_str(), output.c_str() );
					str.writeToFile( fileName );
				}
			}
		}
	}
}

// Generate _ic_families.forage_source()
void	SaveFamiliesForageSource()
{
	CSString output;

	printf( "-- GROUP ICONS FOR _ic_groups.forage_source --\n");
	
	output = "<?xml version=\"1.0\"?>\n";
	output+= "<FORM Revision=\"$Revision: 1.9 $\" State=\"modified\">\n";
	output+= "    <STRUCT>\n";
	output+= "    <ARRAY Name=\"Icons\">\n";

	for ( uint i=0; i!=MPFamilies.size(); ++i )
	{
		output+= toString("      <ATOM Value=\"%s\"/>\n", MPFamilies[i].Icon.c_str());
	}

    output+= "    </ARRAY>\n";
	output+= "  </STRUCT>\n";
	output+= "  <STRUCT/>\n";
	output+= "  <STRUCT/>\n";
	output+= "  <STRUCT/>\n";
	output+= "  <STRUCT/>\n";
	output+= "  <LOG></LOG>\n";
	output+= "</FORM>\n";

	output.writeToFile( IC_FAMILIES_FORAGE_SOURCE.c_str() );
}


// Generate documentation
void GenerateDoc()
{
	CProducedDocHtml			MainDoc;
	CFile::createDirectory("doc");
	MainDoc.open( "doc\\rm.html", "Raw materials by generation order", true );
	MainDoc.write( "<table cellpadding=\"1\" cellspacing=\"1\" border=\"0\"><tbody>\n" );
	MainDoc.write( "<tr>" );
	for ( uint32 c=0; c!=DtNbCols; ++c )
	{
		MainDoc.write( "<td><b><a href=\"rm_" + string(DataColStr[c]) + ".html\">" + string(DataColStr[c]) + "</a></b></td>" );
	}
	MainDoc.write( "</tr>" );
	for ( CRMData::CItems::const_iterator isd=SortableData.items().begin(); isd!=SortableData.items().end(); ++isd )
	{
		MainDoc.write( (*isd).toHTMLRow() );
	}
	MainDoc.write( "</tbody><table>\n" );
	
	// Produce alt docs
	CProducedDocHtml			AltDocs[DtNbCols];
	for ( uint32 c=0; c!=DtNbCols; ++c )
	{
		AltDocs[c].open( "doc\\rm_" + string(DataColStr[c]) + ".html", "Raw materials by " + string(DataColStr[c]), true );
		AltDocs[c].write( "<table cellpadding=\"1\" cellspacing=\"1\" border=\"0\"><tbody>\n" );
		AltDocs[c].write( "<tr>" );
		for ( uint32 cc=0; cc!=DtNbCols; ++cc )
			if ( cc == c )
				AltDocs[c].write( "<td><b>" + string(DataColStr[cc]) + "</b></td>" );
			else
				AltDocs[c].write( "<td><b><a href=\"rm_" + string(DataColStr[cc]) + ".html\">" + string(DataColStr[cc]) + "</a></b></td>" );
		AltDocs[c].write( "</tr>" );
		string previousKey = "[NO PREVIOUS]"; // not a blank string, because it may be a valid value
		string previousName;
		for ( CRMData::CLookup::const_iterator isd=SortableData.lookup( c ).begin(); isd!=SortableData.lookup( c ).end(); ++isd )
		{
			const TRMItem& item = SortableData.getRow( (*isd).second );
			AltDocs[c].write( item.toHTMLRow( c, (*isd).first, previousKey, DtName, previousName ) );

			previousKey = (*isd).first;
			previousName = item.Fields[DtName][0];
		}
		AltDocs[c].write( "</tbody><table>\n" );
		AltDocs[c].save();
	}
}


// Initialize directories from raw_material_generation.cfg
void SetupDirectories()
{
	CSString data;
	
	if ( ! CFile::fileExists( "raw_material_generation.cfg" ) )
	{
		nlError( "raw_material_generation.cfg not found\n");
		exit( 1 );
	}
		
	data.readFromFile( "raw_material_generation.cfg" );

	LEVEL_DESIGN_PATH = data.splitFrom( "LevelDesignPath = \"").splitTo( "\"" );
	TRANSLATION_PATH = data.splitFrom( "TranslationPath = \"" ).splitTo( "\"" );

	printf( "Level Design Path : %s\nTranslation Path : %s\n\n", LEVEL_DESIGN_PATH.c_str(), TRANSLATION_PATH.c_str() );
	
	ITEM_MP_FAMILY_TYP = LEVEL_DESIGN_PATH + "leveldesign\\DFN\\game_elem\\_item\\item_mp_family.typ";
	ITEM_MP_GROUPE_TYP = LEVEL_DESIGN_PATH + "leveldesign\\DFN\\game_elem\\_item\\item_mp_group.typ";
	ITEM_MP_PARAM_DFN = LEVEL_DESIGN_PATH + "leveldesign\\DFN\\game_elem\\_item\\_item_mp_param.dfn";
	MP_DIRECTORY = LEVEL_DESIGN_PATH + "leveldesign\\game_element\\sitem\\raw_material\\";
	DEPOSIT_MPS = LEVEL_DESIGN_PATH + "leveldesign\\game_element\\deposit_system\\mps\\"; 

	RAW_MATERIAL_ASSIGN = LEVEL_DESIGN_PATH + "leveldesign\\Game_elem\\Creature\\raw_material_assignment\\";

	IC_FAMILIES_FORAGE_SOURCE = LEVEL_DESIGN_PATH + "leveldesign\\game_element\\forage_source\\_ic_families.forage_source";
	
	WK_UXT = TRANSLATION_PATH + "work\\wk.uxt";
	ITEM_WORDS_WK = TRANSLATION_PATH + "work\\item_words_wk.txt";
}


// Browse all raw mats families to do all related processes
void LoadFamillesMP()
{
	printf( "-- LOADING RAW MATERIAL FAMILIES --\n" );

	// Preload wk.uxt, item_mp_family.typ, and item_mp_group.typ (avoid to reload them each time)
	FamilyTypContent.readFromFile( ITEM_MP_FAMILY_TYP );
	GroupTypContent.readFromFile( ITEM_MP_GROUPE_TYP );
	WKContent.readFromFile( WK_UXT );
	
	// avoid huge resize of vector<string>
	MPFamilies.reserve(1000);

	CSString fileData, ligne;

	if ( ! CFile::fileExists( "rm_fam_prop.csv" ) )
	{
		nlError( "rm_fam_prop.csv not found\n");
		exit( 1 );
	}


	fileData.readFromFile( "rm_fam_prop.csv" );

	ligne = fileData.splitTo( "\n", true );

	while ( !ligne.empty() )
	{
		NewMP( ligne );	
		ligne = fileData.splitTo( "\n", true );
	} 

	// we manually add Primitive Necklace because it's not specified in raw mats list
	CreatePrimitiveNecklace();
}


// Programme principal
int main( int argc, char* argv[] )
{
	new CApplicationContext;
	SortableData.init( true );
	
	SetupDirectories();

	LoadCraftParts();

	LoadCreatureFiles();

	InitCreatureMP();

	LoadFamillesMP();
	
	ItemNamesSave();

	LoadCustomizedProperties();

	SaveFamiliesForageSource();
	
	printf( "-- GENERATING DOCUMENTATION --\n" );
	try
	{
		GenerateDoc();
	}
	catch(const Exception &e)
	{
		nlwarning(e.what());
		nlwarning("HTML Doc generation failed\n");
	}

	printf( "-- DONE --\n" );
	
	return 0;
}