فهرست منبع

- all fonts handling is now in new file, UIFramework/Fonts.cpp/h
- common base class for H3 bmp and ttf fonts
- replaced fonts.txt with fonts.json

Ivan Savenko 13 سال پیش
والد
کامیت
b5ebf443fc

+ 3 - 8
client/BattleInterface/CBattleInterface.cpp

@@ -2271,14 +2271,9 @@ void CBattleInterface::showAliveStack(const CStack *stack, SDL_Surface * to)
 		SDL_Rect temp_rect = genRect(amountNormal->h, amountNormal->w, creAnims[ID]->pos.x + xAdd, creAnims[ID]->pos.y + yAdd);
 		SDL_BlitSurface(amountBG, NULL, to, &temp_rect);
 		//blitting amount
-		CSDL_Ext::printAtMiddle(
-			makeNumberShort(stack->count),
-			creAnims[ID]->pos.x + xAdd + 15,
-			creAnims[ID]->pos.y + yAdd + 5,
-			FONT_TINY,
-			Colors::WHITE,
-			to
-		);
+
+		Point textPos(creAnims[ID]->pos.x + xAdd + 15, creAnims[ID]->pos.y + yAdd + 5);
+		graphics->fonts[FONT_TINY]->renderTextCenter(to, makeNumberShort(stack->count), Colors::WHITE, textPos);
 	}
 }
 

+ 9 - 5
client/BattleInterface/CBattleInterfaceClasses.cpp

@@ -23,6 +23,7 @@
 #include "../../lib/CTownHandler.h"
 #include "../CBitmapHandler.h"
 #include "../CCreatureWindow.h"
+#include "../CMessage.h"
 
 CBattleConsole::~CBattleConsole()
 {
@@ -31,24 +32,27 @@ CBattleConsole::~CBattleConsole()
 
 void CBattleConsole::showAll(SDL_Surface * to)
 {
+	Point textPos(pos.x + pos.w/2, pos.y + 11);
+
 	if(ingcAlter.size())
 	{
-		CSDL_Ext::printAtMiddleWB(ingcAlter, pos.x + pos.w/2, pos.y + 11, FONT_SMALL, 80, Colors::WHITE, to);
+		graphics->fonts[FONT_SMALL]->renderTextLinesCenter(to, CMessage::breakText(ingcAlter, pos.w, FONT_SMALL), Colors::WHITE, textPos);
 	}
 	else if(alterTxt.size())
 	{
-		CSDL_Ext::printAtMiddleWB(alterTxt, pos.x + pos.w/2, pos.y + 11, FONT_SMALL, 80, Colors::WHITE, to);
+		graphics->fonts[FONT_SMALL]->renderTextLinesCenter(to, CMessage::breakText(alterTxt, pos.w, FONT_SMALL), Colors::WHITE, textPos);
 	}
 	else if(texts.size())
 	{
 		if(texts.size()==1)
 		{
-			CSDL_Ext::printAtMiddleWB(texts[0], pos.x + pos.w/2, pos.y + 11, FONT_SMALL, 80, Colors::WHITE, to);
+			graphics->fonts[FONT_SMALL]->renderTextLinesCenter(to, CMessage::breakText(texts[0], pos.w, FONT_SMALL), Colors::WHITE, textPos);
 		}
 		else
 		{
-			CSDL_Ext::printAtMiddleWB(texts[lastShown-1], pos.x + pos.w/2, pos.y + 11, FONT_SMALL, 80, Colors::WHITE, to);
-			CSDL_Ext::printAtMiddleWB(texts[lastShown], pos.x + pos.w/2, pos.y + 27, FONT_SMALL, 80, Colors::WHITE, to);
+			graphics->fonts[FONT_SMALL]->renderTextLinesCenter(to, CMessage::breakText(texts[lastShown - 1], pos.w, FONT_SMALL), Colors::WHITE, textPos);
+			textPos.y += 16;
+			graphics->fonts[FONT_SMALL]->renderTextLinesCenter(to, CMessage::breakText(texts[lastShown], pos.w, FONT_SMALL), Colors::WHITE, textPos);
 		}
 	}
 }

+ 9 - 10
client/CAdvmapInterface.cpp

@@ -335,20 +335,19 @@ CResDataBar::~CResDataBar()
 void CResDataBar::draw(SDL_Surface * to)
 {
 	blitAt(bg,pos.x,pos.y,to);
-	char * buf = new char[15];
 	for (int i=0;i<7;i++)
 	{
-		SDL_itoa(LOCPLINT->cb->getResourceAmount(i),buf,10);
-		printAt(buf,txtpos[i].first,txtpos[i].second,FONT_SMALL,Colors::WHITE,to);
+		std::string text = boost::lexical_cast<std::string>(LOCPLINT->cb->getResourceAmount(i));
+
+		graphics->fonts[FONT_SMALL]->renderTextLeft(to, text, Colors::WHITE, Point(txtpos[i].first,txtpos[i].second));
 	}
 	std::vector<std::string> temp;
-	SDL_itoa(LOCPLINT->cb->getDate(3),buf,10); temp+=std::string(buf);
-	SDL_itoa(LOCPLINT->cb->getDate(2),buf,10); temp+=std::string(buf);
-	SDL_itoa(LOCPLINT->cb->getDate(1),buf,10); temp+=std::string(buf);
-	printAt(processStr(datetext,temp),txtpos[7].first,txtpos[7].second,FONT_SMALL,Colors::WHITE,to);
-	temp.clear();
-	//updateRect(&pos,screen);
-	delete[] buf;
+
+	temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(3)));
+	temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(2)));
+	temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(1)));
+
+	graphics->fonts[FONT_SMALL]->renderTextLeft(to, processStr(datetext,temp), Colors::WHITE, Point(txtpos[7].first,txtpos[7].second));
 }
 
 void CResDataBar::show(SDL_Surface * to)

+ 1 - 1
client/CAnimation.cpp

@@ -1390,7 +1390,7 @@ void CShowableAnim::rotate(bool on, bool vertical)
 }
 
 CCreatureAnim::CCreatureAnim(int x, int y, std::string name, Rect picPos, ui8 flags, EAnimType type):
-	CShowableAnim(x,y,name,flags,3,type)
+	CShowableAnim(x,y,name,flags,4,type)
 {
 	xOffset = picPos.x;
 	yOffset = picPos.y;

+ 5 - 4
client/CCreatureWindow.cpp

@@ -562,8 +562,9 @@ void CCreatureWindow::showAll(SDL_Surface * to)
 
 void CCreatureWindow::show(SDL_Surface * to)
 {
-	if (count.size()) //army stack
-		printTo(count, pos.x + 114, pos.y + 174,FONT_TIMES, Colors::WHITE, to);
+	CIntObject::show(to);
+	if (!count.empty()) //army stack
+		graphics->fonts[FONT_TIMES]->renderTextRight(to, count, Colors::WHITE, Point(pos.x + 114, pos.y + 174));
 }
 
 
@@ -710,8 +711,8 @@ void CBonusItem::showAll (SDL_Surface * to)
 {
 	if (visible)
 	{
-		printAt(name, pos.x + 72, pos.y + 6, FONT_SMALL, Colors::YELLOW, to);
-		printAt(description, pos.x + 72, pos.y + 30, FONT_SMALL, Colors::WHITE, to);
+		graphics->fonts[FONT_SMALL]->renderTextLeft(to, name, Colors::YELLOW, Point(pos.x + 72, pos.y + 6));
+		graphics->fonts[FONT_SMALL]->renderTextLeft(to, name, Colors::WHITE,  Point(pos.x + 72, pos.y + 30));
 		if (bonusGraphics && bonusGraphics->bg)
 			blitAtLoc(bonusGraphics->bg, 12, 2, to);
 	}

+ 3 - 22
client/CHeroWindow.cpp

@@ -339,28 +339,9 @@ void CHeroWindow::showAll(SDL_Surface * to)
 	printAtMiddleLoc(CGI->generaltexth->jktexts[4], 262, 99, FONT_SMALL, Colors::YELLOW, to);
 	 
 	//dismiss / quest log
-	std::vector<std::string> toPrin = CMessage::breakText(CGI->generaltexth->jktexts[8]);
-	if(toPrin.size()==1)
-	{
-	 	printAtLoc(toPrin[0], 372, 439, FONT_SMALL, Colors::WHITE, to);
-	}
-	else
-	{
-	 	printAtLoc(toPrin[0], 372, 430, FONT_SMALL, Colors::WHITE, to);
-	 	printAtLoc(toPrin[1], 372, 446, FONT_SMALL, Colors::WHITE, to);
-	}
-	 
-	toPrin = CMessage::breakText(CGI->generaltexth->jktexts[9]);
-	if(toPrin.size()==1)
-	{
-	 	printAtLoc(toPrin[0], 512, 439, FONT_SMALL, Colors::WHITE, to);
-	}
-	else
-	{
-	 	printAtLoc(toPrin[0], 512, 430, FONT_SMALL, Colors::WHITE, to);
-	 	printAtLoc(toPrin[1], 512, 446, FONT_SMALL, Colors::WHITE, to);
-	}
-	 
+	printAtMiddleWBLoc(CGI->generaltexth->jktexts[8], 388, 455, FONT_SMALL, 50, Colors::WHITE, to);
+	printAtMiddleWBLoc(CGI->generaltexth->jktexts[9], 534, 455, FONT_SMALL, 50, Colors::WHITE, to);
+
 	//printing primary skills' amounts
 	for(int m=0; m<4; ++m)
 	{

+ 1 - 0
client/CMakeLists.txt

@@ -14,6 +14,7 @@ set(client_SRCS
         UIFramework/CGuiHandler.cpp
         UIFramework/CIntObject.cpp
         UIFramework/CIntObjectClasses.cpp
+        UIFramework/Fonts.cpp
         UIFramework/Geometries.cpp
         UIFramework/CCursorHandler.cpp
         UIFramework/SDL_Extensions.cpp

+ 30 - 176
client/CMessage.cpp

@@ -58,8 +58,8 @@ struct ComponentsToBlit
 	std::vector< std::vector<ComponentResolved*> > comps;
 	int w, h;
 
-	void blitCompsOnSur(SDL_Surface * _or, int inter, int &curh, SDL_Surface *ret);
-	ComponentsToBlit(std::vector<CComponent*> & SComps, int maxw, SDL_Surface* _or); //c-tor
+	void blitCompsOnSur(bool blitOr, int inter, int &curh, SDL_Surface *ret);
+	ComponentsToBlit(std::vector<CComponent*> & SComps, int maxw, bool blitOr); //c-tor
 	~ComponentsToBlit(); //d-tor
 };
 
@@ -131,9 +131,7 @@ SDL_Surface * CMessage::drawDialogBox(int w, int h, int playerColor)
 	return ret;
 }
 
-/* The map file contains long texts, with or without line breaks. This
- * method takes such a text and breaks it into into several lines. */
-std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineSize/*=30*/, const boost::function<int(char)> &charMetric /*= 0*/, bool allowLeadingWhitespace /*= false*/ )
+std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineSize, EFonts font )
 {
 	std::vector<std::string> ret;
 
@@ -153,11 +151,8 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineSi
 				opened=true;
 			else if (text[z]=='}')
 				opened=false;
-			else if(charMetric)
-				lineLength += charMetric(text[z]);
 			else
-				lineLength++;
-
+				lineLength += graphics->fonts[font]->getSymbolWidth(text[z]);
 			z++;
 		}
 
@@ -176,8 +171,8 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineSi
 			if (pos > 0)
 				z = pos+1;
 		}
-		
-		if(z) //non-blank line 
+
+		if(z) //non-blank line
 		{
 			ret.push_back(text.substr(0, z));
 
@@ -187,7 +182,7 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineSi
 
 			text.erase(0, z);
 		}
-		else if(text[z] == 0x0a) //blank line 
+		else if(text[z] == 0x0a) //blank line
 		{
 			ret.push_back(""); //add empty string, no extra actions needed
 		}
@@ -204,8 +199,9 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineSi
 			lineManuallyBroken = true;
 		}
 
-		if(!allowLeadingWhitespace || !lineManuallyBroken)
-			boost::algorithm::trim_left_if(text,boost::algorithm::is_any_of(std::string(" "))); 
+		//if(!allowLeadingWhitespace || !lineManuallyBroken)
+		if(!lineManuallyBroken)
+			boost::algorithm::trim_left_if(text,boost::algorithm::is_any_of(std::string(" ")));
 
 		if (opened)
 		{
@@ -216,164 +212,18 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineSi
 	}
 
 	/* Trim whitespaces of every line. */
-	if(!allowLeadingWhitespace)
+	//if(!allowLeadingWhitespace)
 		for (size_t i=0; i<ret.size(); i++)
 			boost::algorithm::trim(ret[i]);
 
 	return ret;
 }
 
-std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineWidth, EFonts font )
-{
-	return breakText(text, maxLineWidth, boost::bind(&Font::getCharWidth, graphics->fonts[font], _1), true);
-}
-
-std::pair<int,int> CMessage::getMaxSizes(std::vector<std::vector<SDL_Surface*> > * txtg, int fontHeight)
-{
-	std::pair<int,int> ret;
-	ret.first = -1;
-	ret.second=0;
-	for (size_t i=0; i<txtg->size();i++) //we are searching widest line and total height
-	{
-		int lw=0;
-		for (size_t j=0;j<(*txtg)[i].size();j++)
-		{
-			lw+=(*txtg)[i][j]->w;
-			ret.second+=(*txtg)[i][j]->h;
-		}
-		if(!(*txtg)[i].size())
-			ret.second+=fontHeight;
-		if (ret.first<lw)
-			ret.first=lw;
-	}
-	return ret;
-}
-
-// Blit the text in txtg onto one surface. txtg contains lines of
-// text. Each line can be split into pieces. Currently only lines with
-// the same height are supported (ie. fontHeight).
-SDL_Surface * CMessage::blitTextOnSur(std::vector<std::vector<SDL_Surface*> > * txtg, int fontHeight, int & curh, SDL_Surface * ret, int xCenterPos)
-{
-	for (size_t i=0; i<txtg->size(); i++, curh += fontHeight)
-	{
-		int lw=0; //line width
-		for (size_t j=0;j<(*txtg)[i].size();j++)
-			lw+=(*txtg)[i][j]->w; 
-
-		int pw = (xCenterPos < 0)  ?  ret->w/2  :  xCenterPos; 
-		pw -= lw/2; //x coord for the start of the text
-
-		int tw = pw;
-		for (size_t j=0;j<(*txtg)[i].size();j++) //blit text
-		{
-			SDL_Surface *surf = (*txtg)[i][j];
-			blitAt(surf, tw, curh, ret);
-			tw+=surf->w;
-			SDL_FreeSurface(surf);
-			(*txtg)[i][j] = NULL;
-		}
-	}
-
-	return ret;
-}
-
-SDL_Surface * FNT_RenderText (EFonts font, std::string text, SDL_Color kolor= Colors::WHITE)
-{
-	if (graphics->fontsTrueType[font])
-		return TTF_RenderText_Blended(graphics->fontsTrueType[font], text.c_str(), kolor);
-	const Font *f = graphics->fonts[font];
-	int w = f->getWidth(text.c_str()),
-	    h = f->height;
-	SDL_Surface * ret = CSDL_Ext::newSurface(w, h, screen);
-	CSDL_Ext::fillRect (ret, NULL, SDL_MapRGB(ret->format,128,128,128));//if use default black - no shadowing
-	SDL_SetColorKey(ret,SDL_SRCCOLORKEY,SDL_MapRGB(ret->format,128,128,128));
-	CSDL_Ext::printAt(text.c_str(), 0, 0, font, kolor, ret);
-	return ret;
-}
-
-std::vector<std::vector<SDL_Surface*> > * CMessage::drawText(std::vector<std::string> * brtext, int &fontHeigh, EFonts font)
-{
-	std::vector<std::vector<SDL_Surface*> > * txtg = new std::vector<std::vector<SDL_Surface*> >();
-	txtg->resize(brtext->size());
-	if (graphics->fontsTrueType[font])
-		fontHeigh = TTF_FontHeight(graphics->fontsTrueType[font]);
-	else
-		fontHeigh = graphics->fonts[font]->height;
-
-	for (size_t i=0; i<brtext->size();i++) //foreach line
-	{
-		while((*brtext)[i].length()) //if something left
-		{
-			size_t z;
-
-			/* Handle normal text. */
-			z = 0;
-			while(z < (*brtext)[i].length() && (*brtext)[i][z] != ('{'))
-				z++;
-
-			if (z)
-				(*txtg)[i].push_back(FNT_RenderText(font, (*brtext)[i].substr(0,z), Colors::WHITE));
-			(*brtext)[i].erase(0,z);
-
-			if ((*brtext)[i].length() && (*brtext)[i][0] == '{')
-				/* Remove '{' */
-				(*brtext)[i].erase(0,1);
-
-			if ((*brtext)[i].length()==0)
-				/* End of line */
-				continue;
-
-			/* This text will be highlighted. */
-			z = 0;
-			while(z < (*brtext)[i].length() && (*brtext)[i][z] != ('}'))
-				z++;
-
-			if (z)
-				(*txtg)[i].push_back(FNT_RenderText(font, (*brtext)[i].substr(0,z), Colors::YELLOW));
-			(*brtext)[i].erase(0,z);
-
-			if ((*brtext)[i].length() && (*brtext)[i][0] == '}')
-				/* Remove '}' */
-				(*brtext)[i].erase(0,1);
-
-		} //ends while((*brtext)[i].length())
-	} //ends for(int i=0; i<brtext->size();i++)
-	return txtg;
-}
-
-SDL_Surface * CMessage::drawBoxTextBitmapSub( int player, std::string text, SDL_Surface* bitmap, std::string sub, int charperline/*=30*/, int imgToBmp/*=55*/ )
-{
-	int curh;
-	int fontHeight;
-	std::vector<std::string> tekst = breakText(text,charperline);
-	std::vector<std::vector<SDL_Surface*> > * txtg = drawText(&tekst, fontHeight);
-	std::pair<int,int> txts = getMaxSizes(txtg, fontHeight), boxs;
-	boxs.first = std::max(txts.first,bitmap->w) // text/bitmap max width
-		+ 50; //side margins
-	boxs.second =
-		(curh=45) //top margin
-		+ txts.second //text total height
-		+ imgToBmp //text <=> img
-		+ bitmap->h
-		+ 5 // to sibtitle
-		+ (*txtg)[0][0]->h
-		+ 30;
-	SDL_Surface *ret = drawDialogBox(boxs.first,boxs.second,player);
-	blitTextOnSur(txtg,fontHeight,curh,ret);
-	curh += imgToBmp;
-	blitAt(bitmap,(ret->w/2)-(bitmap->w/2),curh,ret);
-	curh += bitmap->h + 5;
-	CSDL_Ext::printAtMiddle(sub,ret->w/2,curh+10,FONT_SMALL,Colors::WHITE,ret);
-	delete txtg;
-	return ret;
-}
-
 void CMessage::drawIWindow(CInfoWindow * ret, std::string text, int player)
 {
-	SDL_Surface * _or = NULL;
-
+	bool blitOr = false;
 	if(dynamic_cast<CSelWindow*>(ret)) //it's selection window, so we'll blit "or" between components
-		_or = FNT_RenderText(FONT_MEDIUM,CGI->generaltexth->allTexts[4],Colors::WHITE);
+		blitOr = true;
 
 	const int sizes[][2] = {{400, 125}, {500, 150}, {600, 200}, {480, 400}};
 
@@ -392,7 +242,7 @@ void CMessage::drawIWindow(CInfoWindow * ret, std::string text, int player)
 
 	std::pair<int,int> winSize(ret->text->pos.w, ret->text->pos.h); //start with text size
 
-	ComponentsToBlit comps(ret->components,500,_or);
+	ComponentsToBlit comps(ret->components,500, blitOr);
 	if (ret->components.size())
 		winSize.second += 10 + comps.h; //space to first component
 
@@ -436,7 +286,7 @@ void CMessage::drawIWindow(CInfoWindow * ret, std::string text, int player)
 	if (ret->components.size())
 	{
 		curh += BEFORE_COMPONENTS;
-		comps.blitCompsOnSur (_or, BETWEEN_COMPS, curh, ret->bitmap);
+		comps.blitCompsOnSur (blitOr, BETWEEN_COMPS, curh, ret->bitmap);
 	}
 	if(ret->buttons.size())
 	{
@@ -452,9 +302,6 @@ void CMessage::drawIWindow(CInfoWindow * ret, std::string text, int player)
 	}
 	for(size_t i=0; i<ret->components.size(); i++)
 		ret->components[i]->moveBy(Point(ret->pos.x, ret->pos.y));
-
-	if(_or)
-		SDL_FreeSurface(_or);
 }
 
 void CMessage::drawBorder(int playerColor, SDL_Surface * ret, int w, int h, int x, int y)
@@ -562,8 +409,10 @@ ComponentsToBlit::~ComponentsToBlit()
 
 }
 
-ComponentsToBlit::ComponentsToBlit(std::vector<CComponent*> & SComps, int maxw, SDL_Surface* _or)
+ComponentsToBlit::ComponentsToBlit(std::vector<CComponent*> & SComps, int maxw, bool blitOr)
 {
+	int orWidth = graphics->fonts[FONT_MEDIUM]->getStringWidth(CGI->generaltexth->allTexts[4]);
+
 	w = h = 0;
 	if(SComps.empty())
 		return;
@@ -576,7 +425,7 @@ ComponentsToBlit::ComponentsToBlit(std::vector<CComponent*> & SComps, int maxw,
 	{
 		ComponentResolved *cur = new ComponentResolved(SComps[i]);
 
-		int toadd = (cur->pos.w + BETWEEN_COMPS + (_or ? _or->w : 0));
+		int toadd = (cur->pos.w + BETWEEN_COMPS + (blitOr ? orWidth : 0));
 		if (curw + toadd > maxw)
 		{
 			curr++;
@@ -603,8 +452,10 @@ ComponentsToBlit::ComponentsToBlit(std::vector<CComponent*> & SComps, int maxw,
 	}
 }
 
-void ComponentsToBlit::blitCompsOnSur( SDL_Surface * _or, int inter, int &curh, SDL_Surface *ret )
+void ComponentsToBlit::blitCompsOnSur( bool blitOr, int inter, int &curh, SDL_Surface *ret )
 {
+	int orWidth = graphics->fonts[FONT_MEDIUM]->getStringWidth(CGI->generaltexth->allTexts[4]);
+
 	for (size_t i=0;i<comps.size();i++)//for each row
 	{
 		int totalw=0, maxHeight=0;
@@ -616,8 +467,8 @@ void ComponentsToBlit::blitCompsOnSur( SDL_Surface * _or, int inter, int &curh,
 		}
 
 		//add space between comps in this row
-		if(_or)
-			totalw += (inter*2+_or->w) * ((comps)[i].size() - 1);
+		if(blitOr)
+			totalw += (inter*2+orWidth) * ((comps)[i].size() - 1);
 		else
 			totalw += (inter) * ((comps)[i].size() - 1);
 
@@ -636,11 +487,14 @@ void ComponentsToBlit::blitCompsOnSur( SDL_Surface * _or, int inter, int &curh,
 			//if there is subsequent component blit "or"
 			if(j<((comps)[i].size()-1))
 			{
-				if(_or)
+				if(blitOr)
 				{
 					curw+=inter;
-					blitAt(_or,curw,middleh-(_or->h/2),ret);
-					curw+=_or->w;
+
+					graphics->fonts[FONT_MEDIUM]->renderTextLeft(ret, CGI->generaltexth->allTexts[4], Colors::WHITE,
+					        Point(curw,middleh-(graphics->fonts[FONT_MEDIUM]->getLineHeight()/2)));
+
+					curw+=orWidth;
 				}
 				curw+=inter;
 			}

+ 1 - 7
client/CMessage.h

@@ -1,6 +1,6 @@
 #pragma once
 
-#include "FontBase.h"
+#include "Graphics.h"
 #include "UIFramework/Geometries.h"
 
 
@@ -28,7 +28,6 @@ class CMessage
 public:
 	//Function usd only in CMessage.cpp
 	static std::pair<int,int> getMaxSizes(std::vector<std::vector<SDL_Surface*> > * txtg, int fontHeight);
-	static std::vector<std::vector<SDL_Surface*> > * drawText(std::vector<std::string> * brtext, int &fontHeigh, EFonts font = FONT_MEDIUM);
 	static SDL_Surface * blitTextOnSur(std::vector<std::vector<SDL_Surface*> > * txtg, int fontHeight, int & curh, SDL_Surface * ret, int xCenterPos=-1); //xPos==-1 works as if ret->w/2
 
 	/// Draw border on exiting surface
@@ -37,14 +36,9 @@ public:
 	/// Draw simple dialog box (borders and background only)
 	static SDL_Surface * drawDialogBox(int w, int h, int playerColor=1);
 
-	/// Draw simple dialog box and blit bitmap with text on it
-	static SDL_Surface * drawBoxTextBitmapSub(int player, std::string text, SDL_Surface* bitmap, std::string sub, int charPerline=30, int imgToBmp=55);
-
-
 	static void drawIWindow(CInfoWindow * ret, std::string text, int player);
 
 	/// split text in lines
-	static std::vector<std::string> breakText(std::string text, size_t maxLineSize=30, const boost::function<int(char)> &charMetric = boost::function<int(char)>(), bool allowLeadingWhitespace = false);
 	static std::vector<std::string> breakText(std::string text, size_t maxLineWidth, EFonts font);
 
 	/// constructor

+ 39 - 40
client/CPreGame.cpp

@@ -1369,7 +1369,6 @@ void SelectionTab::printMaps(SDL_Surface *to)
 
 
 	SDL_Color itemColor;
-#define POS(xx, yy) pos.x + xx, pos.y + yy + line*25
 	for (int line = 0; line < positions  &&  elemIdx < curItems.size(); elemIdx++, line++)
 	{
 		CMapInfo *currentItem = curItems[elemIdx];
@@ -1384,7 +1383,7 @@ void SelectionTab::printMaps(SDL_Surface *to)
 			//amount of players
 			std::ostringstream ostr(std::ostringstream::out);
 			ostr << currentItem->playerAmnt << "/" << currentItem->humanPlayers;
-			CSDL_Ext::printAt(ostr.str(), POS(29, 120), FONT_SMALL, itemColor, to);
+			printAtLoc(ostr.str(), 29, 120 + line * 25, FONT_SMALL, itemColor, to);
 
 			//map size
 			std::string temp2 = "C";
@@ -1403,7 +1402,7 @@ void SelectionTab::printMaps(SDL_Surface *to)
 				temp2="XL";
 				break;
 			}
-			CSDL_Ext::printAtMiddle(temp2, POS(70, 128), FONT_SMALL, itemColor, to);
+			printAtMiddleLoc(temp2, 70, 128 + line * 25, FONT_SMALL, itemColor, to);
 
 			int temp=-1;
 			switch (currentItem->mapHeader->version)
@@ -1425,28 +1424,28 @@ void SelectionTab::printMaps(SDL_Surface *to)
 				tlog2 << "Warning: " << currentItem->fileURI << " has wrong version!\n";
 				continue;
 			}
-			blitAt(format->ourImages[temp].bitmap, POS(88, 117), to);
+			blitAtLoc(format->ourImages[temp].bitmap, 88, 117 + line * 25, to);
 
 			//victory conditions
 			if (currentItem->mapHeader->victoryCondition.condition == EVictoryConditionType::WINSTANDARD)
 				temp = 11;
 			else
 				temp = currentItem->mapHeader->victoryCondition.condition;
-			blitAt(CGP->victory->ourImages[temp].bitmap, POS(306, 117), to);
+			blitAtLoc(CGP->victory->ourImages[temp].bitmap, 306, 117 + line * 25, to);
 
 			//loss conditions
 			if (currentItem->mapHeader->lossCondition.typeOfLossCon == ELossConditionType::LOSSSTANDARD)
 				temp=3;
 			else
 				temp=currentItem->mapHeader->lossCondition.typeOfLossCon;
-			blitAt(CGP->loss->ourImages[temp].bitmap, POS(339, 117), to);
+			blitAtLoc(CGP->loss->ourImages[temp].bitmap, 339, 117 + line * 25, to);
 		}
 		else //if campaign
 		{
 			//number of maps in campaign
 			std::ostringstream ostr(std::ostringstream::out);
 			ostr << CGI->generaltexth->campaignRegionNames[ currentItem->campaignHeader->mapVersion ].size();
-			CSDL_Ext::printAt(ostr.str(), POS(29, 120), FONT_SMALL, itemColor, to);
+			printAtLoc(ostr.str(), 29, 120 + line * 25, FONT_SMALL, itemColor, to);
 		}
 
 		std::string name;
@@ -1467,10 +1466,8 @@ void SelectionTab::printMaps(SDL_Surface *to)
 		}
 
 		//print name
-		CSDL_Ext::printAtMiddle(CSDL_Ext::trimToFit(name, 185, FONT_SMALL), POS(213, 128), FONT_SMALL, itemColor, to);
-
+		printAtMiddleLoc(name, 213, 128 + line * 25, FONT_SMALL, itemColor, to);
 	}
-#undef POS
 }
 
 void SelectionTab::showAll(SDL_Surface * to)
@@ -1490,14 +1487,14 @@ void SelectionTab::showAll(SDL_Surface * to)
 		title = CGI->generaltexth->arraytxt[231];
 		break;
 	case CMenuScreen::campaignList:
-		title = "Select a Campaign"; //TODO: find where is the title
+		title = CGI->generaltexth->arraytxt[726];
 		break;
 	}
 
-	CSDL_Ext::printAtMiddle(title, pos.x+205, pos.y+28, FONT_MEDIUM, Colors::YELLOW, to); //Select a Scenario to Play
+	printAtMiddleLoc(title, 205, 28, FONT_MEDIUM, Colors::YELLOW, to); //Select a Scenario to Play
 	if(tabType != CMenuScreen::campaignList)
 	{
-		CSDL_Ext::printAtMiddle(CGI->generaltexth->allTexts[510], pos.x+87, pos.y+62, FONT_SMALL, Colors::YELLOW, to); //Map sizes
+		printAtMiddleLoc(CGI->generaltexth->allTexts[510], 87, 62, FONT_SMALL, Colors::YELLOW, to); //Map sizes
 	}
 }
 
@@ -1871,13 +1868,12 @@ CChatBox::CChatBox(const Rect &rect)
 	addUsedEvents(KEYBOARD);
 	captureAllKeys = true;
 
-	const int height = graphics->fonts[FONT_SMALL]->height;
+	const int height = graphics->fonts[FONT_SMALL]->getLineHeight();
 	inputBox = new CTextInput(Rect(0, rect.h - height, rect.w, height));
 	inputBox->removeUsedEvents(KEYBOARD);
 	chatHistory = new CTextBox("", Rect(0, 0, rect.w, rect.h - height), 1);
 
-	SDL_Color green = {0,252,0, SDL_ALPHA_OPAQUE};
-	chatHistory->color = green;
+	chatHistory->color = Colors::GREEN;
 }
 
 void CChatBox::keyPressed(const SDL_KeyboardEvent & key)
@@ -1989,7 +1985,7 @@ void InfoCard::showAll(SDL_Surface * to)
 			{
 				if(i->second.playerID != PlayerSettings::PLAYER_AI)
 				{
-					printAtLoc(i->second.name, 24, 285 + playerSoFar++ * graphics->fonts[FONT_SMALL]->height, FONT_SMALL, Colors::WHITE, to);
+					printAtLoc(i->second.name, 24, 285 + playerSoFar++ * graphics->fonts[FONT_SMALL]->getLineHeight(), FONT_SMALL, Colors::WHITE, to);
 					playerNames.erase(i->second.playerID);
 				}
 			}
@@ -1997,7 +1993,7 @@ void InfoCard::showAll(SDL_Surface * to)
 			playerSoFar = 0;
 			for (auto i = playerNames.cbegin(); i != playerNames.cend(); i++)
 			{
-				printAtLoc(i->second, 193, 285 + playerSoFar++ * graphics->fonts[FONT_SMALL]->height, FONT_SMALL, Colors::WHITE, to);
+				printAtLoc(i->second, 193, 285 + playerSoFar++ * graphics->fonts[FONT_SMALL]->getLineHeight(), FONT_SMALL, Colors::WHITE, to);
 			}
 
 		}
@@ -2064,8 +2060,8 @@ void InfoCard::showAll(SDL_Surface * to)
 				printToLoc((static_cast<const CMapInfo*>(SEL->current))->date,308,34, FONT_SMALL, Colors::WHITE, to);
 
 			//print flags
-			int fx = 34  + graphics->fonts[FONT_SMALL]->getWidth(CGI->generaltexth->allTexts[390].c_str());
-			int ex = 200 + graphics->fonts[FONT_SMALL]->getWidth(CGI->generaltexth->allTexts[391].c_str());
+			int fx = 34  + graphics->fonts[FONT_SMALL]->getStringWidth(CGI->generaltexth->allTexts[390]);
+			int ex = 200 + graphics->fonts[FONT_SMALL]->getStringWidth(CGI->generaltexth->allTexts[391]);
 
 			int myT;
 
@@ -2117,7 +2113,7 @@ void InfoCard::showAll(SDL_Surface * to)
 
 		//name
 		if (name.length())
-			printAtLoc(CSDL_Ext::trimToFit(name, 300, FONT_BIG), 26, 39, FONT_BIG, Colors::YELLOW, to);
+			printAtLoc(name, 26, 39, FONT_BIG, Colors::YELLOW, to);
 		else
 			printAtLoc("Unnamed", 26, 39, FONT_BIG, Colors::YELLOW, to);
 	}
@@ -2150,14 +2146,16 @@ void InfoCard::clickRight( tribool down, bool previousState )
 void InfoCard::showTeamsPopup()
 {
 	SDL_Surface *bmp = CMessage::drawDialogBox(256, 90 + 50 * SEL->current->mapHeader->howManyTeams);
-	CSDL_Ext::printAtMiddle(CGI->generaltexth->allTexts[657], 128, 30, FONT_MEDIUM, Colors::YELLOW, bmp); //{Team Alignments}
+
+	graphics->fonts[FONT_MEDIUM]->renderTextCenter(bmp, CGI->generaltexth->allTexts[657], Colors::YELLOW, Point(128, 30));
 
 	for(int i = 0; i < SEL->current->mapHeader->howManyTeams; i++)
 	{
 		std::vector<ui8> flags;
 		std::string hlp = CGI->generaltexth->allTexts[656]; //Team %d
 		hlp.replace(hlp.find("%d"), 2, boost::lexical_cast<std::string>(i+1));
-		CSDL_Ext::printAtMiddle(hlp, 128, 65 + 50*i, FONT_SMALL, Colors::WHITE, bmp);
+
+		graphics->fonts[FONT_SMALL]->renderTextCenter(bmp, hlp, Colors::WHITE, Point(128, 65 + 50 * i));
 
 		for(int j = 0; j < GameConstants::PLAYER_LIMIT; j++)
 			if((SEL->current->mapHeader->players[j].canHumanPlay || SEL->current->mapHeader->players[j].canComputerPlay)
@@ -2224,11 +2222,11 @@ void OptionsTab::showAll(SDL_Surface * to)
 {
 	CIntObject::showAll(to);
 	printAtMiddleLoc(CGI->generaltexth->allTexts[515], 222, 30, FONT_BIG, Colors::YELLOW, to);
-	printAtMiddleWBLoc(CGI->generaltexth->allTexts[516], 222, 58, FONT_SMALL, 55, Colors::WHITE, to); //Select starting options, handicap, and name for each player in the game.
-	printAtMiddleWBLoc(CGI->generaltexth->allTexts[517], 107, 102, FONT_SMALL, 14, Colors::YELLOW, to); //Player Name Handicap Type
-	printAtMiddleWBLoc(CGI->generaltexth->allTexts[518], 197, 102, FONT_SMALL, 10, Colors::YELLOW, to); //Starting Town
-	printAtMiddleWBLoc(CGI->generaltexth->allTexts[519], 273, 102, FONT_SMALL, 10, Colors::YELLOW, to); //Starting Hero
-	printAtMiddleWBLoc(CGI->generaltexth->allTexts[520], 349, 102, FONT_SMALL, 10, Colors::YELLOW, to); //Starting Bonus
+	printAtMiddleWBLoc(CGI->generaltexth->allTexts[516], 222, 68, FONT_SMALL, 300, Colors::WHITE, to); //Select starting options, handicap, and name for each player in the game.
+	printAtMiddleWBLoc(CGI->generaltexth->allTexts[517], 107, 110, FONT_SMALL, 100, Colors::YELLOW, to); //Player Name Handicap Type
+	printAtMiddleWBLoc(CGI->generaltexth->allTexts[518], 197, 110, FONT_SMALL, 70, Colors::YELLOW, to); //Starting Town
+	printAtMiddleWBLoc(CGI->generaltexth->allTexts[519], 273, 110, FONT_SMALL, 70, Colors::YELLOW, to); //Starting Hero
+	printAtMiddleWBLoc(CGI->generaltexth->allTexts[520], 349, 110, FONT_SMALL, 70, Colors::YELLOW, to); //Starting Bonus
 	printAtMiddleLoc(CGI->generaltexth->allTexts[521], 222, 538, FONT_SMALL, Colors::YELLOW, to); // Player Turn Duration
 	if (turnDuration)
 		printAtMiddleLoc(CGI->generaltexth->turnDurations[turnDuration->value], 319,559, FONT_SMALL, Colors::WHITE, to);//Turn duration value
@@ -2558,7 +2556,7 @@ void OptionsTab::PlayerOptionsEntry::showAll(SDL_Surface * to)
 {
 	CIntObject::showAll(to);
 	printAtMiddleLoc(s.name, 55, 10, FONT_SMALL, Colors::WHITE, to);
-	printAtMiddleWBLoc(CGI->generaltexth->arraytxt[206+whoCanPlay], 28, 34, FONT_TINY, 8, Colors::WHITE, to);
+	printAtMiddleWBLoc(CGI->generaltexth->arraytxt[206+whoCanPlay], 28, 39, FONT_TINY, 50, Colors::WHITE, to);
 }
 
 void OptionsTab::PlayerOptionsEntry::update()
@@ -3130,16 +3128,15 @@ void CBonusSelection::init()
 
 	//campaign name
 	if (ourCampaign->camp->header.name.length())
-		CSDL_Ext::printAt(ourCampaign->camp->header.name, 481, 28, FONT_BIG, Colors::YELLOW, background);
+		graphics->fonts[FONT_BIG]->renderTextLeft(background, ourCampaign->camp->header.name, Colors::YELLOW, Point(481, 28));
 	else
-		CSDL_Ext::printAt("Unnamed", 481, 28, FONT_BIG, Colors::YELLOW, background);
+		graphics->fonts[FONT_BIG]->renderTextLeft(background, CGI->generaltexth->allTexts[508], Colors::YELLOW, Point(481, 28));
 
 	//map size icon
 	sizes = CDefHandler::giveDef("SCNRMPSZ.DEF");
 
-
 	//campaign description
-	CSDL_Ext::printAt(CGI->generaltexth->allTexts[38], 481, 63, FONT_SMALL, Colors::YELLOW, background);
+	graphics->fonts[FONT_SMALL]->renderTextLeft(background, CGI->generaltexth->allTexts[38], Colors::YELLOW, Point(481, 63));
 
 	cmpgDesc = new CTextBox(ourCampaign->camp->header.description, Rect(480, 86, 286, 117), 1);
 	//cmpgDesc->showAll(background);
@@ -3148,7 +3145,7 @@ void CBonusSelection::init()
 	mapDesc = new CTextBox("", Rect(480, 280, 286, 117), 1);
 
 	//bonus choosing
-	CSDL_Ext::printAt(CGI->generaltexth->allTexts[71], 511, 432, FONT_MEDIUM, Colors::WHITE, background); //Choose a bonus:
+	graphics->fonts[FONT_MEDIUM]->renderTextLeft(background, CGI->generaltexth->allTexts[71], Colors::WHITE, Point(511, 432));
 	bonuses = new CHighlightableButtonsGroup(bind(&CBonusSelection::selectBonus, this, _1));
 
 	//set left part of window
@@ -3181,15 +3178,15 @@ void CBonusSelection::init()
 // 	}
 
 	//allies / enemies
-	CSDL_Ext::printAt(CGI->generaltexth->allTexts[390] + ":", 486, 407, FONT_SMALL, Colors::WHITE, background); //Allies
-	CSDL_Ext::printAt(CGI->generaltexth->allTexts[391] + ":", 619, 407, FONT_SMALL, Colors::WHITE, background); //Enemies
+	graphics->fonts[FONT_SMALL]->renderTextLeft(background, CGI->generaltexth->allTexts[390] + ":", Colors::WHITE, Point(486, 407));
+	graphics->fonts[FONT_SMALL]->renderTextLeft(background, CGI->generaltexth->allTexts[391] + ":", Colors::WHITE, Point(619, 407));
 
 	SDL_FreeSurface(panel);
 
 	//difficulty
 	std::vector<std::string> difficulty;
 	boost::split(difficulty, CGI->generaltexth->allTexts[492], boost::is_any_of(" "));
-	CSDL_Ext::printAt(difficulty.back(), 689, 432, FONT_MEDIUM, Colors::WHITE, background); //Difficulty
+	graphics->fonts[FONT_MEDIUM]->renderTextLeft(background, difficulty.back(), Colors::WHITE, Point(689, 432));
 
 	//difficulty pics
 	for (int b=0; b<ARRAY_COUNT(diffPics); ++b)
@@ -3350,8 +3347,8 @@ void CBonusSelection::show(SDL_Surface * to)
 	blitAtLoc(sizes->ourImages[temp].bitmap, 735, 26, to);
 
 	//flags
-	int fx = 496  + graphics->fonts[FONT_SMALL]->getWidth(CGI->generaltexth->allTexts[390].c_str());
-	int ex = 629 + graphics->fonts[FONT_SMALL]->getWidth(CGI->generaltexth->allTexts[391].c_str());
+	int fx = 496  + graphics->fonts[FONT_SMALL]->getStringWidth(CGI->generaltexth->allTexts[390]);
+	int ex = 629 + graphics->fonts[FONT_SMALL]->getStringWidth(CGI->generaltexth->allTexts[391]);
 	int myT;
 	myT = ourHeader->players[playerColor].team;
 	for (auto i = sInfo.playerInfos.cbegin(); i != sInfo.playerInfos.cend(); i++)
@@ -4045,7 +4042,9 @@ std::string CLoadingScreen::getBackground()
 CLoadingScreen::CLoadingScreen(boost::function<void ()> loader):
     CWindowObject(BORDERED, getBackground()),
     loadingThread(loader)
-{}
+{
+	CCS->musich->stopMusic(5000);
+}
 
 CLoadingScreen::~CLoadingScreen()
 {

+ 6 - 10
client/CSpellWindow.cpp

@@ -321,7 +321,7 @@ void CSpellWindow::showAll(SDL_Surface * to)
 
 	std::ostringstream mana;
 	mana<<myHero->mana;
-	CSDL_Ext::printAtMiddle(mana.str(), pos.x+435, pos.y +426, FONT_SMALL, Colors::YELLOW, to);
+	printAtMiddleLoc(mana.str(), 435, 426, FONT_SMALL, Colors::YELLOW, to);
 	
 	statusBar->showAll(to);
 
@@ -800,12 +800,8 @@ void CSpellWindow::SpellArea::clickRight(tribool down, bool previousState)
 			boost::algorithm::replace_first(dmgInfo, "%d", boost::lexical_cast<std::string>(causedDmg));
 		}
 
-		SDL_Surface *spellBox = CMessage::drawBoxTextBitmapSub(
-			owner->myInt->playerID,
-			CGI->spellh->spells[mySpell]->descriptions[schoolLevel] + dmgInfo, this->owner->spells->ourImages[mySpell].bitmap,
-			CGI->spellh->spells[mySpell]->name,30,30);
-		CInfoPopup *vinya = new CInfoPopup(spellBox, true);
-		GH.pushInt(vinya);
+		CRClickPopup::createAndPush(CGI->spellh->spells[mySpell]->descriptions[schoolLevel] + dmgInfo,
+		                            new CComponent(CComponent::spell, mySpell));
 	}
 }
 
@@ -850,13 +846,13 @@ void CSpellWindow::SpellArea::showAll(SDL_Surface * to)
 		secondLineColor = Colors::WHITE;
 	}
 	//printing spell's name
-	CSDL_Ext::printAtMiddle(spell->name, pos.x + 39, pos.y + 70, FONT_TINY, firstLineColor, to);
+	printAtMiddleLoc(spell->name, 39, 70, FONT_TINY, firstLineColor, to);
 	//printing lvl
-	CSDL_Ext::printAtMiddle(CGI->generaltexth->allTexts[171 + spell->level], pos.x + 39, pos.y + 82, FONT_TINY, secondLineColor, to);
+	printAtMiddleLoc(CGI->generaltexth->allTexts[171 + spell->level], 39, 82, FONT_TINY, secondLineColor, to);
 	//printing  cost
 	std::ostringstream ss;
 	ss << CGI->generaltexth->allTexts[387] << ": " << spellCost;
-	CSDL_Ext::printAtMiddle(ss.str(), pos.x + 39, pos.y + 94, FONT_TINY, secondLineColor, to);
+	printAtMiddleLoc(ss.str(), 39, 94, FONT_TINY, secondLineColor, to);
 }
 
 void CSpellWindow::SpellArea::setSpell(int spellID)

+ 0 - 35
client/FontBase.h

@@ -1,35 +0,0 @@
-#pragma once
-
-/*
- * FontBase.h, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
-
-enum EFonts
-{
-	FONT_BIG, FONT_CALLI, FONT_CREDITS, FONT_HIGH_SCORE, FONT_MEDIUM, FONT_SMALL, FONT_TIMES, FONT_TINY, FONT_VERD
-};
-
-struct Font
-{
-	struct Char
-	{
-		si32 leftOffset, width, rightOffset;
-		ui8 *pixels;
-	};
-
-	Char chars[256];
-	ui8 height;
-
-	ui8 *data;
-
-	Font(ui8 *Data);
-	~Font();
-	int getWidth(const char *text) const;
-	int getCharWidth(char c) const;
-};

+ 17 - 18
client/GUIClasses.cpp

@@ -880,7 +880,7 @@ void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize)
 	std::vector<std::string> textLines = CMessage::breakText(getSubtitle(), std::max<int>(80, pos.w), font);
 	BOOST_FOREACH(auto & line, textLines)
 	{
-		int height = graphics->fonts[font]->height;
+		int height = graphics->fonts[font]->getLineHeight();
 		CLabel * label = new CLabel(pos.w/2, pos.h + height/2, font, CENTER, Colors::WHITE, line);
 
 		pos.h += height;
@@ -1748,24 +1748,22 @@ void CMinorResDataBar::show(SDL_Surface * to)
 void CMinorResDataBar::showAll(SDL_Surface * to)
 {
 	blitAt(bg,pos.x,pos.y,to);
-	char buf[30];
 	for (int i=0;i<7;i++)
 	{
-		SDL_itoa(LOCPLINT->cb->getResourceAmount(i),buf,10);
-		CSDL_Ext::printAtMiddle(buf,pos.x + 50 + 76*i,pos.y+pos.h/2,FONT_SMALL,Colors::WHITE,to);
+		std::string text = boost::lexical_cast<std::string>(LOCPLINT->cb->getResourceAmount(i));
+
+		graphics->fonts[FONT_SMALL]->renderTextCenter(to, text, Colors::WHITE, Point(pos.x + 50 + 76 * i, pos.y + pos.h/2));
 	}
 	std::vector<std::string> temp;
-	SDL_itoa(LOCPLINT->cb->getDate(3),buf,10); temp.push_back(std::string(buf));
-	SDL_itoa(LOCPLINT->cb->getDate(2),buf,10); temp.push_back(buf);
-	SDL_itoa(LOCPLINT->cb->getDate(1),buf,10); temp.push_back(buf);
-	CSDL_Ext::printAtMiddle(CSDL_Ext::processStr(
-		CGI->generaltexth->allTexts[62]
-	+": %s, "
-		+ CGI->generaltexth->allTexts[63]
-	+ ": %s, "
-		+	CGI->generaltexth->allTexts[64]
-	+ ": %s",temp)
-		,pos.x+545+(pos.w-545)/2,pos.y+pos.h/2,FONT_SMALL,Colors::WHITE,to);
+
+	temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(3)));
+	temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(2)));
+	temp.push_back(boost::lexical_cast<std::string>(LOCPLINT->cb->getDate(1)));
+
+	std::string datetext =  CGI->generaltexth->allTexts[62]+": %s, " + CGI->generaltexth->allTexts[63]
+	                        + ": %s, " + CGI->generaltexth->allTexts[64] + ": %s";
+
+	graphics->fonts[FONT_SMALL]->renderTextCenter(to, processStr(datetext,temp), Colors::WHITE, Point(pos.x+545+(pos.w-545)/2,pos.y+pos.h/2));
 }
 
 CMinorResDataBar::CMinorResDataBar()
@@ -3663,7 +3661,7 @@ void CTavernWindow::show(SDL_Surface * to)
 			boost::algorithm::replace_first(recruit->hoverTexts[0],"%s",sel->h->type->heroClass->name);
 		}
 
-		printAtMiddleWB(sel->descr,pos.x+146,pos.y+389,FONT_SMALL,40,Colors::WHITE,to);
+		printAtMiddleWBLoc(sel->descr, 146, 389, FONT_SMALL, 200, Colors::WHITE, to);
 		CSDL_Ext::drawBorder(to,sel->pos.x-2,sel->pos.y-2,sel->pos.w+4,sel->pos.h+4,int3(247,223,123));
 	}
 }
@@ -3728,13 +3726,14 @@ void CInGameConsole::show(SDL_Surface * to)
 	boost::unique_lock<boost::mutex> lock(texts_mx);
 	for(std::list< std::pair< std::string, int > >::iterator it = texts.begin(); it != texts.end(); ++it, ++number)
 	{
-		SDL_Color green = {0,0xff,0,0};
 		Point leftBottomCorner(0, screen->h);
 		if(LOCPLINT->battleInt)
 		{
 			leftBottomCorner = LOCPLINT->battleInt->pos.bottomLeft();
 		}
-		CSDL_Ext::printAt(it->first, leftBottomCorner.x + 50, leftBottomCorner.y - texts.size() * 20 - 80 + number*20, FONT_MEDIUM, green);
+		graphics->fonts[FONT_MEDIUM]->renderTextLeft(to, it->first, Colors::GREEN,
+		    Point(leftBottomCorner.x + 50, leftBottomCorner.y - texts.size() * 20 - 80 + number*20));
+
 		if(SDL_GetTicks() - it->second > defaultTimeout)
 		{
 			toDel.push_back(it);

+ 13 - 85
client/Graphics.cpp

@@ -124,7 +124,6 @@ Graphics::Graphics()
 
 	std::vector<Task> tasks; //preparing list of graphics to load
 	tasks += boost::bind(&Graphics::loadFonts,this);
-	tasks += boost::bind(&Graphics::loadTrueType,this);
 	tasks += boost::bind(&Graphics::loadPaletteAndColors,this);
 	tasks += boost::bind(&Graphics::loadHeroFlags,this);
 	tasks += boost::bind(&Graphics::initializeBattleGraphics,this);
@@ -386,38 +385,24 @@ void Graphics::blueToPlayersAdv(SDL_Surface * sur, int player)
 	}
 }
 
-void Graphics::loadTrueType()
-{
-	for(int i = 0; i < FONTS_NUMBER; i++)
-		fontsTrueType[i] = NULL;
-}
-
-Font * Graphics::loadFont( const char * name )
+void Graphics::loadFonts()
 {
-	ui8 * hlp = CResourceHandler::get()->loadData(
-	                ResourceID(std::string("DATA/") + name, EResType::FONT)).first.release();
-	if(!hlp)
-	{
-		tlog1 << "Error: cannot load font: " << name << std::endl;
-		return NULL;
-	}
+	const JsonNode config(ResourceID("config/fonts.json"));
 
-	int magic =  SDL_SwapLE32(*(const Uint32*)hlp);
-	if(magic != 589598 && magic != 589599)
-		tlog1 << "Suspicious font file, fname " << name << "n";
+	const JsonVector & bmpConf = config["bitmap"].Vector();
+	const JsonNode   & ttfConf = config["trueType"];
 
-	Font *ret = new Font(hlp);
-	return ret;
-}
+	assert(bmpConf.size() == FONTS_NUMBER);
 
-void Graphics::loadFonts()
-{
-	static const char *fontnames [] = {"BIGFONT.FNT", "CALLI10R.FNT", "CREDITS.FNT", "HISCORE.FNT", "MEDFONT.FNT",
-								"SMALFONT.FNT", "TIMES08R.FNT", "TINY.FNT", "VERD10B.FNT"} ;
+	for (size_t i=0; i<FONTS_NUMBER; i++)
+	{
+		std::string filename = bmpConf[i].String();
 
-	assert(ARRAY_COUNT(fontnames) == FONTS_NUMBER);
-	for(int i = 0; i < FONTS_NUMBER; i++)
-		fonts[i] = loadFont(fontnames[i]);
+		if (ttfConf[filename].isNull()) // no ttf override
+			fonts[i] = new CBitmapFont(filename);
+		else
+			fonts[i] = new CTrueTypeFont(ttfConf[filename]);
+	}
 }
 
 CDefEssential * Graphics::getDef( const CGObjectInstance * obj )
@@ -447,60 +432,3 @@ void Graphics::loadErmuToPicture()
 	}
 	assert (etp_idx == 44);
 }
-
-Font::Font(ui8 *Data)
-{
-	data = Data;
-	int i = 0;
-
-	height = data[5];
-
-	i = 32;
-	for(int ci = 0; ci < 256; ci++)
-	{
-		chars[ci].leftOffset = read_le_u32(data + i); i+=4;
-		chars[ci].width = read_le_u32(data + i); i+=4;
-		chars[ci].rightOffset = read_le_u32(data + i); i+=4;
-
-		//if(ci>=30)
-		//	tlog0 << ci << ". (" << (char)ci << "). Width: " << chars[ci].width << " U1/U2:" << chars[ci].unknown1 << "/" << chars[ci].unknown2 << std::endl;
-	}
-	for(int ci = 0; ci < 256; ci++)
-	{
-		int offset =  read_le_u32(data + i); i+=4;
-		chars[ci].pixels = data + 4128 + offset;
-	}
-}
-
-Font::~Font()
-{
-	delete [] data;
-}
-
-int Font::getWidth(const char *text ) const
-{
-	int length = std::strlen(text);
-	int ret = 0;
-
-	for(int i = 0; i < length; i++)
-	{
-		ui8 c = text[i];
-		ret += chars[c].width + chars[c].leftOffset + chars[c].rightOffset;
-	}
-
-	return ret;
-}
-
-int Font::getCharWidth( char c ) const
-{
-	const Char &C = chars[(ui8)c];
-	return C.width + C.leftOffset + C.rightOffset;;
-}
-
-/*
-void Font::WriteAt(const char *text, SDL_Surface *sur, int x, int y )
-{
-	 SDL_Surface *SDL_CreateRGBSurfaceFrom(pixels, w, h, 8, int pitch,
-                        224, 28, 3, 0);
-}
-*/

+ 7 - 7
client/Graphics.h

@@ -1,7 +1,7 @@
 #pragma once
 
 
-#include "FontBase.h"
+#include "UIFramework/Fonts.h"
 #include "../lib/GameConstants.h"
 #include "UIFramework/Geometries.h"
 
@@ -27,7 +27,10 @@ struct InfoAboutTown;
 class CGObjectInstance;
 class CGDefInfo;
 
-typedef struct _TTF_Font TTF_Font; //from SDL_ttf.h
+enum EFonts
+{
+	FONT_BIG, FONT_CALLI, FONT_CREDITS, FONT_HIGH_SCORE, FONT_MEDIUM, FONT_SMALL, FONT_TIMES, FONT_TINY, FONT_VERD
+};
 
 /// Handles fonts, hero images, town images, various graphics
 class Graphics
@@ -35,9 +38,8 @@ class Graphics
 public:
 	//Fonts
 	static const int FONTS_NUMBER = 9;
-	Font *fonts[FONTS_NUMBER];
-	TTF_Font * fontsTrueType[FONTS_NUMBER];//true type fonts, if some of the fonts not loaded - NULL
-
+	IFont * fonts[FONTS_NUMBER];
+	\
 	//various graphics
 	SDL_Color * playerColors; //array [8]
 	SDL_Color * neutralColor;
@@ -77,9 +79,7 @@ public:
 	CDefEssential *  loadHeroAnim(const std::string &name, const std::vector<std::pair<int,int> > &rotations);
 	void loadErmuToPicture();
 	void blueToPlayersAdv(SDL_Surface * sur, int player); //replaces blue interface colour with a color of player
-	void loadTrueType();
 	void loadFonts();
-	Font *loadFont(const char * name);
 };
 
 extern Graphics * graphics;

+ 1 - 1
client/UIFramework/CGuiHandler.cpp

@@ -424,7 +424,7 @@ void CGuiHandler::drawFPSCounter()
 	Uint32 black = SDL_MapRGB(screen->format, 10, 10, 10);
 	SDL_FillRect(screen, &overlay, black);
 	std::string fps = boost::lexical_cast<std::string>(mainFPSmng->fps);
-	CSDL_Ext::printAt(fps, 10, 10, FONT_BIG, yellow, screen);
+	graphics->fonts[FONT_BIG]->renderTextLeft(screen, fps, yellow, Point(10, 10));
 }
 
 SDLKey CGuiHandler::arrowToNum( SDLKey key )

+ 6 - 5
client/UIFramework/CIntObject.cpp

@@ -2,6 +2,7 @@
 #include "CIntObject.h"
 #include "CGuiHandler.h"
 #include "SDL_Extensions.h"
+#include "CMessage.h"
 
 CIntObject::CIntObject(int used_, Point pos_):
 	parent_m(nullptr),
@@ -129,17 +130,17 @@ CIntObject::~CIntObject()
 
 void CIntObject::printAtLoc( const std::string & text, int x, int y, EFonts font, SDL_Color kolor/*=Colors::WHITE*/, SDL_Surface * dst/*=screen*/ )
 {
-	CSDL_Ext::printAt(text, pos.x + x, pos.y + y, font, kolor, dst);
+	graphics->fonts[font]->renderTextLeft(dst, text, kolor, Point(pos.x + x, pos.y + y));
 }
 
 void CIntObject::printAtMiddleLoc( const std::string & text, int x, int y, EFonts font, SDL_Color kolor/*=Colors::WHITE*/, SDL_Surface * dst/*=screen*/ )
 {
-	CSDL_Ext::printAtMiddle(text, pos.x + x, pos.y + y, font, kolor, dst);
+	printAtMiddleLoc(text, Point(x,y), font, kolor, dst);
 }
 
 void CIntObject::printAtMiddleLoc(const std::string & text, const Point &p, EFonts font, SDL_Color kolor, SDL_Surface * dst)
 {
-	printAtMiddleLoc(text, p.x, p.y, font, kolor, dst);
+	graphics->fonts[font]->renderTextCenter(dst, text, kolor, pos.topLeft() + p);
 }
 
 void CIntObject::blitAtLoc( SDL_Surface * src, int x, int y, SDL_Surface * dst )
@@ -154,12 +155,12 @@ void CIntObject::blitAtLoc(SDL_Surface * src, const Point &p, SDL_Surface * dst)
 
 void CIntObject::printAtMiddleWBLoc( const std::string & text, int x, int y, EFonts font, int charpr, SDL_Color kolor, SDL_Surface * dst)
 {
-	CSDL_Ext::printAtMiddleWB(text, pos.x + x, pos.y + y, font, charpr, kolor, dst);
+	graphics->fonts[font]->renderTextLinesCenter(dst, CMessage::breakText(text, charpr, font), kolor, Point(pos.x + x, pos.y + y));
 }
 
 void CIntObject::printToLoc( const std::string & text, int x, int y, EFonts font, SDL_Color kolor, SDL_Surface * dst )
 {
-	CSDL_Ext::printTo(text, pos.x + x, pos.y + y, font, kolor, dst);
+	graphics->fonts[font]->renderTextRight(dst, text, kolor, Point(pos.x + x, pos.y + y));
 }
 
 void CIntObject::addUsedEvents(ui16 newActions)

+ 1 - 1
client/UIFramework/CIntObject.h

@@ -2,7 +2,7 @@
 
 #include <SDL_events.h>
 #include "Geometries.h"
-#include "../FontBase.h"
+#include "../Graphics.h"
 
 struct SDL_Surface;
 class CPicture;

+ 40 - 27
client/UIFramework/CIntObjectClasses.cpp

@@ -1147,7 +1147,8 @@ void CStatusBar::show(SDL_Surface * to)
 	SDL_Rect srcRect = genRect(pos.h,pos.w,0,0);
 	SDL_Rect dstRect = genRect(pos.h,pos.w,pos.x,pos.y);
 	CSDL_Ext::blitSurface(bg,&srcRect,to,&dstRect);
-	CSDL_Ext::printAtMiddle(current,middlex,middley,FONT_SMALL,Colors::WHITE,to);
+
+	graphics->fonts[FONT_SMALL]->renderTextCenter(to, current, Colors::WHITE, Point(middlex, middley));
 }
 
 std::string CStatusBar::getCurrent()
@@ -1214,8 +1215,18 @@ void CLabel::showAll(SDL_Surface * to)
 	if(!toPrint.length())
 		return;
 
-	static void (*printer[3])(const std::string &, int, int, EFonts, SDL_Color, SDL_Surface *) = {&CSDL_Ext::printAt, &CSDL_Ext::printAtMiddle, &CSDL_Ext::printTo}; //array of printing functions
-	printer[alignment](toPrint, pos.x + textOffset.x, pos.y + textOffset.y, font, color, to);
+	switch (alignment)
+	{
+	break; case TOPLEFT :
+		CIntObject::printAtLoc(toPrint, textOffset.x, textOffset.y, font, color, to);
+	break; case CENTER :
+		CIntObject::printAtMiddleLoc(toPrint, textOffset.x, textOffset.y, font, color, to);
+	break; case BOTTOMRIGHT :
+		CIntObject::printToLoc(toPrint, textOffset.x, textOffset.y, font, color, to);
+	break; default :
+		assert(0);
+	}
+
 }
 
 CLabel::CLabel(int x, int y, EFonts Font /*= FONT_SMALL*/, EAlignment Align, const SDL_Color &Color /*= Colors::WHITE*/, const std::string &Text /*= ""*/)
@@ -1228,8 +1239,8 @@ CLabel::CLabel(int x, int y, EFonts Font /*= FONT_SMALL*/, EAlignment Align, con
 	bg = NULL;
 	ignoreLeadingWhitespace = false;
 
-	pos.w = graphics->fonts[font]->getWidth(text.c_str());
-	pos.h = graphics->fonts[font]->height;
+	pos.w = graphics->fonts[font]->getStringWidth(text.c_str());
+	pos.h = graphics->fonts[font]->getLineHeight();
 }
 
 std::string CLabel::visibleText()
@@ -1267,7 +1278,7 @@ void CBoundedLabel::setTxt(const std::string &Txt)
 
 void CBoundedLabel::blitLine(SDL_Surface *to, Point where, std::string what)
 {
-	const Font &f = *graphics->fonts[font];
+	const IFont * f = graphics->fonts[font];
 
 	size_t begin = 0;
 	size_t end;
@@ -1277,14 +1288,16 @@ void CBoundedLabel::blitLine(SDL_Surface *to, Point where, std::string what)
 	do
 	{
 		end = what.find_first_of(delimeters[currDelimeter % 2], begin);
-		std::string toPrint = what.substr(begin, end);
-		if (currDelimeter % 2)
-			CSDL_Ext::printAt(toPrint, where.x, where.y, font, Colors::YELLOW, to);
-		else
-			CSDL_Ext::printAt(toPrint, where.x, where.y, font, color, to);
-		begin = end;
-		where.x += f.getWidth(toPrint.c_str());
-
+		if (begin != end)
+		{
+			std::string toPrint = what.substr(begin, end-1);
+			if (currDelimeter % 2) // Enclosed in {} text - set to yellow
+				graphics->fonts[font]->renderTextLeft(to, toPrint, Colors::YELLOW, where);
+			else // Non-enclosed text
+				graphics->fonts[font]->renderTextLeft(to, toPrint, color, where);
+			begin = end;
+			where.x += f->getStringWidth(toPrint.c_str());
+		}
 		currDelimeter++;
 	}
 	while (begin++ != std::string::npos);
@@ -1294,11 +1307,11 @@ void CBoundedLabel::showAll(SDL_Surface * to)
 {
 	CIntObject::showAll(to);
 
-	const Font &f = *graphics->fonts[font];
-	int lineHeight =  f.height;
+	const IFont * f = graphics->fonts[font];
+	int lineHeight =  f->getLineHeight();
 	int lineCapacity = pos.h / lineHeight;
 
-	int dy = f.height; //line height
+	int dy = f->getLineHeight(); //line height
 	int base_y = pos.y;
 	if(alignment == CENTER)
 		base_y += std::max((pos.h - maxH)/2,0);
@@ -1312,7 +1325,7 @@ void CBoundedLabel::showAll(SDL_Surface * to)
 		int x = pos.x;
 		if(alignment == CENTER)
 		{
-			x += (pos.w - f.getWidth(line.c_str())) / 2;
+			x += pos.w - f->getStringWidth(line.c_str()) / 2;
 		}
 
 		blitLine(to, Point(x, base_y + i * dy), line);
@@ -1323,15 +1336,15 @@ void CBoundedLabel::recalculateLines(const std::string &Txt)
 {
 	lines.clear();
 
-	const Font &f = *graphics->fonts[font];
-	int lineHeight =  f.height;
+	const IFont * f = graphics->fonts[font];
+	int lineHeight =  f->getLineHeight();
 
 	lines = CMessage::breakText(Txt, pos.w, font);
 
 	maxH = lineHeight * lines.size();
 	maxW = 0;
 	BOOST_FOREACH(const std::string &line, lines)
-		vstd::amax(maxW, f.getWidth(line.c_str()));
+		vstd::amax(maxW, f->getStringWidth(line.c_str()));
 }
 
 CLabelGroup::CLabelGroup(EFonts Font, EAlignment Align, const SDL_Color &Color):
@@ -1362,8 +1375,8 @@ void CTextBox::recalculateLines(const std::string &Txt)
 
 	vstd::clear_pointer(slider);
 	lines.clear();
-	const Font &f = *graphics->fonts[font];
-	int lineHeight =  f.height;
+	const IFont * f = graphics->fonts[font];
+	int lineHeight =  f->getLineHeight();
 	int lineCapacity = pos.h / lineHeight;
 
 	lines = CMessage::breakText(Txt, pos.w, font);
@@ -1379,15 +1392,15 @@ void CTextBox::recalculateLines(const std::string &Txt)
 	maxH = lineHeight * lines.size();
 	maxW = 0;
 	BOOST_FOREACH(const std::string &line, lines)
-		vstd::amax(maxW, f.getWidth(line.c_str()));
+		vstd::amax(maxW, f->getStringWidth(line));
 }
 
 void CTextBox::showAll(SDL_Surface * to)
 {
 	CIntObject::showAll(to);
 
-	const Font &f = *graphics->fonts[font];
-	int dy = f.height; //line height
+	const IFont * f = graphics->fonts[font];
+	int dy = f->getLineHeight(); //line height
 	int base_y = pos.y;
 	if(alignment == CENTER)
 		base_y += std::max((pos.h - maxH)/2,0);
@@ -1403,7 +1416,7 @@ void CTextBox::showAll(SDL_Surface * to)
 		int x = pos.x;
 		if(alignment == CENTER)
 		{
-			x += (pos.w - f.getWidth(line.c_str())) / 2;
+			x += (pos.w - f->getStringWidth(line.c_str())) / 2;
 			if(slider)
 				x -= slider->pos.w / 2 + 5;
 		}

+ 274 - 0
client/UIFramework/Fonts.cpp

@@ -0,0 +1,274 @@
+#include "StdInc.h"
+#include "Fonts.h"
+
+#include <SDL_ttf.h>
+
+#include "SDL_Pixels.h"
+#include "../../lib/JsonNode.h"
+#include "../../lib/vcmi_endian.h"
+#include "../../lib/Filesystem/CResourceLoader.h"
+
+/*
+ * Fonts.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+
+void IFont::renderTextLeft(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const
+{
+	renderText(surface, data, color, pos);
+}
+
+void IFont::renderTextRight(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const
+{
+	Point size(getStringWidth(data), getLineHeight());
+	renderText(surface, data, color, pos - size);
+}
+
+void IFont::renderTextCenter(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const
+{
+	Point size(getStringWidth(data), getLineHeight());
+	renderText(surface, data, color, pos - size / 2);
+}
+
+void IFont::renderTextLinesLeft(SDL_Surface * surface, const std::vector<std::string> & data, const SDL_Color & color, const Point & pos) const
+{
+	Point currPos = pos;
+
+	BOOST_FOREACH(const std::string & line, data)
+	{
+		renderTextLeft(surface, line, color, currPos);
+		currPos.y += getLineHeight();
+	}
+}
+
+void IFont::renderTextLinesRight(SDL_Surface * surface, const std::vector<std::string> & data, const SDL_Color & color, const Point & pos) const
+{
+	Point currPos = pos;
+	currPos.y -= data.size() * getLineHeight();
+
+	BOOST_FOREACH(const std::string & line, data)
+	{
+		renderTextRight(surface, line, color, currPos);
+		currPos.y += getLineHeight();
+	}
+}
+
+void IFont::renderTextLinesCenter(SDL_Surface * surface, const std::vector<std::string> & data, const SDL_Color & color, const Point & pos) const
+{
+	Point currPos = pos;
+	currPos.y -= data.size() * getLineHeight()/2;
+
+	BOOST_FOREACH(const std::string & line, data)
+	{
+		renderTextCenter(surface, line, color, currPos);
+		currPos.y += getLineHeight();
+	}
+}
+
+std::array<CBitmapFont::Char, CBitmapFont::totalChars> CBitmapFont::loadChars() const
+{
+	std::array<Char, totalChars> ret;
+
+	size_t offset = 32;
+
+	for (size_t i=0; i< ret.size(); i++)
+	{
+		ret[i].leftOffset =  read_le_u32(data.first.get() + offset); offset+=4;
+		ret[i].width =       read_le_u32(data.first.get() + offset); offset+=4;
+		ret[i].rightOffset = read_le_u32(data.first.get() + offset); offset+=4;
+	}
+
+	for (size_t i=0; i< ret.size(); i++)
+	{
+		int pixelOffset =  read_le_u32(data.first.get() + offset); offset+=4;
+		ret[i].pixels = data.first.get() + 4128 + pixelOffset;
+
+		assert(pixelOffset + 4128 < data.second);
+	}
+	return ret;
+}
+
+CBitmapFont::CBitmapFont(const std::string & filename):
+    data(CResourceHandler::get()->loadData(ResourceID("data/" + filename, EResType::BMP_FONT))),
+    chars(loadChars()),
+    height(data.first.get()[5])
+{}
+
+size_t CBitmapFont::getLineHeight() const
+{
+	return height;
+}
+
+size_t CBitmapFont::getSymbolWidth(char data) const
+{
+	const Char & ch = chars[ui8(data)];
+	return ch.leftOffset + ch.width + ch.rightOffset;
+}
+
+size_t CBitmapFont::getStringWidth(const std::string & data) const
+{
+	size_t width = 0;
+
+	BOOST_FOREACH(auto & ch, data)
+	{
+		width += getSymbolWidth(ch);
+	}
+	return width;
+}
+
+void CBitmapFont::renderCharacter(SDL_Surface * surface, const Char & character, const SDL_Color & color, int &posX, int &posY) const
+{
+	Rect clipRect;
+	SDL_GetClipRect(surface, &clipRect);
+
+	posX += character.leftOffset;
+
+	TColorPutter colorPutter = CSDL_Ext::getPutterFor(surface, 0);
+
+	Uint8 bpp = surface->format->BytesPerPixel;
+
+	// start of line, may differ from 0 due to end of surface or clipped surface
+	int lineBegin = std::max<int>(0, clipRect.y - posY);
+	int lineEnd   = std::min<int>(height, clipRect.y + clipRect.h - posY - 1);
+
+	// start end end of each row, may differ from 0
+	int rowBegin = std::max<int>(0, clipRect.x - posX);
+	int rowEnd   = std::min<int>(character.width, clipRect.x + clipRect.w - posX - 1);
+
+	//for each line in symbol
+	for(int dy = lineBegin; dy <lineEnd; dy++)
+	{
+		Uint8 *dstLine = (Uint8*)surface->pixels;
+		Uint8 *srcLine = character.pixels;
+
+		// shift source\destination pixels to current position
+		dstLine += (posY+dy) * surface->pitch + posX * bpp;
+		srcLine += dy * character.width;
+
+		//for each column in line
+		for(int dx = rowBegin; dx < rowEnd; dx++)
+		{
+			Uint8* dstPixel = dstLine + dx*bpp;
+			switch(srcLine[dx])
+			{
+			case 1: //black "shadow"
+				std::fill(dstPixel, dstPixel + bpp, 0);
+				break;
+			case 255: //text colour
+				colorPutter(dstPixel, color.r, color.g, color.b);
+				break;
+			default :
+				break; //transparency
+			}
+		}
+	}
+	posX += character.width;
+	posX += character.rightOffset;
+}
+
+void CBitmapFont::renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const
+{
+	if (data.empty())
+		return;
+
+	assert(surface);
+
+	int posX = pos.x;
+	int posY = pos.y;
+
+	// Safe to remove but I would like to trace all cases of this. Please report on Mantis or send note to me
+	// Ivan
+	assert(data[0] != '{');
+	assert(data[data.size()-1] != '}');
+
+	SDL_LockSurface(surface);
+	// for each symbol
+	for(size_t index = 0; index < data.size(); index++)
+	{
+		renderCharacter(surface, chars[ui8(data[index])], color, posX, posY);
+	}
+	SDL_UnlockSurface(surface);
+}
+
+std::pair<std::unique_ptr<ui8[]>, ui64> CTrueTypeFont::loadData(const JsonNode & config)
+{
+	std::string filename = "Data/" + config["file"].String();
+	return CResourceHandler::get()->loadData(ResourceID(filename, EResType::TTF_FONT));
+}
+
+TTF_Font * CTrueTypeFont::loadFont(const JsonNode &config)
+{
+	int pointSize = config["size"].Float();
+
+	if(!TTF_WasInit() && TTF_Init()==-1)
+		throw std::runtime_error(std::string("Failed to initialize true type support: ") + TTF_GetError() + "\n");
+
+	return TTF_OpenFontRW(SDL_RWFromConstMem(data.first.get(), data.second), 1, pointSize);
+}
+
+int CTrueTypeFont::getFontStyle(const JsonNode &config)
+{
+	const JsonVector & names = config["style"].Vector();
+	int ret = 0;
+	BOOST_FOREACH(const JsonNode & node, names)
+	{
+		if (node.String() == "bold")
+			ret |= TTF_STYLE_BOLD;
+		else if (node.String() == "italic")
+			ret |= TTF_STYLE_ITALIC;
+	}
+	return ret;
+}
+
+CTrueTypeFont::CTrueTypeFont(const JsonNode & fontConfig):
+    data(loadData(fontConfig)),
+    font(loadFont(fontConfig), TTF_CloseFont),
+    blended(fontConfig["blend"].Bool())
+{
+	assert(font);
+
+	TTF_SetFontStyle(font.get(), getFontStyle(fontConfig));
+}
+
+size_t CTrueTypeFont::getLineHeight() const
+{
+	return TTF_FontHeight(font.get());
+}
+
+size_t CTrueTypeFont::getSymbolWidth(char data) const
+{
+	int advance;
+	TTF_GlyphMetrics(font.get(), data, NULL, NULL, NULL, NULL, &advance);
+	return advance;
+}
+
+size_t CTrueTypeFont::getStringWidth(const std::string & data) const
+{
+	int width;
+	TTF_SizeText(font.get(), data.c_str(), &width, NULL);
+	return width;
+}
+
+void CTrueTypeFont::renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const
+{
+	if (!data.empty())
+	{
+		SDL_Surface * rendered;
+		if (blended)
+			rendered = TTF_RenderText_Solid(font.get(), data.c_str(), color);
+		else
+			rendered = TTF_RenderText_Blended(font.get(), data.c_str(), color);
+
+		assert(rendered);
+
+		Rect rect(pos.x, pos.y, rendered->w, rendered->h);
+		SDL_BlitSurface(rendered, NULL, surface, &rect);
+		SDL_FreeSurface(rendered);
+	}
+}

+ 110 - 0
client/UIFramework/Fonts.h

@@ -0,0 +1,110 @@
+#pragma once
+
+/*
+ * Fonts.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+class JsonNode;
+
+struct Point;
+struct SDL_Surface;
+struct SDL_Color;
+
+typedef struct _TTF_Font TTF_Font;
+
+class IFont
+{
+protected:
+	/// Internal function to render font, see renderTextLeft
+	virtual void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const = 0;
+
+public:
+	virtual ~IFont()
+	{}
+
+	/// Returns height of font
+	virtual size_t getLineHeight() const = 0;
+	/// Returns width of a single symbol
+	virtual size_t getSymbolWidth(char data) const = 0;
+	/// Return width of the string
+	virtual size_t getStringWidth(const std::string & data) const = 0;
+
+	/**
+	 * @param surface - destination to print text on
+	 * @param data - string to print
+	 * @param color - font color
+	 * @param pos - position of rendered font
+	 */
+	/// pos = topleft corner of the text
+	void renderTextLeft(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const;
+	/// pos = center of the text
+	void renderTextRight(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const;
+	/// pos = bottomright corner of the text
+	void renderTextCenter(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const;
+
+	/**
+	 * @param maxWidth -  max width in pixels of one line
+	 */
+	/// pos = topleft corner of the text
+	void renderTextLinesLeft(SDL_Surface * surface, const std::vector<std::string> & data, const SDL_Color & color, const Point & pos) const;
+	/// pos = center of the text
+	void renderTextLinesRight(SDL_Surface * surface, const std::vector<std::string> & data, const SDL_Color & color, const Point & pos) const;
+	/// pos = bottomright corner of the text
+	void renderTextLinesCenter(SDL_Surface * surface, const std::vector<std::string> & data, const SDL_Color & color, const Point & pos) const;
+};
+
+class CBitmapFont : public IFont
+{
+	static const size_t totalChars = 256;
+
+	struct Char
+	{
+		si32 leftOffset;
+		ui32 width;
+		si32 rightOffset;
+		ui8 *pixels; // pixels of this character, part of BitmapFont::data
+	};
+
+	const std::pair<std::unique_ptr<ui8[]>, ui64> data;
+
+	const std::array<Char, totalChars> chars;
+	const ui8 height;
+
+	std::array<Char, totalChars> loadChars() const;
+
+	void renderCharacter(SDL_Surface * surface, const Char & character, const SDL_Color & color, int &posX, int &posY) const;
+
+	void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const;
+public:
+	CBitmapFont(const std::string & filename);
+
+	size_t getLineHeight() const;
+	size_t getSymbolWidth(char data) const;
+	size_t getStringWidth(const std::string & data) const;
+};
+
+class CTrueTypeFont : public IFont
+{
+	const std::pair<std::unique_ptr<ui8[]>, ui64> data;
+
+	const std::unique_ptr<TTF_Font, void (*)(TTF_Font*)> font;
+	const bool blended;
+
+	std::pair<std::unique_ptr<ui8[]>, ui64> loadData(const JsonNode & config);
+	TTF_Font * loadFont(const JsonNode & config);
+	int getFontStyle(const JsonNode & config);
+
+	void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const;
+public:
+	CTrueTypeFont(const JsonNode & fontConfig);
+
+	size_t getLineHeight() const;
+	size_t getSymbolWidth(char data) const;
+	size_t getStringWidth(const std::string & data) const;
+};

+ 6 - 0
client/UIFramework/Geometries.h

@@ -48,6 +48,12 @@ struct Point
 		return Point(x+b.x,y+b.y);
 	}
 
+	template<typename T>
+	Point operator/(const T &div) const
+	{
+		return Point(x/div, y/div);
+	}
+
 	template<typename T>
 	Point operator*(const T &mul) const
 	{

+ 1 - 322
client/UIFramework/SDL_Extensions.cpp

@@ -11,6 +11,7 @@
 const SDL_Color Colors::YELLOW = { 229, 215, 123, 0 };
 const SDL_Color Colors::WHITE = { 255, 243, 222, 0 };
 const SDL_Color Colors::METALLIC_GOLD = { 173, 142, 66, 0 };
+const SDL_Color Colors::GREEN = { 0, 255, 0, 0 };
 
 SDL_Surface * CSDL_Ext::newSurface(int w, int h, SDL_Surface * mod) //creates new surface, with flags/format same as in surface given
 {
@@ -76,311 +77,6 @@ void updateRect (SDL_Rect * rect, SDL_Surface * scr)
 	SDL_UpdateRect(scr,rect->x,rect->y,rect->w,rect->h);
 }
 
-void printAtMiddleWB(const std::string & text, int x, int y, TTF_Font * font, int charpr, SDL_Color kolor, SDL_Surface * dst)
-{
-	std::vector<std::string> ws = CMessage::breakText(text,charpr);
-	std::vector<SDL_Surface*> wesu;
-	wesu.resize(ws.size());
-	for (size_t i=0; i < wesu.size(); ++i)
-	{
-		wesu[i]=TTF_RenderText_Blended(font,ws[i].c_str(),kolor);
-	}
-
-	int tox=0, toy=0;
-	for (size_t i=0; i < wesu.size(); ++i)
-	{
-		toy+=wesu[i]->h;
-		if (tox < wesu[i]->w)
-			tox=wesu[i]->w;
-	}
-	int evx, evy = y - (toy/2);
-	for (size_t i=0; i < wesu.size(); ++i)
-	{
-		evx = (x - (tox/2)) + ((tox-wesu[i]->w)/2);
-		blitAt(wesu[i],evx,evy,dst);
-		evy+=wesu[i]->h;
-	}
-
-
-	for (size_t i=0; i < wesu.size(); ++i)
-		SDL_FreeSurface(wesu[i]);
-}
-
-void printAtWB(const std::string & text, int x, int y, TTF_Font * font, int charpr, SDL_Color kolor, SDL_Surface * dst)
-{
-	std::vector<std::string> ws = CMessage::breakText(text,charpr);
-	std::vector<SDL_Surface*> wesu;
-	wesu.resize(ws.size());
-	for (size_t i=0; i < wesu.size(); ++i)
-		wesu[i]=TTF_RenderText_Blended(font,ws[i].c_str(),kolor);
-
-	int evy = y;
-	for (size_t i=0; i < wesu.size(); ++i)
-	{
-		blitAt(wesu[i],x,evy,dst);
-		evy+=wesu[i]->h;
-	}
-
-	for (size_t i=0; i < wesu.size(); ++i)
-		SDL_FreeSurface(wesu[i]);
-}
-
-void CSDL_Ext::printAtWB(const std::string & text, int x, int y, EFonts font, int charpr, SDL_Color kolor, SDL_Surface * dst)
-{
-	if (graphics->fontsTrueType[font])
-	{
-		printAtWB(text,x, y, graphics->fontsTrueType[font], charpr, kolor, dst);
-		return;
-	}
-	const Font *f = graphics->fonts[font];
-	std::vector<std::string> ws = CMessage::breakText(text,charpr);
-
-	int cury = y;
-	for (size_t i=0; i < ws.size(); ++i)
-	{
-		printAt(ws[i], x, cury, font, kolor, dst);
-		cury += f->height;
-	}
-}
-
-
-void CSDL_Ext::printAtMiddleWB( const std::string & text, int x, int y, EFonts font, int charpr, SDL_Color kolor/*=Colors::YELLOW*/, SDL_Surface * dst/*=screen*/ )
-{
-	if (graphics->fontsTrueType[font])
-	{
-		printAtMiddleWB(text,x, y, graphics->fontsTrueType[font], charpr, kolor, dst);
-		return;
-	}
-
-	const Font *f = graphics->fonts[font];
-	std::vector<std::string> ws = CMessage::breakText(text,charpr);
-	int totalHeight = ws.size() * f->height;
-
-	int cury = y - totalHeight/2;
-	for (size_t i=0; i < ws.size(); ++i)
-	{
-		printAt(ws[i], x - f->getWidth(ws[i].c_str())/2, cury, font, kolor, dst);
-		cury += f->height;
-	}
-}
-
-void printAtMiddle(const std::string & text, int x, int y, TTF_Font * font, SDL_Color kolor, SDL_Surface * dst, ui8 quality=2)
-{
-	if(text.length()==0) return;
-	SDL_Surface * temp;
-	switch (quality)
-	{
-	case 0:
-		temp = TTF_RenderText_Solid(font,text.c_str(),kolor);
-		break;
-	case 1:
-		SDL_Color tem;
-		tem.b = 0xff-kolor.b;
-		tem.g = 0xff-kolor.g;
-		tem.r = 0xff-kolor.r;
-		tem.unused = 0xff-kolor.unused;
-		temp = TTF_RenderText_Shaded(font,text.c_str(),kolor,tem);
-		break;
-	case 2:
-		temp = TTF_RenderText_Blended(font,text.c_str(),kolor);
-		break;
-	default:
-		temp = TTF_RenderText_Blended(font,text.c_str(),kolor);
-		break;
-	}
-	SDL_Rect dstRect = genRect(temp->h, temp->w, x-(temp->w/2), y-(temp->h/2));
-	CSDL_Ext::blitSurface(temp, NULL, dst, &dstRect);
-	SDL_FreeSurface(temp);
-}
-
-void CSDL_Ext::printAtMiddle( const std::string & text, int x, int y, EFonts font, SDL_Color kolor/*=Colors::WHITE*/, SDL_Surface * dst/*=screen*/ )
-{
-	if (graphics->fontsTrueType[font])
-	{
-		printAtMiddle(text,x, y, graphics->fontsTrueType[font], kolor, dst);
-		return;
-	}
-	const Font *f = graphics->fonts[font];
-	int nx = x - f->getWidth(text.c_str())/2,
-		ny = y - f->height/2;
-
-	printAt(text, nx, ny, font, kolor, dst);
-}
-
-void printAt(const std::string & text, int x, int y, TTF_Font * font, SDL_Color kolor, SDL_Surface * dst, ui8 quality=2, bool refresh=false)
-{
-	if (text.length()==0)
-		return;
-	SDL_Surface * temp;
-	switch (quality)
-	{
-	case 0:
-		temp = TTF_RenderText_Solid(font,text.c_str(),kolor);
-		break;
-	case 1:
-		SDL_Color tem;
-		tem.b = 0xff-kolor.b;
-		tem.g = 0xff-kolor.g;
-		tem.r = 0xff-kolor.r;
-		tem.unused = 0xff-kolor.unused;
-		temp = TTF_RenderText_Shaded(font,text.c_str(),kolor,tem);
-		break;
-	case 2:
-		temp = TTF_RenderText_Blended(font,text.c_str(),kolor);
-		break;
-	default:
-		temp = TTF_RenderText_Blended(font,text.c_str(),kolor);
-		break;
-	}
-	SDL_Rect dstRect = genRect(temp->h,temp->w,x,y);
-	CSDL_Ext::blitSurface(temp,NULL,dst,&dstRect);
-	if(refresh)
-		SDL_UpdateRect(dst,x,y,temp->w,temp->h);
-	SDL_FreeSurface(temp);
-}
-
-void CSDL_Ext::printAt( const std::string & text, int dstX, int dstY, EFonts font, SDL_Color color, SDL_Surface * dst)
-{
-	if(!text.size())
-		return;
-
-	if (graphics->fontsTrueType[font])
-	{
-		printAt(text,dstX, dstY, graphics->fontsTrueType[font], color, dst);
-		return;
-	}
-
-	assert(dst);
-	assert(font < Graphics::FONTS_NUMBER);
-
-	Rect clipRect;
-	SDL_GetClipRect(dst, &clipRect);
-
-	const Font *f = graphics->fonts[font];
-	const Uint8 bpp = dst->format->BytesPerPixel;
-
-	TColorPutter colorPutter = getPutterFor(dst, 0);
-
-	//if text is in {} braces, we'll ommit them
-	const int textBegin = (text[0] == '{' ? 1 : 0);
-	const int textEnd = (text[text.size()-1] == '}' ? text.size()-1 : text.size());
-
-	SDL_LockSurface(dst);
-	// for each symbol
-	for(int index = textBegin; index < textEnd; index++)
-	{
-		const ui8 symbol = text[index];
-		dstX += f->chars[symbol].leftOffset;
-
-		int lineBegin = std::max<int>(0, clipRect.y - dstY);
-		int lineEnd   = std::min<int>(f->height, clipRect.y + clipRect.h - dstY - 1);
-
-		int rowBegin = std::max(0, clipRect.x - dstX);
-		int rowEnd   = std::min(f->chars[symbol].width, clipRect.x + clipRect.w - dstX - 1);
-
-		//for each line in symbol
-		for(int dy = lineBegin; dy <lineEnd; dy++)
-		{
-			Uint8 *dstLine = (Uint8*)dst->pixels;
-			Uint8 *srcLine = f->chars[symbol].pixels;
-
-			dstLine += (dstY+dy) * dst->pitch + dstX * bpp;
-			srcLine += dy * f->chars[symbol].width;
-
-			//for each column in line
-			for(int dx = rowBegin; dx < rowEnd; dx++)
-			{
-				Uint8* dstPixel = dstLine + dx*bpp;
-				switch(*(srcLine + dx))
-				{
-				case 1: //black "shadow"
-					memset(dstPixel, 0, bpp);
-					break;
-				case 255: //text colour
-					colorPutter(dstPixel, color.r, color.g, color.b);
-					break;
-				}
-			}
-		}
-
-		dstX += f->chars[symbol].width;
-		dstX += f->chars[symbol].rightOffset;
-	}
-	SDL_UnlockSurface(dst);
-}
-
-void printTo(const std::string & text, int x, int y, TTF_Font * font, SDL_Color kolor, SDL_Surface * dst, ui8 quality=2)
-{
-	if (text.length()==0)
-		return;
-	SDL_Surface * temp;
-	switch (quality)
-	{
-	case 0:
-		temp = TTF_RenderText_Solid(font,text.c_str(),kolor);
-		break;
-	case 1:
-		SDL_Color tem;
-		tem.b = 0xff-kolor.b;
-		tem.g = 0xff-kolor.g;
-		tem.r = 0xff-kolor.r;
-		tem.unused = 0xff-kolor.unused;
-		temp = TTF_RenderText_Shaded(font,text.c_str(),kolor,tem);
-		break;
-	case 2:
-		temp = TTF_RenderText_Blended(font,text.c_str(),kolor);
-		break;
-	default:
-		temp = TTF_RenderText_Blended(font,text.c_str(),kolor);
-		break;
-	}
-	SDL_Rect dstRect = genRect(temp->h,temp->w,x-temp->w,y-temp->h);
-	CSDL_Ext::blitSurface(temp,NULL,dst,&dstRect);
-	SDL_UpdateRect(dst,x-temp->w,y-temp->h,temp->w,temp->h);
-	SDL_FreeSurface(temp);
-}
-
-void CSDL_Ext::printTo( const std::string & text, int x, int y, EFonts font, SDL_Color kolor/*=Colors::WHITE*/, SDL_Surface * dst/*=screen*/ )
-{
-	if (graphics->fontsTrueType[font])
-	{
-		printTo(text,x, y, graphics->fontsTrueType[font], kolor, dst);
-		return;
-	}
-	const Font *f = graphics->fonts[font];
-	printAt(text, x - f->getWidth(text.c_str()), y - f->height, font, kolor, dst);
-}
-
-void printToWR(const std::string & text, int x, int y, TTF_Font * font, SDL_Color kolor, SDL_Surface * dst, ui8 quality=2)
-{
-	if (text.length()==0)
-		return;
-	SDL_Surface * temp;
-	switch (quality)
-	{
-	case 0:
-		temp = TTF_RenderText_Solid(font,text.c_str(),kolor);
-		break;
-	case 1:
-		SDL_Color tem;
-		tem.b = 0xff-kolor.b;
-		tem.g = 0xff-kolor.g;
-		tem.r = 0xff-kolor.r;
-		tem.unused = 0xff-kolor.unused;
-		temp = TTF_RenderText_Shaded(font,text.c_str(),kolor,tem);
-		break;
-	case 2:
-		temp = TTF_RenderText_Blended(font,text.c_str(),kolor);
-		break;
-	default:
-		temp = TTF_RenderText_Blended(font,text.c_str(),kolor);
-		break;
-	}
-	SDL_Rect dstRect = genRect(temp->h,temp->w,x-temp->w,y-temp->h);
-	CSDL_Ext::blitSurface(temp,NULL,dst,&dstRect);
-	SDL_FreeSurface(temp);
-}
-
 // Vertical flip
 SDL_Surface * CSDL_Ext::rotate01(SDL_Surface * toRot)
 {
@@ -1282,23 +978,6 @@ void CSDL_Ext::fillTexture(SDL_Surface *dst, SDL_Surface * src)
 	}
 }
 
-std::string CSDL_Ext::trimToFit(std::string text, int widthLimit, EFonts font)
-{
-	int widthSoFar = 0;
-	for(auto i = text.begin(); i != text.end(); i++)
-	{
-		widthSoFar += graphics->fonts[font]->getCharWidth(*i);
-		if(widthSoFar > widthLimit)
-		{
-			//remove all characteres past limit
-			text.erase(i, text.end());
-			break;
-		}
-	}
-
-	return text;
-}
-
 template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<2>(int, int);
 template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<3>(int, int);
 template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<4>(int, int);

+ 4 - 9
client/UIFramework/SDL_Extensions.h

@@ -3,7 +3,7 @@
 #include <SDL_video.h>
 #include <SDL_ttf.h>
 #include "../../lib/int3.h"
-#include "../FontBase.h"
+#include "../Graphics.h"
 #include "Geometries.h"
 
 /*
@@ -51,6 +51,9 @@ public:
 
 	/** the metallic gold color used mostly as a border around buttons */
 	static const SDL_Color METALLIC_GOLD;
+
+	/** green color used for in-game console */
+	static const SDL_Color GREEN;
 };
 
 //MSVC gives an error when calling abs with ui64 -> we add template that will match calls with unsigned arg and return it
@@ -169,12 +172,6 @@ namespace CSDL_Ext
 	int blit8bppAlphaTo24bpp(const SDL_Surface * src, const SDL_Rect * srcRect, SDL_Surface * dst, SDL_Rect * dstRect); //blits 8 bpp surface with alpha channel to 24 bpp surface
 	Uint32 colorToUint32(const SDL_Color * color); //little endian only
 
-	void printAtWB(const std::string & text, int x, int y, EFonts font, int charpr, SDL_Color kolor=Colors::WHITE, SDL_Surface * dst=screen);
-	void printAt(const std::string & text, int x, int y, EFonts font, SDL_Color kolor=Colors::WHITE, SDL_Surface * dst=screen);
-	void printTo(const std::string & text, int x, int y, EFonts font, SDL_Color kolor=Colors::WHITE, SDL_Surface * dst=screen);
-	void printAtMiddle(const std::string & text, int x, int y, EFonts font, SDL_Color kolor=Colors::WHITE, SDL_Surface * dst=screen);
-	void printAtMiddleWB(const std::string & text, int x, int y, EFonts font, int charpr, SDL_Color kolor=Colors::YELLOW, SDL_Surface * dst=screen);
-
 	void update(SDL_Surface * what = screen); //updates whole surface (default - main screen)
 	void drawBorder(SDL_Surface * sur, int x, int y, int w, int h, const int3 &color);
 	void drawBorder(SDL_Surface * sur, const SDL_Rect &r, const int3 &color);
@@ -196,6 +193,4 @@ namespace CSDL_Ext
 	template<int bpp>
 	void applyEffectBpp( SDL_Surface * surf, const SDL_Rect * rect, int mode );
 	void applyEffect(SDL_Surface * surf, const SDL_Rect * rect, int mode); //mode: 0 - sepia, 1 - grayscale
-
-	std::string trimToFit(std::string text, int widthLimit, EFonts font);
 }

+ 37 - 0
config/fonts.json

@@ -0,0 +1,37 @@
+{
+	// Original HoMM 3 bitmap fonts
+	// Stored in H3Bitmap.lod with fnt extension
+	// Warning: Do not change number of entries in this list
+	"bitmap" :
+	[
+		"BIGFONT",  // Mostly used for window titles
+		"CALLI10R", // Unused in VCMI
+		"CREDITS",  // Used for credits menu
+		"HISCORE",  // Unused in VCMI
+		"MEDFONT",  // Some titles
+		"SMALFONT", // Most of the messages
+		"TIMES08R", // Used to display amounts on creature card 
+		"TINY",     // Some text
+		"VERD10B"   // Unused in VCMI
+	],
+	
+	// True type replacements
+	// Should be in format:
+	// <replaced bitmap font name, case-sensetive> : <true type font description>
+	// "file" - file to load font from, must be in data/ directory
+	// "size" - point size of font
+	// "style" - italic and\or bold, indicates font style
+	// "blend" - if set to true, font will be antialiased
+	"trueType":
+	{
+		//"BIGFONT"  : { "file" : "TimesNewRoman.ttf", "size" : 20, "style" : ["bold"], "blend" : true},
+		//"CALLI10R" : { "file" : "Georgia.ttf",       "size" : 10},
+		//"CREDITS"  : { "file" : "TimesNewRoman.ttf", "size" : 13, "blend" : true},
+		//"HISCORE"  : { "file" : "Georgia.ttf",       "size" : 13},
+		//"MEDFONT"  : { "file" : "TimesNewRoman.ttf", "size" : 16, "blend" : true},
+		//"SMALFONT" : { "file" : "Georgia.ttf",       "size" : 13},
+		//"TIMES08R" : { "file" : "TimesNewRoman.ttf", "size" :  8, "blend" : true},
+		//"TINY"     : { "file" : "Georgia.ttf",       "size" : 10},
+		//"VERD10B"  : { "file" : "Georgia.ttf",       "size" : 13}
+	}
+}

+ 0 - 29
config/fonts.txt

@@ -1,29 +0,0 @@
-0	0	tnrb.ttf	20
-0	1	georgia.ttf	10
-0	2	tnrb.ttf	13
-0	3	georgia.ttf	13
-0	4	tnrb.ttf	16
-0	5	georgia.ttf	13
-0	6	tnrb.ttf	08
-0	7	georgia.ttf	10
-0	8	georgia.ttf	13
--1
-
-Format: 
-1 = enabled, 0=disabled
-font numeric ID
-TTF font name
-font size
-
-
-ID of fonts:
-
-0  BIGFONT.FNT //windows titles
-1  CALLI10R.FNT ?
-2  CREDITS.FNT 
-3  HISCORE.FNT 
-4  MEDFONT.FNT //most used font
-5  SMALFONT.FNT //most used font
-6  TIMES08R.FNT ?
-7  TINY.FNT //used for small texts
-8  VERD10B.FNT ?

+ 2 - 0
lib/CCreatureHandler.cpp

@@ -363,6 +363,8 @@ void CCreatureHandler::loadCreatures()
 			}
 		}
 
+		c->nameRef = creature["name"].Vector().at(0).String();
+
 		/* A creature can have several names. */
 		BOOST_FOREACH(const JsonNode &name, creature["name"].Vector())
 		{

+ 6 - 3
lib/Filesystem/CResourceLoader.cpp

@@ -230,7 +230,8 @@ EResType::Type EResTypeHelper::getTypeFromExtension(std::string extension)
 	        (".MSG",   EResType::MASK)
 	        (".H3C",   EResType::CAMPAIGN)
 	        (".H3M",   EResType::MAP)
-	        (".FNT",   EResType::FONT)
+	        (".FNT",   EResType::BMP_FONT)
+	        (".TTF",   EResType::TTF_FONT)
 	        (".BMP",   EResType::IMAGE)
 	        (".JPG",   EResType::IMAGE)
 	        (".PCX",   EResType::IMAGE)
@@ -241,7 +242,8 @@ EResType::Type EResTypeHelper::getTypeFromExtension(std::string extension)
 	        (".SMK",   EResType::VIDEO)
 	        (".BIK",   EResType::VIDEO)
 	        (".MJPG",  EResType::VIDEO)
-			(".MPG",   EResType::VIDEO)
+	        (".MPG",   EResType::VIDEO)
+	        (".AVI",   EResType::VIDEO)
 	        (".MP3",   EResType::MUSIC)
 	        (".OGG",   EResType::MUSIC)
 	        (".LOD",   EResType::ARCHIVE_LOD)
@@ -269,7 +271,8 @@ std::string EResTypeHelper::getEResTypeAsString(EResType::Type type)
 		MAP_ENUM(MASK)
 		MAP_ENUM(CAMPAIGN)
 		MAP_ENUM(MAP)
-		MAP_ENUM(FONT)
+		MAP_ENUM(BMP_FONT)
+		MAP_ENUM(TTF_FONT)
 		MAP_ENUM(IMAGE)
 		MAP_ENUM(VIDEO)
 		MAP_ENUM(SOUND)

+ 2 - 1
lib/Filesystem/CResourceLoader.h

@@ -46,7 +46,8 @@ namespace EResType
 		MASK,
 		CAMPAIGN,
 		MAP,
-		FONT,
+		BMP_FONT,
+		TTF_FONT,
 		IMAGE,
 		VIDEO,
 		SOUND,

+ 1090 - 1089
lib/JsonNode.cpp

@@ -1,1089 +1,1090 @@
-/*
- * JsonNode.cpp, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
- 
-#include "StdInc.h"
-#include "JsonNode.h"
-
-#include "HeroBonus.h"
-#include "Filesystem/CResourceLoader.h"
-
-using namespace JsonDetail;
-
-static const JsonNode nullNode;
-
-JsonNode::JsonNode(JsonType Type):
-	type(DATA_NULL)
-{
-	setType(Type);
-}
-
-JsonNode::JsonNode(const char *data, size_t datasize):
-	type(DATA_NULL)
-{
-	JsonParser parser(data, datasize, *this);
-	JsonValidator validator(*this);
-}
-
-JsonNode::JsonNode(ResourceID && fileURI):
-	type(DATA_NULL)
-{
-	std::string filename = CResourceHandler::get()->getResourceName(fileURI);
-	FILE * file = fopen(filename.c_str(), "rb");
-	if (!file)
-	{
-		tlog1 << "Failed to open file " << filename << "\n";
-		perror("Last system error was ");
-		return;
-	}
-
-	fseek(file, 0, SEEK_END);
-	size_t datasize = ftell(file);
-	fseek(file, 0, SEEK_SET);
-
-	char *input = new char[datasize];
-	datasize = fread((void*)input, 1, datasize, file);
-	fclose(file);
-
-	JsonParser parser(input, datasize, *this);
-	JsonValidator validator(*this);
-	delete [] input;
-}
-
-JsonNode::JsonNode(const JsonNode &copy):
-	type(DATA_NULL)
-{
-	setType(copy.getType());
-	switch(type)
-	{
-		break; case DATA_NULL:
-		break; case DATA_BOOL:   Bool() =   copy.Bool();
-		break; case DATA_FLOAT:  Float() =  copy.Float();
-		break; case DATA_STRING: String() = copy.String();
-		break; case DATA_VECTOR: Vector() = copy.Vector();
-		break; case DATA_STRUCT: Struct() = copy.Struct();
-	}
-}
-
-JsonNode::~JsonNode()
-{
-	setType(DATA_NULL);
-}
-
-void JsonNode::swap(JsonNode &b)
-{
-	using std::swap;
-	swap(data, b.data);
-	swap(type, b.type);
-}
-
-JsonNode & JsonNode::operator =(JsonNode node)
-{
-	swap(node);
-	return *this;
-}
-
-bool JsonNode::operator == (const JsonNode &other) const
-{
-	if (getType() == other.getType())
-	{
-		switch(type)
-		{
-			break; case DATA_NULL:   return true;
-			break; case DATA_BOOL:   return Bool() == other.Bool();
-			break; case DATA_FLOAT:  return Float() == other.Float();
-			break; case DATA_STRING: return String() == other.String();
-			break; case DATA_VECTOR: return Vector() == other.Vector();
-			break; case DATA_STRUCT: return Struct() == other.Struct();
-		}
-	}
-	return false;
-}
-
-bool JsonNode::operator != (const JsonNode &other) const
-{
-	return !(*this == other);
-}
-
-JsonNode::JsonType JsonNode::getType() const
-{
-	return type;
-}
-
-void JsonNode::setType(JsonType Type)
-{
-	if (type == Type)
-		return;
-
-	//Reset node to NULL
-	if (Type != DATA_NULL)
-		setType(DATA_NULL);
-
-	switch (type)
-	{
-		break; case DATA_STRING:  delete data.String;
-		break; case DATA_VECTOR:  delete data.Vector;
-		break; case DATA_STRUCT:  delete data.Struct;
-		break; default:
-		break;
-	}
-	//Set new node type
-	type = Type;
-	switch(type)
-	{
-		break; case DATA_NULL:
-		break; case DATA_BOOL:   data.Bool = false;
-		break; case DATA_FLOAT:  data.Float = 0;
-		break; case DATA_STRING: data.String = new std::string;
-		break; case DATA_VECTOR: data.Vector = new JsonVector;
-		break; case DATA_STRUCT: data.Struct = new JsonMap;
-	}
-}
-
-bool JsonNode::isNull() const
-{
-	return type == DATA_NULL;
-}
-
-bool & JsonNode::Bool()
-{
-	setType(DATA_BOOL);
-	return data.Bool;
-}
-
-double & JsonNode::Float()
-{
-	setType(DATA_FLOAT);
-	return data.Float;
-}
-
-std::string & JsonNode::String()
-{
-	setType(DATA_STRING);
-	return *data.String;
-}
-
-JsonVector & JsonNode::Vector()
-{
-	setType(DATA_VECTOR);
-	return *data.Vector;
-}
-
-JsonMap & JsonNode::Struct()
-{
-	setType(DATA_STRUCT);
-	return *data.Struct;
-}
-
-const bool boolDefault = false;
-const bool & JsonNode::Bool() const
-{
-	if (type == DATA_NULL)
-		return boolDefault;
-	assert(type == DATA_BOOL);
-	return data.Bool;
-}
-
-const double floatDefault = 0;
-const double & JsonNode::Float() const
-{
-	if (type == DATA_NULL)
-		return floatDefault;
-	assert(type == DATA_FLOAT);
-	return data.Float;
-}
-
-const std::string stringDefault = std::string();
-const std::string & JsonNode::String() const
-{
-	if (type == DATA_NULL)
-		return stringDefault;
-	assert(type == DATA_STRING);
-	return *data.String;
-}
-
-const JsonVector vectorDefault = JsonVector();
-const JsonVector & JsonNode::Vector() const
-{
-	if (type == DATA_NULL)
-		return vectorDefault;
-	assert(type == DATA_VECTOR);
-	return *data.Vector;
-}
-
-const JsonMap mapDefault = JsonMap();
-const JsonMap & JsonNode::Struct() const
-{
-	if (type == DATA_NULL)
-		return mapDefault;
-	assert(type == DATA_STRUCT);
-	return *data.Struct;
-}
-
-JsonNode & JsonNode::operator[](std::string child)
-{
-	return Struct()[child];
-}
-
-const JsonNode & JsonNode::operator[](std::string child) const
-{
-	JsonMap::const_iterator it = Struct().find(child);
-	if (it != Struct().end())
-		return it->second;
-	return nullNode;
-}
-////////////////////////////////////////////////////////////////////////////////
-
-template<typename Iterator>
-void JsonWriter::writeContainer(Iterator begin, Iterator end)
-{
-	if (begin == end)
-		return;
-
-	prefix += '\t';
-	end--;
-	while (begin != end)
-	{
-		writeEntry(begin++);
-		out<<",\n";
-	}
-
-	writeEntry(begin);
-	out<<"\n";
-	prefix.resize(prefix.size()-1);
-}
-
-void JsonWriter::writeEntry(JsonMap::const_iterator entry)
-{
-	out << prefix;
-	writeString(entry->first);
-	out << " : ";
-	writeNode(entry->second);
-}
-
-void JsonWriter::writeEntry(JsonVector::const_iterator entry)
-{
-	out << prefix;
-	writeNode(*entry);
-}
-
-void JsonWriter::writeString(const std::string &string)
-{
-	static const std::string escaped = "\"\\/\b\f\n\r\t";
-
-	out <<'\"';
-	size_t pos=0, start=0;
-	for (; pos<string.size(); pos++)
-	{
-		size_t escapedChar = escaped.find(string[pos]);
-
-		if (escapedChar != std::string::npos)
-		{
-			out.write(string.data()+start, pos - start);
-			out << '\\' << escaped[escapedChar];
-			start = pos;
-		}
-	}
-	out.write(string.data()+start, pos - start);
-	out <<'\"';
-}
-
-void JsonWriter::writeNode(const JsonNode &node)
-{
-	switch(node.getType())
-	{
-		break; case JsonNode::DATA_NULL:
-			out << "null";
-
-		break; case JsonNode::DATA_BOOL:
-			if (node.Bool())
-				out << "true";
-			else
-				out << "false";
-
-		break; case JsonNode::DATA_FLOAT:
-			out << node.Float();
-
-		break; case JsonNode::DATA_STRING:
-			writeString(node.String());
-
-		break; case JsonNode::DATA_VECTOR:
-			out << "[" << "\n";
-			writeContainer(node.Vector().begin(), node.Vector().end());
-			out << prefix << "]";
-
-		break; case JsonNode::DATA_STRUCT:
-			out << "{" << "\n";
-			writeContainer(node.Struct().begin(), node.Struct().end());
-			out << prefix << "}";
-	}
-}
-
-JsonWriter::JsonWriter(std::ostream &output, const JsonNode &node):
-	out(output)
-{
-	writeNode(node);
-}
-
-std::ostream & operator<<(std::ostream &out, const JsonNode &node)
-{
-	JsonWriter(out, node);
-	return out << "\n";
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-JsonParser::JsonParser(const char * inputString, size_t stringSize, JsonNode &root):
-	input(inputString, stringSize),
-	lineCount(1),
-	lineStart(0),
-	pos(0)
-{
-	extractValue(root);
-	extractWhitespace(false);
-
-	//Warn if there are any non-whitespace symbols left
-	if (pos < input.size())
-		error("Not all file was parsed!", true);
-
-	//TODO: better way to show errors (like printing file name as well)
-	tlog3<<errors;
-}
-
-bool JsonParser::extractSeparator()
-{
-	if (!extractWhitespace())
-		return false;
-
-	if ( input[pos] !=':')
-		return error("Separator expected");
-
-	pos++;
-	return true;
-}
-
-bool JsonParser::extractValue(JsonNode &node)
-{
-	if (!extractWhitespace())
-		return false;
-
-	switch (input[pos])
-	{
-		case '\"': return extractString(node);
-		case 'n' : return extractNull(node);
-		case 't' : return extractTrue(node);
-		case 'f' : return extractFalse(node);
-		case '{' : return extractStruct(node);
-		case '[' : return extractArray(node);
-		case '-' : return extractFloat(node);
-		default:
-		{
-			if (input[pos] >= '0' && input[pos] <= '9')
-				return extractFloat(node);
-			return error("Value expected!");
-		}
-	}
-}
-
-bool JsonParser::extractWhitespace(bool verbose)
-{
-	while (true)
-	{
-		while (pos < input.size() && (ui8)input[pos] <= ' ')
-		{
-			if (input[pos] == '\n')
-			{
-				lineCount++;
-				lineStart = pos+1;
-			}
-			pos++;
-		}
-		if (pos >= input.size() || input[pos] != '/')
-			break;
-
-		pos++;
-		if (pos == input.size())
-			break;
-		if (input[pos] == '/')
-			pos++;
-		else
-			error("Comments must consist from two slashes!", true);
-
-		while (pos < input.size() && input[pos] != '\n')
-			pos++;
-	}
-
-	if (pos >= input.size() && verbose)
-		return error("Unexpected end of file!");
-	return true;
-}
-
-bool JsonParser::extractEscaping(std::string &str)
-{
-	switch(input[pos])
-	{
-		break; case '\"': str += '\"';
-		break; case '\\': str += '\\';
-		break; case  '/': str += '/';
-		break; case 'b': str += '\b';
-		break; case 'f': str += '\f';
-		break; case 'n': str += '\n';
-		break; case 'r': str += '\r';
-		break; case 't': str += '\t';
-		break; default: return error("Unknown escape sequence!", true);
-	};
-	return true;
-}
-
-bool JsonParser::extractString(std::string &str)
-{
-	if (input[pos] != '\"')
-		return error("String expected!");
-	pos++;
-
-	size_t first = pos;
-
-	while (pos != input.size())
-	{
-		if (input[pos] == '\"') // Correct end of string
-		{
-			str.append( &input[first], pos-first);
-			pos++;
-			return true;
-		}
-		if (input[pos] == '\\') // Escaping
-		{
-			str.append( &input[first], pos-first);
-			first = pos++;
-			if (pos == input.size())
-				break;
-			extractEscaping(str);
-		}
-		if (input[pos] == '\n') // end-of-line
-		{
-			str.append( &input[first], pos-first);
-			return error("Closing quote not found!", true);
-		}
-		if ((unsigned char)(input[pos]) < ' ') // control character
-		{
-			str.append( &input[first], pos-first);
-			first = pos+1;
-			error("Illegal character in the string!", true);
-		}
-		pos++;
-	}
-	return error("Unterminated string!");
-}
-
-bool JsonParser::extractString(JsonNode &node)
-{
-	std::string str;
-	if (!extractString(str))
-		return false;
-
-	node.setType(JsonNode::DATA_STRING);
-	node.String() = str;
-	return true;
-}
-
-bool JsonParser::extractLiteral(const std::string &literal)
-{
-	if (literal.compare(0, literal.size(), &input[pos], literal.size()) != 0)
-	{
-		while (pos < input.size() && ((input[pos]>'a' && input[pos]<'z')
-		                           || (input[pos]>'A' && input[pos]<'Z')))
-			pos++;
-		return error("Unknown literal found", true);
-	}
-
-	pos += literal.size();
-	return true;
-}
-
-bool JsonParser::extractNull(JsonNode &node)
-{
-	if (!extractLiteral("null"))
-		return false;
-
-	node.setType(JsonNode::DATA_NULL);
-	return true;
-}
-
-bool JsonParser::extractTrue(JsonNode &node)
-{
-	if (!extractLiteral("true"))
-		return false;
-
-	node.Bool() = true;
-	return true;
-}
-
-bool JsonParser::extractFalse(JsonNode &node)
-{
-	if (!extractLiteral("false"))
-		return false;
-
-	node.Bool() = false;
-	return true;
-}
-
-bool JsonParser::extractStruct(JsonNode &node)
-{
-	node.setType(JsonNode::DATA_STRUCT);
-	pos++;
-
-	if (!extractWhitespace())
-		return false;
-
-	//Empty struct found
-	if (input[pos] == '}')
-	{
-		pos++;
-		return true;
-	}
-
-	while (true)
-	{
-		if (!extractWhitespace())
-			return false;
-
-		std::string key;
-		if (!extractString(key))
-			return false;
-
-		if (node.Struct().find(key) != node.Struct().end())
-			error("Dublicated element encountered!", true);
-
-		if (!extractSeparator())
-			return false;
-
-		if (!extractElement(node.Struct()[key], '}'))
-			return false;
-
-		if (input[pos] == '}')
-		{
-			pos++;
-			return true;
-		}
-	}
-}
-
-bool JsonParser::extractArray(JsonNode &node)
-{
-	pos++;
-	node.setType(JsonNode::DATA_VECTOR);
-
-	if (!extractWhitespace())
-		return false;
-
-	//Empty array found
-	if (input[pos] == ']')
-	{
-		pos++;
-		return true;
-	}
-
-	while (true)
-	{
-		//NOTE: currently 50% of time is this vector resizing.
-		//May be useful to use list during parsing and then swap() all items to vector
-		node.Vector().resize(node.Vector().size()+1);
-
-		if (!extractElement(node.Vector().back(), ']'))
-			return false;
-
-		if (input[pos] == ']')
-		{
-			pos++;
-			return true;
-		}
-	}
-}
-
-bool JsonParser::extractElement(JsonNode &node, char terminator)
-{
-	if (!extractValue(node))
-		return false;
-
-	if (!extractWhitespace())
-		return false;
-
-	bool comma = (input[pos] == ',');
-	if (comma )
-	{
-		pos++;
-		if (!extractWhitespace())
-			return false;
-	}
-
-	if (input[pos] == terminator)
-		return true;
-
-	if (!comma)
-		error("Comma expected!", true);
-
-	return true;
-}
-
-bool JsonParser::extractFloat(JsonNode &node)
-{
-	assert(input[pos] == '-' || (input[pos] >= '0' && input[pos] <= '9'));
-	bool negative=false;
-	double result=0;
-
-	if (input[pos] == '-')
-	{
-		pos++;
-		negative = true;
-	}
-
-	if (input[pos] < '0' || input[pos] > '9')
-		return error("Number expected!");
-
-	//Extract integer part
-	while (input[pos] >= '0' && input[pos] <= '9')
-	{
-		result = result*10+(input[pos]-'0');
-		pos++;
-	}
-
-	if (input[pos] == '.')
-	{
-		//extract fractional part
-		pos++;
-		double fractMult = 0.1;
-		if (input[pos] < '0' || input[pos] > '9')
-			return error("Decimal part expected!");
-
-		while (input[pos] >= '0' && input[pos] <= '9')
-		{
-			result = result + fractMult*(input[pos]-'0');
-			fractMult /= 10;
-			pos++;
-		}
-	}
-	//TODO: exponential part
-	if (negative)
-		result = -result;
-
-	node.setType(JsonNode::DATA_FLOAT);
-	node.Float() = result;
-	return true;
-}
-
-bool JsonParser::error(const std::string &message, bool warning)
-{
-	std::ostringstream stream;
-	std::string type(warning?" warning: ":" error: ");
-
-	stream << "At line " << lineCount << ", position "<<pos-lineStart
-	       << type << message <<"\n";
-	errors += stream.str();
-
-	return warning;
-}
-
-static const std::map<std::string, JsonNode::JsonType> stringToType =
-	boost::assign::map_list_of
-		("null",   JsonNode::DATA_NULL)   ("bool",   JsonNode::DATA_BOOL)
-		("number", JsonNode::DATA_FLOAT)  ("string", JsonNode::DATA_STRING)
-		("array",  JsonNode::DATA_VECTOR) ("object", JsonNode::DATA_STRUCT);
-
-//Check current schema entry for validness and converts "type" string to JsonType
-bool JsonValidator::validateSchema(JsonNode::JsonType &type, const JsonNode &schema)
-{
-	if (schema.isNull())
-		return addMessage("Missing schema for current entry!");
-
-	const JsonNode &nodeType = schema["type"];
-	if (nodeType.isNull())
-		return addMessage("Entry type is not defined in schema!");
-
-	if (nodeType.getType() != JsonNode::DATA_STRING)
-		return addMessage("Entry type must be string!");
-
-	std::map<std::string, JsonNode::JsonType>::const_iterator iter = stringToType.find(nodeType.String());
-
-	if (iter == stringToType.end())
-		return addMessage("Unknown entry type found!");
-
-	type = iter->second;
-	return true;
-}
-
-//Replaces node with default value if needed and calls type-specific validators
-bool JsonValidator::validateType(JsonNode &node, const JsonNode &schema, JsonNode::JsonType type)
-{
-	if (node.isNull())
-	{
-		const JsonNode & defaultValue = schema["default"];
-		if (defaultValue.isNull())
-			return addMessage("Null entry without default entry!");
-		else
-			node = defaultValue;
-	}
-	if (minimize && node == schema["default"])
-	{
-		node.setType(JsonNode::DATA_NULL);
-		return false;
-	}
-
-	if (type != node.getType())
-	{
-		node.setType(JsonNode::DATA_NULL);
-		return addMessage("Type mismatch!");
-	}
-
-	if (type == JsonNode::DATA_VECTOR)
-		return validateItems(node, schema["items"]);
-
-	if (type == JsonNode::DATA_STRUCT)
-		return validateProperties(node, schema["properties"]);
-
-	return true;
-}
-
-// Basic checks common for any nodes
-bool JsonValidator::validateNode(JsonNode &node, const JsonNode &schema, const std::string &name)
-{
-	currentPath.push_back(name);
-
-	JsonNode::JsonType type = JsonNode::DATA_NULL;
-	if (!validateSchema(type, schema)
-	 || !validateType(node, schema, type))
-	{
-		node.setType(JsonNode::DATA_NULL);
-		currentPath.pop_back();
-		return false;
-	}
-	currentPath.pop_back();
-	return true;
-}
-
-//Checks "items" entry from schema (type-specific check for Vector)
-bool JsonValidator::validateItems(JsonNode &node, const JsonNode &schema)
-{
-	JsonNode::JsonType type = JsonNode::DATA_NULL;
-	if (!validateSchema(type, schema))
-		return false;
-
-	bool result = true;
-	BOOST_FOREACH(JsonNode &entry, node.Vector())
-	{
-		if (!validateType(entry, schema, type))
-		{
-			result = false;
-			entry.setType(JsonNode::DATA_NULL);
-		}
-	}
-	return result;
-}
-
-//Checks "propertries" entry from schema (type-specific check for Struct)
-//Function is similar to merging of two sorted lists - check every entry that present in one of the input nodes
-bool JsonValidator::validateProperties(JsonNode &node, const JsonNode &schema)
-{
-	if (schema.isNull())
-		return addMessage("Properties entry is missing for struct in schema");
-
-	JsonMap::iterator nodeIter = node.Struct().begin();
-	JsonMap::const_iterator schemaIter = schema.Struct().begin();
-
-	while (nodeIter != node.Struct().end() && schemaIter != schema.Struct().end())
-	{
-		if (nodeIter->first < schemaIter->first) //No schema for entry
-		{
-			validateNode(nodeIter->second, nullNode, nodeIter->first);
-
-			JsonMap::iterator toRemove = nodeIter++;
-			node.Struct().erase(toRemove);
-		}
-		else
-		if (schemaIter->first < nodeIter->first) //No entry
-		{
-			if (!validateNode(node[schemaIter->first], schemaIter->second, schemaIter->first))
-				node.Struct().erase(schemaIter->first);
-			schemaIter++;
-		}
-		else //both entry and schema are present
-		{
-			JsonMap::iterator current = nodeIter++;
-			if (!validateNode(current->second, schemaIter->second, current->first))
-				node.Struct().erase(current);
-
-			schemaIter++;
-		}
-	}
-	while (nodeIter != node.Struct().end())
-	{
-		validateNode(nodeIter->second, nullNode, nodeIter->first);
-		JsonMap::iterator toRemove = nodeIter++;
-		node.Struct().erase(toRemove);
-	}
-
-	while (schemaIter != schema.Struct().end())
-	{
-		if (!validateNode(node[schemaIter->first], schemaIter->second, schemaIter->first))
-			node.Struct().erase(schemaIter->first);
-		schemaIter++;
-	}
-	return true;
-}
-
-bool JsonValidator::addMessage(const std::string &message)
-{
-	std::ostringstream stream;
-
-	stream << "At ";
-	BOOST_FOREACH(const std::string &path, currentPath)
-		stream << path<<"/";
-	stream << "\t Error: " << message <<"\n";
-	errors += stream.str();
-	return false;
-}
-
-JsonValidator::JsonValidator(JsonNode &root, bool Minimize):
-	minimize(Minimize)
-{
-	if (root.getType() != JsonNode::DATA_STRUCT)
-		return;
-
-	JsonNode schema;
-	schema.swap(root["schema"]);
-	root.Struct().erase("schema");
-
-	if (!schema.isNull())
-	{
-		validateProperties(root, schema);
-	}
-	//This message is quite annoying now - most files do not have schemas. May be re-enabled later
-	//else
-	//	addMessage("Schema not found!", true);
-
-	//TODO: better way to show errors (like printing file name as well)
-	tlog3<<errors;
-}
-
-JsonValidator::JsonValidator(JsonNode &root, const JsonNode &schema, bool Minimize):
-	minimize(Minimize)
-{
-	validateProperties(root, schema);
-	if (schema.isNull())
-		addMessage("Schema not found!");
-	tlog3<<errors;
-}
-
-Bonus * JsonUtils::parseBonus (const JsonVector &ability_vec) //TODO: merge with AddAbility, create universal parser for all bonus properties
-{
-	Bonus * b = new Bonus();
-	std::string type = ability_vec[0].String();
-	auto it = bonusNameMap.find(type);
-	if (it == bonusNameMap.end())
-	{
-		tlog1 << "Error: invalid ability type " << type << " in creatures.txt" << std::endl;
-		return b;
-	}
-	b->type = it->second;
-	b->val = ability_vec[1].Float();
-	b->subtype = ability_vec[2].Float();
-	b->additionalInfo = ability_vec[3].Float();
-	b->duration = Bonus::PERMANENT; //TODO: handle flags (as integer)
-	b->turnsRemain = 0;
-	return b;
-}
-template <typename T>
-const T & parseByMap(const std::map<std::string, T> & map, const JsonNode * val, std::string err)
-{
-	static T defaultValue;
-	if (!val->isNull())
-	{
-		std::string type = val->String();
-		auto it = map.find(type);
-		if (it == map.end())
-		{
-			tlog1 << "Error: invalid " << err << type << std::endl;
-			return defaultValue;
-		}
-		else
-		{
-			return it->second;
-		}
-	}
-	else
-		return defaultValue;
-};
-
-Bonus * JsonUtils::parseBonus (const JsonNode &ability)
-{
-
-	Bonus * b = new Bonus();
-	const JsonNode *value;
-
-	std::string type = ability["type"].String();
-	auto it = bonusNameMap.find(type);
-	if (it == bonusNameMap.end())
-	{
-		tlog1 << "Error: invalid ability type " << type << std::endl;
-		return b;
-	}
-	b->type = it->second;
-
-	value = &ability["subtype"];
-	if (!value->isNull())
-		b->subtype = value->Float();
-
-	value = &ability["val"];
-	if (!value->isNull())
-		b->val = value->Float();
-
-	value = &ability["valueType"];
-	if (!value->isNull())
-		b->valType = parseByMap(bonusValueMap, value, "value type ");
-
-	value = &ability["additionalInfo"];
-	if (!value->isNull())
-		b->additionalInfo = value->Float();
-
-	value = &ability["turns"];
-	if (!value->isNull())
-		b->turnsRemain = value->Float();
-
-	value = &ability["sourceID"];
-	if (!value->isNull())
-		b->sid = value->Float();
-
-	value = &ability["description"];
-	if (!value->isNull())
-		b->description = value->String();
-
-	value = &ability["effectRange"];
-	if (!value->isNull())
-		b->valType = parseByMap(bonusLimitEffect, value, "effect range ");
-	value = &ability["duration"];
-	if (!value->isNull())
-		b->valType = parseByMap(bonusDurationMap, value, "duration type ");
-	value = &ability["source"];
-	if (!value->isNull())
-		b->valType = parseByMap(bonusSourceMap, value, "source type ");
-
-	value = &ability["limiter"];
-	if (!value->isNull())
-		b->limiter = parseByMap(bonusLimiterMap, value, "limiter type ");
-
-	value = &ability["propagator"];
-	if (!value->isNull())
-		b->propagator = parseByMap(bonusPropagatorMap, value, "propagator type ");
-
-	return b;
-}
-
-//returns first Key with value equal to given one
-template<class Key, class Val>
-Key reverseMapFirst(const Val & val, const std::map<Key, Val> map)
-{
-	BOOST_FOREACH(auto it, map)
-	{
-		if(it.second == val)
-		{
-			return it.first;
-		}
-	}
-	assert(0);
-	return "";
-}
-
-void JsonUtils::unparseBonus( JsonNode &node, const Bonus * bonus )
-{
-	node["type"].String() = reverseMapFirst<std::string, int>(bonus->type, bonusNameMap);
-	node["subtype"].Float() = bonus->subtype;
-	node["val"].Float() = bonus->val;
-	node["valueType"].String() = reverseMapFirst<std::string, int>(bonus->valType, bonusValueMap);
-	node["additionalInfo"].Float() = bonus->additionalInfo;
-	node["turns"].Float() = bonus->turnsRemain;
-	node["sourceID"].Float() = bonus->source;
-	node["description"].String() = bonus->description;
-	node["effectRange"].String() = reverseMapFirst<std::string, int>(bonus->effectRange, bonusLimitEffect);
-	node["duration"].String() = reverseMapFirst<std::string, int>(bonus->duration, bonusDurationMap);
-	node["source"].String() = reverseMapFirst<std::string, int>(bonus->source, bonusSourceMap);
-	if(bonus->limiter)
-	{
-		node["limiter"].String() = reverseMapFirst<std::string, TLimiterPtr>(bonus->limiter, bonusLimiterMap);
-	}
-	if(bonus->propagator)
-	{
-		node["propagator"].String() = reverseMapFirst<std::string, TPropagatorPtr>(bonus->propagator, bonusPropagatorMap);
-	}
-}
-
-void JsonUtils::minimize(JsonNode & node, const JsonNode& schema)
-{
-	JsonValidator validator(node, schema, true);
-}
-
-void JsonUtils::validate(JsonNode & node, const JsonNode& schema)
-{
-	JsonValidator validator(node, schema, false);
-}
-
-void JsonUtils::merge(JsonNode & dest, JsonNode & source)
-{
-	if (dest.getType() == JsonNode::DATA_NULL)
-	{
-		std::swap(dest, source);
-		return;
-	}
-
-	switch (source.getType())
-	{
-		break; case JsonNode::DATA_NULL:   dest.setType(JsonNode::DATA_NULL);
-		break; case JsonNode::DATA_BOOL:   std::swap(dest.Bool(), source.Bool());
-		break; case JsonNode::DATA_FLOAT:  std::swap(dest.Float(), source.Float());
-		break; case JsonNode::DATA_STRING: std::swap(dest.String(), source.String());
-		break; case JsonNode::DATA_VECTOR:
-		{
-			size_t total = std::min(source.Vector().size(), dest.Vector().size());
-
-			for (size_t i=0; i< total; i++)
-				merge(dest.Vector()[i], source.Vector()[i]);
-
-			if (source.Vector().size() < dest.Vector().size())
-			{
-				//reserve place and *move* data from source to dest
-				source.Vector().reserve(source.Vector().size() + dest.Vector().size());
-
-				std::move(source.Vector().begin(), source.Vector().end(),
-				          std::back_inserter(dest.Vector()));
-			}
-		}
-		break; case JsonNode::DATA_STRUCT:
-		{
-			//recursively merge all entries from struct
-			BOOST_FOREACH(auto & node, source.Struct())
-				merge(dest[node.first], node.second);
-		}
-	}
-}
-
-void JsonUtils::mergeCopy(JsonNode & dest, JsonNode source)
-{
-	// uses copy created in stack to safely merge two nodes
-	merge(dest, source);
-}
-
-JsonNode JsonUtils::assembleFromFiles(std::vector<std::string> files)
-{
-	JsonNode result;
-
-	BOOST_FOREACH(std::string file, files)
-	{
-		JsonNode section(ResourceID(file, EResType::TEXT));
-		merge(result, section);
-	}
-	return result;
-}
+/*
+ * JsonNode.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#include "StdInc.h"
+#include "JsonNode.h"
+
+#include "HeroBonus.h"
+#include "Filesystem/CResourceLoader.h"
+
+using namespace JsonDetail;
+
+static const JsonNode nullNode;
+
+JsonNode::JsonNode(JsonType Type):
+	type(DATA_NULL)
+{
+	setType(Type);
+}
+
+JsonNode::JsonNode(const char *data, size_t datasize):
+	type(DATA_NULL)
+{
+	JsonParser parser(data, datasize, *this);
+	JsonValidator validator(*this);
+}
+
+JsonNode::JsonNode(ResourceID && fileURI):
+	type(DATA_NULL)
+{
+	std::string filename = CResourceHandler::get()->getResourceName(fileURI);
+	FILE * file = fopen(filename.c_str(), "rb");
+	if (!file)
+	{
+		tlog1 << "Failed to open file " << filename << "\n";
+		perror("Last system error was ");
+		return;
+	}
+
+	fseek(file, 0, SEEK_END);
+	size_t datasize = ftell(file);
+	fseek(file, 0, SEEK_SET);
+
+	char *input = new char[datasize];
+	datasize = fread((void*)input, 1, datasize, file);
+	fclose(file);
+
+	JsonParser parser(input, datasize, *this);
+	JsonValidator validator(*this);
+	delete [] input;
+}
+
+JsonNode::JsonNode(const JsonNode &copy):
+	type(DATA_NULL)
+{
+	setType(copy.getType());
+	switch(type)
+	{
+		break; case DATA_NULL:
+		break; case DATA_BOOL:   Bool() =   copy.Bool();
+		break; case DATA_FLOAT:  Float() =  copy.Float();
+		break; case DATA_STRING: String() = copy.String();
+		break; case DATA_VECTOR: Vector() = copy.Vector();
+		break; case DATA_STRUCT: Struct() = copy.Struct();
+	}
+}
+
+JsonNode::~JsonNode()
+{
+	setType(DATA_NULL);
+}
+
+void JsonNode::swap(JsonNode &b)
+{
+	using std::swap;
+	swap(data, b.data);
+	swap(type, b.type);
+}
+
+JsonNode & JsonNode::operator =(JsonNode node)
+{
+	swap(node);
+	return *this;
+}
+
+bool JsonNode::operator == (const JsonNode &other) const
+{
+	if (getType() == other.getType())
+	{
+		switch(type)
+		{
+			break; case DATA_NULL:   return true;
+			break; case DATA_BOOL:   return Bool() == other.Bool();
+			break; case DATA_FLOAT:  return Float() == other.Float();
+			break; case DATA_STRING: return String() == other.String();
+			break; case DATA_VECTOR: return Vector() == other.Vector();
+			break; case DATA_STRUCT: return Struct() == other.Struct();
+		}
+	}
+	return false;
+}
+
+bool JsonNode::operator != (const JsonNode &other) const
+{
+	return !(*this == other);
+}
+
+JsonNode::JsonType JsonNode::getType() const
+{
+	return type;
+}
+
+void JsonNode::setType(JsonType Type)
+{
+	if (type == Type)
+		return;
+
+	//Reset node to NULL
+	if (Type != DATA_NULL)
+		setType(DATA_NULL);
+
+	switch (type)
+	{
+		break; case DATA_STRING:  delete data.String;
+		break; case DATA_VECTOR:  delete data.Vector;
+		break; case DATA_STRUCT:  delete data.Struct;
+		break; default:
+		break;
+	}
+	//Set new node type
+	type = Type;
+	switch(type)
+	{
+		break; case DATA_NULL:
+		break; case DATA_BOOL:   data.Bool = false;
+		break; case DATA_FLOAT:  data.Float = 0;
+		break; case DATA_STRING: data.String = new std::string;
+		break; case DATA_VECTOR: data.Vector = new JsonVector;
+		break; case DATA_STRUCT: data.Struct = new JsonMap;
+	}
+}
+
+bool JsonNode::isNull() const
+{
+	return type == DATA_NULL;
+}
+
+bool & JsonNode::Bool()
+{
+	setType(DATA_BOOL);
+	return data.Bool;
+}
+
+double & JsonNode::Float()
+{
+	setType(DATA_FLOAT);
+	return data.Float;
+}
+
+std::string & JsonNode::String()
+{
+	setType(DATA_STRING);
+	return *data.String;
+}
+
+JsonVector & JsonNode::Vector()
+{
+	setType(DATA_VECTOR);
+	return *data.Vector;
+}
+
+JsonMap & JsonNode::Struct()
+{
+	setType(DATA_STRUCT);
+	return *data.Struct;
+}
+
+const bool boolDefault = false;
+const bool & JsonNode::Bool() const
+{
+	if (type == DATA_NULL)
+		return boolDefault;
+	assert(type == DATA_BOOL);
+	return data.Bool;
+}
+
+const double floatDefault = 0;
+const double & JsonNode::Float() const
+{
+	if (type == DATA_NULL)
+		return floatDefault;
+	assert(type == DATA_FLOAT);
+	return data.Float;
+}
+
+const std::string stringDefault = std::string();
+const std::string & JsonNode::String() const
+{
+	if (type == DATA_NULL)
+		return stringDefault;
+	assert(type == DATA_STRING);
+	return *data.String;
+}
+
+const JsonVector vectorDefault = JsonVector();
+const JsonVector & JsonNode::Vector() const
+{
+	if (type == DATA_NULL)
+		return vectorDefault;
+	assert(type == DATA_VECTOR);
+	return *data.Vector;
+}
+
+const JsonMap mapDefault = JsonMap();
+const JsonMap & JsonNode::Struct() const
+{
+	if (type == DATA_NULL)
+		return mapDefault;
+	assert(type == DATA_STRUCT);
+	return *data.Struct;
+}
+
+JsonNode & JsonNode::operator[](std::string child)
+{
+	return Struct()[child];
+}
+
+const JsonNode & JsonNode::operator[](std::string child) const
+{
+	JsonMap::const_iterator it = Struct().find(child);
+	if (it != Struct().end())
+		return it->second;
+	return nullNode;
+}
+////////////////////////////////////////////////////////////////////////////////
+
+template<typename Iterator>
+void JsonWriter::writeContainer(Iterator begin, Iterator end)
+{
+	if (begin == end)
+		return;
+
+	prefix += '\t';
+	end--;
+	while (begin != end)
+	{
+		writeEntry(begin++);
+		out<<",\n";
+	}
+
+	writeEntry(begin);
+	out<<"\n";
+	prefix.resize(prefix.size()-1);
+}
+
+void JsonWriter::writeEntry(JsonMap::const_iterator entry)
+{
+	out << prefix;
+	writeString(entry->first);
+	out << " : ";
+	writeNode(entry->second);
+}
+
+void JsonWriter::writeEntry(JsonVector::const_iterator entry)
+{
+	out << prefix;
+	writeNode(*entry);
+}
+
+void JsonWriter::writeString(const std::string &string)
+{
+	static const std::string escaped = "\"\\/\b\f\n\r\t";
+
+	out <<'\"';
+	size_t pos=0, start=0;
+	for (; pos<string.size(); pos++)
+	{
+		size_t escapedChar = escaped.find(string[pos]);
+
+		if (escapedChar != std::string::npos)
+		{
+			out.write(string.data()+start, pos - start);
+			out << '\\' << escaped[escapedChar];
+			start = pos;
+		}
+	}
+	out.write(string.data()+start, pos - start);
+	out <<'\"';
+}
+
+void JsonWriter::writeNode(const JsonNode &node)
+{
+	switch(node.getType())
+	{
+		break; case JsonNode::DATA_NULL:
+			out << "null";
+
+		break; case JsonNode::DATA_BOOL:
+			if (node.Bool())
+				out << "true";
+			else
+				out << "false";
+
+		break; case JsonNode::DATA_FLOAT:
+			out << node.Float();
+
+		break; case JsonNode::DATA_STRING:
+			writeString(node.String());
+
+		break; case JsonNode::DATA_VECTOR:
+			out << "[" << "\n";
+			writeContainer(node.Vector().begin(), node.Vector().end());
+			out << prefix << "]";
+
+		break; case JsonNode::DATA_STRUCT:
+			out << "{" << "\n";
+			writeContainer(node.Struct().begin(), node.Struct().end());
+			out << prefix << "}";
+	}
+}
+
+JsonWriter::JsonWriter(std::ostream &output, const JsonNode &node):
+	out(output)
+{
+	writeNode(node);
+}
+
+std::ostream & operator<<(std::ostream &out, const JsonNode &node)
+{
+	JsonWriter(out, node);
+	return out << "\n";
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+JsonParser::JsonParser(const char * inputString, size_t stringSize, JsonNode &root):
+	input(inputString, stringSize),
+	lineCount(1),
+	lineStart(0),
+	pos(0)
+{
+	extractValue(root);
+	extractWhitespace(false);
+
+	//Warn if there are any non-whitespace symbols left
+	if (pos < input.size())
+		error("Not all file was parsed!", true);
+
+	//TODO: better way to show errors (like printing file name as well)
+	tlog3<<errors;
+}
+
+bool JsonParser::extractSeparator()
+{
+	if (!extractWhitespace())
+		return false;
+
+	if ( input[pos] !=':')
+		return error("Separator expected");
+
+	pos++;
+	return true;
+}
+
+bool JsonParser::extractValue(JsonNode &node)
+{
+	if (!extractWhitespace())
+		return false;
+
+	switch (input[pos])
+	{
+		case '\"': return extractString(node);
+		case 'n' : return extractNull(node);
+		case 't' : return extractTrue(node);
+		case 'f' : return extractFalse(node);
+		case '{' : return extractStruct(node);
+		case '[' : return extractArray(node);
+		case '-' : return extractFloat(node);
+		default:
+		{
+			if (input[pos] >= '0' && input[pos] <= '9')
+				return extractFloat(node);
+			return error("Value expected!");
+		}
+	}
+}
+
+bool JsonParser::extractWhitespace(bool verbose)
+{
+	while (true)
+	{
+		while (pos < input.size() && (ui8)input[pos] <= ' ')
+		{
+			if (input[pos] == '\n')
+			{
+				lineCount++;
+				lineStart = pos+1;
+			}
+			pos++;
+		}
+		if (pos >= input.size() || input[pos] != '/')
+			break;
+
+		pos++;
+		if (pos == input.size())
+			break;
+		if (input[pos] == '/')
+			pos++;
+		else
+			error("Comments must consist from two slashes!", true);
+
+		while (pos < input.size() && input[pos] != '\n')
+			pos++;
+	}
+
+	if (pos >= input.size() && verbose)
+		return error("Unexpected end of file!");
+	return true;
+}
+
+bool JsonParser::extractEscaping(std::string &str)
+{
+	switch(input[pos])
+	{
+		break; case '\"': str += '\"';
+		break; case '\\': str += '\\';
+		break; case  '/': str += '/';
+		break; case 'b': str += '\b';
+		break; case 'f': str += '\f';
+		break; case 'n': str += '\n';
+		break; case 'r': str += '\r';
+		break; case 't': str += '\t';
+		break; default: return error("Unknown escape sequence!", true);
+	};
+	return true;
+}
+
+bool JsonParser::extractString(std::string &str)
+{
+	if (input[pos] != '\"')
+		return error("String expected!");
+	pos++;
+
+	size_t first = pos;
+
+	while (pos != input.size())
+	{
+		if (input[pos] == '\"') // Correct end of string
+		{
+			str.append( &input[first], pos-first);
+			pos++;
+			return true;
+		}
+		if (input[pos] == '\\') // Escaping
+		{
+			str.append( &input[first], pos-first);
+			pos++;
+			if (pos == input.size())
+				break;
+			extractEscaping(str);
+			first = pos + 1;
+		}
+		if (input[pos] == '\n') // end-of-line
+		{
+			str.append( &input[first], pos-first);
+			return error("Closing quote not found!", true);
+		}
+		if ((unsigned char)(input[pos]) < ' ') // control character
+		{
+			str.append( &input[first], pos-first);
+			first = pos+1;
+			error("Illegal character in the string!", true);
+		}
+		pos++;
+	}
+	return error("Unterminated string!");
+}
+
+bool JsonParser::extractString(JsonNode &node)
+{
+	std::string str;
+	if (!extractString(str))
+		return false;
+
+	node.setType(JsonNode::DATA_STRING);
+	node.String() = str;
+	return true;
+}
+
+bool JsonParser::extractLiteral(const std::string &literal)
+{
+	if (literal.compare(0, literal.size(), &input[pos], literal.size()) != 0)
+	{
+		while (pos < input.size() && ((input[pos]>'a' && input[pos]<'z')
+		                           || (input[pos]>'A' && input[pos]<'Z')))
+			pos++;
+		return error("Unknown literal found", true);
+	}
+
+	pos += literal.size();
+	return true;
+}
+
+bool JsonParser::extractNull(JsonNode &node)
+{
+	if (!extractLiteral("null"))
+		return false;
+
+	node.setType(JsonNode::DATA_NULL);
+	return true;
+}
+
+bool JsonParser::extractTrue(JsonNode &node)
+{
+	if (!extractLiteral("true"))
+		return false;
+
+	node.Bool() = true;
+	return true;
+}
+
+bool JsonParser::extractFalse(JsonNode &node)
+{
+	if (!extractLiteral("false"))
+		return false;
+
+	node.Bool() = false;
+	return true;
+}
+
+bool JsonParser::extractStruct(JsonNode &node)
+{
+	node.setType(JsonNode::DATA_STRUCT);
+	pos++;
+
+	if (!extractWhitespace())
+		return false;
+
+	//Empty struct found
+	if (input[pos] == '}')
+	{
+		pos++;
+		return true;
+	}
+
+	while (true)
+	{
+		if (!extractWhitespace())
+			return false;
+
+		std::string key;
+		if (!extractString(key))
+			return false;
+
+		if (node.Struct().find(key) != node.Struct().end())
+			error("Dublicated element encountered!", true);
+
+		if (!extractSeparator())
+			return false;
+
+		if (!extractElement(node.Struct()[key], '}'))
+			return false;
+
+		if (input[pos] == '}')
+		{
+			pos++;
+			return true;
+		}
+	}
+}
+
+bool JsonParser::extractArray(JsonNode &node)
+{
+	pos++;
+	node.setType(JsonNode::DATA_VECTOR);
+
+	if (!extractWhitespace())
+		return false;
+
+	//Empty array found
+	if (input[pos] == ']')
+	{
+		pos++;
+		return true;
+	}
+
+	while (true)
+	{
+		//NOTE: currently 50% of time is this vector resizing.
+		//May be useful to use list during parsing and then swap() all items to vector
+		node.Vector().resize(node.Vector().size()+1);
+
+		if (!extractElement(node.Vector().back(), ']'))
+			return false;
+
+		if (input[pos] == ']')
+		{
+			pos++;
+			return true;
+		}
+	}
+}
+
+bool JsonParser::extractElement(JsonNode &node, char terminator)
+{
+	if (!extractValue(node))
+		return false;
+
+	if (!extractWhitespace())
+		return false;
+
+	bool comma = (input[pos] == ',');
+	if (comma )
+	{
+		pos++;
+		if (!extractWhitespace())
+			return false;
+	}
+
+	if (input[pos] == terminator)
+		return true;
+
+	if (!comma)
+		error("Comma expected!", true);
+
+	return true;
+}
+
+bool JsonParser::extractFloat(JsonNode &node)
+{
+	assert(input[pos] == '-' || (input[pos] >= '0' && input[pos] <= '9'));
+	bool negative=false;
+	double result=0;
+
+	if (input[pos] == '-')
+	{
+		pos++;
+		negative = true;
+	}
+
+	if (input[pos] < '0' || input[pos] > '9')
+		return error("Number expected!");
+
+	//Extract integer part
+	while (input[pos] >= '0' && input[pos] <= '9')
+	{
+		result = result*10+(input[pos]-'0');
+		pos++;
+	}
+
+	if (input[pos] == '.')
+	{
+		//extract fractional part
+		pos++;
+		double fractMult = 0.1;
+		if (input[pos] < '0' || input[pos] > '9')
+			return error("Decimal part expected!");
+
+		while (input[pos] >= '0' && input[pos] <= '9')
+		{
+			result = result + fractMult*(input[pos]-'0');
+			fractMult /= 10;
+			pos++;
+		}
+	}
+	//TODO: exponential part
+	if (negative)
+		result = -result;
+
+	node.setType(JsonNode::DATA_FLOAT);
+	node.Float() = result;
+	return true;
+}
+
+bool JsonParser::error(const std::string &message, bool warning)
+{
+	std::ostringstream stream;
+	std::string type(warning?" warning: ":" error: ");
+
+	stream << "At line " << lineCount << ", position "<<pos-lineStart
+	       << type << message <<"\n";
+	errors += stream.str();
+
+	return warning;
+}
+
+static const std::map<std::string, JsonNode::JsonType> stringToType =
+	boost::assign::map_list_of
+		("null",   JsonNode::DATA_NULL)   ("bool",   JsonNode::DATA_BOOL)
+		("number", JsonNode::DATA_FLOAT)  ("string", JsonNode::DATA_STRING)
+		("array",  JsonNode::DATA_VECTOR) ("object", JsonNode::DATA_STRUCT);
+
+//Check current schema entry for validness and converts "type" string to JsonType
+bool JsonValidator::validateSchema(JsonNode::JsonType &type, const JsonNode &schema)
+{
+	if (schema.isNull())
+		return addMessage("Missing schema for current entry!");
+
+	const JsonNode &nodeType = schema["type"];
+	if (nodeType.isNull())
+		return addMessage("Entry type is not defined in schema!");
+
+	if (nodeType.getType() != JsonNode::DATA_STRING)
+		return addMessage("Entry type must be string!");
+
+	std::map<std::string, JsonNode::JsonType>::const_iterator iter = stringToType.find(nodeType.String());
+
+	if (iter == stringToType.end())
+		return addMessage("Unknown entry type found!");
+
+	type = iter->second;
+	return true;
+}
+
+//Replaces node with default value if needed and calls type-specific validators
+bool JsonValidator::validateType(JsonNode &node, const JsonNode &schema, JsonNode::JsonType type)
+{
+	if (node.isNull())
+	{
+		const JsonNode & defaultValue = schema["default"];
+		if (defaultValue.isNull())
+			return addMessage("Null entry without default entry!");
+		else
+			node = defaultValue;
+	}
+	if (minimize && node == schema["default"])
+	{
+		node.setType(JsonNode::DATA_NULL);
+		return false;
+	}
+
+	if (type != node.getType())
+	{
+		node.setType(JsonNode::DATA_NULL);
+		return addMessage("Type mismatch!");
+	}
+
+	if (type == JsonNode::DATA_VECTOR)
+		return validateItems(node, schema["items"]);
+
+	if (type == JsonNode::DATA_STRUCT)
+		return validateProperties(node, schema["properties"]);
+
+	return true;
+}
+
+// Basic checks common for any nodes
+bool JsonValidator::validateNode(JsonNode &node, const JsonNode &schema, const std::string &name)
+{
+	currentPath.push_back(name);
+
+	JsonNode::JsonType type = JsonNode::DATA_NULL;
+	if (!validateSchema(type, schema)
+	 || !validateType(node, schema, type))
+	{
+		node.setType(JsonNode::DATA_NULL);
+		currentPath.pop_back();
+		return false;
+	}
+	currentPath.pop_back();
+	return true;
+}
+
+//Checks "items" entry from schema (type-specific check for Vector)
+bool JsonValidator::validateItems(JsonNode &node, const JsonNode &schema)
+{
+	JsonNode::JsonType type = JsonNode::DATA_NULL;
+	if (!validateSchema(type, schema))
+		return false;
+
+	bool result = true;
+	BOOST_FOREACH(JsonNode &entry, node.Vector())
+	{
+		if (!validateType(entry, schema, type))
+		{
+			result = false;
+			entry.setType(JsonNode::DATA_NULL);
+		}
+	}
+	return result;
+}
+
+//Checks "propertries" entry from schema (type-specific check for Struct)
+//Function is similar to merging of two sorted lists - check every entry that present in one of the input nodes
+bool JsonValidator::validateProperties(JsonNode &node, const JsonNode &schema)
+{
+	if (schema.isNull())
+		return addMessage("Properties entry is missing for struct in schema");
+
+	JsonMap::iterator nodeIter = node.Struct().begin();
+	JsonMap::const_iterator schemaIter = schema.Struct().begin();
+
+	while (nodeIter != node.Struct().end() && schemaIter != schema.Struct().end())
+	{
+		if (nodeIter->first < schemaIter->first) //No schema for entry
+		{
+			validateNode(nodeIter->second, nullNode, nodeIter->first);
+
+			JsonMap::iterator toRemove = nodeIter++;
+			node.Struct().erase(toRemove);
+		}
+		else
+		if (schemaIter->first < nodeIter->first) //No entry
+		{
+			if (!validateNode(node[schemaIter->first], schemaIter->second, schemaIter->first))
+				node.Struct().erase(schemaIter->first);
+			schemaIter++;
+		}
+		else //both entry and schema are present
+		{
+			JsonMap::iterator current = nodeIter++;
+			if (!validateNode(current->second, schemaIter->second, current->first))
+				node.Struct().erase(current);
+
+			schemaIter++;
+		}
+	}
+	while (nodeIter != node.Struct().end())
+	{
+		validateNode(nodeIter->second, nullNode, nodeIter->first);
+		JsonMap::iterator toRemove = nodeIter++;
+		node.Struct().erase(toRemove);
+	}
+
+	while (schemaIter != schema.Struct().end())
+	{
+		if (!validateNode(node[schemaIter->first], schemaIter->second, schemaIter->first))
+			node.Struct().erase(schemaIter->first);
+		schemaIter++;
+	}
+	return true;
+}
+
+bool JsonValidator::addMessage(const std::string &message)
+{
+	std::ostringstream stream;
+
+	stream << "At ";
+	BOOST_FOREACH(const std::string &path, currentPath)
+		stream << path<<"/";
+	stream << "\t Error: " << message <<"\n";
+	errors += stream.str();
+	return false;
+}
+
+JsonValidator::JsonValidator(JsonNode &root, bool Minimize):
+	minimize(Minimize)
+{
+	if (root.getType() != JsonNode::DATA_STRUCT)
+		return;
+
+	JsonNode schema;
+	schema.swap(root["schema"]);
+	root.Struct().erase("schema");
+
+	if (!schema.isNull())
+	{
+		validateProperties(root, schema);
+	}
+	//This message is quite annoying now - most files do not have schemas. May be re-enabled later
+	//else
+	//	addMessage("Schema not found!", true);
+
+	//TODO: better way to show errors (like printing file name as well)
+	tlog3<<errors;
+}
+
+JsonValidator::JsonValidator(JsonNode &root, const JsonNode &schema, bool Minimize):
+	minimize(Minimize)
+{
+	validateProperties(root, schema);
+	if (schema.isNull())
+		addMessage("Schema not found!");
+	tlog3<<errors;
+}
+
+Bonus * JsonUtils::parseBonus (const JsonVector &ability_vec) //TODO: merge with AddAbility, create universal parser for all bonus properties
+{
+	Bonus * b = new Bonus();
+	std::string type = ability_vec[0].String();
+	auto it = bonusNameMap.find(type);
+	if (it == bonusNameMap.end())
+	{
+		tlog1 << "Error: invalid ability type " << type << " in creatures.txt" << std::endl;
+		return b;
+	}
+	b->type = it->second;
+	b->val = ability_vec[1].Float();
+	b->subtype = ability_vec[2].Float();
+	b->additionalInfo = ability_vec[3].Float();
+	b->duration = Bonus::PERMANENT; //TODO: handle flags (as integer)
+	b->turnsRemain = 0;
+	return b;
+}
+template <typename T>
+const T & parseByMap(const std::map<std::string, T> & map, const JsonNode * val, std::string err)
+{
+	static T defaultValue;
+	if (!val->isNull())
+	{
+		std::string type = val->String();
+		auto it = map.find(type);
+		if (it == map.end())
+		{
+			tlog1 << "Error: invalid " << err << type << std::endl;
+			return defaultValue;
+		}
+		else
+		{
+			return it->second;
+		}
+	}
+	else
+		return defaultValue;
+};
+
+Bonus * JsonUtils::parseBonus (const JsonNode &ability)
+{
+
+	Bonus * b = new Bonus();
+	const JsonNode *value;
+
+	std::string type = ability["type"].String();
+	auto it = bonusNameMap.find(type);
+	if (it == bonusNameMap.end())
+	{
+		tlog1 << "Error: invalid ability type " << type << std::endl;
+		return b;
+	}
+	b->type = it->second;
+
+	value = &ability["subtype"];
+	if (!value->isNull())
+		b->subtype = value->Float();
+
+	value = &ability["val"];
+	if (!value->isNull())
+		b->val = value->Float();
+
+	value = &ability["valueType"];
+	if (!value->isNull())
+		b->valType = parseByMap(bonusValueMap, value, "value type ");
+
+	value = &ability["additionalInfo"];
+	if (!value->isNull())
+		b->additionalInfo = value->Float();
+
+	value = &ability["turns"];
+	if (!value->isNull())
+		b->turnsRemain = value->Float();
+
+	value = &ability["sourceID"];
+	if (!value->isNull())
+		b->sid = value->Float();
+
+	value = &ability["description"];
+	if (!value->isNull())
+		b->description = value->String();
+
+	value = &ability["effectRange"];
+	if (!value->isNull())
+		b->valType = parseByMap(bonusLimitEffect, value, "effect range ");
+	value = &ability["duration"];
+	if (!value->isNull())
+		b->valType = parseByMap(bonusDurationMap, value, "duration type ");
+	value = &ability["source"];
+	if (!value->isNull())
+		b->valType = parseByMap(bonusSourceMap, value, "source type ");
+
+	value = &ability["limiter"];
+	if (!value->isNull())
+		b->limiter = parseByMap(bonusLimiterMap, value, "limiter type ");
+
+	value = &ability["propagator"];
+	if (!value->isNull())
+		b->propagator = parseByMap(bonusPropagatorMap, value, "propagator type ");
+
+	return b;
+}
+
+//returns first Key with value equal to given one
+template<class Key, class Val>
+Key reverseMapFirst(const Val & val, const std::map<Key, Val> map)
+{
+	BOOST_FOREACH(auto it, map)
+	{
+		if(it.second == val)
+		{
+			return it.first;
+		}
+	}
+	assert(0);
+	return "";
+}
+
+void JsonUtils::unparseBonus( JsonNode &node, const Bonus * bonus )
+{
+	node["type"].String() = reverseMapFirst<std::string, int>(bonus->type, bonusNameMap);
+	node["subtype"].Float() = bonus->subtype;
+	node["val"].Float() = bonus->val;
+	node["valueType"].String() = reverseMapFirst<std::string, int>(bonus->valType, bonusValueMap);
+	node["additionalInfo"].Float() = bonus->additionalInfo;
+	node["turns"].Float() = bonus->turnsRemain;
+	node["sourceID"].Float() = bonus->source;
+	node["description"].String() = bonus->description;
+	node["effectRange"].String() = reverseMapFirst<std::string, int>(bonus->effectRange, bonusLimitEffect);
+	node["duration"].String() = reverseMapFirst<std::string, int>(bonus->duration, bonusDurationMap);
+	node["source"].String() = reverseMapFirst<std::string, int>(bonus->source, bonusSourceMap);
+	if(bonus->limiter)
+	{
+		node["limiter"].String() = reverseMapFirst<std::string, TLimiterPtr>(bonus->limiter, bonusLimiterMap);
+	}
+	if(bonus->propagator)
+	{
+		node["propagator"].String() = reverseMapFirst<std::string, TPropagatorPtr>(bonus->propagator, bonusPropagatorMap);
+	}
+}
+
+void JsonUtils::minimize(JsonNode & node, const JsonNode& schema)
+{
+	JsonValidator validator(node, schema, true);
+}
+
+void JsonUtils::validate(JsonNode & node, const JsonNode& schema)
+{
+	JsonValidator validator(node, schema, false);
+}
+
+void JsonUtils::merge(JsonNode & dest, JsonNode & source)
+{
+	if (dest.getType() == JsonNode::DATA_NULL)
+	{
+		std::swap(dest, source);
+		return;
+	}
+
+	switch (source.getType())
+	{
+		break; case JsonNode::DATA_NULL:   dest.setType(JsonNode::DATA_NULL);
+		break; case JsonNode::DATA_BOOL:   std::swap(dest.Bool(), source.Bool());
+		break; case JsonNode::DATA_FLOAT:  std::swap(dest.Float(), source.Float());
+		break; case JsonNode::DATA_STRING: std::swap(dest.String(), source.String());
+		break; case JsonNode::DATA_VECTOR:
+		{
+			size_t total = std::min(source.Vector().size(), dest.Vector().size());
+
+			for (size_t i=0; i< total; i++)
+				merge(dest.Vector()[i], source.Vector()[i]);
+
+			if (source.Vector().size() < dest.Vector().size())
+			{
+				//reserve place and *move* data from source to dest
+				source.Vector().reserve(source.Vector().size() + dest.Vector().size());
+
+				std::move(source.Vector().begin(), source.Vector().end(),
+				          std::back_inserter(dest.Vector()));
+			}
+		}
+		break; case JsonNode::DATA_STRUCT:
+		{
+			//recursively merge all entries from struct
+			BOOST_FOREACH(auto & node, source.Struct())
+				merge(dest[node.first], node.second);
+		}
+	}
+}
+
+void JsonUtils::mergeCopy(JsonNode & dest, JsonNode source)
+{
+	// uses copy created in stack to safely merge two nodes
+	merge(dest, source);
+}
+
+JsonNode JsonUtils::assembleFromFiles(std::vector<std::string> files)
+{
+	JsonNode result;
+
+	BOOST_FOREACH(std::string file, files)
+	{
+		JsonNode section(ResourceID(file, EResType::TEXT));
+		merge(result, section);
+	}
+	return result;
+}