Browse Source

- replaced loadToIt with better H3 parser.
- moved hero class to heroes.json

Ivan Savenko 13 years ago
parent
commit
cf15ca1cf0

File diff suppressed because it is too large
+ 154 - 0
config/heroes.json


+ 15 - 19
lib/CArtHandler.cpp

@@ -240,14 +240,11 @@ void CArtHandler::loadArtifacts(bool onlyTxt)
 	static std::map<char, CArtifact::EartClass> classes = 
 	  map_list_of('S',CArtifact::ART_SPECIAL)('T',CArtifact::ART_TREASURE)('N',CArtifact::ART_MINOR)('J',CArtifact::ART_MAJOR)('R',CArtifact::ART_RELIC);
 
-	auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/ARTRAITS.TXT"));
-	std::string buf((char*)textFile.first.get(), textFile.second);
-	std::string dump, pom;
-	int it=0;
-	for(int i=0; i<2; ++i)
-	{
-		loadToIt(dump,buf,it,3);
-	}
+	CLegacyConfigParser parser("DATA/ARTRAITS.TXT");
+
+	parser.endLine(); // header
+	parser.endLine();
+
 	VLC->generaltexth->artifNames.resize(GameConstants::ARTIFACTS_QUANTITY);
 	VLC->generaltexth->artifDescriptions.resize(GameConstants::ARTIFACTS_QUANTITY);
 	std::map<ui32,ui8>::iterator itr;
@@ -265,26 +262,25 @@ void CArtHandler::loadArtifacts(bool onlyTxt)
 		}
 		CArtifact &nart = *art;
 		nart.id=i;
-		loadToIt(VLC->generaltexth->artifNames[i],buf,it,4);
-		loadToIt(pom,buf,it,4);
-		nart.price=atoi(pom.c_str());
+		VLC->generaltexth->artifNames[i] = parser.readString();
+
+		nart.price= parser.readNumber();
+
 		nart.possibleSlots[ArtBearer::HERO]; //we want to generate map entry even if it will be empty
 		nart.possibleSlots[ArtBearer::CREATURE]; //we want to generate map entry even if it will be empty
 		nart.possibleSlots[ArtBearer::COMMANDER];
+
 		for(int j=0;j<slots.size();j++)
 		{
-			loadToIt(pom,buf,it,4);
-			if(pom.size() && pom[0]=='x')
+			if(parser.readString() == "x")
 				nart.possibleSlots[ArtBearer::HERO].push_back(slots[j]);
 		}
-		loadToIt(pom,buf,it,4);
-		nart.aClass = classes[pom[0]];
+		nart.aClass = classes[parser.readString()[0]];
 
 		//load description and remove quotation marks
-		std::string &desc = VLC->generaltexth->artifDescriptions[i];
-		loadToIt(desc,buf,it,3);
-		if(desc[0] == '\"' && desc[desc.size()-1] == '\"')
-			desc = desc.substr(1,desc.size()-2);
+		VLC->generaltexth->artifDescriptions[i] = parser.readString();
+
+		parser.endLine();
 
 		if(onlyTxt)
 			continue;

+ 54 - 64
lib/CBuildingHandler.cpp

@@ -2,9 +2,9 @@
 #include "CBuildingHandler.h"
 
 #include "CGeneralTextHandler.h"
-#include "../lib/Filesystem/CResourceLoader.h"
-#include "../lib/VCMI_Lib.h"
-#include "../lib/JsonNode.h"
+#include "VCMI_Lib.h"
+#include "Filesystem/CResourceLoader.h"
+#include "JsonNode.h"
 #include "GameConstants.h"
 
 /*
@@ -17,88 +17,78 @@
  *
  */
 
-static ui32 readNr(std::string &in, int &it)
+CBuilding * readBuilding(CLegacyConfigParser & parser, int townID, int buildID)
 {
-	int last=it;
-	for(;last<in.size();last++)
-		if(in[last]=='\t' || in[last]=='\n' || in[last]==' ' || in[last]=='\r' || in[last]=='\n')
-			break;
-	if(last==in.size())
-		throw std::runtime_error("Cannot read number...");
-
-	std::istringstream ss(in.substr(it,last-it));
-	it+=(1+last-it);
-	ss >> last;
-	return last;
-}
-static CBuilding * readBg(std::string &buf, int& it)
-{
-	CBuilding * nb = new CBuilding();
-	for(int res=0;res<7;res++)
-		nb->resources[res] = readNr(buf,it);
-	/*nb->refName = */readTo(buf,it,'\n');
-	//reference name is omitted, it's seems to be useless
-	return nb;
+	CBuilding * ret = new CBuilding;
+	ret->tid = townID;
+	ret->bid = buildID;
+	for (size_t i=0; i< ret->resources.size(); i++)
+		ret->resources[i] = parser.readNumber();
+
+	parser.endLine();
+	return ret;
 }
+
 void CBuildingHandler::loadBuildings()
 {
-	auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/BUILDING.TXT"));
-	std::string buf((char*)textFile.first.get(), textFile.second);
-
-	std::string temp;
-	int it=0; //buf iterator
+	CLegacyConfigParser parser("DATA/BUILDING.TXT");
+	buildings.resize(GameConstants::F_NUMBER);
 
-	temp = readTo(buf,it,'\n');temp = readTo(buf,it,'\n');//read 2 lines of file info
+	parser.endLine(); // header
+	parser.endLine();
 
-	//read 9 special buildings for every faction
-	buildings.resize(GameConstants::F_NUMBER);
-	for(int i=0;i<GameConstants::F_NUMBER;i++)
+	//Unique buildings
+	for (size_t town=0; town<GameConstants::F_NUMBER; town++)
 	{
-		temp = readTo(buf,it,'\n');//read blank line and faction name
-		temp = readTo(buf,it,'\n');
-		for(int bg = 0; bg<9; bg++)
+		parser.endLine(); //header
+		parser.endLine();
+
+		int buildID = 17;
+		do
 		{
-			CBuilding *nb = readBg(buf,it);
-			nb->tid = i;
-			nb->bid = bg+17;
-			buildings[i][bg+17] = nb;
+			buildings[town][buildID] = readBuilding(parser, town, buildID);
+			buildID++;
 		}
+		while (!parser.isNextEntryEmpty());
 	}
 
-	//reading 17 neutral (common) buildings
-	temp = readTo(buf,it,'\n');temp = readTo(buf,it,'\n');temp = readTo(buf,it,'\n');//neutral buildings - skip 3 lines
-	for(int bg = 0; bg<17; bg++)
+	// Common buildings
+	parser.endLine(); // header
+	parser.endLine();
+	parser.endLine();
+
+	int buildID = 0;
+	do
 	{
-		CBuilding *nb = readBg(buf,it);
-		for(int f=0;f<GameConstants::F_NUMBER;f++)
+		buildings[0][buildID] = readBuilding(parser, 0, buildID);
+
+		for (size_t town=1; town<GameConstants::F_NUMBER; town++)
 		{
-			buildings[f][bg] = new CBuilding(*nb);
-			buildings[f][bg]->tid = f;
-			buildings[f][bg]->bid = bg;
+			buildings[town][buildID] = new CBuilding(*buildings[0][buildID]);
+			buildings[town][buildID]->tid = town;
 		}
-		delete nb;
+		buildID++;
 	}
+	while (!parser.isNextEntryEmpty());
 
-	//create Grail entries
-	for(int i=0; i<GameConstants::F_NUMBER; i++)
-		buildings[i][26] = new CBuilding(i,26);
+	parser.endLine(); //header
+	parser.endLine();
 
-	//reading 14 per faction dwellings
-	temp = readTo(buf,it,'\n');temp = readTo(buf,it,'\n');//dwellings - skip 2 lines
-	for(int i=0;i<GameConstants::F_NUMBER;i++)
+	//Dwellings
+	for (size_t town=0; town<GameConstants::F_NUMBER; town++)
 	{
-		temp = readTo(buf,it,'\n');//read blank line
-		temp = readTo(buf,it,'\n');// and faction name
-		for(int bg = 0; ; bg++)
+		parser.endLine(); //header
+		parser.endLine();
+
+		int buildID = 30;
+		do
 		{
-			CBuilding *nb = readBg(buf,it);
-			nb->tid = i;
-			nb->bid = bg+30;
-			buildings[i][bg+30] = nb;
-			if (it >= buf.size() || buf[it] == '\t') //read till empty line
-				break;
+			buildings[town][buildID] = readBuilding(parser, town, buildID);
+			buildID++;
 		}
+		while (!parser.isNextEntryEmpty());
 	}
+
 	/////done reading BUILDING.TXT*****************************
 	const JsonNode config(ResourceID("config/hall.json"));
 

+ 40 - 50
lib/CCreatureHandler.cpp

@@ -1,10 +1,11 @@
 #include "StdInc.h"
 #include "CCreatureHandler.h"
 
+#include "CGeneralTextHandler.h"
 #include "Filesystem/CResourceLoader.h"
-#include "../lib/VCMI_Lib.h"
-#include "../lib/CGameState.h"
-#include "../lib/JsonNode.h"
+#include "VCMI_Lib.h"
+#include "CGameState.h"
+#include "JsonNode.h"
 #include "CHeroHandler.h"
 #include "CModHandler.h"
 
@@ -496,10 +497,8 @@ void CCreatureHandler::loadCreatures()
 
 	if (VLC->modh->modules.STACK_EXP) 	//reading default stack experience bonuses
 	{
-		auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/CREXPBON.TXT"));
-		std::string buf((char*)textFile.first.get(), textFile.second);
-		int it = 0;
-		si32 creid = -1;
+		CLegacyConfigParser parser("DATA/CREXPBON.TXT");
+
 		Bonus b; //prototype with some default properties
 		b.source = Bonus::STACK_EXPERIENCE;
 		b.duration = Bonus::PERMANENT;
@@ -508,52 +507,50 @@ void CCreatureHandler::loadCreatures()
 		b.additionalInfo = 0;
 		b.turnsRemain = 0;
 		BonusList bl;
-		std::string dump2;
 
-		loadToIt (dump2, buf, it, 3); //ignore first line
-		loadToIt (dump2, buf, it, 4); //ignore index
+		parser.endLine();
 
-		loadStackExp(b, bl, buf, it);
+		parser.readString(); //ignore index
+		loadStackExp(b, bl, parser);
 		BOOST_FOREACH(Bonus * b, bl)
 			addBonusForAllCreatures(b); //health bonus is common for all
+		parser.endLine();
 
-		loadToIt (dump2, buf, it, 3); //crop comment
 		for (i = 1; i < 7; ++i)
 		{
 			for (int j = 0; j < 4; ++j) //four modifiers common for tiers
 			{
-				loadToIt (dump2, buf, it, 4); //ignore index
+				parser.readString(); //ignore index
 				bl.clear();
-				loadStackExp(b, bl, buf, it);
+				loadStackExp(b, bl, parser);
 				BOOST_FOREACH(Bonus * b, bl)
 					addBonusForTier(i, b);
-				loadToIt (dump2, buf, it, 3); //crop comment
+				parser.endLine();
 			}
 		}
 		for (int j = 0; j < 4; ++j) //tier 7
 		{
-			loadToIt (dump2, buf, it, 4); //ignore index
+			parser.readString(); //ignore index
 			bl.clear();
-			loadStackExp(b, bl, buf, it);
+			loadStackExp(b, bl, parser);
 			BOOST_FOREACH(Bonus * b, bl)
 			{
 				addBonusForTier(7, b);
 				creaturesOfLevel[0].addNewBonus(b); //bonuses from level 7 are given to high-level creatures
 			}
-			loadToIt (dump2, buf, it, 3); //crop comment
+			parser.endLine();
 		}
 		do //parse everything that's left
 		{
-			loadToIt(creid, buf, it, 4); //get index
-			b.sid = creid; //id = this particular creature ID
-			loadStackExp(b, creatures[creid]->getBonusList(), buf, it); //add directly to CCreature Node
-			loadToIt (dump2, buf, it, 3); //crop comment
-		} while (it < buf.size());
+			b.sid = parser.readNumber(); //id = this particular creature ID
+			loadStackExp(b, creatures[b.sid]->getBonusList(), parser); //add directly to CCreature Node
+		}
+		while (parser.endLine());
 
 		//Calculate rank exp values, formula appears complicated bu no parsing needed
 		expRanks.resize(8);
 		int dif = 0;
-		it = 8000; //ignore name of this variable
+		int it = 8000; //ignore name of this variable
 		expRanks[0].push_back(it);
 		for (int j = 1; j < 10; ++j) //used for tiers 8-10, and all other probably
 		{
@@ -572,25 +569,22 @@ void CCreatureHandler::loadCreatures()
 			}
 		}
 
-		textFile = CResourceHandler::get()->loadData(ResourceID("DATA/CREXPMOD.TXT"));
-		buf = std::string((char*)textFile.first.get(), textFile.second);
-		it = 0;
-		loadToIt (dump2, buf, it, 3); //ignore first line
+		CLegacyConfigParser expBonParser("DATA/CREXPMOD.TXT");
+
+		expBonParser.endLine(); //header
 
 		maxExpPerBattle.resize(8);
-		si32 val;
 		for (i = 1; i < 8; ++i)
 		{
-			loadToIt (dump2, buf, it, 4); //index
-			loadToIt (dump2, buf, it, 4); //float multiplier -> hardcoded
-			loadToIt (dump2, buf, it, 4); //ignore upgrade mod? ->hardcoded
-			loadToIt (dump2, buf, it, 4); //already calculated
-			loadToIt (val, buf, it, 4);
-			maxExpPerBattle[i] = (ui32)val;
-			loadToIt (val, buf, it, 4); //11th level
-			val += (si32)expRanks[i].back();
-			expRanks[i].push_back((ui32)val);
-			loadToIt (dump2, buf, it, 3); //crop comment
+			expBonParser.readString(); //index
+			expBonParser.readString(); //float multiplier -> hardcoded
+			expBonParser.readString(); //ignore upgrade mod? ->hardcoded
+			expBonParser.readString(); //already calculated
+
+			maxExpPerBattle[i] = expBonParser.readNumber();
+			expRanks[i].push_back(expRanks[i].back() + expBonParser.readNumber());
+
+			expBonParser.endLine();
 		}
 		//skeleton gets exp penalty
 			creatures[56].get()->addBonus(-50, Bonus::EXP_MULTIPLIER, -1);
@@ -601,9 +595,6 @@ void CCreatureHandler::loadCreatures()
 			maxExpPerBattle[0] = maxExpPerBattle[7];
 
 	}//end of Stack Experience
-	//experiment - add 100 to attack for creatures of tier 1
-// 	Bonus *b = new Bonus(Bonus::PERMANENT, Bonus::PRIMARY_SKILL, Bonus::OTHER, +100, 0, 0);
-// 	addBonusForTier(1, b);
 
 	tlog5 << "\t\tReading config/commanders.json" << std::endl;
 	const JsonNode config3(ResourceID("config/commanders.json"));
@@ -709,12 +700,11 @@ void CCreatureHandler::loadUnitAnimInfo(CCreature & unit, std::string & src, int
 	i+=2;
 }
 
-void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, std::string & src, int & it) //help function for parsing CREXPBON.txt
+void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigParser & parser) //help function for parsing CREXPBON.txt
 {
-	std::string buf, mod;
 	bool enable = false; //some bonuses are activated with values 2 or 1
-	loadToIt(buf, src, it, 4);
-	loadToIt(mod, src, it, 4);
+	std::string buf = parser.readString();
+	std::string mod = parser.readString();
 
 	switch (buf[0])
 	{
@@ -954,10 +944,10 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, std::string & src
 	{
 		if (b.type != Bonus::REBIRTH)
 			b.val = 0; //on-off ability, no value specified
-		loadToIt (curVal, src, it, 4); // 0 level is never active
+		curVal = parser.readNumber();// 0 level is never active
 		for (int i = 1; i < 11; ++i)
 		{
-			loadToIt (curVal, src, it, 4);
+			curVal = parser.readNumber();
 			if (curVal == 1)
 			{
 				b.limiter.reset (new RankRangeLimiter(i));
@@ -968,10 +958,10 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, std::string & src
 	}
 	else
 	{
-		loadToIt (lastVal, src, it, 4); //basic value, not particularly useful but existent
+		lastVal = parser.readNumber(); //basic value, not particularly useful but existent
 		for (int i = 1; i < 11; ++i)
 		{
-			loadToIt (curVal, src, it, 4);
+			curVal = parser.readNumber();
 			if (b.type == Bonus::HATE)
 				curVal *= 10; //odd fix
 			if (curVal > lastVal) //threshold, add new bonus

+ 2 - 1
lib/CCreatureHandler.h

@@ -16,6 +16,7 @@
  *
  */
 
+class CLegacyConfigParser;
 class CCreatureHandler;
 class CCreature;
 
@@ -125,7 +126,7 @@ public:
 	void buildBonusTreeForTiers();
 	void loadAnimationInfo();
 	void loadUnitAnimInfo(CCreature & unit, std::string & src, int & i);
-	void loadStackExp(Bonus & b, BonusList & bl, std::string & src, int & it);
+	void loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigParser &parser);
 	int stringToNumber(std::string & s);//help function for parsing CREXPBON.txt
 
 	bool isGood (si8 faction) const;

+ 321 - 461
lib/CGeneralTextHandler.cpp

@@ -2,9 +2,11 @@
 #include "CGeneralTextHandler.h"
 
 #include "Filesystem/CResourceLoader.h"
-#include "VCMI_Lib.h"
+#include "Filesystem/CInputStream.h"
 #include "GameConstants.h"
 
+// #include <locale> //needed?
+
 /*
  * CGeneralTextHandler.cpp, part of VCMI engine
  *
@@ -15,561 +17,419 @@
  *
  */
 
-std::string readTo(const std::string &in, int &it, char end)
+//Helper for string -> float conversion
+class LocaleWithComma: public std::numpunct<char>
+{
+protected:
+	char do_decimal_point() const
+	{
+		return ',';
+	}
+};
+
+CLegacyConfigParser::CLegacyConfigParser(std::string URI)
 {
-	int pom = it;
-	int last = in.find_first_of(end,it);
-	it+=(1+last-it);
-	return in.substr(pom,last-pom);
+	init(CResourceHandler::get()->load(ResourceID(URI, EResType::TEXT)));
 }
 
-void trimQuotation(std::string &op)
+CLegacyConfigParser::CLegacyConfigParser(const std::unique_ptr<CInputStream> & input)
 {
-	if(op.length() && op[0] == '\"' && op[op.size()-1] == '\"')
-		op = op.substr(1,op.size()-2);
+	init(input);
 }
 
-std::string getTextFile(std::string filename)
+void CLegacyConfigParser::init(const std::unique_ptr<CInputStream> & input)
 {
-	auto file = CResourceHandler::get()->loadData(
-	               ResourceID(std::string("DATA/") + filename, EResType::TEXT));
+	data.reset(new char[input->getSize()]);
+	input->read((ui8*)data.get(), input->getSize());
 
-	return std::string((char*)file.first.get(), file.second);
+	curr = data.get();
+	end = curr + input->getSize();
 }
 
-void CGeneralTextHandler::load()
+std::string CLegacyConfigParser::extractQuotedPart()
 {
-	std::string buf1 = getTextFile("ZELP.TXT");
-	int itr=0, eol=-1, eolnext=-1, pom;
-	eolnext = buf1.find_first_of('\r',itr);
-	while(itr<buf1.size())
-	{
-		eol = eolnext; //end of this line
-		eolnext = buf1.find_first_of('\r',eol+1); //end of the next line
-		pom=buf1.find_first_of('\t',itr); //upcoming tab
-		if(eol<0 || pom<0)
-			break;
-		if(pom>eol) //in current line there is not tab
-			zelp.push_back(std::pair<std::string,std::string>());
-		else
-		{
-			zelp.push_back
-				(std::pair<std::string,std::string>
-				(buf1.substr(itr,pom-itr),
-				buf1.substr(pom+1,eol-pom-1)));
-			boost::algorithm::replace_all(zelp[zelp.size()-1].first,"\t","");
-			boost::algorithm::replace_all(zelp[zelp.size()-1].second,"\t","");
-			trimQuotation(zelp.back().second);
-		}
-		itr=eol+2;
-	}
-	std::string buf = getTextFile("VCDESC.TXT");
-	int andame = buf.size();
-	int i=0; //buf iterator
-	for(int gg=0; gg<14; ++gg)
-	{
-		int befi=i;
-		for(; i<andame; ++i)
-		{
-			if(buf[i]=='\r')
-				break;
-		}
-		victoryConditions[gg] = buf.substr(befi, i-befi);
-		i+=2;
-	}
-	buf = getTextFile("LCDESC.TXT");
-	andame = buf.size();
-	i=0; //buf iterator
-	for(int gg=0; gg<4; ++gg)
-	{
-		int befi=i;
-		for(; i<andame; ++i)
-		{
-			if(buf[i]=='\r')
-				break;
-		}
-		lossCondtions[gg] = buf.substr(befi, i-befi);
-		i+=2;
-	}
+	assert(*curr == '\"');
 
-	hTxts.resize(GameConstants::HEROES_QUANTITY);
+	curr++; // skip quote
+	char * begin = curr;
 
-	buf = getTextFile("HEROSPEC.TXT");
-	i=0;
-	std::string dump;
-	for(int iii=0; iii<2; ++iii)
-	{
-		loadToIt(dump,buf,i,3);
-	}
-	for (int iii=0;iii<hTxts.size();iii++)
-	{
-		loadToIt(hTxts[iii].bonusName,buf,i,4);
-		loadToIt(hTxts[iii].shortBonus,buf,i,4);
-		loadToIt(hTxts[iii].longBonus,buf,i,3);
-		trimQuotation(hTxts[iii].longBonus);
-	}
+	while (curr != end && *curr != '\"')
+		curr++;
 
-	buf = getTextFile("HEROBIOS.TXT");
-	i=0;
-	for (int iii=0;iii<hTxts.size();iii++)
-	{
-		loadToIt(hTxts[iii].biography,buf,i,3);
-		trimQuotation(hTxts[iii].biography);
-	}
+	return std::string(begin, curr++); //increment curr to close quote
+}
 
-	int it;
-	buf = getTextFile("BLDGNEUT.TXT");
-	andame = buf.size(), it=0;
+std::string CLegacyConfigParser::extractQuotedString()
+{
+	assert(*curr == '\"');
 
-	for(int b=0;b<15;b++)
-	{
-		std::string name = readTo(buf,it,'\t'),
-			description = readTo(buf,it,'\n');
-		for(int fi=0;fi<GameConstants::F_NUMBER;fi++)
-		{
-			buildings[fi][b].first = name;
-			buildings[fi][b].second = description;
-		}
-	}
-	buf1 = readTo(buf,it,'\n');buf1 = readTo(buf,it,'\n');buf1 = readTo(buf,it,'\n');//silo,blacksmith,moat - useless???
-	//shipyard with the ship
-	std::string name = readTo(buf,it,'\t'),
-		description = readTo(buf,it,'\n');
-	for(int fi=0;fi<GameConstants::F_NUMBER;fi++)
+	std::string ret;
+	while (true)
 	{
-		buildings[fi][20].first = name;
-		buildings[fi][20].second = description;
-	}
+		ret += extractQuotedPart();
 
-	for(int fi=0;fi<GameConstants::F_NUMBER;fi++)
-	{
-		buildings[fi][16].first = readTo(buf,it,'\t'),
-			buildings[fi][16].second = readTo(buf,it,'\n');
+		if (curr < end && *curr == '\"') //double quote - add it to string and continue
+			ret += '\"';
+		else // end of string
+			return ret;
 	}
-	/////done reading "BLDGNEUT.TXT"******************************
+}
+
+std::string CLegacyConfigParser::extractNormalString()
+{
+	char * begin = curr;
+
+	while (curr < end && *curr != '\t' && *curr != '\r')//find end of string
+		curr++;
+
+	return std::string(begin, curr);
+}
+
+std::string CLegacyConfigParser::readString()
+{
+	if (curr >= end || *curr == '\n')
+		return "";
+
+	std::string ret;
+
+	if (*curr == '\"')
+		ret = extractQuotedString();// quoted text - find closing quote
+	else
+		ret = extractNormalString();//string without quotes - copy till \t or \r
+
+	curr++;
+	return ret;
+}
+
+float CLegacyConfigParser::readNumber()
+{
+	std::string input = readString();
+
+	std::istringstream stream(input);
+
+	if (input.find(',') != std::string::npos) // code to handle conversion with comma as decimal separator
+		stream.imbue(std::locale(std::locale(), new LocaleWithComma));
+
+	int result;
+	if ( !(stream >> result) )
+		return 0;
+	return result;
+}
 
-	buf = getTextFile("BLDGSPEC.TXT");
-	andame = buf.size(), it=0;
-	for(int f=0;f<GameConstants::F_NUMBER;f++)
+bool CLegacyConfigParser::isNextEntryEmpty()
+{
+	return curr >= end || *curr == '\n' || *curr == '\r' || *curr == '\t';
+}
+
+bool CLegacyConfigParser::endLine()
+{
+	while (curr < end && *curr !=  '\n')
+		readString();
+
+	curr++;
+
+	return curr < end;
+}
+
+void readToVector(std::string sourceName, std::vector<std::string> & dest)
+{
+	CLegacyConfigParser parser(sourceName);
+	do
 	{
-		for(int b=0;b<9;b++)
-		{
-			buildings[f][17+b].first = readTo(buf,it,'\t');
-			buildings[f][17+b].second = readTo(buf,it,'\n');
-		}
-		buildings[f][26].first = readTo(buf,it,'\t');
-		buildings[f][26].second = readTo(buf,it,'\n');
-		buildings[f][15].first = readTo(buf,it,'\t'); //resource silo
-		buildings[f][15].second = readTo(buf,it,'\n');//resource silo
+		dest.push_back(parser.readString());
 	}
-	/////done reading BLDGSPEC.TXT*********************************
+	while (parser.endLine());
+}
 
-	buf = getTextFile("DWELLING.TXT");
-	andame = buf.size(), it=0;
-	for(int f=0;f<GameConstants::F_NUMBER;f++)
-	{
-		for(int b=0;b<14;b++)
+void CGeneralTextHandler::load()
+{
+	readToVector("DATA/VCDESC.TXT",   victoryConditions);
+	readToVector("DATA/LCDESC.TXT",   lossCondtions);
+	readToVector("DATA/TCOMMAND.TXT", tcommands);
+	readToVector("DATA/HALLINFO.TXT", hcommands);
+	readToVector("DATA/CASTINFO.TXT", fcommands);
+	readToVector("DATA/ADVEVENT.TXT", advobtxt);
+	readToVector("DATA/XTRAINFO.TXT", xtrainfo);
+	readToVector("DATA/RESTYPES.TXT", restypes);
+	readToVector("DATA/TERRNAME.TXT", terrainNames);
+	readToVector("DATA/RANDSIGN.TXT", randsign);
+	readToVector("DATA/ZCRGN1.TXT",   creGens);
+	readToVector("DATA/CRGEN4.TXT",   creGens4);
+	readToVector("DATA/OVERVIEW.TXT", overview);
+	readToVector("DATA/ARRAYTXT.TXT", arraytxt);
+	readToVector("DATA/PRISKILL.TXT", primarySkillNames);
+	readToVector("DATA/JKTEXT.TXT",   jktexts);
+	readToVector("DATA/TVRNINFO.TXT", tavernInfo);
+	readToVector("DATA/TURNDUR.TXT",  turnDurations);
+	readToVector("DATA/HEROSCRN.TXT", heroscrn);
+	readToVector("DATA/ARTEVENT.TXT", artifEvents);
+	readToVector("DATA/TENTCOLR.TXT", tentColors);
+	readToVector("DATA/SKILLLEV.TXT", levels);
+	readToVector("DATA/OBJNAMES.TXT", names);
+
+	{
+		CLegacyConfigParser parser("DATA/GENRLTXT.TXT");
+		parser.endLine();
+		do
 		{
-			buildings[f][30+b].first = readTo(buf,it,'\t');
-			buildings[f][30+b].second = readTo(buf,it,'\n');
+			allTexts.push_back(parser.readString());
 		}
+		while (parser.endLine());
 	}
-
-	//remove prceeding / trailing whitespaces nad quoation marks from buildings descriptions
-	for(std::map<int, std::map<int, std::pair<std::string, std::string> > >::iterator i = buildings.begin(); i != buildings.end(); i++)
 	{
-		for(std::map<int, std::pair<std::string, std::string> >::iterator j = i->second.begin(); j != i->second.end(); j++)
+		CLegacyConfigParser parser("DATA/ZELP.TXT");
+		do
 		{
-			std::string &str = j->second.second;
-			boost::algorithm::trim(str);
-			trimQuotation(str);
+			std::string first = parser.readString();
+			std::string second = parser.readString();
+			zelp.push_back(std::make_pair(first, second));
 		}
+		while (parser.endLine());
 	}
-
-	buf = getTextFile("TCOMMAND.TXT");
-	itr=0;
-	while(itr<buf.length()-1)
 	{
-		std::string tmp;
-		loadToIt(tmp, buf, itr, 3);
-		tcommands.push_back(tmp);
-	}
+		CLegacyConfigParser parser("DATA/HEROSPEC.TXT");
+		CLegacyConfigParser bioParser("DATA/HEROBIOS.TXT");
 
-	buf = getTextFile("HALLINFO.TXT");
-	itr=0;
-	while(itr<buf.length()-1)
-	{
-		std::string tmp;
-		loadToIt(tmp, buf, itr, 3);
-		hcommands.push_back(tmp);
-	}
+		//skip header
+		parser.endLine();
+		parser.endLine();
 
-	buf = getTextFile("CASTINFO.TXT");
-	itr=0;
-	while(itr<buf.length()-1)
-	{
-		std::string tmp;
-		loadToIt(tmp, buf, itr, 3);
-		fcommands.push_back(tmp);
+		do
+		{
+			HeroTexts texts;
+			texts.bonusName  = parser.readString();
+			texts.shortBonus = parser.readString();
+			texts.longBonus  = parser.readString();
+			texts.biography  = bioParser.readString();
+			hTxts.push_back(texts);
+		}
+		while (parser.endLine() && bioParser.endLine());
 	}
-
-	std::istringstream ins, namess;
-	ins.str(getTextFile("TOWNTYPE.TXT"));
-	namess.str(getTextFile("TOWNNAME.TXT"));
-	int si=0;
-	char bufname[75];
-	while (!ins.eof())
 	{
-		ins.getline(bufname,50);
-		townTypes.push_back(std::string(bufname).substr(0,strlen(bufname)-1));
-		townNames.resize(si+1);
+		CLegacyConfigParser parser("DATA/BLDGNEUT.TXT");
 
-		for (int i=0; i<GameConstants::NAMES_PER_TOWN; i++)
+		for(int i=0; i<15; i++)
 		{
-			namess.getline(bufname,50);
-			townNames[si].push_back(std::string(bufname).substr(0,strlen(bufname)-1));
+			std::string name  = parser.readString();
+			std::string descr = parser.readString();
+			parser.endLine();
+
+			for(int j=0; j<GameConstants::F_NUMBER; j++)
+			{
+				buildings[j][i].first = name;
+				buildings[j][i].second = descr;
+			}
 		}
-		si++;
-	}
+		parser.endLine(); // silo
+		parser.endLine(); // blacksmith  //unused entries
+		parser.endLine(); // moat
 
-	tlog5 << "\t\tReading OBJNAMES \n";
-	buf = getTextFile("OBJNAMES.TXT");
-	it=0; //hope that -1 will not break this
-	while (it<buf.length()-1)
-	{
-		std::string nobj;
-		loadToIt(nobj, buf, it, 3);
-		if(nobj.size() && (nobj[nobj.size()-1]==(char)10 || nobj[nobj.size()-1]==(char)13 || nobj[nobj.size()-1]==(char)9))
+		//shipyard with the ship
+		std::string name  = parser.readString();
+		std::string descr = parser.readString();
+		parser.endLine();
+
+		for(int j=0; j<GameConstants::F_NUMBER; j++)
 		{
-			nobj = nobj.substr(0, nobj.size()-1);
+			buildings[j][20].first = name;
+			buildings[j][20].second = descr;
 		}
-		names.push_back(nobj);
-	}
 
-	tlog5 << "\t\tReading ADVEVENT \n";
-	buf = getTextFile("ADVEVENT.TXT");
-	it=0;
-	std::string temp;
-	while (it<buf.length()-1)
-	{
-		loadToIt(temp,buf,it,3);
-		if (temp[0]=='\"')
+		//blacksmith
+		for(int j=0; j<GameConstants::F_NUMBER; j++)
 		{
-			temp = temp.substr(1,temp.length()-2);
+			buildings[j][16].first =  parser.readString();
+			buildings[j][16].second = parser.readString();
+			parser.endLine();
 		}
-		boost::algorithm::replace_all(temp,"\"\"","\"");
-		advobtxt.push_back(temp);
-	}
-
-	tlog5 << "\t\tReading XTRAINFO \n";
-	buf = getTextFile("XTRAINFO.TXT");
-	it=0;
-	while (it<buf.length()-1)
-	{
-		loadToIt(temp,buf,it,3);
-		xtrainfo.push_back(temp);
 	}
-
-	tlog5 << "\t\tReading MINENAME \n";
-	buf = getTextFile("MINENAME.TXT");
-	it=0;
-	while (it<buf.length()-1)
 	{
-		loadToIt(temp,buf,it,3);
-		mines.push_back(std::pair<std::string,std::string>(temp,""));
-	}
+		CLegacyConfigParser parser("DATA/BLDGSPEC.TXT");
 
-	tlog5 << "\t\tReading MINEEVNT \n";
-	buf = getTextFile("MINEEVNT.TXT");
-	it=0;
-	i=0;
-	while (it<buf.length()-1)
-	{
-		loadToIt(temp,buf,it,3);
-		temp = temp.substr(1,temp.length()-2);
-		if(i < mines.size())
-			mines[i++].second = temp;
-		else
-			tlog2 << "Warning - too much entries in MINEEVNT. Omitting this one: " << temp << std::endl;
-	}
-
-	tlog5 << "\t\tReading RESTYPES \n";
-	buf = getTextFile("RESTYPES.TXT");
-	it=0;
-	while (it<buf.length()-1)
-	{
-		loadToIt(temp,buf,it,3);
-		restypes.push_back(temp);
-	}
+		for(int town=0; town<GameConstants::F_NUMBER; town++)
+		{
+			for(int build=0; build<9; build++)
+			{
+				buildings[town][17+build].first =  parser.readString();
+				buildings[town][17+build].second = parser.readString();
+				parser.endLine();
+			}
+			buildings[town][26].first =  parser.readString(); // Grail
+			buildings[town][26].second = parser.readString();
+			parser.endLine();
 
-	tlog5 << "\t\tReading TERRNAME \n";
-	buf = getTextFile("TERRNAME.TXT");
-	it=0;
-	while (it<buf.length()-1)
-	{
-		loadToIt(temp,buf,it,3);
-		terrainNames.push_back(temp);
+			buildings[town][15].first =  parser.readString(); // Resource silo
+			buildings[town][15].second = parser.readString();
+			parser.endLine();
+		}
 	}
-
-	tlog5 << "\t\tReading RANDSIGN \n";
-	buf = getTextFile("RANDSIGN.TXT");
-	it=0;
-	while (it<buf.length()-1)
-	{
-		loadToIt(temp,buf,it,3);
-		randsign.push_back(temp);
-	}	
-
-	tlog5 << "\t\tReading ZCRGN1 \n";
-	buf = getTextFile("ZCRGN1.TXT");
-	it=0;
-	while (it<buf.length()-1)
 	{
-		loadToIt(temp,buf,it,3);
-		creGens.push_back(temp);
-	}
+		CLegacyConfigParser parser("DATA/DWELLING.TXT");
 
-	tlog5 << "\t\tReading CRGN4 \n";
-	buf = getTextFile("CRGEN4.TXT");
-	it=0;
-	while (it<buf.length()-1)
-	{
-		loadToIt(temp,buf,it,3);
-		creGens4.push_back(temp);
+		for(int town=0; town<GameConstants::F_NUMBER; town++)
+		{
+			for(int build=0; build<14; build++)
+			{
+				buildings[town][30+build].first =  parser.readString();
+				buildings[town][30+build].second = parser.readString();
+				parser.endLine();
+			}
+		}
 	}
-
-	buf = getTextFile("GENRLTXT.TXT");
-	std::string tmp;
-	andame = buf.size();
-	i=0; //buf iterator
-	for(; i<andame; ++i)
 	{
-		if(buf[i]=='\r')
-			break;
-	}
+		CLegacyConfigParser typeParser("DATA/TOWNTYPE.TXT");
+		CLegacyConfigParser nameParser("DATA/TOWNNAME.TXT");
+		do
+		{
+			townTypes.push_back(typeParser.readString());
 
-	i+=2;
-	std::string buflet;
-	for(int jj=0; jj<764; ++jj)
-	{
-		loadToIt(buflet, buf, i, 2);
-		trimQuotation(buflet);
-		boost::algorithm::replace_all(buflet,"\"\"","\"");
-		allTexts.push_back(buflet);
+			townNames.push_back(std::vector<std::string>());
+			for (int i=0; i<GameConstants::NAMES_PER_TOWN; i++)
+			{
+				townNames.back().push_back(nameParser.readString());
+				nameParser.endLine();
+			}
+		}
+		while (typeParser.endLine());
 	}
-
-	std::string  stro = getTextFile("Overview.txt");
-	itr=0;
-	while(itr<stro.length()-1)
 	{
-		loadToIt(tmp, stro, itr, 3);
-		trimQuotation(tmp);
-		overview.push_back(tmp);
-	}
+		CLegacyConfigParser nameParser("DATA/MINENAME.TXT");
+		CLegacyConfigParser eventParser("DATA/MINEEVNT.TXT");
 
-	std::string  strc = getTextFile("PLCOLORS.TXT");
-	itr=0;
-	while(itr<strc.length()-1)
-	{
-		loadToIt(tmp, strc, itr, 3);
-		colors.push_back(tmp);
-		tmp[0] = toupper(tmp[0]);
-		capColors.push_back(tmp);
+		do
+		{
+			std::string name  = nameParser.readString();
+			std::string event = eventParser.readString();
+			mines.push_back(std::make_pair(name, event));
+		}
+		while (nameParser.endLine() && eventParser.endLine());
 	}
-
-	std::string  strs = getTextFile("ARRAYTXT.TXT");
-
-	itr=0;
-	while(itr<strs.length()-1)
 	{
-		loadToIt(tmp, strs, itr, 3);
-		trimQuotation(tmp);
-		arraytxt.push_back(tmp);
-	}
+		CLegacyConfigParser parser("DATA/PLCOLORS.TXT");
+		do
+		{
+			std::string color = parser.readString();
+			colors.push_back(color);
 
-	itr = 0;
-	std::string strin = getTextFile("PRISKILL.TXT");
-	for(int hh=0; hh<4; ++hh)
-	{
-		loadToIt(tmp, strin, itr, 3);
-		primarySkillNames.push_back(tmp);
+			color[0] = toupper(color[0]);
+			capColors.push_back(color);
+		}
+		while (parser.endLine());
 	}
-
-	itr = 0;
-	strin = getTextFile("JKTEXT.TXT");
-	for(int hh=0; hh<45; ++hh)
 	{
-		loadToIt(tmp, strin, itr, 3);
-		trimQuotation(tmp); 
-		jktexts.push_back(tmp);
-	}
+		CLegacyConfigParser parser("DATA/SSTRAITS.TXT");
 
-	itr = 0;
-	strin = getTextFile("TVRNINFO.TXT");
-	for(int hh=0; hh<8; ++hh)
-	{
-		loadToIt(tmp, strin, itr, 3);
-		tavernInfo.push_back(tmp);
-	}
+		//skip header
+		parser.endLine();
+		parser.endLine();
 
-	itr = 0;
-	strin = getTextFile("TURNDUR.TXT");
-	for(int hh=0; hh<11; ++hh)
-	{
-		loadToIt(tmp, strin, itr, 3);
-		turnDurations.push_back(tmp);
-	}
+		do
+		{
+			skillName.push_back(parser.readString());
 
-	itr = 0;
-	strin = getTextFile("HEROSCRN.TXT");
-	for(int hh=0; hh<33; ++hh)
-	{
-		loadToIt(tmp, strin, itr, 3);
-		heroscrn.push_back(tmp);
+			skillInfoTexts.push_back(std::vector<std::string>());
+			for(int j = 0; j < 3; j++)
+				skillInfoTexts.back().push_back(parser.readString());
+		}
+		while (parser.endLine());
 	}
-
-	itr = 0;
-	strin = getTextFile("ARTEVENT.TXT");
-	for(; itr<strin.size();)
 	{
-		loadToIt(tmp, strin, itr, 2);
-	//	boost::algorithm::trim(tmp);
-		trimQuotation(tmp);
-		boost::algorithm::replace_all(tmp,"\"\"","\"");
-		artifEvents.push_back(tmp);
-	}
+		CLegacyConfigParser parser("DATA/SEERHUT.TXT");
 
-	buf = getTextFile("SSTRAITS.TXT");
-	it=0;
+		//skip header
+		parser.endLine();
 
-	for(int i=0; i<2; ++i)
-		loadToIt(dump,buf,it,3);
+		while (parser.endLine());
 
-	skillName.resize(GameConstants::SKILL_QUANTITY);
-	skillInfoTexts.resize(GameConstants::SKILL_QUANTITY);
-	for (int i=0; i<GameConstants::SKILL_QUANTITY; i++)
-	{
-		skillInfoTexts[i].resize(3);
-		loadToIt(skillName[i],buf,it,4);
-		loadToIt(skillInfoTexts[i][0],buf,it,4);
-		loadToIt(skillInfoTexts[i][1],buf,it,4);
-		loadToIt(skillInfoTexts[i][2],buf,it,3);
-		for(int j = 0; j < 3; j++)
-			trimQuotation(skillInfoTexts[i][j]);
-	}
-	buf = getTextFile("SKILLLEV.TXT");
-	it=0;
-	for(int i=0; i<6; ++i)
-	{
-		std::string buffo;
-		loadToIt(buffo,buf,it,3);
-		levels.push_back(buffo);
-	}
+		for (int i = 0; i < 6; ++i)
+			seerEmpty.push_back(parser.readString());
 
-	buf = getTextFile ("SEERHUT.TXT");
-	it = 0;
-	loadToIt (dump, buf, it, 3);
-	loadToIt (dump, buf, it, 4); //dump description
-	seerEmpty.resize(6);
-	for (i = 0; i < 5; ++i)
-	{
-		loadToIt(seerEmpty[i], buf, it, 4);
-		trimQuotation (seerEmpty[i]);
-	}
-	loadToIt (seerEmpty[5], buf, it, 3);
-	trimQuotation (seerEmpty[5]);
-	int j,k;
-	quests.resize(10);
-	for (i = 0; i < 9; ++i) //9 types of quests
-	{
-		quests[i].resize(5);
-		for (j = 0; j < 5; ++j)
+		quests.resize(10);
+		for (int i = 0; i < 9; ++i) //9 types of quests
 		{
-			loadToIt (dump, buf, it, 4); //front description
-			quests[i][j].resize(6);
-			for (k = 0; k < 5; ++k)
+			quests[i].resize(5);
+			for (int j = 0; j < 5; ++j)
 			{
-				loadToIt (quests[i][j][k], buf, it, 4);
-				trimQuotation (quests[i][j][k]);
+				parser.readString(); //front description
+				for (int k = 0; k < 6; ++k)
+					quests[i][j].push_back(parser.readString());
+
+				parser.endLine();
 			}
-			loadToIt (quests[i][j][5], buf, it, 3);
-			trimQuotation (quests[i][j][5]);
 		}
-	}
-	quests[9].resize(1);
-	quests[9][0].resize(6);
+		quests[9].resize(1);
 
-	for (k = 0; k < 5; ++k) //Time limit
-	{
-		loadToIt (quests[9][0][k], buf, it, 4);
+		for (int k = 0; k < 6; ++k) //Time limit
+		{
+			quests[9][0].push_back(parser.readString());
+		}
+		parser.endLine();
+
+		parser.endLine(); // empty line
+		parser.endLine(); // header
+
+		for (int i = 0; i < 48; ++i)
+		{
+			seerNames.push_back(parser.readString());
+			parser.endLine();
+		}
 	}
-	loadToIt (quests[9][0][k], buf, it, 3);
-	for (i = 0; i < 2; ++i) //gap description
-		loadToIt(dump,buf,it,3);
-	seerNames.resize(48);
-	for (i = 0; i < 48; ++i)
-		loadToIt(seerNames[i], buf, it, 3);
-
-	buf = getTextFile("TENTCOLR.TXT");
-	itr=0;
-	while(itr<buf.length()-1)
 	{
-		std::string tmp;
-		loadToIt(tmp, buf, itr, 3);
-		tentColors.push_back(tmp);
-	}
+		CLegacyConfigParser parser("DATA/CAMPTEXT.TXT");
 
-	//campaigns
-	buf = getTextFile ("CAMPTEXT.TXT");
-	it = 0;
-	loadToIt (dump, buf, it, 3); //comment
-	std::string nameBuf;
-	do //map names
-	{
-		loadToIt(nameBuf, buf, it, 3);
-		if(nameBuf.size())
+		//skip header
+		parser.endLine();
+
+		std::string text;
+		do
 		{
-			campaignMapNames.push_back(nameBuf);
+			text = parser.readString();
+			parser.endLine();
+			if (!text.empty())
+				campaignMapNames.push_back(parser.readString());
 		}
-	} while (nameBuf.size());
+		while (parser.endLine() && !text.empty());
 
-	campaignRegionNames.resize(campaignMapNames.size()); //allocating space
-	for(int g=0; g<campaignMapNames.size(); ++g) //region names
-	{
-		do //dump comments and empty lines
-		{
-			loadToIt(nameBuf, buf, it, 3);
-		} while (!nameBuf.size() || nameBuf[0] != '/');
-		do //actual names
+		for (size_t i=0; i<campaignMapNames.size(); i++)
 		{
-			loadToIt(nameBuf, buf, it, 3);
-			if(nameBuf.size())
+			do // skip empty space and header
 			{
-				campaignRegionNames[g].push_back(nameBuf);
+				text = parser.readString();
 			}
-		} while (nameBuf.size());
-	}
+			while (parser.endLine() && text.empty());
 
-	buf = getTextFile ("ZCREXP.TXT");
-	it = 0;
-	loadToIt (dump, buf, it, 3); //comment
-	for (int i = 0; i < 459; ++i) //some texts seem to be empty
+			campaignRegionNames.push_back(std::vector<std::string>());
+			do
+			{
+				text = parser.readString();
+				parser.endLine();
+				if (!text.empty())
+					campaignRegionNames.back().push_back(parser.readString());
+			}
+			while (parser.endLine() && !text.empty());
+		}
+	}
 	{
-		loadToIt(dump, buf, it, 4); //description, usually useless
-		loadToIt(nameBuf, buf, it, 3);
-		zcrexp.push_back(nameBuf);
+		CLegacyConfigParser parser("DATA/ZCREXP.TXT");
+		parser.endLine();//header
+		do
+		{
+			parser.readString(); //ignore 1st column with description
+			zcrexp.push_back(parser.readString());
+		}
+		while (parser.endLine());
 	}
 
+	std::string buffer;
 	std::ifstream ifs(CResourceHandler::get()->getResourceName(ResourceID("config/threatlevel.txt")), std::ios::binary);
-	getline(ifs, buf); //skip 1st line
+	getline(ifs, buffer); //skip 1st line
 	for (int i = 0; i < 13; ++i)
 	{
-		getline(ifs, buf);
-		threat.push_back(buf);
+		getline(ifs, buffer);
+		threat.push_back(buffer);
 	}
 }
 
-
 std::string CGeneralTextHandler::getTitle(const std::string & text)
 {
 	std::string ret;

+ 37 - 6
lib/CGeneralTextHandler.h

@@ -1,7 +1,5 @@
 #pragma once
 
-
-
 /*
  * CGeneralTextHandler.h, part of VCMI engine
  *
@@ -12,8 +10,41 @@
  *
  */
 
-DLL_LINKAGE void loadToIt(std::string &dest, const std::string &src, int &iter, int mode);
-std::string readTo(const std::string &in, int &it, char end);
+class CInputStream;
+
+/// Parser for any text files from H3
+class CLegacyConfigParser
+{
+	std::unique_ptr<char[]> data;
+	char * curr;
+	char * end;
+
+	void init(const std::unique_ptr<CInputStream> & input);
+
+	/// extracts part of quoted string.
+	std::string extractQuotedPart();
+
+	/// extracts quoted string. Any end of lines are ignored, double-quote is considered as "escaping"
+	std::string extractQuotedString();
+
+	/// extracts non-quoted string
+	std::string extractNormalString();
+
+public:
+	/// read one entry from current line. Return ""/0 if end of line reached
+	std::string readString();
+	float readNumber();
+
+	/// returns true if next entry is empty
+	bool isNextEntryEmpty();
+
+	/// end current line
+	bool endLine();
+
+	CLegacyConfigParser(std::string URI);
+	CLegacyConfigParser(const std::unique_ptr<CInputStream> & input);
+};
+
 class DLL_LINKAGE CGeneralTextHandler //Handles general texts
 {
 public:
@@ -49,8 +80,8 @@ public:
 	std::map<int, std::map<int, std::pair<std::string, std::string> > > buildings; //map[town id][building id] => pair<name, description>
 
 	std::vector<std::pair<std::string,std::string> > zelp;
-	std::string lossCondtions[4];
-	std::string victoryConditions[14];
+	std::vector<std::string> lossCondtions;
+	std::vector<std::string> victoryConditions;
 
 	//objects
 	std::vector<std::string> names; //vector of objects; i-th object in vector has subnumber i

+ 62 - 130
lib/CHeroHandler.cpp

@@ -1,14 +1,13 @@
 #include "StdInc.h"
 #include "CHeroHandler.h"
 
+#include "CGeneralTextHandler.h"
 #include "Filesystem/CResourceLoader.h"
-#include "../lib/VCMI_Lib.h"
-#include "../lib/JsonNode.h"
+#include "VCMI_Lib.h"
+#include "JsonNode.h"
 #include "GameConstants.h"
-#include <boost/version.hpp>
 #include "BattleHex.h"
 
-void loadToIt(std::string &dest, const std::string &src, int &iter, int mode);
 /*
  * CHeroHandler.cpp, part of VCMI engine
  *
@@ -114,7 +113,6 @@ void CHeroHandler::loadObstacles()
 		}
 	};
 
-
 	const JsonNode config(ResourceID("config/obstacles.json"));
 	loadObstacles(config["obstacles"], false, obstacles);
 	loadObstacles(config["absoluteObstacles"], true, absoluteObstacles);
@@ -164,68 +162,27 @@ void CHeroHandler::loadPuzzleInfo()
 void CHeroHandler::loadHeroes()
 {
 	VLC->heroh = this;
-	auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/HOTRAITS.TXT"));
-	std::string buf((char*)textFile.first.get(), textFile.second);
-	int it=0;
-	std::string dump;
-	for(int i=0; i<2; ++i)
-	{
-		loadToIt(dump,buf,it,3);
-	}
+	CLegacyConfigParser parser("DATA/HOTRAITS.TXT");
+
+	parser.endLine(); //ignore header
+	parser.endLine();
 
-	int numberOfCurrentClassHeroes = 0;
-	int currentClass = 0;
-	int additHero = 0;
-	CHero::EHeroClasses addTab[12];
-	addTab[0] = CHero::KNIGHT;
-	addTab[1] = CHero::WITCH;
-	addTab[2] = CHero::KNIGHT;
-	addTab[3] = CHero::WIZARD;
-	addTab[4] = CHero::RANGER;
-	addTab[5] = CHero::BARBARIAN;
-	addTab[6] = CHero::DEATHKNIGHT;
-	addTab[7] = CHero::WARLOCK;
-	addTab[8] = CHero::KNIGHT;
-	addTab[9] = CHero::WARLOCK;
-	addTab[10] = CHero::BARBARIAN;
-	addTab[11] = CHero::DEMONIAC;
-
-	
 	for (int i=0; i<GameConstants::HEROES_QUANTITY; i++)
 	{
-		CHero * nher = new CHero;
-		if(currentClass<18)
-		{
-			nher->heroType = static_cast<CHero::EHeroClasses>(currentClass);
-			++numberOfCurrentClassHeroes;
-			if(numberOfCurrentClassHeroes==8)
-			{
-				numberOfCurrentClassHeroes = 0;
-				++currentClass;
-			}
-		}
-		else
-		{
-			nher->heroType = addTab[additHero++];
-		}
-
-		std::string pom ;
-		loadToIt(nher->name,buf,it,4);
+		CHero * hero = new CHero;
+		hero->name = parser.readString();
 
 		for(int x=0;x<3;x++)
 		{
-			loadToIt(pom,buf,it,4);
-			nher->lowStack[x] = atoi(pom.c_str());
-			loadToIt(pom,buf,it,4);
-			nher->highStack[x] = atoi(pom.c_str());
-			loadToIt(nher->refTypeStack[x],buf,it,(x==2) ? (3) : (4));
-			int hlp = nher->refTypeStack[x].find_first_of(' ',0);
-			if(hlp>=0)
-				nher->refTypeStack[x].replace(hlp,1,"");
+			hero->lowStack[x] = parser.readNumber();
+			hero->highStack[x] = parser.readNumber();
+			hero->refTypeStack[x] = parser.readString();
+			boost::algorithm::replace_all(hero->refTypeStack[x], " ", ""); //remove spaces
 		}
-	
-		nher->ID = heroes.size();
-		heroes.push_back(nher);
+		parser.endLine();
+
+		hero->ID = heroes.size();
+		heroes.push_back(hero);
 	}
 
 	// Load heroes information
@@ -236,6 +193,7 @@ void CHeroHandler::loadHeroes()
 
 		// sex: 0=male, 1=female
 		heroes[hid]->sex = !!hero["female"].Bool();
+		heroes[hid]->heroType = CHero::EHeroClasses(hero["class"].Float());
 
 		BOOST_FOREACH(const JsonNode &set, hero["skill_set"].Vector()) {
 			heroes[hid]->secSkillsInit.push_back(std::make_pair(set["skill"].Float(), set["level"].Float()));
@@ -246,18 +204,16 @@ void CHeroHandler::loadHeroes()
 			heroes[hid]->startingSpell = value->Float();
 		}
 
-		value = &hero["specialties"];
-		if (!value->isNull()) {
-			BOOST_FOREACH(const JsonNode &specialty, value->Vector()) {
-				SSpecialtyInfo dummy;
+		BOOST_FOREACH(const JsonNode &specialty, hero["specialties"].Vector())
+		{
+			SSpecialtyInfo dummy;
 
-				dummy.type = specialty["type"].Float();
-				dummy.val = specialty["val"].Float();
-				dummy.subtype = specialty["subtype"].Float();
-				dummy.additionalinfo = specialty["info"].Float();
+			dummy.type = specialty["type"].Float();
+			dummy.val = specialty["val"].Float();
+			dummy.subtype = specialty["subtype"].Float();
+			dummy.additionalinfo = specialty["info"].Float();
 
-				heroes[hid]->spec.push_back(dummy); //put a copy of dummy
-			}
+			heroes[hid]->spec.push_back(dummy); //put a copy of dummy
 		}
 	}
 
@@ -285,98 +241,74 @@ void CHeroHandler::loadHeroes()
 	}
 	expPerLevel.pop_back();//last value is broken
 
-	//ballistics info
-	textFile = CResourceHandler::get()->loadData(ResourceID("DATA/BALLIST.TXT"));
-	buf = std::string((char*)textFile.first.get(), textFile.second);
-	it = 0;
-	for(int i=0; i<22; ++i)
-	{
-		loadToIt(dump,buf,it,4);
-	}
-	for(int lvl=0; lvl<4; ++lvl)
+	CLegacyConfigParser ballParser("DATA/BALLIST.TXT");
+
+	ballParser.endLine(); //header
+	ballParser.endLine();
+
+	do
 	{
+		ballParser.readString();
+		ballParser.readString();
+
 		CHeroHandler::SBallisticsLevelInfo bli;
-		si32 tempNum;
-		loadToIt(tempNum,buf,it,4);
-		bli.keep = tempNum;
-		loadToIt(tempNum,buf,it,4);
-		bli.tower = tempNum;
-		loadToIt(tempNum,buf,it,4);
-		bli.gate = tempNum;
-		loadToIt(tempNum,buf,it,4);
-		bli.wall = tempNum;
-		loadToIt(tempNum,buf,it,4);
-		bli.shots = tempNum;
-		loadToIt(tempNum,buf,it,4);
-		bli.noDmg = tempNum;
-		loadToIt(tempNum,buf,it,4);
-		bli.oneDmg = tempNum;
-		loadToIt(tempNum,buf,it,4);
-		bli.twoDmg = tempNum;
-		loadToIt(tempNum,buf,it,4);
-		bli.sum = tempNum;
-		if(lvl!=3)
-		{
-			loadToIt(dump,buf,it,4);
-		}
+		bli.keep   = ballParser.readNumber();
+		bli.tower  = ballParser.readNumber();
+		bli.gate   = ballParser.readNumber();
+		bli.wall   = ballParser.readNumber();
+		bli.shots  = ballParser.readNumber();
+		bli.noDmg  = ballParser.readNumber();
+		bli.oneDmg = ballParser.readNumber();
+		bli.twoDmg = ballParser.readNumber();
+		bli.sum    = ballParser.readNumber();
 		ballistics.push_back(bli);
 	}
+	while (ballParser.endLine());
 }
 
 void CHeroHandler::loadHeroClasses()
 {
-	auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/HCTRAITS.TXT"));
-	std::istringstream str(std::string((char*)textFile.first.get(), textFile.second)); //we'll be reading from it
-	const int BUFFER_SIZE = 5000;
-	char buffer[BUFFER_SIZE+1];
+	CLegacyConfigParser parser("DATA/HCTRAITS.TXT");
 
-	for(int i=0; i<3; ++i) str.getline(buffer, BUFFER_SIZE); //omitting rubbish
+	parser.endLine(); // header
+	parser.endLine();
 
-
-	for(int ss=0; ss<18; ++ss) //18 classes of hero (including conflux)
+	do
 	{
 		CHeroClass * hc = new CHeroClass;
-		hc->alignment = ss / 6;
-
-		char name[BUFFER_SIZE+1];
-		str.get(name, BUFFER_SIZE, '\t');
-		hc->name = name;
-		//workaround for locale issue (different localisations use different decimal separator)
-		int intPart,fracPart;
-		str >> intPart;
-		str.ignore();//ignore decimal separator
-		str >> fracPart;
-		hc->aggression = intPart + fracPart/100.0;
-		
-		str >> hc->initialAttack;
-		str >> hc->initialDefence;
-		str >> hc->initialPower;
-		str >> hc->initialKnowledge;
+		hc->alignment = heroClasses.size() / 6;
+
+		hc->name             = parser.readString();
+		hc->aggression       = parser.readNumber();
+		hc->initialAttack    = parser.readNumber();
+		hc->initialDefence   = parser.readNumber();
+		hc->initialPower     = parser.readNumber();
+		hc->initialKnowledge = parser.readNumber();
 
 		hc->primChance.resize(GameConstants::PRIMARY_SKILLS);
 		for(int x=0; x<GameConstants::PRIMARY_SKILLS; ++x)
 		{
-			str >> hc->primChance[x].first;
+			hc->primChance[x].first = parser.readNumber();
 		}
 		for(int x=0; x<GameConstants::PRIMARY_SKILLS; ++x)
 		{
-			str >> hc->primChance[x].second;
+			hc->primChance[x].second = parser.readNumber();
 		}
 
 		hc->proSec.resize(GameConstants::SKILL_QUANTITY);
 		for(int dd=0; dd<GameConstants::SKILL_QUANTITY; ++dd)
 		{
-			str >> hc->proSec[dd];
+			hc->proSec[dd] = parser.readNumber();
 		}
 
 		for(int dd=0; dd<ARRAY_COUNT(hc->selectionProbability); ++dd)
 		{
-			str >> hc->selectionProbability[dd];
+			hc->selectionProbability[dd] = parser.readNumber();
 		}
 
 		heroClasses.push_back(hc);
-		str.getline(buffer, BUFFER_SIZE); //removing end of line characters
 	}
+	while (parser.endLine() && !parser.isNextEntryEmpty());
 }
 
 void CHeroHandler::initHeroClasses()

+ 0 - 1
lib/CObjectHandler.cpp

@@ -43,7 +43,6 @@ using namespace boost::assign;
 std::map<int,std::map<int, std::vector<int> > > CGTeleport::objs;
 std::vector<std::pair<int, int> > CGTeleport::gates;
 IGameCallback * IObjectInterface::cb = NULL;
-DLL_LINKAGE void loadToIt(std::string &dest, const std::string &src, int &iter, int mode);
 extern boost::rand48 ran;
 std::map <ui8, std::set <ui8> > CGKeys::playerKeyMap;
 std::map <si32, std::vector<si32> > CGMagi::eyelist;

+ 72 - 77
lib/CSpellHandler.cpp

@@ -1,9 +1,10 @@
 #include "StdInc.h"
 #include "CSpellHandler.h"
 
+#include "CGeneralTextHandler.h"
 #include "Filesystem/CResourceLoader.h"
-#include "../lib/VCMI_Lib.h"
-#include "../lib/JsonNode.h"
+#include "VCMI_Lib.h"
+#include "JsonNode.h"
 #include <cctype>
 #include "GameConstants.h"
 #include "BattleHex.h"
@@ -255,11 +256,6 @@ bool CSpell::isRisingSpell() const
 	return vstd::contains(VLC->spellh->risingSpells, id);
 }
 
-static bool startsWithX(const std::string &s)
-{
-	return s.size() && s[0] == 'x';
-}
-
 bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos)
 {
 	int3 diff = pos - center;
@@ -269,83 +265,82 @@ bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos)
 		return false;
 }
 
+CSpell * CSpellHandler::loadSpell(CLegacyConfigParser & parser)
+{
+	CSpell * spell = new CSpell; //new currently being read spell
+
+	spell->name    = parser.readString();
+	spell->abbName = parser.readString();
+	spell->level   = parser.readNumber();
+	spell->earth   = parser.readString() == "x";
+	spell->water   = parser.readString() == "x";
+	spell->fire    = parser.readString() == "x";
+	spell->air     = parser.readString() == "x";
+
+	for (int i = 0; i < 4 ; i++)
+		spell->costs.push_back(parser.readNumber());
+
+	spell->power = parser.readNumber();
+	for (int i = 0; i < 4 ; i++)
+		spell->powers.push_back(parser.readNumber());
+
+	for (int i = 0; i < 9 ; i++)
+		spell->probabilities.push_back(parser.readNumber());
+
+	for (int i = 0; i < 4 ; i++)
+		spell->AIVals.push_back(parser.readNumber());
+
+	for (int i = 0; i < 4 ; i++)
+		spell->descriptions.push_back(parser.readString());
+
+	spell->attributes = parser.readString();
+	spell->mainEffectAnim = -1;
+	return spell;
+}
+
 void CSpellHandler::loadSpells()
 {
-	auto textFile = CResourceHandler::get()->loadData(ResourceID("DATA/SPTRAITS.TXT"));
-	std::string buf((char*)textFile.first.get(), textFile.second);
-
-	std::string pom;
-	int andame = buf.size(), i=0; //buf iterator
-	for(int z=0; z<5; ++z)
-		loadToIt(pom,buf,i,3);
-
-	bool combSpells=false; //true, if we are reading combat spells
-	bool creatureAbility=false; //if true, only creature can use this spell
-	int ifHit = 0;
-	while(i<andame)
+	CLegacyConfigParser parser("DATA/SPTRAITS.TXT");
+
+	for(int i=0; i<5; i++) // header
+		parser.endLine();
+
+	do //read adventure map spells
 	{
-		if(spells.size()==81)
-			break;
-		CSpell * nsp = new CSpell; //new currently being read spell
+		CSpell * spell = loadSpell(parser);
+		spell->id = spells.size();
+		spell->combatSpell = false;
+		spell->creatureAbility = false;
+		spells.push_back(spell);
+	}
+	while (parser.endLine() && !parser.isNextEntryEmpty());
 
-		loadToIt(nsp->name,buf,i,4);
-		if(nsp->name == std::string(""))
-		{
-			if(ifHit == 0)
-			{
-				combSpells = true;
-			}
-			if(ifHit == 1)
-			{
-				creatureAbility = true;
-			}
-			for(int z=0; z<3; ++z)
-				loadToIt(pom,buf,i,3);
-			loadToIt(nsp->name,buf,i,4);
-			++ifHit;
-		}
+	for(int i=0; i<3; i++)
+		parser.endLine();
 
-		loadToIt(nsp->abbName,buf,i,4);
-		loadToIt(nsp->level,buf,i,4);
-		loadToIt(pom,buf,i,4);
-		nsp->earth = startsWithX(pom);
-		loadToIt(pom,buf,i,4);
-		nsp->water = startsWithX(pom);
-		loadToIt(pom,buf,i,4);
-		nsp->fire = startsWithX(pom);
-		loadToIt(pom,buf,i,4);
-		nsp->air = startsWithX(pom);
-
-		nsp->costs.resize(4);
-		for (int z = 0; z < 4 ; z++)
-			loadToIt(nsp->costs[z],buf,i,4);
-		loadToIt(nsp->power,buf,i,4);
-		nsp->powers.resize(4);
-		for (int z = 0; z < 4 ; z++)
-			loadToIt(nsp->powers[z],buf,i,4);
-
-		nsp->probabilities.resize(9);
-		for (int z = 0; z < 9 ; z++)
-			loadToIt(nsp->probabilities[z],buf,i,4);
-
-		nsp->AIVals.resize(4);
-		for (int z = 0; z < 4 ; z++)
-			loadToIt(nsp->AIVals[z],buf,i,4);
-
-		nsp->descriptions.resize(4);
-		for (int z = 0; z < 4 ; z++)
-		{
-			loadToIt(nsp->descriptions[z],buf,i,4);
-			boost::algorithm::replace_all(nsp->descriptions[z],"\"","");
-		}
+	do //read battle spells
+	{
+		CSpell * spell = loadSpell(parser);
+		spell->id = spells.size();
+		spell->combatSpell = true;
+		spell->creatureAbility = false;
+		spells.push_back(spell);
+	}
+	while (parser.endLine() && !parser.isNextEntryEmpty());
 
-		loadToIt(nsp->attributes,buf,i,3);
-		nsp->id = spells.size();
-		nsp->combatSpell = combSpells;
-		nsp->creatureAbility = creatureAbility;
-		nsp->mainEffectAnim = -1;
-		spells.push_back(nsp);
+	for(int i=0; i<3; i++)
+		parser.endLine();
+
+	do //read creature abilities
+	{
+		CSpell * spell = loadSpell(parser);
+		spell->id = spells.size();
+		spell->combatSpell = true;
+		spell->creatureAbility = true;
+		spells.push_back(spell);
 	}
+	while (parser.endLine() && !parser.isNextEntryEmpty());
+
 	boost::replace_first (spells[47]->attributes, "2", ""); // disrupting ray will now affect single creature
 
 	//loading of additional spell traits

+ 3 - 0
lib/CSpellHandler.h

@@ -14,6 +14,7 @@
  *
  */
 
+class CLegacyConfigParser;
 struct BattleHex;
 
 class DLL_LINKAGE CSpell
@@ -86,6 +87,8 @@ bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos); //for spe
 
 class DLL_LINKAGE CSpellHandler
 {
+	CSpell * loadSpell(CLegacyConfigParser & parser);
+
 public:
 	CSpellHandler();
 	std::vector< ConstTransitivePtr<CSpell> > spells;

+ 4 - 0
lib/Filesystem/CResourceLoader.cpp

@@ -173,6 +173,10 @@ void CResourceLoader::addLoader(std::string mountPoint, shared_ptr<ISimpleResour
 		// Create identifier and locator and add them to the resources list
 		ResourceID ident(mountPoint, entry.first.getName(), entry.first.getType());
 		ResourceLocator locator(loader.get(), entry.second);
+
+		if (ident.getType() == EResType::OTHER)
+			tlog5 << "Warning: unknown file type: " << entry.second << "\n";
+
 		resources[ident].push_back(locator);
 	}
 }

+ 0 - 115
lib/VCMI_Lib.cpp

@@ -44,121 +44,6 @@ DLL_LINKAGE void initDLL(CConsoleHandler *Console, std::ostream *Logfile)
 	//HANDLE_EXCEPTION;
 }
 
-DLL_LINKAGE void loadToIt(std::string &dest, const std::string &src, int &iter, int mode)
-{
-	switch(mode)
-	{
-	case 0:
-		{
-			int hmcr = 0;
-			for(; iter<src.size(); ++iter)
-			{
-				if(src[iter]=='\t')
-					++hmcr;
-				if(hmcr==1)
-					break;
-			}
-			++iter;
-
-			int befi=iter;
-			for(; iter<src.size(); ++iter)
-			{
-				if(src[iter]=='\t')
-					break;
-			}
-			dest = src.substr(befi, iter-befi);
-			++iter;
-
-			hmcr = 0;
-			for(; iter<src.size(); ++iter)
-			{
-				if(src[iter]=='\r')
-					++hmcr;
-				if(hmcr==1)
-					break;
-			}
-			iter+=2;
-			break;
-		}
-	case 1:
-		{
-			int hmcr = 0;
-			for(; iter<src.size(); ++iter)
-			{
-				if(src[iter]=='\t')
-					++hmcr;
-				if(hmcr==1)
-					break;
-			}
-			++iter;
-
-			int befi=iter;
-			for(; iter<src.size(); ++iter)
-			{
-				if(src[iter]=='\r')
-					break;
-			}
-			dest = src.substr(befi, iter-befi);
-			iter+=2;
-			break;
-		}
-	case 2:
-		{
-			int befi=iter;
-			for(; iter<src.size(); ++iter)
-			{
-				if(src[iter]=='\t')
-					break;
-			}
-			dest = src.substr(befi, iter-befi);
-			++iter;
-
-			int hmcr = 0;
-			for(; iter<src.size(); ++iter)
-			{
-				if(src[iter]=='\r')
-					++hmcr;
-				if(hmcr==1)
-					break;
-			}
-			iter+=2;
-			break;
-		}
-	case 3:
-		{
-			int befi=iter;
-			for(; iter<src.size(); ++iter)
-			{
-				if(src[iter]=='\r')
-					break;
-			}
-			dest = src.substr(befi, iter-befi);
-			iter+=2;
-			break;
-		}
-	case 4:
-		{
-			int befi=iter;
-			for(; iter<src.size(); ++iter)
-			{
-				if(src[iter]=='\t')
-					break;
-			}
-			dest = src.substr(befi, iter-befi);
-			iter++;
-			break;
-		}
-	}
-}
-
-
-DLL_LINKAGE void loadToIt(si32 &dest, const std::string &src, int &iter, int mode)
-{
-	std::string pom;
-	loadToIt(pom,src,iter,mode);
-	dest = atol(pom.c_str());
-}
-
 void LibClasses::loadFilesystem()
 {
 	CStopWatch totalTime;

+ 0 - 2
lib/VCMI_Lib.h

@@ -58,6 +58,4 @@ public:
 
 extern DLL_LINKAGE LibClasses * VLC;
 
-DLL_LINKAGE void loadToIt(std::string &dest, const std::string &src, int &iter, int mode);
-DLL_LINKAGE void loadToIt(si32 &dest, const std::string &src, int &iter, int mode);
 DLL_LINKAGE void initDLL(CConsoleHandler *Console, std::ostream *Logfile);

Some files were not shown because too many files changed in this diff