mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	- new files JsonNode.cpp/.h
- CAnimation can use JSON configs from Sprites folder instead of .def files
This commit is contained in:
		| @@ -4,6 +4,7 @@ | ||||
| #include <SDL_image.h> | ||||
|  | ||||
| #include "../lib/CLodHandler.h" | ||||
| #include "../lib/JsonNode.h" | ||||
| #include "CBitmapHandler.h" | ||||
| #include "Graphics.h" | ||||
| #include "CAnimation.h" | ||||
| @@ -22,7 +23,7 @@ | ||||
| extern DLL_EXPORT CLodHandler *spriteh; | ||||
| extern DLL_EXPORT CLodHandler *bitmaph; | ||||
|  | ||||
| typedef std::map <size_t, std::vector <std::string> > source_map; | ||||
| typedef std::map <size_t, std::vector <JsonNode> > source_map; | ||||
| typedef std::map<size_t, IImage* > image_map; | ||||
| typedef std::map<size_t, image_map > group_map; | ||||
|  | ||||
| @@ -68,19 +69,6 @@ public: | ||||
| 	~CompImageLoader(); | ||||
| }; | ||||
|  | ||||
| //Small internal class for parsing texts | ||||
| class TextParser | ||||
| { | ||||
| 	int type; | ||||
| 	size_t position; | ||||
| 	std::string text; | ||||
| 	std::string getLine(); | ||||
| public: | ||||
| 	TextParser(const std::string &name); | ||||
| 	int getType() const; | ||||
| 	void parseFile(std::map<size_t, std::vector <std::string> > &result); | ||||
| }; | ||||
|  | ||||
| /************************************************************************* | ||||
|  *  DefFile, class used for def loading                                  * | ||||
|  *************************************************************************/ | ||||
| @@ -588,6 +576,23 @@ SDLImage::SDLImage(std::string filename, bool compressed): | ||||
| 		fullSize.x = surf->w; | ||||
| 		fullSize.y = surf->h; | ||||
| 	} | ||||
| 	if (compressed) | ||||
| 	{ | ||||
| 		SDL_Surface *temp = surf; | ||||
| 		// add RLE flag | ||||
| 		if (surf->format->palette) | ||||
| 		{ | ||||
| 			const SDL_Color &c = temp->format->palette->colors[0]; | ||||
| 			SDL_SetColorKey(temp, (SDL_SRCCOLORKEY | SDL_RLEACCEL), | ||||
| 				SDL_MapRGB(temp -> format, c.r, c.g, c.b)); | ||||
| 		} | ||||
| 		else | ||||
| 			SDL_SetColorKey(temp, SDL_RLEACCEL, 0); | ||||
|  | ||||
| 		// convert surface to enable RLE | ||||
| 		surf = SDL_ConvertSurface(temp, temp->format, temp->flags); | ||||
| 		SDL_FreeSurface(temp); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void SDLImage::draw(SDL_Surface *where, int posX, int posY, Rect *src, unsigned char rotation) const | ||||
| @@ -851,54 +856,6 @@ CompImage::~CompImage() | ||||
|  *  CAnimation for animations handling, can load part of file if needed  * | ||||
|  *************************************************************************/ | ||||
|  | ||||
| TextParser::TextParser(const std::string &name): | ||||
| 	type(0), | ||||
| 	position(std::string::npos) | ||||
| { | ||||
| 	if (!spriteh->haveFile(name, FILE_TEXT)) | ||||
| 		return; | ||||
| 	text = spriteh->getTextFile(name); | ||||
| 	position = text.find('\n'); | ||||
| 	std::string typeStr = text.substr(0, position); | ||||
| 	boost::algorithm::trim(typeStr); | ||||
| 	if (typeStr == "Replace") | ||||
| 		type = 1; | ||||
| 	else if (typeStr == "Append") | ||||
| 		type = 2; | ||||
| } | ||||
|  | ||||
| std::string TextParser::getLine() | ||||
| { | ||||
| 	size_t lineStart = position+1; | ||||
| 	position = text.find('\n', lineStart); | ||||
| 	return text.substr(lineStart, position-lineStart); | ||||
| } | ||||
|  | ||||
| int TextParser::getType() const | ||||
| { | ||||
| 	return type; | ||||
| } | ||||
|  | ||||
| void TextParser::parseFile(std::map<size_t, std::vector <std::string> > &result) | ||||
| { | ||||
| 	std::string baseDir = getLine(); | ||||
|  | ||||
| 	while (position != std::string::npos) | ||||
| 	{ | ||||
| 		std::string buf = getLine(); | ||||
| 		size_t currentBlock = atoi(buf.c_str()); | ||||
|  | ||||
| 		while (position != std::string::npos) | ||||
| 		{ | ||||
| 			std::string res = getLine(); | ||||
| 			boost::algorithm::trim(res); | ||||
| 			if (res.empty()) | ||||
| 				break; | ||||
| 			result[currentBlock].push_back(baseDir+res); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| IImage * CAnimation::getFromExtraDef(std::string filename) | ||||
| { | ||||
| 	size_t pos = filename.find(':'); | ||||
| @@ -936,7 +893,7 @@ bool CAnimation::loadFrame(CDefFile * file, size_t frame, size_t group) | ||||
| 	} | ||||
|  | ||||
| 	//try to get image from def | ||||
| 	if (source[group][frame].empty()) | ||||
| 	if (source[group][frame].getType() == JsonNode::DATA_NULL) | ||||
| 	{ | ||||
| 		if (compressed) | ||||
| 			images[group][frame] = new CompImage(file, frame, group); | ||||
| @@ -945,9 +902,12 @@ bool CAnimation::loadFrame(CDefFile * file, size_t frame, size_t group) | ||||
| 	} | ||||
| 	else //load from separate file | ||||
| 	{ | ||||
| 		IImage * img = getFromExtraDef(source[group][frame].c_str()); | ||||
| 		if (!img)//TODO: load compressed image | ||||
| 			img = new SDLImage(source[group][frame].c_str()); | ||||
| 		std::string filename = source[group][frame].Struct().find("file")->second.String(); | ||||
| 	 | ||||
| 		IImage * img = getFromExtraDef(filename); | ||||
| 		if (!img) | ||||
| 			img = new SDLImage(filename, compressed); | ||||
|  | ||||
| 		images[group][frame] = img; | ||||
| 		return true; | ||||
| 	} | ||||
| @@ -974,16 +934,45 @@ bool CAnimation::unloadFrame(size_t frame, size_t group) | ||||
|  | ||||
| void CAnimation::init(CDefFile * file) | ||||
| { | ||||
| 	TextParser txtFile(name); | ||||
|  | ||||
| 	if ( file && txtFile.getType() != 1 ) | ||||
| 	if (file) | ||||
| 	{ | ||||
| 		const std::map<size_t, size_t> defEntries = file->getEntries(); | ||||
|  | ||||
| 		for (std::map<size_t, size_t>::const_iterator mapIt = defEntries.begin(); mapIt!=defEntries.end(); ++mapIt) | ||||
| 			source[mapIt->first].resize(mapIt->second); | ||||
| 	} | ||||
| 	txtFile.parseFile(source); | ||||
|  | ||||
| 	if (spriteh->haveFile(name, FILE_TEXT)) | ||||
| 	{ | ||||
| 		std::string configFile = spriteh->getTextFile(name); | ||||
| 		const JsonNode &config(configFile); | ||||
|  | ||||
| 		JsonMap::const_iterator rootEntry = config.Struct().find("sequences"); | ||||
| 		if (rootEntry != config.Struct().end()) | ||||
| 		{ | ||||
| 			//TODO: Process sequences group | ||||
| 		} | ||||
| 		 | ||||
| 		rootEntry = config.Struct().find("images"); | ||||
| 		if (rootEntry != config.Struct().end()) | ||||
| 		{ | ||||
| 			JsonVector vector = rootEntry->second.Vector(); | ||||
| 			 | ||||
| 			for (JsonVector::const_iterator it = vector.begin(); it!=vector.end(); ++it) | ||||
| 			{ | ||||
| 				JsonMap::const_iterator entry = it->Struct().find("group"); | ||||
|  | ||||
| 				size_t group=0; | ||||
| 				if (entry != it->Struct().end()) | ||||
| 					group = entry->second.Float(); | ||||
|  | ||||
| 				size_t frame = it->Struct().find("frame")->second.Float(); | ||||
| 				if (source[group].size() <= frame) | ||||
| 					source[group].resize(frame+1); | ||||
| 				source[group][frame] = *it; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| CDefFile * CAnimation::getFile() const | ||||
|   | ||||
| @@ -24,6 +24,7 @@ | ||||
| struct SDL_Surface; | ||||
| class SDLImageLoader; | ||||
| class CompImageLoader; | ||||
| class JsonNode; | ||||
|  | ||||
| /// Class for def loading, methods are based on CDefHandler | ||||
| /// After loading will store general info (palette and frame offsets) and pointer to file itself | ||||
| @@ -164,7 +165,7 @@ class CAnimation | ||||
| { | ||||
| private: | ||||
| 	//source[group][position] - file with this frame, if string is empty - image located in def file | ||||
| 	std::map<size_t, std::vector <std::string> > source; | ||||
| 	std::map<size_t, std::vector <JsonNode> > source; | ||||
|  | ||||
| 	//bitmap[group][position], store objects with loaded bitmaps | ||||
| 	std::map<size_t, std::map<size_t, IImage* > > images; | ||||
| @@ -327,8 +328,8 @@ public: | ||||
| 	{ | ||||
| 		WHOLE_ANIM=-1, //just for convenience | ||||
| 		MOVING=0, //will automatically add MOVE_START and MOVE_END to queue | ||||
| 		MOUSEON=1, | ||||
| 		HOLDING=2, | ||||
| 		MOUSEON=1, //rename to IDLE | ||||
| 		HOLDING=2, //rename to STANDING | ||||
| 		HITTED=3, | ||||
| 		DEFENCE=4, | ||||
| 		DEATH=5, | ||||
| @@ -351,6 +352,8 @@ public: | ||||
| 		DHEX_ATTACK_DOWN=19, | ||||
| 		MOVE_START=20, //no need to use this two directly - MOVING will be enought | ||||
| 		MOVE_END=21 | ||||
| 		//MOUSEON=22 //special group for border-only images - IDLE will be used as base | ||||
| 		//READY=23 //same but STANDING is base | ||||
| 	}; | ||||
|  | ||||
| private: | ||||
|   | ||||
| @@ -250,6 +250,7 @@ void CLodHandler::init(const std::string lodFile, const std::string dirName) | ||||
| { | ||||
| 	#define EXT(NAME, TYPE) extMap.insert(std::pair<std::string, LodFileType>(NAME, TYPE)); | ||||
| 	EXT(".TXT", FILE_TEXT); | ||||
| 	EXT(".JSON",FILE_TEXT); | ||||
| 	EXT(".DEF", FILE_ANIMATION); | ||||
| 	EXT(".MSK", FILE_MASK); | ||||
| 	EXT(".MSG", FILE_MASK); | ||||
|   | ||||
							
								
								
									
										560
									
								
								lib/JsonNode.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										560
									
								
								lib/JsonNode.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,560 @@ | ||||
| #define VCMI_DLL | ||||
| #include "JsonNode.h" | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <fstream> | ||||
| #include <sstream> | ||||
|  | ||||
| JsonNode::JsonNode(JsonType Type): | ||||
| 	type(DATA_NULL) | ||||
| { | ||||
| 	setType(Type); | ||||
| } | ||||
|  | ||||
| JsonNode::JsonNode(std::string input): | ||||
| 	type(DATA_NULL) | ||||
| { | ||||
| 	JsonParser parser(input, *this); | ||||
| } | ||||
|  | ||||
| JsonNode::JsonNode(const JsonNode ©): | ||||
| 	type(DATA_NULL) | ||||
| { | ||||
| 	*this = copy; | ||||
| } | ||||
|  | ||||
| JsonNode::~JsonNode() | ||||
| { | ||||
| 	setType(DATA_NULL); | ||||
| } | ||||
|  | ||||
| JsonNode & JsonNode::operator =(const JsonNode &node) | ||||
| { | ||||
| 	setType(node.getType()); | ||||
| 	switch(type) | ||||
| 	{ | ||||
| 		break; case DATA_NULL: | ||||
| 		break; case DATA_BOOL:   Bool() =   node.Bool(); | ||||
| 		break; case DATA_FLOAT:  Float() =  node.Float(); | ||||
| 		break; case DATA_STRING: String() = node.String(); | ||||
| 		break; case DATA_VECTOR: Vector() = node.Vector(); | ||||
| 		break; case DATA_STRUCT: Struct() = node.Struct(); | ||||
| 	} | ||||
| 	return *this; | ||||
| } | ||||
|  | ||||
| JsonNode::JsonType JsonNode::getType() const | ||||
| { | ||||
| 	return type; | ||||
| } | ||||
|  | ||||
| void JsonNode::setType(JsonType Type) | ||||
| { | ||||
| 	if (type == Type) | ||||
| 		return; | ||||
|  | ||||
| 	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; | ||||
| 	} | ||||
| 	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::Bool() | ||||
| { | ||||
| 	setType(DATA_BOOL); | ||||
| 	return data.Bool; | ||||
| } | ||||
|  | ||||
| float & 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 & JsonNode::Bool() const | ||||
| { | ||||
| 	assert(type == DATA_BOOL); | ||||
| 	return data.Bool; | ||||
| } | ||||
|  | ||||
| const float & JsonNode::Float() const | ||||
| { | ||||
| 	assert(type == DATA_FLOAT); | ||||
| 	return data.Float; | ||||
| } | ||||
|  | ||||
| const std::string & JsonNode::String() const | ||||
| { | ||||
| 	assert(type == DATA_STRING); | ||||
| 	return *data.String; | ||||
| } | ||||
|  | ||||
| const JsonVector & JsonNode::Vector() const | ||||
| { | ||||
| 	assert(type == DATA_VECTOR); | ||||
| 	return *data.Vector; | ||||
| } | ||||
|  | ||||
| const JsonMap & JsonNode::Struct() const | ||||
| { | ||||
| 	assert(type == DATA_STRUCT); | ||||
| 	return *data.Struct; | ||||
| } | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| //Helper to write content of map/vector | ||||
| template<class iterator> | ||||
| void writeContainer(const iterator &begin, const iterator &end, std::ostream &out, std::string prefix) | ||||
| { | ||||
| 	if (begin == end) | ||||
| 		return; | ||||
|  | ||||
| 	iterator last = end; | ||||
| 	last--; | ||||
|  | ||||
| 	for (iterator it=begin; it != last; ++it) | ||||
| 	{ | ||||
| 		writeNode(it, out, prefix); | ||||
| 		out<<",\n"; | ||||
| 	} | ||||
| 	writeNode(last, out, prefix); | ||||
| 	out<<"\n"; | ||||
| } | ||||
|  | ||||
| void writeNode(JsonVector::const_iterator it, std::ostream &out, std::string prefix) | ||||
| { | ||||
| 	out << prefix; | ||||
| 	it->write(out, prefix); | ||||
| } | ||||
|  | ||||
| void writeNode(JsonMap::const_iterator it, std::ostream &out, std::string prefix) | ||||
| { | ||||
| 	out << prefix << '\"' << it->first << '\"' << " : "; | ||||
| 	it->second.write(out, prefix); | ||||
| } | ||||
|  | ||||
| void JsonNode::write(std::ostream &out, std::string prefix) const | ||||
| { | ||||
| 	switch(type) | ||||
| 	{ | ||||
| 		break; case DATA_NULL: | ||||
| 			out << "null"; | ||||
|  | ||||
| 		break; case DATA_BOOL: | ||||
| 			if (Bool()) | ||||
| 				out << "true"; | ||||
| 			else | ||||
| 				out << "false"; | ||||
|  | ||||
| 		break; case DATA_FLOAT: | ||||
| 			out << Float(); | ||||
|  | ||||
| 		break; case DATA_STRING: | ||||
| 			out << "\"" << String() << "\""; | ||||
|  | ||||
| 		break; case DATA_VECTOR: | ||||
| 			out << "[" << "\n"; | ||||
| 			writeContainer(Vector().begin(), Vector().end(), out, prefix+'\t'); | ||||
| 			out << prefix << "]"; | ||||
|  | ||||
| 		break; case DATA_STRUCT: | ||||
| 			out << "{" << "\n"; | ||||
| 			writeContainer(Struct().begin(), Struct().end(), out, prefix+'\t'); | ||||
| 			out << prefix << "}"; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| std::ostream & operator<<(std::ostream &out, const JsonNode &node) | ||||
| { | ||||
| 	node.write(out); | ||||
| 	return out << "\n"; | ||||
| } | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| JsonParser::JsonParser(const std::string inputString, JsonNode &root): | ||||
| 	input(inputString), | ||||
| 	lineCount(1), | ||||
| 	lineStart(0), | ||||
| 	pos(0) | ||||
| { | ||||
| 	extractValue(root); | ||||
|  | ||||
| 	//Warn if there are any non-whitespace symbols left | ||||
| 	if (input.find_first_not_of(" \r\t\n", pos) != std::string::npos) | ||||
| 		error("Not all file was parsed!", true); | ||||
|  | ||||
| 	//TODO: better way to show errors | ||||
| 	tlog2<<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() | ||||
| { | ||||
| 	while (true) | ||||
| 	{ | ||||
| 		while (pos < input.size() && (unsigned char)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 should have two slashes!", true); | ||||
|  | ||||
| 		pos = input.find('\n', pos); | ||||
| 	} | ||||
|  | ||||
| 	if (pos >= input.size()) | ||||
| 		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("Uknown 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 += input.substr(first, pos-first); | ||||
| 			pos++; | ||||
| 			return true; | ||||
| 		} | ||||
| 		if (input[pos] == '\\') // Escaping | ||||
| 		{ | ||||
| 			str += input.substr(first, pos-first); | ||||
| 			first = pos++; | ||||
| 			if (pos == input.size()) | ||||
| 				break; | ||||
| 			extractEscaping(str); | ||||
| 		} | ||||
| 		if (input[pos] == '\n') // end-of-line | ||||
| 		{ | ||||
| 			str += input.substr(first, pos-first); | ||||
| 			return error("Closing quote not found!", true); | ||||
| 		} | ||||
| 		if (input[pos] < ' ') // control character | ||||
| 		{ | ||||
| 			str += input.substr(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 (input.compare(pos, literal.size(), literal) != 0) | ||||
| 	{ | ||||
| 		pos = input.find_first_of(" \n\r\t", 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.setType(JsonNode::DATA_BOOL); | ||||
| 	node.Bool() = true; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool JsonParser::extractFalse(JsonNode &node) | ||||
| { | ||||
| 	if (!extractLiteral("false")) | ||||
| 		return false; | ||||
|  | ||||
| 	node.setType(JsonNode::DATA_BOOL); | ||||
| 	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); | ||||
|  | ||||
| 		JsonNode &child = node.Struct()[key]; | ||||
|  | ||||
| 		if (!extractSeparator()) | ||||
| 			return false; | ||||
|  | ||||
| 		if (!extractValue(child)) | ||||
| 			return false; | ||||
|  | ||||
| 		if (!extractWhitespace()) | ||||
| 			return false; | ||||
|  | ||||
| 		bool comma = (input[pos] == ','); | ||||
| 		if (comma ) | ||||
| 		{ | ||||
| 			pos++; | ||||
| 			if (!extractWhitespace()) | ||||
| 				return false; | ||||
| 		} | ||||
|  | ||||
| 		if (input[pos] == '}') | ||||
| 		{ | ||||
| 			pos++; | ||||
| 			return true; | ||||
| 		} | ||||
|  | ||||
| 		if (!comma) | ||||
| 			error("Comma expected!", 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) | ||||
| 	{ | ||||
| 		node.Vector().resize(node.Vector().size()+1); | ||||
|  | ||||
| 		if (!extractValue(node.Vector().back())) | ||||
| 			return false; | ||||
|  | ||||
| 		if (!extractWhitespace()) | ||||
| 			return false; | ||||
|  | ||||
| 		bool comma = (input[pos] == ','); | ||||
| 		if (comma ) | ||||
| 		{ | ||||
| 			pos++; | ||||
| 			if (!extractWhitespace()) | ||||
| 				return false; | ||||
| 		} | ||||
|  | ||||
| 		if (input[pos] == ']') | ||||
| 		{ | ||||
| 			pos++; | ||||
| 			return true; | ||||
| 		} | ||||
|  | ||||
| 		if (!comma) | ||||
| 			error("Comma expected!", true); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool JsonParser::extractFloat(JsonNode &node) | ||||
| { | ||||
| 	assert(input[pos] == '-' || (input[pos] >= '0' && input[pos] <= '9')); | ||||
| 	bool negative=false; | ||||
| 	float 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++; | ||||
| 		float 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; | ||||
| } | ||||
							
								
								
									
										111
									
								
								lib/JsonNode.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								lib/JsonNode.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "../global.h" | ||||
|  | ||||
| #include <iostream> | ||||
| #include <map> | ||||
| #include <string> | ||||
| #include <vector> | ||||
|  | ||||
| class JsonNode; | ||||
| typedef std::map <std::string, JsonNode> JsonMap; | ||||
| typedef std::vector <JsonNode> JsonVector; | ||||
|  | ||||
| class DLL_EXPORT JsonNode | ||||
| { | ||||
| public: | ||||
| 	enum JsonType | ||||
| 	{ | ||||
| 		DATA_NULL, | ||||
| 		DATA_BOOL, | ||||
| 		DATA_FLOAT, | ||||
| 		DATA_STRING, | ||||
| 		DATA_VECTOR, | ||||
| 		DATA_STRUCT | ||||
| 	}; | ||||
|  | ||||
| private: | ||||
| 	union JsonData | ||||
| 	{ | ||||
| 		bool Bool; | ||||
| 		float Float; | ||||
| 		std::string* String; | ||||
| 		JsonVector* Vector; | ||||
| 		JsonMap* Struct; | ||||
| 	}; | ||||
|  | ||||
| 	JsonType type; | ||||
| 	JsonData data; | ||||
|  | ||||
| public: | ||||
| 	//Create empty node | ||||
| 	JsonNode(JsonType Type = DATA_NULL); | ||||
| 	//Create tree from Json-formatted input | ||||
| 	JsonNode(std::string input); | ||||
| 	//Copy c-tor | ||||
| 	JsonNode(const JsonNode ©); | ||||
|  | ||||
| 	~JsonNode(); | ||||
|  | ||||
| 	// Deep copy of this node | ||||
| 	JsonNode& operator =(const JsonNode &node); | ||||
|  | ||||
| 	//Convert node to another type. Converting to NULL will clear all data | ||||
| 	void setType(JsonType Type); | ||||
| 	JsonType getType() const; | ||||
|  | ||||
| 	//non-const acessors, node will change type on type mismatch | ||||
| 	bool & Bool(); | ||||
| 	int & Int(); | ||||
| 	float & Float(); | ||||
| 	std::string & String(); | ||||
| 	JsonVector & Vector(); | ||||
| 	JsonMap & Struct(); | ||||
|  | ||||
| 	//const acessors, will cause assertion failure on type mismatch | ||||
| 	const bool & Bool() const; | ||||
| 	const int & Int() const; | ||||
| 	const float & Float() const; | ||||
| 	const std::string & String() const; | ||||
| 	const JsonVector & Vector() const; | ||||
| 	const JsonMap & Struct() const; | ||||
|  | ||||
| 	//formatted output of this node in JSON format | ||||
| 	void write(std::ostream &out, std::string prefix="") const; | ||||
| }; | ||||
|  | ||||
| std::ostream & operator<<(std::ostream &out, const JsonNode &node); | ||||
|  | ||||
|  | ||||
| //Internal class for std::string -> JsonNode conversion | ||||
| class JsonParser | ||||
| { | ||||
| 	std::string errors;     // Contains description of all encountered errors | ||||
| 	const std::string input;// Input data | ||||
| 	unsigned int lineCount; // Currently parsed line, starting from 1 | ||||
| 	size_t lineStart;       // Position of current line start | ||||
| 	size_t pos;             // Current position of parser | ||||
|  | ||||
| 	//Helpers | ||||
| 	bool extractEscaping(std::string &str); | ||||
| 	bool extractLiteral(const std::string &literal); | ||||
| 	bool extractString(std::string &string); | ||||
| 	bool extractWhitespace(); | ||||
| 	bool extractSeparator(); | ||||
|  | ||||
| 	//Methods for extracting JSON data | ||||
| 	bool extractArray(JsonNode &node); | ||||
| 	bool extractFalse(JsonNode &node); | ||||
| 	bool extractFloat(JsonNode &node); | ||||
| 	bool extractNull(JsonNode &node); | ||||
| 	bool extractString(JsonNode &node); | ||||
| 	bool extractStruct(JsonNode &node); | ||||
| 	bool extractTrue(JsonNode &node); | ||||
| 	bool extractValue(JsonNode &node); | ||||
|  | ||||
| 	//Add error\warning message to list | ||||
| 	bool error(const std::string &message, bool warning=false); | ||||
|  | ||||
| public: | ||||
| 	JsonParser(const std::string inputString, JsonNode &root); | ||||
| }; | ||||
| @@ -50,6 +50,8 @@ libvcmi_la_SOURCES = \ | ||||
| 	IGameCallback.h \ | ||||
| 	IGameEventsReceiver.h \ | ||||
| 	Interprocess.h \ | ||||
| 	JsonNode.cpp \ | ||||
| 	JsonNode.h \ | ||||
| 	NetPacks.h \ | ||||
| 	NetPacksLib.cpp \ | ||||
| 	ResourceSet.cpp \ | ||||
|   | ||||
| @@ -90,9 +90,9 @@ am_libvcmi_la_OBJECTS = libvcmi_la-BattleAction.lo \ | ||||
| 	libvcmi_la-CObjectHandler.lo libvcmi_la-CSpellHandler.lo \ | ||||
| 	libvcmi_la-CTownHandler.lo libvcmi_la-Connection.lo \ | ||||
| 	libvcmi_la-HeroBonus.lo libvcmi_la-IGameCallback.lo \ | ||||
| 	libvcmi_la-NetPacksLib.lo libvcmi_la-ResourceSet.lo \ | ||||
| 	libvcmi_la-RegisterTypes.lo libvcmi_la-VCMI_Lib.lo \ | ||||
| 	libvcmi_la-map.lo | ||||
| 	libvcmi_la-JsonNode.lo libvcmi_la-NetPacksLib.lo \ | ||||
| 	libvcmi_la-ResourceSet.lo libvcmi_la-RegisterTypes.lo \ | ||||
| 	libvcmi_la-VCMI_Lib.lo libvcmi_la-map.lo | ||||
| libvcmi_la_OBJECTS = $(am_libvcmi_la_OBJECTS) | ||||
| AM_V_lt = $(am__v_lt_$(V)) | ||||
| am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) | ||||
| @@ -330,6 +330,8 @@ libvcmi_la_SOURCES = \ | ||||
| 	IGameCallback.h \ | ||||
| 	IGameEventsReceiver.h \ | ||||
| 	Interprocess.h \ | ||||
| 	JsonNode.cpp \ | ||||
| 	JsonNode.h \ | ||||
| 	NetPacks.h \ | ||||
| 	NetPacksLib.cpp \ | ||||
| 	ResourceSet.cpp \ | ||||
| @@ -437,6 +439,7 @@ distclean-compile: | ||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-Connection.Plo@am__quote@ | ||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-HeroBonus.Plo@am__quote@ | ||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-IGameCallback.Plo@am__quote@ | ||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-JsonNode.Plo@am__quote@ | ||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-NetPacksLib.Plo@am__quote@ | ||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-RegisterTypes.Plo@am__quote@ | ||||
| @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvcmi_la-ResourceSet.Plo@am__quote@ | ||||
| @@ -635,6 +638,14 @@ libvcmi_la-IGameCallback.lo: IGameCallback.cpp | ||||
| @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ | ||||
| @am__fastdepCXX_FALSE@	$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvcmi_la_CXXFLAGS) $(CXXFLAGS) -c -o libvcmi_la-IGameCallback.lo `test -f 'IGameCallback.cpp' || echo '$(srcdir)/'`IGameCallback.cpp | ||||
|  | ||||
| libvcmi_la-JsonNode.lo: JsonNode.cpp | ||||
| @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvcmi_la_CXXFLAGS) $(CXXFLAGS) -MT libvcmi_la-JsonNode.lo -MD -MP -MF $(DEPDIR)/libvcmi_la-JsonNode.Tpo -c -o libvcmi_la-JsonNode.lo `test -f 'JsonNode.cpp' || echo '$(srcdir)/'`JsonNode.cpp | ||||
| @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libvcmi_la-JsonNode.Tpo $(DEPDIR)/libvcmi_la-JsonNode.Plo | ||||
| @am__fastdepCXX_FALSE@	$(AM_V_CXX) @AM_BACKSLASH@ | ||||
| @AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='JsonNode.cpp' object='libvcmi_la-JsonNode.lo' libtool=yes @AMDEPBACKSLASH@ | ||||
| @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ | ||||
| @am__fastdepCXX_FALSE@	$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvcmi_la_CXXFLAGS) $(CXXFLAGS) -c -o libvcmi_la-JsonNode.lo `test -f 'JsonNode.cpp' || echo '$(srcdir)/'`JsonNode.cpp | ||||
|  | ||||
| libvcmi_la-NetPacksLib.lo: NetPacksLib.cpp | ||||
| @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libvcmi_la_CXXFLAGS) $(CXXFLAGS) -MT libvcmi_la-NetPacksLib.lo -MD -MP -MF $(DEPDIR)/libvcmi_la-NetPacksLib.Tpo -c -o libvcmi_la-NetPacksLib.lo `test -f 'NetPacksLib.cpp' || echo '$(srcdir)/'`NetPacksLib.cpp | ||||
| @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libvcmi_la-NetPacksLib.Tpo $(DEPDIR)/libvcmi_la-NetPacksLib.Plo | ||||
|   | ||||
		Reference in New Issue
	
	Block a user