mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-13 19:54:17 +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