From 2118bbd0cd0e6c678ea82a86448a071feb73aad7 Mon Sep 17 00:00:00 2001 From: Aruki Date: Sat, 22 Sep 2018 03:26:15 -0600 Subject: [PATCH] More fixes. Property/serialization changes are finally basically finished now. Serialized property templates now load & display correctly in the editor --- src/Common/Serialization/IArchive.h | 29 +- src/Core/GameProject/CGameProject.cpp | 1 - src/Core/Resource/CLight.cpp | 2 +- src/Core/Resource/Factory/CTemplateLoader.cpp | 51 ++-- src/Core/Resource/Script/CMasterTemplate.cpp | 75 ++++- src/Core/Resource/Script/CMasterTemplate.h | 7 +- src/Core/Resource/Script/CScriptTemplate.cpp | 2 + src/Core/Resource/Script/CScriptTemplate.h | 2 + src/Core/Resource/Script/IPropertyNew.cpp | 260 +++++++++--------- src/Core/Resource/Script/IPropertyNew.h | 55 ++-- .../Script/Property/CAnimationProperty.h | 4 +- .../Script/Property/CAnimationSetProperty.h | 11 +- .../Resource/Script/Property/CArrayProperty.h | 17 +- .../Resource/Script/Property/CAssetProperty.h | 14 +- .../Resource/Script/Property/CBoolProperty.h | 4 +- .../Resource/Script/Property/CByteProperty.h | 4 +- .../Resource/Script/Property/CColorProperty.h | 18 +- .../Resource/Script/Property/CEnumProperty.h | 8 + .../Resource/Script/Property/CFlagsProperty.h | 4 +- .../Resource/Script/Property/CFloatProperty.h | 4 +- .../Resource/Script/Property/CGuidProperty.h | 4 +- .../Resource/Script/Property/CIntProperty.h | 4 +- .../Script/Property/CPointerProperty.h | 6 + .../Script/Property/CSequenceProperty.h | 4 +- .../Resource/Script/Property/CShortProperty.h | 4 +- .../Resource/Script/Property/CSoundProperty.h | 4 +- .../Script/Property/CSplineProperty.h | 4 +- .../Script/Property/CStringProperty.h | 4 +- .../Script/Property/CStructProperty.cpp | 44 ++- .../Script/Property/CStructProperty.h | 6 + .../Script/Property/CVectorProperty.h | 15 +- src/Editor/PropertyEdit/CPropertyView.cpp | 6 +- 32 files changed, 405 insertions(+), 272 deletions(-) diff --git a/src/Common/Serialization/IArchive.h b/src/Common/Serialization/IArchive.h index 98372917..ef0993e4 100644 --- a/src/Common/Serialization/IArchive.h +++ b/src/Common/Serialization/IArchive.h @@ -97,8 +97,13 @@ template struct TIsContainer< std::set > : std::true_type {}; template struct TIsContainer< std::map > : std::true_type {}; template struct TIsContainer< std::unordered_map > : std::true_type {}; +/** Class that determines if the type is a smart pointer */ +template struct TIsSmartPointer : std::false_type {}; +template struct TIsSmartPointer< std::shared_ptr > : std::true_type {}; +template struct TIsSmartPointer< std::unique_ptr > : std::true_type {}; + /** Helper macro that tells us whether the parameter supports default property values */ -#define SUPPORTS_DEFAULT_VALUES (!std::is_pointer_v && std::is_copy_assignable_v && THasEqualTo::value && !TIsContainer::value) +#define SUPPORTS_DEFAULT_VALUES (!std::is_pointer_v && std::is_copy_assignable_v && THasEqualTo::value && !TIsContainer::value && !TIsSmartPointer::value) /** TSerialParameter - name/value pair for generic serial parameters */ template @@ -241,14 +246,26 @@ struct SerialType None); }; -/** Helper for determining the type used by a given abstract object class (i.e. the type returned by the Type() function) */ -#define ABSTRACT_TYPE decltype( std::declval().Type() ) - /** For abstract types, determine what kind of ArchiveConstructor the type has */ template struct ArchiveConstructorType { - typedef ABSTRACT_TYPE ObjType; + /** Figure out the type being used to represent the object type. + * If there isn't a type function, then it doesn't matter; just substitute int. + */ + template + static constexpr auto HasTypeMethod(int) -> decltype( std::declval().Type() ) + { + return std::declval().Type(); + } + + template + static constexpr int HasTypeMethod(...) + { + return 0; + } + + using ObjType = decltype(HasTypeMethod(0)); enum { Basic, Advanced, None }; @@ -340,7 +357,7 @@ public: mParmStack.reserve(16); } - virtual ~IArchive() {} + virtual ~IArchive() { ASSERT(mParmStack.empty()); } // Serialize archive version. Always call after opening a file. void SerializeVersion() diff --git a/src/Core/GameProject/CGameProject.cpp b/src/Core/GameProject/CGameProject.cpp index 66114a91..826de08d 100644 --- a/src/Core/GameProject/CGameProject.cpp +++ b/src/Core/GameProject/CGameProject.cpp @@ -266,7 +266,6 @@ CGameProject* CGameProject::LoadProject(const TString& rkProjPath, IProgressNoti return nullptr; } - CTemplateLoader::LoadGameTemplates(pProj->mGame); pProj->mProjFileLock.Lock(ProjPath); pProj->mpGameInfo->LoadGameInfo(pProj->mGame); pProj->mpAudioManager->LoadAssets(); diff --git a/src/Core/Resource/CLight.cpp b/src/Core/Resource/CLight.cpp index bbb9c3ec..53b93976 100644 --- a/src/Core/Resource/CLight.cpp +++ b/src/Core/Resource/CLight.cpp @@ -137,7 +137,7 @@ CStructPropertyNew* CLight::GetProperties() const if (!pProperties) { pProperties = (CStructPropertyNew*) IPropertyNew::CreateIntrinsic(EPropertyTypeNew::Struct, - nullptr, + ePrime, 0, "Light"); diff --git a/src/Core/Resource/Factory/CTemplateLoader.cpp b/src/Core/Resource/Factory/CTemplateLoader.cpp index 70af4743..e29d79c9 100644 --- a/src/Core/Resource/Factory/CTemplateLoader.cpp +++ b/src/Core/Resource/Factory/CTemplateLoader.cpp @@ -189,14 +189,14 @@ IPropertyNew* CTemplateLoader::LoadProperty(XMLElement* pElem, CScriptTemplate* // create property as a copy of the archetype if (pArchetype != nullptr) { - pProp = IPropertyNew::CreateCopy(pArchetype, pParent); + pProp = IPropertyNew::CreateCopy(pArchetype); } } // no archetype, so do normal create if (!pProp) { - pProp = IPropertyNew::Create(Type, pParent, mGame, pScript, false); + pProp = IPropertyNew::Create(Type, mGame); } // we need to have a valid property by this point @@ -340,7 +340,7 @@ IPropertyNew* CTemplateLoader::LoadProperty(XMLElement* pElem, CScriptTemplate* } else { - pArray->mpItemArchetype = IPropertyNew::Create(EPropertyTypeNew::Struct, pArray, mGame, pScript, false); + pArray->mpItemArchetype = IPropertyNew::Create(EPropertyTypeNew::Struct, mGame); pStruct = TPropCast(pArray->mpItemArchetype); pStruct->mFlags = EPropertyFlag::IsAtomic | EPropertyFlag::IsArrayArchetype; } @@ -358,17 +358,16 @@ IPropertyNew* CTemplateLoader::LoadProperty(XMLElement* pElem, CScriptTemplate* { LoadProperties(pProperties, pScript, pStruct, rkTemplateName); } - - if (Type == EPropertyTypeNew::Array) - { - pStruct->PostInitialize(); - } } if (IsNewProperty) + { CMasterTemplate::AddProperty(pProp, mMasterDir + rkTemplateName); - pProp->PostInitialize(); + if (pParent) + pParent->mChildren.push_back(pProp); + } + return pProp; } @@ -393,11 +392,7 @@ CStructPropertyNew* CTemplateLoader::LoadStructArchetype(const TString& rkTempla if (!Doc.Error()) { pArchetype = TPropCast( - IPropertyNew::Create(EPropertyTypeNew::Struct, - nullptr, - mGame, - nullptr, - false) + IPropertyNew::Create(EPropertyTypeNew::Struct, mGame) ); ASSERT(pArchetype != nullptr); @@ -438,7 +433,7 @@ CStructPropertyNew* CTemplateLoader::LoadStructArchetype(const TString& rkTempla ASSERT(pSubPropsElem); LoadProperties(pSubPropsElem, nullptr, pArchetype, rkTemplateFileName); - pArchetype->PostInitialize(); + pArchetype->Initialize(nullptr, nullptr, 0); mpMaster->mPropertyTemplates.emplace( std::make_pair( @@ -474,11 +469,7 @@ CEnumProperty* CTemplateLoader::LoadEnumArchetype(const TString& rkTemplateFileN { // use static_cast so this code works for both enum and choice pArchetype = static_cast( - IPropertyNew::Create(bIsChoice ? EPropertyTypeNew::Choice : EPropertyTypeNew::Enum, - nullptr, - mGame, - nullptr, - false) + IPropertyNew::Create(bIsChoice ? EPropertyTypeNew::Choice : EPropertyTypeNew::Enum, mGame) ); ASSERT(pArchetype != nullptr); @@ -493,7 +484,7 @@ CEnumProperty* CTemplateLoader::LoadEnumArchetype(const TString& rkTemplateFileN ASSERT(pEnumers); LoadEnumerators(pEnumers, pArchetype, rkTemplateFileName); - pArchetype->PostInitialize(); + pArchetype->Initialize(nullptr, nullptr, 0); mpMaster->mPropertyTemplates.emplace( std::make_pair( @@ -528,11 +519,7 @@ CFlagsProperty* CTemplateLoader::LoadFlagsArchetype(const TString& rkTemplateFil if (!Doc.Error()) { pArchetype = TPropCast( - IPropertyNew::Create(EPropertyTypeNew::Flags, - nullptr, - mGame, - nullptr, - false) + IPropertyNew::Create(EPropertyTypeNew::Flags, mGame) ); ASSERT(pArchetype != nullptr); @@ -547,7 +534,7 @@ CFlagsProperty* CTemplateLoader::LoadFlagsArchetype(const TString& rkTemplateFil ASSERT(pFlags); LoadBitFlags(pFlags, pArchetype, rkTemplateFileName); - pArchetype->PostInitialize(); + pArchetype->Initialize(nullptr, nullptr, 0); mpMaster->mPropertyTemplates.emplace( std::make_pair( @@ -645,7 +632,7 @@ CScriptTemplate* CTemplateLoader::LoadScriptTemplate(XMLDocument *pDoc, const TS pScript->mObjectID = ObjectID; pScript->mSourceFile = rkTemplateName; - IPropertyNew* pBaseStruct = IPropertyNew::Create(EPropertyTypeNew::Struct, nullptr, mGame, pScript); + IPropertyNew* pBaseStruct = IPropertyNew::Create(EPropertyTypeNew::Struct, mGame); pScript->mpProperties = std::make_unique( *TPropCast(pBaseStruct) ); XMLElement *pRoot = pDoc->FirstChildElement("ScriptTemplate"); @@ -1139,7 +1126,7 @@ TString CTemplateLoader::ErrorName(XMLError Error) } // ************ PUBLIC ************ -#define USE_NEW_TEMPLATES 0 +#define USE_NEW_TEMPLATES 1 void CTemplateLoader::LoadGameList() { @@ -1193,7 +1180,7 @@ void CTemplateLoader::LoadGameList() for (auto Iter = MasterList.begin(); Iter != MasterList.end(); Iter++) { CMasterTemplate* pMaster = *Iter; - const TString kMasterPath = kTemplatesDir + pMaster->GetDirectory() + "Game.xml"; + const TString kMasterPath = pMaster->GetGameDirectory(true) + "Game.xml"; CXMLReader Reader(kMasterPath); ASSERT(Reader.IsValid()); @@ -1354,7 +1341,7 @@ void CTemplateLoader::SaveGameList() SGameInfo Info; Info.Game = pMaster->Game(); Info.Name = pMaster->GameName(); - Info.MasterPath = pMaster->GetDirectory() + "Game.xml"; + Info.MasterPath = pMaster->GetGameDirectory() + "Game.xml"; Writer << SerialParameter("Game", Info); } Writer.ParamEnd(); @@ -1373,7 +1360,7 @@ void CTemplateLoader::SaveGameList() for (auto Iter = MasterList.begin(); Iter != MasterList.end(); Iter++) { CMasterTemplate* pMasterTemplate = *Iter; - TString MasterFilePath = kTemplatesDir + pMasterTemplate->GetDirectory() + "Game.xml"; + TString MasterFilePath = pMasterTemplate->GetGameDirectory(true) + "Game.xml"; FileUtil::MakeDirectory( MasterFilePath.GetFileDirectory() ); CXMLWriter Writer(MasterFilePath, "Game", 0, pMasterTemplate->Game()); diff --git a/src/Core/Resource/Script/CMasterTemplate.cpp b/src/Core/Resource/Script/CMasterTemplate.cpp index 08f1075c..7e4cacd3 100644 --- a/src/Core/Resource/Script/CMasterTemplate.cpp +++ b/src/Core/Resource/Script/CMasterTemplate.cpp @@ -17,17 +17,52 @@ void CMasterTemplate::Serialize(IArchive& Arc) void CMasterTemplate::LoadSubTemplates() { - //todo + for (auto Iter = mScriptTemplates.begin(); Iter != mScriptTemplates.end(); Iter++) + Internal_LoadScriptTemplate( Iter->second ); + + for (auto Iter = mPropertyTemplates.begin(); Iter != mPropertyTemplates.end(); Iter++) + Internal_LoadPropertyTemplate( Iter->second ); +} + +void CMasterTemplate::Internal_LoadScriptTemplate(SScriptTemplatePath& Path) +{ + ASSERT(Path.pTemplate == nullptr); // make sure it hasn't been loaded yet + + const TString kGameDir = GetGameDirectory(true); + const TString kTemplateFilePath = kGameDir + Path.Path; + CXMLReader Reader(kTemplateFilePath); + ASSERT(Reader.IsValid()); + + Path.pTemplate = std::make_shared(this, Path.ID.ID, Path.Path); + Path.pTemplate->Serialize(Reader); + Path.pTemplate->PostLoad(); +} + +void CMasterTemplate::Internal_LoadPropertyTemplate(SPropertyTemplatePath& Path) +{ + if (Path.pTemplate != nullptr) // don't load twice + return; + + const TString kGameDir = GetGameDirectory(true); + const TString kTemplateFilePath = kGameDir + Path.Path; + CXMLReader Reader(kTemplateFilePath); + ASSERT(Reader.IsValid()); + + Reader << SerialParameter("PropertyArchetype", Path.pTemplate); + ASSERT(Path.pTemplate != nullptr); + + Path.pTemplate->SetPropertyFlags( EPropertyFlag::IsArchetype ); + Path.pTemplate->Initialize(nullptr, nullptr, 0); } void CMasterTemplate::SaveSubTemplates() { - TString GameDir = "../templates_new/" + GetDirectory(); + const TString kGameDir = GetGameDirectory(true); for (auto Iter = mScriptTemplates.begin(); Iter != mScriptTemplates.end(); Iter++) { SScriptTemplatePath& Path = Iter->second; - TString OutPath = GameDir + Path.Path; + TString OutPath = kGameDir + Path.Path; FileUtil::MakeDirectory( OutPath.GetFileDirectory() ); CXMLWriter Writer(OutPath, "ScriptObject", 0, Game()); @@ -37,11 +72,11 @@ void CMasterTemplate::SaveSubTemplates() for (auto Iter = mPropertyTemplates.begin(); Iter != mPropertyTemplates.end(); Iter++) { SPropertyTemplatePath& Path = Iter->second; - TString OutPath = GameDir + Path.Path; + TString OutPath = kGameDir + Path.Path; FileUtil::MakeDirectory( OutPath.GetFileDirectory() ); - CXMLWriter Writer(OutPath, "PropertyArchetype", 0, Game()); - Path.pTemplate->Serialize(Writer); + CXMLWriter Writer(OutPath, "PropertyTemplate", 0, Game()); + Writer << SerialParameter("PropertyArchetype", Path.pTemplate); } } @@ -115,10 +150,34 @@ SMessage CMasterTemplate::MessageByIndex(u32 Index) return SMessage(Iter->first, Iter->second); } -IPropertyNew* CMasterTemplate::FindPropertyArchetype(const TString& kTypeName) const +IPropertyNew* CMasterTemplate::FindPropertyArchetype(const TString& kTypeName) { auto Iter = mPropertyTemplates.find(kTypeName); - return (Iter != mPropertyTemplates.end()) ? Iter->second.pTemplate.get() : nullptr; + + // Should require Iter to be valid in the future. For now, this is possible for some of the transition template loader code. +// ASSERT(Iter != mPropertyTemplates.end()); // Requested archetype property does not exist; missing or malformed template + if (Iter == mPropertyTemplates.end()) + { + return nullptr; + } + + // If the template isn't loaded yet, then load it. + // This has to be done here to allow recursion while loading other property archetypes, because some properties may + // request archetypes of other properties that haven't been loaded yet during their load. + SPropertyTemplatePath& Path = Iter->second; + if (!Path.pTemplate) + { + Internal_LoadPropertyTemplate(Path); + ASSERT(Path.pTemplate != nullptr); // Load failed; missing or malformed template + } + + return Path.pTemplate.get(); +} + +TString CMasterTemplate::GetGameDirectory(bool Absolute) const +{ + TString Out = mSourceFile.GetFileDirectory(); + return Absolute ? "../templates_new/" + Out : Out; } // ************ STATIC ************ diff --git a/src/Core/Resource/Script/CMasterTemplate.h b/src/Core/Resource/Script/CMasterTemplate.h index 7c00aefd..24e83295 100644 --- a/src/Core/Resource/Script/CMasterTemplate.h +++ b/src/Core/Resource/Script/CMasterTemplate.h @@ -124,6 +124,9 @@ class CMasterTemplate static std::map smPropertyNames; static u32 smGameListVersion; + void Internal_LoadScriptTemplate(SScriptTemplatePath& Path); + void Internal_LoadPropertyTemplate(SPropertyTemplatePath& Path); + public: CMasterTemplate(); void Serialize(IArchive& Arc); @@ -139,7 +142,8 @@ public: SMessage MessageByID(u32 MessageID); SMessage MessageByID(const CFourCC& MessageID); SMessage MessageByIndex(u32 Index); - IPropertyNew* FindPropertyArchetype(const TString& kTypeName) const; + IPropertyNew* FindPropertyArchetype(const TString& kTypeName); + TString GetGameDirectory(bool Absolute = false) const; // Inline Accessors inline EGame Game() const { return mGame; } @@ -148,7 +152,6 @@ public: inline u32 NumStates() const { return mStates.size(); } inline u32 NumMessages() const { return mMessages.size(); } inline bool IsLoadedSuccessfully() { return mFullyLoaded; } - inline TString GetDirectory() const { return mSourceFile.GetFileDirectory(); } // Static static CMasterTemplate* MasterForGame(EGame Game); diff --git a/src/Core/Resource/Script/CScriptTemplate.cpp b/src/Core/Resource/Script/CScriptTemplate.cpp index c875ffb9..590f9ffa 100644 --- a/src/Core/Resource/Script/CScriptTemplate.cpp +++ b/src/Core/Resource/Script/CScriptTemplate.cpp @@ -79,6 +79,8 @@ void CScriptTemplate::Serialize(IArchive& Arc) void CScriptTemplate::PostLoad() { + mpProperties->Initialize(nullptr, this, 0); + if (!mNameIDString.IsEmpty()) mpNameProperty = TPropCast( mpProperties->ChildByIDString(mNameIDString) ); if (!mPositionIDString.IsEmpty()) mpPositionProperty = TPropCast( mpProperties->ChildByIDString(mPositionIDString) ); if (!mRotationIDString.IsEmpty()) mpRotationProperty = TPropCast( mpProperties->ChildByIDString(mRotationIDString) ); diff --git a/src/Core/Resource/Script/CScriptTemplate.h b/src/Core/Resource/Script/CScriptTemplate.h index 69d7d77c..ae041198 100644 --- a/src/Core/Resource/Script/CScriptTemplate.h +++ b/src/Core/Resource/Script/CScriptTemplate.h @@ -130,6 +130,8 @@ private: bool mVisible; public: + // Default constructor. Don't use. This is only here so the serializer doesn't complain + CScriptTemplate() { ASSERT(false); } // Old constructor CScriptTemplate(CMasterTemplate *pMaster); // New constructor diff --git a/src/Core/Resource/Script/IPropertyNew.cpp b/src/Core/Resource/Script/IPropertyNew.cpp index ea475f10..df9bc29e 100644 --- a/src/Core/Resource/Script/IPropertyNew.cpp +++ b/src/Core/Resource/Script/IPropertyNew.cpp @@ -9,10 +9,12 @@ #include "Core/Resource/Script/CScriptTemplate.h" /** IPropertyNew */ -IPropertyNew::IPropertyNew() +IPropertyNew::IPropertyNew(EGame Game) : mpParent( nullptr ) , mpPointerParent( nullptr ) , mpArchetype( nullptr ) + , mGame( Game ) + , mpScriptTemplate( nullptr ) , mOffset( -1 ) , mID( -1 ) , mCookPreference( ECookPreferenceNew::Default ) @@ -20,41 +22,6 @@ IPropertyNew::IPropertyNew() , mMaxVersion( FLT_MAX ) {} -void IPropertyNew::_CalcOffset() -{ - // For standard properties, append to the end of the parent. - bool IsRootArrayArchetype = (IsArrayArchetype() && TPropCast(mpParent) != nullptr); - - if (mpParent && !IsRootArrayArchetype) - { - // When we have a parent, our data is usually located inside the parent's property data. So we want to - // position ourself at the end of the parent's existing children so we don't overlap any other properties. - IPropertyNew* pLastChild = (mpParent->mChildren.empty() ? nullptr : mpParent->mChildren.back()); - - if (pLastChild) - { - mOffset = pLastChild->mOffset + pLastChild->DataSize(); - } - else if (mpParent != mpPointerParent) - { - mOffset = mpParent->mOffset; - } - else - { - mOffset = 0; - } - - mOffset = ALIGN(mOffset, DataAlignment()); - } - // Array archetypes are accessed differently because they have no way of knowing - // which array index is meant to be accessed. So the offset is 0 and the caller - // is responsible for passing in a pointer to the correct array item. - else - { - mOffset = 0; - } -} - void IPropertyNew::_ClearChildren() { for (int ChildIdx = 0; ChildIdx < mChildren.size(); ChildIdx++) @@ -109,7 +76,6 @@ void IPropertyNew::Serialize(IArchive& rArc) IPropertyNew* pArchetype = pMaster->FindPropertyArchetype(ArchetypeName); // The archetype must exist, or else the template file is malformed. - //@TODO: I think this will actually always fail right now, because property archetype loading has not been implemented yet ASSERT(pArchetype != nullptr); InitFromArchetype(pArchetype); @@ -118,7 +84,12 @@ void IPropertyNew::Serialize(IArchive& rArc) // In MP1, the game data does not use property IDs, so we serialize the name directly. // In MP2 and on, property names are looked up based on the property ID via the property name map. - if (rArc.Game() <= ePrime && !IsArchetype()) + // Exceptions: Properties that are not in the name map still need to serialize their names. + // This includes root-level properties, and properties of atomic structs. + // + // We can't currently tell if this property is atomic, as the flag hasn't been serialized and the parent + // hasn't been set, but atomic sub-properties don't use hash IDs, so we can do a pseudo-check against the ID. + if (rArc.Game() <= ePrime || IsRootParent() || mID <= 0xFF) { rArc << SerialParameter("Name", mName, mpArchetype ? SH_Optional : 0, mpArchetype ? mpArchetype->mName : ""); } @@ -137,7 +108,6 @@ void IPropertyNew::InitFromArchetype(IPropertyNew* pOther) //@todo maybe somehow use Serialize for this instead? mpArchetype = pOther; mFlags = pOther->mFlags & EPropertyFlag::ArchetypeCopyFlags; - mID = pOther->mID; mName = pOther->mName; mDescription = pOther->mDescription; mSuffix = pOther->mSuffix; @@ -145,12 +115,10 @@ void IPropertyNew::InitFromArchetype(IPropertyNew* pOther) mMinVersion = pOther->mMinVersion; mMaxVersion = pOther->mMaxVersion; - // Copy children - _ClearChildren(); - - for (u32 ChildIdx = 0; ChildIdx < pOther->mChildren.size(); ChildIdx++) + // Copy ID only if our existing ID is not valid. + if (mID == 0xFFFFFFFF) { - CreateCopy( pOther->mChildren[ChildIdx], this ); + mID = pOther->mID; } } @@ -183,6 +151,62 @@ TString IPropertyNew::GetTemplateFileName() } } +void IPropertyNew::Initialize(IPropertyNew* pInParent, CScriptTemplate* pInTemplate, u32 InOffset) +{ + // Make sure we only get initialized once. + ASSERT( (mFlags & EPropertyFlag::IsInitialized) == 0 ); + mFlags |= EPropertyFlag::IsInitialized; + + mpParent = pInParent; + mOffset = InOffset; + mpScriptTemplate = pInTemplate; + + // Look up property name if needed. + if (Game() >= eEchoesDemo && !IsRootParent() && !IsIntrinsic() && !mpParent->IsAtomic()) + { + mName = CMasterTemplate::PropertyName(mID); + } + + // Set any fields dependent on the parent... + if (mpParent) + { + mFlags |= mpParent->mFlags & EPropertyFlag::InheritableFlags; + + if (mpParent->IsPointerType()) + { + mpPointerParent = mpParent; + } + else + { + mpPointerParent = mpParent->mpPointerParent; + } + } + + // Allow subclasses to handle any initialization tasks + PostInitialize(); + + // Now, route initialization to any child properties... + u32 ChildOffset = mOffset; + + for (int ChildIdx = 0; ChildIdx < mChildren.size(); ChildIdx++) + { + IPropertyNew* pChild = mChildren[ChildIdx]; + + // update offset and round up to the child's alignment + if (ChildIdx > 0) + { + ChildOffset += mChildren[ChildIdx-1]->DataSize(); + } + ChildOffset = ALIGN(ChildOffset, pChild->DataAlignment()); + + // Don't call Initialize on intrinsic children as they have already been initialized. + if (!pChild->IsIntrinsic()) + { + pChild->Initialize(this, pInTemplate, ChildOffset); + } + } +} + void* IPropertyNew::RawValuePtr(void* pData) const { // For array archetypes, the caller needs to provide the pointer to the correct array item @@ -266,8 +290,27 @@ void IPropertyNew::SetSuffix(const TString& rkNewSuffix) mSuffix = rkNewSuffix; } +void IPropertyNew::SetPropertyFlags(FPropertyFlags FlagsToSet) +{ + mFlags |= FlagsToSet; +} + bool IPropertyNew::HasAccurateName() { + // Exceptions for the three hardcoded 4CC property IDs + if (mID == FOURCC('XFRM') || mID == FOURCC('INAM') || mID == FOURCC('ACTV')) + return true; + + // Children of atomic properties defer to parents. Intrinsic properties also defer to parents. + if ( (mpParent && mpParent->IsAtomic()) || IsIntrinsic() ) + { + if (mpParent) + return mpParent->HasAccurateName(); + else + return true; + } + + // For everything else, hash the property name and check if it is a match for the property ID if (!mFlags.HasFlag(EPropertyFlag::HasCachedNameCheck)) { CCRC32 Hash; @@ -293,114 +336,79 @@ EGame IPropertyNew::Game() const } IPropertyNew* IPropertyNew::Create(EPropertyTypeNew Type, - IPropertyNew* pParent, - EGame Game, - CScriptTemplate* pScript, - bool CallPostInit /*= true*/) + EGame Game) { IPropertyNew* pOut = nullptr; switch (Type) { - case EPropertyTypeNew::Bool: pOut = new CBoolProperty; break; - case EPropertyTypeNew::Byte: pOut = new CByteProperty; break; - case EPropertyTypeNew::Short: pOut = new CShortProperty; break; - case EPropertyTypeNew::Int: pOut = new CIntProperty; break; - case EPropertyTypeNew::Float: pOut = new CFloatProperty; break; - case EPropertyTypeNew::Choice: pOut = new CChoiceProperty; break; - case EPropertyTypeNew::Enum: pOut = new CEnumProperty; break; - case EPropertyTypeNew::Flags: pOut = new CFlagsProperty; break; - case EPropertyTypeNew::String: pOut = new CStringProperty; break; - case EPropertyTypeNew::Vector: pOut = new CVectorProperty; break; - case EPropertyTypeNew::Color: pOut = new CColorProperty; break; - case EPropertyTypeNew::Asset: pOut = new CAssetProperty; break; - case EPropertyTypeNew::Sound: pOut = new CSoundProperty; break; - case EPropertyTypeNew::Animation: pOut = new CAnimationProperty; break; - case EPropertyTypeNew::AnimationSet: pOut = new CAnimationSetProperty; break; - case EPropertyTypeNew::Sequence: pOut = new CSequenceProperty; break; - case EPropertyTypeNew::Spline: pOut = new CSplineProperty; break; - case EPropertyTypeNew::Guid: pOut = new CGuidProperty; break; - case EPropertyTypeNew::Pointer: pOut = new CPointerProperty; break; - case EPropertyTypeNew::Struct: pOut = new CStructPropertyNew; break; - case EPropertyTypeNew::Array: pOut = new CArrayProperty; break; - } - - if (!pOut) - { - // this shouldn't be possible! unhandled type! someone fucked up! - ASSERT(false); - return nullptr; - } - - // Set parent and offset - pOut->mpParent = pParent; - - if (pParent) - { - pOut->mFlags = pParent->mFlags & EPropertyFlag::InheritableFlags; - - if (pParent->IsPointerType()) - { - pOut->mpPointerParent = pParent; - } - else - { - pOut->mpPointerParent = pParent->mpPointerParent; - } - } - - // Set other metadata - pOut->mGame = Game; - pOut->mpScriptTemplate = pScript; - pOut->_CalcOffset(); - - // Add to the parent's array. This needs to be done -after- we calculate offset, as adding a child to - // the parent property will change the offset that gets calculated. - if (pParent) - { - pParent->mChildren.push_back(pOut); - } - - if (CallPostInit) - { - pOut->PostInitialize(); + case EPropertyTypeNew::Bool: pOut = new CBoolProperty(Game); break; + case EPropertyTypeNew::Byte: pOut = new CByteProperty(Game); break; + case EPropertyTypeNew::Short: pOut = new CShortProperty(Game); break; + case EPropertyTypeNew::Int: pOut = new CIntProperty(Game); break; + case EPropertyTypeNew::Float: pOut = new CFloatProperty(Game); break; + case EPropertyTypeNew::Choice: pOut = new CChoiceProperty(Game); break; + case EPropertyTypeNew::Enum: pOut = new CEnumProperty(Game); break; + case EPropertyTypeNew::Flags: pOut = new CFlagsProperty(Game); break; + case EPropertyTypeNew::String: pOut = new CStringProperty(Game); break; + case EPropertyTypeNew::Vector: pOut = new CVectorProperty(Game); break; + case EPropertyTypeNew::Color: pOut = new CColorProperty(Game); break; + case EPropertyTypeNew::Asset: pOut = new CAssetProperty(Game); break; + case EPropertyTypeNew::Sound: pOut = new CSoundProperty(Game); break; + case EPropertyTypeNew::Animation: pOut = new CAnimationProperty(Game); break; + case EPropertyTypeNew::AnimationSet: pOut = new CAnimationSetProperty(Game); break; + case EPropertyTypeNew::Sequence: pOut = new CSequenceProperty(Game); break; + case EPropertyTypeNew::Spline: pOut = new CSplineProperty(Game); break; + case EPropertyTypeNew::Guid: pOut = new CGuidProperty(Game); break; + case EPropertyTypeNew::Pointer: pOut = new CPointerProperty(Game); break; + case EPropertyTypeNew::Struct: pOut = new CStructPropertyNew(Game); break; + case EPropertyTypeNew::Array: pOut = new CArrayProperty(Game); break; } + // If this assertion fails, then there is an unhandled type! + ASSERT(pOut != nullptr); return pOut; } -IPropertyNew* IPropertyNew::CreateCopy(IPropertyNew* pArchetype, - IPropertyNew* pParent) +IPropertyNew* IPropertyNew::CreateCopy(IPropertyNew* pArchetype) { - // Note this is mainly going to be used to create copies from struct/enum/flag archetype properties. - // Properties that have archetypes will never be the root property of a script template, and there - // is no case where we will be creating archetypes outside this context. As such, pParent should - // always be valid. - ASSERT(pParent != nullptr); - - IPropertyNew* pOut = Create(pArchetype->Type(), pParent, pParent->mGame, pParent->mpScriptTemplate, false); + IPropertyNew* pOut = Create(pArchetype->Type(), pArchetype->mGame); pOut->InitFromArchetype(pArchetype); pArchetype->mSubInstances.push_back(pOut); return pOut; } +IPropertyNew* IPropertyNew::CreateIntrinsic(EPropertyTypeNew Type, + EGame Game, + u32 Offset, + const TString& rkName) +{ + IPropertyNew* pOut = Create(Type, Game); + pOut->mFlags |= EPropertyFlag::IsIntrinsic; + pOut->SetName(rkName); + pOut->Initialize(nullptr, nullptr, Offset); + return pOut; +} + IPropertyNew* IPropertyNew::CreateIntrinsic(EPropertyTypeNew Type, IPropertyNew* pParent, u32 Offset, const TString& rkName) { - IPropertyNew* pOut = Create(Type, pParent, pParent ? pParent->mGame : eUnknownGame, nullptr, false); - pOut->mOffset = Offset; + // pParent should always be valid. + // If you are creating a root property, call the other overload takes an EGame instead of a parent. + ASSERT(pParent != nullptr); + + IPropertyNew* pOut = Create(Type, pParent->mGame); + pOut->mFlags |= EPropertyFlag::IsIntrinsic; pOut->SetName(rkName); - pOut->PostInitialize(); + pOut->Initialize(pParent, nullptr, Offset); + pParent->mChildren.push_back(pOut); return pOut; } IPropertyNew* IPropertyNew::ArchiveConstructor(EPropertyTypeNew Type, const IArchive& Arc) { - IPropertyNew* pParent = Arc.FindParentObject(); - CScriptTemplate* pTemplate = (pParent ? pParent->ScriptTemplate() : Arc.FindParentObject()); - EGame Game = Arc.Game(); - return Create(Type, pParent, Game, pTemplate); + return Create(Type, Arc.Game()); } diff --git a/src/Core/Resource/Script/IPropertyNew.h b/src/Core/Resource/Script/IPropertyNew.h index 1640082c..7a949afd 100644 --- a/src/Core/Resource/Script/IPropertyNew.h +++ b/src/Core/Resource/Script/IPropertyNew.h @@ -19,12 +19,16 @@ typedef TString TIDString; /** Property flags */ enum class EPropertyFlag : u32 { + /** Property has been fully initialized and has had PostLoad called */ + IsInitialized = 0x1, /** Property is an archetype (a template for other properties to copy from) */ - IsArchetype = 0x1, + IsArchetype = 0x2, /** Property is an array archetype (a template for elements of an array property) */ - IsArrayArchetype = 0x2, + IsArrayArchetype = 0x4, /** This property and all its children are a single unit and do not have individual property IDs, sizes, etc. */ - IsAtomic = 0x4, + IsAtomic = 0x8, + /** This is a property of a C++ class, not a script object */ + IsIntrinsic = 0x10, /** We have cached whether the property name is correct */ HasCachedNameCheck = 0x40000000, /** The name of the property is a match for the property ID hash */ @@ -122,8 +126,8 @@ protected: /** Archetype property; source property that we copied metadata from */ IPropertyNew* mpArchetype; - /** Sub-instances of archetype properties. For non-archetypes, will be empty. @todo better - * method of storing this? maybe a linked list? */ + /** Sub-instances of archetype properties. For non-archetypes, will be empty. + * @todo this really oughta be a linked list */ std::vector mSubInstances; /** Child properties; these appear underneath this property on the UI */ @@ -155,13 +159,9 @@ protected: float mMaxVersion; /** Private constructor - use static methods to instantiate */ - IPropertyNew(); - void _CalcOffset(); + IPropertyNew(EGame Game); void _ClearChildren(); - /** Called after property is created and fully initialized */ - virtual void PostInitialize() {} - public: virtual ~IPropertyNew(); @@ -175,11 +175,11 @@ public: virtual void RevertToDefault(void* pData) const = 0; virtual void SerializeValue(void* pData, IArchive& Arc) const = 0; + virtual void PostInitialize() {} virtual void PropertyValueChanged(void* pPropertyData) {} virtual bool IsNumericalType() const { return false; } virtual bool IsPointerType() const { return false; } virtual TString ValueAsString(void* pData) const { return ""; } - virtual const char* HashableTypeName() const; virtual void* GetChildDataPointer(void* pPropertyData) const; virtual void Serialize(IArchive& rArc); @@ -188,6 +188,7 @@ public: virtual TString GetTemplateFileName(); /** Utility methods */ + void Initialize(IPropertyNew* pInParent, CScriptTemplate* pInTemplate, u32 InOffset); void* RawValuePtr(void* pData) const; IPropertyNew* ChildByID(u32 ID) const; IPropertyNew* ChildByIDString(const TIDString& rkIdString); @@ -195,6 +196,7 @@ public: void SetName(const TString& rkNewName); void SetDescription(const TString& rkNewDescription); void SetSuffix(const TString& rkNewSuffix); + void SetPropertyFlags(FPropertyFlags FlagsToSet); bool HasAccurateName(); /** Accessors */ @@ -216,17 +218,19 @@ public: inline bool IsArchetype() const { return mFlags.HasFlag(EPropertyFlag::IsArchetype); } inline bool IsArrayArchetype() const { return mFlags.HasFlag(EPropertyFlag::IsArrayArchetype); } inline bool IsAtomic() const { return mFlags.HasFlag(EPropertyFlag::IsAtomic); } + inline bool IsIntrinsic() const { return mFlags.HasFlag(EPropertyFlag::IsIntrinsic); } inline bool IsRootParent() const { return mpParent == nullptr; } /** Create */ static IPropertyNew* Create(EPropertyTypeNew Type, - IPropertyNew* pParent, - EGame Game, - CScriptTemplate* pScript, - bool CallPostInit = true); + EGame Game); - static IPropertyNew* CreateCopy(IPropertyNew* pArchetype, - IPropertyNew* pParent); + static IPropertyNew* CreateCopy(IPropertyNew* pArchetype); + + static IPropertyNew* CreateIntrinsic(EPropertyTypeNew Type, + EGame Game, + u32 Offset, + const TString& rkName); static IPropertyNew* CreateIntrinsic(EPropertyTypeNew Type, IPropertyNew* pParent, @@ -326,8 +330,8 @@ public: protected: PropType mDefaultValue; - TTypedPropertyNew() - : IPropertyNew() + TTypedPropertyNew(EGame Game) + : IPropertyNew(Game) { memset(&mDefaultValue, 0, sizeof(PropType)); } @@ -369,6 +373,11 @@ public: return mDefaultValue; } + inline void SetDefaultValue(const PropType& kInDefaultValue) + { + mDefaultValue = kInDefaultValue; + } + inline static EPropertyTypeNew StaticType() { return PropEnum; } }; @@ -376,8 +385,8 @@ template class TSerializeableTypedProperty : public TTypedPropertyNew { protected: - TSerializeableTypedProperty() - : TTypedPropertyNew() + TSerializeableTypedProperty(EGame Game) + : TTypedPropertyNew(Game) {} public: @@ -443,8 +452,8 @@ protected: PropType mMinValue; PropType mMaxValue; - TNumericalPropertyNew() - : TSerializeableTypedProperty() + TNumericalPropertyNew(EGame Game) + : TSerializeableTypedProperty(Game) , mMinValue( -1 ) , mMaxValue( -1 ) {} diff --git a/src/Core/Resource/Script/Property/CAnimationProperty.h b/src/Core/Resource/Script/Property/CAnimationProperty.h index 410fc6a2..c7b73bbd 100644 --- a/src/Core/Resource/Script/Property/CAnimationProperty.h +++ b/src/Core/Resource/Script/Property/CAnimationProperty.h @@ -8,8 +8,8 @@ class CAnimationProperty : public TSerializeableTypedProperty< u32, EPropertyTyp friend class IPropertyNew; protected: - CAnimationProperty() - : TSerializeableTypedProperty() + CAnimationProperty(EGame Game) + : TSerializeableTypedProperty(Game) {} public: diff --git a/src/Core/Resource/Script/Property/CAnimationSetProperty.h b/src/Core/Resource/Script/Property/CAnimationSetProperty.h index 1cc025fe..039c8647 100644 --- a/src/Core/Resource/Script/Property/CAnimationSetProperty.h +++ b/src/Core/Resource/Script/Property/CAnimationSetProperty.h @@ -8,16 +8,13 @@ class CAnimationSetProperty : public TSerializeableTypedProperty< CAnimationPara friend class IPropertyNew; protected: - CAnimationSetProperty() - : TSerializeableTypedProperty() - {} - -public: - virtual void PostInitialize() + CAnimationSetProperty(EGame Game) + : TSerializeableTypedProperty(Game) { - mDefaultValue.SetGame(Game()); + mDefaultValue.SetGame(Game); } +public: virtual void SerializeValue(void* pData, IArchive& Arc) const { Value(pData).Serialize(Arc); diff --git a/src/Core/Resource/Script/Property/CArrayProperty.h b/src/Core/Resource/Script/Property/CArrayProperty.h index e4f3a0cb..85dfa28f 100644 --- a/src/Core/Resource/Script/Property/CArrayProperty.h +++ b/src/Core/Resource/Script/Property/CArrayProperty.h @@ -45,8 +45,8 @@ class CArrayProperty : public TTypedPropertyNew } protected: - CArrayProperty() - : TTypedPropertyNew() + CArrayProperty(EGame Game) + : TTypedPropertyNew(Game) , mpItemArchetype(nullptr) {} @@ -109,6 +109,11 @@ public: { TTypedPropertyNew::Serialize(rArc); rArc << SerialParameter("ItemArchetype", mpItemArchetype); + + if (rArc.IsReader()) + { + mpItemArchetype->SetPropertyFlags( EPropertyFlag::IsArrayArchetype ); + } } virtual void SerializeValue(void* pData, IArchive& Arc) const @@ -134,7 +139,13 @@ public: { TTypedPropertyNew::InitFromArchetype(pOther); CArrayProperty* pOtherArray = static_cast(pOther); - mpItemArchetype = IPropertyNew::CreateCopy(pOtherArray->mpItemArchetype, this); + mpItemArchetype = IPropertyNew::CreateCopy(pOtherArray->mpItemArchetype); + } + + virtual void PostInitialize() + { + TTypedPropertyNew::PostInitialize(); + mpItemArchetype->Initialize(this, mpScriptTemplate, 0); } u32 ArrayCount(void* pPropertyData) const diff --git a/src/Core/Resource/Script/Property/CAssetProperty.h b/src/Core/Resource/Script/Property/CAssetProperty.h index 4c90b199..07bab214 100644 --- a/src/Core/Resource/Script/Property/CAssetProperty.h +++ b/src/Core/Resource/Script/Property/CAssetProperty.h @@ -7,18 +7,18 @@ class CAssetProperty : public TSerializeableTypedProperty { friend class CTemplateLoader; + friend class IPropertyNew; + CResTypeFilter mTypeFilter; -public: - virtual void PostInitialize() +protected: + CAssetProperty::CAssetProperty(EGame Game) + : TSerializeableTypedProperty(Game) { - // Init default value to an invalid ID depending on the game - if (!mDefaultValue.IsValid()) - { - mDefaultValue = CAssetID::InvalidID( mGame ); - } + mDefaultValue = CAssetID::InvalidID( mGame ); } +public: virtual void Serialize(IArchive& rArc) { TSerializeableTypedProperty::Serialize(rArc); diff --git a/src/Core/Resource/Script/Property/CBoolProperty.h b/src/Core/Resource/Script/Property/CBoolProperty.h index 5ff51e55..f02247c5 100644 --- a/src/Core/Resource/Script/Property/CBoolProperty.h +++ b/src/Core/Resource/Script/Property/CBoolProperty.h @@ -8,8 +8,8 @@ class CBoolProperty : public TSerializeableTypedProperty< bool, EPropertyTypeNew friend class IPropertyNew; protected: - CBoolProperty() - : TSerializeableTypedProperty() + CBoolProperty(EGame Game) + : TSerializeableTypedProperty(Game) {} public: diff --git a/src/Core/Resource/Script/Property/CByteProperty.h b/src/Core/Resource/Script/Property/CByteProperty.h index fcf31557..52c2bac9 100644 --- a/src/Core/Resource/Script/Property/CByteProperty.h +++ b/src/Core/Resource/Script/Property/CByteProperty.h @@ -8,8 +8,8 @@ class CByteProperty : public TNumericalPropertyNew< s8, EPropertyTypeNew::Byte > friend class IPropertyNew; protected: - CByteProperty() - : TNumericalPropertyNew() + CByteProperty(EGame Game) + : TNumericalPropertyNew(Game) {} public: diff --git a/src/Core/Resource/Script/Property/CColorProperty.h b/src/Core/Resource/Script/Property/CColorProperty.h index ea6ff90d..746ae7ce 100644 --- a/src/Core/Resource/Script/Property/CColorProperty.h +++ b/src/Core/Resource/Script/Property/CColorProperty.h @@ -2,27 +2,25 @@ #define CCOLORPROPERTY_H #include "../IPropertyNew.h" +#include "CFloatProperty.h" class CColorProperty : public TSerializeableTypedProperty< CColor, EPropertyTypeNew::Color > { friend class IPropertyNew; protected: - CColorProperty() - : TSerializeableTypedProperty() + CColorProperty(EGame Game) + : TSerializeableTypedProperty(Game) {} public: virtual void PostInitialize() { - IPropertyNew* pR = Create(EPropertyTypeNew::Float, this, mGame, mpScriptTemplate); - IPropertyNew* pG = Create(EPropertyTypeNew::Float, this, mGame, mpScriptTemplate); - IPropertyNew* pB = Create(EPropertyTypeNew::Float, this, mGame, mpScriptTemplate); - IPropertyNew* pA = Create(EPropertyTypeNew::Float, this, mGame, mpScriptTemplate); - pR->SetName("R"); - pG->SetName("G"); - pB->SetName("B"); - pA->SetName("A"); + CreateIntrinsic(EPropertyTypeNew::Float, this, mOffset + 0, "R"); + CreateIntrinsic(EPropertyTypeNew::Float, this, mOffset + 4, "G"); + CreateIntrinsic(EPropertyTypeNew::Float, this, mOffset + 8, "B"); + CreateIntrinsic(EPropertyTypeNew::Float, this, mOffset + 12, "A"); + TPropCast( mChildren.back() )->SetDefaultValue(1.0f); } virtual void SerializeValue(void* pData, IArchive& Arc) const diff --git a/src/Core/Resource/Script/Property/CEnumProperty.h b/src/Core/Resource/Script/Property/CEnumProperty.h index be7462b3..3c8d4d4d 100644 --- a/src/Core/Resource/Script/Property/CEnumProperty.h +++ b/src/Core/Resource/Script/Property/CEnumProperty.h @@ -14,6 +14,8 @@ template class TEnumPropertyBase : public TSerializeableTypedProperty { friend class CTemplateLoader; + friend class IPropertyNew; + struct SEnumValue { TString Name; @@ -43,6 +45,12 @@ class TEnumPropertyBase : public TSerializeableTypedProperty /** XML template file that this enum originated from; for archetypes */ TString mSourceFile; +protected: + /** Constructor */ + TEnumPropertyBase(EGame Game) + : TSerializeableTypedProperty(Game) + {} + public: virtual const char* GetHashableTypeName() const { diff --git a/src/Core/Resource/Script/Property/CFlagsProperty.h b/src/Core/Resource/Script/Property/CFlagsProperty.h index 1c5ec6ee..4a369010 100644 --- a/src/Core/Resource/Script/Property/CFlagsProperty.h +++ b/src/Core/Resource/Script/Property/CFlagsProperty.h @@ -38,8 +38,8 @@ class CFlagsProperty : public TSerializeableTypedProperty, EPropertyType friend class IPropertyNew; protected: - CGuidProperty() - : TTypedPropertyNew() + CGuidProperty(EGame Game) + : TTypedPropertyNew(Game) {} public: diff --git a/src/Core/Resource/Script/Property/CIntProperty.h b/src/Core/Resource/Script/Property/CIntProperty.h index 796fba1d..ecb4856a 100644 --- a/src/Core/Resource/Script/Property/CIntProperty.h +++ b/src/Core/Resource/Script/Property/CIntProperty.h @@ -8,8 +8,8 @@ class CIntProperty : public TNumericalPropertyNew< s32, EPropertyTypeNew::Int > friend class IPropertyNew; protected: - CIntProperty() - : TNumericalPropertyNew() + CIntProperty(EGame Game) + : TNumericalPropertyNew(Game) {} public: diff --git a/src/Core/Resource/Script/Property/CPointerProperty.h b/src/Core/Resource/Script/Property/CPointerProperty.h index 9718ea55..913b2b61 100644 --- a/src/Core/Resource/Script/Property/CPointerProperty.h +++ b/src/Core/Resource/Script/Property/CPointerProperty.h @@ -6,6 +6,12 @@ class CPointerProperty : public TTypedPropertyNew { friend class CTemplateLoader; + friend class IPropertyNew; + + CPointerProperty(EGame Game) + : TTypedPropertyNew(Game) + {} + public: virtual bool IsPointerType() const { diff --git a/src/Core/Resource/Script/Property/CSequenceProperty.h b/src/Core/Resource/Script/Property/CSequenceProperty.h index f4edcac3..ca9f9589 100644 --- a/src/Core/Resource/Script/Property/CSequenceProperty.h +++ b/src/Core/Resource/Script/Property/CSequenceProperty.h @@ -8,8 +8,8 @@ class CSequenceProperty : public TTypedPropertyNew< s32, EPropertyTypeNew::Seque friend class IPropertyNew; protected: - CSequenceProperty() - : TTypedPropertyNew() + CSequenceProperty(EGame Game) + : TTypedPropertyNew(Game) {} virtual void SerializeValue(void* pData, IArchive& rArc) const diff --git a/src/Core/Resource/Script/Property/CShortProperty.h b/src/Core/Resource/Script/Property/CShortProperty.h index d40d088a..17ed9471 100644 --- a/src/Core/Resource/Script/Property/CShortProperty.h +++ b/src/Core/Resource/Script/Property/CShortProperty.h @@ -8,8 +8,8 @@ class CShortProperty : public TNumericalPropertyNew< s16, EPropertyTypeNew::Shor friend class IPropertyNew; protected: - CShortProperty() - : TNumericalPropertyNew() + CShortProperty(EGame Game) + : TNumericalPropertyNew(Game) {} public: diff --git a/src/Core/Resource/Script/Property/CSoundProperty.h b/src/Core/Resource/Script/Property/CSoundProperty.h index af7c2c20..3622ff5a 100644 --- a/src/Core/Resource/Script/Property/CSoundProperty.h +++ b/src/Core/Resource/Script/Property/CSoundProperty.h @@ -8,8 +8,8 @@ class CSoundProperty : public TSerializeableTypedProperty< s32, EPropertyTypeNew friend class IPropertyNew; protected: - CSoundProperty() - : TSerializeableTypedProperty() + CSoundProperty(EGame Game) + : TSerializeableTypedProperty(Game) {} public: diff --git a/src/Core/Resource/Script/Property/CSplineProperty.h b/src/Core/Resource/Script/Property/CSplineProperty.h index 4a479981..06a83d21 100644 --- a/src/Core/Resource/Script/Property/CSplineProperty.h +++ b/src/Core/Resource/Script/Property/CSplineProperty.h @@ -8,8 +8,8 @@ class CSplineProperty : public TTypedPropertyNew< std::vector, EPropertyTy friend class IPropertyNew; protected: - CSplineProperty() - : TTypedPropertyNew() + CSplineProperty(EGame Game) + : TTypedPropertyNew(Game) {} public: diff --git a/src/Core/Resource/Script/Property/CStringProperty.h b/src/Core/Resource/Script/Property/CStringProperty.h index 8a277731..7e318c95 100644 --- a/src/Core/Resource/Script/Property/CStringProperty.h +++ b/src/Core/Resource/Script/Property/CStringProperty.h @@ -8,8 +8,8 @@ class CStringProperty : public TSerializeableTypedProperty< TString, EPropertyTy friend class IPropertyNew; protected: - CStringProperty() - : TSerializeableTypedProperty() + CStringProperty(EGame Game) + : TSerializeableTypedProperty(Game) {} public: diff --git a/src/Core/Resource/Script/Property/CStructProperty.cpp b/src/Core/Resource/Script/Property/CStructProperty.cpp index db67ab12..da51bb49 100644 --- a/src/Core/Resource/Script/Property/CStructProperty.cpp +++ b/src/Core/Resource/Script/Property/CStructProperty.cpp @@ -21,12 +21,8 @@ u32 CStructPropertyNew::DataSize() const u32 CStructPropertyNew::DataAlignment() const { - // TODO. Should be aligned with the first child, but this function is called before children are loaded. - // So for now just use 8 to ensure correct alignment for all child types, but this is wasteful... - // It's also problematic for casting property data to a struct - return 8; - - //return (mChildren.empty() ? 1 : mChildren[0]->DataAlignment()); + // Structs are aligned to the first child property. + return (mChildren.empty() ? 1 : mChildren[0]->DataAlignment()); } void CStructPropertyNew::Construct(void* pData) const @@ -67,16 +63,25 @@ void CStructPropertyNew::RevertToDefault(void* pData) const const char* CStructPropertyNew::HashableTypeName() const { - if (IsArchetype() || !mpArchetype) - return *mName; - else - return mpArchetype->HashableTypeName(); + return mpArchetype ? mpArchetype->HashableTypeName() : *mName; } void CStructPropertyNew::Serialize(IArchive& rArc) { IPropertyNew::Serialize(rArc); + // Serialize atomic flag + bool Atomic = IsAtomic(); + rArc << SerialParameter("Atomic", Atomic, SH_Optional, false); + + if (rArc.IsReader()) + { + if (Atomic) + mFlags.SetFlag(EPropertyFlag::IsAtomic); + else + mFlags.ClearFlag(EPropertyFlag::IsAtomic); + } + // Serialize archetype if (mpArchetype) { @@ -96,13 +101,13 @@ void CStructPropertyNew::Serialize(IArchive& rArc) if (rArc.ParamBegin("Element", SH_IgnoreName)) { // Serialize type and ID, then look up the matching property and serialize it. - // We don't really need the type, but it's a good sanity check, and it's also helpful + // We don't really need the type, but it's a good sanity check, and it's also good practice // to guarantee that parameters are read in order, as some serializers are order-dependent. EPropertyTypeNew ChildType; u32 ChildID; rArc << SerialParameter("Type", ChildType, SH_Attribute) - << SerialParameter("ID", ChildID, SH_Attribute); + << SerialParameter("ID", ChildID, SH_Attribute | SH_HexDisplay ); IPropertyNew* pChild = ChildByID(ChildID); ASSERT(pChild != nullptr && pChild->Type() == ChildType); @@ -154,6 +159,21 @@ void CStructPropertyNew::SerializeValue(void* pData, IArchive& Arc) const } } +void CStructPropertyNew::InitFromArchetype(IPropertyNew* pOther) +{ + IPropertyNew::InitFromArchetype(pOther); + + // Copy children + _ClearChildren(); + mChildren.reserve( pOther->NumChildren() ); + + for (u32 ChildIdx = 0; ChildIdx < pOther->NumChildren(); ChildIdx++) + { + IPropertyNew* pChild = CreateCopy( pOther->ChildByIndex(ChildIdx) ); + mChildren.push_back( pChild ); + } +} + bool CStructPropertyNew::ShouldSerialize() const { if (IPropertyNew::ShouldSerialize()) diff --git a/src/Core/Resource/Script/Property/CStructProperty.h b/src/Core/Resource/Script/Property/CStructProperty.h index e4e4b9eb..44374eb8 100644 --- a/src/Core/Resource/Script/Property/CStructProperty.h +++ b/src/Core/Resource/Script/Property/CStructProperty.h @@ -6,6 +6,7 @@ class CStructPropertyNew : public IPropertyNew { friend class CTemplateLoader; + friend class IPropertyNew; public: // Must be a valid type for TPropertyRef @@ -15,6 +16,10 @@ protected: /** For archetypes, the filename of the template XML file. */ TString mTemplateFileName; + CStructPropertyNew(EGame Game) + : IPropertyNew(Game) + {} + public: virtual EPropertyTypeNew Type() const; virtual u32 DataSize() const; @@ -26,6 +31,7 @@ public: virtual const char* HashableTypeName() const; virtual void Serialize(IArchive& rArc); virtual void SerializeValue(void* pData, IArchive& Arc) const; + virtual void InitFromArchetype(IPropertyNew* pOther); virtual bool ShouldSerialize() const; virtual TString GetTemplateFileName(); diff --git a/src/Core/Resource/Script/Property/CVectorProperty.h b/src/Core/Resource/Script/Property/CVectorProperty.h index 6a3fc968..0d9a368a 100644 --- a/src/Core/Resource/Script/Property/CVectorProperty.h +++ b/src/Core/Resource/Script/Property/CVectorProperty.h @@ -8,19 +8,16 @@ class CVectorProperty : public TSerializeableTypedProperty< CVector3f, EProperty friend class IPropertyNew; protected: - CVectorProperty() - : TSerializeableTypedProperty() + CVectorProperty(EGame Game) + : TSerializeableTypedProperty(Game) {} public: - virtual void PostInitialize() + virtual void PostInitialize() override { - IPropertyNew* pX = Create(EPropertyTypeNew::Float, this, mGame, mpScriptTemplate); - IPropertyNew* pY = Create(EPropertyTypeNew::Float, this, mGame, mpScriptTemplate); - IPropertyNew* pZ = Create(EPropertyTypeNew::Float, this, mGame, mpScriptTemplate); - pX->SetName("X"); - pY->SetName("Y"); - pZ->SetName("Z"); + CreateIntrinsic(EPropertyTypeNew::Float, this, mOffset + 0, "X"); + CreateIntrinsic(EPropertyTypeNew::Float, this, mOffset + 4, "Y"); + CreateIntrinsic(EPropertyTypeNew::Float, this, mOffset + 8, "Z"); } virtual void SerializeValue(void* pData, IArchive& Arc) const diff --git a/src/Editor/PropertyEdit/CPropertyView.cpp b/src/Editor/PropertyEdit/CPropertyView.cpp index 3891297e..98143d46 100644 --- a/src/Editor/PropertyEdit/CPropertyView.cpp +++ b/src/Editor/PropertyEdit/CPropertyView.cpp @@ -235,7 +235,11 @@ void CPropertyView::CreateContextMenu(const QPoint& rkPos) mpMenuProperty = pProp; QMenu Menu; - Menu.addAction(mpEditTemplateAction); + + if (!pProp->IsIntrinsic()) + { + Menu.addAction(mpEditTemplateAction); + } if (mpEditor->CurrentGame() >= eEchoesDemo) {