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()) if (Reader.IsValid())
{ {
CAssetID FileIDLength = CAssetID::GameIDLength(Reader.Game()); EIDLength FileIDLength = CAssetID::GameIDLength(Reader.Game());
if (FileIDLength == mIDLength) if (FileIDLength == mIDLength)
{ {

View File

@ -531,6 +531,10 @@ void CGameExporter::ExportResourceEditorData()
It->Save(true); It->Save(true);
else else
It->UpdateDependencies(); 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) , mID(rkID)
, mpDirectory(nullptr) , mpDirectory(nullptr)
, mName(rkFilename) , mName(rkFilename)
, mMetadataDirty(false)
, mCachedSize(-1) , mCachedSize(-1)
, mCachedUppercaseName(rkFilename.ToUpper()) , mCachedUppercaseName(rkFilename.ToUpper())
{ {
@ -35,12 +36,87 @@ CResourceEntry::~CResourceEntry()
if (mpDependencies) delete mpDependencies; 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; u32 Flags = mFlags & eREF_SavedFlags;
rArc << SERIAL_AUTO(Flags); rArc << SERIAL_AUTO(Flags);
if (rArc.IsReader()) mFlags = Flags & eREF_SavedFlags; 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 // 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 // we regenerate the dependencies from scratch instead of reading the tree if the
// file version number is too low // file version number is too low
@ -92,12 +168,12 @@ bool CResourceEntry::HasCookedVersion() const
TString CResourceEntry::RawAssetPath(bool Relative) const TString CResourceEntry::RawAssetPath(bool Relative) const
{ {
return CookedAssetPath(Relative) + ".raw"; return CookedAssetPath(Relative) + ".rsraw";
} }
TString CResourceEntry::RawExtension() const TString CResourceEntry::RawExtension() const
{ {
return CookedExtension().ToString() + ".raw"; return CookedExtension().ToString() + ".rsraw";
} }
TString CResourceEntry::CookedAssetPath(bool Relative) const TString CResourceEntry::CookedAssetPath(bool Relative) const
@ -113,6 +189,11 @@ CFourCC CResourceEntry::CookedExtension() const
return mpTypeInfo->CookedExtension(Game()); return mpTypeInfo->CookedExtension(Game());
} }
TString CResourceEntry::MetadataFilePath(bool Relative) const
{
return CookedAssetPath(Relative) + ".rsmeta";
}
bool CResourceEntry::IsInDirectory(CVirtualDirectory *pDir) const bool CResourceEntry::IsInDirectory(CVirtualDirectory *pDir) const
{ {
CVirtualDirectory *pParentDir = mpDirectory; CVirtualDirectory *pParentDir = mpDirectory;
@ -146,7 +227,7 @@ bool CResourceEntry::NeedsRecook() const
// toggled to arbitrarily flag any asset for recook. // toggled to arbitrarily flag any asset for recook.
if (!HasRawVersion()) return false; if (!HasRawVersion()) return false;
if (!HasCookedVersion()) return true; if (!HasCookedVersion()) return true;
if (mFlags.HasFlag(eREF_NeedsRecook)) return true; if (HasFlag(eREF_NeedsRecook)) return true;
return (FileUtil::LastModifiedTime(CookedAssetPath()) < FileUtil::LastModifiedTime(RawAssetPath())); return (FileUtil::LastModifiedTime(CookedAssetPath()) < FileUtil::LastModifiedTime(RawAssetPath()));
} }
@ -188,7 +269,7 @@ bool CResourceEntry::Save(bool SkipCacheSave /*= false*/)
return false; return false;
} }
mFlags |= eREF_NeedsRecook; SetFlag(eREF_NeedsRecook);
} }
// This resource type doesn't have a raw format; save cooked instead // 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 // Resource has been saved; now make sure metadata, dependencies, and packages are all up to date
mFlags |= eREF_HasBeenModified; SetFlag(eREF_HasBeenModified);
SaveMetadata();
UpdateDependencies(); UpdateDependencies();
mpStore->SetCacheDataDirty();
if (!SkipCacheSave) if (!SkipCacheSave)
{ {
@ -250,9 +330,9 @@ bool CResourceEntry::Cook()
if (Success) if (Success)
{ {
mFlags &= ~eREF_NeedsRecook; ClearFlag(eREF_NeedsRecook);
mFlags |= eREF_HasBeenModified; SetFlag(eREF_HasBeenModified);
mpStore->SetCacheDataDirty(); SaveMetadata();
} }
return Success; return Success;
@ -458,3 +538,21 @@ EGame CResourceEntry::Game() const
{ {
return mpStore ? mpStore->Game() : eUnknownGame; 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 enum EResEntryFlag
{ {
eREF_NeedsRecook = 0x00000001, // Resource has been updated but not recooked 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_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_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 // 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) DECLARE_FLAGS(EResEntryFlag, FResEntryFlags)
@ -37,6 +39,7 @@ class CResourceEntry
TString mName; TString mName;
FResEntryFlags mFlags; FResEntryFlags mFlags;
mutable bool mMetadataDirty;
mutable u64 mCachedSize; mutable u64 mCachedSize;
mutable TString mCachedUppercaseName; // This is used to speed up case-insensitive sorting and filtering. mutable TString mCachedUppercaseName; // This is used to speed up case-insensitive sorting and filtering.
@ -46,6 +49,9 @@ public:
EResType Type); EResType Type);
~CResourceEntry(); ~CResourceEntry();
bool LoadMetadata();
bool SaveMetadata(bool ForceSave = false);
void SerializeMetadata(IArchive& rArc);
void SerializeCacheData(IArchive& rArc); void SerializeCacheData(IArchive& rArc);
void UpdateDependencies(); void UpdateDependencies();
@ -55,6 +61,7 @@ public:
TString RawExtension() const; TString RawExtension() const;
TString CookedAssetPath(bool Relative = false) const; TString CookedAssetPath(bool Relative = false) const;
CFourCC CookedExtension() const; CFourCC CookedExtension() const;
TString MetadataFilePath(bool Relative = false) const;
bool IsInDirectory(CVirtualDirectory *pDir) const; bool IsInDirectory(CVirtualDirectory *pDir) const;
u64 Size() const; u64 Size() const;
bool NeedsRecook() const; bool NeedsRecook() const;
@ -68,9 +75,14 @@ public:
CGameProject* Project() const; CGameProject* Project() const;
EGame Game() const; EGame Game() const;
void SetFlag(EResEntryFlag Flag);
void ClearFlag(EResEntryFlag Flag);
// Accessors // Accessors
void SetDirty() { mFlags.SetFlag(eREF_NeedsRecook); } inline void SetDirty() { SetFlag(eREF_NeedsRecook); }
void SetHidden(bool Hidden) { Hidden ? mFlags.SetFlag(eREF_Hidden) : mFlags.ClearFlag(eREF_Hidden); } 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 IsLoaded() const { return mpResource != nullptr; }
inline bool IsCategorized() const { return mpDirectory && mpDirectory->FullPath() != "Uncategorized/"; } inline bool IsCategorized() const { return mpDirectory && mpDirectory->FullPath() != "Uncategorized/"; }
@ -85,7 +97,6 @@ public:
inline TString Name() const { return mName; } inline TString Name() const { return mName; }
inline const TString& UppercaseName() const { return mCachedUppercaseName; } inline const TString& UppercaseName() const { return mCachedUppercaseName; }
inline EResType ResourceType() const { return mpTypeInfo->Type(); } inline EResType ResourceType() const { return mpTypeInfo->Type(); }
inline bool IsHidden() const { return mFlags.HasFlag(eREF_Hidden); }
protected: protected:
CResource* InternalLoad(IInputStream& rInput); CResource* InternalLoad(IInputStream& rInput);

View File

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