diff --git a/src/Common/NBasics.h b/src/Common/NBasics.h index 1f7c8192..fe44d4cb 100644 --- a/src/Common/NBasics.h +++ b/src/Common/NBasics.h @@ -9,13 +9,13 @@ namespace NBasics /** Remove an element from a vector */ template -bool VectorRemoveOne(std::vector& rVector, const T& rkElement) +bool VectorRemoveOne(std::vector& Vector, const T& kElement) { - for (auto Iter = rVector.begin(); Iter != rVector.end(); Iter++) + for (auto Iter = Vector.begin(); Iter != Vector.end(); Iter++) { - if (*Iter == rkElement) + if (*Iter == kElement) { - rVector.erase(Iter); + Vector.erase(Iter); return true; } } @@ -24,15 +24,15 @@ bool VectorRemoveOne(std::vector& rVector, const T& rkElement) /** Remove all occurrences of an element from a vector. Returns the number of elements that were removed. */ template -int VectorRemoveAll(std::vector& rVector, const T& rkElement) +int VectorRemoveAll(std::vector& Vector, const T& kElement) { int NumRemoved = 0; - for (auto Iter = rVector.begin(); Iter != rVector.end(); Iter++) + for (auto Iter = Vector.begin(); Iter != Vector.end(); Iter++) { - if (*Iter == rkElement) + if (*Iter == kElement) { - Iter = rVector.erase(Iter); + Iter = Vector.erase(Iter); NumRemoved++; } } @@ -40,6 +40,34 @@ int VectorRemoveAll(std::vector& rVector, const T& rkElement) return NumRemoved; } +/** Returns whether the vector contains the given element */ +template +bool VectorContains(std::vector& Vector, const T& kElement) +{ + for (auto Iter = Vector.begin(); Iter != Vector.end(); Iter++) + { + if (*Iter == kElement) + { + return true; + } + } + + return false; +} + +/** Adds an element to a vector only if it is not already present */ +template +bool VectorAddUnique(std::vector& Vector, const T& kElement) +{ + if (!VectorContainsElement(Vector, kElement)) + { + Vector.push_back(kElement); + return true; + } + + return false; +} + } #endif // NBASICS_H diff --git a/src/Common/Serialization/IArchive.h b/src/Common/Serialization/IArchive.h index 0df22e7f..fc40acd4 100644 --- a/src/Common/Serialization/IArchive.h +++ b/src/Common/Serialization/IArchive.h @@ -57,22 +57,24 @@ /** ESerialHint - Parameter hint flags */ enum ESerialHint { - SH_HexDisplay = 0x1, // The parameter should be written in hex in text formats - SH_Optional = 0x2, // The parameter should not be written to the file if its value matches the default value - SH_NeverSave = 0x4, // The parameter should not be saved to files - SH_AlwaysSave = 0x8, // The parameter should always be saved regardless of if it matches the default value + SH_HexDisplay = 0x1, // The parameter should be written in hex in text formats. + SH_Optional = 0x2, // The parameter should not be written to the file if its value matches the default value. + SH_NeverSave = 0x4, // The parameter should not be saved to files. + SH_AlwaysSave = 0x8, // The parameter should always be saved regardless of if it matches the default value. SH_Attribute = 0x10, // The parameter is an attribute of another parameter. Attributes cannot have children. SH_IgnoreName = 0x20, // The parameter name will not be used to validate file data. May yield incorrect results if used improperly! + SH_InheritHints = 0x40, // The parameter will inherit hints from its parent parameter (except for this flag). + SH_Proxy = 0x80, // The parameter is a proxy of the parent and will display inline instead of as a child parameter. }; /** EArchiveFlags */ enum EArchiveFlags { - AF_Reader = 0x1, // Archive reads data - AF_Writer = 0x2, // Archive writes data - AF_Text = 0x4, // Archive reads/writes to a text format - AF_Binary = 0x8, // Archive reads/writes to a binary format - AF_NoSkipping = 0x10, // Properties are never skipped + AF_Reader = 0x1, // Archive reads data. + AF_Writer = 0x2, // Archive writes data. + AF_Text = 0x4, // Archive reads/writes to a text format. + AF_Binary = 0x8, // Archive reads/writes to a binary format. + AF_NoSkipping = 0x10, // Properties are never skipped. }; /** Shortcut macro for enable_if */ @@ -128,7 +130,14 @@ inline ParameterMatchesDefault( const TSerialParameter& kParameter ) } template -ENABLE_IF( !SUPPORTS_DEFAULT_VALUES, bool ) +ENABLE_IF( !SUPPORTS_DEFAULT_VALUES && TIsContainer::value, bool ) +inline ParameterMatchesDefault( const TSerialParameter& kParameter ) +{ + return kParameter.rValue.size() == 0; +} + +template +ENABLE_IF( !SUPPORTS_DEFAULT_VALUES && !TIsContainer::value, bool ) inline ParameterMatchesDefault( const TSerialParameter& ) { return false; @@ -149,7 +158,15 @@ inline InitParameterToDefault( TSerialParameter& Param ) } template -ENABLE_IF( !SUPPORTS_DEFAULT_VALUES, bool ) +ENABLE_IF( !SUPPORTS_DEFAULT_VALUES && TIsContainer::value, bool ) +inline InitParameterToDefault( TSerialParameter& Param ) +{ + Param.rValue.clear(); + return true; +} + +template +ENABLE_IF( !SUPPORTS_DEFAULT_VALUES && !TIsContainer::value, bool ) inline InitParameterToDefault( TSerialParameter& ) { return false; @@ -266,7 +283,6 @@ public: virtual ~IArchive() {} -protected: // Serialize archive version. Always call after opening a file. void SerializeVersion() { @@ -275,6 +291,25 @@ protected: << SerialParameter("Game", mGame, SH_Attribute | SH_Optional, eUnknownGame); } +private: + // Attempts to start a new parameter. Return whether the parameter should be serialized. + template + bool InternalStartParam(const TSerialParameter& Param) + { + bool IsProxy = (Param.HintFlags & SH_Proxy) != 0; + return ShouldSerializeParameter(Param) && (IsProxy || ParamBegin(Param.pkName, Param.HintFlags) ); + } + + // Ends a parameter. + template + void InternalEndParam(const TSerialParameter& Param) + { + if ((Param.HintFlags & SH_Proxy) == 0) + { + ParamEnd(); + } + } + // Return whether this parameter should be serialized template bool ShouldSerializeParameter(const TSerialParameter& Param) @@ -318,7 +353,7 @@ protected: // Parameter stack handling template - inline void PushParameter(const TSerialParameter& Param) + inline void PushParameter(TSerialParameter& Param) { #if _DEBUG if (mParmStack.size() > 0) @@ -328,6 +363,13 @@ protected: } #endif + // For InheritHints parameters, and for proxy parameters, copy the hint flags from the parent parameter. + if (Param.HintFlags & (SH_InheritHints | SH_Proxy)) + { + Param.HintFlags |= mParmStack.back().HintFlags; + Param.HintFlags &= ~SH_InheritHints; + } + SParmStackEntry Entry; Entry.TypeID = typeid(ValType).hash_code(); Entry.TypeSize = sizeof(ValType); @@ -357,11 +399,10 @@ public: { PushParameter(rParam); - if (ShouldSerializeParameter(rParam) - && ParamBegin(rParam.pkName, rParam.HintFlags)) + if (InternalStartParam(rParam)) { SerializePrimitive(rParam.rValue, rParam.HintFlags); - ParamEnd(); + InternalEndParam(rParam); } else if (IsReader()) InitParameterToDefault(rParam); @@ -377,8 +418,7 @@ public: ASSERT( !(mArchiveFlags & AF_Writer) || rParam.rValue != nullptr ); PushParameter(rParam); - if (ShouldSerializeParameter(rParam) - && ParamBegin(rParam.pkName, rParam.HintFlags)) + if (InternalStartParam(rParam)) { // Support for old versions of archives that serialize types on non-abstract polymorphic pointers if (ArchiveVersion() < eArVer_Refactor && IsReader() && std::is_polymorphic_v) @@ -398,7 +438,7 @@ public: else if (IsReader()) rParam.rValue = nullptr; - ParamEnd(); + InternalEndParam(rParam); } PopParameter(rParam); @@ -412,11 +452,10 @@ public: { PushParameter(rParam); - if (ShouldSerializeParameter(rParam) - && ParamBegin(rParam.pkName, rParam.HintFlags)) + if (InternalStartParam(rParam)) { Serialize(*this, rParam.rValue); - ParamEnd(); + InternalEndParam(rParam); } else if (IsReader()) InitParameterToDefault(rParam); @@ -432,8 +471,7 @@ public: ASSERT( !IsWriter() || rParam.rValue != nullptr ); PushParameter(rParam); - if (ShouldSerializeParameter(rParam) - && ParamBegin(rParam.pkName)) + if (InternalStartParam(rParam)) { // Support for old versions of archives that serialize types on non-abstract polymorphic pointers if (ArchiveVersion() < eArVer_Refactor && IsReader() && std::is_polymorphic_v) @@ -453,7 +491,7 @@ public: else if (IsReader()) rParam.rValue = nullptr; - ParamEnd(); + InternalEndParam(rParam); } PopParameter(rParam); @@ -467,11 +505,10 @@ public: { PushParameter(rParam); - if (ShouldSerializeParameter(rParam) && - ParamBegin(rParam.pkName, rParam.HintFlags)) + if (InternalStartParam(rParam)) { rParam.rValue.Serialize(*this); - ParamEnd(); + InternalEndParam(rParam); } else if (IsReader()) InitParameterToDefault(rParam); @@ -486,8 +523,7 @@ public: { PushParameter(rParam); - if (ShouldSerializeParameter(rParam) && - ParamBegin(rParam.pkName, rParam.HintFlags)) + if (InternalStartParam(rParam)) { // Support for old versions of archives that serialize types on non-abstract polymorphic pointers if (ArchiveVersion() < eArVer_Refactor && IsReader() && std::is_polymorphic_v) @@ -507,7 +543,7 @@ public: else if (IsReader()) rParam.rValue = nullptr; - ParamEnd(); + InternalEndParam(rParam); } PopParameter(rParam); @@ -521,8 +557,7 @@ public: { PushParameter(rParam); - if (ShouldSerializeParameter(rParam) && - ParamBegin(rParam.pkName, rParam.HintFlags)) + if (InternalStartParam(rParam)) { if (PreSerializePointer( (void*&) rParam.rValue, rParam.HintFlags )) { @@ -560,7 +595,7 @@ public: else if (IsReader()) rParam.rValue = nullptr; - ParamEnd(); + InternalEndParam(rParam); } else { @@ -664,11 +699,36 @@ public: } }; +/** Function that serializes a value directly */ +template +ENABLE_IF( IS_SERIAL_TYPE(Primitive), IArchive& ) +inline SerializeDirect(IArchive& Arc, ValType& Value) +{ + Arc.SerializePrimitive(Value, SH_InheritHints); + return Arc; +} + +template +ENABLE_IF( IS_SERIAL_TYPE(Global), IArchive& ) +inline SerializeDirect(IArchive& Arc, ValType& Value) +{ + Serialize(Arc, Value); + return Arc; +} + +template +ENABLE_IF( IS_SERIAL_TYPE(Member), IArchive& ) +inline SerializeDirect(IArchive& Arc, ValType& Value) +{ + Value.Serialize(Arc); + return Arc; +} + #if WITH_CODEGEN // Default enum serializer; can be overridden #include -template::value>::type> +template::value >::type> inline void Serialize(IArchive& Arc, T& Val) { if (Arc.IsTextFormat()) @@ -709,7 +769,7 @@ inline void Serialize(IArchive& Arc, std::vector& Vector) for (u32 i = 0; i < Size; i++) { // SH_IgnoreName to preserve compatibility with older files that may have differently-named items - Arc << SerialParameter("Item", Vector[i], SH_IgnoreName); + Arc << SerialParameter("Element", Vector[i], SH_IgnoreName); } } @@ -742,7 +802,7 @@ inline void Serialize(IArchive& Arc, std::list& List) } for (auto Iter = List.begin(); Iter != List.end(); Iter++) - Arc << SerialParameter("Item", *Iter, SH_IgnoreName); + Arc << SerialParameter("Element", *Iter, SH_IgnoreName); } // Overload for TStringList and TWideStringList so they can use the TString/TWideString serialize functions @@ -770,7 +830,7 @@ inline void Serialize(IArchive& Arc, std::set& Set) for (u32 i = 0; i < Size; i++) { T Val; - Arc << SerialParameter("Item", Val, SH_IgnoreName); + Arc << SerialParameter("Element", Val, SH_IgnoreName); Set.insert(Val); } } @@ -780,7 +840,7 @@ inline void Serialize(IArchive& Arc, std::set& Set) for (auto Iter = Set.begin(); Iter != Set.end(); Iter++) { T Val = *Iter; - Arc << SerialParameter("Item", Val, SH_IgnoreName); + Arc << SerialParameter("Element", Val, SH_IgnoreName); } } } @@ -799,7 +859,7 @@ inline void SerializeMap_Internal(IArchive& Arc, MapType& Map) KeyType Key; ValType Val; - if (Arc.ParamBegin("Item", SH_IgnoreName)) + if (Arc.ParamBegin("Element", SH_IgnoreName)) { Arc << SerialParameter("Key", Key, SH_IgnoreName) << SerialParameter("Value", Val, SH_IgnoreName); @@ -820,7 +880,7 @@ inline void SerializeMap_Internal(IArchive& Arc, MapType& Map) KeyType Key = Iter->first; ValType Val = Iter->second; - if (Arc.ParamBegin("Item", SH_IgnoreName)) + if (Arc.ParamBegin("Element", SH_IgnoreName)) { Arc << SerialParameter("Key", Key, SH_IgnoreName) << SerialParameter("Value", Val, SH_IgnoreName); @@ -843,6 +903,27 @@ inline void Serialize(IArchive& Arc, std::unordered_map& Map) SerializeMap_Internal >(Arc, Map); } +// Smart pointer serialize methods +template +void Serialize(IArchive& Arc, std::unique_ptr& Pointer) +{ + T* pRawPtr = Pointer.get(); + Arc << SerialParameter("RawPointer", pRawPtr, SH_Proxy); + + if (Arc.IsReader()) + Pointer = std::unique_ptr(pRawPtr); +} + +template +void Serialize(IArchive& Arc, std::shared_ptr& Pointer) +{ + T* pRawPtr = Pointer.get(); + Arc << SerialParameter("RawPointer", pRawPtr, SH_Proxy); + + if (Arc.IsReader()) + Pointer = std::shared_ptr(pRawPtr); +} + // Remove header-only macros #undef ENABLE_IF #undef SUPPORTS_DEFAULT_VALUES diff --git a/src/Core/Resource/CResTypeFilter.h b/src/Core/Resource/CResTypeFilter.h index 2f259ac4..a8e18769 100644 --- a/src/Core/Resource/CResTypeFilter.h +++ b/src/Core/Resource/CResTypeFilter.h @@ -50,7 +50,7 @@ public: void Serialize(IArchive& rArc) { if (rArc.IsReader()) mGame = rArc.Game(); - rArc << SerialParameter("AcceptedTypes", mAcceptedTypes); + rArc << SerialParameter("AcceptedTypes", mAcceptedTypes, SH_Proxy); } inline bool Accepts(EResType Type) const diff --git a/src/Core/Resource/Factory/CTemplateLoader.cpp b/src/Core/Resource/Factory/CTemplateLoader.cpp index 566c8f7f..347d94fb 100644 --- a/src/Core/Resource/Factory/CTemplateLoader.cpp +++ b/src/Core/Resource/Factory/CTemplateLoader.cpp @@ -375,8 +375,8 @@ IPropertyNew* CTemplateLoader::LoadProperty(XMLElement* pElem, CScriptTemplate* CStructPropertyNew* CTemplateLoader::LoadStructArchetype(const TString& rkTemplateFileName) { // Check whether this struct has already been read - auto it = mpMaster->mStructTemplates.find(rkTemplateFileName); - CStructPropertyNew* pArchetype = (it == mpMaster->mStructTemplates.end() ? nullptr : it->second); + TString StructName = rkTemplateFileName.GetFileName(false); + CStructPropertyNew* pArchetype = mpMaster->FindStructArchetype(StructName); // If the struct template hasn't been read yet, then we read it and add it to master's list if (!pArchetype) @@ -432,8 +432,13 @@ CStructPropertyNew* CTemplateLoader::LoadStructArchetype(const TString& rkTempla ASSERT(pSubPropsElem); LoadProperties(pSubPropsElem, nullptr, pArchetype, rkTemplateFileName); - mpMaster->mStructTemplates[rkTemplateFileName] = pArchetype; pArchetype->PostInitialize(); + + mpMaster->mStructTemplates.emplace( + std::make_pair( + StructName, + CMasterTemplate::SPropertyTemplatePath(rkTemplateFileName, pArchetype) + )); } } @@ -444,8 +449,8 @@ CStructPropertyNew* CTemplateLoader::LoadStructArchetype(const TString& rkTempla CEnumProperty* CTemplateLoader::LoadEnumArchetype(const TString& rkTemplateFileName, bool bIsChoice) { // Check whether this struct has already been read - auto it = mpMaster->mEnumTemplates.find(rkTemplateFileName); - CEnumProperty* pArchetype = (it == mpMaster->mEnumTemplates.end() ? nullptr : it->second); + TString EnumName = rkTemplateFileName.GetFileName(false); + CEnumProperty* pArchetype = mpMaster->FindEnumArchetype(EnumName); // If the enum template hasn't been read yet, then we read it and add it to master's list if (!pArchetype) @@ -475,8 +480,13 @@ CEnumProperty* CTemplateLoader::LoadEnumArchetype(const TString& rkTemplateFileN ASSERT(pEnumers); LoadEnumerators(pEnumers, pArchetype, rkTemplateFileName); - mpMaster->mEnumTemplates[rkTemplateFileName] = pArchetype; pArchetype->PostInitialize(); + + mpMaster->mEnumTemplates.emplace( + std::make_pair( + EnumName, + CMasterTemplate::SPropertyTemplatePath(rkTemplateFileName, pArchetype) + )); } } @@ -487,8 +497,8 @@ CEnumProperty* CTemplateLoader::LoadEnumArchetype(const TString& rkTemplateFileN CFlagsProperty* CTemplateLoader::LoadFlagsArchetype(const TString& rkTemplateFileName) { // Check whether this struct has already been read - auto it = mpMaster->mFlagsTemplates.find(rkTemplateFileName); - CFlagsProperty* pArchetype = (it == mpMaster->mFlagsTemplates.end() ? nullptr : it->second); + TString FlagsName = rkTemplateFileName.GetFileName(false); + CFlagsProperty* pArchetype = mpMaster->FindFlagsArchetype(FlagsName); // If the enum template hasn't been read yet, then we read it and add it to master's list if (!pArchetype) @@ -517,8 +527,14 @@ CFlagsProperty* CTemplateLoader::LoadFlagsArchetype(const TString& rkTemplateFil ASSERT(pFlags); LoadBitFlags(pFlags, pArchetype, rkTemplateFileName); - mpMaster->mFlagsTemplates[rkTemplateFileName] = pArchetype; pArchetype->PostInitialize(); + + mpMaster->mFlagsTemplates.emplace( + std::make_pair( + FlagsName, + CMasterTemplate::SPropertyTemplatePath(rkTemplateFileName, pArchetype) + )); + } } @@ -937,28 +953,14 @@ void CTemplateLoader::LoadMasterTemplate(XMLDocument *pDoc, CMasterTemplate *pMa mMasterDir = pMaster->mSourceFile.GetFileDirectory(); XMLElement *pRoot = pDoc->FirstChildElement("MasterTemplate"); - mpMaster->mVersion = TString(pRoot->Attribute("version")).ToInt32(); - XMLElement *pElem = pRoot->FirstChildElement(); while (pElem) { TString NodeName = pElem->Name(); - // Versions - if (NodeName == "versions") - { - XMLElement *pVersion = pElem->FirstChildElement("version"); - - while (pVersion) - { - mpMaster->mGameVersions.push_back(pVersion->GetText()); - pVersion = pVersion->NextSiblingElement("version"); - } - } - // Objects - else if (NodeName == "objects") + if (NodeName == "objects") { XMLElement *pObj = pElem->FirstChildElement("object"); @@ -984,7 +986,13 @@ void CTemplateLoader::LoadMasterTemplate(XMLDocument *pDoc, CMasterTemplate *pMa CScriptTemplate *pTemp = LoadScriptTemplate(&ScriptXML, TemplateName, ID); if (pTemp) - mpMaster->mTemplates[ID] = pTemp; + { + mpMaster->mScriptTemplates.emplace( + std::make_pair( + ID, + CMasterTemplate::SScriptTemplatePath(ID, TemplateName, pTemp) + )); + } } pObj = pObj->NextSiblingElement("object"); @@ -1007,7 +1015,7 @@ void CTemplateLoader::LoadMasterTemplate(XMLDocument *pDoc, CMasterTemplate *pMa StateID = CFourCC(StrID).ToLong(); TString StateName = pState->Attribute("name"); - mpMaster->mStates[StateID] = SState(StateID, StateName); + mpMaster->mStates[StateID] = StateName; pState = pState->NextSiblingElement("state"); } } @@ -1028,7 +1036,7 @@ void CTemplateLoader::LoadMasterTemplate(XMLDocument *pDoc, CMasterTemplate *pMa MessageID = CFourCC(StrID).ToLong(); TString MessageName = pMessage->Attribute("name"); - mpMaster->mMessages[MessageID] = SMessage(MessageID, MessageName); + mpMaster->mMessages[MessageID] = MessageName; pMessage = pMessage->NextSiblingElement("message"); } } @@ -1229,3 +1237,60 @@ void CTemplateLoader::LoadPropertyList(XMLDocument *pDoc, const TString& ListNam } } } + +void CTemplateLoader::SaveGameList() +{ + const TString kTemplatesDir = "../templates_new/"; + FileUtil::MakeDirectory( kTemplatesDir ); + + // Write game list + { + const TString kGameListPath = kTemplatesDir + "GameList.xml"; + CXMLWriter Writer(kGameListPath, "GameList"); + TString PropertyListPath = "PropertyNameMap.xml"; + Writer << SerialParameter("PropertyList", PropertyListPath, 0); + + Writer.ParamBegin("Games", 0); + + for (auto Iter = CMasterTemplate::smMasterMap.begin(); Iter != CMasterTemplate::smMasterMap.end(); Iter++) + { + struct SGameInfo + { + EGame Game; + TString Name; + TString MasterPath; + + void Serialize(IArchive& Arc) + { + Arc << SerialParameter("ID", Game, SH_Attribute) + << SerialParameter("Name", Name) + << SerialParameter("MasterTemplate", MasterPath); + } + }; + + CMasterTemplate* pMaster = Iter->second; + SGameInfo Info; + Info.Game = pMaster->Game(); + Info.Name = pMaster->GameName(); + Info.MasterPath = pMaster->GetDirectory() + "MasterTemplate.xml"; + Writer << SerialParameter("Game", Info); + } + Writer.ParamEnd(); + } + + // Write master templates + { + std::list MasterList = CMasterTemplate::MasterList(); + + for (auto Iter = MasterList.begin(); Iter != MasterList.end(); Iter++) + { + CMasterTemplate* pMasterTemplate = *Iter; + TString MasterFilePath = kTemplatesDir + pMasterTemplate->GetDirectory() + "Game.xml"; + FileUtil::MakeDirectory( MasterFilePath.GetFileDirectory() ); + + CXMLWriter Writer(MasterFilePath, "Game", 0, pMasterTemplate->Game()); + pMasterTemplate->Serialize(Writer); + pMasterTemplate->SaveSubTemplates(); + } + } +} diff --git a/src/Core/Resource/Factory/CTemplateLoader.h b/src/Core/Resource/Factory/CTemplateLoader.h index a4423540..352119ad 100644 --- a/src/Core/Resource/Factory/CTemplateLoader.h +++ b/src/Core/Resource/Factory/CTemplateLoader.h @@ -50,6 +50,8 @@ public: static void LoadGameTemplates(EGame Game); static void LoadAllGames(); static void LoadPropertyList(tinyxml2::XMLDocument* pDoc, const TString& rkListName); + + static void SaveGameList(); }; #endif // CTEMPLATELOADER_H diff --git a/src/Core/Resource/Script/CLink.h b/src/Core/Resource/Script/CLink.h index c2bd538f..1b876d80 100644 --- a/src/Core/Resource/Script/CLink.h +++ b/src/Core/Resource/Script/CLink.h @@ -8,20 +8,56 @@ struct SState { - u32 ID; + union { + u32 ID; + CFourCC ID_4CC; + }; TString Name; - SState() {} - SState(u32 _ID, const TString& rkName) : ID(_ID), Name(rkName) {} + SState() + {} + + SState(u32 InID, const TString& kInName) + : ID(InID) + , Name(kInName) + {} + + void Serialize(IArchive& Arc) + { + if (Arc.Game() <= ePrime) + Arc << SerialParameter("ID", ID, SH_Attribute | SH_HexDisplay); + else + Arc << SerialParameter("ID", ID_4CC, SH_Attribute); + + Arc << SerialParameter("Name", Name, SH_Attribute); + } }; struct SMessage { - u32 ID; + union { + u32 ID; + CFourCC ID_4CC; + }; TString Name; - SMessage() {} - SMessage(u32 _ID, const TString& rkName) : ID(_ID), Name(rkName) {} + SMessage() + {} + + SMessage(u32 InID, const TString& kInName) + : ID(InID) + , Name(kInName) + {} + + void Serialize(IArchive& Arc) + { + if (Arc.Game() <= ePrime) + Arc << SerialParameter("ID", ID, SH_Attribute | SH_HexDisplay); + else + Arc << SerialParameter("ID", ID_4CC, SH_Attribute); + + Arc << SerialParameter("Name", Name, SH_Attribute); + } }; class CLink diff --git a/src/Core/Resource/Script/CMasterTemplate.cpp b/src/Core/Resource/Script/CMasterTemplate.cpp index a569b37a..4ddf8f09 100644 --- a/src/Core/Resource/Script/CMasterTemplate.cpp +++ b/src/Core/Resource/Script/CMasterTemplate.cpp @@ -3,34 +3,81 @@ #include CMasterTemplate::CMasterTemplate() - : mVersion(0) - , mFullyLoaded(false) + : mFullyLoaded(false) { } -CMasterTemplate::~CMasterTemplate() +void CMasterTemplate::Serialize(IArchive& Arc) { - for (auto it = mTemplates.begin(); it != mTemplates.end(); it++) - delete it->second; + Arc << SerialParameter("ScriptObjects", mScriptTemplates) + << SerialParameter("Structs", mStructTemplates) + << SerialParameter("Enums", mEnumTemplates) + << SerialParameter("Flags", mFlagsTemplates) + << SerialParameter("States", mStates) + << SerialParameter("Messages", mMessages); +} + +void CMasterTemplate::LoadSubTemplates() +{ + //todo +} + +void CMasterTemplate::SaveSubTemplates() +{ + TString GameDir = "../templates_new/" + GetDirectory(); + + for (auto Iter = mScriptTemplates.begin(); Iter != mScriptTemplates.end(); Iter++) + { + SScriptTemplatePath& Path = Iter->second; + TString OutPath = GameDir + Path.Path; + + FileUtil::MakeDirectory( OutPath.GetFileDirectory() ); + CXMLWriter Writer(OutPath, "ScriptObject", 0, Game()); + Path.pTemplate->Serialize(Writer); + } + + for (auto Iter = mStructTemplates.begin(); Iter != mStructTemplates.end(); Iter++) + { + SPropertyTemplatePath& Path = Iter->second; + TString OutPath = GameDir + Path.Path; + + FileUtil::MakeDirectory( OutPath.GetFileDirectory() ); + CXMLWriter Writer(OutPath, "Struct", 0, Game()); + Path.pTemplate->Serialize(Writer); + } + + for (auto Iter = mEnumTemplates.begin(); Iter != mEnumTemplates.end(); Iter++) + { + SPropertyTemplatePath& Path = Iter->second; + TString OutPath = GameDir + Path.Path; + + FileUtil::MakeDirectory( OutPath.GetFileDirectory() ); + CXMLWriter Writer(OutPath, "Enum", 0, Game()); + Path.pTemplate->Serialize(Writer); + } + + for (auto Iter = mFlagsTemplates.begin(); Iter != mFlagsTemplates.end(); Iter++) + { + SPropertyTemplatePath& Path = Iter->second; + TString OutPath = GameDir + Path.Path; + + FileUtil::MakeDirectory( OutPath.GetFileDirectory() ); + CXMLWriter Writer(OutPath, "Flags", 0, Game()); + Path.pTemplate->Serialize(Writer); + } } u32 CMasterTemplate::GameVersion(TString VersionName) { - VersionName = VersionName.ToLower(); - - for (u32 iVer = 0; iVer < mGameVersions.size(); iVer++) - if (mGameVersions[iVer].ToLower() == VersionName) - return iVer; - return -1; } CScriptTemplate* CMasterTemplate::TemplateByID(u32 ObjectID) { - auto it = mTemplates.find(ObjectID); + auto it = mScriptTemplates.find(ObjectID); - if (it != mTemplates.end()) - return it->second; + if (it != mScriptTemplates.end()) + return it->second.pTemplate.get(); else return nullptr; } @@ -42,16 +89,16 @@ CScriptTemplate* CMasterTemplate::TemplateByID(const CFourCC& ObjectID) CScriptTemplate* CMasterTemplate::TemplateByIndex(u32 Index) { - auto it = mTemplates.begin(); - return (std::next(it, Index))->second; + auto it = mScriptTemplates.begin(); + return (std::next(it, Index))->second.pTemplate.get(); } SState CMasterTemplate::StateByID(u32 StateID) { - auto it = mStates.find(StateID); + auto Iter = mStates.find(StateID); - if (it != mStates.end()) - return it->second; + if (Iter != mStates.end()) + return SState(Iter->first, Iter->second); else return SState(-1, "Invalid"); } @@ -63,16 +110,17 @@ SState CMasterTemplate::StateByID(const CFourCC& State) SState CMasterTemplate::StateByIndex(u32 Index) { - auto it = mStates.begin(); - return (std::next(it, Index))->second; + auto Iter = mStates.begin(); + Iter = std::next(Iter, Index); + return SState(Iter->first, Iter->second); } SMessage CMasterTemplate::MessageByID(u32 MessageID) { - auto it = mMessages.find(MessageID); + auto Iter = mMessages.find(MessageID); - if (it != mMessages.end()) - return it->second; + if (Iter != mMessages.end()) + return SMessage(Iter->first, Iter->second); else return SMessage(-1, "Invalid"); } @@ -84,18 +132,31 @@ SMessage CMasterTemplate::MessageByID(const CFourCC& MessageID) SMessage CMasterTemplate::MessageByIndex(u32 Index) { - auto it = mMessages.begin(); - return (std::next(it, Index))->second; + auto Iter = mMessages.begin(); + Iter = std::next(Iter, Index); + return SMessage(Iter->first, Iter->second); } -CStructPropertyNew* CMasterTemplate::StructAtSource(const TString& rkSource) + +CStructPropertyNew* CMasterTemplate::FindStructArchetype(const TString& kStructName) const { - auto InfoIt = mStructTemplates.find(rkSource); + auto Iter = mStructTemplates.find(kStructName); + IPropertyNew* pProperty = (Iter != mStructTemplates.end()) ? Iter->second.pTemplate.get() : nullptr; + return TPropCast(pProperty); +} - if (InfoIt != mStructTemplates.end()) - return InfoIt->second; +CEnumProperty* CMasterTemplate::FindEnumArchetype(const TString& kEnumName) const +{ + auto Iter = mEnumTemplates.find(kEnumName); + IPropertyNew* pProperty = (Iter != mEnumTemplates.end()) ? Iter->second.pTemplate.get() : nullptr; + return TPropCast(pProperty); +} - else return nullptr; +CFlagsProperty* CMasterTemplate::FindFlagsArchetype(const TString& kFlagsName) const +{ + auto Iter = mFlagsTemplates.find(kFlagsName); + IPropertyNew* pProperty = (Iter != mFlagsTemplates.end()) ? Iter->second.pTemplate.get() : nullptr; + return TPropCast(pProperty); } // ************ STATIC ************ diff --git a/src/Core/Resource/Script/CMasterTemplate.h b/src/Core/Resource/Script/CMasterTemplate.h index 288e1feb..75b49dcc 100644 --- a/src/Core/Resource/Script/CMasterTemplate.h +++ b/src/Core/Resource/Script/CMasterTemplate.h @@ -8,25 +8,113 @@ #include #include +/** Serialization aid + * Retro switched from using integers to fourCCs to represent IDs in several cases (states/messages, object IDs). + * This struct is functionally an integer but it serializes as an int for MP1 and a fourCC for MP2 and on. + */ +struct SObjId +{ + union { + u32 ID; + CFourCC ID_4CC; + }; + + inline SObjId() {} + inline SObjId(u32 InID) : ID(InID) {} + inline SObjId(CFourCC InID) : ID_4CC(InID) {} + + inline operator u32() const { return ID; } + inline operator CFourCC() const { return ID_4CC; } + + void Serialize(IArchive& Arc) + { + if (Arc.Game() <= ePrime) + Arc.SerializePrimitive(ID, SH_HexDisplay); + else + Arc.SerializePrimitive(ID_4CC, 0); + } +}; + class CMasterTemplate { friend class CTemplateLoader; friend class CTemplateWriter; + /** Struct holding a reference to a script object template */ + struct SScriptTemplatePath + { + /** Script object ID */ + SObjId ID; + + /** File path to the template file, relative to the game directory */ + TString Path; + + /** Template in memory */ + std::shared_ptr pTemplate; + + /** Constructor */ + SScriptTemplatePath() + : ID(0) + {} + + SScriptTemplatePath(u32 InID, const TString& kInPath, CScriptTemplate* pInTemplate) + : ID(InID) + , Path(kInPath) + , pTemplate( std::shared_ptr(pInTemplate) ) + {} + + SScriptTemplatePath(const CFourCC& kInID, const TString& kInPath, CScriptTemplate* pInTemplate) + : ID(kInID) + , Path(kInPath) + , pTemplate( std::shared_ptr(pInTemplate) ) + {} + + /** Serializer */ + void Serialize(IArchive& Arc) + { + Arc << SerialParameter("ID", ID, SH_Attribute) + << SerialParameter("Path", Path, SH_Attribute); + } + }; + + /** Struct holding a reference to a property template */ + struct SPropertyTemplatePath + { + /** File path to the template file, relative to the game directory */ + TString Path; + + /** Template in memory */ + std::shared_ptr pTemplate; + + /** Constructor */ + SPropertyTemplatePath() + {} + + SPropertyTemplatePath(const TString& kInPath, IPropertyNew* pInTemplate) + : Path(kInPath) + , pTemplate( std::shared_ptr(pInTemplate) ) + {} + + /** Serializer */ + void Serialize(IArchive& Arc) + { + Arc << SerialParameter("Path", Path, SH_Attribute); + } + }; + EGame mGame; TString mGameName; TString mSourceFile; - u32 mVersion; bool mFullyLoaded; - std::vector mGameVersions; - std::map mStructTemplates; - std::map mEnumTemplates; - std::map mFlagsTemplates; + /** Template arrays */ + std::map mScriptTemplates; + std::map mStructTemplates; + std::map mEnumTemplates; + std::map mFlagsTemplates; - std::map mTemplates; - std::map mStates; - std::map mMessages; + std::map mStates; + std::map mMessages; struct SPropIDInfo { @@ -40,7 +128,9 @@ class CMasterTemplate public: CMasterTemplate(); - ~CMasterTemplate(); + void Serialize(IArchive& Arc); + void LoadSubTemplates(); + void SaveSubTemplates(); u32 GameVersion(TString VersionName); CScriptTemplate* TemplateByID(u32 ObjectID); CScriptTemplate* TemplateByID(const CFourCC& ObjectID); @@ -51,13 +141,15 @@ public: SMessage MessageByID(u32 MessageID); SMessage MessageByID(const CFourCC& MessageID); SMessage MessageByIndex(u32 Index); - CStructPropertyNew* StructAtSource(const TString& rkSource); + + CStructPropertyNew* FindStructArchetype(const TString& kStructName) const; + CEnumProperty* FindEnumArchetype(const TString& kEnumName) const; + CFlagsProperty* FindFlagsArchetype(const TString& kFlagsName) const; // Inline Accessors inline EGame Game() const { return mGame; } inline TString GameName() const { return mGameName; } - inline u32 NumGameVersions() const { return mGameVersions.empty() ? 1 : mGameVersions.size(); } - inline u32 NumScriptTemplates() const { return mTemplates.size(); } + inline u32 NumScriptTemplates() const { return mScriptTemplates.size(); } inline u32 NumStates() const { return mStates.size(); } inline u32 NumMessages() const { return mMessages.size(); } inline bool IsLoadedSuccessfully() { return mFullyLoaded; } diff --git a/src/Core/Resource/Script/CScriptTemplate.cpp b/src/Core/Resource/Script/CScriptTemplate.cpp index a087b619..c875ffb9 100644 --- a/src/Core/Resource/Script/CScriptTemplate.cpp +++ b/src/Core/Resource/Script/CScriptTemplate.cpp @@ -8,6 +8,7 @@ #include #include +// Old constructor CScriptTemplate::CScriptTemplate(CMasterTemplate *pMaster) : mpMaster(pMaster) , mpProperties(nullptr) @@ -24,10 +25,58 @@ CScriptTemplate::CScriptTemplate(CMasterTemplate *pMaster) { } +// New constructor +CScriptTemplate::CScriptTemplate(CMasterTemplate* pInMaster, u32 InObjectID, const TString& kInFilePath) + : mRotationType(eRotationEnabled) + , mScaleType(eScaleEnabled) + , mPreviewScale(1.f) + , mVolumeShape(eNoShape) + , mVolumeScale(1.f) + , mSourceFile(kInFilePath) + , mObjectID(InObjectID) + , mpMaster(pInMaster) + , mpNameProperty(nullptr) + , mpPositionProperty(nullptr) + , mpRotationProperty(nullptr) + , mpScaleProperty(nullptr) + , mpActiveProperty(nullptr) + , mpLightParametersProperty(nullptr) + , mVisible(true) +{ +} + CScriptTemplate::~CScriptTemplate() { } +void CScriptTemplate::Serialize(IArchive& Arc) +{ + Arc << SerialParameter("Modules", mModules, SH_Optional) + << SerialParameter("Properties", mpProperties); + + if (Arc.ParamBegin("EditorProperties", 0)) + { + Arc << SerialParameter("NameProperty", mNameIDString, SH_Optional) + << SerialParameter("PositionProperty", mPositionIDString, SH_Optional) + << SerialParameter("RotationProperty", mRotationIDString, SH_Optional) + << SerialParameter("ScaleProperty", mScaleIDString, SH_Optional) + << SerialParameter("ActiveProperty", mActiveIDString, SH_Optional) + << SerialParameter("LightParametersProperty", mLightParametersIDString, SH_Optional); + + Arc.ParamEnd(); + } + + Arc << SerialParameter("Assets", mAssets, SH_Optional) + << SerialParameter("Attachments", mAttachments, SH_Optional) + << SerialParameter("RotationType", mRotationType, SH_Optional, eRotationEnabled) + << SerialParameter("ScaleType", mScaleType, SH_Optional, eScaleEnabled) + << SerialParameter("PreviewScale", mPreviewScale, SH_Optional, 1.0f) + << SerialParameter("VolumeShape", mVolumeShape, SH_Optional, eNoShape) + << SerialParameter("VolumeScale", mVolumeScale, SH_Optional, 1.0f) + << SerialParameter("VolumeConditionProperty", mVolumeConditionIDString, SH_Optional) + << SerialParameter("VolumeConditions", mVolumeConditions, SH_Optional); +} + void CScriptTemplate::PostLoad() { if (!mNameIDString.IsEmpty()) mpNameProperty = TPropCast( mpProperties->ChildByIDString(mNameIDString) ); diff --git a/src/Core/Resource/Script/CScriptTemplate.h b/src/Core/Resource/Script/CScriptTemplate.h index 364be38b..69d7d77c 100644 --- a/src/Core/Resource/Script/CScriptTemplate.h +++ b/src/Core/Resource/Script/CScriptTemplate.h @@ -26,6 +26,13 @@ struct SAttachment TIDString AttachProperty; // Must point to a CMDL! TString LocatorName; EAttachType AttachType; + + void Serialize(IArchive& Arc) + { + Arc << SerialParameter("AttachProperty", AttachProperty, SH_Attribute) + << SerialParameter("LocatorName", LocatorName, SH_Attribute) + << SerialParameter("AttachType", AttachType, SH_Attribute); + } }; /* @@ -52,41 +59,28 @@ public: private: struct SEditorAsset { - enum { + enum EAssetType { eModel, eAnimParams, eBillboard, eCollision } AssetType; - enum { + enum EAssetSource { eProperty, eFile } AssetSource; TIDString AssetLocation; s32 ForceNodeIndex; // Force animsets to use specific node instead of one from property + + void Serialize(IArchive& Arc) + { + Arc << SerialParameter("Type", AssetType, SH_Attribute) + << SerialParameter("Source", AssetSource, SH_Attribute) + << SerialParameter("Location", AssetLocation, SH_Attribute) + << SerialParameter("ForceCharacterIndex", ForceNodeIndex, SH_Attribute | SH_Optional, (s32) -1); + } }; - CMasterTemplate* mpMaster; - std::unique_ptr mpProperties; - std::list mObjectList; std::vector mModules; - TString mSourceFile; - u32 mObjectID; - bool mVisible; - - // Editor Properties - TIDString mNameIDString; - TIDString mPositionIDString; - TIDString mRotationIDString; - TIDString mScaleIDString; - TIDString mActiveIDString; - TIDString mLightParametersIDString; - - CStringProperty* mpNameProperty; - CVectorProperty* mpPositionProperty; - CVectorProperty* mpRotationProperty; - CVectorProperty* mpScaleProperty; - CBoolProperty* mpActiveProperty; - CStructPropertyNew* mpLightParametersProperty; - + std::unique_ptr mpProperties; std::vector mAssets; std::vector mAttachments; @@ -99,15 +93,47 @@ private: float mVolumeScale; TIDString mVolumeConditionIDString; + TString mSourceFile; + u32 mObjectID; + + // Editor Properties + TIDString mNameIDString; + TIDString mPositionIDString; + TIDString mRotationIDString; + TIDString mScaleIDString; + TIDString mActiveIDString; + TIDString mLightParametersIDString; + + CMasterTemplate* mpMaster; + std::list mObjectList; + + CStringProperty* mpNameProperty; + CVectorProperty* mpPositionProperty; + CVectorProperty* mpRotationProperty; + CVectorProperty* mpScaleProperty; + CBoolProperty* mpActiveProperty; + CStructPropertyNew* mpLightParametersProperty; + struct SVolumeCondition { - int Value; + u32 Value; EVolumeShape Shape; float Scale; + + void Serialize(IArchive& Arc) + { + Arc << SerialParameter("Value", Value) + << SerialParameter("Shape", Shape) + << SerialParameter("Scale", Scale, SH_Optional, 1.0f); + } }; std::vector mVolumeConditions; + bool mVisible; public: + // Old constructor CScriptTemplate(CMasterTemplate *pMaster); + // New constructor + CScriptTemplate(CMasterTemplate* pMaster, u32 ObjectID, const TString& kFilePath); ~CScriptTemplate(); void Serialize(IArchive& rArc); void PostLoad(); @@ -133,11 +159,11 @@ public: const SAttachment& Attachment(u32 Index) const { return mAttachments[Index]; } const std::vector& RequiredModules() const { return mModules; } - inline CStringProperty* NameProperty() const { return mpNameProperty; } - inline CVectorProperty* PositionProperty() const { return mpPositionProperty; } - inline CVectorProperty* RotationProperty() const { return mpRotationProperty; } - inline CVectorProperty* ScaleProperty() const { return mpScaleProperty; } - inline CBoolProperty* ActiveProperty() const { return mpActiveProperty; } + inline CStringProperty* NameProperty() const { return mpNameProperty; } + inline CVectorProperty* PositionProperty() const { return mpPositionProperty; } + inline CVectorProperty* RotationProperty() const { return mpRotationProperty; } + inline CVectorProperty* ScaleProperty() const { return mpScaleProperty; } + inline CBoolProperty* ActiveProperty() const { return mpActiveProperty; } inline CStructPropertyNew* LightParametersProperty() const { return mpLightParametersProperty; } inline void SetVisible(bool Visible) { mVisible = Visible; } diff --git a/src/Core/Resource/Script/IPropertyNew.cpp b/src/Core/Resource/Script/IPropertyNew.cpp index b7f62567..bf78f19b 100644 --- a/src/Core/Resource/Script/IPropertyNew.cpp +++ b/src/Core/Resource/Script/IPropertyNew.cpp @@ -93,14 +93,14 @@ void* IPropertyNew::GetChildDataPointer(void* pPropertyData) const void IPropertyNew::Serialize(IArchive& rArc) { - if (rArc.Game() <= ePrime) + if (rArc.Game() <= ePrime && !IsArchetype()) { rArc << SerialParameter("Name", mName); } - rArc << SerialParameter("ID", mID, SH_HexDisplay | SH_Optional, (u32) 0xFFFFFFFF) + rArc << SerialParameter("ID", mID, SH_HexDisplay | SH_Attribute | SH_Optional, (u32) 0xFFFFFFFF) << SerialParameter("Description", mDescription, SH_Optional) - << SerialParameter("CookPref", mCookPreference, SH_Optional, ECookPreferenceNew::Default) + << SerialParameter("CookPreference", mCookPreference, SH_Optional, ECookPreferenceNew::Default) << SerialParameter("MinVersion", mMinVersion, SH_Optional, 0.f) << SerialParameter("MaxVersion", mMaxVersion, SH_Optional, FLT_MAX); diff --git a/src/Core/Resource/Script/IPropertyNew.h b/src/Core/Resource/Script/IPropertyNew.h index c6cc6367..2472a30a 100644 --- a/src/Core/Resource/Script/IPropertyNew.h +++ b/src/Core/Resource/Script/IPropertyNew.h @@ -376,7 +376,7 @@ protected: public: virtual void Serialize(IArchive& rArc) { - IPropertyNew::Serialize(rArc); + TTypedPropertyNew::Serialize(rArc); // Determine if default value should be serialized as optional. // All MP1 properties should be optional. For MP2 and on, we set optional @@ -436,14 +436,14 @@ protected: public: virtual void Serialize(IArchive& rArc) { - TTypedPropertyNew::Serialize(rArc); + TSerializeableTypedProperty::Serialize(rArc); rArc << SerialParameter("Min", mMinValue, SH_Optional, (PropType) -1) << SerialParameter("Max", mMaxValue, SH_Optional, (PropType) -1); } virtual void InitFromArchetype(IPropertyNew* pOther) { - TTypedPropertyNew::InitFromArchetype(pOther); + TSerializeableTypedProperty::InitFromArchetype(pOther); TNumericalPropertyNew* pCastOther = static_cast(pOther); mMinValue = pCastOther->mMinValue; mMaxValue = pCastOther->mMaxValue; @@ -451,7 +451,7 @@ public: virtual void PropertyValueChanged(void* pPropertyData) { - IPropertyNew::PropertyValueChanged(pPropertyData); + TSerializeableTypedProperty::PropertyValueChanged(pPropertyData); if (mMinValue >= 0 && mMaxValue >= 0) { diff --git a/src/Core/Resource/Script/Property/CEnumProperty.h b/src/Core/Resource/Script/Property/CEnumProperty.h index b1ae5990..5e9d611d 100644 --- a/src/Core/Resource/Script/Property/CEnumProperty.h +++ b/src/Core/Resource/Script/Property/CEnumProperty.h @@ -122,16 +122,17 @@ typedef TEnumPropertyBase CEnumProperty; template<> inline CEnumProperty* TPropCast(IPropertyNew* pProperty) { - EPropertyTypeNew InType = pProperty->Type(); + if (pProperty) + { + EPropertyTypeNew InType = pProperty->Type(); - if (InType == EPropertyTypeNew::Enum || InType == EPropertyTypeNew::Choice) - { - return static_cast(pProperty); - } - else - { - return nullptr; + if (InType == EPropertyTypeNew::Enum || InType == EPropertyTypeNew::Choice) + { + return static_cast(pProperty); + } } + + return nullptr; } template<> diff --git a/src/Editor/WorldEditor/CTemplateEditDialog.cpp b/src/Editor/WorldEditor/CTemplateEditDialog.cpp index 24d7bdd0..cbf170d2 100644 --- a/src/Editor/WorldEditor/CTemplateEditDialog.cpp +++ b/src/Editor/WorldEditor/CTemplateEditDialog.cpp @@ -128,24 +128,38 @@ void CTemplateEditDialog::ApplyChanges() // ************ PROTECTED ************ void CTemplateEditDialog::AddTemplate(IPropertyNew* pProp) { - TString Source = pProp->GetTemplateFileName(); + IPropertyNew* pArchetype = pProp->Archetype(); - if (!Source.IsEmpty()) + if (pArchetype) { - CStructPropertyNew* pStruct = CMasterTemplate::MasterForGame(pProp->Game())->StructAtSource(Source); + pArchetype = pArchetype->RootParent(); - if (!mStructTemplatesToResave.contains(pStruct)) - mStructTemplatesToResave << pStruct; + switch (pArchetype->Type()) + { + + case EPropertyTypeNew::Struct: + { + CStructPropertyNew* pStruct = TPropCast(pArchetype); + if (!mStructTemplatesToResave.contains(pStruct)) + { + mStructTemplatesToResave << pStruct; + } + break; + } + + default: + Log::Warning("Couldn't resave unsupported property archetype: " + TString( EnumValueName(pArchetype->Type()) )); + break; + } } else { CScriptTemplate *pScript = pProp->ScriptTemplate(); - if (pScript) + if (pScript && !mScriptTemplatesToResave.contains(pScript)) { - if (!mScriptTemplatesToResave.contains(pScript)) - mScriptTemplatesToResave << pScript; + mScriptTemplatesToResave << pScript; } else