2013-01-16 16:02:01 +03:00
/*
* 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"
2013-04-02 20:06:43 +03:00
# include "ScopeGuard.h"
2013-01-16 16:02:01 +03:00
# include "HeroBonus.h"
2013-07-28 17:49:50 +03:00
# include "filesystem/Filesystem.h"
2013-01-16 16:02:01 +03:00
# include "VCMI_Lib.h" //for identifier resolution
# include "CModHandler.h"
2013-10-26 00:45:14 +03:00
# include "CGeneralTextHandler.h"
2013-10-26 22:33:34 +03:00
# include "JsonDetail.h"
2013-01-16 16:02:01 +03:00
using namespace JsonDetail ;
class LibClasses ;
class CModHandler ;
static const JsonNode nullNode ;
JsonNode : : JsonNode ( JsonType Type ) :
2017-11-26 23:18:18 +02:00
type ( JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
{
setType ( Type ) ;
}
JsonNode : : JsonNode ( const char * data , size_t datasize ) :
2017-11-26 23:18:18 +02:00
type ( JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
{
2013-03-09 16:16:35 +03:00
JsonParser parser ( data , datasize ) ;
* this = parser . parse ( " <unknown> " ) ;
2013-01-16 16:02:01 +03:00
}
JsonNode : : JsonNode ( ResourceID & & fileURI ) :
2017-11-26 23:18:18 +02:00
type ( JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
{
2013-07-28 17:49:50 +03:00
auto file = CResourceHandler : : get ( ) - > load ( fileURI ) - > readAll ( ) ;
2013-01-16 16:02:01 +03:00
2013-03-09 16:16:35 +03:00
JsonParser parser ( reinterpret_cast < char * > ( file . first . get ( ) ) , file . second ) ;
* this = parser . parse ( fileURI . getName ( ) ) ;
2013-01-16 16:02:01 +03:00
}
2016-01-27 10:38:35 +02:00
JsonNode : : JsonNode ( const ResourceID & fileURI ) :
2017-11-26 23:18:18 +02:00
type ( JsonType : : DATA_NULL )
2016-01-27 10:38:35 +02:00
{
auto file = CResourceHandler : : get ( ) - > load ( fileURI ) - > readAll ( ) ;
JsonParser parser ( reinterpret_cast < char * > ( file . first . get ( ) ) , file . second ) ;
* this = parser . parse ( fileURI . getName ( ) ) ;
}
2013-11-08 23:36:26 +03:00
JsonNode : : JsonNode ( ResourceID & & fileURI , bool & isValidSyntax ) :
2017-11-26 23:18:18 +02:00
type ( JsonType : : DATA_NULL )
2013-11-08 23:36:26 +03:00
{
auto file = CResourceHandler : : get ( ) - > load ( fileURI ) - > readAll ( ) ;
JsonParser parser ( reinterpret_cast < char * > ( file . first . get ( ) ) , file . second ) ;
* this = parser . parse ( fileURI . getName ( ) ) ;
isValidSyntax = parser . isValid ( ) ;
}
2013-01-16 16:02:01 +03:00
JsonNode : : JsonNode ( const JsonNode & copy ) :
2017-11-26 23:18:18 +02:00
type ( JsonType : : DATA_NULL ) ,
2013-11-06 16:42:58 +03:00
meta ( copy . meta )
2013-01-16 16:02:01 +03:00
{
setType ( copy . getType ( ) ) ;
switch ( type )
{
2017-11-26 23:18:18 +02:00
break ; case JsonType : : DATA_NULL :
break ; case JsonType : : DATA_BOOL : Bool ( ) = copy . Bool ( ) ;
break ; case JsonType : : DATA_FLOAT : Float ( ) = copy . Float ( ) ;
break ; case JsonType : : DATA_STRING : String ( ) = copy . String ( ) ;
break ; case JsonType : : DATA_VECTOR : Vector ( ) = copy . Vector ( ) ;
break ; case JsonType : : DATA_STRUCT : Struct ( ) = copy . Struct ( ) ;
break ; case JsonType : : DATA_INTEGER : Integer ( ) = copy . Integer ( ) ;
2013-01-16 16:02:01 +03:00
}
}
JsonNode : : ~ JsonNode ( )
{
2017-11-26 23:18:18 +02:00
setType ( JsonType : : DATA_NULL ) ;
2013-01-16 16:02:01 +03:00
}
void JsonNode : : swap ( JsonNode & b )
{
using std : : swap ;
2013-04-25 17:03:35 +03:00
swap ( meta , b . meta ) ;
2013-01-16 16:02:01 +03:00
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 )
{
2017-11-26 23:18:18 +02:00
case JsonType : : DATA_NULL : return true ;
case JsonType : : DATA_BOOL : return Bool ( ) = = other . Bool ( ) ;
case JsonType : : DATA_FLOAT : return Float ( ) = = other . Float ( ) ;
case JsonType : : DATA_STRING : return String ( ) = = other . String ( ) ;
case JsonType : : DATA_VECTOR : return Vector ( ) = = other . Vector ( ) ;
case JsonType : : DATA_STRUCT : return Struct ( ) = = other . Struct ( ) ;
case JsonType : : DATA_INTEGER : return Integer ( ) = = other . Integer ( ) ;
2013-01-16 16:02:01 +03:00
}
}
return false ;
}
bool JsonNode : : operator ! = ( const JsonNode & other ) const
{
return ! ( * this = = other ) ;
}
JsonNode : : JsonType JsonNode : : getType ( ) const
{
return type ;
}
2013-04-25 17:03:35 +03:00
void JsonNode : : setMeta ( std : : string metadata , bool recursive )
{
meta = metadata ;
if ( recursive )
{
switch ( type )
{
2017-11-26 23:18:18 +02:00
break ; case JsonType : : DATA_VECTOR :
2013-04-25 17:03:35 +03:00
{
2013-06-29 16:05:48 +03:00
for ( auto & node : Vector ( ) )
2013-04-25 17:03:35 +03:00
{
node . setMeta ( metadata ) ;
}
}
2017-11-26 23:18:18 +02:00
break ; case JsonType : : DATA_STRUCT :
2013-04-25 17:03:35 +03:00
{
2013-06-29 16:05:48 +03:00
for ( auto & node : Struct ( ) )
2013-04-25 17:03:35 +03:00
{
node . second . setMeta ( metadata ) ;
}
}
}
}
}
2013-01-16 16:02:01 +03:00
void JsonNode : : setType ( JsonType Type )
{
if ( type = = Type )
return ;
2016-11-13 12:38:42 +02:00
//float<->int conversion
2017-11-26 23:18:18 +02:00
if ( type = = JsonType : : DATA_FLOAT & & Type = = JsonType : : DATA_INTEGER )
2016-11-13 12:38:42 +02:00
{
si64 converted = data . Float ;
type = Type ;
data . Integer = converted ;
return ;
}
2017-11-26 23:18:18 +02:00
else if ( type = = JsonType : : DATA_INTEGER & & Type = = JsonType : : DATA_FLOAT )
2016-11-13 12:38:42 +02:00
{
double converted = data . Integer ;
type = Type ;
data . Float = converted ;
return ;
}
2013-06-26 14:18:27 +03:00
//Reset node to nullptr
2017-11-26 23:18:18 +02:00
if ( Type ! = JsonType : : DATA_NULL )
setType ( JsonType : : DATA_NULL ) ;
2013-01-16 16:02:01 +03:00
switch ( type )
{
2017-11-26 23:18:18 +02:00
break ; case JsonType : : DATA_STRING : delete data . String ;
break ; case JsonType : : DATA_VECTOR : delete data . Vector ;
break ; case JsonType : : DATA_STRUCT : delete data . Struct ;
2013-01-16 16:02:01 +03:00
break ; default :
break ;
}
//Set new node type
type = Type ;
switch ( type )
{
2017-11-26 23:18:18 +02:00
break ; case JsonType : : DATA_NULL :
break ; case JsonType : : DATA_BOOL : data . Bool = false ;
break ; case JsonType : : DATA_FLOAT : data . Float = 0 ;
break ; case JsonType : : DATA_STRING : data . String = new std : : string ( ) ;
break ; case JsonType : : DATA_VECTOR : data . Vector = new JsonVector ( ) ;
break ; case JsonType : : DATA_STRUCT : data . Struct = new JsonMap ( ) ;
break ; case JsonType : : DATA_INTEGER : data . Integer = 0 ;
2013-01-16 16:02:01 +03:00
}
}
bool JsonNode : : isNull ( ) const
{
2017-11-26 23:18:18 +02:00
return type = = JsonType : : DATA_NULL ;
2013-01-16 16:02:01 +03:00
}
2016-11-13 12:38:42 +02:00
bool JsonNode : : isNumber ( ) const
{
2017-11-26 23:18:18 +02:00
return type = = JsonType : : DATA_INTEGER | | type = = JsonType : : DATA_FLOAT ;
2016-11-13 12:38:42 +02:00
}
2017-09-16 00:23:31 +02:00
bool JsonNode : : containsBaseData ( ) const
2017-09-13 08:58:14 +02:00
{
switch ( type )
{
2017-09-15 13:32:32 +02:00
case JsonType : : DATA_NULL :
2017-09-16 00:23:31 +02:00
return false ;
2017-09-15 13:32:32 +02:00
case JsonType : : DATA_STRUCT :
for ( auto elem : * data . Struct )
{
2017-09-16 00:23:31 +02:00
if ( elem . second . containsBaseData ( ) )
return true ;
2017-09-15 13:32:32 +02:00
}
return false ;
2017-09-16 00:23:31 +02:00
default :
//other types (including vector) cannot be extended via merge
return true ;
2017-09-13 08:58:14 +02:00
}
}
2017-09-14 03:03:44 +02:00
bool JsonNode : : isCompact ( ) const
{
switch ( type )
{
2017-09-16 00:30:57 +02:00
case JsonType : : DATA_VECTOR :
for ( JsonNode & elem : * data . Vector )
{
if ( ! elem . isCompact ( ) )
return false ;
}
return true ;
case JsonType : : DATA_STRUCT :
{
int propertyCount = data . Struct - > size ( ) ;
if ( propertyCount = = 0 )
return true ;
else if ( propertyCount = = 1 )
return data . Struct - > begin ( ) - > second . isCompact ( ) ;
}
return false ;
default :
return true ;
2017-09-14 03:03:44 +02:00
}
}
2013-04-02 20:06:43 +03:00
void JsonNode : : clear ( )
{
2017-11-26 23:18:18 +02:00
setType ( JsonType : : DATA_NULL ) ;
2013-04-02 20:06:43 +03:00
}
2013-01-16 16:02:01 +03:00
bool & JsonNode : : Bool ( )
{
2017-11-26 23:18:18 +02:00
setType ( JsonType : : DATA_BOOL ) ;
2013-01-16 16:02:01 +03:00
return data . Bool ;
}
double & JsonNode : : Float ( )
{
2017-11-26 23:18:18 +02:00
setType ( JsonType : : DATA_FLOAT ) ;
2013-01-16 16:02:01 +03:00
return data . Float ;
}
2016-11-13 12:38:42 +02:00
si64 & JsonNode : : Integer ( )
{
2017-11-26 23:18:18 +02:00
setType ( JsonType : : DATA_INTEGER ) ;
2016-11-13 12:38:42 +02:00
return data . Integer ;
}
2013-01-16 16:02:01 +03:00
std : : string & JsonNode : : String ( )
{
2017-11-26 23:18:18 +02:00
setType ( JsonType : : DATA_STRING ) ;
2013-01-16 16:02:01 +03:00
return * data . String ;
}
JsonVector & JsonNode : : Vector ( )
{
2017-11-26 23:18:18 +02:00
setType ( JsonType : : DATA_VECTOR ) ;
2013-01-16 16:02:01 +03:00
return * data . Vector ;
}
JsonMap & JsonNode : : Struct ( )
{
2017-11-26 23:18:18 +02:00
setType ( JsonType : : DATA_STRUCT ) ;
2013-01-16 16:02:01 +03:00
return * data . Struct ;
}
const bool boolDefault = false ;
2016-11-13 12:38:42 +02:00
bool JsonNode : : Bool ( ) const
2013-01-16 16:02:01 +03:00
{
2017-11-26 23:18:18 +02:00
if ( type = = JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
return boolDefault ;
2017-11-26 23:18:18 +02:00
assert ( type = = JsonType : : DATA_BOOL ) ;
2013-01-16 16:02:01 +03:00
return data . Bool ;
}
const double floatDefault = 0 ;
2016-11-13 12:38:42 +02:00
double JsonNode : : Float ( ) const
2013-01-16 16:02:01 +03:00
{
2017-11-26 23:18:18 +02:00
if ( type = = JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
return floatDefault ;
2017-11-26 23:18:18 +02:00
else if ( type = = JsonType : : DATA_INTEGER )
2016-11-13 12:38:42 +02:00
return data . Integer ;
2017-11-26 23:18:18 +02:00
assert ( type = = JsonType : : DATA_FLOAT ) ;
2013-01-16 16:02:01 +03:00
return data . Float ;
}
2016-11-13 12:38:42 +02:00
const si64 integetDefault = 0 ;
si64 JsonNode : : Integer ( ) const
{
2017-11-26 23:18:18 +02:00
if ( type = = JsonType : : DATA_NULL )
2016-11-13 12:38:42 +02:00
return integetDefault ;
2017-11-26 23:18:18 +02:00
else if ( type = = JsonType : : DATA_FLOAT )
2016-11-13 12:38:42 +02:00
return data . Float ;
2017-11-26 23:18:18 +02:00
assert ( type = = JsonType : : DATA_INTEGER ) ;
2016-11-13 12:38:42 +02:00
return data . Integer ;
}
2013-01-16 16:02:01 +03:00
const std : : string stringDefault = std : : string ( ) ;
const std : : string & JsonNode : : String ( ) const
{
2017-11-26 23:18:18 +02:00
if ( type = = JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
return stringDefault ;
2017-11-26 23:18:18 +02:00
assert ( type = = JsonType : : DATA_STRING ) ;
2013-01-16 16:02:01 +03:00
return * data . String ;
}
const JsonVector vectorDefault = JsonVector ( ) ;
const JsonVector & JsonNode : : Vector ( ) const
{
2017-11-26 23:18:18 +02:00
if ( type = = JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
return vectorDefault ;
2017-11-26 23:18:18 +02:00
assert ( type = = JsonType : : DATA_VECTOR ) ;
2013-01-16 16:02:01 +03:00
return * data . Vector ;
}
const JsonMap mapDefault = JsonMap ( ) ;
const JsonMap & JsonNode : : Struct ( ) const
{
2017-11-26 23:18:18 +02:00
if ( type = = JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
return mapDefault ;
2017-11-26 23:18:18 +02:00
assert ( type = = JsonType : : DATA_STRUCT ) ;
2013-01-16 16:02:01 +03:00
return * data . Struct ;
}
JsonNode & JsonNode : : operator [ ] ( std : : string child )
{
return Struct ( ) [ child ] ;
}
const JsonNode & JsonNode : : operator [ ] ( std : : string child ) const
{
2013-06-29 16:05:48 +03:00
auto it = Struct ( ) . find ( child ) ;
2013-01-16 16:02:01 +03:00
if ( it ! = Struct ( ) . end ( ) )
return it - > second ;
return nullNode ;
}
2013-04-02 20:06:43 +03:00
// to avoid duplicating const and non-const code
template < typename Node >
Node & resolvePointer ( Node & in , const std : : string & pointer )
{
if ( pointer . empty ( ) )
return in ;
assert ( pointer [ 0 ] = = ' / ' ) ;
size_t splitPos = pointer . find ( ' / ' , 1 ) ;
std : : string entry = pointer . substr ( 1 , splitPos - 1 ) ;
std : : string remainer = splitPos = = std : : string : : npos ? " " : pointer . substr ( splitPos ) ;
2017-11-26 23:18:18 +02:00
if ( in . getType ( ) = = JsonNode : : JsonType : : DATA_VECTOR )
2013-04-02 20:06:43 +03:00
{
if ( entry . find_first_not_of ( " 0123456789 " ) ! = std : : string : : npos ) // non-numbers in string
throw std : : runtime_error ( " Invalid Json pointer " ) ;
if ( entry . size ( ) > 1 & & entry [ 0 ] = = ' 0 ' ) // leading zeros are not allowed
throw std : : runtime_error ( " Invalid Json pointer " ) ;
size_t index = boost : : lexical_cast < size_t > ( entry ) ;
if ( in . Vector ( ) . size ( ) > index )
return in . Vector ( ) [ index ] . resolvePointer ( remainer ) ;
}
return in [ entry ] . resolvePointer ( remainer ) ;
}
const JsonNode & JsonNode : : resolvePointer ( const std : : string & jsonPointer ) const
{
return : : resolvePointer ( * this , jsonPointer ) ;
}
JsonNode & JsonNode : : resolvePointer ( const std : : string & jsonPointer )
{
return : : resolvePointer ( * this , jsonPointer ) ;
}
2017-09-14 03:03:44 +02:00
std : : string JsonNode : : toJson ( bool compact ) const
2017-08-11 15:27:42 +02:00
{
std : : ostringstream out ;
2017-09-14 03:03:44 +02:00
JsonWriter writer ( out , compact ) ;
2017-08-11 15:27:42 +02:00
writer . writeNode ( * this ) ;
out < < " \n " ;
return out . str ( ) ;
}
2013-02-25 15:18:45 +03:00
///JsonUtils
2016-09-19 23:36:35 +02:00
void JsonUtils : : parseTypedBonusShort ( const JsonVector & source , std : : shared_ptr < Bonus > dest )
2013-02-25 15:18:45 +03:00
{
dest - > val = source [ 1 ] . Float ( ) ;
resolveIdentifier ( source [ 2 ] , dest - > subtype ) ;
dest - > additionalInfo = source [ 3 ] . Float ( ) ;
dest - > duration = Bonus : : PERMANENT ; //TODO: handle flags (as integer)
2016-01-27 10:38:35 +02:00
dest - > turnsRemain = 0 ;
2013-02-25 15:18:45 +03:00
}
2016-09-19 23:36:35 +02:00
std : : shared_ptr < Bonus > JsonUtils : : parseBonus ( const JsonVector & ability_vec ) //TODO: merge with AddAbility, create universal parser for all bonus properties
2013-01-16 16:02:01 +03:00
{
2016-09-19 23:36:35 +02:00
auto b = std : : make_shared < Bonus > ( ) ;
2013-01-16 16:02:01 +03:00
std : : string type = ability_vec [ 0 ] . String ( ) ;
auto it = bonusNameMap . find ( type ) ;
if ( it = = bonusNameMap . end ( ) )
{
2018-02-06 08:22:28 +02:00
logMod - > error ( " Error: invalid ability type %s. " , type ) ;
2013-01-16 16:02:01 +03:00
return b ;
}
b - > type = it - > second ;
2016-01-27 10:38:35 +02:00
2013-02-25 15:18:45 +03:00
parseTypedBonusShort ( ability_vec , b ) ;
2013-01-16 16:02:01 +03:00
return b ;
}
template < typename T >
const T & parseByMap ( const std : : map < std : : string , T > & map , const JsonNode * val , std : : string err )
{
2013-04-21 15:49:26 +03:00
static T defaultValue = T ( ) ;
2013-01-16 16:02:01 +03:00
if ( ! val - > isNull ( ) )
{
std : : string type = val - > String ( ) ;
auto it = map . find ( type ) ;
if ( it = = map . end ( ) )
{
2018-02-06 08:22:28 +02:00
logMod - > error ( " Error: invalid %s%s. " , err , type ) ;
2013-01-16 16:02:01 +03:00
return defaultValue ;
}
else
{
return it - > second ;
}
}
else
return defaultValue ;
2013-04-21 15:49:26 +03:00
}
2013-01-16 16:02:01 +03:00
2016-10-22 10:00:55 +02:00
void JsonUtils : : resolveIdentifier ( si32 & var , const JsonNode & node , std : : string name )
2013-01-16 16:02:01 +03:00
{
2013-04-25 17:03:35 +03:00
const JsonNode & value = node [ name ] ;
if ( ! value . isNull ( ) )
2013-01-16 16:02:01 +03:00
{
2013-04-25 17:03:35 +03:00
switch ( value . getType ( ) )
2013-01-16 16:02:01 +03:00
{
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_INTEGER :
2016-11-13 12:38:42 +02:00
var = value . Integer ( ) ;
break ;
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_FLOAT :
2013-04-25 17:03:35 +03:00
var = value . Float ( ) ;
2013-01-16 16:02:01 +03:00
break ;
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_STRING :
2013-04-25 17:03:35 +03:00
VLC - > modh - > identifiers . requestIdentifier ( value , [ & ] ( si32 identifier )
2013-01-16 16:02:01 +03:00
{
var = identifier ;
} ) ;
break ;
default :
2018-02-06 08:22:28 +02:00
logMod - > error ( " Error! Wrong identifier used for value of %s. " , name ) ;
2013-01-16 16:02:01 +03:00
}
}
}
2016-10-22 10:00:55 +02:00
void JsonUtils : : resolveIdentifier ( const JsonNode & node , si32 & var )
2013-01-16 16:02:01 +03:00
{
switch ( node . getType ( ) )
{
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_INTEGER :
2016-11-13 12:38:42 +02:00
var = node . Integer ( ) ;
break ;
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_FLOAT :
2013-01-16 16:02:01 +03:00
var = node . Float ( ) ;
break ;
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_STRING :
2016-10-22 10:00:55 +02:00
VLC - > modh - > identifiers . requestIdentifier ( node , [ & ] ( si32 identifier )
2013-01-16 16:02:01 +03:00
{
var = identifier ;
} ) ;
break ;
default :
2017-08-30 23:23:19 +02:00
logMod - > error ( " Error! Wrong identifier used for identifier! " ) ;
2013-01-16 16:02:01 +03:00
}
}
2016-09-19 23:36:35 +02:00
std : : shared_ptr < Bonus > JsonUtils : : parseBonus ( const JsonNode & ability )
2013-01-16 16:02:01 +03:00
{
2016-09-19 23:36:35 +02:00
auto b = std : : make_shared < Bonus > ( ) ;
2016-10-22 10:00:55 +02:00
if ( ! parseBonus ( ability , b . get ( ) ) )
{
return nullptr ;
}
return b ;
}
bool JsonUtils : : parseBonus ( const JsonNode & ability , Bonus * b )
{
2013-01-16 16:02:01 +03:00
const JsonNode * value ;
std : : string type = ability [ " type " ] . String ( ) ;
auto it = bonusNameMap . find ( type ) ;
if ( it = = bonusNameMap . end ( ) )
{
2018-02-06 08:22:28 +02:00
logMod - > error ( " Error: invalid ability type %s. " , type ) ;
2016-10-22 10:00:55 +02:00
return false ;
2013-01-16 16:02:01 +03:00
}
b - > type = it - > second ;
2016-10-22 10:00:55 +02:00
resolveIdentifier ( b - > subtype , ability , " subtype " ) ;
2013-01-16 16:02:01 +03:00
2013-03-14 20:21:16 +03:00
b - > val = ability [ " val " ] . Float ( ) ;
2013-01-16 16:02:01 +03:00
value = & ability [ " valueType " ] ;
if ( ! value - > isNull ( ) )
2013-02-02 01:04:25 +03:00
b - > valType = static_cast < Bonus : : ValueType > ( parseByMap ( bonusValueMap , value , " value type " ) ) ;
2013-01-16 16:02:01 +03:00
2016-10-22 10:00:55 +02:00
resolveIdentifier ( b - > additionalInfo , ability , " addInfo " ) ;
2013-01-16 16:02:01 +03:00
2013-03-14 20:21:16 +03:00
b - > turnsRemain = ability [ " turns " ] . Float ( ) ;
2013-01-16 16:02:01 +03:00
2013-03-14 20:21:16 +03:00
b - > sid = ability [ " sourceID " ] . Float ( ) ;
2013-01-16 16:02:01 +03:00
2013-03-14 20:21:16 +03:00
b - > description = ability [ " description " ] . String ( ) ;
2013-01-16 16:02:01 +03:00
value = & ability [ " effectRange " ] ;
if ( ! value - > isNull ( ) )
2013-02-02 01:04:25 +03:00
b - > effectRange = static_cast < Bonus : : LimitEffect > ( parseByMap ( bonusLimitEffect , value , " effect range " ) ) ;
2013-01-16 19:41:01 +03:00
2013-01-16 16:02:01 +03:00
value = & ability [ " duration " ] ;
if ( ! value - > isNull ( ) )
2013-01-16 19:41:01 +03:00
{
switch ( value - > getType ( ) )
{
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_STRING :
2013-01-16 19:41:01 +03:00
b - > duration = parseByMap ( bonusDurationMap , value , " duration type " ) ;
break ;
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_VECTOR :
2013-01-16 19:41:01 +03:00
{
ui16 dur = 0 ;
2013-06-29 16:05:48 +03:00
for ( const JsonNode & d : value - > Vector ( ) )
2013-01-16 19:41:01 +03:00
{
dur | = parseByMap ( bonusDurationMap , & d , " duration type " ) ;
}
b - > duration = dur ;
}
break ;
default :
2017-08-30 23:23:19 +02:00
logMod - > error ( " Error! Wrong bonus duration format. " ) ;
2013-01-16 19:41:01 +03:00
}
}
2013-01-16 16:02:01 +03:00
value = & ability [ " source " ] ;
if ( ! value - > isNull ( ) )
2013-02-02 01:04:25 +03:00
b - > source = static_cast < Bonus : : BonusSource > ( parseByMap ( bonusSourceMap , value , " source type " ) ) ;
2013-01-16 16:02:01 +03:00
value = & ability [ " limiters " ] ;
if ( ! value - > isNull ( ) )
{
2013-06-29 16:05:48 +03:00
for ( const JsonNode & limiter : value - > Vector ( ) )
2013-01-16 16:02:01 +03:00
{
2013-01-16 19:41:01 +03:00
switch ( limiter . getType ( ) )
2013-01-16 16:02:01 +03:00
{
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_STRING : //pre-defined limiters
2013-01-16 16:02:01 +03:00
b - > limiter = parseByMap ( bonusLimiterMap , & limiter , " limiter type " ) ;
break ;
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_STRUCT : //customizable limiters
2013-01-16 16:02:01 +03:00
{
2015-12-29 04:43:33 +02:00
std : : shared_ptr < ILimiter > l ;
2013-01-16 16:02:01 +03:00
if ( limiter [ " type " ] . String ( ) = = " CREATURE_TYPE_LIMITER " )
{
2015-12-29 04:43:33 +02:00
std : : shared_ptr < CCreatureTypeLimiter > l2 = std : : make_shared < CCreatureTypeLimiter > ( ) ; //TODO: How the hell resolve pointer to creature?
2013-01-18 14:49:15 +03:00
const JsonVector vec = limiter [ " parameters " ] . Vector ( ) ;
2013-04-25 17:03:35 +03:00
VLC - > modh - > identifiers . requestIdentifier ( " creature " , vec [ 0 ] , [ = ] ( si32 creature )
2013-01-18 14:49:15 +03:00
{
2016-10-22 10:00:55 +02:00
l2 - > setCreature ( CreatureID ( creature ) ) ;
2013-01-18 14:49:15 +03:00
} ) ;
if ( vec . size ( ) > 1 )
{
l2 - > includeUpgrades = vec [ 1 ] . Bool ( ) ;
}
else
l2 - > includeUpgrades = false ;
2013-01-19 21:52:13 +03:00
l = l2 ;
2013-01-16 16:02:01 +03:00
}
if ( limiter [ " type " ] . String ( ) = = " HAS_ANOTHER_BONUS_LIMITER " )
{
2015-12-29 04:43:33 +02:00
std : : shared_ptr < HasAnotherBonusLimiter > l2 = std : : make_shared < HasAnotherBonusLimiter > ( ) ;
2013-01-16 16:02:01 +03:00
const JsonVector vec = limiter [ " parameters " ] . Vector ( ) ;
std : : string anotherBonusType = vec [ 0 ] . String ( ) ;
2016-10-22 10:00:55 +02:00
auto it = bonusNameMap . find ( anotherBonusType ) ;
2013-01-16 16:02:01 +03:00
if ( it = = bonusNameMap . end ( ) )
{
2018-02-06 08:22:28 +02:00
logMod - > error ( " Error: invalid ability type %s. " , anotherBonusType ) ;
2013-01-16 16:02:01 +03:00
continue ;
}
l2 - > type = it - > second ;
if ( vec . size ( ) > 1 )
{
2016-10-22 10:00:55 +02:00
resolveIdentifier ( vec [ 1 ] , l2 - > subtype ) ;
2013-01-16 16:02:01 +03:00
l2 - > isSubtypeRelevant = true ;
}
l = l2 ;
}
b - > addLimiter ( l ) ;
}
break ;
}
}
}
value = & ability [ " propagator " ] ;
if ( ! value - > isNull ( ) )
b - > propagator = parseByMap ( bonusPropagatorMap , value , " propagator type " ) ;
2017-09-11 02:36:02 +02:00
value = & ability [ " updater " ] ;
2017-09-17 05:48:01 +02:00
if ( ! value - > isNull ( ) )
2017-09-11 02:36:02 +02:00
{
const JsonNode & updaterJson = * value ;
2017-09-19 00:08:28 +02:00
switch ( updaterJson . getType ( ) )
2017-09-11 02:36:02 +02:00
{
2017-09-19 00:08:28 +02:00
case JsonNode : : JsonType : : DATA_STRING :
b - > addUpdater ( parseByMap ( bonusUpdaterMap , & updaterJson , " updater type " ) ) ;
break ;
case JsonNode : : JsonType : : DATA_STRUCT :
if ( updaterJson [ " type " ] . String ( ) = = " GROWS_WITH_LEVEL " )
{
std : : shared_ptr < GrowsWithLevelUpdater > updater = std : : make_shared < GrowsWithLevelUpdater > ( ) ;
const JsonVector param = updaterJson [ " parameters " ] . Vector ( ) ;
updater - > valPer20 = param [ 0 ] . Integer ( ) ;
if ( param . size ( ) > 1 )
updater - > stepSize = param [ 1 ] . Integer ( ) ;
b - > addUpdater ( updater ) ;
}
else
logMod - > warn ( " Unknown updater type \" %s \" " , updaterJson [ " type " ] . String ( ) ) ;
break ;
2017-09-11 02:36:02 +02:00
}
}
2016-10-22 10:00:55 +02:00
return true ;
2013-01-16 16:02:01 +03:00
}
//returns first Key with value equal to given one
template < class Key , class Val >
2013-11-07 15:48:41 +03:00
Key reverseMapFirst ( const Val & val , const std : : map < Key , Val > & map )
2013-01-16 16:02:01 +03:00
{
2013-06-29 16:05:48 +03:00
for ( auto it : map )
2013-01-16 16:02:01 +03:00
{
if ( it . second = = val )
{
return it . first ;
}
}
assert ( 0 ) ;
return " " ;
}
2016-09-19 23:36:35 +02:00
void JsonUtils : : unparseBonus ( JsonNode & node , const std : : shared_ptr < Bonus > & bonus )
2013-01-16 16:02:01 +03:00
{
2013-02-16 17:03:47 +03:00
node [ " type " ] . String ( ) = reverseMapFirst < std : : string , Bonus : : BonusType > ( bonus - > type , bonusNameMap ) ;
2013-01-16 16:02:01 +03:00
node [ " subtype " ] . Float ( ) = bonus - > subtype ;
node [ " val " ] . Float ( ) = bonus - > val ;
2013-02-16 17:03:47 +03:00
node [ " valueType " ] . String ( ) = reverseMapFirst < std : : string , Bonus : : ValueType > ( bonus - > valType , bonusValueMap ) ;
2013-01-16 16:02:01 +03:00
node [ " additionalInfo " ] . Float ( ) = bonus - > additionalInfo ;
node [ " turns " ] . Float ( ) = bonus - > turnsRemain ;
node [ " sourceID " ] . Float ( ) = bonus - > source ;
node [ " description " ] . String ( ) = bonus - > description ;
2013-02-16 17:03:47 +03:00
node [ " effectRange " ] . String ( ) = reverseMapFirst < std : : string , Bonus : : LimitEffect > ( bonus - > effectRange , bonusLimitEffect ) ;
node [ " duration " ] . String ( ) = reverseMapFirst < std : : string , ui16 > ( bonus - > duration , bonusDurationMap ) ;
node [ " source " ] . String ( ) = reverseMapFirst < std : : string , Bonus : : BonusSource > ( bonus - > source , bonusSourceMap ) ;
2013-01-16 16:02:01 +03:00
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 ) ;
}
}
2013-04-02 20:06:43 +03:00
void minimizeNode ( JsonNode & node , const JsonNode & schema )
{
2013-04-03 16:39:46 +03:00
if ( schema [ " type " ] . String ( ) = = " object " )
2013-04-02 20:06:43 +03:00
{
2013-04-03 16:39:46 +03:00
std : : set < std : : string > foundEntries ;
2013-06-29 16:05:48 +03:00
for ( auto & entry : schema [ " required " ] . Vector ( ) )
2013-04-03 16:39:46 +03:00
{
std : : string name = entry . String ( ) ;
foundEntries . insert ( name ) ;
2013-04-02 20:06:43 +03:00
minimizeNode ( node [ name ] , schema [ " properties " ] [ name ] ) ;
2013-04-03 16:39:46 +03:00
if ( vstd : : contains ( node . Struct ( ) , name ) & &
node [ name ] = = schema [ " properties " ] [ name ] [ " default " ] )
{
node . Struct ( ) . erase ( name ) ;
}
}
// erase all unhandled entries
for ( auto it = node . Struct ( ) . begin ( ) ; it ! = node . Struct ( ) . end ( ) ; )
2013-04-02 20:06:43 +03:00
{
2013-04-03 16:39:46 +03:00
if ( ! vstd : : contains ( foundEntries , it - > first ) )
it = node . Struct ( ) . erase ( it ) ;
else
it + + ;
2013-04-02 20:06:43 +03:00
}
}
}
void JsonUtils : : minimize ( JsonNode & node , std : : string schemaName )
{
minimizeNode ( node , getSchema ( schemaName ) ) ;
}
2013-04-03 16:39:46 +03:00
// FIXME: except for several lines function is identical to minimizeNode. Some way to reduce duplication?
2013-04-02 20:06:43 +03:00
void maximizeNode ( JsonNode & node , const JsonNode & schema )
2013-01-16 16:02:01 +03:00
{
2013-04-03 16:39:46 +03:00
// "required" entry can only be found in object/struct
if ( schema [ " type " ] . String ( ) = = " object " )
2013-04-02 20:06:43 +03:00
{
2013-04-03 16:39:46 +03:00
std : : set < std : : string > foundEntries ;
2013-04-02 20:06:43 +03:00
2013-04-03 16:39:46 +03:00
// check all required entries that have default version
2013-06-29 16:05:48 +03:00
for ( auto & entry : schema [ " required " ] . Vector ( ) )
2013-04-02 20:06:43 +03:00
{
2013-04-03 16:39:46 +03:00
std : : string name = entry . String ( ) ;
foundEntries . insert ( name ) ;
if ( node [ name ] . isNull ( ) & &
! schema [ " properties " ] [ name ] [ " default " ] . isNull ( ) )
{
node [ name ] = schema [ " properties " ] [ name ] [ " default " ] ;
}
2013-04-02 20:06:43 +03:00
maximizeNode ( node [ name ] , schema [ " properties " ] [ name ] ) ;
2013-04-03 16:39:46 +03:00
}
// erase all unhandled entries
for ( auto it = node . Struct ( ) . begin ( ) ; it ! = node . Struct ( ) . end ( ) ; )
{
if ( ! vstd : : contains ( foundEntries , it - > first ) )
it = node . Struct ( ) . erase ( it ) ;
else
it + + ;
}
2013-04-02 20:06:43 +03:00
}
2013-01-16 16:02:01 +03:00
}
2013-04-02 20:06:43 +03:00
void JsonUtils : : maximize ( JsonNode & node , std : : string schemaName )
2013-01-16 16:02:01 +03:00
{
2013-04-02 20:06:43 +03:00
maximizeNode ( node , getSchema ( schemaName ) ) ;
}
bool JsonUtils : : validate ( const JsonNode & node , std : : string schemaName , std : : string dataName )
{
2013-10-26 22:33:34 +03:00
std : : string log = Validation : : check ( schemaName , node ) ;
if ( ! log . empty ( ) )
{
2017-08-30 23:23:19 +02:00
logMod - > warn ( " Data in %s is invalid! " , dataName ) ;
logMod - > warn ( log ) ;
2018-02-06 08:22:28 +02:00
logMod - > trace ( " %s json: %s " , dataName , node . toJson ( true ) ) ;
2013-10-26 22:33:34 +03:00
}
return log . empty ( ) ;
2013-04-02 20:06:43 +03:00
}
const JsonNode & getSchemaByName ( std : : string name )
{
// cached schemas to avoid loading json data multiple times
static std : : map < std : : string , JsonNode > loadedSchemas ;
if ( vstd : : contains ( loadedSchemas , name ) )
return loadedSchemas [ name ] ;
std : : string filename = " config/schemas/ " + name + " .json " ;
if ( CResourceHandler : : get ( ) - > existsResource ( ResourceID ( filename ) ) )
{
loadedSchemas [ name ] = JsonNode ( ResourceID ( filename ) ) ;
return loadedSchemas [ name ] ;
}
2017-08-30 23:23:19 +02:00
logMod - > error ( " Error: missing schema with name %s! " , name ) ;
2013-04-02 20:06:43 +03:00
assert ( 0 ) ;
return nullNode ;
}
const JsonNode & JsonUtils : : getSchema ( std : : string URI )
{
2013-04-12 23:49:02 +03:00
size_t posColon = URI . find ( ' : ' ) ;
size_t posHash = URI . find ( ' # ' ) ;
2017-08-11 19:03:05 +02:00
if ( posColon = = std : : string : : npos )
{
2017-08-30 23:23:19 +02:00
logMod - > error ( " Invalid schema URI:%s " , URI ) ;
2017-08-11 19:03:05 +02:00
return nullNode ;
}
2013-04-02 20:06:43 +03:00
2013-04-12 23:49:02 +03:00
std : : string protocolName = URI . substr ( 0 , posColon ) ;
std : : string filename = URI . substr ( posColon + 1 , posHash - posColon - 1 ) ;
2013-04-02 20:06:43 +03:00
2017-08-11 19:03:05 +02:00
if ( protocolName ! = " vcmi " )
2013-04-02 20:06:43 +03:00
{
2017-08-30 23:23:19 +02:00
logMod - > error ( " Error: unsupported URI protocol for schema: %s " , URI ) ;
2013-04-02 20:06:43 +03:00
return nullNode ;
}
2013-04-12 23:49:02 +03:00
// check if json pointer if present (section after hash in string)
2017-08-11 19:03:05 +02:00
if ( posHash = = std : : string : : npos | | posHash = = URI . size ( ) - 1 )
2013-04-12 23:49:02 +03:00
return getSchemaByName ( filename ) ;
else
return getSchemaByName ( filename ) . resolvePointer ( URI . substr ( posHash + 1 ) ) ;
2013-01-16 16:02:01 +03:00
}
void JsonUtils : : merge ( JsonNode & dest , JsonNode & source )
{
2017-11-26 23:18:18 +02:00
if ( dest . getType ( ) = = JsonNode : : JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
{
std : : swap ( dest , source ) ;
return ;
}
switch ( source . getType ( ) )
{
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_NULL :
2013-04-25 17:03:35 +03:00
{
dest . clear ( ) ;
break ;
}
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_BOOL :
case JsonNode : : JsonType : : DATA_FLOAT :
case JsonNode : : JsonType : : DATA_INTEGER :
case JsonNode : : JsonType : : DATA_STRING :
case JsonNode : : JsonType : : DATA_VECTOR :
2013-01-16 16:02:01 +03:00
{
2013-12-04 13:36:39 +03:00
std : : swap ( dest , source ) ;
2013-04-25 17:03:35 +03:00
break ;
2013-01-16 16:02:01 +03:00
}
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_STRUCT :
2013-01-16 16:02:01 +03:00
{
//recursively merge all entries from struct
2013-06-29 16:05:48 +03:00
for ( auto & node : source . Struct ( ) )
2013-01-16 16:02:01 +03:00
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 ) ;
}
2014-05-19 01:28:44 +03:00
void JsonUtils : : inherit ( JsonNode & descendant , const JsonNode & base )
{
2014-06-15 19:43:01 +03:00
JsonNode inheritedNode ( base ) ;
2014-05-19 01:28:44 +03:00
merge ( inheritedNode , descendant ) ;
descendant . swap ( inheritedNode ) ;
}
2017-09-13 08:58:14 +02:00
JsonNode JsonUtils : : intersect ( const std : : vector < JsonNode > & nodes , bool pruneEmpty )
{
if ( nodes . size ( ) = = 0 )
return nullNode ;
JsonNode result = nodes [ 0 ] ;
for ( int i = 1 ; i < nodes . size ( ) ; i + + )
{
if ( result . isNull ( ) )
break ;
result = JsonUtils : : intersect ( result , nodes [ i ] , pruneEmpty ) ;
}
return result ;
}
JsonNode JsonUtils : : intersect ( const JsonNode & a , const JsonNode & b , bool pruneEmpty )
{
if ( a . getType ( ) = = JsonNode : : JsonType : : DATA_STRUCT & & b . getType ( ) = = JsonNode : : JsonType : : DATA_STRUCT )
{
// intersect individual properties
JsonNode result ( JsonNode : : JsonType : : DATA_STRUCT ) ;
for ( auto property : a . Struct ( ) )
{
if ( vstd : : contains ( b . Struct ( ) , property . first ) )
{
JsonNode propertyIntersect = JsonUtils : : intersect ( property . second , b . Struct ( ) . find ( property . first ) - > second ) ;
2017-09-16 00:23:31 +02:00
if ( pruneEmpty & & ! propertyIntersect . containsBaseData ( ) )
2017-09-13 08:58:14 +02:00
continue ;
result [ property . first ] = propertyIntersect ;
}
}
return result ;
}
else
{
// not a struct - same or different, no middle ground
if ( a = = b )
return a ;
}
return nullNode ;
}
2017-09-13 10:27:01 +02:00
JsonNode JsonUtils : : difference ( const JsonNode & node , const JsonNode & base )
{
2017-09-17 04:45:23 +02:00
auto addsInfo = [ ] ( JsonNode diff ) - > bool
{
switch ( diff . getType ( ) )
{
case JsonNode : : JsonType : : DATA_NULL :
return false ;
case JsonNode : : JsonType : : DATA_STRUCT :
return diff . Struct ( ) . size ( ) > 0 ;
default :
return true ;
}
} ;
2017-09-14 03:03:44 +02:00
if ( node . getType ( ) = = JsonNode : : JsonType : : DATA_STRUCT & & base . getType ( ) = = JsonNode : : JsonType : : DATA_STRUCT )
2017-09-13 10:27:01 +02:00
{
// subtract individual properties
2017-09-14 03:03:44 +02:00
JsonNode result ( JsonNode : : JsonType : : DATA_STRUCT ) ;
2017-09-13 10:27:01 +02:00
for ( auto property : node . Struct ( ) )
{
if ( vstd : : contains ( base . Struct ( ) , property . first ) )
{
2017-09-16 00:23:31 +02:00
const JsonNode propertyDifference = JsonUtils : : difference ( property . second , base . Struct ( ) . find ( property . first ) - > second ) ;
2017-09-17 04:45:23 +02:00
if ( addsInfo ( propertyDifference ) )
2017-09-16 00:23:31 +02:00
result [ property . first ] = propertyDifference ;
2017-09-13 10:27:01 +02:00
}
else
{
result [ property . first ] = property . second ;
}
}
return result ;
}
else
{
if ( node = = base )
return nullNode ;
}
return node ;
}
2013-01-16 16:02:01 +03:00
JsonNode JsonUtils : : assembleFromFiles ( std : : vector < std : : string > files )
{
2013-11-08 23:36:26 +03:00
bool isValid ;
return assembleFromFiles ( files , isValid ) ;
}
JsonNode JsonUtils : : assembleFromFiles ( std : : vector < std : : string > files , bool & isValid )
{
isValid = true ;
2013-01-16 16:02:01 +03:00
JsonNode result ;
2013-06-29 16:05:48 +03:00
for ( std : : string file : files )
2013-01-16 16:02:01 +03:00
{
2013-11-08 23:36:26 +03:00
bool isValidFile ;
JsonNode section ( ResourceID ( file , EResType : : TEXT ) , isValidFile ) ;
2013-01-16 16:02:01 +03:00
merge ( result , section ) ;
2013-11-08 23:36:26 +03:00
isValid | = isValidFile ;
2013-01-16 16:02:01 +03:00
}
return result ;
}
2013-04-26 00:50:55 +03:00
JsonNode JsonUtils : : assembleFromFiles ( std : : string filename )
{
JsonNode result ;
2013-07-28 17:49:50 +03:00
ResourceID resID ( filename , EResType : : TEXT ) ;
2013-04-26 00:50:55 +03:00
2013-07-28 17:49:50 +03:00
for ( auto & loader : CResourceHandler : : get ( ) - > getResourcesWithName ( resID ) )
2013-04-26 00:50:55 +03:00
{
// FIXME: some way to make this code more readable
2013-07-28 17:49:50 +03:00
auto stream = loader - > load ( resID ) ;
2013-04-26 00:50:55 +03:00
std : : unique_ptr < ui8 [ ] > textData ( new ui8 [ stream - > getSize ( ) ] ) ;
stream - > read ( textData . get ( ) , stream - > getSize ( ) ) ;
JsonNode section ( ( char * ) textData . get ( ) , stream - > getSize ( ) ) ;
merge ( result , section ) ;
}
return result ;
}
2017-09-12 01:56:38 +02:00
DLL_LINKAGE JsonNode JsonUtils : : boolNode ( bool value )
{
JsonNode node ;
node . Bool ( ) = value ;
return node ;
}
DLL_LINKAGE JsonNode JsonUtils : : floatNode ( double value )
{
JsonNode node ;
node . Float ( ) = value ;
return node ;
}
DLL_LINKAGE JsonNode JsonUtils : : stringNode ( std : : string value )
{
JsonNode node ;
node . String ( ) = value ;
return node ;
}
DLL_LINKAGE JsonNode JsonUtils : : intNode ( si64 value )
{
JsonNode node ;
node . Integer ( ) = value ;
return node ;
}