Added resource metadata files

This commit is contained in:
Aruki 2017-07-02 02:17:04 -06:00
parent 5a398423e1
commit 4b73d0abcc
6 changed files with 139 additions and 23 deletions

Binary file not shown.

View File

@ -9,7 +9,7 @@ bool CAssetNameMap::LoadAssetNames(TString Path /*= ""*/)
if (Reader.IsValid())
{
CAssetID FileIDLength = CAssetID::GameIDLength(Reader.Game());
EIDLength FileIDLength = CAssetID::GameIDLength(Reader.Game());
if (FileIDLength == mIDLength)
{

View File

@ -531,6 +531,10 @@ void CGameExporter::ExportResourceEditorData()
It->Save(true);
else
It->UpdateDependencies();
// Set flags, save metadata
It->SetFlag(eREF_IsRetroResource);
It->SaveMetadata(true);
}
}

View File

@ -19,6 +19,7 @@ CResourceEntry::CResourceEntry(CResourceStore *pStore, const CAssetID& rkID,
, mID(rkID)
, mpDirectory(nullptr)
, mName(rkFilename)
, mMetadataDirty(false)
, mCachedSize(-1)
, mCachedUppercaseName(rkFilename.ToUpper())
{
@ -35,12 +36,87 @@ CResourceEntry::~CResourceEntry()
if (mpDependencies) delete mpDependencies;
}
void CResourceEntry::SerializeCacheData(IArchive& rArc)
bool CResourceEntry::LoadMetadata()
{
ASSERT(!mMetadataDirty);
TString Path = MetadataFilePath();
if (FileUtil::Exists(Path))
{
// Validate file
CFileInStream MetaFile(Path, IOUtil::eBigEndian);
u32 Magic = MetaFile.ReadLong();
if (Magic == FOURCC('META'))
{
CSerialVersion Version(MetaFile);
CBinaryReader Reader(&MetaFile, Version);
SerializeMetadata(Reader);
return true;
}
else
{
Log::Error(Path + ": Failed to load metadata file, invalid magic: " + CFourCC(Magic).ToString());
}
}
return false;
}
bool CResourceEntry::SaveMetadata(bool ForceSave /*= false*/)
{
if (mMetadataDirty || ForceSave)
{
TString Path = MetadataFilePath();
TString Dir = Path.GetFileDirectory();
FileUtil::MakeDirectory(Dir);
CFileOutStream MetaFile(Path, IOUtil::eBigEndian);
if (MetaFile.IsValid())
{
MetaFile.WriteLong(0); // Magic dummy
CSerialVersion Version(IArchive::skCurrentArchiveVersion, 0, Game());
Version.Write(MetaFile);
// Scope the binary writer to ensure it finishes before we go back to write the magic value
{
CBinaryWriter Writer(&MetaFile, Version);
SerializeMetadata(Writer);
}
MetaFile.GoTo(0);
MetaFile.WriteLong(FOURCC('META'));
mMetadataDirty = false;
return true;
}
}
return false;
}
void CResourceEntry::SerializeMetadata(IArchive& rArc)
{
// Serialize ID. If we already have a valid ID then don't allow the file to override it.
CAssetID ID = mID;
rArc << SERIAL("AssetID", ID);
if (rArc.IsReader() && !mID.IsValid())
mID = ID;
// Serialize type
rArc << SERIAL("Type", mpTypeInfo);
// Serialize flags
u32 Flags = mFlags & eREF_SavedFlags;
rArc << SERIAL_AUTO(Flags);
if (rArc.IsReader()) mFlags = Flags & eREF_SavedFlags;
}
void CResourceEntry::SerializeCacheData(IArchive& rArc)
{
// Note: If the dependency tree format is changed this should be adjusted so that
// we regenerate the dependencies from scratch instead of reading the tree if the
// file version number is too low
@ -92,12 +168,12 @@ bool CResourceEntry::HasCookedVersion() const
TString CResourceEntry::RawAssetPath(bool Relative) const
{
return CookedAssetPath(Relative) + ".raw";
return CookedAssetPath(Relative) + ".rsraw";
}
TString CResourceEntry::RawExtension() const
{
return CookedExtension().ToString() + ".raw";
return CookedExtension().ToString() + ".rsraw";
}
TString CResourceEntry::CookedAssetPath(bool Relative) const
@ -113,6 +189,11 @@ CFourCC CResourceEntry::CookedExtension() const
return mpTypeInfo->CookedExtension(Game());
}
TString CResourceEntry::MetadataFilePath(bool Relative) const
{
return CookedAssetPath(Relative) + ".rsmeta";
}
bool CResourceEntry::IsInDirectory(CVirtualDirectory *pDir) const
{
CVirtualDirectory *pParentDir = mpDirectory;
@ -146,7 +227,7 @@ bool CResourceEntry::NeedsRecook() const
// toggled to arbitrarily flag any asset for recook.
if (!HasRawVersion()) return false;
if (!HasCookedVersion()) return true;
if (mFlags.HasFlag(eREF_NeedsRecook)) return true;
if (HasFlag(eREF_NeedsRecook)) return true;
return (FileUtil::LastModifiedTime(CookedAssetPath()) < FileUtil::LastModifiedTime(RawAssetPath()));
}
@ -188,7 +269,7 @@ bool CResourceEntry::Save(bool SkipCacheSave /*= false*/)
return false;
}
mFlags |= eREF_NeedsRecook;
SetFlag(eREF_NeedsRecook);
}
// This resource type doesn't have a raw format; save cooked instead
@ -203,11 +284,10 @@ bool CResourceEntry::Save(bool SkipCacheSave /*= false*/)
}
}
// Resource has been saved; now make sure dependencies, cache data, and packages are all up to date
mFlags |= eREF_HasBeenModified;
// Resource has been saved; now make sure metadata, dependencies, and packages are all up to date
SetFlag(eREF_HasBeenModified);
SaveMetadata();
UpdateDependencies();
mpStore->SetCacheDataDirty();
if (!SkipCacheSave)
{
@ -250,9 +330,9 @@ bool CResourceEntry::Cook()
if (Success)
{
mFlags &= ~eREF_NeedsRecook;
mFlags |= eREF_HasBeenModified;
mpStore->SetCacheDataDirty();
ClearFlag(eREF_NeedsRecook);
SetFlag(eREF_HasBeenModified);
SaveMetadata();
}
return Success;
@ -458,3 +538,21 @@ EGame CResourceEntry::Game() const
{
return mpStore ? mpStore->Game() : eUnknownGame;
}
void CResourceEntry::SetFlag(EResEntryFlag Flag)
{
if (!HasFlag(Flag))
{
mFlags.SetFlag(Flag);
mMetadataDirty = true;
}
}
void CResourceEntry::ClearFlag(EResEntryFlag Flag)
{
if (HasFlag(Flag))
{
mFlags.ClearFlag(Flag);
mMetadataDirty = true;
}
}

View File

@ -17,12 +17,14 @@ class CDependencyTree;
enum EResEntryFlag
{
eREF_NeedsRecook = 0x00000001, // Resource has been updated but not recooked
// UNUSED = 0x00000002,
eREF_IsRetroResource = 0x00000002, // Resource is from the original game, not user-created
eREF_Hidden = 0x00000004, // Resource is hidden, doesn't show up in resource browser
eREF_HasBeenModified = 0x00000008, // Resource has been modified and resaved by the user
eREF_IsUserResource = 0x00000010, // Resource was created by the user (i.e. isn't a Retro Studios asset)
eREF_AutoResName = 0x00000010, // Resource name is auto-generated
eREF_AutoResDir = 0x00000020, // Resource directory name is auto-generated
// Flags that save to the cache file
eREF_SavedFlags = eREF_NeedsRecook | eREF_Hidden | eREF_HasBeenModified | eREF_IsUserResource
eREF_SavedFlags = eREF_NeedsRecook | eREF_IsRetroResource | eREF_Hidden | eREF_HasBeenModified |
eREF_AutoResName | eREF_AutoResDir
};
DECLARE_FLAGS(EResEntryFlag, FResEntryFlags)
@ -37,6 +39,7 @@ class CResourceEntry
TString mName;
FResEntryFlags mFlags;
mutable bool mMetadataDirty;
mutable u64 mCachedSize;
mutable TString mCachedUppercaseName; // This is used to speed up case-insensitive sorting and filtering.
@ -46,6 +49,9 @@ public:
EResType Type);
~CResourceEntry();
bool LoadMetadata();
bool SaveMetadata(bool ForceSave = false);
void SerializeMetadata(IArchive& rArc);
void SerializeCacheData(IArchive& rArc);
void UpdateDependencies();
@ -55,6 +61,7 @@ public:
TString RawExtension() const;
TString CookedAssetPath(bool Relative = false) const;
CFourCC CookedExtension() const;
TString MetadataFilePath(bool Relative = false) const;
bool IsInDirectory(CVirtualDirectory *pDir) const;
u64 Size() const;
bool NeedsRecook() const;
@ -68,9 +75,14 @@ public:
CGameProject* Project() const;
EGame Game() const;
void SetFlag(EResEntryFlag Flag);
void ClearFlag(EResEntryFlag Flag);
// Accessors
void SetDirty() { mFlags.SetFlag(eREF_NeedsRecook); }
void SetHidden(bool Hidden) { Hidden ? mFlags.SetFlag(eREF_Hidden) : mFlags.ClearFlag(eREF_Hidden); }
inline void SetDirty() { SetFlag(eREF_NeedsRecook); }
inline void SetHidden(bool Hidden) { Hidden ? SetFlag(eREF_Hidden) : ClearFlag(eREF_Hidden); }
inline bool HasFlag(EResEntryFlag Flag) const { return mFlags.HasFlag(Flag); }
inline bool IsHidden() const { return HasFlag(eREF_Hidden); }
inline bool IsLoaded() const { return mpResource != nullptr; }
inline bool IsCategorized() const { return mpDirectory && mpDirectory->FullPath() != "Uncategorized/"; }
@ -85,7 +97,6 @@ public:
inline TString Name() const { return mName; }
inline const TString& UppercaseName() const { return mCachedUppercaseName; }
inline EResType ResourceType() const { return mpTypeInfo->Type(); }
inline bool IsHidden() const { return mFlags.HasFlag(eREF_Hidden); }
protected:
CResource* InternalLoad(IInputStream& rInput);

View File

@ -138,7 +138,7 @@ bool CResourceStore::LoadCacheFile()
// Cache header
CFourCC Magic(CacheFile);
if (Magic != "CACH")
if (Magic != FOURCC('CACH'))
{
Log::Error("Invalid resource cache data magic: " + Magic.ToString());
return false;
@ -183,8 +183,8 @@ bool CResourceStore::SaveCacheFile()
}
// Cache header
CFourCC("CACH").Write(CacheFile);
CSerialVersion Version(0, 0, mGame);
CacheFile.WriteLong(0); // Magic dummy. Magic isn't written until the rest of the file is saved successfully.
CSerialVersion Version(IArchive::skCurrentArchiveVersion, 0, mGame);
Version.Write(CacheFile);
u32 ResCountOffset = CacheFile.Tell();
@ -216,6 +216,8 @@ bool CResourceStore::SaveCacheFile()
CacheFile.Seek(ResCountOffset, SEEK_SET);
CacheFile.WriteLong(ResCount);
CacheFile.Seek(0, SEEK_SET);
CacheFile.WriteLong( FOURCC('CACH') );
mCacheFileDirty = false;
return true;
}
@ -295,7 +297,7 @@ void CResourceStore::ConditionalDeleteDirectory(CVirtualDirectory *pDir)
{
if (pDir->IsEmpty())
{
// If this directory is part of the project, then we should delete the corresponding filesystem directories
// If this directory is part of the project, then we should delete the corresponding filesystem directory
if (pDir->GetRoot() == mpDatabaseRoot && !pDir->IsRoot())
{
FileUtil::DeleteDirectory(ResourcesDir() + pDir->FullPath(), true);
@ -338,6 +340,7 @@ CResourceEntry* CResourceStore::RegisterResource(const CAssetID& rkID, EResType
if (IsValidResourcePath(rkDir, rkName))
{
pEntry = new CResourceEntry(this, rkID, rkDir, rkName, Type);
pEntry->LoadMetadata();
mResourceEntries[rkID] = pEntry;
}