/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. // Description : main header file #ifndef CRYINCLUDE_CRYCOMMON_ISERIALIZE_H #define CRYINCLUDE_CRYCOMMON_ISERIALIZE_H #pragma once #include // <> required for Interfuscator #include // <> required for Interfuscator #include "CountedValue.h" #include "MiniQueue.h" #include #include #include #include class CTimeValue; // Forward template declaration template class InterpolatedValue_tpl; // Unfortunately this needs to be here - should be in CryNetwork somewhere. struct SNetObjectID { static const uint16 InvalidId #ifndef __GNUC__ // This is a workaround for a GCC bug in combination with STLPORT (leading // to an undefined symbol SNetObjectID::InvalidId). For GCC the definition // of InvalidId is in CryCommon/CryCommon.cpp. = ~uint16(0) #endif ; SNetObjectID() : id(InvalidId) , salt(0) {} SNetObjectID(uint16 i, uint16 s) : id(i) , salt(s) {} uint16 id; uint16 salt; ILINE bool operator!() const { return id == InvalidId; } typedef uint16 (SNetObjectID::* unknown_bool_type); ILINE operator unknown_bool_type() const { return !!(*this) ? &SNetObjectID::id : NULL; } ILINE bool operator!=(const SNetObjectID& rhs) const { return !(*this == rhs); } ILINE bool operator==(const SNetObjectID& rhs) const { return id == rhs.id && salt == rhs.salt; } ILINE bool operator<(const SNetObjectID& rhs) const { return id < rhs.id || (id == rhs.id && salt < rhs.salt); } ILINE bool operator>(const SNetObjectID& rhs) const { return id > rhs.id || (id == rhs.id && salt > rhs.salt); } bool IsLegal() const { return salt != 0; } const char* GetText(char* tmpBuf = nullptr, size_t bufferSize = 0) const { static char singlebuf[64]; if (!tmpBuf) { tmpBuf = singlebuf; bufferSize = sizeof(singlebuf); } if (id == InvalidId) { sprintf_s(tmpBuf, bufferSize, ""); } else if (!salt) { sprintf_s(tmpBuf, bufferSize, "illegal:%d:%d", id, salt); } else { sprintf_s(tmpBuf, bufferSize, "%d:%d", id, salt); } return tmpBuf; } uint32 GetAsUint32() const { return (uint32(salt) << 16) | id; } void GetMemoryUsage(ICrySizer* pSizer) const { /*nothing*/} AUTO_STRUCT_INFO }; // this enumeration details what "kind" of serialization we are // performing, so that classes that want to, for instance, tailor // the data they present depending on where data is being written // to can do so enum ESerializationTarget { eST_SaveGame, eST_Network, eST_Script }; // this inner class defines an interface so that OnUpdate // functions can be passed abstractly through to concrete // serialization classes struct ISerializeUpdateFunction { // virtual ~ISerializeUpdateFunction(){} virtual void Execute() = 0; // }; // concrete implementation of IUpdateFunction for a general functor class template class CSerializeUpdateFunction : public ISerializeUpdateFunction { public: CSerializeUpdateFunction(F_Update& update) : m_rUpdate(update) {} virtual void Execute() { m_rUpdate(); } private: F_Update m_rUpdate; }; ////////////////////////////////////////////////////////////////////////// // Temporary class for string serialization. ////////////////////////////////////////////////////////////////////////// struct SSerializeString { AUTO_STRUCT_INFO SSerializeString() {}; SSerializeString(const SSerializeString& src) { m_str.assign(src.c_str()); }; explicit SSerializeString(const char* sbegin, const char* send) : m_str(sbegin, send) {}; ~SSerializeString() {} // Casting to const char* SSerializeString(const char* s) : m_str(s) { }; //operator const char* () const { return m_str; } SSerializeString& operator =(const SSerializeString& src) { m_str.assign(src.c_str()); return *this; } SSerializeString& operator =(const char* src) { m_str.assign(src); return *this; } bool operator != (const SSerializeString& src) { return m_str != src.m_str; } size_t size() const { return m_str.size(); } size_t length() const { return m_str.length(); } const char* c_str() const { return m_str.c_str(); }; bool empty() const { return m_str.empty(); } void resize(int sz) { m_str.resize(sz); } void reserve(int sz) { m_str.reserve(sz); } void set_string(const string& s) { m_str.assign(s.begin(), s.size()); } #if !defined(RESOURCE_COMPILER) void set_string(const CryStringLocal& s) { m_str.assign(s.begin(), s.size()); } #endif template void set_string(const CryFixedStringT& s) { m_str.assign(s.begin(), s.size()); } operator const string () const { return m_str; } private: string m_str; }; // the ISerialize is intended to be implemented by objects that need // to read and write from various data sources, in such a way that // different tradeoffs can be balanced by the object that is being // serialized, and so that objects being serialized need only write // a single function in order to be read from and written to struct ISerialize { static const int ENUM_POLICY_TAG = 0xe0000000; ILINE ISerialize() {} // virtual ~ISerialize(){} // this is for string values -- they need special support virtual void ReadStringValue(const char* name, SSerializeString& curValue, uint32 policy = 0) = 0; virtual void WriteStringValue(const char* name, SSerializeString& buffer, uint32 policy = 0) = 0; // this function should be implemented to call the passed in interface // if we are reading, and to not call it if we are writing virtual void Update(ISerializeUpdateFunction* pUpdate) = 0; // for network updates: notify the network engine that this value was only partially read and we // should re-request an update from the server soon virtual void FlagPartialRead() = 0; ////////////////////////////////////////////////////////////////////////// // these functions should be implemented to deal with groups ////////////////////////////////////////////////////////////////////////// // Begins a serialization group - must be matched by an EndGroup // szName is preferably as short as possible for performance reasons // Spaces in szName cause undefined behaviour, use alpha characters,underscore and numbers only for a name. virtual void BeginGroup(const char* szName) = 0; virtual bool BeginOptionalGroup(const char* szName, bool condition) = 0; virtual void EndGroup() = 0; ////////////////////////////////////////////////////////////////////////// virtual bool IsReading() const = 0; virtual bool ShouldCommitValues() const = 0; virtual ESerializationTarget GetSerializationTarget() const = 0; virtual bool Ok() const = 0; // // declare all primitive Value() implementations #define SERIALIZATION_TYPE(T) \ virtual void Value(const char* name, T & x, uint32 policy) = 0; #include "SerializationTypes.h" #undef SERIALIZATION_TYPE // declare all primitive Value() implementations #define SERIALIZATION_TYPE(T) \ virtual void ValueWithDefault(const char* name, T & x, const T&defaultValue) = 0; #include "SerializationTypes.h" SERIALIZATION_TYPE(SSerializeString) #undef SERIALIZATION_TYPE void Value(const char* name, AZ::Vector3& x) { } template void Value(const char* name, B& x) { Value(name, x, 0); } template void Value(const char* name, B& x, uint32 policy); template void ValueWithDefault(const char* name, B& x, const B& defaultValue); }; // this class provides a wrapper so that ISerialize can be used much more // easily; it is a template so that if we need to wrap a more specific // ISerialize implementation we can do so easily template class CSerializeWrapper { public: CSerializeWrapper(TISerialize* pSerialize) : m_pSerialize(pSerialize) { } // we provide a wrapper around the abstract implementation // ISerialize to allow easy changing of our // interface, and easy implementation of our details. // some of the wrappers are trivial, however for consistency, they // have been made to follow the trend. // the value function allows us to declare that a value needs // to be serialized/deserialized; we can pass a serialization policy // in order to compress the value, and an update function to allow // us to be informed of when this value is changed template ILINE void Value(const char* szName, T_Value& value, int policy) { m_pSerialize->Value(szName, value, policy); } template ILINE void Value(const char* szName, T_Value& value) { m_pSerialize->Value(szName, value); } void Value(const char* szName, string& value, int policy) { if (IsWriting()) { SSerializeString& serializeString = SetSharedSerializeString(value); m_pSerialize->WriteStringValue(szName, serializeString, policy); } else { if (GetSerializationTarget() != eST_Script) { value = ""; } SSerializeString& serializeString = SetSharedSerializeString(value); m_pSerialize->ReadStringValue(szName, serializeString, policy); value = serializeString.c_str(); } } ILINE void Value(const char* szName, string& value) { Value(szName, value, 0); } void Value(const char* szName, const string& value, int policy) { if (IsWriting()) { SSerializeString& serializeString = SetSharedSerializeString(value); m_pSerialize->WriteStringValue(szName, serializeString, policy); } else { assert(0 && "This function can only be used for Writing"); } } ILINE void Value(const char* szName, const string& value) { Value(szName, value, 0); } template void Value(const char* szName, CryStringLocalT& value, int policy) { if (IsWriting()) { SSerializeString& serializeString = SetSharedSerializeString(value); m_pSerialize->WriteStringValue(szName, serializeString, policy); } else { if (GetSerializationTarget() != eST_Script) { value = ""; } SSerializeString& serializeString = SetSharedSerializeString(value); m_pSerialize->ReadStringValue(szName, serializeString, policy); value = serializeString.c_str(); } } template ILINE void Value(const char* szName, CryStringLocalT& value) { Value(szName, value, 0); } template void Value(const char* szName, const CryStringLocalT& value, int policy) { if (IsWriting()) { SSerializeString& serializeString = SetSharedSerializeString(value); m_pSerialize->WriteStringValue(szName, serializeString, policy); } else { assert(0 && "This function can only be used for Writing"); } } template ILINE void Value(const char* szName, const CryStringLocalT& value) { Value(szName, value, 0); } template void Value(const char* szName, CryFixedStringT& value, int policy) { if (IsWriting()) { SSerializeString& serializeString = SetSharedSerializeString(value); m_pSerialize->WriteStringValue(szName, serializeString, policy); } else { if (GetSerializationTarget() != eST_Script) { value = ""; } SSerializeString& serializeString = SetSharedSerializeString(value); m_pSerialize->ReadStringValue(szName, serializeString, policy); assert(serializeString.length() <= S); value = serializeString.c_str(); } } template ILINE void Value(const char* szName, CryFixedStringT& value) { Value(szName, value, 0); } template void Value(const char* szName, T_Class* pInst, T_Value (T_Class::* get)() const, void (T_Class::* set)(T_Value)) { if (IsWriting()) { T_Value temp = (pInst->*get)(); Value(szName, temp); } else { T_Value temp; Value(szName, temp); (pInst->*set)(temp); } } void Value(const char* name, IScriptTable* pTable) { ScriptAnyValue any(pTable); Value(name, any); } void Value(const char* name, SmartScriptTable& pTable) { ScriptAnyValue any(pTable); Value(name, any); if (IsReading()) { if (any.type == ANY_TTABLE) { pTable = any.table; } else { pTable = SmartScriptTable(); } } } template void Value(const char* name, InterpolatedValue_tpl& val) { if (IsWriting()) { T a = val.Get(); Value(name, a); } if (IsReading()) { T a; Value(name, a); val.SetGoal(a); } } template void Value(const char* name, InterpolatedValue_tpl& val, int policy) { if (IsWriting()) { T a = val.Get(); Value(name, a, policy); } if (IsReading()) { T a; Value(name, a, policy); val.SetGoal(a); } } template void Value(const char* name, CountedValue& countedValue) { if (!BeginOptionalGroup(name, true)) { return; } if (IsWriting()) { T rawValue = countedValue.Peek(); Value("Value", rawValue); typename CountedValue::TCountedID rawId = countedValue.GetLatestID(); Value("Id", rawId, 'ui32'); } if (IsReading()) { T rawValue; Value("Value", rawValue); typename CountedValue::TCountedID rawId; Value("Id", rawId, 'ui32'); countedValue.UpdateDuringSerializationOnly(rawValue, rawId); } EndGroup(); } template void Value(const char* name, CountedValue& countedValue, int policy) { if (!BeginOptionalGroup(name, true)) { return; } if (IsWriting()) { T rawValue = countedValue.Peek(); Value("Value", rawValue, policy); typename CountedValue::TCountedID rawId = countedValue.GetLatestID(); Value("Id", rawId, 'ui32'); } if (IsReading()) { T rawValue; Value("Value", rawValue, policy); typename CountedValue::TCountedID rawId; Value("Id", rawId, 'ui32'); countedValue.UpdateDuringSerializationOnly(rawValue, rawId); } EndGroup(); } bool ValueChar(const char* name, char* buffer, int len) { string temp; if (IsReading()) { Value(name, temp); if ((int)temp.length() > len - 1) { return false; // truncated read } memcpy(buffer, temp.data(), temp.length() + 1); buffer[len - 1] = 0; } else { temp = string(buffer, buffer + len); Value(name, temp); } return true; } template void ValueWithDefault(const char* name, T& x, const T& defaultValue) { m_pSerialize->ValueWithDefault(name, x, defaultValue); } void ValueWithDefault(const char* szName, string& value, const string& defaultValue) { static SSerializeString defaultSerializeString; { defaultSerializeString.set_string(defaultValue); } SSerializeString& serializeString = SetSharedSerializeString(value); m_pSerialize->ValueWithDefault(szName, serializeString, defaultSerializeString); if (IsReading()) { value = serializeString.c_str(); } } template void Value(const char* szName, T_Class* pInst, T_Value (T_Class::* get)() const, void (T_Class::* set)(T_Value), const T_SerializationPolicy& policy) { if (IsWriting()) { T_Value temp = (pInst->*get)(); Value(szName, temp, policy); } else { T_Value temp = static_cast(0); Value(szName, temp, policy); (pInst->*set)(temp); } } // a value that is written by referring to a map of key/value pairs - we receive the key, and write the value template void MappedValue(const char* szName, T_Key& value, const T_Map& mapper) { typedef typename T_Map::ValueType T_Value; if (IsWriting()) { T_Value write = mapper.KeyToValue(value); Value(szName, write); } else { T_Value read; Value(szName, read); value = mapper.ValueToKey(read); } } #define CONTAINER_VALUE(container_type, insert_function) \ template \ void Value(const char* name, container_type&cont) \ { \ if (!BeginOptionalGroup(name, true)) {return; } \ if (IsWriting()) \ { \ uint32 count = cont.size(); \ Value("Size", count); \ for (typename container_type::iterator iter = cont.begin(); iter != cont.end(); ++iter) \ { \ BeginGroup("i"); \ T_Value value = *iter; \ Value("v", value); \ EndGroup(); \ } \ } \ else \ { \ cont.clear(); \ uint32 count = 0; \ Value("Size", count); \ while (count--) \ { \ BeginGroup("i"); \ T_Value temp; \ Value("v", temp); \ cont.insert_function(temp); \ EndGroup(); \ } \ } \ EndGroup(); \ } \ template \ void MappedValue(const char* name, container_type&cont, const T_Map&mapper) \ { \ if (!BeginOptionalGroup(name, true)) {return; } \ if (IsWriting()) \ { \ uint32 count = cont.size(); \ Value("Size", count); \ for (typename container_type::iterator iter = cont.begin(); iter != cont.end(); ++iter) \ { \ BeginGroup("i"); \ MappedValue("v", *iter, mapper); \ EndGroup(); \ } \ } \ else \ { \ cont.clear(); \ uint32 count = 0; \ Value("Size", count); \ while (count--) \ { \ BeginGroup("i"); \ T_Value temp; \ MappedValue("v", temp, mapper); \ cont.insert_function(temp); \ EndGroup(); \ } \ } \ EndGroup(); \ } #define PAIR_CONTAINER_VALUE(container_type, insert_function) \ template \ void Value(const char* name, container_type, Allocator>&cont) \ { \ if (!BeginOptionalGroup(name, true)) {return; } \ if (IsWriting()) \ { \ uint32 count = cont.size(); \ Value("Size", count); \ for (typename container_type, Allocator>::iterator iter = cont.begin(); iter != cont.end(); ++iter) \ { \ BeginGroup("i"); \ T_Value1 value1 = iter->first; \ T_Value2 value2 = iter->second; \ Value("v1", value1); \ Value("v2", value2); \ EndGroup(); \ } \ } \ else \ { \ cont.clear(); \ uint32 count = 0; \ Value("Size", count); \ while (count--) \ { \ BeginGroup("i"); \ T_Value1 temp1; \ T_Value2 temp2; \ Value("v1", temp1); \ Value("v2", temp2); \ cont.insert_function(std::pair(temp1, temp2)); \ EndGroup(); \ } \ } \ EndGroup(); \ } \ CONTAINER_VALUE(std::vector, push_back); CONTAINER_VALUE(std::list, push_back); CONTAINER_VALUE(std::set, insert); CONTAINER_VALUE(std::deque, push_back); CONTAINER_VALUE(VectorSet, insert); CONTAINER_VALUE(DynArray, insert); PAIR_CONTAINER_VALUE(std::list, push_back); PAIR_CONTAINER_VALUE(std::vector, push_back); template void DummyValues(uint32 numDummyValues) { T_Value dummyValue; for (uint32 i = 0; i < numDummyValues; ++i) { Value("Value", dummyValue); } } template void Value(const char* name, MiniQueue& cont) { if (!BeginOptionalGroup(name, true)) { return; } if (IsWriting()) { uint32 count = cont.Size(); Value("Size", count); for (typename MiniQueue::SIterator iter = cont.Begin(); iter != cont.End(); ++iter) { T_Value value = *iter; Value("Value", value); } } else { cont.Clear(); uint32 count = 0; Value("Size", count); while (count--) { T_Value temp; Value("Value", temp); cont.Push(temp); } } DummyValues(cont.Capacity() - cont.Size()); EndGroup(); } template void MappedValue(const char* name, MiniQueue& cont, const T_Map& mapper) { if (!BeginOptionalGroup(name, true)) { return; } if (IsWriting()) { uint8 count = cont.Size(); Value("Size", count); for (typename MiniQueue::SIterator iter = cont.Begin(); iter != cont.End(); ++iter) { BeginGroup("i"); MappedValue("Value", *iter, mapper); EndGroup(); } } else { cont.Clear(); uint8 count = 0; Value("Size", count); while (count--) { BeginGroup("i"); T_Value temp; MappedValue("Value", temp, mapper); cont.Push(temp); EndGroup(); } } EndGroup(); } #define MAP_CONTAINER_VALUE(container_type) \ template \ void Value(const char* name, container_type&cont) \ { \ if (!BeginOptionalGroup(name, true)) {return; } \ if (IsWriting()) \ { \ uint32 count = (uint32)cont.size(); \ Value("Size", count); \ for (typename container_type::iterator iter = cont.begin(); iter != cont.end(); ++iter) \ { \ T_Key tempKey = iter->first; \ BeginGroup("pair"); \ Value("k", tempKey); \ Value("v", iter->second); \ EndGroup(); \ } \ } \ else \ { \ cont.clear(); \ uint32 count; \ Value("Size", count); \ while (count--) \ { \ std::pair temp; \ BeginGroup("pair"); \ Value("k", temp.first); \ Value("v", temp.second); \ EndGroup(); \ cont.insert(temp); \ } \ } \ EndGroup(); \ } #define HASH_CONTAINER_VALUE(container_type) \ template \ void Value(const char* name, container_type&cont) \ { \ if (!BeginOptionalGroup(name, true)) {return; } \ if (IsWriting()) \ { \ uint32 count = (uint32)cont.size(); \ Value("Size", count); \ for (typename container_type::iterator iter = cont.begin(); iter != cont.end(); ++iter) \ { \ T_Key tempKey = iter->first; \ BeginGroup("pair"); \ Value("k", tempKey); \ Value("v", iter->second); \ EndGroup(); \ } \ } \ else \ { \ cont.clear(); \ uint32 count; \ Value("Size", count); \ while (count--) \ { \ AZStd::pair temp; \ BeginGroup("pair"); \ Value("k", temp.first); \ Value("v", temp.second); \ EndGroup(); \ cont.insert(temp); \ } \ } \ EndGroup(); \ } MAP_CONTAINER_VALUE(std::map); MAP_CONTAINER_VALUE(std::multimap); MAP_CONTAINER_VALUE(VectorMap); HASH_CONTAINER_VALUE(AZStd::unordered_map); template ILINE void EnumValue(const char* szName, T_Value& value, T_Value first, T_Value last) { int32 nValue = int32(value) - first; Value(szName, nValue, ISerialize::ENUM_POLICY_TAG | (last - first)); value = T_Value(nValue + first); } template ILINE void EnumValue(const char* szName, T_Class* pClass, T_Value (T_Class::* GetValue)() const, void (T_Class::* SetValue)(T_Value), T_Value first, T_Value last) { bool w = IsWriting(); int nValue; if (w) { nValue = int32((pClass->*GetValue)()) - first; } Value(szName, nValue, ISerialize::ENUM_POLICY_TAG | (last - first)); if (!w) { (pClass->*SetValue)(T_Value(nValue + first)); } } /* // we can request that a functor be called whenever our values // are being updated by calling this function template ILINE void OnUpdate( F_Update& update ) { CUpdateFunction func(update); m_pSerialize->Update( &func ); } template ILINE void OnUpdate( T * pCls, void (T::*func)() ) { class CFunc : public IUpdateFunction { public: CFunc( T * pCls, void (T::*func)() ) : m_pCls(pCls), m_func(func) {} virtual void Execute() { (m_pCls->*m_func)(); } private: T * m_pCls; void (T::*m_func)(); }; CFunc ifunc( pCls, func ); m_pSerialize->Update( &ifunc ); } */ // groups help us find common data ILINE void BeginGroup(const char* szName) { m_pSerialize->BeginGroup(szName); } ILINE bool BeginOptionalGroup(const char* szName, bool condition) { return m_pSerialize->BeginOptionalGroup(szName, condition); } ILINE void EndGroup() { m_pSerialize->EndGroup(); } // fetch the serialization target ILINE ESerializationTarget GetSerializationTarget() const { return m_pSerialize->GetSerializationTarget(); } ILINE bool IsWriting() const { return !m_pSerialize->IsReading(); } ILINE bool IsReading() const { return m_pSerialize->IsReading(); } ILINE bool ShouldCommitValues() const { assert(m_pSerialize->IsReading()); return m_pSerialize->ShouldCommitValues(); } ILINE bool Ok() const { return m_pSerialize->Ok(); } friend ILINE TISerialize* GetImpl(CSerializeWrapper ser) { return ser.m_pSerialize; } ILINE void FlagPartialRead() { m_pSerialize->FlagPartialRead(); } operator CSerializeWrapper() { return CSerializeWrapper(m_pSerialize); } SSerializeString& SetSharedSerializeString(const string& str) { static SSerializeString serializeString; serializeString.set_string(str); return serializeString; } #if !defined(RESOURCE_COMPILER) SSerializeString& SetSharedSerializeString(const CryStringLocal& str) { static SSerializeString serializeString; serializeString.set_string(str); return serializeString; } #endif template SSerializeString& SetSharedSerializeString(const CryFixedStringT& str) { static SSerializeString serializeString; serializeString.set_string(str); return serializeString; } private: TISerialize* m_pSerialize; }; // default serialize class to use!! typedef CSerializeWrapper TSerialize; // simple struct to declare something serializable... useful for // exposition struct ISerializable { virtual ~ISerializable(){} virtual void SerializeWith(TSerialize) = 0; }; template void ISerialize::Value(const char* name, B& x, uint32 policy) { if (!BeginOptionalGroup(name, true)) { return; } TSerialize ser(this); x.Serialize(ser); EndGroup(); } // //template <> //void ISerialize::Value(const char * name, AZ::Vector3& x, uint32 policy) //{ // //} // Based off ValueWithDefault in SimpleSerialize.h template void ISerialize::ValueWithDefault(const char* name, B& x, const B& defaultValue) { if (BeginOptionalGroup(name, x != defaultValue)) { TSerialize ser(this); x.Serialize(ser); EndGroup(); } else if (IsReading()) { x = defaultValue; } } ////////////////////////////////////////////////////////////////////////// // Used to automatically Begin/End group in serialization stream ////////////////////////////////////////////////////////////////////////// struct SSerializeScopedBeginGroup { SSerializeScopedBeginGroup(TSerialize& ser, const char* sGroupName) { m_pSer = &ser; m_pSer->BeginGroup(sGroupName); } ~SSerializeScopedBeginGroup() { m_pSer->EndGroup(); } private: TSerialize* m_pSer; }; #endif // CRYINCLUDE_CRYCOMMON_ISERIALIZE_H