diff --git a/src/Common/Serialization/CXMLReader.h b/src/Common/Serialization/CXMLReader.h index a7995779..01921505 100644 --- a/src/Common/Serialization/CXMLReader.h +++ b/src/Common/Serialization/CXMLReader.h @@ -18,17 +18,30 @@ public: // Load XML and set current element to the root element; read version mDoc.LoadFile(*rkFileName); mpCurElem = mDoc.FirstChildElement(); - ASSERT(mpCurElem != nullptr); - mArchiveVersion = (u16) TString( mpCurElem->Attribute("ArchiveVer") ).ToInt32(10); - mFileVersion = (u16) TString( mpCurElem->Attribute("FileVer") ).ToInt32(10); - const char *pkGameAttr = mpCurElem->Attribute("Game"); - mGame = pkGameAttr ? GetGameForID( CFourCC(pkGameAttr) ) : eUnknownGame; + if (mpCurElem != nullptr) + { + mArchiveVersion = (u16) TString( mpCurElem->Attribute("ArchiveVer") ).ToInt32(10); + mFileVersion = (u16) TString( mpCurElem->Attribute("FileVer") ).ToInt32(10); + const char *pkGameAttr = mpCurElem->Attribute("Game"); + mGame = pkGameAttr ? GetGameForID( CFourCC(pkGameAttr) ) : eUnknownGame; + } + else + { + Log::Error("Failed to open XML for read: " + rkFileName); + } + } + + inline bool IsValid() const + { + return mpCurElem != nullptr; } // Interface virtual bool ParamBegin(const char *pkName) { + ASSERT(IsValid()); + // Push new parent if needed if (!mJustEndedParam) { diff --git a/src/Common/Serialization/CXMLWriter.h b/src/Common/Serialization/CXMLWriter.h index 2a828d09..5375dc00 100644 --- a/src/Common/Serialization/CXMLWriter.h +++ b/src/Common/Serialization/CXMLWriter.h @@ -11,11 +11,13 @@ class CXMLWriter : public IArchive tinyxml2::XMLDocument mDoc; TString mOutFilename; tinyxml2::XMLElement *mpCurElem; + bool mSaved; public: CXMLWriter(const TString& rkFileName, const TString& rkRootName, u16 FileVersion, EGame Game = eUnknownGame) : IArchive(false, true) , mOutFilename(rkFileName) + , mSaved(false) { SetVersion(skCurrentArchiveVersion, FileVersion, Game); @@ -34,12 +36,43 @@ public: ~CXMLWriter() { - mDoc.SaveFile(*mOutFilename); + if (!mSaved) + { + bool SaveSuccess = Save(); + ASSERT(SaveSuccess); + } + } + + inline bool Save() + { + if (mSaved) + { + Log::Error("Attempted to save XML twice!"); + return false; + } + + tinyxml2::XMLError Error = mDoc.SaveFile(*mOutFilename); + mSaved = true; + + if (Error != tinyxml2::XML_SUCCESS) + { + Log::Error("Failed to save XML file: " + mOutFilename); + return false; + } + else + return true; + } + + inline bool IsValid() const + { + return mpCurElem != nullptr && !mSaved; } // Interface virtual bool ParamBegin(const char *pkName) { + ASSERT(IsValid()); + tinyxml2::XMLElement *pElem = mDoc.NewElement(pkName); mpCurElem->LinkEndChild(pElem); mpCurElem = pElem; diff --git a/src/Core/GameProject/CAssetNameMap.cpp b/src/Core/GameProject/CAssetNameMap.cpp index 6444d4c6..951b82b0 100644 --- a/src/Core/GameProject/CAssetNameMap.cpp +++ b/src/Core/GameProject/CAssetNameMap.cpp @@ -1,15 +1,22 @@ #include "CAssetNameMap.h" -void CAssetNameMap::LoadAssetNames(TString Path /*= gkAssetMapPath*/) +bool CAssetNameMap::LoadAssetNames(TString Path /*= gkAssetMapPath*/) { CXMLReader Reader(Path); - Serialize(Reader); + + if (Reader.IsValid()) + { + Serialize(Reader); + return true; + } + else return false; } -void CAssetNameMap::SaveAssetNames(TString Path /*= gkAssetMapPath*/) +bool CAssetNameMap::SaveAssetNames(TString Path /*= gkAssetMapPath*/) { CXMLWriter Writer(Path, "AssetNameMap", 0, eUnknownGame); Serialize(Writer); + return Writer.Save(); } bool CAssetNameMap::GetNameInfo(CAssetID ID, TString& rOutDirectory, TString& rOutName) diff --git a/src/Core/GameProject/CAssetNameMap.h b/src/Core/GameProject/CAssetNameMap.h index 3495d7c4..77197dfa 100644 --- a/src/Core/GameProject/CAssetNameMap.h +++ b/src/Core/GameProject/CAssetNameMap.h @@ -50,8 +50,8 @@ class CAssetNameMap public: CAssetNameMap() : mIsValid(true) {} - void LoadAssetNames(TString Path = gkAssetMapPath); - void SaveAssetNames(TString Path = gkAssetMapPath); + bool LoadAssetNames(TString Path = gkAssetMapPath); + bool SaveAssetNames(TString Path = gkAssetMapPath); bool GetNameInfo(CAssetID ID, TString& rOutDirectory, TString& rOutName); void CopyFromStore(CResourceStore *pStore); diff --git a/src/Core/GameProject/CGameExporter.cpp b/src/Core/GameProject/CGameExporter.cpp index b0a1a5a6..2e91277a 100644 --- a/src/Core/GameProject/CGameExporter.cpp +++ b/src/Core/GameProject/CGameExporter.cpp @@ -342,7 +342,8 @@ void CGameExporter::LoadPaks() // Add package to project and save mpProject->AddPackage(pPackage); #if SAVE_PACKAGE_DEFINITIONS - pPackage->Save(); + bool SaveSuccess = pPackage->Save(); + ASSERT(SaveSuccess); #endif } #endif @@ -461,7 +462,8 @@ void CGameExporter::ExportCookedResources() #if EXPORT_COOKED mpStore->SaveResourceDatabase(); #endif - mpProject->Save(); + bool SaveSuccess = mpProject->Save(); + ASSERT(SaveSuccess); } } diff --git a/src/Core/GameProject/CGameInfo.cpp b/src/Core/GameProject/CGameInfo.cpp index 25558a14..fac3332c 100644 --- a/src/Core/GameProject/CGameInfo.cpp +++ b/src/Core/GameProject/CGameInfo.cpp @@ -1,29 +1,35 @@ #include "CGameInfo.h" #include -void CGameInfo::LoadGameInfo(EGame Game) +bool CGameInfo::LoadGameInfo(EGame Game) { Game = RoundGame(Game); mGame = Game; TString Path = GetDefaultGameInfoPath(Game); - if (FileUtil::Exists(Path)) - LoadGameInfo(Path); + return LoadGameInfo(Path); } -void CGameInfo::LoadGameInfo(TString Path) +bool CGameInfo::LoadGameInfo(TString Path) { CXMLReader Reader(Path); - Serialize(Reader); + + if (Reader.IsValid()) + { + Serialize(Reader); + return true; + } + else return false; } -void CGameInfo::SaveGameInfo(TString Path /*= ""*/) +bool CGameInfo::SaveGameInfo(TString Path /*= ""*/) { ASSERT(mGame != eUnknownGame); // can't save game info that was never loaded if (Path.IsEmpty()) Path = GetDefaultGameInfoPath(mGame); CXMLWriter Writer(Path, "GameInfo", 0, mGame); Serialize(Writer); + return Writer.Save(); } void CGameInfo::Serialize(IArchive& rArc) diff --git a/src/Core/GameProject/CGameInfo.h b/src/Core/GameProject/CGameInfo.h index 8a9227bb..272286cc 100644 --- a/src/Core/GameProject/CGameInfo.h +++ b/src/Core/GameProject/CGameInfo.h @@ -21,9 +21,9 @@ public: : mGame(eUnknownGame) {} - void LoadGameInfo(EGame Game); - void LoadGameInfo(TString Path); - void SaveGameInfo(TString Path = ""); + bool LoadGameInfo(EGame Game); + bool LoadGameInfo(TString Path); + bool SaveGameInfo(TString Path = ""); void Serialize(IArchive& rArc); TString GetAreaName(const CAssetID& rkID) const; diff --git a/src/Core/GameProject/CGameProject.cpp b/src/Core/GameProject/CGameProject.cpp index 27b77dea..de535198 100644 --- a/src/Core/GameProject/CGameProject.cpp +++ b/src/Core/GameProject/CGameProject.cpp @@ -7,7 +7,10 @@ CGameProject *CGameProject::mspActiveProject = nullptr; CGameProject::~CGameProject() { - ASSERT(!mpResourceStore->IsDirty()); + if (mpResourceStore) + { + ASSERT(!mpResourceStore->IsDirty()); + } if (IsActive()) { @@ -15,16 +18,23 @@ CGameProject::~CGameProject() gpResourceStore = nullptr; } + for (u32 iPkg = 0; iPkg < mPackages.size(); iPkg++) + delete mPackages[iPkg]; + delete mpAudioManager; delete mpGameInfo; delete mpResourceStore; } -void CGameProject::Save() +bool CGameProject::Save() { + mProjFileLock.Release(); TString ProjPath = ProjectPath().ToUTF8(); CXMLWriter Writer(ProjPath, "GameProject", eVer_Current, mGame); Serialize(Writer); + bool SaveSuccess = Writer.Save(); + mProjFileLock.Lock(*ProjPath); + return SaveSuccess; } void CGameProject::Serialize(IArchive& rArc) @@ -60,22 +70,31 @@ void CGameProject::Serialize(IArchive& rArc) // Load resource store ASSERT(mpResourceStore == nullptr); mpResourceStore = new CResourceStore(this); - mpResourceStore->LoadResourceDatabase(); - // Load packages - for (u32 iPkg = 0; iPkg < mPackages.size(); iPkg++) - delete mPackages[iPkg]; - mPackages.clear(); + if (!mpResourceStore->LoadResourceDatabase()) + mLoadSuccess = false; - for (u32 iPkg = 0; iPkg < PackageList.size(); iPkg++) + else { - const TWideString& rkPackagePath = PackageList[iPkg]; - TString PackageName = TWideString(rkPackagePath.GetFileName(false)).ToUTF8(); - TWideString PackageDir = rkPackagePath.GetFileDirectory(); + // Load packages + ASSERT(mPackages.empty()); - CPackage *pPackage = new CPackage(this, PackageName, PackageDir); - pPackage->Load(); - mPackages.push_back(pPackage); + for (u32 iPkg = 0; iPkg < PackageList.size(); iPkg++) + { + const TWideString& rkPackagePath = PackageList[iPkg]; + TString PackageName = TWideString(rkPackagePath.GetFileName(false)).ToUTF8(); + TWideString PackageDir = rkPackagePath.GetFileDirectory(); + + CPackage *pPackage = new CPackage(this, PackageName, PackageDir); + bool PackageLoadSuccess = pPackage->Load(); + mPackages.push_back(pPackage); + + if (!PackageLoadSuccess) + { + mLoadSuccess = false; + break; + } + } } } } @@ -160,6 +179,7 @@ CGameProject* CGameProject::CreateProjectForExport( pProj->mProjectRoot.Replace(L"/", L"\\"); pProj->mpResourceStore = new CResourceStore(pProj, pExporter, L"Content\\", L"Cooked\\", Game); pProj->mpGameInfo->LoadGameInfo(Game); + pProj->mLoadSuccess = true; return pProj; } @@ -171,10 +191,24 @@ CGameProject* CGameProject::LoadProject(const TWideString& rkProjPath) TString ProjPath = rkProjPath.ToUTF8(); CXMLReader Reader(ProjPath); + + if (!Reader.IsValid()) + { + delete pProj; + return nullptr; + } + pProj->mGame = Reader.Game(); pProj->Serialize(Reader); - CTemplateLoader::LoadGameTemplates(pProj->mGame); + if (!pProj->mLoadSuccess) + { + delete pProj; + return nullptr; + } + + CTemplateLoader::LoadGameTemplates(pProj->mGame); + pProj->mProjFileLock.Lock(*ProjPath); pProj->mpGameInfo->LoadGameInfo(pProj->mGame); pProj->mpAudioManager->LoadAssets(); return pProj; diff --git a/src/Core/GameProject/CGameProject.h b/src/Core/GameProject/CGameProject.h index 0b4c2a03..58fca0d0 100644 --- a/src/Core/GameProject/CGameProject.h +++ b/src/Core/GameProject/CGameProject.h @@ -6,6 +6,7 @@ #include "CResourceStore.h" #include "Core/CAudioManager.h" #include "Core/Resource/Script/CMasterTemplate.h" +#include #include #include #include @@ -31,6 +32,11 @@ class CGameProject CGameInfo *mpGameInfo; CAudioManager *mpAudioManager; + // Keep file handle open for the .prj file to prevent users from opening the same project + // in multiple instances of PWE + CFileLock mProjFileLock; + bool mLoadSuccess; + enum EProjectVersion { eVer_Initial, @@ -50,6 +56,7 @@ class CGameProject , mBuildVersion(0.f) , mResourceDBPath(L"ResourceDB.rdb") , mpResourceStore(nullptr) + , mLoadSuccess(true) { mpGameInfo = new CGameInfo(); mpAudioManager = new CAudioManager(this); @@ -58,7 +65,7 @@ class CGameProject public: ~CGameProject(); - void Save(); + bool Save(); void Serialize(IArchive& rArc); void SetActive(); void GetWorldList(std::list& rOut) const; diff --git a/src/Core/GameProject/CPackage.cpp b/src/Core/GameProject/CPackage.cpp index f57d951d..69387d11 100644 --- a/src/Core/GameProject/CPackage.cpp +++ b/src/Core/GameProject/CPackage.cpp @@ -10,21 +10,28 @@ using namespace tinyxml2; -void CPackage::Load() +bool CPackage::Load() { TWideString DefPath = DefinitionPath(false); CXMLReader Reader(DefPath.ToUTF8()); - Serialize(Reader); - UpdateDependencyCache(); + + if (Reader.IsValid()) + { + Serialize(Reader); + UpdateDependencyCache(); + return true; + } + else return false; } -void CPackage::Save() +bool CPackage::Save() { TWideString DefPath = DefinitionPath(false); FileUtil::MakeDirectory(DefPath.GetFileDirectory()); CXMLWriter Writer(DefPath.ToUTF8(), "PackageDefinition", 0, mpProject ? mpProject->Game() : eUnknownGame); Serialize(Writer); + return Writer.Save(); } void CPackage::Serialize(IArchive& rArc) diff --git a/src/Core/GameProject/CPackage.h b/src/Core/GameProject/CPackage.h index 459f98fb..5e8504e7 100644 --- a/src/Core/GameProject/CPackage.h +++ b/src/Core/GameProject/CPackage.h @@ -71,8 +71,8 @@ public: , mNeedsRecook(false) {} - void Load(); - void Save(); + bool Load(); + bool Save(); void Serialize(IArchive& rArc); void UpdateDependencyCache(); diff --git a/src/Core/GameProject/CResourceEntry.cpp b/src/Core/GameProject/CResourceEntry.cpp index 1427ada1..e2f0c852 100644 --- a/src/Core/GameProject/CResourceEntry.cpp +++ b/src/Core/GameProject/CResourceEntry.cpp @@ -195,9 +195,17 @@ bool CResourceEntry::Save(bool SkipCacheSave /*= false*/) TString SerialName = mpTypeInfo->TypeName(); SerialName.RemoveWhitespace(); + CXMLWriter Writer(Path, SerialName, 0, mGame); mpResource->Serialize(Writer); + if (!Writer.Save()) + { + Log::Error("Failed to save raw resource: " + Path); + DEBUG_BREAK; + return false; + } + mFlags |= eREF_NeedsRecook; } @@ -286,16 +294,28 @@ CResource* CResourceEntry::Load() gpResourceStore = mpStore; CXMLReader Reader(RawAssetPath()); - mpResource->Serialize(Reader); - mpStore->TrackLoadedResource(this); - gpResourceStore = pOldStore; + if (!Reader.IsValid()) + { + Log::Error("Failed to load raw resource; falling back on cooked. Raw path: " + RawAssetPath()); + delete mpResource; + mpResource = nullptr; + } + + else + { + mpResource->Serialize(Reader); + mpStore->TrackLoadedResource(this); + gpResourceStore = pOldStore; + } } - return mpResource; + if (mpResource) + return mpResource; } - else if (HasCookedVersion()) + ASSERT(!mpResource); + if (HasCookedVersion()) { CFileInStream File(CookedAssetPath().ToStdString(), IOUtil::eBigEndian); diff --git a/src/Core/GameProject/CResourceStore.cpp b/src/Core/GameProject/CResourceStore.cpp index 1c8598d6..b1b8b646 100644 --- a/src/Core/GameProject/CResourceStore.cpp +++ b/src/Core/GameProject/CResourceStore.cpp @@ -108,7 +108,7 @@ void CResourceStore::SerializeResourceDatabase(IArchive& rArc) } } -void CResourceStore::LoadResourceDatabase() +bool CResourceStore::LoadResourceDatabase() { ASSERT(!mDatabasePath.IsEmpty()); TString Path = DatabasePath().ToUTF8(); @@ -118,27 +118,45 @@ void CResourceStore::LoadResourceDatabase() CXMLReader Reader(Path); + if (!Reader.IsValid()) + { + Log::Error("Failed to open resource database for load: " + Path); + return false; + } + if (mpProj) ASSERT(mpProj->Game() == Reader.Game()); mGame = Reader.Game(); SerializeResourceDatabase(Reader); - LoadCacheFile(); + return LoadCacheFile(); } -void CResourceStore::SaveResourceDatabase() +bool CResourceStore::SaveResourceDatabase() { TString Path = DatabasePath().ToUTF8(); CXMLWriter Writer(Path, "ResourceDB", 0, mGame); SerializeResourceDatabase(Writer); - mDatabaseDirty = false; + bool SaveSuccess = Writer.Save(); + + if (SaveSuccess) + mDatabaseDirty = false; + else + Log::Error("Failed to save resource database: " + Path); + + return SaveSuccess; } -void CResourceStore::LoadCacheFile() +bool CResourceStore::LoadCacheFile() { TString CachePath = CacheDataPath().ToUTF8(); CFileInStream CacheFile(CachePath.ToStdString(), IOUtil::eBigEndian); - ASSERT(CacheFile.IsValid()); + + if (!CacheFile.IsValid()) + { + Log::Error("Failed to open cache file for load: " + CachePath); + return false; + } // Cache header CFourCC Magic(CacheFile); @@ -146,7 +164,7 @@ void CResourceStore::LoadCacheFile() if (Magic != "CACH") { Log::Error("Invalid resource cache data magic: " + Magic.ToString()); - return; + return false; } CSerialVersion Version(CacheFile); @@ -173,13 +191,19 @@ void CResourceStore::LoadCacheFile() CacheFile.Seek(EntryCacheEnd, SEEK_SET); } + return true; } -void CResourceStore::SaveCacheFile() +bool CResourceStore::SaveCacheFile() { TString CachePath = CacheDataPath().ToUTF8(); CFileOutStream CacheFile(CachePath.ToStdString(), IOUtil::eBigEndian); - ASSERT(CacheFile.IsValid()); + + if (!CacheFile.IsValid()) + { + Log::Error("Failed to open cache file for save: " + CachePath); + return false; + } // Cache header CFourCC("CACH").Write(CacheFile); @@ -219,6 +243,7 @@ void CResourceStore::SaveCacheFile() CacheFile.Seek(ResCountOffset, SEEK_SET); CacheFile.WriteLong(ResCount); mCacheFileDirty = false; + return true; } void CResourceStore::ConditionalSaveStore() diff --git a/src/Core/GameProject/CResourceStore.h b/src/Core/GameProject/CResourceStore.h index e5802486..627cccbb 100644 --- a/src/Core/GameProject/CResourceStore.h +++ b/src/Core/GameProject/CResourceStore.h @@ -52,10 +52,10 @@ public: CResourceStore(CGameProject *pProject); ~CResourceStore(); void SerializeResourceDatabase(IArchive& rArc); - void LoadResourceDatabase(); - void SaveResourceDatabase(); - void LoadCacheFile(); - void SaveCacheFile(); + bool LoadResourceDatabase(); + bool SaveResourceDatabase(); + bool LoadCacheFile(); + bool SaveCacheFile(); void ConditionalSaveStore(); void SetProject(CGameProject *pProj); void CloseProject(); diff --git a/src/Editor/CExportGameDialog.cpp b/src/Editor/CExportGameDialog.cpp index 78693aba..770f23ae 100644 --- a/src/Editor/CExportGameDialog.cpp +++ b/src/Editor/CExportGameDialog.cpp @@ -335,12 +335,20 @@ void CExportGameDialog::Export() CAssetNameMap NameMap; if (!NameMapPath.isEmpty()) - NameMap.LoadAssetNames( TO_TSTRING(NameMapPath) ); - - if (!NameMap.IsValid()) { - UICommon::ErrorMsg(this, "The Asset Name Map is invalid and cannot be used! See the log for more information."); - return; + bool LoadSuccess = NameMap.LoadAssetNames( TO_TSTRING(NameMapPath) ); + + if (!LoadSuccess) + { + UICommon::ErrorMsg(this, "Failed to load the asset name map!"); + return; + } + + else if (!NameMap.IsValid()) + { + UICommon::ErrorMsg(this, "The Asset Name Map is invalid and cannot be used! See the log for more information."); + return; + } } // Verify game info is valid @@ -350,12 +358,20 @@ void CExportGameDialog::Export() return; } - // Do export - close(); - CGameInfo GameInfo; if (!GameInfoPath.isEmpty()) - GameInfo.LoadGameInfo( TO_TSTRING(GameInfoPath) ); + { + bool LoadSuccess = GameInfo.LoadGameInfo( TO_TSTRING(GameInfoPath) ); + + if (!LoadSuccess) + { + UICommon::ErrorMsg(this, "Failed to load game info!"); + return; + } + } + + // Do export + close(); CGameExporter Exporter(mGame, mRegion, mGameTitle, mGameID, mBuildVer); TString StrExportDir = TO_TSTRING(ExportDir); diff --git a/src/Editor/CProjectOverviewDialog.cpp b/src/Editor/CProjectOverviewDialog.cpp index e16d8efa..185144e6 100644 --- a/src/Editor/CProjectOverviewDialog.cpp +++ b/src/Editor/CProjectOverviewDialog.cpp @@ -49,7 +49,9 @@ void CProjectOverviewDialog::InternalLoadProject(const QString& rkPath) } else - Log::Error("Failed to load project"); + { + UICommon::ErrorMsg(this, "Failed to open project! Is it already open in another Prime World Editor instance?"); + } } void CProjectOverviewDialog::OpenProject() diff --git a/src/Editor/ResourceBrowser/CResourceBrowser.cpp b/src/Editor/ResourceBrowser/CResourceBrowser.cpp index 3bdeef61..115534d7 100644 --- a/src/Editor/ResourceBrowser/CResourceBrowser.cpp +++ b/src/Editor/ResourceBrowser/CResourceBrowser.cpp @@ -322,7 +322,18 @@ void CResourceBrowser::OnGenerateAssetNames() void CResourceBrowser::OnImportNamesFromAssetNameMap() { CAssetNameMap Map; - Map.LoadAssetNames(); + bool LoadSuccess = Map.LoadAssetNames(); + + if (!LoadSuccess) + { + UICommon::ErrorMsg(this, "Import failed; couldn't load asset name map!"); + return; + } + else if (!Map.IsValid()) + { + UICommon::ErrorMsg(this, "Import failed; the input asset name map is invalid! See the log for details."); + return; + } for (CResourceIterator It(mpStore); It; ++It) { @@ -335,6 +346,7 @@ void CResourceBrowser::OnImportNamesFromAssetNameMap() mpStore->ConditionalSaveStore(); RefreshResources(); RefreshDirectories(); + UICommon::InfoMsg(this, "Success", "New asset names imported successfully!"); } void CResourceBrowser::ExportAssetNames() @@ -346,10 +358,29 @@ void CResourceBrowser::ExportAssetNames() CAssetNameMap NameMap; if (FileUtil::Exists(OutFileStr)) - NameMap.LoadAssetNames(OutFileStr); + { + bool LoadSuccess = NameMap.LoadAssetNames(OutFileStr); + + if (!LoadSuccess) + { + UICommon::ErrorMsg(this, "Unable to export; failed to load existing names from the original asset name map file!"); + return; + } + + else if (!NameMap.IsValid()) + { + UICommon::ErrorMsg(this, "Unable to export; the original asset name map file is invalid! See the log for details."); + return; + } + } NameMap.CopyFromStore(mpStore); - NameMap.SaveAssetNames(OutFileStr); + bool SaveSuccess = NameMap.SaveAssetNames(OutFileStr); + + if (SaveSuccess) + UICommon::ErrorMsg(this, "Failed to export asset names!"); + else + UICommon::InfoMsg(this, "Success", "Asset names exported successfully!"); } void CResourceBrowser::UpdateFilter() diff --git a/src/Editor/UICommon.h b/src/Editor/UICommon.h index 65774903..bad19c20 100644 --- a/src/Editor/UICommon.h +++ b/src/Editor/UICommon.h @@ -102,6 +102,11 @@ inline QString OpenDirDialog(QWidget *pParent, const QString& rkCaption, const Q } // QMessageBox wrappers +inline void InfoMsg(QWidget *pParent, QString InfoBoxTitle, QString InfoText) +{ + QMessageBox::information(pParent, InfoBoxTitle, InfoText); +} + inline void ErrorMsg(QWidget *pParent, QString ErrorText) { QMessageBox::warning(pParent, "Error", ErrorText); diff --git a/src/FileIO/CFileLock.h b/src/FileIO/CFileLock.h new file mode 100644 index 00000000..28d6e3aa --- /dev/null +++ b/src/FileIO/CFileLock.h @@ -0,0 +1,34 @@ +#ifndef CFILELOCK_H +#define CFILELOCK_H + +#include + +// Maintain a file handle to prevent other processes from accessing the file. +class CFileLock +{ + FILE* mpFile; + +public: + CFileLock() + : mpFile(nullptr) + {} + + ~CFileLock() + { + Release(); + } + + void Lock(const char *pkPath) + { + Release(); + mpFile = fopen(pkPath, "a+"); + } + + void Release() + { + if (mpFile) + fclose(mpFile); + } +}; + +#endif // CFILELOCK_H diff --git a/src/FileIO/FileIO.pro b/src/FileIO/FileIO.pro index 83af5dd8..2acf25e0 100644 --- a/src/FileIO/FileIO.pro +++ b/src/FileIO/FileIO.pro @@ -41,7 +41,8 @@ HEADERS += \ IOUtil.h \ IInputStream.h \ IOutputStream.h \ - CBitStreamInWrapper.h + CBitStreamInWrapper.h \ + CFileLock.h # Source Files SOURCES += \ diff --git a/templates/mp1/Enums/VulnerabilityType.xml b/templates/mp1/Enums/VulnerabilityType.xml index 5934b074..d455fd43 100644 --- a/templates/mp1/Enums/VulnerabilityType.xml +++ b/templates/mp1/Enums/VulnerabilityType.xml @@ -7,7 +7,7 @@ - - + +