Changed EGame to an enum class. Created NGameList and NPropertyMap to change how templates are managed/saved/loaded. Added support for property map keeping track of ID/type pairs.

This commit is contained in:
Aruki
2018-10-07 17:53:19 -06:00
parent 42d021e757
commit 84d689e104
108 changed files with 1074 additions and 1198 deletions

View File

@@ -1,9 +1,11 @@
#include "CGameTemplate.h"
#include "NPropertyMap.h"
#include "Core/Resource/Factory/CWorldLoader.h"
#include <Common/Log.h>
CGameTemplate::CGameTemplate()
: mFullyLoaded(false)
, mDirty(false)
{
}
@@ -15,35 +17,57 @@ void CGameTemplate::Serialize(IArchive& Arc)
<< SerialParameter("Messages", mMessages);
}
void CGameTemplate::LoadSubTemplates()
void CGameTemplate::Load(const TString& kFilePath)
{
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 CGameTemplate::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);
CXMLReader Reader(kFilePath);
ASSERT(Reader.IsValid());
Path.pTemplate = std::make_shared<CScriptTemplate>(this, Path.ID.ID, Path.Path);
Path.pTemplate->Serialize(Reader);
Path.pTemplate->PostLoad();
mGame = Reader.Game();
Serialize(Reader);
mSourceFile = kFilePath;
mFullyLoaded = true;
// Load all sub-templates
const TString gkGameRoot = GetGameDirectory();
for (auto Iter = mScriptTemplates.begin(); Iter != mScriptTemplates.end(); Iter++)
{
SScriptTemplatePath& ScriptPath = Iter->second;
TString AbsPath = gkGameRoot + ScriptPath.Path;
ScriptPath.pTemplate = std::make_shared<CScriptTemplate>(this, ScriptPath.ID, AbsPath);
}
for (auto Iter = mPropertyTemplates.begin(); Iter != mPropertyTemplates.end(); Iter++)
{
// For properties, remember that property archetypes can reference other archetypes which
// may not be loaded yet.. so if this happens, the referenced property will be loaded,
// meaning property templates can be loaded out of order, so we need to make sure
// that we don't load any template more than once.
SPropertyTemplatePath& PropertyPath = Iter->second;
if (!PropertyPath.pTemplate)
{
Internal_LoadPropertyTemplate(Iter->second);
}
}
}
void CGameTemplate::Save()
{
CXMLWriter Writer(mSourceFile, "Game", 0, mGame);
ASSERT(Writer.IsValid());
Serialize(Writer);
mDirty = false;
}
/** Internal function for loading a property template from a file. */
void CGameTemplate::Internal_LoadPropertyTemplate(SPropertyTemplatePath& Path)
{
if (Path.pTemplate != nullptr) // don't load twice
return;
const TString kGameDir = GetGameDirectory(true);
const TString kGameDir = GetGameDirectory();
const TString kTemplateFilePath = kGameDir + Path.Path;
CXMLReader Reader(kTemplateFilePath);
ASSERT(Reader.IsValid());
@@ -51,44 +75,49 @@ void CGameTemplate::Internal_LoadPropertyTemplate(SPropertyTemplatePath& Path)
Reader << SerialParameter("PropertyArchetype", Path.pTemplate);
ASSERT(Path.pTemplate != nullptr);
Path.pTemplate->SetPropertyFlags( EPropertyFlag::IsArchetype );
Path.pTemplate->Initialize(nullptr, nullptr, 0);
}
void CGameTemplate::SaveSubTemplates()
void CGameTemplate::SaveGameTemplates(bool ForceAll /*= false*/)
{
const TString kGameDir = GetGameDirectory(true);
const TString kGameDir = GetGameDirectory();
if (mDirty || ForceAll)
{
Save();
}
for (auto Iter = mScriptTemplates.begin(); Iter != mScriptTemplates.end(); Iter++)
{
SScriptTemplatePath& Path = Iter->second;
TString OutPath = kGameDir + Path.Path;
FileUtil::MakeDirectory( OutPath.GetFileDirectory() );
CXMLWriter Writer(OutPath, "ScriptObject", 0, Game());
Path.pTemplate->Serialize(Writer);
if( Path.pTemplate )
{
Path.pTemplate->Save(ForceAll);
}
}
for (auto Iter = mPropertyTemplates.begin(); Iter != mPropertyTemplates.end(); Iter++)
{
SPropertyTemplatePath& Path = Iter->second;
TString OutPath = kGameDir + Path.Path;
FileUtil::MakeDirectory( OutPath.GetFileDirectory() );
CXMLWriter Writer(OutPath, "PropertyTemplate", 0, Game());
Writer << SerialParameter("PropertyArchetype", Path.pTemplate);
if( Path.pTemplate )
{
if( ForceAll || Path.pTemplate->IsDirty() )
{
const TString kOutPath = kGameDir + Path.Path;
FileUtil::MakeDirectory( kOutPath.GetFileDirectory() );
CXMLWriter Writer(kOutPath, "PropertyTemplate", 0, Game());
ASSERT(Writer.IsValid());
Writer << SerialParameter("PropertyArchetype", Path.pTemplate);
Path.pTemplate->ClearDirtyFlag();
}
}
}
}
void CGameTemplate::SaveScriptTemplate(CScriptTemplate* pTemplate)
{
ASSERT( pTemplate->GameTemplate() == this );
}
void CGameTemplate::SavePropertyTemplate(IProperty* pProperty)
{
}
u32 CGameTemplate::GameVersion(TString VersionName)
{
return -1;
@@ -162,9 +191,8 @@ SMessage CGameTemplate::MessageByIndex(u32 Index)
IProperty* CGameTemplate::FindPropertyArchetype(const TString& kTypeName)
{
auto Iter = mPropertyTemplates.find(kTypeName);
ASSERT(Iter != mPropertyTemplates.end()); // Requested archetype property does not exist; missing or malformed template
// 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;
@@ -183,200 +211,14 @@ IProperty* CGameTemplate::FindPropertyArchetype(const TString& kTypeName)
return Path.pTemplate.get();
}
TString CGameTemplate::GetGameDirectory(bool Absolute) const
TString CGameTemplate::GetPropertyArchetypeFilePath(const TString& kTypeName)
{
TString Out = mSourceFile.GetFileDirectory();
return Absolute ? "../templates_new/" + Out : Out;
auto Iter = mPropertyTemplates.find(kTypeName);
ASSERT(Iter != mPropertyTemplates.end());
return GetGameDirectory() + Iter->second.Path;
}
// ************ STATIC ************
CGameTemplate* CGameTemplate::GetGameTemplate(EGame Game)
TString CGameTemplate::GetGameDirectory() const
{
auto it = smGameMap.find(Game);
if (it != smGameMap.end())
return it->second;
else
return nullptr;
return mSourceFile.GetFileDirectory();
}
std::list<CGameTemplate*> CGameTemplate::GameTemplateList()
{
std::list<CGameTemplate*> list;
for (auto it = smGameMap.begin(); it != smGameMap.end(); it++)
list.push_back(it->second);
return list;
}
TString CGameTemplate::FindGameName(EGame Game)
{
CGameTemplate *pGame = GetGameTemplate(Game);
return pGame ? pGame->GameName() : "Unknown Game";
}
EGame CGameTemplate::FindGameForName(const TString& rkName)
{
std::list<CGameTemplate*> Games = GameTemplateList();
for (auto It = Games.begin(); It != Games.end(); It++)
{
CGameTemplate *pGame = *It;
if (pGame->GameName() == rkName)
return pGame->Game();
}
return eUnknownGame;
}
TString CGameTemplate::PropertyName(u32 PropertyID)
{
auto it = smPropertyNames.find(PropertyID);
if (it != smPropertyNames.end())
return it->second;
else
return "Unknown";
}
// Removing these functions for now. I'm not sure of the best way to go about implementing them under the new system yet.
u32 CGameTemplate::CreatePropertyID(IProperty* pProp)
{
// MP1 properties don't have IDs so we can use this function to create one to track instances of a particular property.
// To ensure the IDs are unique we'll create a hash using two things: the struct source file and the ID string (relative to the struct).
//
// Note for properties that have accurate names we can apply a CRC32 to the name to generate a hash equivalent to what the hash would
// have been if this were an MP2/3 property. In an ideal world where every property was named, this would be great. However, we have a
// lot of properties that have generic names like "Unknown", and they should be tracked separately as they are in all likelihood
// different properties. So for this reason, we only want to track sub-instances of one property under one ID.
TString IDString = pProp->Archetype()->IDString(true);
TString TemplateFile = pProp->GetTemplateFileName();
CCRC32 Hash;
Hash.Hash(*IDString);
Hash.Hash(*TemplateFile);
return Hash.Digest();
}
void CGameTemplate::AddProperty(IProperty* pProp, const TString& rkTemplateName /*= ""*/)
{
u32 ID;
if (pProp->Game() >= eEchoesDemo)
ID = pProp->ID();
// Use a different ID for MP1
else
{
// For MP1 we only really need to track properties that come from struct templates.
IProperty* pArchetype = pProp->Archetype();
if (!pArchetype ||
pArchetype->ScriptTemplate() != nullptr ||
pArchetype->RootParent()->Type() != EPropertyType::Struct)
return;
ID = CreatePropertyID(pProp);
}
auto it = smIDMap.find(ID);
// Add this property/template to existing ID info
if (it != smIDMap.end())
{
SPropIDInfo& rInfo = it->second;
rInfo.PropertyList.push_back(pProp);
if (!rkTemplateName.IsEmpty())
{
bool NewTemplate = true;
for (u32 iTemp = 0; iTemp < rInfo.XMLList.size(); iTemp++)
{
if (rInfo.XMLList[iTemp] == rkTemplateName)
{
NewTemplate = false;
break;
}
}
if (NewTemplate)
rInfo.XMLList.push_back(rkTemplateName);
}
}
// Create new ID info
else
{
SPropIDInfo Info;
if (!rkTemplateName.IsEmpty()) Info.XMLList.push_back(rkTemplateName);
Info.PropertyList.push_back(pProp);
smIDMap[ID] = Info;
}
}
void CGameTemplate::RenameProperty(IProperty* pProp, const TString& rkNewName)
{
u32 ID = pProp->ID();
if (ID <= 0xFF) ID = CreatePropertyID(pProp);
RenameProperty(ID, rkNewName);
}
void CGameTemplate::RenameProperty(u32 ID, const TString& rkNewName)
{
// Game name list
auto NameIt = smPropertyNames.find(ID);
TString Original;
if (NameIt != smPropertyNames.end())
{
Original = NameIt->second;
smPropertyNames[ID] = rkNewName;
}
// Properties
auto InfoIt = smIDMap.find(ID);
if (InfoIt != smIDMap.end())
{
const SPropIDInfo& rkInfo = InfoIt->second;
for (u32 PropertyIdx = 0; PropertyIdx < rkInfo.PropertyList.size(); PropertyIdx++)
{
if (Original.IsEmpty() || rkInfo.PropertyList[PropertyIdx]->Name() == Original)
rkInfo.PropertyList[PropertyIdx]->SetName(rkNewName);
}
}
}
void CGameTemplate::XMLsUsingID(u32 ID, std::vector<TString>& rOutList)
{
auto InfoIt = smIDMap.find(ID);
if (InfoIt != smIDMap.end())
{
const SPropIDInfo& rkInfo = InfoIt->second;
rOutList = rkInfo.XMLList;
}
}
const std::vector<IProperty*>* CGameTemplate::TemplatesWithMatchingID(IProperty* pProp)
{
u32 ID = pProp->ID();
if (ID <= 0xFF) ID = CreatePropertyID(pProp);
auto InfoIt = smIDMap.find(ID);
if (InfoIt != smIDMap.end())
{
const SPropIDInfo& rkInfo = InfoIt->second;
return &rkInfo.PropertyList;
}
return nullptr;
}
std::map<u32, CGameTemplate::SPropIDInfo> CGameTemplate::smIDMap;
std::map<EGame, CGameTemplate*> CGameTemplate::smGameMap;
std::map<u32, TString> CGameTemplate::smPropertyNames;
u32 CGameTemplate::smGameListVersion;

View File

@@ -28,84 +28,85 @@ struct SObjId
void Serialize(IArchive& Arc)
{
if (Arc.Game() <= ePrime)
if (Arc.Game() <= EGame::Prime)
Arc.SerializePrimitive(ID, SH_HexDisplay);
else
Arc.SerializePrimitive(ID_4CC, 0);
}
};
/** Struct holding a reference to a script object template */
struct SScriptTemplatePath
{
/** Script object ID */
SObjId ID;
/** File path to the template file, relative to the game directory */
TString Path;
/** Template in memory */
std::shared_ptr<CScriptTemplate> pTemplate;
/** Constructor */
SScriptTemplatePath()
: ID(0)
{}
SScriptTemplatePath(u32 InID, const TString& kInPath, CScriptTemplate* pInTemplate)
: ID(InID)
, Path(kInPath)
, pTemplate( std::shared_ptr<CScriptTemplate>(pInTemplate) )
{}
SScriptTemplatePath(const CFourCC& kInID, const TString& kInPath, CScriptTemplate* pInTemplate)
: ID(kInID)
, Path(kInPath)
, pTemplate( std::shared_ptr<CScriptTemplate>(pInTemplate) )
{}
/** Serializer */
void Serialize(IArchive& Arc)
{
Arc << SerialParameter("ID", ID, SH_Attribute)
<< SerialParameter("Path", Path, SH_Attribute);
}
};
/** Struct holding a reference to a property template */
struct SPropertyTemplatePath
{
/** File path to the template file, relative to the game directory */
TString Path;
/** Template in memory */
std::shared_ptr<IProperty> pTemplate;
/** Constructor */
SPropertyTemplatePath()
{}
SPropertyTemplatePath(const TString& kInPath, IProperty* pInTemplate)
: Path(kInPath)
, pTemplate( std::shared_ptr<IProperty>(pInTemplate) )
{}
/** Serializer */
void Serialize(IArchive& Arc)
{
Arc << SerialParameter("Path", Path, SH_Attribute);
}
};
/** CGameTemplate - Per-game template data */
class CGameTemplate
{
friend class CTemplateLoader;
friend class CTemplateWriter;
/** Struct holding a reference to a script object template */
struct SScriptTemplatePath
{
/** Script object ID */
SObjId ID;
/** File path to the template file, relative to the game directory */
TString Path;
/** Template in memory */
std::shared_ptr<CScriptTemplate> pTemplate;
/** Constructor */
SScriptTemplatePath()
: ID(0)
{}
SScriptTemplatePath(u32 InID, const TString& kInPath, CScriptTemplate* pInTemplate)
: ID(InID)
, Path(kInPath)
, pTemplate( std::shared_ptr<CScriptTemplate>(pInTemplate) )
{}
SScriptTemplatePath(const CFourCC& kInID, const TString& kInPath, CScriptTemplate* pInTemplate)
: ID(kInID)
, Path(kInPath)
, pTemplate( std::shared_ptr<CScriptTemplate>(pInTemplate) )
{}
/** Serializer */
void Serialize(IArchive& Arc)
{
Arc << SerialParameter("ID", ID, SH_Attribute)
<< SerialParameter("Path", Path, SH_Attribute);
}
};
/** Struct holding a reference to a property template */
struct SPropertyTemplatePath
{
/** File path to the template file, relative to the game directory */
TString Path;
/** Template in memory */
std::shared_ptr<IProperty> pTemplate;
/** Constructor */
SPropertyTemplatePath()
{}
SPropertyTemplatePath(const TString& kInPath, IProperty* pInTemplate)
: Path(kInPath)
, pTemplate( std::shared_ptr<IProperty>(pInTemplate) )
{}
/** Serializer */
void Serialize(IArchive& Arc)
{
Arc << SerialParameter("Path", Path, SH_Attribute);
}
};
EGame mGame;
TString mGameName;
TString mSourceFile;
bool mFullyLoaded;
bool mDirty;
/** Template arrays */
std::map<SObjId, SScriptTemplatePath> mScriptTemplates;
@@ -114,26 +115,16 @@ class CGameTemplate
std::map<SObjId, TString> mStates;
std::map<SObjId, TString> mMessages;
struct SPropIDInfo
{
std::vector<TString> XMLList; // List of script/struct templates that use this ID
std::vector<IProperty*> PropertyList; // List of all properties that use this ID
};
static std::map<u32, SPropIDInfo> smIDMap;
static std::map<EGame, CGameTemplate*> smGameMap;
static std::map<u32, TString> smPropertyNames;
static u32 smGameListVersion;
void Internal_LoadScriptTemplate(SScriptTemplatePath& Path);
/** Internal function for loading a property template from a file. */
void Internal_LoadPropertyTemplate(SPropertyTemplatePath& Path);
public:
CGameTemplate();
void Serialize(IArchive& Arc);
void LoadSubTemplates();
void SaveSubTemplates();
void SaveScriptTemplate(CScriptTemplate* pTemplate);
void SavePropertyTemplate(IProperty* pProperty);
void Load(const TString& kFilePath);
void Save();
void SaveGameTemplates(bool ForceAll = false);
u32 GameVersion(TString VersionName);
CScriptTemplate* TemplateByID(u32 ObjectID);
CScriptTemplate* TemplateByID(const CFourCC& ObjectID);
@@ -145,28 +136,15 @@ public:
SMessage MessageByID(const CFourCC& MessageID);
SMessage MessageByIndex(u32 Index);
IProperty* FindPropertyArchetype(const TString& kTypeName);
TString GetGameDirectory(bool Absolute = false) const;
TString GetPropertyArchetypeFilePath(const TString& kTypeName);
TString GetGameDirectory() const;
// Inline Accessors
inline EGame Game() const { return mGame; }
inline TString GameName() const { return mGameName; }
inline u32 NumScriptTemplates() const { return mScriptTemplates.size(); }
inline u32 NumStates() const { return mStates.size(); }
inline u32 NumMessages() const { return mMessages.size(); }
inline bool IsLoadedSuccessfully() { return mFullyLoaded; }
// Static
static CGameTemplate* GetGameTemplate(EGame Game);
static std::list<CGameTemplate*> GameTemplateList();
static TString FindGameName(EGame Game);
static EGame FindGameForName(const TString& rkName);
static TString PropertyName(u32 PropertyID);
static u32 CreatePropertyID(IProperty *pTemp);
static void AddProperty(IProperty *pTemp, const TString& rkTemplateName = "");
static void RenameProperty(IProperty *pTemp, const TString& rkNewName);
static void RenameProperty(u32 ID, const TString& rkNewName);
static void XMLsUsingID(u32 ID, std::vector<TString>& rOutList);
static const std::vector<IProperty*>* TemplatesWithMatchingID(IProperty *pTemp);
};
#endif // CGAMETEMPLATE_H

View File

@@ -24,7 +24,7 @@ struct SState
void Serialize(IArchive& Arc)
{
if (Arc.Game() <= ePrime)
if (Arc.Game() <= EGame::Prime)
Arc << SerialParameter("ID", ID, SH_Attribute | SH_HexDisplay);
else
Arc << SerialParameter("ID", ID_4CC, SH_Attribute);
@@ -51,7 +51,7 @@ struct SMessage
void Serialize(IArchive& Arc)
{
if (Arc.Game() <= ePrime)
if (Arc.Game() <= EGame::Prime)
Arc << SerialParameter("ID", ID, SH_Attribute | SH_HexDisplay);
else
Arc << SerialParameter("ID", ID_4CC, SH_Attribute);

View File

@@ -13,6 +13,7 @@ CScriptTemplate::CScriptTemplate(CGameTemplate *pGame)
: mpGame(pGame)
, mpProperties(nullptr)
, mVisible(true)
, mDirty(false)
, mpNameProperty(nullptr)
, mpPositionProperty(nullptr)
, mpRotationProperty(nullptr)
@@ -42,7 +43,23 @@ CScriptTemplate::CScriptTemplate(CGameTemplate* pInGame, u32 InObjectID, const T
, mpActiveProperty(nullptr)
, mpLightParametersProperty(nullptr)
, mVisible(true)
, mDirty(false)
{
// Load
CXMLReader Reader(kInFilePath);
ASSERT(Reader.IsValid());
Serialize(Reader);
// Post load initialization
mSourceFile = kInFilePath;
mpProperties->Initialize(nullptr, this, 0);
if (!mNameIDString.IsEmpty()) mpNameProperty = TPropCast<CStringProperty>( mpProperties->ChildByIDString(mNameIDString) );
if (!mPositionIDString.IsEmpty()) mpPositionProperty = TPropCast<CVectorProperty>( mpProperties->ChildByIDString(mPositionIDString) );
if (!mRotationIDString.IsEmpty()) mpRotationProperty = TPropCast<CVectorProperty>( mpProperties->ChildByIDString(mRotationIDString) );
if (!mScaleIDString.IsEmpty()) mpScaleProperty = TPropCast<CVectorProperty>( mpProperties->ChildByIDString(mScaleIDString) );
if (!mActiveIDString.IsEmpty()) mpActiveProperty = TPropCast<CBoolProperty>( mpProperties->ChildByIDString(mActiveIDString) );
if (!mLightParametersIDString.IsEmpty()) mpLightParametersProperty = TPropCast<CStructProperty>( mpProperties->ChildByIDString(mLightParametersIDString) );
}
CScriptTemplate::~CScriptTemplate()
@@ -77,16 +94,15 @@ void CScriptTemplate::Serialize(IArchive& Arc)
<< SerialParameter("VolumeConditions", mVolumeConditions, SH_Optional);
}
void CScriptTemplate::PostLoad()
void CScriptTemplate::Save(bool Force)
{
mpProperties->Initialize(nullptr, this, 0);
if (!mNameIDString.IsEmpty()) mpNameProperty = TPropCast<CStringProperty>( mpProperties->ChildByIDString(mNameIDString) );
if (!mPositionIDString.IsEmpty()) mpPositionProperty = TPropCast<CVectorProperty>( mpProperties->ChildByIDString(mPositionIDString) );
if (!mRotationIDString.IsEmpty()) mpRotationProperty = TPropCast<CVectorProperty>( mpProperties->ChildByIDString(mRotationIDString) );
if (!mScaleIDString.IsEmpty()) mpScaleProperty = TPropCast<CVectorProperty>( mpProperties->ChildByIDString(mScaleIDString) );
if (!mActiveIDString.IsEmpty()) mpActiveProperty = TPropCast<CBoolProperty>( mpProperties->ChildByIDString(mActiveIDString) );
if (!mLightParametersIDString.IsEmpty()) mpLightParametersProperty = TPropCast<CStructProperty>( mpProperties->ChildByIDString(mLightParametersIDString) );
if (mDirty || Force)
{
CXMLWriter Writer(mSourceFile, "ScriptObject", 0, mpGame->Game());
ASSERT(Writer.IsValid());
Serialize(Writer);
mDirty = false;
}
}
EGame CScriptTemplate::Game() const

View File

@@ -127,6 +127,7 @@ private:
};
std::vector<SVolumeCondition> mVolumeConditions;
bool mVisible;
bool mDirty;
public:
// Default constructor. Don't use. This is only here so the serializer doesn't complain
@@ -137,7 +138,7 @@ public:
CScriptTemplate(CGameTemplate* pGame, u32 ObjectID, const TString& kFilePath);
~CScriptTemplate();
void Serialize(IArchive& rArc);
void PostLoad();
void Save(bool Force = false);
EGame Game() const;
// Property Fetching
@@ -168,6 +169,8 @@ public:
inline CStructProperty* LightParametersProperty() const { return mpLightParametersProperty; }
inline void SetVisible(bool Visible) { mVisible = Visible; }
inline void MarkDirty() { mDirty = true; }
inline bool IsDirty() const { return mDirty; }
// Object Tracking
u32 NumObjects() const;

View File

@@ -22,7 +22,7 @@ public:
virtual const char* HashableTypeName() const
{
return (Game() <= eEchoes ? "AnimationSet" : "CharacterAnimationSet");
return (Game() <= EGame::Echoes ? "AnimationSet" : "CharacterAnimationSet");
}
virtual CAnimationParameters GetSerializationDefaultValue()

View File

@@ -109,11 +109,6 @@ public:
{
TTypedProperty::Serialize(rArc);
rArc << SerialParameter("ItemArchetype", mpItemArchetype);
if (rArc.IsReader())
{
mpItemArchetype->SetPropertyFlags( EPropertyFlag::IsArrayArchetype );
}
}
virtual void SerializeValue(void* pData, IArchive& Arc) const

View File

@@ -42,9 +42,6 @@ class TEnumPropertyBase : public TSerializeableTypedProperty<s32, TypeEnum>
};
std::vector<SEnumValue> mValues;
/** XML template file that this enum originated from; for archetypes */
TString mSourceFile;
protected:
/** Constructor */
TEnumPropertyBase(EGame Game)
@@ -66,7 +63,7 @@ public:
TTypedProperty::Serialize(rArc);
TEnumPropertyBase* pArchetype = static_cast<TEnumPropertyBase*>(mpArchetype);
u32 DefaultValueFlags = SH_HexDisplay | (pArchetype || Game() <= ePrime ? SH_Optional : 0);
u32 DefaultValueFlags = SH_HexDisplay | (pArchetype || Game() <= EGame::Prime ? SH_Optional : 0);
rArc << SerialParameter("DefaultValue", mDefaultValue, DefaultValueFlags, pArchetype ? pArchetype->mDefaultValue : 0);
if (!pArchetype || !rArc.CanSkipParameters() || mValues != pArchetype->mValues)
@@ -87,12 +84,6 @@ public:
mValues = pOtherEnum->mValues;
}
virtual TString GetTemplateFileName()
{
ASSERT(IsArchetype() || mpArchetype);
return IsArchetype() ? mSourceFile : mpArchetype->GetTemplateFileName();
}
void AddValue(TString ValueName, u32 ValueID)
{
mValues.push_back( SEnumValue(ValueName, ValueID) );

View File

@@ -37,12 +37,6 @@ void CFlagsProperty::InitFromArchetype(IProperty* pOther)
mAllFlags = pOtherFlags->mAllFlags;
}
TString CFlagsProperty::GetTemplateFileName()
{
ASSERT(IsArchetype() || mpArchetype);
return IsArchetype() ? mSourceFile : mpArchetype->GetTemplateFileName();
}
/**
* Checks whether there are any unrecognized bits toggled on in the property value.
* Returns the mask of any invalid bits. If all bits are valid, returns 0.

View File

@@ -35,9 +35,6 @@ class CFlagsProperty : public TSerializeableTypedProperty<u32, EPropertyType::Fl
std::vector<SBitFlag> mBitFlags;
u32 mAllFlags;
/** XML template file that this enum originated from; for archetypes */
TString mSourceFile;
CFlagsProperty(EGame Game)
: TSerializeableTypedProperty(Game)
, mAllFlags(0)
@@ -65,7 +62,6 @@ public:
virtual void PostInitialize();
virtual void SerializeValue(void* pData, IArchive& rArc) const;
virtual void InitFromArchetype(IProperty* pOther);
virtual TString GetTemplateFileName();
/**
* Checks whether there are any unrecognized bits toggled on in the property value.

View File

@@ -149,6 +149,8 @@ void CPropertyNameGenerator::Generate(const SPropertyNameGenerationParameters& r
FullHash.Hash( *rkParams.TypeNames[TypeIdx] );
u32 PropertyID = FullHash.Digest();
//@FIXME
#if 0
// Check if this hash is a property ID - it's valid if there are any XMLs using this ID
SGeneratedPropertyName PropertyName;
CGameTemplate::XMLsUsingID(PropertyID, PropertyName.XmlList);
@@ -202,6 +204,7 @@ void CPropertyNameGenerator::Generate(const SPropertyNameGenerationParameters& r
Log::Write(LogMsg);
}
}
#endif
}
// Every 250 tests, check with the progress notifier. Update the progress

View File

@@ -6,6 +6,14 @@ EPropertyType CStructProperty::Type() const
return EPropertyType::Struct;
}
void CStructProperty::PostInitialize()
{
IProperty::PostInitialize();
// All structs should have an archetype.
ASSERT( IsRootParent() || mpArchetype != nullptr );
}
u32 CStructProperty::DataSize() const
{
if (!mChildren.empty())
@@ -74,12 +82,9 @@ void CStructProperty::Serialize(IArchive& rArc)
bool Atomic = IsAtomic();
rArc << SerialParameter("Atomic", Atomic, SH_Optional, false);
if (rArc.IsReader())
if (rArc.IsReader() && Atomic)
{
if (Atomic)
mFlags.SetFlag(EPropertyFlag::IsAtomic);
else
mFlags.ClearFlag(EPropertyFlag::IsAtomic);
mFlags.SetFlag(EPropertyFlag::IsAtomic);
}
// Serialize archetype
@@ -187,9 +192,3 @@ bool CStructProperty::ShouldSerialize() const
return false;
}
TString CStructProperty::GetTemplateFileName()
{
ASSERT(IsArchetype() || mpArchetype);
return IsArchetype() ? mTemplateFileName : mpArchetype->GetTemplateFileName();
}

View File

@@ -13,15 +13,13 @@ public:
typedef void* ValueType;
protected:
/** For archetypes, the filename of the template XML file. */
TString mTemplateFileName;
CStructProperty(EGame Game)
: IProperty(Game)
{}
public:
virtual EPropertyType Type() const;
virtual void PostInitialize();
virtual u32 DataSize() const;
virtual u32 DataAlignment() const;
virtual void Construct(void* pData) const;
@@ -33,7 +31,6 @@ public:
virtual void SerializeValue(void* pData, IArchive& Arc) const;
virtual void InitFromArchetype(IProperty* pOther);
virtual bool ShouldSerialize() const;
virtual TString GetTemplateFileName();
inline static EPropertyType StaticType() { return EPropertyType::Struct; }
};

View File

@@ -7,6 +7,8 @@
#include "Core/Resource/Script/CGameTemplate.h"
#include "Core/Resource/Script/CScriptTemplate.h"
#include "Core/Resource/Script/NGameList.h"
#include "Core/Resource/Script/NPropertyMap.h"
/** IProperty */
IProperty::IProperty(EGame Game)
@@ -25,7 +27,15 @@ IProperty::IProperty(EGame Game)
void IProperty::_ClearChildren()
{
for (int ChildIdx = 0; ChildIdx < mChildren.size(); ChildIdx++)
{
// Unregister children from the name map. This has to be done before actually deleting them.
if (mChildren[ChildIdx]->UsesNameMap())
{
NPropertyMap::UnregisterProperty(mChildren[ChildIdx]);
}
delete mChildren[ChildIdx];
}
mChildren.clear();
}
@@ -35,13 +45,17 @@ IProperty::~IProperty()
// Remove from archetype
if( mpArchetype != nullptr )
{
// If you crash here, it most likely means this property was not added to the archetype's sub-instances list.
NBasics::VectorRemoveOne(mpArchetype->mSubInstances, this);
}
// If this is an archetype, all our sub-instances should have destructed first.
// If this is an archetype, make sure no sub-instances have a reference to us.
if( IsArchetype() )
{
ASSERT(mSubInstances.empty());
for( int SubIdx = 0; SubIdx < mSubInstances.size(); SubIdx++ )
{
mSubInstances[SubIdx]->mpArchetype = nullptr;
}
}
// Delete children
@@ -72,7 +86,7 @@ void IProperty::Serialize(IArchive& rArc)
if (rArc.IsReader() && !ArchetypeName.IsEmpty())
{
CGameTemplate* pGame = CGameTemplate::GetGameTemplate( Game() );
CGameTemplate* pGame = NGameList::GetGameTemplate( Game() );
IProperty* pArchetype = pGame->FindPropertyArchetype(ArchetypeName);
// The archetype must exist, or else the template file is malformed.
@@ -89,7 +103,7 @@ void IProperty::Serialize(IArchive& rArc)
//
// 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() || IsArrayArchetype() || mID <= 0xFF)
if (rArc.Game() <= EGame::Prime || IsRootParent() || IsArrayArchetype() || mID <= 0xFF)
{
rArc << SerialParameter("Name", mName, mpArchetype ? SH_Optional : 0, mpArchetype ? mpArchetype->mName : "");
}
@@ -107,6 +121,8 @@ void IProperty::InitFromArchetype(IProperty* pOther)
{
//@todo maybe somehow use Serialize for this instead?
mpArchetype = pOther;
mpArchetype->mSubInstances.push_back(this);
mFlags = pOther->mFlags & EPropertyFlag::ArchetypeCopyFlags;
mName = pOther->mName;
mDescription = pOther->mDescription;
@@ -133,24 +149,6 @@ bool IProperty::ShouldSerialize() const
mMaxVersion != mpArchetype->mMaxVersion;
}
TString IProperty::GetTemplateFileName()
{
if (mpScriptTemplate)
{
return mpScriptTemplate->SourceFile();
}
else if (IsArchetype())
{
IProperty* pRootParent = RootParent();
ASSERT(pRootParent != this);
return pRootParent->GetTemplateFileName();
}
else
{
return mpArchetype->GetTemplateFileName();
}
}
void IProperty::Initialize(IProperty* pInParent, CScriptTemplate* pInTemplate, u32 InOffset)
{
// Make sure we only get initialized once.
@@ -161,12 +159,6 @@ void IProperty::Initialize(IProperty* pInParent, CScriptTemplate* pInTemplate, u
mOffset = InOffset;
mpScriptTemplate = pInTemplate;
// Look up property name if needed.
if (Game() >= eEchoesDemo && !IsRootParent() && !IsIntrinsic() && !mpParent->IsAtomic() && !IsArrayArchetype())
{
mName = CGameTemplate::PropertyName(mID);
}
// Set any fields dependent on the parent...
if (mpParent)
{
@@ -180,6 +172,21 @@ void IProperty::Initialize(IProperty* pInParent, CScriptTemplate* pInTemplate, u
{
mpPointerParent = mpParent->mpPointerParent;
}
if (mpParent->Type() == EPropertyType::Array)
{
mFlags |= EPropertyFlag::IsArrayArchetype;
}
}
else if (!mpScriptTemplate)
{
mFlags |= EPropertyFlag::IsArchetype;
}
// Register the property if needed.
if (UsesNameMap())
{
NPropertyMap::RegisterProperty(this);
}
// Allow subclasses to handle any initialization tasks
@@ -259,7 +266,21 @@ IProperty* IProperty::ChildByIDString(const TIDString& rkIdString)
}
}
bool IProperty::ShouldCook(void*pPropertyData) const
TString IProperty::GetTemplateFileName()
{
if (mpScriptTemplate)
{
return mpScriptTemplate->SourceFile();
}
else
{
CGameTemplate* pGameTemplate = NGameList::GetGameTemplate(Game());
IProperty* pTemplateRoot = (IsArchetype() ? RootParent() : mpArchetype);
return pGameTemplate->GetPropertyArchetypeFilePath( pTemplateRoot->Name() );
}
}
bool IProperty::ShouldCook(void* pPropertyData) const
{
switch (mCookPreference)
{
@@ -270,7 +291,7 @@ bool IProperty::ShouldCook(void*pPropertyData) const
return false;
default:
return (Game() < eReturns ? true : !MatchesDefault(pPropertyData));
return (Game() < EGame::DKCReturns ? true : !MatchesDefault(pPropertyData));
}
}
@@ -278,28 +299,67 @@ void IProperty::SetName(const TString& rkNewName)
{
mName = rkNewName;
mFlags.ClearFlag(EPropertyFlag::HasCachedNameCheck);
// in Echoes and on, since property names are referenced by ID, renaming a property
// doesn't directly affect the serialized data, so it doesn't need to be flagged dirty
if (mGame <= EGame::Prime)
{
MarkDirty();
}
}
void IProperty::SetDescription(const TString& rkNewDescription)
{
mDescription = rkNewDescription;
MarkDirty();
}
void IProperty::SetSuffix(const TString& rkNewSuffix)
{
mSuffix = rkNewSuffix;
MarkDirty();
}
void IProperty::SetPropertyFlags(FPropertyFlags FlagsToSet)
void IProperty::MarkDirty()
{
mFlags |= FlagsToSet;
// This property is either part of a script template, or a property archetype.
// Figure out which one, set the dirty flag as needed
if (mpScriptTemplate)
{
mpScriptTemplate->MarkDirty();
}
else
{
RootParent()->mFlags |= EPropertyFlag::IsDirty;
}
}
void IProperty::ClearDirtyFlag()
{
if (!mpScriptTemplate)
{
RootParent()->mFlags &= ~EPropertyFlag::IsDirty;
}
}
bool IProperty::UsesNameMap()
{
return Game() >= EGame::EchoesDemo &&
!IsRootParent() &&
!IsIntrinsic() &&
!mpParent->IsAtomic() && // Atomic properties can use the name map, but their children shouldn't
!IsArrayArchetype();
}
bool IProperty::HasAccurateName()
{
// Exceptions for the three hardcoded 4CC property IDs
if (mID == FOURCC('XFRM') || mID == FOURCC('INAM') || mID == FOURCC('ACTV'))
if (mID == FOURCC('XFRM') ||
mID == FOURCC('INAM') ||
mID == FOURCC('ACTV'))
{
return true;
}
// Children of atomic properties defer to parents. Intrinsic properties and array archetypes also defer to parents.
if ( (mpParent && mpParent->IsAtomic()) || IsIntrinsic() || IsArrayArchetype() )
@@ -374,7 +434,6 @@ IProperty* IProperty::CreateCopy(IProperty* pArchetype)
{
IProperty* pOut = Create(pArchetype->Type(), pArchetype->mGame);
pOut->InitFromArchetype(pArchetype);
pArchetype->mSubInstances.push_back(pOut);
return pOut;
}

View File

@@ -29,6 +29,8 @@ enum class EPropertyFlag : u32
IsAtomic = 0x8,
/** This is a property of a C++ class, not a script object */
IsIntrinsic = 0x10,
/** Property has been modified, and needs to be resaved. Only valid on archetypes */
IsDirty = 0x20,
/** We have cached whether the property name is correct */
HasCachedNameCheck = 0x40000000,
/** The name of the property is a match for the property ID hash */
@@ -185,18 +187,20 @@ public:
virtual void Serialize(IArchive& rArc);
virtual void InitFromArchetype(IProperty* pOther);
virtual bool ShouldSerialize() const;
virtual TString GetTemplateFileName();
/** Utility methods */
void Initialize(IProperty* pInParent, CScriptTemplate* pInTemplate, u32 InOffset);
void* RawValuePtr(void* pData) const;
IProperty* ChildByID(u32 ID) const;
IProperty* ChildByIDString(const TIDString& rkIdString);
TString GetTemplateFileName();
bool ShouldCook(void* pPropertyData) const;
void SetName(const TString& rkNewName);
void SetDescription(const TString& rkNewDescription);
void SetSuffix(const TString& rkNewSuffix);
void SetPropertyFlags(FPropertyFlags FlagsToSet);
void MarkDirty();
void ClearDirtyFlag();
bool UsesNameMap();
bool HasAccurateName();
/** Accessors */
@@ -219,6 +223,7 @@ public:
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 IsDirty() const { return mFlags.HasFlag(EPropertyFlag::IsDirty); }
inline bool IsRootParent() const { return mpParent == nullptr; }
/** Create */
@@ -337,7 +342,7 @@ protected:
}
public:
virtual EPropertyType Type() const { return PropEnum; }
virtual EPropertyType Type() const { return PropEnum; }
virtual u32 DataSize() const { return sizeof(PropType); }
virtual u32 DataAlignment() const { return alignof(PropType); }
virtual void Construct(void* pData) const { new(ValuePtr(pData)) PropType(mDefaultValue); }
@@ -400,7 +405,7 @@ public:
// on property types that don't have default values in the game executable.
bool MakeOptional = false;
if (Game() <= ePrime || pArchetype != nullptr)
if (Game() <= EGame::Prime || pArchetype != nullptr)
{
MakeOptional = true;
}