Beispiel #1
0
// Bool version
bool	getVarFromConfigFile(CConfigFile &cf, const string &name, bool &variable, bool defaultValue = false)
{
	CConfigFile::CVar	*ptr = cf.getVarPtr(name);
	bool	success;
	variable = ((success = (ptr != NULL)) ? (ptr->asInt() != 0) : defaultValue);
	return success;
}
Beispiel #2
0
// String version
bool	getVarFromConfigFile(CConfigFile &cf, const string &name, string &variable, const string &defaultValue = string(""))
{
	CConfigFile::CVar	*ptr = cf.getVarPtr(name);
	bool	success;
	variable = ((success = (ptr != NULL)) ? ptr->asString() : defaultValue);
	return success;
}
Beispiel #3
0
// Float version
bool	getVarFromConfigFile(CConfigFile &cf, const string &name, float &variable, float defaultValue = 0.0f)
{
	CConfigFile::CVar	*ptr = cf.getVarPtr(name);
	bool	success;
	variable = ((success = (ptr != NULL)) ? ptr->asFloat() : defaultValue);
	return success;
}
/** Generate list of spell
  */
static void generateSpellList(CConfigFile &cf, const std::string &sheetName)
{
	CConfigFile::CVar *spellList = cf.getVarPtr("spell_list");
	if (!spellList)
	{
		nlwarning("Can't read spell list");
		return;
	}	
	COFile f;
	if (!f.open(sheetName, false, true))
	{
		nlwarning("Can't write %s", sheetName.c_str());
		return;
	}
	try
	{	
		COXml xmlStreamOut;
		xmlStreamOut.init(&f);
		xmlStreamOut.xmlPush("FORM");
		IStream &xmlStream = xmlStreamOut;
		xmlStream.xmlPush("STRUCT");
		xmlStream.xmlPushBegin("ARRAY");
			xmlStream.xmlSetAttrib("Name");
			std::string name = "List";
			xmlStream.serial(name);					
		xmlStream.xmlPushEnd();				
		for(uint k = 0; k < (uint) spellList->size(); ++k)
		{
			std::vector<std::string> result;
			NLMISC::splitString(spellList->asString(k), "|", result);
			if (result.size()  < 2)
			{
				nlwarning("Should provide at list spell name and id");
			}
			xmlStream.xmlPush("STRUCT");				
				writeAtom(xmlStream, "ID", result[1]);
				writeAtom(xmlStream, "SheetBaseName", result[0]);
			xmlStream.xmlPop();
		}
		xmlStream.xmlPop(); // STRUCT
		xmlStream.xmlPop(); // FORM
	}
	catch(const EStream &)
	{
		nlwarning("Cant write %s", sheetName.c_str());
	}
}
//****************************************************************************************************************************
int main(int argc, char* argv[])
{
	if (argc < 2)
	{
		nlwarning("Usage : %s config_file_name.cfg", argv[0]);
		return -1;
	}
	CConfigFile cf;
	try
	{	
		cf.load(argv[1]);
	}
	catch(const NLMISC::EConfigFile &)
	{
		nlwarning("Error in config file %s", argv[1]);
		return -1;
	}	
	catch(...)
	{
		nlwarning("Can't read config file %s", argv[1]);
		return -1;
	}
	// output for sheets
	std::string outputPath;
	CConfigFile::CVar *outputPathVar = cf.getVarPtr("output_path");
	if (outputPathVar)
	{
		outputPath = outputPathVar->asString() + "/";
	}
	// output for 'levels' parents
	std::string levelParentsPath;
	CConfigFile::CVar *levelParentsPathVar = cf.getVarPtr("level_parents");
	if (levelParentsPathVar)
	{
		levelParentsPath = levelParentsPathVar->asString() + "/";
	}
	// output for projectile parents
	std::string projectileParentsPath;
	CConfigFile::CVar *projectileParentsPathVar = cf.getVarPtr("projectile_base");
	if (projectileParentsPathVar)
	{
		projectileParentsPath= projectileParentsPathVar->asString() + "/";
	}	
	// output for 'projectile by level and mode' parents
	std::string projectileByLevelAndModeParentsPath;
	CConfigFile::CVar *projectileByLevelAndModeParentsPathVar = cf.getVarPtr("projectile_by_level_and_mode_parents");
	if (projectileByLevelAndModeParentsPathVar)
	{
		projectileByLevelAndModeParentsPath = projectileByLevelAndModeParentsPathVar->asString() + "/";
	}
	// output for 'base spells' parents
	std::string baseSpellPath;
	CConfigFile::CVar *baseSpellPathVar = cf.getVarPtr("spell_base");
	if (baseSpellPathVar)
	{
		baseSpellPath = baseSpellPathVar->asString() + "/";
	}
	// output for 'spell by levels' parents
	std::string spellByLevelParentsPath;
	CConfigFile::CVar *spellByLevelParentsPathVar = cf.getVarPtr("spell_by_level_parents");
	if (spellByLevelParentsPathVar)
	{
		spellByLevelParentsPath = spellByLevelParentsPathVar->asString() + "/";
	}
	// output for 'final spell'
	std::string finalSpellPath;
	CConfigFile::CVar *finalSpellPathVar = cf.getVarPtr("final_spells");
	if (finalSpellPathVar)
	{
		finalSpellPath = finalSpellPathVar->asString() + "/";
	}
	


	// read number of levels
	CConfigFile::CVar *numLevelVar = cf.getVarPtr("num_levels");
	if (!numLevelVar)
	{
		nlwarning("Can't read number of spell levels");
		return -1;
	}
	uint numSpellLevels = numLevelVar->asInt();

	std::vector<CUserParams> userParams(numSpellLevels);

	// read user params set for each level
	for(uint level = 0; level < numSpellLevels; ++level)
	{
		std::string varName = toString("user_params_level%d", (int) (level + 1));
		CConfigFile::CVar *up = cf.getVarPtr(varName);
		if (!up)
		{
			nlwarning("Can't read var %s", varName.c_str());
		}
		else
		{
			for(uint k = 0; k < CUserParams::NumUserParams; ++k)
			{
				userParams[level].UserParam[k] = up->asString(k);
			}
		}		
	}

	// read types of spells (offensif, curatif ...)
	CConfigFile::CVar *spellTypesList = cf.getVarPtr("spell_types"); 
	if (!spellTypesList)
	{
		nlwarning("Can't read types of spells");
		return -1;
	}
	// read modes of spells
	CConfigFile::CVar *spellModesList = cf.getVarPtr("spell_modes");

	// read name of ps for projectiles
	std::vector<std::string> projPSNames;
	projPSNames.resize(spellTypesList->size() * spellModesList->size());
	CConfigFile::CVar *projectileNames = cf.getVarPtr("projectile_fx");
	if (projectileNames)
	{
		for(uint k = 0; k < (uint) projectileNames->size(); ++k)
		{
			// entry are expected to have the following form : 
			// "type|mode|fx_name.ps"
			std::vector<std::string> params;
			NLMISC::splitString(projectileNames->asString(k), "|", params);
			if (params.size() != 3)
			{
				nlwarning("Bad param for projectile ps name : %s", projectileNames->asString(k).c_str());
			}
			else
			{
				bool found = false;
				// find the mode				
				for (uint mode = 0; mode < (uint) spellModesList->size(); ++mode)
				{
					if (spellModesList->asString(mode) == params[1])
					{						
						for (uint type = 0; type < (uint) spellTypesList->size(); ++type)
						{
							if (spellTypesList->asString(type) == params[0])
							{
								projPSNames[type + mode * spellTypesList->size()] = params[2];
								//nlwarning("%s : found", projectileNames->asString(k).c_str());
								found = true;
								break;
							}
						}
						if (found) break;
					}
				}
				//if (!found) nlwarning("%s : not found", projectileNames->asString(k).c_str());
			}			
		}
	}

	nlinfo("Generate projectiles parent sheets...");
	// gen projectiles base sheet
	CSpellSheet baseProjectileSheet;
	baseProjectileSheet.writeSheet(outputPath + projectileParentsPath + "_projectile_base.spell");	
	// gen projectiles parent sheets
	for(uint type = 0; type < (uint) spellTypesList->size(); ++type)
	{
		for(uint mode = 0; mode < (uint) spellModesList->size(); ++mode)
		{
			std::string sheetName = "_projectile_" + spellTypesList->asString(type) + "_" + spellModesList->asString(mode) + ".spell";
			CSpellSheet ss;
			ss.Parents.push_back("_projectile_base.spell");
			// affect ps name if known
			ss.Projectile.FX[0].PSName = projPSNames[type + mode * spellTypesList->size()];
			ss.writeSheet(outputPath + projectileParentsPath + sheetName);			
		}
	}
	
	nlinfo("Generate sheets by level...");
	// generate sheets by level
	for(uint level = 0; level < numSpellLevels; ++level)
	{		
		// gen projectiles by level sheets (parent sheets)
		std::string sheetName = toString("_projectile_lvl%d.spell", (int) (level + 1));
		CSpellSheet projectileSheet;
		projectileSheet.Projectile.setUserParams(userParams[level]);
		projectileSheet.writeSheet(outputPath + levelParentsPath + sheetName);
		// gen impact level sheets
		sheetName = toString("_impact_lvl%d.spell", (int) (level + 1));
		CSpellSheet impactSheet;
		impactSheet.Impact.setUserParams(userParams[level]);
		impactSheet.writeSheet(outputPath + levelParentsPath + sheetName);
		
	}

	nlinfo("Generate projectile list (by mode, type and levels)...");
	// generate projectile list (by mode, type and levels)
	for(uint type = 0; type < (uint) spellTypesList->size(); ++type)
	{
		for(uint mode = 0; mode < (uint) spellModesList->size(); ++mode)
		{
			for(uint level = 0; level < (uint) numSpellLevels; ++level)
			{			
				CSpellSheet ss;
				ss.Parents.resize(2);
				ss.Parents[0] = toString("_projectile_lvl%d.spell", (int) (level + 1)); // inherit level
				ss.Parents[1] = "_projectile_" + spellTypesList->asString(type) + "_" + spellModesList->asString(mode) + ".spell"; // inherit mode and type
				std::string sheetName = "_projectile_" + spellTypesList->asString(type) + "_" + spellModesList->asString(mode) + toString("_lvl%d.spell", (int) (level + 1));
				ss.writeSheet(outputPath + projectileByLevelAndModeParentsPath + sheetName);			
			}
		}
	}
	//
	nlinfo("Generate spell list...");
	// read list of spells
	// the string format for spells is : "sheet_name|ps_name"
	// the name of the particle system is optionnal
	CConfigFile::CVar *spellList = cf.getVarPtr("spell_list");
	if (!spellList)
	{
		nlwarning("Can't read spell list");
		return -1;
	}
	for(uint k = 0; k < (uint) spellList->size(); ++k)
	{
		std::string spellName = spellList->asString(k);
		std::vector<std::string> result;
		NLMISC::splitString(spellName, "|", result);
		if (result.size()  < 3)
		{
			nlwarning("Should provide at least spell name, id and mode");
		}
		else
		{		
			// generate parent sheet
			CSpellSheet baseSpellSheet;
			if (result.size() > 3)
			{			
				baseSpellSheet.Impact.FX[0].PSName = result[3];
			}
			baseSpellSheet.writeSheet(outputPath + baseSpellPath + "_" + result[0] + ".spell");
			// generate child sheet
			// - by spell level
			// - by spell type (chain, bomb, spray ...)

			// save spells by level
			for(uint level = 0; level < numSpellLevels; ++level)
			{
				CSpellSheet leveledSpell;
				leveledSpell.Parents.resize(2);
				leveledSpell.Parents[0] = "_" + result[0] + ".spell";
				leveledSpell.Parents[1] = toString("_impact_lvl%d.spell", (int) (level + 1));
				leveledSpell.writeSheet(outputPath + spellByLevelParentsPath + "_" + result[0] + toString("_lvl%d.spell", (int) (level + 1)));
			}

			// save spell with good projectile and level
			for(uint level = 0; level < numSpellLevels; ++level)
			{
				for(uint mode = 0; mode < (uint) spellModesList->size(); ++mode)
				{
					CSpellSheet finalSheet;
					finalSheet.Parents.resize(2);
					finalSheet.Parents[0] = "_" + result[0] + toString("_lvl%d.spell", (int) (level + 1));
					finalSheet.Parents[1] = "_projectile_" + result[2] + "_" + spellModesList->asString(mode) + toString("_lvl%d.spell", (int) (level + 1));
					//finalSheet.writeSheet(outputPath + finalSpellPath + result[0] + toString("_lvl%d_", (int) (level + 1)) + result[2] + "_" + spellModesList->asString(mode) + ".spell");
					finalSheet.writeSheet(outputPath + finalSpellPath + result[0] + "_" + spellModesList->asString(mode) + toString("_lvl%d", (int) (level + 1)) + ".spell");
				}
			}
		}
	}
	// generate spell list with their ids
	CConfigFile::CVar *spellListFile = cf.getVarPtr("spell_list_file");
	if (spellListFile)
	{	
		generateSpellList(cf, outputPath + spellListFile->asString());
	}
	nlinfo("Done");
	return 0;
}
Beispiel #6
0
/* Load the config file and the related words files. Return false in case of failure.
 * Config file variables:
 * - WordsPath: where to find <filter>_words_<languageCode>.txt
 * - LanguageCode: language code (ex: en for English)
 * - Utf8: results are in UTF8, otherwise in ANSI string
 * - Filter: "*" for all files (default) or a name (ex: "item").
 * - AdditionalFiles/AdditionalFileColumnTitles
 */
bool CWordsDictionary::init( const string& configFileName )
{
	// Read config file
	bool cfFound = false;
	CConfigFile cf;
	try
	{
		cf.load( configFileName );
		cfFound = true;
	}
	catch ( EConfigFile& e )
	{
		nlwarning( "WD: %s", e.what() );
	}
	string wordsPath, languageCode, filter = "*";
	vector<string> additionalFiles, additionalFileColumnTitles;
	bool filterAll = true, utf8 = false;
	if ( cfFound )
	{
		CConfigFile::CVar *v = cf.getVarPtr( "WordsPath" );
		if ( v )
		{
			wordsPath = v->asString();
			/*if ( (!wordsPath.empty()) && (wordsPath[wordsPath.size()-1]!='/') )
				wordsPath += '/';*/
		}
		v = cf.getVarPtr( "LanguageCode" );
		if ( v )
			languageCode = v->asString();
		v = cf.getVarPtr( "Utf8" );
		if ( v )
			utf8 = (v->asInt() == 1);
		v = cf.getVarPtr( "Filter" );
		if ( v )
		{
			filter = v->asString();
			filterAll = (filter == "*");
		}
		v = cf.getVarPtr( "AdditionalFiles" );
		if ( v )
		{
			for ( uint i=0; i!=v->size(); ++i )
				additionalFiles.push_back( v->asString( i ) );
			v = cf.getVarPtr( "AdditionalFileColumnTitles" );
			if ( v->size() != additionalFiles.size() )
			{
				nlwarning( "AdditionalFiles and AdditionalFileColumnTitles have different size, ignoring second one" );
				additionalFileColumnTitles.resize( v->size(), DefaultColTitle );
			}
			else
			{
				for ( uint i=0; i!=v->size(); ++i )
					additionalFileColumnTitles.push_back( v->asString( i ) );
			}
		}

	}
	if ( languageCode.empty() )
		languageCode = "en";

	// Load all found words files
	const string ext = ".txt";
	vector<string> fileList;
	CPath::getPathContent( wordsPath, false, false, true, fileList );
	for ( vector<string>::const_iterator ifl=fileList.begin(); ifl!=fileList.end(); ++ifl )
	{
		const string& filename = (*ifl);
		string::size_type p = string::npos;
		bool isAdditionalFile = false;

		// Test if filename is in additional file list
		uint iAdditionalFile;
		for ( iAdditionalFile=0; iAdditionalFile!=additionalFiles.size(); ++iAdditionalFile )
		{
			if ( (p = filename.find( additionalFiles[iAdditionalFile] )) != string::npos )
			{
				isAdditionalFile = true;
				break;
			}
		}

		// Or test if filename is a words_*.txt file
		string pattern = string("_words_") + languageCode + ext;
		if ( isAdditionalFile ||
			 ((p = filename.find( pattern )) != string::npos) )
		{
			// Skip if a filter is specified and does not match the current file
			if ( (!filterAll) && (filename.find( filter+pattern ) == string::npos) )
				continue;

			// Load file
			nldebug( "WD: Loading %s", filename.c_str() );
			_FileList.push_back( filename );
			string::size_type origSize = filename.size() - ext.size();
			const string truncFilename = CFile::getFilenameWithoutExtension( filename );
			const string wordType = isAdditionalFile ? "" : truncFilename.substr( 0, p - (origSize - truncFilename.size()) );
			const string colTitle = isAdditionalFile ? additionalFileColumnTitles[iAdditionalFile] : DefaultColTitle;

			// Load Unicode Excel words file
			STRING_MANAGER::TWorksheet worksheet;
			STRING_MANAGER::loadExcelSheet( filename, worksheet );
			uint ck, cw = 0;
			if ( worksheet.findId( ck ) && worksheet.findCol( ucstring(colTitle), cw ) ) // =>
			{
				for ( std::vector<STRING_MANAGER::TWorksheet::TRow>::iterator ip = worksheet.begin(); ip!=worksheet.end(); ++ip )
				{
					if ( ip == worksheet.begin() ) // skip first row
						continue;
					STRING_MANAGER::TWorksheet::TRow& row = *ip;
					_Keys.push_back( row[ck].toString() );
					string word = utf8 ? row[cw].toUtf8() : row[cw].toString();
					_Words.push_back( word );
				}
			}
			else
				nlwarning( "WD: %s ID or %s not found in %s", wordType.c_str(), colTitle.c_str(), filename.c_str() );
		}
	}

	if ( _Keys.empty() )
	{
		if ( wordsPath.empty() )
			nlwarning( "WD: WordsPath missing in config file %s", configFileName.c_str() );
		nlwarning( "WD: %s_words_%s.txt not found", filter.c_str(), languageCode.c_str() );
		return false;
	}
	else
		return true;
}
Beispiel #7
0
//******************************************************************************************************
BOOL CDialogFlags::OnInitDialog()
{
    CDialog::OnInitDialog();

    HRSRC rsc = FindResource(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_ENTITY_ICONS_TGA), "TGA");
    if (rsc != NULL)
    {
        NLMISC::CBitmap bm;
        if (_Plugin->getPluginAccess()->buildNLBitmapFromTGARsc(rsc, AfxGetInstanceHandle(), bm))
        {
            m_EntityColorList.setIconBitmap(bm, ENTITY_ICON_SIZE);
        }
    }


    DownloadCtrl.SetRange(512, 32768);
    DetailsDistanceCtrl.SetRange(0, 100);

    for(uint k = 0; k < EntityDisplayModeCount; ++k)
    {
        loadEntityDisplayInfoToRegistry(_EntityDisplayInfo[k], ENTITY_DISPLAY_MODE_REG_ID[k]);
    }

    setCurrentEntityDisplayMode(EntityType);

    updateCtrlGrayedState();

    ((CComboBox *) GetDlgItem(IDC_DISPLAY_MODE_COMBO))->SetCurSel(0);

    ((CButton *) GetDlgItem(IDC_HP_DOWN))->SetCheck(1);
    ((CButton *) GetDlgItem(IDC_CLOSE_UP_SHOW_TYPE))->SetCheck(1);
    ((CButton *) GetDlgItem(IDC_CLOSE_UP_SHOW_MODE))->SetCheck(1);

    ((CButton *) GetDlgItem(IDC_CLOSE_UP_SHOW_HP))->SetCheck(1);

    ::CRect shardListRect;
    GetDlgItem(IDC_SHARD_LIST_PLACEMENT)->GetWindowRect(shardListRect);
    ScreenToClient(&shardListRect);
    ShardCtrl.create(WS_CHILD|WS_TABSTOP, shardListRect, this, 0, REGKEY_BASE_PATH "\\shard_list", 10);
    ShardCtrl.ShowWindow (SW_SHOW);
    CFont* font = GetFont ();
    ShardCtrl.SetFont(font);
    ShardCtrl.refreshStrings();
    //ShardCtrl.setFocus();
    if (ShardCtrl.getCount() != 0)
    {
        ShardCtrl.setCurSel(0);
    }

    CConfigFile ConfigFile;
    ConfigFile.load ("world_editor_plugin.cfg");
    CConfigFile::CVar *var = ConfigFile.getVarPtr("MOSHost");
    if (var)
    {
        ShardCtrl.pushString(var->asString().c_str());
        ShardCtrl.setCurSel(var->asString());
    }

    return TRUE;  // return TRUE unless you set the focus to a control
    // EXCEPTION: OCX Property Pages should return FALSE
}
Beispiel #8
0
/*
 * CSV -> Georges
 */
void	convertCsvFile( const string &file, bool generate, const string& sheetType )
{
	const uint			BUFFER_SIZE = 16*1024;
	char			lineBuffer[BUFFER_SIZE];
	FILE			*s;

	vector<string>	fields;
	vector<string>	args;

	if ((s = fopen(file.c_str(), "r")) == NULL)
	{
		fprintf(stderr, "Can't find file %s to convert\n", file.c_str());
		return;
	}

	loadSheetPath();

	UFormLoader *formLoader = UFormLoader::createLoader ();
	NLMISC::CSmartPtr<CForm> form;
	NLMISC::CSmartPtr<UFormDfn> formDfn;


	fgets(lineBuffer, BUFFER_SIZE, s);
	explode(std::string(lineBuffer), std::string(SEPARATOR), fields);

	vector<bool> activeFields( fields.size(), true );

	// Load DFN (generation only)
	set<CDfnField>	dfnFields;
	if ( generate )
	{
		formDfn = formLoader->loadFormDfn( (sheetType + ".dfn").c_str() );
		if ( ! formDfn )
			nlerror( "Can't find DFN for %s", sheetType.c_str() );
		fillFromDFN( formLoader, dfnFields, formDfn, "", sheetType );

		// Display missing fields and check fields against DFN
		uint i;
		for ( i=1; i!=fields.size(); ++i )
		{
			eraseCarriageReturnsAndMakeBlankNonAsciiChars( fields[i] );
			if ( fields[i].empty() )
			{
				nlinfo( "Skipping field #%u (empty)", i );
				activeFields[i] = false;
			}
			else if ( nlstricmp( fields[i], "parent" ) == 0 )
			{
				strlwr( fields[i] ); // non-const version
			}
			else
			{
				set<CDfnField>::iterator ist = dfnFields.find( CDfnField(fields[i]) );
				if ( ist == dfnFields.end() )
				{
					nlinfo( "Skipping field #%u (%s, not found in %s DFN)", i, fields[i].c_str(), sheetType.c_str() );
					activeFields[i] = false;
				}
			}
		}
		for ( i=1; i!=fields.size(); ++i )
		{
			if ( activeFields[i] )
				nlinfo( "Selected field: %s", fields[i].c_str() );
		}
	}

	string addExtension = "." + sheetType;
	uint dirmapLetterIndex = ~0;
	bool dirmapLetterBackward = false;
	vector<string> dirmapDirs;
	string dirmapSheetCode;
	bool WriteEmptyProperties = false, WriteSheetsToDisk = true;
	bool ForceInsertParents = false;

	if ( generate )
	{
		// Get the directory mapping
		try
		{
			CConfigFile dirmapcfg;
			dirmapcfg.load( sheetType + "_dirmap.cfg" );

			if ( OutputPath.empty() )
			{
				CConfigFile::CVar *path = dirmapcfg.getVarPtr( "OutputPath" );
				if ( path )
					OutputPath = path->asString();
				if ( ! OutputPath.empty() )
				{
					if ( OutputPath[OutputPath.size()-1] != '/' )
						OutputPath += '/';
					else if ( ! CFile::isDirectory( OutputPath ) )
						nlwarning( "Output path does not exist" );
				}
			}

			CConfigFile::CVar *letterIndex1 = dirmapcfg.getVarPtr( "LetterIndex" );
			if ( letterIndex1 && letterIndex1->asInt() > 0 )
			{
				dirmapLetterIndex = letterIndex1->asInt() - 1;

				CConfigFile::CVar *letterWay = dirmapcfg.getVarPtr( "LetterWay" );
				dirmapLetterBackward = (letterWay && (letterWay->asInt() == 1));
				
				CConfigFile::CVar dirs = dirmapcfg.getVar( "Directories" );
				for ( uint idm=0; idm!=dirs.size(); ++idm )
				{
					dirmapDirs.push_back( dirs.asString( idm ) );
					nlinfo( "Directory: %s", dirmapDirs.back().c_str() );
					if ( ! CFile::isExists( OutputPath + dirmapDirs.back() ) )
					{
						CFile::createDirectory( OutputPath + dirmapDirs.back() );
					}
					else
					{
						if ( ! CFile::isDirectory( OutputPath + dirmapDirs.back() ) )
						{
							nlwarning( "Already existing but not a directory!" );
						}
					}
				}

				nlinfo( "Mapping letter #%u (%s) of sheet name to directory", dirmapLetterIndex + 1, dirmapLetterBackward?"backward":"forward" );
			}
			
			CConfigFile::CVar *sheetCode = dirmapcfg.getVarPtr( "AddSheetCode" );
			if ( sheetCode )
				dirmapSheetCode = sheetCode->asString();
			nlinfo( "Sheet code: %s", dirmapSheetCode.c_str() );

			if ( ! dirmapLetterBackward )
				dirmapLetterIndex += dirmapSheetCode.size();

			CConfigFile::CVar *wep = dirmapcfg.getVarPtr( "WriteEmptyProperties" );
			if ( wep )
				WriteEmptyProperties = (wep->asInt() == 1);
			nlinfo( "Write empty properties mode: %s", WriteEmptyProperties ? "ON" : "OFF" );

			CConfigFile::CVar *wstd = dirmapcfg.getVarPtr( "WriteSheetsToDisk" );
			if ( wstd )
				WriteSheetsToDisk = (wstd->asInt() == 1);
			nlinfo( "Write sheets to disk mode: %s", WriteSheetsToDisk ? "ON" : "OFF" );

			CConfigFile::CVar *fiparents = dirmapcfg.getVarPtr( "ForceInsertParents" );
			if ( fiparents )
				ForceInsertParents = (fiparents->asInt() == 1);
			nlinfo( "Force insert parents mode: %s", ForceInsertParents ? "ON" : "OFF" );
		}
		catch ( EConfigFile& e )
		{
			nlwarning( "Problem in directory mapping: %s", e.what() );
		}
		

		nlinfo( "Using output path: %s", OutputPath.c_str() );
		nlinfo( "Press a key to generate *.%s", sheetType.c_str() );
		getchar();
		nlinfo( "Generating...." );

	}
	else
		nlinfo("Updating modifications (only modified fields are updated)");

	set<string> newSheets;
	uint nbNewSheets = 0, nbModifiedSheets = 0, nbUnchangedSheets = 0, nbWritten = 0;
	while (!feof(s))
	{
		lineBuffer[0] = '\0';
		fgets(lineBuffer, BUFFER_SIZE, s);
		explode(std::string(lineBuffer), std::string(SEPARATOR), args);

		if (args.size() < 1)
			continue;

		eraseCarriageReturnsAndMakeBlankNonAsciiChars( args[0] );
		replaceTrueAndFalseTagFromCsv(args);

		// Skip empty lines
		if ( args[0].empty() || (args[0] == string(".")+sheetType) )
			continue;

		//nldebug( "%s: %u", args[0].c_str(), args.size() );
		string	filebase = dirmapSheetCode+args[0]; /*+"."+sheetType;*/
		if (filebase.find("."+sheetType) == string::npos)
		{
			filebase += "." + sheetType;
		}
		strlwr	(filebase);
		string	filename, dirbase;
		bool	isNewSheet=true;

		// Locate existing sheet
//		map<string, string>::iterator it = inputSheetPathContent.find( CFile::getFilenameWithoutExtension( filebase ) );
		map<string, string>::iterator it = inputSheetPathContent.find( CFile::getFilename( filebase ) );
		
		if	(it == inputSheetPathContent.end())
		{
			// Not found
			if ( ! generate )
			{
				if ( ! filebase.empty() )
				{
					nlwarning( "Sheet %s not found", filebase.c_str( )); 
					continue;
				}
			}			
			else
			{
				// Load template sheet
				filename = strlwr( static_cast<const string&>(filebase) );
				form = (CForm*)formLoader->loadForm( (string("_empty.") + sheetType).c_str() );
				if (form == NULL)
				{
					nlerror( "Can't load sheet _empty.%s", sheetType.c_str() );
				}

				// Deduce directory from sheet name
				if ( dirmapLetterIndex != ~0 )
				{
					if ( dirmapLetterIndex < filebase.size() )
					{
						uint letterIndex;
						char c;
						if ( dirmapLetterBackward )
							letterIndex = filebase.size() - 1 - (CFile::getExtension( filebase ).size()+1) - dirmapLetterIndex;
						else
							letterIndex = dirmapLetterIndex;
						c = tolower( filebase[letterIndex] );
						vector<string>::const_iterator idm;
						for ( idm=dirmapDirs.begin(); idm!=dirmapDirs.end(); ++idm )
						{
							if ( (! (*idm).empty()) && (tolower((*idm)[0]) == c) )
							{
								dirbase = (*idm) + "/";
								break;
							}
						}
						if ( idm==dirmapDirs.end() )
						{
							nlinfo( "Directory mapping not found for %s (index %u)", filebase.c_str(), letterIndex );
							dirbase = ""; // put into root
						}
					}
					else
					{
						nlerror( "Can't map directory with letter #%u, greater than size of %s + code", dirmapLetterIndex, filebase.c_str() );
					}
				}

				nlinfo( "New sheet: %s", filebase.c_str() );
				++nbNewSheets;
				if ( ! newSheets.insert( filebase ).second )
					nlwarning( "Found duplicate sheet: %s", filebase.c_str() );
				isNewSheet = true;
			}
		}
		else // an existing sheet was found
		{

			// Load sheet (skip if failed)
			dirbase = "";
			filename = (*it).second; // whole path
			form = (CForm*)formLoader->loadForm( filename.c_str() );
			if (form == NULL)
			{
				nlwarning( "Can't load sheet %s", filename.c_str() );
				continue;
			}

			isNewSheet = false;
		}

		const	UFormElm	&rootForm=form->getRootNode();
		bool displayed = false;
		bool isModified = false;
		uint i;
		for (	i=1;	i<args.size		()
					&&	i<fields.size	();	++i )
		{
			const string	&var = fields[i];
			string			&val = args[i];

			eraseCarriageReturnsAndMakeBlankNonAsciiChars( val );

			// Skip column with inactive field (empty or not in DFN)
			if ( (! activeFields[i]) )
				continue;

			// Skip setting of empty cell except if required
			if ( (! WriteEmptyProperties) && val.empty() )
				continue;

			// Special case for parent sheet
			if	(var == "parent") // already case-lowered
			{
				vector<string> parentVals;
				explode( val, std::string(ARRAY_SEPARATOR), parentVals );
				if ( (parentVals.size() == 1) && (parentVals[0].empty()) )
					parentVals.clear();

				if ( (isNewSheet || ForceInsertParents) && (! parentVals.empty()) )
				{
					// This is slow. Opti: insertParent() should have an option to do it without loading the form
					//	parent have same type that this object (postulat).
					uint	nbinsertedparents=0;

					for ( uint p=0; p!=parentVals.size(); ++p )
					{						
						string	localExtension=(parentVals[p].find(addExtension)==string::npos)?addExtension:"";
						string	parentName=parentVals[p]+localExtension;

						CSmartPtr<CForm> parentForm = (CForm*)formLoader->loadForm(CFile::getFilename(parentName.c_str()).c_str());
						if ( ! parentForm )
						{
							nlwarning( "Can't load parent form %s", parentName.c_str() );
						}
						else
						{
							form->insertParent( p, parentName.c_str(), parentForm );
							isModified=true;
							displayed = true;
							nbinsertedparents++;
						}

					}
					nlinfo( "Inserted %u parent(s)", nbinsertedparents );
				}
				// NOTE: Changing the parent is not currently implemented!
				continue;
			}

			const	UFormElm	*fieldForm=NULL;
						
			if	(rootForm.getNodeByName(&fieldForm, var.c_str()))
			{
				UFormDfn	*dfnForm=const_cast<UFormElm&>(rootForm).getStructDfn();
				nlassert(dfnForm);
								
				vector<string> memberVals;
				explode( val, std::string(ARRAY_SEPARATOR), memberVals );
				uint32	memberIndex=0;
				
				while (memberIndex<memberVals.size())
				{
					const	uint	currentMemberIndex=memberIndex;
					std::string		memberVal=memberVals[memberIndex];
					memberIndex++;
					
					if	(!memberVal.empty())
					{
						if (memberVal[0] == '"')
							memberVal.erase(0, 1);
						if (memberVal.size()>0 && memberVal[memberVal.size()-1] == '"')
							memberVal.resize(memberVal.size()-1);
						
						if (memberVal == "ValueForm" ||
							memberVal == "ValueParentForm" ||
							memberVal == "ValueDefaultDfn" ||
							memberVal == "ValueDefaultType" ||
							memberVal == "ERR")
							continue;
					}


					//				nlassert(fieldDfn);
					//				virtual bool getEntryFilenameExt (uint entry, std::string &name) const = 0;
					//				virtual bool getEntryFilename (uint entry, std::string &name) const = 0;
					if (dfnForm)
					{
						string	fileName;
						string	fileNameExt;
						bool	toto=false;
						static	string	filenameTyp("filename.typ");
						string	extension;
			
						uint	fieldIndex;
						if (dfnForm->getEntryIndexByName (fieldIndex, var))	//	field exists.
						{
							dfnForm->getEntryFilename(fieldIndex,fileName);
							if (fileName==filenameTyp)
							{
								dfnForm->getEntryFilenameExt(fieldIndex,fileNameExt);
								if (	!fileNameExt.empty()
									&&	fileNameExt!="*.*")
								{
									string::size_type	index=fileNameExt.find(".");
									if (index==string::npos)	//	not found.
									{
										extension=fileNameExt;
									}
									else
									{
										extension=fileNameExt.substr(index+1);
									}
									
									if	(memberVal.find(extension)==string::npos)	// extension not found.
									{
										memberVal=NLMISC::toString("%s.%s",memberVal.c_str(),extension.c_str());
									}
									
								}
								
							}
							
						}
						
					}
					

					if	(dfnForm->isAnArrayEntryByName(var))
					{
						if	(	!isNewSheet
							&&	fieldForm!=NULL)
						{
							uint arraySize;
							const UFormElm *arrayNode = NULL;
							if (fieldForm->isArray() 
								&& fieldForm->getArraySize(arraySize) && arraySize == memberVals.size())
							{
								string	test;
								if	(	fieldForm->getArrayValue(test, currentMemberIndex)
									&&	test==memberVal	)
								{
									continue;
								}
							}
						}
						//nldebug( "%s: %s '%s'", args[0].c_str(), var.c_str(), memberVal.c_str() );
						// need to put the value at the correct index.
						const	std::string	fieldName=NLMISC::toString("%s[%d]", var.c_str(), currentMemberIndex).c_str();
						const_cast<UFormElm&>(rootForm).setValueByName(memberVal.c_str(), fieldName.c_str());
						isModified=true;
						displayed = true;
					}
					else
					{
						if	(!isNewSheet)
						{
							string	test;
							if	(	rootForm.getValueByName(test,var.c_str())
								&&	test==memberVal	)
							{
								continue;
							}
							
						}					
						//nldebug( "%s: %s '%s'", args[0].c_str(), var.c_str(), memberVal.c_str() );
						const_cast<UFormElm&>(rootForm).setValueByName(memberVal.c_str(), var.c_str());
						isModified=true;
						displayed = true;
					}
					
					if	(!isNewSheet)
					{
						isModified = true;
						if (!displayed)
							nlinfo("in %s:", filename.c_str());
						displayed = true;
						nlinfo("%s = %s", var.c_str(), memberVal.c_str());
					}

				}

			}
			else	//	field Node not found :\ (bad)
			{
			}
			
		}

		if ( ! isNewSheet )
		{
			if ( isModified )
				++nbModifiedSheets;
			else
				++nbUnchangedSheets;
		}

		// Write sheet
		if ( isNewSheet || displayed )
		{
			if ( WriteSheetsToDisk )
			{
				++nbWritten;
				string	path = isNewSheet ? OutputPath : "";
				string	ext = (filename.find( addExtension ) == string::npos) ? addExtension : "";
				string	absoluteFileName=path + dirbase + filename + ext;
				
//				nlinfo("opening: %s",  absoluteFileName.c_str() );
				COFile	output(absoluteFileName);
				if	(!output.isOpen())
				{
					nlinfo("creating path: %s",  (path + dirbase).c_str() );
					NLMISC::CFile::createDirectory(path + dirbase);
				}

//				nlinfo("opening2: %s",  absoluteFileName.c_str() );
				output.open	(absoluteFileName);
				
				if (!output.isOpen())
				{					
					nlinfo("ERROR! cannot create file path: %s",  absoluteFileName.c_str() );
				}
				else
				{
					form->write(output, true);
					output.close();
					
					if	(!CPath::exists(filename + ext))
						CPath::addSearchFile(absoluteFileName);					
				}					

			}
			clearSheet( form, &form->getRootNode() );
		}

	}
	nlinfo( "%u sheets processed (%u new, %u modified, %u unchanged - %u written)", nbNewSheets+nbModifiedSheets+nbUnchangedSheets, nbNewSheets, nbModifiedSheets, nbUnchangedSheets, nbWritten );
	UFormLoader::releaseLoader (formLoader);
}