mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-16 08:27:01 +00:00
Added support for deleting resources + minor fixes
This commit is contained in:
@@ -297,6 +297,15 @@ CGameProject* CGameProject::LoadProject(const TString& rkProjPath, IProgressNoti
|
||||
gpResourceStore = pOldStore;
|
||||
}
|
||||
|
||||
// Create hidden files directory, if needed
|
||||
TString HiddenDir = pProj->HiddenFilesDir();
|
||||
|
||||
if (!FileUtil::Exists(HiddenDir))
|
||||
{
|
||||
FileUtil::MakeDirectory(HiddenDir);
|
||||
FileUtil::MarkHidden(HiddenDir, true);
|
||||
}
|
||||
|
||||
pProj->mpAudioManager->LoadAssets();
|
||||
pProj->mpTweakManager->LoadTweaks();
|
||||
return pProj;
|
||||
|
||||
@@ -84,6 +84,7 @@ public:
|
||||
// Directory Handling
|
||||
inline TString ProjectRoot() const { return mProjectRoot; }
|
||||
inline TString ProjectPath() const { return mProjectRoot + FileUtil::SanitizeName(mProjectName, false) + ".prj"; }
|
||||
inline TString HiddenFilesDir() const { return mProjectRoot + ".project/"; }
|
||||
inline TString DiscDir(bool Relative) const { return Relative ? "Disc/" : mProjectRoot + "Disc/"; }
|
||||
inline TString PackagesDir(bool Relative) const { return Relative ? "Packages/" : mProjectRoot + "Packages/"; }
|
||||
inline TString ResourcesDir(bool Relative) const { return Relative ? "Resources/" : mProjectRoot + "Resources/"; }
|
||||
|
||||
@@ -113,6 +113,9 @@ bool CResourceEntry::LoadMetadata()
|
||||
|
||||
bool CResourceEntry::SaveMetadata(bool ForceSave /*= false*/)
|
||||
{
|
||||
// Make sure we aren't saving a deleted resource
|
||||
ASSERT( !HasFlag(EResEntryFlag::MarkedForDeletion) );
|
||||
|
||||
if (mMetadataDirty || ForceSave)
|
||||
{
|
||||
TString Path = MetadataFilePath();
|
||||
@@ -491,6 +494,9 @@ bool CResourceEntry::CanMoveTo(const TString& rkDir, const TString& rkName)
|
||||
|
||||
bool CResourceEntry::MoveAndRename(const TString& rkDir, const TString& rkName, bool IsAutoGenDir /*= false*/, bool IsAutoGenName /*= false*/)
|
||||
{
|
||||
// Make sure we are not moving a deleted resource.
|
||||
ASSERT( !IsMarkedForDeletion() );
|
||||
|
||||
if (!CanMoveTo(rkDir, rkName)) return false;
|
||||
|
||||
// Store old paths
|
||||
@@ -501,7 +507,15 @@ bool CResourceEntry::MoveAndRename(const TString& rkDir, const TString& rkName,
|
||||
TString OldMetaPath = MetadataFilePath();
|
||||
|
||||
// Set new directory and name
|
||||
CVirtualDirectory *pNewDir = mpStore->GetVirtualDirectory(rkDir, true);
|
||||
bool DirAlreadyExisted = true;
|
||||
CVirtualDirectory *pNewDir = mpStore->GetVirtualDirectory(rkDir, false);
|
||||
|
||||
if (!pNewDir)
|
||||
{
|
||||
pNewDir = mpStore->GetVirtualDirectory(rkDir, true);
|
||||
DirAlreadyExisted = false;
|
||||
}
|
||||
|
||||
if (pNewDir == mpDirectory && rkName == mName) return false;
|
||||
|
||||
// Check if we can legally move to this spot
|
||||
@@ -580,7 +594,7 @@ bool CResourceEntry::MoveAndRename(const TString& rkDir, const TString& rkName,
|
||||
// If we succeeded, finish the move
|
||||
if (FSMoveSuccess)
|
||||
{
|
||||
if (mpDirectory != pOldDir)
|
||||
if (mpDirectory != pOldDir && pOldDir != nullptr)
|
||||
{
|
||||
FSMoveSuccess = pOldDir->RemoveChildResource(this);
|
||||
ASSERT(FSMoveSuccess == true); // this shouldn't be able to fail
|
||||
@@ -605,7 +619,11 @@ bool CResourceEntry::MoveAndRename(const TString& rkDir, const TString& rkName,
|
||||
errorf("MOVE FAILED: %s", *MoveFailReason);
|
||||
mpDirectory = pOldDir;
|
||||
mName = OldName;
|
||||
mpStore->ConditionalDeleteDirectory(pNewDir, false);
|
||||
|
||||
if (!DirAlreadyExisted)
|
||||
{
|
||||
mpStore->ConditionalDeleteDirectory(pNewDir, true);
|
||||
}
|
||||
|
||||
if (FileUtil::Exists(NewRawPath))
|
||||
FileUtil::MoveFile(NewRawPath, OldRawPath);
|
||||
@@ -627,6 +645,96 @@ bool CResourceEntry::Rename(const TString& rkName, bool IsAutoGenName /*= false*
|
||||
return MoveAndRename(mpDirectory->FullPath(), rkName, false, IsAutoGenName);
|
||||
}
|
||||
|
||||
void CResourceEntry::MarkDeleted(bool InDeleted)
|
||||
{
|
||||
// Flags resource for future deletion. "Deleted" resources remain in memory (which
|
||||
// allows them to easily be un-deleted) but cannot be looked up in the resource
|
||||
// store and will not save back out to the resource database. Their file data is
|
||||
// stored in a temporary directory, which allows them to be moved back if the user
|
||||
// un-does the deletion.
|
||||
if (IsMarkedForDeletion() != InDeleted)
|
||||
{
|
||||
SetFlagEnabled(EResEntryFlag::MarkedForDeletion, InDeleted);
|
||||
|
||||
// Restore old name/directory if un-deleting
|
||||
if (!InDeleted)
|
||||
{
|
||||
// Our directory path is stored in the Name field - see below for explanation
|
||||
int NameEnd = mName.IndexOf('|');
|
||||
ASSERT( NameEnd != -1 );
|
||||
|
||||
TString DirPath = mName.ChopFront(NameEnd + 1);
|
||||
mName = mName.ChopBack( mName.Size() - NameEnd);
|
||||
mpDirectory = mpStore->GetVirtualDirectory( DirPath, true );
|
||||
ASSERT( mpDirectory != nullptr );
|
||||
mpDirectory->AddChild("", this);
|
||||
}
|
||||
|
||||
TString CookedPath = CookedAssetPath();
|
||||
TString RawPath = RawAssetPath();
|
||||
TString MetaPath = MetadataFilePath();
|
||||
|
||||
TString PathBase = mpStore->DeletedResourcePath() + mID.ToString() + ".";
|
||||
TString DelCookedPath = PathBase + CookedExtension().ToString();
|
||||
TString DelRawPath = DelCookedPath + ".rsraw";
|
||||
TString DelMetaPath = DelCookedPath + ".rsmeta";
|
||||
|
||||
// If we are deleting...
|
||||
if (InDeleted)
|
||||
{
|
||||
// Temporarily store our directory path in the name string.
|
||||
// This is a hack, but we can't store the directory pointer because it may have been
|
||||
// deleted and remade by the user by the time the resource is un-deleted, which
|
||||
// means it is not safe to access later. Separating the name and the path with
|
||||
// the '|' character is safe because this character is not allowed in filenames
|
||||
// (which is enforced in FileUtil::IsValidName()).
|
||||
mName = mName + "|" + mpDirectory->FullPath();
|
||||
|
||||
// Remove from parent directory.
|
||||
mpDirectory->RemoveChildResource(this);
|
||||
mpDirectory = nullptr;
|
||||
|
||||
// Move any resource files out of the project into a temporary folder.
|
||||
FileUtil::MakeDirectory(DelMetaPath.GetFileDirectory());
|
||||
|
||||
if (FileUtil::Exists(MetaPath))
|
||||
{
|
||||
FileUtil::MoveFile(MetaPath, DelMetaPath);
|
||||
}
|
||||
if (FileUtil::Exists(RawPath))
|
||||
{
|
||||
FileUtil::MoveFile(RawPath, DelRawPath);
|
||||
}
|
||||
if (FileUtil::Exists(CookedPath))
|
||||
{
|
||||
FileUtil::MoveFile(CookedPath, DelCookedPath);
|
||||
}
|
||||
}
|
||||
// If we are un-deleting...
|
||||
else
|
||||
{
|
||||
// Move any resource files out of the temporary folder back into the project.
|
||||
FileUtil::MakeDirectory(MetaPath.GetFileDirectory());
|
||||
|
||||
if (FileUtil::Exists(DelMetaPath))
|
||||
{
|
||||
FileUtil::MoveFile(DelMetaPath, MetaPath);
|
||||
}
|
||||
if (FileUtil::Exists(DelRawPath))
|
||||
{
|
||||
FileUtil::MoveFile(DelRawPath, RawPath);
|
||||
}
|
||||
if (FileUtil::Exists(DelCookedPath))
|
||||
{
|
||||
FileUtil::MoveFile(DelCookedPath, CookedPath);
|
||||
}
|
||||
}
|
||||
|
||||
mpStore->SetCacheDirty();
|
||||
debugf("%s FOR DELETION: [%s] %s", InDeleted ? "MARKED" : "UNMARKED", *ID().ToString(), *CookedPath.GetFileName());
|
||||
}
|
||||
}
|
||||
|
||||
CGameProject* CResourceEntry::Project() const
|
||||
{
|
||||
return mpStore ? mpStore->Project() : nullptr;
|
||||
|
||||
@@ -21,6 +21,7 @@ enum class EResEntryFlag
|
||||
HasBeenModified = 0x00000008, // Resource has been modified and resaved by the user
|
||||
AutoResName = 0x00000010, // Resource name is auto-generated
|
||||
AutoResDir = 0x00000020, // Resource directory name is auto-generated
|
||||
MarkedForDeletion = 0x00000040, // Resource has been marked for deletion by the user
|
||||
};
|
||||
DECLARE_FLAGS(EResEntryFlag, FResEntryFlags)
|
||||
|
||||
@@ -76,6 +77,7 @@ public:
|
||||
bool MoveAndRename(const TString& rkDir, const TString& rkName, bool IsAutoGenDir = false, bool IsAutoGenName = false);
|
||||
bool Move(const TString& rkDir, bool IsAutoGenDir = false);
|
||||
bool Rename(const TString& rkName, bool IsAutoGenName = false);
|
||||
void MarkDeleted(bool InDeleted);
|
||||
|
||||
CGameProject* Project() const;
|
||||
EGame Game() const;
|
||||
@@ -90,6 +92,7 @@ public:
|
||||
inline void SetHidden(bool Hidden) { SetFlagEnabled(EResEntryFlag::Hidden, Hidden); }
|
||||
inline bool HasFlag(EResEntryFlag Flag) const { return mFlags.HasFlag(Flag); }
|
||||
inline bool IsHidden() const { return HasFlag(EResEntryFlag::Hidden); }
|
||||
inline bool IsMarkedForDeletion() const { return HasFlag(EResEntryFlag::MarkedForDeletion); }
|
||||
|
||||
inline bool IsLoaded() const { return mpResource != nullptr; }
|
||||
inline bool IsCategorized() const { return mpDirectory && !mpDirectory->FullPath().CaseInsensitiveCompare( mpStore->DefaultResourceDirPath() ); }
|
||||
|
||||
@@ -22,12 +22,16 @@ public:
|
||||
|
||||
virtual CResourceEntry* Next()
|
||||
{
|
||||
if (mIter != mpkStore->mResourceEntries.end())
|
||||
do
|
||||
{
|
||||
mpCurEntry = mIter->second;
|
||||
mIter++;
|
||||
if (mIter != mpkStore->mResourceEntries.end())
|
||||
{
|
||||
mpCurEntry = mIter->second;
|
||||
mIter++;
|
||||
}
|
||||
else mpCurEntry = nullptr;
|
||||
}
|
||||
else mpCurEntry = nullptr;
|
||||
while (mpCurEntry && mpCurEntry->IsMarkedForDeletion());
|
||||
|
||||
return mpCurEntry;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,22 @@ bool CResourceStore::SerializeDatabaseCache(IArchive& rArc)
|
||||
{
|
||||
// Serialize resources
|
||||
uint32 ResourceCount = mResourceEntries.size();
|
||||
|
||||
if (rArc.IsWriter())
|
||||
{
|
||||
// Make sure deleted resources aren't included in the count.
|
||||
// We can't use CResourceIterator because it skips MarkedForDeletion resources.
|
||||
for (auto Iter = mResourceEntries.begin(); Iter != mResourceEntries.end(); Iter++)
|
||||
{
|
||||
CResourceEntry* pEntry = Iter->second;
|
||||
|
||||
if (pEntry->IsMarkedForDeletion())
|
||||
{
|
||||
ResourceCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rArc << SerialParameter("ResourceCount", ResourceCount);
|
||||
|
||||
if (rArc.IsReader())
|
||||
@@ -85,10 +101,13 @@ bool CResourceStore::SerializeDatabaseCache(IArchive& rArc)
|
||||
{
|
||||
for (CResourceIterator It(this); It; ++It)
|
||||
{
|
||||
if (rArc.ParamBegin("Resource", 0))
|
||||
if (!It->IsMarkedForDeletion())
|
||||
{
|
||||
It->SerializeEntryInfo(rArc, false);
|
||||
rArc.ParamEnd();
|
||||
if (rArc.ParamBegin("Resource", 0))
|
||||
{
|
||||
It->SerializeEntryInfo(rArc, false);
|
||||
rArc.ParamEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,18 +159,22 @@ bool CResourceStore::LoadDatabaseCache()
|
||||
}
|
||||
else
|
||||
{
|
||||
// Database is succesfully loaded at this point
|
||||
// Database is successfully loaded at this point
|
||||
if (mpProj)
|
||||
{
|
||||
ASSERT(mpProj->Game() == Reader.Game());
|
||||
}
|
||||
|
||||
mGame = Reader.Game();
|
||||
}
|
||||
|
||||
mGame = Reader.Game();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CResourceStore::SaveDatabaseCache()
|
||||
{
|
||||
TString Path = DatabasePath();
|
||||
debugf("Saving database cache...");
|
||||
|
||||
CBasicBinaryWriter Writer(Path, FOURCC('CACH'), 0, mGame);
|
||||
|
||||
@@ -182,6 +205,14 @@ void CResourceStore::SetProject(CGameProject *pProj)
|
||||
mDatabasePath = mpProj->ProjectRoot();
|
||||
mpDatabaseRoot = new CVirtualDirectory(this);
|
||||
mGame = mpProj->Game();
|
||||
|
||||
// Clear deleted files from previous runs
|
||||
TString DeletedPath = DeletedResourcePath();
|
||||
|
||||
if (FileUtil::Exists(DeletedPath))
|
||||
{
|
||||
FileUtil::ClearDirectory(DeletedPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,6 +246,14 @@ void CResourceStore::CloseProject()
|
||||
It = mResourceEntries.erase(It);
|
||||
}
|
||||
|
||||
// Clear deleted files from previous runs
|
||||
TString DeletedPath = DeletedResourcePath();
|
||||
|
||||
if (FileUtil::Exists(DeletedPath))
|
||||
{
|
||||
FileUtil::ClearDirectory(DeletedPath);
|
||||
}
|
||||
|
||||
delete mpDatabaseRoot;
|
||||
mpDatabaseRoot = nullptr;
|
||||
mpProj = nullptr;
|
||||
@@ -256,12 +295,27 @@ TString CResourceStore::DefaultResourceDirPath() const
|
||||
return StaticDefaultResourceDirPath( mGame );
|
||||
}
|
||||
|
||||
TString CResourceStore::DeletedResourcePath() const
|
||||
{
|
||||
return mpProj->HiddenFilesDir() / "delete/";
|
||||
}
|
||||
|
||||
CResourceEntry* CResourceStore::FindEntry(const CAssetID& rkID) const
|
||||
{
|
||||
if (!rkID.IsValid()) return nullptr;
|
||||
auto Found = mResourceEntries.find(rkID);
|
||||
if (Found == mResourceEntries.end()) return nullptr;
|
||||
else return Found->second;
|
||||
if (rkID.IsValid())
|
||||
{
|
||||
auto Found = mResourceEntries.find(rkID);
|
||||
|
||||
if (Found != mResourceEntries.end())
|
||||
{
|
||||
CResourceEntry* pEntry = Found->second;
|
||||
|
||||
if (!pEntry->IsMarkedForDeletion())
|
||||
return pEntry;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CResourceEntry* CResourceStore::FindEntry(const TString& rkPath) const
|
||||
@@ -411,6 +465,8 @@ CResourceEntry* CResourceStore::CreateNewResource(const CAssetID& rkID, EResourc
|
||||
{
|
||||
TrackLoadedResource(pEntry);
|
||||
}
|
||||
|
||||
debugf("CREATED NEW RESOURCE: [%s] %s", *rkID.ToString(), *pEntry->CookedAssetPath());
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
@@ -52,6 +52,7 @@ public:
|
||||
void CreateVirtualDirectory(const TString& rkPath);
|
||||
void ConditionalDeleteDirectory(CVirtualDirectory *pDir, bool Recurse);
|
||||
TString DefaultResourceDirPath() const;
|
||||
TString DeletedResourcePath() const;
|
||||
|
||||
bool IsResourceRegistered(const CAssetID& rkID) const;
|
||||
CResourceEntry* CreateNewResource(const CAssetID& rkID, EResourceType Type, const TString& rkDir, const TString& rkName);
|
||||
|
||||
@@ -46,6 +46,28 @@ bool CVirtualDirectory::IsDescendantOf(CVirtualDirectory *pDir) const
|
||||
return (this == pDir) || (mpParent && pDir && (mpParent == pDir || mpParent->IsDescendantOf(pDir)));
|
||||
}
|
||||
|
||||
bool CVirtualDirectory::IsSafeToDelete() const
|
||||
{
|
||||
// Return false if we contain any referenced assets.
|
||||
for (CResourceEntry* pEntry : mResources)
|
||||
{
|
||||
if (pEntry->IsLoaded() && pEntry->Resource()->IsReferenced())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (CVirtualDirectory* pSubdir : mSubdirectories)
|
||||
{
|
||||
if (!pSubdir->IsSafeToDelete())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TString CVirtualDirectory::FullPath() const
|
||||
{
|
||||
if (IsRoot())
|
||||
|
||||
@@ -26,6 +26,7 @@ public:
|
||||
|
||||
bool IsEmpty(bool CheckFilesystem) const;
|
||||
bool IsDescendantOf(CVirtualDirectory *pDir) const;
|
||||
bool IsSafeToDelete() const;
|
||||
TString FullPath() const;
|
||||
TString AbsolutePath() const;
|
||||
CVirtualDirectory* GetRoot();
|
||||
|
||||
Reference in New Issue
Block a user