| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 | /* * CConfigHandler.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 "CConfigHandler.h"#include "filesystem/Filesystem.h"#include "GameConstants.h"#include "VCMIDirs.h"#include "json/JsonUtils.h"VCMI_LIB_NAMESPACE_BEGINSettingsStorage settings;SettingsStorage persistentStorage;SettingsStorage shortcutsConfig;template<typename Accessor>SettingsStorage::NodeAccessor<Accessor>::NodeAccessor(SettingsStorage & _parent, std::vector<std::string> _path):	parent(_parent),	path(std::move(_path)){}template<typename Accessor>SettingsStorage::NodeAccessor<Accessor> SettingsStorage::NodeAccessor<Accessor>::operator[](const std::string & nextNode) const{	std::vector<std::string> newPath = path;	newPath.push_back(nextNode);	return NodeAccessor(parent, newPath);}template<typename Accessor>SettingsStorage::NodeAccessor<Accessor>::operator Accessor() const{	return Accessor(parent, path);}template<typename Accessor>SettingsStorage::NodeAccessor<Accessor> SettingsStorage::NodeAccessor<Accessor>::operator () (std::vector<std::string> _path) const{	std::vector<std::string> newPath = path;	newPath.insert( newPath.end(), _path.begin(), _path.end());	return NodeAccessor(parent, newPath);}SettingsStorage::SettingsStorage():	write(NodeAccessor<Settings>(*this, std::vector<std::string>() )),	listen(NodeAccessor<SettingsListener>(*this, std::vector<std::string>() )){}SettingsStorage::~SettingsStorage(){	// hack for possible crash due to static destruction order (setting storage can be destroyed before all listeners have died)	for(SettingsListener * listener : listeners)		listener->terminate();}void SettingsStorage::init(const std::string & dataFilename, const std::string & schema){	this->dataFilename = dataFilename;	this->schema = schema;	JsonPath confName = JsonPath::builtin(dataFilename);	config = JsonUtils::assembleFromFiles(confName.getOriginalName());	// Probably new install. Create config file to save settings to	if (!CResourceHandler::get("local")->existsResource(confName))	{		CResourceHandler::get("local")->createResource(dataFilename);		if(schema.empty())			invalidateNode(std::vector<std::string>());	}	if(!schema.empty())	{		JsonUtils::maximize(config, schema);		JsonUtils::validate(config, schema, "settings");	}}void SettingsStorage::invalidateNode(const std::vector<std::string> &changedPath){	for(SettingsListener * listener : listeners)		listener->nodeInvalidated(changedPath);	JsonNode savedConf = config;	savedConf.Struct().erase("session");	if(!schema.empty())		JsonUtils::minimize(savedConf, schema);	std::fstream file(CResourceHandler::get()->getResourceName(JsonPath::builtin(dataFilename))->c_str(), std::ofstream::out | std::ofstream::trunc);	file << savedConf.toString();}JsonNode & SettingsStorage::getNode(const std::vector<std::string> & path){	JsonNode *node = &config;	for(const std::string & value : path)		node = &(*node)[value];	return *node;}Settings SettingsStorage::get(const std::vector<std::string> & path){	return Settings(*this, path);}const JsonNode & SettingsStorage::operator[](const std::string & value) const{	return config[value];}const JsonNode & SettingsStorage::toJsonNode() const{	return config;}SettingsListener::SettingsListener(SettingsStorage & _parent, std::vector<std::string> _path):	parent(_parent),	path(std::move(_path)){	parent.listeners.insert(this);}SettingsListener::SettingsListener(const SettingsListener &sl):	parent(sl.parent),	path(sl.path),	callback(sl.callback){	parent.listeners.insert(this);}void SettingsListener::terminate(){	wasTerminated = true;}SettingsListener::~SettingsListener(){	if (!wasTerminated)		parent.listeners.erase(this);}void SettingsListener::nodeInvalidated(const std::vector<std::string> &changedPath){	if (!callback)		return;	size_t min = std::min(path.size(), changedPath.size());	size_t mismatch = std::mismatch(path.begin(), path.begin()+min, changedPath.begin()).first - path.begin();	if (min == mismatch)		callback(parent.getNode(path));}void SettingsListener::operator() (std::function<void(const JsonNode&)> _callback){	callback = std::move(_callback);}Settings::Settings(SettingsStorage &_parent, const std::vector<std::string> &_path):	parent(_parent),	path(_path),	node(_parent.getNode(_path)),	copy(_parent.getNode(_path)){}Settings::~Settings(){	if (node != copy)		parent.invalidateNode(path);}JsonNode* Settings::operator -> (){	return &node;}const JsonNode* Settings::operator ->() const{	return &node;}const JsonNode & Settings::operator[](const std::string & value) const{	return node[value];}JsonNode & Settings::operator[](const std::string & value){	return node[value];}// Force instantiation of the SettingsStorage::NodeAccessor class template.// That way method definitions can sit in the cpp filetemplate struct SettingsStorage::NodeAccessor<SettingsListener>;template struct SettingsStorage::NodeAccessor<Settings>;VCMI_LIB_NAMESPACE_END
 |