More fixes. Property/serialization changes are finally basically finished now. Serialized property templates now load & display correctly in the editor

This commit is contained in:
Aruki 2018-09-22 03:26:15 -06:00
parent 33e915a638
commit 2118bbd0cd
32 changed files with 405 additions and 272 deletions

View File

@ -97,8 +97,13 @@ template<typename T> struct TIsContainer< std::set<T> > : std::true_type {};
template<typename T, typename V> struct TIsContainer< std::map<T,V> > : std::true_type {}; template<typename T, typename V> struct TIsContainer< std::map<T,V> > : std::true_type {};
template<typename T, typename V> struct TIsContainer< std::unordered_map<T,V> > : std::true_type {}; template<typename T, typename V> struct TIsContainer< std::unordered_map<T,V> > : std::true_type {};
/** Class that determines if the type is a smart pointer */
template<typename> struct TIsSmartPointer : std::false_type {};
template<typename T> struct TIsSmartPointer< std::shared_ptr<T> > : std::true_type {};
template<typename T> struct TIsSmartPointer< std::unique_ptr<T> > : std::true_type {};
/** Helper macro that tells us whether the parameter supports default property values */ /** Helper macro that tells us whether the parameter supports default property values */
#define SUPPORTS_DEFAULT_VALUES (!std::is_pointer_v<ValType> && std::is_copy_assignable_v<ValType> && THasEqualTo<ValType>::value && !TIsContainer<ValType>::value) #define SUPPORTS_DEFAULT_VALUES (!std::is_pointer_v<ValType> && std::is_copy_assignable_v<ValType> && THasEqualTo<ValType>::value && !TIsContainer<ValType>::value && !TIsSmartPointer<ValType>::value)
/** TSerialParameter - name/value pair for generic serial parameters */ /** TSerialParameter - name/value pair for generic serial parameters */
template<typename ValType> template<typename ValType>
@ -241,14 +246,26 @@ struct SerialType
None); 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<ValType>().Type() )
/** For abstract types, determine what kind of ArchiveConstructor the type has */ /** For abstract types, determine what kind of ArchiveConstructor the type has */
template<typename ValType, class ArchiveType> template<typename ValType, class ArchiveType>
struct ArchiveConstructorType 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<typename T>
static constexpr auto HasTypeMethod(int) -> decltype( std::declval<T>().Type() )
{
return std::declval<T>().Type();
}
template<typename T>
static constexpr int HasTypeMethod(...)
{
return 0;
}
using ObjType = decltype(HasTypeMethod<ValType>(0));
enum { Basic, Advanced, None }; enum { Basic, Advanced, None };
@ -340,7 +357,7 @@ public:
mParmStack.reserve(16); mParmStack.reserve(16);
} }
virtual ~IArchive() {} virtual ~IArchive() { ASSERT(mParmStack.empty()); }
// Serialize archive version. Always call after opening a file. // Serialize archive version. Always call after opening a file.
void SerializeVersion() void SerializeVersion()

View File

@ -266,7 +266,6 @@ CGameProject* CGameProject::LoadProject(const TString& rkProjPath, IProgressNoti
return nullptr; return nullptr;
} }
CTemplateLoader::LoadGameTemplates(pProj->mGame);
pProj->mProjFileLock.Lock(ProjPath); pProj->mProjFileLock.Lock(ProjPath);
pProj->mpGameInfo->LoadGameInfo(pProj->mGame); pProj->mpGameInfo->LoadGameInfo(pProj->mGame);
pProj->mpAudioManager->LoadAssets(); pProj->mpAudioManager->LoadAssets();

View File

@ -137,7 +137,7 @@ CStructPropertyNew* CLight::GetProperties() const
if (!pProperties) if (!pProperties)
{ {
pProperties = (CStructPropertyNew*) IPropertyNew::CreateIntrinsic(EPropertyTypeNew::Struct, pProperties = (CStructPropertyNew*) IPropertyNew::CreateIntrinsic(EPropertyTypeNew::Struct,
nullptr, ePrime,
0, 0,
"Light"); "Light");

View File

@ -189,14 +189,14 @@ IPropertyNew* CTemplateLoader::LoadProperty(XMLElement* pElem, CScriptTemplate*
// create property as a copy of the archetype // create property as a copy of the archetype
if (pArchetype != nullptr) if (pArchetype != nullptr)
{ {
pProp = IPropertyNew::CreateCopy(pArchetype, pParent); pProp = IPropertyNew::CreateCopy(pArchetype);
} }
} }
// no archetype, so do normal create // no archetype, so do normal create
if (!pProp) 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 // we need to have a valid property by this point
@ -340,7 +340,7 @@ IPropertyNew* CTemplateLoader::LoadProperty(XMLElement* pElem, CScriptTemplate*
} }
else else
{ {
pArray->mpItemArchetype = IPropertyNew::Create(EPropertyTypeNew::Struct, pArray, mGame, pScript, false); pArray->mpItemArchetype = IPropertyNew::Create(EPropertyTypeNew::Struct, mGame);
pStruct = TPropCast<CStructPropertyNew>(pArray->mpItemArchetype); pStruct = TPropCast<CStructPropertyNew>(pArray->mpItemArchetype);
pStruct->mFlags = EPropertyFlag::IsAtomic | EPropertyFlag::IsArrayArchetype; pStruct->mFlags = EPropertyFlag::IsAtomic | EPropertyFlag::IsArrayArchetype;
} }
@ -358,17 +358,16 @@ IPropertyNew* CTemplateLoader::LoadProperty(XMLElement* pElem, CScriptTemplate*
{ {
LoadProperties(pProperties, pScript, pStruct, rkTemplateName); LoadProperties(pProperties, pScript, pStruct, rkTemplateName);
} }
if (Type == EPropertyTypeNew::Array)
{
pStruct->PostInitialize();
}
} }
if (IsNewProperty) if (IsNewProperty)
{
CMasterTemplate::AddProperty(pProp, mMasterDir + rkTemplateName); CMasterTemplate::AddProperty(pProp, mMasterDir + rkTemplateName);
pProp->PostInitialize(); if (pParent)
pParent->mChildren.push_back(pProp);
}
return pProp; return pProp;
} }
@ -393,11 +392,7 @@ CStructPropertyNew* CTemplateLoader::LoadStructArchetype(const TString& rkTempla
if (!Doc.Error()) if (!Doc.Error())
{ {
pArchetype = TPropCast<CStructPropertyNew>( pArchetype = TPropCast<CStructPropertyNew>(
IPropertyNew::Create(EPropertyTypeNew::Struct, IPropertyNew::Create(EPropertyTypeNew::Struct, mGame)
nullptr,
mGame,
nullptr,
false)
); );
ASSERT(pArchetype != nullptr); ASSERT(pArchetype != nullptr);
@ -438,7 +433,7 @@ CStructPropertyNew* CTemplateLoader::LoadStructArchetype(const TString& rkTempla
ASSERT(pSubPropsElem); ASSERT(pSubPropsElem);
LoadProperties(pSubPropsElem, nullptr, pArchetype, rkTemplateFileName); LoadProperties(pSubPropsElem, nullptr, pArchetype, rkTemplateFileName);
pArchetype->PostInitialize(); pArchetype->Initialize(nullptr, nullptr, 0);
mpMaster->mPropertyTemplates.emplace( mpMaster->mPropertyTemplates.emplace(
std::make_pair( 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 // use static_cast so this code works for both enum and choice
pArchetype = static_cast<CEnumProperty*>( pArchetype = static_cast<CEnumProperty*>(
IPropertyNew::Create(bIsChoice ? EPropertyTypeNew::Choice : EPropertyTypeNew::Enum, IPropertyNew::Create(bIsChoice ? EPropertyTypeNew::Choice : EPropertyTypeNew::Enum, mGame)
nullptr,
mGame,
nullptr,
false)
); );
ASSERT(pArchetype != nullptr); ASSERT(pArchetype != nullptr);
@ -493,7 +484,7 @@ CEnumProperty* CTemplateLoader::LoadEnumArchetype(const TString& rkTemplateFileN
ASSERT(pEnumers); ASSERT(pEnumers);
LoadEnumerators(pEnumers, pArchetype, rkTemplateFileName); LoadEnumerators(pEnumers, pArchetype, rkTemplateFileName);
pArchetype->PostInitialize(); pArchetype->Initialize(nullptr, nullptr, 0);
mpMaster->mPropertyTemplates.emplace( mpMaster->mPropertyTemplates.emplace(
std::make_pair( std::make_pair(
@ -528,11 +519,7 @@ CFlagsProperty* CTemplateLoader::LoadFlagsArchetype(const TString& rkTemplateFil
if (!Doc.Error()) if (!Doc.Error())
{ {
pArchetype = TPropCast<CFlagsProperty>( pArchetype = TPropCast<CFlagsProperty>(
IPropertyNew::Create(EPropertyTypeNew::Flags, IPropertyNew::Create(EPropertyTypeNew::Flags, mGame)
nullptr,
mGame,
nullptr,
false)
); );
ASSERT(pArchetype != nullptr); ASSERT(pArchetype != nullptr);
@ -547,7 +534,7 @@ CFlagsProperty* CTemplateLoader::LoadFlagsArchetype(const TString& rkTemplateFil
ASSERT(pFlags); ASSERT(pFlags);
LoadBitFlags(pFlags, pArchetype, rkTemplateFileName); LoadBitFlags(pFlags, pArchetype, rkTemplateFileName);
pArchetype->PostInitialize(); pArchetype->Initialize(nullptr, nullptr, 0);
mpMaster->mPropertyTemplates.emplace( mpMaster->mPropertyTemplates.emplace(
std::make_pair( std::make_pair(
@ -645,7 +632,7 @@ CScriptTemplate* CTemplateLoader::LoadScriptTemplate(XMLDocument *pDoc, const TS
pScript->mObjectID = ObjectID; pScript->mObjectID = ObjectID;
pScript->mSourceFile = rkTemplateName; pScript->mSourceFile = rkTemplateName;
IPropertyNew* pBaseStruct = IPropertyNew::Create(EPropertyTypeNew::Struct, nullptr, mGame, pScript); IPropertyNew* pBaseStruct = IPropertyNew::Create(EPropertyTypeNew::Struct, mGame);
pScript->mpProperties = std::make_unique<CStructPropertyNew>( *TPropCast<CStructPropertyNew>(pBaseStruct) ); pScript->mpProperties = std::make_unique<CStructPropertyNew>( *TPropCast<CStructPropertyNew>(pBaseStruct) );
XMLElement *pRoot = pDoc->FirstChildElement("ScriptTemplate"); XMLElement *pRoot = pDoc->FirstChildElement("ScriptTemplate");
@ -1139,7 +1126,7 @@ TString CTemplateLoader::ErrorName(XMLError Error)
} }
// ************ PUBLIC ************ // ************ PUBLIC ************
#define USE_NEW_TEMPLATES 0 #define USE_NEW_TEMPLATES 1
void CTemplateLoader::LoadGameList() void CTemplateLoader::LoadGameList()
{ {
@ -1193,7 +1180,7 @@ void CTemplateLoader::LoadGameList()
for (auto Iter = MasterList.begin(); Iter != MasterList.end(); Iter++) for (auto Iter = MasterList.begin(); Iter != MasterList.end(); Iter++)
{ {
CMasterTemplate* pMaster = *Iter; CMasterTemplate* pMaster = *Iter;
const TString kMasterPath = kTemplatesDir + pMaster->GetDirectory() + "Game.xml"; const TString kMasterPath = pMaster->GetGameDirectory(true) + "Game.xml";
CXMLReader Reader(kMasterPath); CXMLReader Reader(kMasterPath);
ASSERT(Reader.IsValid()); ASSERT(Reader.IsValid());
@ -1354,7 +1341,7 @@ void CTemplateLoader::SaveGameList()
SGameInfo Info; SGameInfo Info;
Info.Game = pMaster->Game(); Info.Game = pMaster->Game();
Info.Name = pMaster->GameName(); Info.Name = pMaster->GameName();
Info.MasterPath = pMaster->GetDirectory() + "Game.xml"; Info.MasterPath = pMaster->GetGameDirectory() + "Game.xml";
Writer << SerialParameter("Game", Info); Writer << SerialParameter("Game", Info);
} }
Writer.ParamEnd(); Writer.ParamEnd();
@ -1373,7 +1360,7 @@ void CTemplateLoader::SaveGameList()
for (auto Iter = MasterList.begin(); Iter != MasterList.end(); Iter++) for (auto Iter = MasterList.begin(); Iter != MasterList.end(); Iter++)
{ {
CMasterTemplate* pMasterTemplate = *Iter; CMasterTemplate* pMasterTemplate = *Iter;
TString MasterFilePath = kTemplatesDir + pMasterTemplate->GetDirectory() + "Game.xml"; TString MasterFilePath = pMasterTemplate->GetGameDirectory(true) + "Game.xml";
FileUtil::MakeDirectory( MasterFilePath.GetFileDirectory() ); FileUtil::MakeDirectory( MasterFilePath.GetFileDirectory() );
CXMLWriter Writer(MasterFilePath, "Game", 0, pMasterTemplate->Game()); CXMLWriter Writer(MasterFilePath, "Game", 0, pMasterTemplate->Game());

View File

@ -17,17 +17,52 @@ void CMasterTemplate::Serialize(IArchive& Arc)
void CMasterTemplate::LoadSubTemplates() 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<CScriptTemplate>(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() void CMasterTemplate::SaveSubTemplates()
{ {
TString GameDir = "../templates_new/" + GetDirectory(); const TString kGameDir = GetGameDirectory(true);
for (auto Iter = mScriptTemplates.begin(); Iter != mScriptTemplates.end(); Iter++) for (auto Iter = mScriptTemplates.begin(); Iter != mScriptTemplates.end(); Iter++)
{ {
SScriptTemplatePath& Path = Iter->second; SScriptTemplatePath& Path = Iter->second;
TString OutPath = GameDir + Path.Path; TString OutPath = kGameDir + Path.Path;
FileUtil::MakeDirectory( OutPath.GetFileDirectory() ); FileUtil::MakeDirectory( OutPath.GetFileDirectory() );
CXMLWriter Writer(OutPath, "ScriptObject", 0, Game()); CXMLWriter Writer(OutPath, "ScriptObject", 0, Game());
@ -37,11 +72,11 @@ void CMasterTemplate::SaveSubTemplates()
for (auto Iter = mPropertyTemplates.begin(); Iter != mPropertyTemplates.end(); Iter++) for (auto Iter = mPropertyTemplates.begin(); Iter != mPropertyTemplates.end(); Iter++)
{ {
SPropertyTemplatePath& Path = Iter->second; SPropertyTemplatePath& Path = Iter->second;
TString OutPath = GameDir + Path.Path; TString OutPath = kGameDir + Path.Path;
FileUtil::MakeDirectory( OutPath.GetFileDirectory() ); FileUtil::MakeDirectory( OutPath.GetFileDirectory() );
CXMLWriter Writer(OutPath, "PropertyArchetype", 0, Game()); CXMLWriter Writer(OutPath, "PropertyTemplate", 0, Game());
Path.pTemplate->Serialize(Writer); Writer << SerialParameter("PropertyArchetype", Path.pTemplate);
} }
} }
@ -115,10 +150,34 @@ SMessage CMasterTemplate::MessageByIndex(u32 Index)
return SMessage(Iter->first, Iter->second); return SMessage(Iter->first, Iter->second);
} }
IPropertyNew* CMasterTemplate::FindPropertyArchetype(const TString& kTypeName) const IPropertyNew* CMasterTemplate::FindPropertyArchetype(const TString& kTypeName)
{ {
auto Iter = mPropertyTemplates.find(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 ************ // ************ STATIC ************

View File

@ -124,6 +124,9 @@ class CMasterTemplate
static std::map<u32, TString> smPropertyNames; static std::map<u32, TString> smPropertyNames;
static u32 smGameListVersion; static u32 smGameListVersion;
void Internal_LoadScriptTemplate(SScriptTemplatePath& Path);
void Internal_LoadPropertyTemplate(SPropertyTemplatePath& Path);
public: public:
CMasterTemplate(); CMasterTemplate();
void Serialize(IArchive& Arc); void Serialize(IArchive& Arc);
@ -139,7 +142,8 @@ public:
SMessage MessageByID(u32 MessageID); SMessage MessageByID(u32 MessageID);
SMessage MessageByID(const CFourCC& MessageID); SMessage MessageByID(const CFourCC& MessageID);
SMessage MessageByIndex(u32 Index); SMessage MessageByIndex(u32 Index);
IPropertyNew* FindPropertyArchetype(const TString& kTypeName) const; IPropertyNew* FindPropertyArchetype(const TString& kTypeName);
TString GetGameDirectory(bool Absolute = false) const;
// Inline Accessors // Inline Accessors
inline EGame Game() const { return mGame; } inline EGame Game() const { return mGame; }
@ -148,7 +152,6 @@ public:
inline u32 NumStates() const { return mStates.size(); } inline u32 NumStates() const { return mStates.size(); }
inline u32 NumMessages() const { return mMessages.size(); } inline u32 NumMessages() const { return mMessages.size(); }
inline bool IsLoadedSuccessfully() { return mFullyLoaded; } inline bool IsLoadedSuccessfully() { return mFullyLoaded; }
inline TString GetDirectory() const { return mSourceFile.GetFileDirectory(); }
// Static // Static
static CMasterTemplate* MasterForGame(EGame Game); static CMasterTemplate* MasterForGame(EGame Game);

View File

@ -79,6 +79,8 @@ void CScriptTemplate::Serialize(IArchive& Arc)
void CScriptTemplate::PostLoad() void CScriptTemplate::PostLoad()
{ {
mpProperties->Initialize(nullptr, this, 0);
if (!mNameIDString.IsEmpty()) mpNameProperty = TPropCast<CStringProperty>( mpProperties->ChildByIDString(mNameIDString) ); if (!mNameIDString.IsEmpty()) mpNameProperty = TPropCast<CStringProperty>( mpProperties->ChildByIDString(mNameIDString) );
if (!mPositionIDString.IsEmpty()) mpPositionProperty = TPropCast<CVectorProperty>( mpProperties->ChildByIDString(mPositionIDString) ); if (!mPositionIDString.IsEmpty()) mpPositionProperty = TPropCast<CVectorProperty>( mpProperties->ChildByIDString(mPositionIDString) );
if (!mRotationIDString.IsEmpty()) mpRotationProperty = TPropCast<CVectorProperty>( mpProperties->ChildByIDString(mRotationIDString) ); if (!mRotationIDString.IsEmpty()) mpRotationProperty = TPropCast<CVectorProperty>( mpProperties->ChildByIDString(mRotationIDString) );

View File

@ -130,6 +130,8 @@ private:
bool mVisible; bool mVisible;
public: public:
// Default constructor. Don't use. This is only here so the serializer doesn't complain
CScriptTemplate() { ASSERT(false); }
// Old constructor // Old constructor
CScriptTemplate(CMasterTemplate *pMaster); CScriptTemplate(CMasterTemplate *pMaster);
// New constructor // New constructor

View File

@ -9,10 +9,12 @@
#include "Core/Resource/Script/CScriptTemplate.h" #include "Core/Resource/Script/CScriptTemplate.h"
/** IPropertyNew */ /** IPropertyNew */
IPropertyNew::IPropertyNew() IPropertyNew::IPropertyNew(EGame Game)
: mpParent( nullptr ) : mpParent( nullptr )
, mpPointerParent( nullptr ) , mpPointerParent( nullptr )
, mpArchetype( nullptr ) , mpArchetype( nullptr )
, mGame( Game )
, mpScriptTemplate( nullptr )
, mOffset( -1 ) , mOffset( -1 )
, mID( -1 ) , mID( -1 )
, mCookPreference( ECookPreferenceNew::Default ) , mCookPreference( ECookPreferenceNew::Default )
@ -20,41 +22,6 @@ IPropertyNew::IPropertyNew()
, mMaxVersion( FLT_MAX ) , mMaxVersion( FLT_MAX )
{} {}
void IPropertyNew::_CalcOffset()
{
// For standard properties, append to the end of the parent.
bool IsRootArrayArchetype = (IsArrayArchetype() && TPropCast<CArrayProperty>(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() void IPropertyNew::_ClearChildren()
{ {
for (int ChildIdx = 0; ChildIdx < mChildren.size(); ChildIdx++) for (int ChildIdx = 0; ChildIdx < mChildren.size(); ChildIdx++)
@ -109,7 +76,6 @@ void IPropertyNew::Serialize(IArchive& rArc)
IPropertyNew* pArchetype = pMaster->FindPropertyArchetype(ArchetypeName); IPropertyNew* pArchetype = pMaster->FindPropertyArchetype(ArchetypeName);
// The archetype must exist, or else the template file is malformed. // 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); ASSERT(pArchetype != nullptr);
InitFromArchetype(pArchetype); 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 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. // 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 : ""); 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? //@todo maybe somehow use Serialize for this instead?
mpArchetype = pOther; mpArchetype = pOther;
mFlags = pOther->mFlags & EPropertyFlag::ArchetypeCopyFlags; mFlags = pOther->mFlags & EPropertyFlag::ArchetypeCopyFlags;
mID = pOther->mID;
mName = pOther->mName; mName = pOther->mName;
mDescription = pOther->mDescription; mDescription = pOther->mDescription;
mSuffix = pOther->mSuffix; mSuffix = pOther->mSuffix;
@ -145,12 +115,10 @@ void IPropertyNew::InitFromArchetype(IPropertyNew* pOther)
mMinVersion = pOther->mMinVersion; mMinVersion = pOther->mMinVersion;
mMaxVersion = pOther->mMaxVersion; mMaxVersion = pOther->mMaxVersion;
// Copy children // Copy ID only if our existing ID is not valid.
_ClearChildren(); if (mID == 0xFFFFFFFF)
for (u32 ChildIdx = 0; ChildIdx < pOther->mChildren.size(); ChildIdx++)
{ {
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 void* IPropertyNew::RawValuePtr(void* pData) const
{ {
// For array archetypes, the caller needs to provide the pointer to the correct array item // 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; mSuffix = rkNewSuffix;
} }
void IPropertyNew::SetPropertyFlags(FPropertyFlags FlagsToSet)
{
mFlags |= FlagsToSet;
}
bool IPropertyNew::HasAccurateName() 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)) if (!mFlags.HasFlag(EPropertyFlag::HasCachedNameCheck))
{ {
CCRC32 Hash; CCRC32 Hash;
@ -293,114 +336,79 @@ EGame IPropertyNew::Game() const
} }
IPropertyNew* IPropertyNew::Create(EPropertyTypeNew Type, IPropertyNew* IPropertyNew::Create(EPropertyTypeNew Type,
IPropertyNew* pParent, EGame Game)
EGame Game,
CScriptTemplate* pScript,
bool CallPostInit /*= true*/)
{ {
IPropertyNew* pOut = nullptr; IPropertyNew* pOut = nullptr;
switch (Type) switch (Type)
{ {
case EPropertyTypeNew::Bool: pOut = new CBoolProperty; break; case EPropertyTypeNew::Bool: pOut = new CBoolProperty(Game); break;
case EPropertyTypeNew::Byte: pOut = new CByteProperty; break; case EPropertyTypeNew::Byte: pOut = new CByteProperty(Game); break;
case EPropertyTypeNew::Short: pOut = new CShortProperty; break; case EPropertyTypeNew::Short: pOut = new CShortProperty(Game); break;
case EPropertyTypeNew::Int: pOut = new CIntProperty; break; case EPropertyTypeNew::Int: pOut = new CIntProperty(Game); break;
case EPropertyTypeNew::Float: pOut = new CFloatProperty; break; case EPropertyTypeNew::Float: pOut = new CFloatProperty(Game); break;
case EPropertyTypeNew::Choice: pOut = new CChoiceProperty; break; case EPropertyTypeNew::Choice: pOut = new CChoiceProperty(Game); break;
case EPropertyTypeNew::Enum: pOut = new CEnumProperty; break; case EPropertyTypeNew::Enum: pOut = new CEnumProperty(Game); break;
case EPropertyTypeNew::Flags: pOut = new CFlagsProperty; break; case EPropertyTypeNew::Flags: pOut = new CFlagsProperty(Game); break;
case EPropertyTypeNew::String: pOut = new CStringProperty; break; case EPropertyTypeNew::String: pOut = new CStringProperty(Game); break;
case EPropertyTypeNew::Vector: pOut = new CVectorProperty; break; case EPropertyTypeNew::Vector: pOut = new CVectorProperty(Game); break;
case EPropertyTypeNew::Color: pOut = new CColorProperty; break; case EPropertyTypeNew::Color: pOut = new CColorProperty(Game); break;
case EPropertyTypeNew::Asset: pOut = new CAssetProperty; break; case EPropertyTypeNew::Asset: pOut = new CAssetProperty(Game); break;
case EPropertyTypeNew::Sound: pOut = new CSoundProperty; break; case EPropertyTypeNew::Sound: pOut = new CSoundProperty(Game); break;
case EPropertyTypeNew::Animation: pOut = new CAnimationProperty; break; case EPropertyTypeNew::Animation: pOut = new CAnimationProperty(Game); break;
case EPropertyTypeNew::AnimationSet: pOut = new CAnimationSetProperty; break; case EPropertyTypeNew::AnimationSet: pOut = new CAnimationSetProperty(Game); break;
case EPropertyTypeNew::Sequence: pOut = new CSequenceProperty; break; case EPropertyTypeNew::Sequence: pOut = new CSequenceProperty(Game); break;
case EPropertyTypeNew::Spline: pOut = new CSplineProperty; break; case EPropertyTypeNew::Spline: pOut = new CSplineProperty(Game); break;
case EPropertyTypeNew::Guid: pOut = new CGuidProperty; break; case EPropertyTypeNew::Guid: pOut = new CGuidProperty(Game); break;
case EPropertyTypeNew::Pointer: pOut = new CPointerProperty; break; case EPropertyTypeNew::Pointer: pOut = new CPointerProperty(Game); break;
case EPropertyTypeNew::Struct: pOut = new CStructPropertyNew; break; case EPropertyTypeNew::Struct: pOut = new CStructPropertyNew(Game); break;
case EPropertyTypeNew::Array: pOut = new CArrayProperty; break; case EPropertyTypeNew::Array: pOut = new CArrayProperty(Game); 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();
} }
// If this assertion fails, then there is an unhandled type!
ASSERT(pOut != nullptr);
return pOut; return pOut;
} }
IPropertyNew* IPropertyNew::CreateCopy(IPropertyNew* pArchetype, IPropertyNew* IPropertyNew::CreateCopy(IPropertyNew* pArchetype)
IPropertyNew* pParent)
{ {
// Note this is mainly going to be used to create copies from struct/enum/flag archetype properties. IPropertyNew* pOut = Create(pArchetype->Type(), pArchetype->mGame);
// 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);
pOut->InitFromArchetype(pArchetype); pOut->InitFromArchetype(pArchetype);
pArchetype->mSubInstances.push_back(pOut); pArchetype->mSubInstances.push_back(pOut);
return 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* IPropertyNew::CreateIntrinsic(EPropertyTypeNew Type,
IPropertyNew* pParent, IPropertyNew* pParent,
u32 Offset, u32 Offset,
const TString& rkName) const TString& rkName)
{ {
IPropertyNew* pOut = Create(Type, pParent, pParent ? pParent->mGame : eUnknownGame, nullptr, false); // pParent should always be valid.
pOut->mOffset = Offset; // 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->SetName(rkName);
pOut->PostInitialize(); pOut->Initialize(pParent, nullptr, Offset);
pParent->mChildren.push_back(pOut);
return pOut; return pOut;
} }
IPropertyNew* IPropertyNew::ArchiveConstructor(EPropertyTypeNew Type, IPropertyNew* IPropertyNew::ArchiveConstructor(EPropertyTypeNew Type,
const IArchive& Arc) const IArchive& Arc)
{ {
IPropertyNew* pParent = Arc.FindParentObject<IPropertyNew>(); return Create(Type, Arc.Game());
CScriptTemplate* pTemplate = (pParent ? pParent->ScriptTemplate() : Arc.FindParentObject<CScriptTemplate>());
EGame Game = Arc.Game();
return Create(Type, pParent, Game, pTemplate);
} }

View File

@ -19,12 +19,16 @@ typedef TString TIDString;
/** Property flags */ /** Property flags */
enum class EPropertyFlag : u32 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) */ /** 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) */ /** 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. */ /** 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 */ /** We have cached whether the property name is correct */
HasCachedNameCheck = 0x40000000, HasCachedNameCheck = 0x40000000,
/** The name of the property is a match for the property ID hash */ /** 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 */ /** Archetype property; source property that we copied metadata from */
IPropertyNew* mpArchetype; IPropertyNew* mpArchetype;
/** Sub-instances of archetype properties. For non-archetypes, will be empty. @todo better /** Sub-instances of archetype properties. For non-archetypes, will be empty.
* method of storing this? maybe a linked list? */ * @todo this really oughta be a linked list */
std::vector<IPropertyNew*> mSubInstances; std::vector<IPropertyNew*> mSubInstances;
/** Child properties; these appear underneath this property on the UI */ /** Child properties; these appear underneath this property on the UI */
@ -155,13 +159,9 @@ protected:
float mMaxVersion; float mMaxVersion;
/** Private constructor - use static methods to instantiate */ /** Private constructor - use static methods to instantiate */
IPropertyNew(); IPropertyNew(EGame Game);
void _CalcOffset();
void _ClearChildren(); void _ClearChildren();
/** Called after property is created and fully initialized */
virtual void PostInitialize() {}
public: public:
virtual ~IPropertyNew(); virtual ~IPropertyNew();
@ -175,11 +175,11 @@ public:
virtual void RevertToDefault(void* pData) const = 0; virtual void RevertToDefault(void* pData) const = 0;
virtual void SerializeValue(void* pData, IArchive& Arc) const = 0; virtual void SerializeValue(void* pData, IArchive& Arc) const = 0;
virtual void PostInitialize() {}
virtual void PropertyValueChanged(void* pPropertyData) {} virtual void PropertyValueChanged(void* pPropertyData) {}
virtual bool IsNumericalType() const { return false; } virtual bool IsNumericalType() const { return false; }
virtual bool IsPointerType() const { return false; } virtual bool IsPointerType() const { return false; }
virtual TString ValueAsString(void* pData) const { return ""; } virtual TString ValueAsString(void* pData) const { return ""; }
virtual const char* HashableTypeName() const; virtual const char* HashableTypeName() const;
virtual void* GetChildDataPointer(void* pPropertyData) const; virtual void* GetChildDataPointer(void* pPropertyData) const;
virtual void Serialize(IArchive& rArc); virtual void Serialize(IArchive& rArc);
@ -188,6 +188,7 @@ public:
virtual TString GetTemplateFileName(); virtual TString GetTemplateFileName();
/** Utility methods */ /** Utility methods */
void Initialize(IPropertyNew* pInParent, CScriptTemplate* pInTemplate, u32 InOffset);
void* RawValuePtr(void* pData) const; void* RawValuePtr(void* pData) const;
IPropertyNew* ChildByID(u32 ID) const; IPropertyNew* ChildByID(u32 ID) const;
IPropertyNew* ChildByIDString(const TIDString& rkIdString); IPropertyNew* ChildByIDString(const TIDString& rkIdString);
@ -195,6 +196,7 @@ public:
void SetName(const TString& rkNewName); void SetName(const TString& rkNewName);
void SetDescription(const TString& rkNewDescription); void SetDescription(const TString& rkNewDescription);
void SetSuffix(const TString& rkNewSuffix); void SetSuffix(const TString& rkNewSuffix);
void SetPropertyFlags(FPropertyFlags FlagsToSet);
bool HasAccurateName(); bool HasAccurateName();
/** Accessors */ /** Accessors */
@ -216,17 +218,19 @@ public:
inline bool IsArchetype() const { return mFlags.HasFlag(EPropertyFlag::IsArchetype); } inline bool IsArchetype() const { return mFlags.HasFlag(EPropertyFlag::IsArchetype); }
inline bool IsArrayArchetype() const { return mFlags.HasFlag(EPropertyFlag::IsArrayArchetype); } inline bool IsArrayArchetype() const { return mFlags.HasFlag(EPropertyFlag::IsArrayArchetype); }
inline bool IsAtomic() const { return mFlags.HasFlag(EPropertyFlag::IsAtomic); } 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; } inline bool IsRootParent() const { return mpParent == nullptr; }
/** Create */ /** Create */
static IPropertyNew* Create(EPropertyTypeNew Type, static IPropertyNew* Create(EPropertyTypeNew Type,
IPropertyNew* pParent, EGame Game);
EGame Game,
CScriptTemplate* pScript,
bool CallPostInit = true);
static IPropertyNew* CreateCopy(IPropertyNew* pArchetype, static IPropertyNew* CreateCopy(IPropertyNew* pArchetype);
IPropertyNew* pParent);
static IPropertyNew* CreateIntrinsic(EPropertyTypeNew Type,
EGame Game,
u32 Offset,
const TString& rkName);
static IPropertyNew* CreateIntrinsic(EPropertyTypeNew Type, static IPropertyNew* CreateIntrinsic(EPropertyTypeNew Type,
IPropertyNew* pParent, IPropertyNew* pParent,
@ -326,8 +330,8 @@ public:
protected: protected:
PropType mDefaultValue; PropType mDefaultValue;
TTypedPropertyNew() TTypedPropertyNew(EGame Game)
: IPropertyNew() : IPropertyNew(Game)
{ {
memset(&mDefaultValue, 0, sizeof(PropType)); memset(&mDefaultValue, 0, sizeof(PropType));
} }
@ -369,6 +373,11 @@ public:
return mDefaultValue; return mDefaultValue;
} }
inline void SetDefaultValue(const PropType& kInDefaultValue)
{
mDefaultValue = kInDefaultValue;
}
inline static EPropertyTypeNew StaticType() { return PropEnum; } inline static EPropertyTypeNew StaticType() { return PropEnum; }
}; };
@ -376,8 +385,8 @@ template<typename PropType, EPropertyTypeNew PropEnum>
class TSerializeableTypedProperty : public TTypedPropertyNew<PropType, PropEnum> class TSerializeableTypedProperty : public TTypedPropertyNew<PropType, PropEnum>
{ {
protected: protected:
TSerializeableTypedProperty() TSerializeableTypedProperty(EGame Game)
: TTypedPropertyNew() : TTypedPropertyNew(Game)
{} {}
public: public:
@ -443,8 +452,8 @@ protected:
PropType mMinValue; PropType mMinValue;
PropType mMaxValue; PropType mMaxValue;
TNumericalPropertyNew() TNumericalPropertyNew(EGame Game)
: TSerializeableTypedProperty() : TSerializeableTypedProperty(Game)
, mMinValue( -1 ) , mMinValue( -1 )
, mMaxValue( -1 ) , mMaxValue( -1 )
{} {}

View File

@ -8,8 +8,8 @@ class CAnimationProperty : public TSerializeableTypedProperty< u32, EPropertyTyp
friend class IPropertyNew; friend class IPropertyNew;
protected: protected:
CAnimationProperty() CAnimationProperty(EGame Game)
: TSerializeableTypedProperty() : TSerializeableTypedProperty(Game)
{} {}
public: public:

View File

@ -8,16 +8,13 @@ class CAnimationSetProperty : public TSerializeableTypedProperty< CAnimationPara
friend class IPropertyNew; friend class IPropertyNew;
protected: protected:
CAnimationSetProperty() CAnimationSetProperty(EGame Game)
: TSerializeableTypedProperty() : TSerializeableTypedProperty(Game)
{}
public:
virtual void PostInitialize()
{ {
mDefaultValue.SetGame(Game()); mDefaultValue.SetGame(Game);
} }
public:
virtual void SerializeValue(void* pData, IArchive& Arc) const virtual void SerializeValue(void* pData, IArchive& Arc) const
{ {
Value(pData).Serialize(Arc); Value(pData).Serialize(Arc);

View File

@ -45,8 +45,8 @@ class CArrayProperty : public TTypedPropertyNew<u32, EPropertyTypeNew::Array>
} }
protected: protected:
CArrayProperty() CArrayProperty(EGame Game)
: TTypedPropertyNew() : TTypedPropertyNew(Game)
, mpItemArchetype(nullptr) , mpItemArchetype(nullptr)
{} {}
@ -109,6 +109,11 @@ public:
{ {
TTypedPropertyNew::Serialize(rArc); TTypedPropertyNew::Serialize(rArc);
rArc << SerialParameter("ItemArchetype", mpItemArchetype); rArc << SerialParameter("ItemArchetype", mpItemArchetype);
if (rArc.IsReader())
{
mpItemArchetype->SetPropertyFlags( EPropertyFlag::IsArrayArchetype );
}
} }
virtual void SerializeValue(void* pData, IArchive& Arc) const virtual void SerializeValue(void* pData, IArchive& Arc) const
@ -134,7 +139,13 @@ public:
{ {
TTypedPropertyNew::InitFromArchetype(pOther); TTypedPropertyNew::InitFromArchetype(pOther);
CArrayProperty* pOtherArray = static_cast<CArrayProperty*>(pOther); CArrayProperty* pOtherArray = static_cast<CArrayProperty*>(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 u32 ArrayCount(void* pPropertyData) const

View File

@ -7,18 +7,18 @@
class CAssetProperty : public TSerializeableTypedProperty<CAssetID, EPropertyTypeNew::Asset> class CAssetProperty : public TSerializeableTypedProperty<CAssetID, EPropertyTypeNew::Asset>
{ {
friend class CTemplateLoader; friend class CTemplateLoader;
friend class IPropertyNew;
CResTypeFilter mTypeFilter; CResTypeFilter mTypeFilter;
public: protected:
virtual void PostInitialize() 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) virtual void Serialize(IArchive& rArc)
{ {
TSerializeableTypedProperty::Serialize(rArc); TSerializeableTypedProperty::Serialize(rArc);

View File

@ -8,8 +8,8 @@ class CBoolProperty : public TSerializeableTypedProperty< bool, EPropertyTypeNew
friend class IPropertyNew; friend class IPropertyNew;
protected: protected:
CBoolProperty() CBoolProperty(EGame Game)
: TSerializeableTypedProperty() : TSerializeableTypedProperty(Game)
{} {}
public: public:

View File

@ -8,8 +8,8 @@ class CByteProperty : public TNumericalPropertyNew< s8, EPropertyTypeNew::Byte >
friend class IPropertyNew; friend class IPropertyNew;
protected: protected:
CByteProperty() CByteProperty(EGame Game)
: TNumericalPropertyNew() : TNumericalPropertyNew(Game)
{} {}
public: public:

View File

@ -2,27 +2,25 @@
#define CCOLORPROPERTY_H #define CCOLORPROPERTY_H
#include "../IPropertyNew.h" #include "../IPropertyNew.h"
#include "CFloatProperty.h"
class CColorProperty : public TSerializeableTypedProperty< CColor, EPropertyTypeNew::Color > class CColorProperty : public TSerializeableTypedProperty< CColor, EPropertyTypeNew::Color >
{ {
friend class IPropertyNew; friend class IPropertyNew;
protected: protected:
CColorProperty() CColorProperty(EGame Game)
: TSerializeableTypedProperty() : TSerializeableTypedProperty(Game)
{} {}
public: public:
virtual void PostInitialize() virtual void PostInitialize()
{ {
IPropertyNew* pR = Create(EPropertyTypeNew::Float, this, mGame, mpScriptTemplate); CreateIntrinsic(EPropertyTypeNew::Float, this, mOffset + 0, "R");
IPropertyNew* pG = Create(EPropertyTypeNew::Float, this, mGame, mpScriptTemplate); CreateIntrinsic(EPropertyTypeNew::Float, this, mOffset + 4, "G");
IPropertyNew* pB = Create(EPropertyTypeNew::Float, this, mGame, mpScriptTemplate); CreateIntrinsic(EPropertyTypeNew::Float, this, mOffset + 8, "B");
IPropertyNew* pA = Create(EPropertyTypeNew::Float, this, mGame, mpScriptTemplate); CreateIntrinsic(EPropertyTypeNew::Float, this, mOffset + 12, "A");
pR->SetName("R"); TPropCast<CFloatProperty>( mChildren.back() )->SetDefaultValue(1.0f);
pG->SetName("G");
pB->SetName("B");
pA->SetName("A");
} }
virtual void SerializeValue(void* pData, IArchive& Arc) const virtual void SerializeValue(void* pData, IArchive& Arc) const

View File

@ -14,6 +14,8 @@ template<EPropertyTypeNew TypeEnum>
class TEnumPropertyBase : public TSerializeableTypedProperty<s32, TypeEnum> class TEnumPropertyBase : public TSerializeableTypedProperty<s32, TypeEnum>
{ {
friend class CTemplateLoader; friend class CTemplateLoader;
friend class IPropertyNew;
struct SEnumValue struct SEnumValue
{ {
TString Name; TString Name;
@ -43,6 +45,12 @@ class TEnumPropertyBase : public TSerializeableTypedProperty<s32, TypeEnum>
/** XML template file that this enum originated from; for archetypes */ /** XML template file that this enum originated from; for archetypes */
TString mSourceFile; TString mSourceFile;
protected:
/** Constructor */
TEnumPropertyBase(EGame Game)
: TSerializeableTypedProperty(Game)
{}
public: public:
virtual const char* GetHashableTypeName() const virtual const char* GetHashableTypeName() const
{ {

View File

@ -38,8 +38,8 @@ class CFlagsProperty : public TSerializeableTypedProperty<u32, EPropertyTypeNew:
/** XML template file that this enum originated from; for archetypes */ /** XML template file that this enum originated from; for archetypes */
TString mSourceFile; TString mSourceFile;
CFlagsProperty() CFlagsProperty(EGame Game)
: TSerializeableTypedProperty() : TSerializeableTypedProperty(Game)
, mAllFlags(0) , mAllFlags(0)
{} {}

View File

@ -8,8 +8,8 @@ class CFloatProperty : public TNumericalPropertyNew< float, EPropertyTypeNew::Fl
friend class IPropertyNew; friend class IPropertyNew;
protected: protected:
CFloatProperty() CFloatProperty(EGame Game)
: TNumericalPropertyNew() : TNumericalPropertyNew(Game)
{} {}
public: public:

View File

@ -8,8 +8,8 @@ class CGuidProperty : public TTypedPropertyNew< std::vector<char>, EPropertyType
friend class IPropertyNew; friend class IPropertyNew;
protected: protected:
CGuidProperty() CGuidProperty(EGame Game)
: TTypedPropertyNew() : TTypedPropertyNew(Game)
{} {}
public: public:

View File

@ -8,8 +8,8 @@ class CIntProperty : public TNumericalPropertyNew< s32, EPropertyTypeNew::Int >
friend class IPropertyNew; friend class IPropertyNew;
protected: protected:
CIntProperty() CIntProperty(EGame Game)
: TNumericalPropertyNew() : TNumericalPropertyNew(Game)
{} {}
public: public:

View File

@ -6,6 +6,12 @@
class CPointerProperty : public TTypedPropertyNew<void*, EPropertyTypeNew::Pointer> class CPointerProperty : public TTypedPropertyNew<void*, EPropertyTypeNew::Pointer>
{ {
friend class CTemplateLoader; friend class CTemplateLoader;
friend class IPropertyNew;
CPointerProperty(EGame Game)
: TTypedPropertyNew(Game)
{}
public: public:
virtual bool IsPointerType() const virtual bool IsPointerType() const
{ {

View File

@ -8,8 +8,8 @@ class CSequenceProperty : public TTypedPropertyNew< s32, EPropertyTypeNew::Seque
friend class IPropertyNew; friend class IPropertyNew;
protected: protected:
CSequenceProperty() CSequenceProperty(EGame Game)
: TTypedPropertyNew() : TTypedPropertyNew(Game)
{} {}
virtual void SerializeValue(void* pData, IArchive& rArc) const virtual void SerializeValue(void* pData, IArchive& rArc) const

View File

@ -8,8 +8,8 @@ class CShortProperty : public TNumericalPropertyNew< s16, EPropertyTypeNew::Shor
friend class IPropertyNew; friend class IPropertyNew;
protected: protected:
CShortProperty() CShortProperty(EGame Game)
: TNumericalPropertyNew() : TNumericalPropertyNew(Game)
{} {}
public: public:

View File

@ -8,8 +8,8 @@ class CSoundProperty : public TSerializeableTypedProperty< s32, EPropertyTypeNew
friend class IPropertyNew; friend class IPropertyNew;
protected: protected:
CSoundProperty() CSoundProperty(EGame Game)
: TSerializeableTypedProperty() : TSerializeableTypedProperty(Game)
{} {}
public: public:

View File

@ -8,8 +8,8 @@ class CSplineProperty : public TTypedPropertyNew< std::vector<char>, EPropertyTy
friend class IPropertyNew; friend class IPropertyNew;
protected: protected:
CSplineProperty() CSplineProperty(EGame Game)
: TTypedPropertyNew() : TTypedPropertyNew(Game)
{} {}
public: public:

View File

@ -8,8 +8,8 @@ class CStringProperty : public TSerializeableTypedProperty< TString, EPropertyTy
friend class IPropertyNew; friend class IPropertyNew;
protected: protected:
CStringProperty() CStringProperty(EGame Game)
: TSerializeableTypedProperty() : TSerializeableTypedProperty(Game)
{} {}
public: public:

View File

@ -21,12 +21,8 @@ u32 CStructPropertyNew::DataSize() const
u32 CStructPropertyNew::DataAlignment() const u32 CStructPropertyNew::DataAlignment() const
{ {
// TODO. Should be aligned with the first child, but this function is called before children are loaded. // Structs are aligned to the first child property.
// So for now just use 8 to ensure correct alignment for all child types, but this is wasteful... return (mChildren.empty() ? 1 : mChildren[0]->DataAlignment());
// It's also problematic for casting property data to a struct
return 8;
//return (mChildren.empty() ? 1 : mChildren[0]->DataAlignment());
} }
void CStructPropertyNew::Construct(void* pData) const void CStructPropertyNew::Construct(void* pData) const
@ -67,16 +63,25 @@ void CStructPropertyNew::RevertToDefault(void* pData) const
const char* CStructPropertyNew::HashableTypeName() const const char* CStructPropertyNew::HashableTypeName() const
{ {
if (IsArchetype() || !mpArchetype) return mpArchetype ? mpArchetype->HashableTypeName() : *mName;
return *mName;
else
return mpArchetype->HashableTypeName();
} }
void CStructPropertyNew::Serialize(IArchive& rArc) void CStructPropertyNew::Serialize(IArchive& rArc)
{ {
IPropertyNew::Serialize(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 // Serialize archetype
if (mpArchetype) if (mpArchetype)
{ {
@ -96,13 +101,13 @@ void CStructPropertyNew::Serialize(IArchive& rArc)
if (rArc.ParamBegin("Element", SH_IgnoreName)) if (rArc.ParamBegin("Element", SH_IgnoreName))
{ {
// Serialize type and ID, then look up the matching property and serialize it. // 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. // to guarantee that parameters are read in order, as some serializers are order-dependent.
EPropertyTypeNew ChildType; EPropertyTypeNew ChildType;
u32 ChildID; u32 ChildID;
rArc << SerialParameter("Type", ChildType, SH_Attribute) rArc << SerialParameter("Type", ChildType, SH_Attribute)
<< SerialParameter("ID", ChildID, SH_Attribute); << SerialParameter("ID", ChildID, SH_Attribute | SH_HexDisplay );
IPropertyNew* pChild = ChildByID(ChildID); IPropertyNew* pChild = ChildByID(ChildID);
ASSERT(pChild != nullptr && pChild->Type() == ChildType); 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 bool CStructPropertyNew::ShouldSerialize() const
{ {
if (IPropertyNew::ShouldSerialize()) if (IPropertyNew::ShouldSerialize())

View File

@ -6,6 +6,7 @@
class CStructPropertyNew : public IPropertyNew class CStructPropertyNew : public IPropertyNew
{ {
friend class CTemplateLoader; friend class CTemplateLoader;
friend class IPropertyNew;
public: public:
// Must be a valid type for TPropertyRef // Must be a valid type for TPropertyRef
@ -15,6 +16,10 @@ protected:
/** For archetypes, the filename of the template XML file. */ /** For archetypes, the filename of the template XML file. */
TString mTemplateFileName; TString mTemplateFileName;
CStructPropertyNew(EGame Game)
: IPropertyNew(Game)
{}
public: public:
virtual EPropertyTypeNew Type() const; virtual EPropertyTypeNew Type() const;
virtual u32 DataSize() const; virtual u32 DataSize() const;
@ -26,6 +31,7 @@ public:
virtual const char* HashableTypeName() const; virtual const char* HashableTypeName() const;
virtual void Serialize(IArchive& rArc); virtual void Serialize(IArchive& rArc);
virtual void SerializeValue(void* pData, IArchive& Arc) const; virtual void SerializeValue(void* pData, IArchive& Arc) const;
virtual void InitFromArchetype(IPropertyNew* pOther);
virtual bool ShouldSerialize() const; virtual bool ShouldSerialize() const;
virtual TString GetTemplateFileName(); virtual TString GetTemplateFileName();

View File

@ -8,19 +8,16 @@ class CVectorProperty : public TSerializeableTypedProperty< CVector3f, EProperty
friend class IPropertyNew; friend class IPropertyNew;
protected: protected:
CVectorProperty() CVectorProperty(EGame Game)
: TSerializeableTypedProperty() : TSerializeableTypedProperty(Game)
{} {}
public: public:
virtual void PostInitialize() virtual void PostInitialize() override
{ {
IPropertyNew* pX = Create(EPropertyTypeNew::Float, this, mGame, mpScriptTemplate); CreateIntrinsic(EPropertyTypeNew::Float, this, mOffset + 0, "X");
IPropertyNew* pY = Create(EPropertyTypeNew::Float, this, mGame, mpScriptTemplate); CreateIntrinsic(EPropertyTypeNew::Float, this, mOffset + 4, "Y");
IPropertyNew* pZ = Create(EPropertyTypeNew::Float, this, mGame, mpScriptTemplate); CreateIntrinsic(EPropertyTypeNew::Float, this, mOffset + 8, "Z");
pX->SetName("X");
pY->SetName("Y");
pZ->SetName("Z");
} }
virtual void SerializeValue(void* pData, IArchive& Arc) const virtual void SerializeValue(void* pData, IArchive& Arc) const

View File

@ -235,7 +235,11 @@ void CPropertyView::CreateContextMenu(const QPoint& rkPos)
mpMenuProperty = pProp; mpMenuProperty = pProp;
QMenu Menu; QMenu Menu;
if (!pProp->IsIntrinsic())
{
Menu.addAction(mpEditTemplateAction); Menu.addAction(mpEditTemplateAction);
}
if (mpEditor->CurrentGame() >= eEchoesDemo) if (mpEditor->CurrentGame() >= eEchoesDemo)
{ {