Added ability to rebuild the resource database from the project resources folder. Editor can detect if the resource database is corrupt on load and if so prompts the user to repair it.
This commit is contained in:
parent
1f3df14b02
commit
9a52fe52d4
|
@ -56,6 +56,14 @@ void CAudioManager::LoadAssets()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CAudioManager::ClearAssets()
|
||||||
|
{
|
||||||
|
mAudioGroups.clear();
|
||||||
|
mpAudioLookupTable = nullptr;
|
||||||
|
mpSfxNameList = nullptr;
|
||||||
|
mSfxIdMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
SSoundInfo CAudioManager::GetSoundInfo(u32 SoundID)
|
SSoundInfo CAudioManager::GetSoundInfo(u32 SoundID)
|
||||||
{
|
{
|
||||||
SSoundInfo Out;
|
SSoundInfo Out;
|
||||||
|
|
|
@ -28,6 +28,7 @@ class CAudioManager
|
||||||
public:
|
public:
|
||||||
CAudioManager(CGameProject *pProj);
|
CAudioManager(CGameProject *pProj);
|
||||||
void LoadAssets();
|
void LoadAssets();
|
||||||
|
void ClearAssets();
|
||||||
SSoundInfo GetSoundInfo(u32 SoundID);
|
SSoundInfo GetSoundInfo(u32 SoundID);
|
||||||
void LogSoundInfo(u32 SoundID);
|
void LogSoundInfo(u32 SoundID);
|
||||||
};
|
};
|
||||||
|
|
|
@ -228,7 +228,8 @@ HEADERS += \
|
||||||
Resource/Animation/CSourceAnimData.h \
|
Resource/Animation/CSourceAnimData.h \
|
||||||
Resource/CMapArea.h \
|
Resource/CMapArea.h \
|
||||||
Resource/CSavedStateID.h \
|
Resource/CSavedStateID.h \
|
||||||
IProgressNotifier.h
|
IProgressNotifier.h \
|
||||||
|
IUIRelay.h
|
||||||
|
|
||||||
# Source Files
|
# Source Files
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
@ -334,4 +335,5 @@ SOURCES += \
|
||||||
GameProject/CAssetNameMap.cpp \
|
GameProject/CAssetNameMap.cpp \
|
||||||
GameProject/CGameInfo.cpp \
|
GameProject/CGameInfo.cpp \
|
||||||
Resource/CResTypeInfo.cpp \
|
Resource/CResTypeInfo.cpp \
|
||||||
CompressionUtil.cpp
|
CompressionUtil.cpp \
|
||||||
|
IUIRelay.cpp
|
||||||
|
|
|
@ -76,12 +76,8 @@ void ApplyGeneratedName(CResourceEntry *pEntry, const TString& rkDir, const TStr
|
||||||
if (pEntry->Directory() == pNewDir && pEntry->Name() == NewName) return;
|
if (pEntry->Directory() == pNewDir && pEntry->Name() == NewName) return;
|
||||||
|
|
||||||
// Perform the move
|
// Perform the move
|
||||||
CVirtualDirectory *pOldDir = pEntry->Directory();
|
|
||||||
bool Success = pEntry->Move(pNewDir->FullPath(), NewName, true, true);
|
bool Success = pEntry->Move(pNewDir->FullPath(), NewName, true, true);
|
||||||
ASSERT(Success);
|
ASSERT(Success);
|
||||||
|
|
||||||
// If the old directory is now empty, delete it
|
|
||||||
pEntry->ResourceStore()->ConditionalDeleteDirectory(pOldDir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenerateAssetNames(CGameProject *pProj)
|
void GenerateAssetNames(CGameProject *pProj)
|
||||||
|
@ -657,6 +653,7 @@ void GenerateAssetNames(CGameProject *pProj)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Log::Write("*** Asset Name Generation FINISHED ***");
|
pStore->RootDirectory()->RemoveEmptySubdirectories();
|
||||||
pStore->ConditionalSaveStore();
|
pStore->ConditionalSaveStore();
|
||||||
|
Log::Write("*** Asset Name Generation FINISHED ***");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "CGameProject.h"
|
#include "CGameProject.h"
|
||||||
|
#include "IUIRelay.h"
|
||||||
#include "Core/Resource/Factory/CTemplateLoader.h"
|
#include "Core/Resource/Factory/CTemplateLoader.h"
|
||||||
#include "Core/Resource/Script/CMasterTemplate.h"
|
#include "Core/Resource/Script/CMasterTemplate.h"
|
||||||
#include <Common/Serialization/XML.h>
|
#include <Common/Serialization/XML.h>
|
||||||
|
@ -33,7 +34,7 @@ bool CGameProject::Save()
|
||||||
return SaveSuccess;
|
return SaveSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameProject::Serialize(IArchive& rArc)
|
bool CGameProject::Serialize(IArchive& rArc)
|
||||||
{
|
{
|
||||||
rArc << SERIAL("Name", mProjectName)
|
rArc << SERIAL("Name", mProjectName)
|
||||||
<< SERIAL("Region", mRegion)
|
<< SERIAL("Region", mRegion)
|
||||||
|
@ -50,7 +51,7 @@ void CGameProject::Serialize(IArchive& rArc)
|
||||||
|
|
||||||
rArc << SERIAL("ResourceDB", mResourceDBPath);
|
rArc << SERIAL("ResourceDB", mResourceDBPath);
|
||||||
|
|
||||||
// Packages
|
// Serialize package list
|
||||||
std::vector<TString> PackageList;
|
std::vector<TString> PackageList;
|
||||||
|
|
||||||
if (!rArc.IsReader())
|
if (!rArc.IsReader())
|
||||||
|
@ -61,38 +62,29 @@ void CGameProject::Serialize(IArchive& rArc)
|
||||||
|
|
||||||
rArc << SERIAL_CONTAINER("Packages", PackageList, "Package");
|
rArc << SERIAL_CONTAINER("Packages", PackageList, "Package");
|
||||||
|
|
||||||
|
// Load packages
|
||||||
if (rArc.IsReader())
|
if (rArc.IsReader())
|
||||||
{
|
{
|
||||||
// Load resource store
|
ASSERT(mPackages.empty());
|
||||||
ASSERT(mpResourceStore == nullptr);
|
|
||||||
mpResourceStore = new CResourceStore(this);
|
|
||||||
|
|
||||||
if (!mpResourceStore->LoadResourceDatabase())
|
for (u32 iPkg = 0; iPkg < PackageList.size(); iPkg++)
|
||||||
mLoadSuccess = false;
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// Load packages
|
const TString& rkPackagePath = PackageList[iPkg];
|
||||||
ASSERT(mPackages.empty());
|
TString PackageName = rkPackagePath.GetFileName(false);
|
||||||
|
TString PackageDir = rkPackagePath.GetFileDirectory();
|
||||||
|
|
||||||
for (u32 iPkg = 0; iPkg < PackageList.size(); iPkg++)
|
CPackage *pPackage = new CPackage(this, PackageName, PackageDir);
|
||||||
|
bool PackageLoadSuccess = pPackage->Load();
|
||||||
|
mPackages.push_back(pPackage);
|
||||||
|
|
||||||
|
if (!PackageLoadSuccess)
|
||||||
{
|
{
|
||||||
const TString& rkPackagePath = PackageList[iPkg];
|
return false;
|
||||||
TString PackageName = rkPackagePath.GetFileName(false);
|
|
||||||
TString PackageDir = rkPackagePath.GetFileDirectory();
|
|
||||||
|
|
||||||
CPackage *pPackage = new CPackage(this, PackageName, PackageDir);
|
|
||||||
bool PackageLoadSuccess = pPackage->Load();
|
|
||||||
mPackages.push_back(pPackage);
|
|
||||||
|
|
||||||
if (!PackageLoadSuccess)
|
|
||||||
{
|
|
||||||
mLoadSuccess = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CGameProject::BuildISO(const TString& rkIsoPath, IProgressNotifier *pProgress)
|
bool CGameProject::BuildISO(const TString& rkIsoPath, IProgressNotifier *pProgress)
|
||||||
|
@ -213,16 +205,23 @@ CGameProject* CGameProject::CreateProjectForExport(
|
||||||
pProj->mProjectRoot.Replace("\\", "/");
|
pProj->mProjectRoot.Replace("\\", "/");
|
||||||
pProj->mpResourceStore = new CResourceStore(pProj);
|
pProj->mpResourceStore = new CResourceStore(pProj);
|
||||||
pProj->mpGameInfo->LoadGameInfo(Game);
|
pProj->mpGameInfo->LoadGameInfo(Game);
|
||||||
pProj->mLoadSuccess = true;
|
|
||||||
return pProj;
|
return pProj;
|
||||||
}
|
}
|
||||||
|
|
||||||
CGameProject* CGameProject::LoadProject(const TString& rkProjPath)
|
CGameProject* CGameProject::LoadProject(const TString& rkProjPath, IProgressNotifier *pProgress)
|
||||||
{
|
{
|
||||||
|
// Init project
|
||||||
CGameProject *pProj = new CGameProject;
|
CGameProject *pProj = new CGameProject;
|
||||||
pProj->mProjectRoot = rkProjPath.GetFileDirectory();
|
pProj->mProjectRoot = rkProjPath.GetFileDirectory();
|
||||||
pProj->mProjectRoot.Replace("\\", "/");
|
pProj->mProjectRoot.Replace("\\", "/");
|
||||||
|
|
||||||
|
// Init progress
|
||||||
|
pProgress->SetTask(0, "Loading project: " + rkProjPath.GetFileName());
|
||||||
|
|
||||||
|
// Load main project file
|
||||||
|
pProgress->Report("Loading project settings");
|
||||||
|
bool LoadSuccess = false;
|
||||||
|
|
||||||
TString ProjPath = rkProjPath;
|
TString ProjPath = rkProjPath;
|
||||||
CXMLReader Reader(ProjPath);
|
CXMLReader Reader(ProjPath);
|
||||||
|
|
||||||
|
@ -233,9 +232,37 @@ CGameProject* CGameProject::LoadProject(const TString& rkProjPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
pProj->mGame = Reader.Game();
|
pProj->mGame = Reader.Game();
|
||||||
pProj->Serialize(Reader);
|
|
||||||
|
|
||||||
if (!pProj->mLoadSuccess)
|
if (pProj->Serialize(Reader))
|
||||||
|
{
|
||||||
|
// Load resource database
|
||||||
|
pProgress->Report("Loading resource database");
|
||||||
|
pProj->mpResourceStore = new CResourceStore(pProj);
|
||||||
|
LoadSuccess = pProj->mpResourceStore->LoadResourceDatabase();
|
||||||
|
|
||||||
|
// Validate resource database
|
||||||
|
if (LoadSuccess)
|
||||||
|
{
|
||||||
|
pProgress->Report("Validating resource database");
|
||||||
|
bool DatabaseIsValid = pProj->mpResourceStore->AreAllEntriesValid();
|
||||||
|
|
||||||
|
// Resource database is corrupt. Ask the user if they want to rebuild it.
|
||||||
|
if (!DatabaseIsValid)
|
||||||
|
{
|
||||||
|
bool ShouldRebuild = gpUIRelay->AskYesNoQuestion("Error", "The resource database is corrupt. Attempt to repair it?");
|
||||||
|
|
||||||
|
if (ShouldRebuild)
|
||||||
|
{
|
||||||
|
pProgress->Report("Repairing resource database");
|
||||||
|
pProj->mpResourceStore->RebuildFromDirectory();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LoadSuccess = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!LoadSuccess)
|
||||||
{
|
{
|
||||||
delete pProj;
|
delete pProj;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -36,7 +36,6 @@ class CGameProject
|
||||||
// Keep file handle open for the .prj file to prevent users from opening the same project
|
// Keep file handle open for the .prj file to prevent users from opening the same project
|
||||||
// in multiple instances of PWE
|
// in multiple instances of PWE
|
||||||
CFileLock mProjFileLock;
|
CFileLock mProjFileLock;
|
||||||
bool mLoadSuccess;
|
|
||||||
|
|
||||||
enum EProjectVersion
|
enum EProjectVersion
|
||||||
{
|
{
|
||||||
|
@ -55,7 +54,6 @@ class CGameProject
|
||||||
, mBuildVersion(0.f)
|
, mBuildVersion(0.f)
|
||||||
, mResourceDBPath("ResourceDB.rdb")
|
, mResourceDBPath("ResourceDB.rdb")
|
||||||
, mpResourceStore(nullptr)
|
, mpResourceStore(nullptr)
|
||||||
, mLoadSuccess(true)
|
|
||||||
{
|
{
|
||||||
mpGameInfo = new CGameInfo();
|
mpGameInfo = new CGameInfo();
|
||||||
mpAudioManager = new CAudioManager(this);
|
mpAudioManager = new CAudioManager(this);
|
||||||
|
@ -65,7 +63,7 @@ public:
|
||||||
~CGameProject();
|
~CGameProject();
|
||||||
|
|
||||||
bool Save();
|
bool Save();
|
||||||
void Serialize(IArchive& rArc);
|
bool Serialize(IArchive& rArc);
|
||||||
bool BuildISO(const TString& rkIsoPath, IProgressNotifier *pProgress);
|
bool BuildISO(const TString& rkIsoPath, IProgressNotifier *pProgress);
|
||||||
void GetWorldList(std::list<CAssetID>& rOut) const;
|
void GetWorldList(std::list<CAssetID>& rOut) const;
|
||||||
CAssetID FindNamedResource(const TString& rkName) const;
|
CAssetID FindNamedResource(const TString& rkName) const;
|
||||||
|
@ -84,7 +82,7 @@ public:
|
||||||
u32 FstAddress
|
u32 FstAddress
|
||||||
);
|
);
|
||||||
|
|
||||||
static CGameProject* LoadProject(const TString& rkProjPath);
|
static CGameProject* LoadProject(const TString& rkProjPath, IProgressNotifier *pProgress);
|
||||||
|
|
||||||
// Directory Handling
|
// Directory Handling
|
||||||
inline TString ProjectRoot() const { return mProjectRoot; }
|
inline TString ProjectRoot() const { return mProjectRoot; }
|
||||||
|
|
|
@ -566,7 +566,7 @@ bool CResourceEntry::Move(const TString& rkDir, const TString& rkName, bool IsAu
|
||||||
Log::Error("MOVE FAILED: " + MoveFailReason);
|
Log::Error("MOVE FAILED: " + MoveFailReason);
|
||||||
mpDirectory = pOldDir;
|
mpDirectory = pOldDir;
|
||||||
mName = OldName;
|
mName = OldName;
|
||||||
mpStore->ConditionalDeleteDirectory(pNewDir);
|
mpStore->ConditionalDeleteDirectory(pNewDir, false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,22 +7,22 @@
|
||||||
class CResourceIterator
|
class CResourceIterator
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
CResourceStore *mpStore;
|
const CResourceStore *mpkStore;
|
||||||
std::map<CAssetID, CResourceEntry*>::iterator mIter;
|
std::map<CAssetID, CResourceEntry*>::const_iterator mIter;
|
||||||
CResourceEntry *mpCurEntry;
|
CResourceEntry *mpCurEntry;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CResourceIterator(CResourceStore *pStore = gpResourceStore)
|
CResourceIterator(const CResourceStore *pkStore = gpResourceStore)
|
||||||
: mpStore(pStore)
|
: mpkStore(pkStore)
|
||||||
, mpCurEntry(nullptr)
|
, mpCurEntry(nullptr)
|
||||||
{
|
{
|
||||||
mIter = mpStore->mResourceEntries.begin();
|
mIter = mpkStore->mResourceEntries.begin();
|
||||||
Next();
|
Next();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual CResourceEntry* Next()
|
virtual CResourceEntry* Next()
|
||||||
{
|
{
|
||||||
if (mIter != mpStore->mResourceEntries.end())
|
if (mIter != mpkStore->mResourceEntries.end())
|
||||||
{
|
{
|
||||||
mpCurEntry = mIter->second;
|
mpCurEntry = mIter->second;
|
||||||
mIter++;
|
mIter++;
|
||||||
|
@ -73,7 +73,7 @@ public:
|
||||||
TResourceIterator(CResourceStore *pStore = gpResourceStore)
|
TResourceIterator(CResourceStore *pStore = gpResourceStore)
|
||||||
: CResourceIterator(pStore)
|
: CResourceIterator(pStore)
|
||||||
{
|
{
|
||||||
if (mpCurEntry->ResourceType() != ResType)
|
if (mpCurEntry && mpCurEntry->ResourceType() != ResType)
|
||||||
Next();
|
Next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ CResourceStore::~CResourceStore()
|
||||||
delete It->second;
|
delete It->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CResourceStore::SerializeResourceDatabase(IArchive& rArc)
|
bool CResourceStore::SerializeResourceDatabase(IArchive& rArc)
|
||||||
{
|
{
|
||||||
struct SDatabaseResource
|
struct SDatabaseResource
|
||||||
{
|
{
|
||||||
|
@ -83,6 +83,8 @@ void CResourceStore::SerializeResourceDatabase(IArchive& rArc)
|
||||||
RegisterResource(rRes.ID, rRes.pType->Type(), rRes.Directory, rRes.Name);
|
RegisterResource(rRes.ID, rRes.pType->Type(), rRes.Directory, rRes.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CResourceStore::LoadResourceDatabase()
|
bool CResourceStore::LoadResourceDatabase()
|
||||||
|
@ -105,7 +107,7 @@ bool CResourceStore::LoadResourceDatabase()
|
||||||
ASSERT(mpProj->Game() == Reader.Game());
|
ASSERT(mpProj->Game() == Reader.Game());
|
||||||
|
|
||||||
mGame = Reader.Game();
|
mGame = Reader.Game();
|
||||||
SerializeResourceDatabase(Reader);
|
if (!SerializeResourceDatabase(Reader)) return false;
|
||||||
return LoadCacheFile();
|
return LoadCacheFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,19 +295,17 @@ CVirtualDirectory* CResourceStore::GetVirtualDirectory(const TString& rkPath, bo
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CResourceStore::ConditionalDeleteDirectory(CVirtualDirectory *pDir)
|
void CResourceStore::ConditionalDeleteDirectory(CVirtualDirectory *pDir, bool Recurse)
|
||||||
{
|
{
|
||||||
if (pDir->IsEmpty())
|
if (pDir->IsEmpty() && !pDir->IsRoot())
|
||||||
{
|
{
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
CVirtualDirectory *pParent = pDir->Parent();
|
CVirtualDirectory *pParent = pDir->Parent();
|
||||||
pParent->RemoveChildDirectory(pDir);
|
pParent->RemoveChildDirectory(pDir);
|
||||||
ConditionalDeleteDirectory(pParent);
|
|
||||||
|
if (Recurse)
|
||||||
|
{
|
||||||
|
ConditionalDeleteDirectory(pParent, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,6 +322,98 @@ CResourceEntry* CResourceStore::FindEntry(const TString& rkPath) const
|
||||||
return (mpDatabaseRoot ? mpDatabaseRoot->FindChildResource(rkPath) : nullptr);
|
return (mpDatabaseRoot ? mpDatabaseRoot->FindChildResource(rkPath) : nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CResourceStore::AreAllEntriesValid() const
|
||||||
|
{
|
||||||
|
for (CResourceIterator Iter(this); Iter; ++Iter)
|
||||||
|
{
|
||||||
|
if (!Iter->HasCookedVersion() && !Iter->HasRawVersion())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CResourceStore::ClearDatabase()
|
||||||
|
{
|
||||||
|
// THIS OPERATION REQUIRES THAT ALL RESOURCES ARE UNREFERENCED
|
||||||
|
DestroyUnreferencedResources();
|
||||||
|
ASSERT(mLoadedResources.empty());
|
||||||
|
|
||||||
|
// Clear out existing resource entries and directories
|
||||||
|
for (auto Iter = mResourceEntries.begin(); Iter != mResourceEntries.end(); Iter++)
|
||||||
|
delete Iter->second;
|
||||||
|
mResourceEntries.clear();
|
||||||
|
|
||||||
|
delete mpDatabaseRoot;
|
||||||
|
mpDatabaseRoot = new CVirtualDirectory(this);
|
||||||
|
|
||||||
|
mDatabaseDirty = true;
|
||||||
|
mCacheFileDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CResourceStore::RebuildFromDirectory()
|
||||||
|
{
|
||||||
|
ASSERT(mpProj != nullptr);
|
||||||
|
mpProj->AudioManager()->ClearAssets();
|
||||||
|
ClearDatabase();
|
||||||
|
|
||||||
|
// Get list of resources
|
||||||
|
TString ResDir = ResourcesDir();
|
||||||
|
TStringList ResourceList;
|
||||||
|
FileUtil::GetDirectoryContents(ResDir, ResourceList);
|
||||||
|
|
||||||
|
for (auto Iter = ResourceList.begin(); Iter != ResourceList.end(); Iter++)
|
||||||
|
{
|
||||||
|
TString Path = *Iter;
|
||||||
|
TString RelPath = FileUtil::MakeRelative(Path, ResDir);
|
||||||
|
|
||||||
|
if (FileUtil::IsFile(Path) && Path.GetFileExtension() == "rsmeta")
|
||||||
|
{
|
||||||
|
// Determine resource name
|
||||||
|
TString DirPath = RelPath.GetFileDirectory();
|
||||||
|
TString CookedFilename = RelPath.GetFileName(false); // This call removes the .rsmeta extension
|
||||||
|
TString ResName = CookedFilename.GetFileName(false); // This call removes the cooked extension
|
||||||
|
ASSERT( IsValidResourcePath(DirPath, ResName) );
|
||||||
|
|
||||||
|
// Determine resource type
|
||||||
|
TString CookedExtension = CookedFilename.GetFileExtension();
|
||||||
|
CResTypeInfo *pTypeInfo = CResTypeInfo::TypeForCookedExtension( Game(), CFourCC(CookedExtension) );
|
||||||
|
|
||||||
|
if (!pTypeInfo)
|
||||||
|
{
|
||||||
|
Log::Error("Found resource but couldn't register because failed to identify resource type: " + RelPath);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create resource entry
|
||||||
|
CResourceEntry *pEntry = new CResourceEntry(this, CAssetID::InvalidID(mGame), DirPath, ResName, pTypeInfo->Type());
|
||||||
|
pEntry->LoadMetadata();
|
||||||
|
|
||||||
|
// Validate the entry
|
||||||
|
CAssetID ID = pEntry->ID();
|
||||||
|
ASSERT( mResourceEntries.find(ID) == mResourceEntries.end() );
|
||||||
|
ASSERT( ID.Length() == CAssetID::GameIDLength(mGame) );
|
||||||
|
|
||||||
|
mResourceEntries[ID] = pEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (FileUtil::IsDirectory(Path))
|
||||||
|
GetVirtualDirectory(RelPath, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure audio manager is loaded correctly so AGSC dependencies can be looked up
|
||||||
|
mpProj->AudioManager()->LoadAssets();
|
||||||
|
|
||||||
|
// Update dependencies
|
||||||
|
for (CResourceIterator It(this); It; ++It)
|
||||||
|
It->UpdateDependencies();
|
||||||
|
|
||||||
|
// Update database files
|
||||||
|
mDatabaseDirty = true;
|
||||||
|
mCacheFileDirty = true;
|
||||||
|
ConditionalSaveStore();
|
||||||
|
}
|
||||||
|
|
||||||
bool CResourceStore::IsResourceRegistered(const CAssetID& rkID) const
|
bool CResourceStore::IsResourceRegistered(const CAssetID& rkID) const
|
||||||
{
|
{
|
||||||
return FindEntry(rkID) != nullptr;
|
return FindEntry(rkID) != nullptr;
|
||||||
|
|
|
@ -43,7 +43,7 @@ public:
|
||||||
CResourceStore(const TString& rkDatabasePath);
|
CResourceStore(const TString& rkDatabasePath);
|
||||||
CResourceStore(CGameProject *pProject);
|
CResourceStore(CGameProject *pProject);
|
||||||
~CResourceStore();
|
~CResourceStore();
|
||||||
void SerializeResourceDatabase(IArchive& rArc);
|
bool SerializeResourceDatabase(IArchive& rArc);
|
||||||
bool LoadResourceDatabase();
|
bool LoadResourceDatabase();
|
||||||
bool SaveResourceDatabase();
|
bool SaveResourceDatabase();
|
||||||
bool LoadCacheFile();
|
bool LoadCacheFile();
|
||||||
|
@ -52,12 +52,15 @@ public:
|
||||||
void SetProject(CGameProject *pProj);
|
void SetProject(CGameProject *pProj);
|
||||||
void CloseProject();
|
void CloseProject();
|
||||||
CVirtualDirectory* GetVirtualDirectory(const TString& rkPath, bool AllowCreate);
|
CVirtualDirectory* GetVirtualDirectory(const TString& rkPath, bool AllowCreate);
|
||||||
void ConditionalDeleteDirectory(CVirtualDirectory *pDir);
|
void ConditionalDeleteDirectory(CVirtualDirectory *pDir, bool Recurse);
|
||||||
|
|
||||||
bool IsResourceRegistered(const CAssetID& rkID) const;
|
bool IsResourceRegistered(const CAssetID& rkID) const;
|
||||||
CResourceEntry* RegisterResource(const CAssetID& rkID, EResType Type, const TString& rkDir, const TString& rkName);
|
CResourceEntry* RegisterResource(const CAssetID& rkID, EResType Type, const TString& rkDir, const TString& rkName);
|
||||||
CResourceEntry* FindEntry(const CAssetID& rkID) const;
|
CResourceEntry* FindEntry(const CAssetID& rkID) const;
|
||||||
CResourceEntry* FindEntry(const TString& rkPath) const;
|
CResourceEntry* FindEntry(const TString& rkPath) const;
|
||||||
|
bool AreAllEntriesValid() const;
|
||||||
|
void ClearDatabase();
|
||||||
|
void RebuildFromDirectory();
|
||||||
|
|
||||||
template<typename ResType> ResType* LoadResource(const CAssetID& rkID) { return static_cast<ResType*>(LoadResource(rkID, ResType::StaticType())); }
|
template<typename ResType> ResType* LoadResource(const CAssetID& rkID) { return static_cast<ResType*>(LoadResource(rkID, ResType::StaticType())); }
|
||||||
CResource* LoadResource(const CAssetID& rkID);
|
CResource* LoadResource(const CAssetID& rkID);
|
||||||
|
|
|
@ -155,6 +155,7 @@ bool CVirtualDirectory::AddChild(const TString &rkPath, CResourceEntry *pEntry)
|
||||||
{
|
{
|
||||||
// Create new subdirectory
|
// Create new subdirectory
|
||||||
pSubdir = new CVirtualDirectory(this, DirName, mpStore);
|
pSubdir = new CVirtualDirectory(this, DirName, mpStore);
|
||||||
|
FileUtil::MakeDirectory(mpStore->ResourcesDir() + pSubdir->FullPath());
|
||||||
mSubdirectories.push_back(pSubdir);
|
mSubdirectories.push_back(pSubdir);
|
||||||
|
|
||||||
std::sort(mSubdirectories.begin(), mSubdirectories.end(), [](CVirtualDirectory *pLeft, CVirtualDirectory *pRight) -> bool {
|
std::sort(mSubdirectories.begin(), mSubdirectories.end(), [](CVirtualDirectory *pLeft, CVirtualDirectory *pRight) -> bool {
|
||||||
|
@ -199,6 +200,14 @@ bool CVirtualDirectory::RemoveChildDirectory(CVirtualDirectory *pSubdir)
|
||||||
if (*It == pSubdir)
|
if (*It == pSubdir)
|
||||||
{
|
{
|
||||||
mSubdirectories.erase(It);
|
mSubdirectories.erase(It);
|
||||||
|
|
||||||
|
// If this is part of the resource store, delete the corresponding filesystem directory
|
||||||
|
if (mpStore && pSubdir->GetRoot() == mpStore->RootDirectory())
|
||||||
|
{
|
||||||
|
TString AbsPath = mpStore->DatabaseRootPath() + pSubdir->FullPath();
|
||||||
|
FileUtil::DeleteDirectory(AbsPath, true);
|
||||||
|
}
|
||||||
|
|
||||||
delete pSubdir;
|
delete pSubdir;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -221,6 +230,19 @@ bool CVirtualDirectory::RemoveChildResource(CResourceEntry *pEntry)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CVirtualDirectory::RemoveEmptySubdirectories()
|
||||||
|
{
|
||||||
|
for (u32 SubdirIdx = 0; SubdirIdx < mSubdirectories.size(); SubdirIdx++)
|
||||||
|
{
|
||||||
|
CVirtualDirectory *pDir = mSubdirectories[SubdirIdx];
|
||||||
|
|
||||||
|
if (pDir->IsEmpty())
|
||||||
|
RemoveChildDirectory(pDir);
|
||||||
|
else
|
||||||
|
pDir->RemoveEmptySubdirectories();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ************ STATIC ************
|
// ************ STATIC ************
|
||||||
bool CVirtualDirectory::IsValidDirectoryName(const TString& rkName)
|
bool CVirtualDirectory::IsValidDirectoryName(const TString& rkName)
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,6 +33,7 @@ public:
|
||||||
bool AddChild(const TString& rkPath, CResourceEntry *pEntry);
|
bool AddChild(const TString& rkPath, CResourceEntry *pEntry);
|
||||||
bool RemoveChildDirectory(CVirtualDirectory *pSubdir);
|
bool RemoveChildDirectory(CVirtualDirectory *pSubdir);
|
||||||
bool RemoveChildResource(CResourceEntry *pEntry);
|
bool RemoveChildResource(CResourceEntry *pEntry);
|
||||||
|
void RemoveEmptySubdirectories();
|
||||||
|
|
||||||
static bool IsValidDirectoryName(const TString& rkName);
|
static bool IsValidDirectoryName(const TString& rkName);
|
||||||
static bool IsValidDirectoryPath(TString Path);
|
static bool IsValidDirectoryPath(TString Path);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define IPROGRESSNOTIFIER_H
|
#define IPROGRESSNOTIFIER_H
|
||||||
|
|
||||||
#include <Common/Common.h>
|
#include <Common/Common.h>
|
||||||
|
#include <Math/MathUtil.h>
|
||||||
|
|
||||||
class IProgressNotifier
|
class IProgressNotifier
|
||||||
{
|
{
|
||||||
|
@ -26,19 +27,36 @@ public:
|
||||||
{
|
{
|
||||||
mTaskName = TaskName;
|
mTaskName = TaskName;
|
||||||
mTaskIndex = TaskIndex;
|
mTaskIndex = TaskIndex;
|
||||||
|
mTaskCount = Math::Max(mTaskCount, TaskIndex + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Report(int StepIndex, int StepCount, const TString& rkStepDesc)
|
void Report(int StepIndex, int StepCount, const TString& rkStepDesc)
|
||||||
{
|
{
|
||||||
ASSERT(mTaskCount >= 1);
|
ASSERT(mTaskCount >= 1);
|
||||||
|
|
||||||
|
// Make sure TaskCount and StepCount are at least 1 so we don't have divide-by-zero errors
|
||||||
|
int TaskCount = Math::Max(mTaskCount, 1);
|
||||||
|
StepCount = Math::Max(StepCount, 1);
|
||||||
|
|
||||||
// Calculate percentage
|
// Calculate percentage
|
||||||
float TaskPercent = 1.f / (float) mTaskCount;
|
float TaskPercent = 1.f / (float) TaskCount;
|
||||||
float StepPercent = (StepCount >= 0 ? (float) StepIndex / (float) StepCount : 0.f);
|
float StepPercent = (StepCount >= 0 ? (float) StepIndex / (float) StepCount : 0.f);
|
||||||
float ProgressPercent = (TaskPercent * mTaskIndex) + (TaskPercent * StepPercent);
|
float ProgressPercent = (TaskPercent * mTaskIndex) + (TaskPercent * StepPercent);
|
||||||
UpdateProgress(mTaskName, rkStepDesc, ProgressPercent);
|
UpdateProgress(mTaskName, rkStepDesc, ProgressPercent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Report(const TString& rkStepDesc)
|
||||||
|
{
|
||||||
|
Report(0, 0, rkStepDesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetOneShotTask(const TString& rkTaskDesc)
|
||||||
|
{
|
||||||
|
SetNumTasks(1);
|
||||||
|
SetTask(0, rkTaskDesc);
|
||||||
|
Report(0, 0, "");
|
||||||
|
}
|
||||||
|
|
||||||
virtual bool ShouldCancel() const = 0;
|
virtual bool ShouldCancel() const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
#include "IUIRelay.h"
|
||||||
|
|
||||||
|
IUIRelay *gpUIRelay = nullptr;
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef IUIRELAY_H
|
||||||
|
#define IUIRELAY_H
|
||||||
|
|
||||||
|
#include <Common/TString.h>
|
||||||
|
|
||||||
|
class IUIRelay
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool AskYesNoQuestion(const TString& rkInfoBoxTitle, const TString& rkQuestion) = 0;
|
||||||
|
};
|
||||||
|
extern IUIRelay *gpUIRelay;
|
||||||
|
|
||||||
|
#endif // IUIRELAY_H
|
|
@ -41,24 +41,28 @@ void CEditorApplication::InitEditor()
|
||||||
mpWorldEditor->showMaximized();
|
mpWorldEditor->showMaximized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CEditorApplication::CloseAllEditors()
|
||||||
|
{
|
||||||
|
// Close active editor windows.
|
||||||
|
foreach (IEditor *pEditor, mEditorWindows)
|
||||||
|
{
|
||||||
|
if (pEditor != mpWorldEditor && !pEditor->close())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close world
|
||||||
|
if (!mpWorldEditor->CloseWorld())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mpResourceBrowser->close();
|
||||||
|
mpProjectDialog->close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool CEditorApplication::CloseProject()
|
bool CEditorApplication::CloseProject()
|
||||||
{
|
{
|
||||||
if (mpActiveProject)
|
if (mpActiveProject && CloseAllEditors())
|
||||||
{
|
{
|
||||||
// Close active editor windows. todo: check for unsaved changes
|
|
||||||
foreach (IEditor *pEditor, mEditorWindows)
|
|
||||||
{
|
|
||||||
if (pEditor != mpWorldEditor && !pEditor->close())
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close world
|
|
||||||
if (!mpWorldEditor->CloseWorld())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
mpResourceBrowser->close();
|
|
||||||
mpProjectDialog->close();
|
|
||||||
|
|
||||||
// Emit before actually deleting the project to allow editor references to clean up
|
// Emit before actually deleting the project to allow editor references to clean up
|
||||||
CGameProject *pOldProj = mpActiveProject;
|
CGameProject *pOldProj = mpActiveProject;
|
||||||
mpActiveProject = nullptr;
|
mpActiveProject = nullptr;
|
||||||
|
@ -77,7 +81,12 @@ bool CEditorApplication::OpenProject(const QString& rkProjPath)
|
||||||
|
|
||||||
// Load new project
|
// Load new project
|
||||||
TString Path = TO_TSTRING(rkProjPath);
|
TString Path = TO_TSTRING(rkProjPath);
|
||||||
mpActiveProject = CGameProject::LoadProject(Path);
|
|
||||||
|
CProgressDialog Dialog("Opening " + TO_QSTRING(Path.GetFileName()), true, true, mpWorldEditor);
|
||||||
|
Dialog.DisallowCanceling();
|
||||||
|
QFuture<CGameProject*> Future = QtConcurrent::run(&CGameProject::LoadProject, Path, &Dialog);
|
||||||
|
mpActiveProject = Dialog.WaitForResults(Future);
|
||||||
|
Dialog.close();
|
||||||
|
|
||||||
if (mpActiveProject)
|
if (mpActiveProject)
|
||||||
{
|
{
|
||||||
|
@ -169,7 +178,7 @@ bool CEditorApplication::CookPackageList(QList<CPackage*> PackageList)
|
||||||
{
|
{
|
||||||
if (!PackageList.isEmpty())
|
if (!PackageList.isEmpty())
|
||||||
{
|
{
|
||||||
CProgressDialog Dialog("Cooking package" + QString(PackageList.size() > 1 ? "s" : ""), true, mpWorldEditor);
|
CProgressDialog Dialog("Cooking package" + QString(PackageList.size() > 1 ? "s" : ""), false, true, mpWorldEditor);
|
||||||
|
|
||||||
QFuture<void> Future = QtConcurrent::run([&]()
|
QFuture<void> Future = QtConcurrent::run([&]()
|
||||||
{
|
{
|
||||||
|
@ -191,6 +200,45 @@ bool CEditorApplication::CookPackageList(QList<CPackage*> PackageList)
|
||||||
else return true;
|
else return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CEditorApplication::RebuildResourceDatabase()
|
||||||
|
{
|
||||||
|
bool BrowserIsOpen = mpResourceBrowser->isVisible();
|
||||||
|
|
||||||
|
// Make sure all editors are closed
|
||||||
|
if (mpActiveProject && CloseAllEditors())
|
||||||
|
{
|
||||||
|
// Fake-close the project, but keep it in memory so we can modify the resource store
|
||||||
|
CGameProject *pProj = mpActiveProject;
|
||||||
|
mpActiveProject = nullptr;
|
||||||
|
emit ActiveProjectChanged(nullptr);
|
||||||
|
|
||||||
|
// Rebuild
|
||||||
|
CProgressDialog Dialog("Rebuilding resource database", true, false, mpWorldEditor);
|
||||||
|
Dialog.SetOneShotTask("Rebuilding resource database");
|
||||||
|
Dialog.DisallowCanceling();
|
||||||
|
|
||||||
|
QFuture<void> Future = QtConcurrent::run(pProj->ResourceStore(), &CResourceStore::RebuildFromDirectory);
|
||||||
|
Dialog.WaitForResults(Future);
|
||||||
|
Dialog.close();
|
||||||
|
|
||||||
|
// Set project to active again
|
||||||
|
mpActiveProject = pProj;
|
||||||
|
emit ActiveProjectChanged(pProj);
|
||||||
|
|
||||||
|
// If the resource browser was open before, then reopen it now
|
||||||
|
if (BrowserIsOpen)
|
||||||
|
{
|
||||||
|
mpResourceBrowser->show();
|
||||||
|
mpResourceBrowser->raise();
|
||||||
|
}
|
||||||
|
|
||||||
|
UICommon::InfoMsg(mpWorldEditor, "Success", "Resource database rebuilt successfully!");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// ************ SLOTS ************
|
// ************ SLOTS ************
|
||||||
void CEditorApplication::AddEditor(IEditor *pEditor)
|
void CEditorApplication::AddEditor(IEditor *pEditor)
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,6 +33,7 @@ public:
|
||||||
CEditorApplication(int& rArgc, char **ppArgv);
|
CEditorApplication(int& rArgc, char **ppArgv);
|
||||||
~CEditorApplication();
|
~CEditorApplication();
|
||||||
void InitEditor();
|
void InitEditor();
|
||||||
|
bool CloseAllEditors();
|
||||||
bool CloseProject();
|
bool CloseProject();
|
||||||
bool OpenProject(const QString& rkProjPath);
|
bool OpenProject(const QString& rkProjPath);
|
||||||
void EditResource(CResourceEntry *pEntry);
|
void EditResource(CResourceEntry *pEntry);
|
||||||
|
@ -42,6 +43,8 @@ public:
|
||||||
bool CookAllDirtyPackages();
|
bool CookAllDirtyPackages();
|
||||||
bool CookPackageList(QList<CPackage*> PackageList);
|
bool CookPackageList(QList<CPackage*> PackageList);
|
||||||
|
|
||||||
|
bool RebuildResourceDatabase();
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
inline CGameProject* ActiveProject() const { return mpActiveProject; }
|
inline CGameProject* ActiveProject() const { return mpActiveProject; }
|
||||||
inline CWorldEditor* WorldEditor() const { return mpWorldEditor; }
|
inline CWorldEditor* WorldEditor() const { return mpWorldEditor; }
|
||||||
|
|
|
@ -386,7 +386,7 @@ void CExportGameDialog::Export()
|
||||||
TString StrExportDir = TO_TSTRING(ExportDir);
|
TString StrExportDir = TO_TSTRING(ExportDir);
|
||||||
StrExportDir.EnsureEndsWith('/');
|
StrExportDir.EnsureEndsWith('/');
|
||||||
|
|
||||||
CProgressDialog Dialog("Creating new game project", true, parentWidget());
|
CProgressDialog Dialog("Creating new game project", false, true, parentWidget());
|
||||||
QFuture<bool> Future = QtConcurrent::run(&Exporter, &CGameExporter::Export, mpDisc, StrExportDir, &NameMap, &GameInfo, &Dialog);
|
QFuture<bool> Future = QtConcurrent::run(&Exporter, &CGameExporter::Export, mpDisc, StrExportDir, &NameMap, &GameInfo, &Dialog);
|
||||||
mExportSuccess = Dialog.WaitForResults(Future);
|
mExportSuccess = Dialog.WaitForResults(Future);
|
||||||
|
|
||||||
|
|
|
@ -3,16 +3,17 @@
|
||||||
#include "CEditorApplication.h"
|
#include "CEditorApplication.h"
|
||||||
#include <QCloseEvent>
|
#include <QCloseEvent>
|
||||||
|
|
||||||
CProgressDialog::CProgressDialog(QString OperationName, bool AlertOnFinish, QWidget *pParent)
|
CProgressDialog::CProgressDialog(QString OperationName, bool UseBusyIndicator, bool AlertOnFinish, QWidget *pParent)
|
||||||
: IProgressNotifierUI(pParent)
|
: IProgressNotifierUI(pParent)
|
||||||
, mpUI(new Ui::CProgressDialog)
|
, mpUI(new Ui::CProgressDialog)
|
||||||
|
, mUseBusyIndicator(UseBusyIndicator)
|
||||||
, mAlertOnFinish(AlertOnFinish)
|
, mAlertOnFinish(AlertOnFinish)
|
||||||
, mFinished(false)
|
, mFinished(false)
|
||||||
, mCanceled(false)
|
, mCanceled(false)
|
||||||
{
|
{
|
||||||
mpUI->setupUi(this);
|
mpUI->setupUi(this);
|
||||||
mpUI->ProgressBar->setMinimum(0);
|
mpUI->ProgressBar->setMinimum(0);
|
||||||
mpUI->ProgressBar->setMaximum(10000);
|
mpUI->ProgressBar->setMaximum(UseBusyIndicator ? 0 : 10000);
|
||||||
setWindowTitle(OperationName);
|
setWindowTitle(OperationName);
|
||||||
|
|
||||||
#if WIN32
|
#if WIN32
|
||||||
|
@ -23,7 +24,7 @@ CProgressDialog::CProgressDialog(QString OperationName, bool AlertOnFinish, QWid
|
||||||
|
|
||||||
mpTaskbarProgress = pButton->progress();
|
mpTaskbarProgress = pButton->progress();
|
||||||
mpTaskbarProgress->setMinimum(0);
|
mpTaskbarProgress->setMinimum(0);
|
||||||
mpTaskbarProgress->setMaximum(10000);
|
mpTaskbarProgress->setMaximum(UseBusyIndicator ? 0 : 10000);
|
||||||
mpTaskbarProgress->show();
|
mpTaskbarProgress->show();
|
||||||
|
|
||||||
setWindowFlags(windowFlags() | Qt::MSWindowsFixedSizeDialogHint);
|
setWindowFlags(windowFlags() | Qt::MSWindowsFixedSizeDialogHint);
|
||||||
|
@ -82,10 +83,26 @@ void CProgressDialog::UpdateUI(const QString& rkTaskDesc, const QString& rkStepD
|
||||||
mpUI->TaskLabel->setText(rkTaskDesc);
|
mpUI->TaskLabel->setText(rkTaskDesc);
|
||||||
mpUI->StepLabel->setText(rkStepDesc);
|
mpUI->StepLabel->setText(rkStepDesc);
|
||||||
|
|
||||||
int ProgressValue = 10000 * ProgressPercent;
|
if (rkStepDesc.isEmpty() && !mpUI->StepLabel->isHidden())
|
||||||
mpUI->ProgressBar->setValue(ProgressValue);
|
{
|
||||||
|
mpUI->StepLabel->hide();
|
||||||
|
mpUI->TaskInfoBoxLayout->removeWidget(mpUI->StepLabel);
|
||||||
|
mpUI->TaskInfoBoxLayout->removeItem(mpUI->LabelSpacer);
|
||||||
|
}
|
||||||
|
else if (!rkStepDesc.isEmpty() && mpUI->StepLabel->isHidden())
|
||||||
|
{
|
||||||
|
mpUI->StepLabel->show();
|
||||||
|
mpUI->TaskInfoBoxLayout->addWidget(mpUI->StepLabel);
|
||||||
|
mpUI->TaskInfoBoxLayout->addItem(mpUI->LabelSpacer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mUseBusyIndicator)
|
||||||
|
{
|
||||||
|
int ProgressValue = 10000 * ProgressPercent;
|
||||||
|
mpUI->ProgressBar->setValue(ProgressValue);
|
||||||
|
|
||||||
#if WIN32
|
#if WIN32
|
||||||
mpTaskbarProgress->setValue(ProgressValue);
|
mpTaskbarProgress->setValue(ProgressValue);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ class CProgressDialog : public IProgressNotifierUI
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Ui::CProgressDialog *mpUI;
|
Ui::CProgressDialog *mpUI;
|
||||||
|
bool mUseBusyIndicator;
|
||||||
bool mAlertOnFinish;
|
bool mAlertOnFinish;
|
||||||
bool mFinished;
|
bool mFinished;
|
||||||
bool mCanceled;
|
bool mCanceled;
|
||||||
|
@ -32,7 +33,7 @@ class CProgressDialog : public IProgressNotifierUI
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CProgressDialog(QString OperationName, bool AlertOnFinish, QWidget *pParent = 0);
|
explicit CProgressDialog(QString OperationName, bool UseBusyIndicator, bool AlertOnFinish, QWidget *pParent = 0);
|
||||||
~CProgressDialog();
|
~CProgressDialog();
|
||||||
|
|
||||||
void DisallowCanceling();
|
void DisallowCanceling();
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="TaskInfoBoxLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="TaskLabel">
|
<widget class="QLabel" name="TaskLabel">
|
||||||
<property name="font">
|
<property name="font">
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="LabelSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
|
|
|
@ -117,7 +117,7 @@ void CProjectSettingsDialog::BuildISO()
|
||||||
{
|
{
|
||||||
if (gpEdApp->CookAllDirtyPackages())
|
if (gpEdApp->CookAllDirtyPackages())
|
||||||
{
|
{
|
||||||
CProgressDialog Dialog("Building ISO", true, this);
|
CProgressDialog Dialog("Building ISO", false, true, this);
|
||||||
Dialog.DisallowCanceling();
|
Dialog.DisallowCanceling();
|
||||||
QFuture<void> Future = QtConcurrent::run(pProj, &CGameProject::BuildISO, TO_TSTRING(IsoPath), &Dialog);
|
QFuture<void> Future = QtConcurrent::run(pProj, &CGameProject::BuildISO, TO_TSTRING(IsoPath), &Dialog);
|
||||||
Dialog.WaitForResults(Future);
|
Dialog.WaitForResults(Future);
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef CUIRELAY_H
|
||||||
|
#define CUIRELAY_H
|
||||||
|
|
||||||
|
#include <Core/IUIRelay.h>
|
||||||
|
#include "CEditorApplication.h"
|
||||||
|
#include "WorldEditor/CWorldEditor.h"
|
||||||
|
#include "UICommon.h"
|
||||||
|
|
||||||
|
class CUIRelay : public QObject, public IUIRelay
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CUIRelay(QObject *pParent = 0)
|
||||||
|
: QObject(pParent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Note: All function calls should be deferred with QMetaObject::invokeMethod to ensure
|
||||||
|
// that they run on the UI thread instead of whatever thread we happen to be on.
|
||||||
|
virtual bool AskYesNoQuestion(const TString& rkInfoBoxTitle, const TString& rkQuestion)
|
||||||
|
{
|
||||||
|
bool RetVal;
|
||||||
|
QMetaObject::invokeMethod(this, "AskYesNoQuestionSlot", Qt::BlockingQueuedConnection,
|
||||||
|
Q_RETURN_ARG(bool, RetVal),
|
||||||
|
Q_ARG(QString, TO_QSTRING(rkInfoBoxTitle)),
|
||||||
|
Q_ARG(QString, TO_QSTRING(rkQuestion)) );
|
||||||
|
return RetVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
bool AskYesNoQuestionSlot(const QString& rkInfoBoxTitle, const QString& rkQuestion)
|
||||||
|
{
|
||||||
|
return UICommon::YesNoQuestion(gpEdApp->WorldEditor(), rkInfoBoxTitle, rkQuestion);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CUIRELAY_H
|
|
@ -182,7 +182,8 @@ HEADERS += \
|
||||||
WorldEditor/CPoiMapSidebar.h \
|
WorldEditor/CPoiMapSidebar.h \
|
||||||
WorldEditor/CWorldEditorSidebar.h \
|
WorldEditor/CWorldEditorSidebar.h \
|
||||||
CProgressDialog.h \
|
CProgressDialog.h \
|
||||||
IProgressNotifierUI.h
|
IProgressNotifierUI.h \
|
||||||
|
CUIRelay.h
|
||||||
|
|
||||||
# Source Files
|
# Source Files
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "CResourceBrowser.h"
|
#include "CResourceBrowser.h"
|
||||||
#include "ui_CResourceBrowser.h"
|
#include "ui_CResourceBrowser.h"
|
||||||
|
#include "CProgressDialog.h"
|
||||||
#include "Editor/CEditorApplication.h"
|
#include "Editor/CEditorApplication.h"
|
||||||
#include <Core/GameProject/AssetNameGeneration.h>
|
#include <Core/GameProject/AssetNameGeneration.h>
|
||||||
#include <Core/GameProject/CAssetNameMap.h>
|
#include <Core/GameProject/CAssetNameMap.h>
|
||||||
|
@ -7,6 +8,7 @@
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QtConcurrent/QtConcurrentRun>
|
||||||
|
|
||||||
CResourceBrowser::CResourceBrowser(QWidget *pParent)
|
CResourceBrowser::CResourceBrowser(QWidget *pParent)
|
||||||
: QDialog(pParent)
|
: QDialog(pParent)
|
||||||
|
@ -96,6 +98,7 @@ CResourceBrowser::CResourceBrowser(QWidget *pParent)
|
||||||
connect(pImportFromAssetNameMapAction, SIGNAL(triggered()), this, SLOT(OnImportNamesFromAssetNameMap()));
|
connect(pImportFromAssetNameMapAction, SIGNAL(triggered()), this, SLOT(OnImportNamesFromAssetNameMap()));
|
||||||
connect(pGenerateAssetNamesAction, SIGNAL(triggered()), this, SLOT(OnGenerateAssetNames()));
|
connect(pGenerateAssetNamesAction, SIGNAL(triggered()), this, SLOT(OnGenerateAssetNames()));
|
||||||
connect(mpUI->ExportNamesButton, SIGNAL(clicked()), this, SLOT(ExportAssetNames()));
|
connect(mpUI->ExportNamesButton, SIGNAL(clicked()), this, SLOT(ExportAssetNames()));
|
||||||
|
connect(mpUI->RebuildDatabaseButton, SIGNAL(clicked(bool)), this, SLOT(RebuildResourceDB()));
|
||||||
connect(&mUpdateFilterTimer, SIGNAL(timeout()), this, SLOT(UpdateFilter()));
|
connect(&mUpdateFilterTimer, SIGNAL(timeout()), this, SLOT(UpdateFilter()));
|
||||||
connect(mpFilterAllBox, SIGNAL(toggled(bool)), this, SLOT(OnFilterTypeBoxTicked(bool)));
|
connect(mpFilterAllBox, SIGNAL(toggled(bool)), this, SLOT(OnFilterTypeBoxTicked(bool)));
|
||||||
connect(gpEdApp, SIGNAL(ActiveProjectChanged(CGameProject*)), this, SLOT(UpdateStore()));
|
connect(gpEdApp, SIGNAL(ActiveProjectChanged(CGameProject*)), this, SLOT(UpdateStore()));
|
||||||
|
@ -327,9 +330,18 @@ void CResourceBrowser::OnImportPakContentsTxt()
|
||||||
void CResourceBrowser::OnGenerateAssetNames()
|
void CResourceBrowser::OnGenerateAssetNames()
|
||||||
{
|
{
|
||||||
SelectDirectory(nullptr);
|
SelectDirectory(nullptr);
|
||||||
GenerateAssetNames(mpStore->Project());
|
|
||||||
|
CProgressDialog Dialog("Generating asset names", true, true, this);
|
||||||
|
Dialog.DisallowCanceling();
|
||||||
|
Dialog.SetOneShotTask("Generating asset names");
|
||||||
|
|
||||||
|
QFuture<void> Future = QtConcurrent::run(&GenerateAssetNames, mpStore->Project());
|
||||||
|
Dialog.WaitForResults(Future);
|
||||||
|
|
||||||
RefreshResources();
|
RefreshResources();
|
||||||
RefreshDirectories();
|
RefreshDirectories();
|
||||||
|
|
||||||
|
UICommon::InfoMsg(this, "Complete", "Asset name generation complete!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CResourceBrowser::OnImportNamesFromAssetNameMap()
|
void CResourceBrowser::OnImportNamesFromAssetNameMap()
|
||||||
|
@ -393,6 +405,14 @@ void CResourceBrowser::ExportAssetNames()
|
||||||
UICommon::InfoMsg(this, "Success", "Asset names exported successfully!");
|
UICommon::InfoMsg(this, "Success", "Asset names exported successfully!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CResourceBrowser::RebuildResourceDB()
|
||||||
|
{
|
||||||
|
if (UICommon::YesNoQuestion(this, "Rebuild resource database", "Are you sure you want to rebuild the resource database?"))
|
||||||
|
{
|
||||||
|
gpEdApp->RebuildResourceDatabase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CResourceBrowser::UpdateFilter()
|
void CResourceBrowser::UpdateFilter()
|
||||||
{
|
{
|
||||||
bool OldIsAssetList = InAssetListMode();
|
bool OldIsAssetList = InAssetListMode();
|
||||||
|
|
|
@ -67,6 +67,7 @@ public slots:
|
||||||
void OnGenerateAssetNames();
|
void OnGenerateAssetNames();
|
||||||
void OnImportNamesFromAssetNameMap();
|
void OnImportNamesFromAssetNameMap();
|
||||||
void ExportAssetNames();
|
void ExportAssetNames();
|
||||||
|
void RebuildResourceDB();
|
||||||
void UpdateFilter();
|
void UpdateFilter();
|
||||||
|
|
||||||
void ResetTypeFilter();
|
void ResetTypeFilter();
|
||||||
|
|
|
@ -130,7 +130,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>189</width>
|
<width>189</width>
|
||||||
<height>126</height>
|
<height>117</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -189,6 +189,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="RebuildDatabaseButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Rebuild Database</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "CEditorApplication.h"
|
#include "CEditorApplication.h"
|
||||||
#include <Common/TString.h>
|
#include <Common/TString.h>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
@ -112,6 +113,12 @@ inline void ErrorMsg(QWidget *pParent, QString ErrorText)
|
||||||
QMessageBox::warning(pParent, "Error", ErrorText);
|
QMessageBox::warning(pParent, "Error", ErrorText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool YesNoQuestion(QWidget *pParent, QString InfoBoxTitle, QString Question)
|
||||||
|
{
|
||||||
|
QMessageBox::StandardButton Button = QMessageBox::question(pParent, InfoBoxTitle, Question, QMessageBox::Yes | QMessageBox::No);
|
||||||
|
return Button == QMessageBox::Yes;
|
||||||
|
}
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const QColor kImportantButtonColor(36, 100, 100);
|
const QColor kImportantButtonColor(36, 100, 100);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "CEditorApplication.h"
|
#include "CEditorApplication.h"
|
||||||
|
#include "CUIRelay.h"
|
||||||
#include "UICommon.h"
|
#include "UICommon.h"
|
||||||
#include <Common/Log.h>
|
#include <Common/Log.h>
|
||||||
#include <Core/Resource/Factory/CTemplateLoader.h>
|
#include <Core/Resource/Factory/CTemplateLoader.h>
|
||||||
|
@ -36,6 +37,10 @@ int main(int argc, char *argv[])
|
||||||
if (!Initialized) QMessageBox::warning(0, "Error", "Couldn't open log file. Logging will not work for this session.");
|
if (!Initialized) QMessageBox::warning(0, "Error", "Couldn't open log file. Logging will not work for this session.");
|
||||||
qInstallMessageHandler(QtLogRedirect);
|
qInstallMessageHandler(QtLogRedirect);
|
||||||
|
|
||||||
|
// Create UI relay
|
||||||
|
CUIRelay UIRelay(&App);
|
||||||
|
gpUIRelay = &UIRelay;
|
||||||
|
|
||||||
// Create editor resource store
|
// Create editor resource store
|
||||||
gpEditorStore = new CResourceStore("../resources/EditorResourceDB.rdb");
|
gpEditorStore = new CResourceStore("../resources/EditorResourceDB.rdb");
|
||||||
gpEditorStore->LoadResourceDatabase();
|
gpEditorStore->LoadResourceDatabase();
|
||||||
|
|
Loading…
Reference in New Issue