From 0a9b05241344a3bded1dfa0422e14972bce963e4 Mon Sep 17 00:00:00 2001 From: Aruki Date: Sun, 21 May 2017 18:01:09 -0600 Subject: [PATCH] Added progress bars for most major blocking operations --- src/Common/CFourCC.h | 2 +- src/Core/Core.pro | 3 +- src/Core/GameProject/CGameExporter.cpp | 116 +++++--- src/Core/GameProject/CGameExporter.h | 14 +- src/Core/GameProject/CGameProject.cpp | 12 +- src/Core/GameProject/CGameProject.h | 3 +- src/Core/GameProject/CPackage.cpp | 79 ++++-- src/Core/GameProject/CPackage.h | 3 +- src/Core/IProgressNotifier.h | 48 ++++ src/Editor/CEditorApplication.cpp | 43 ++- src/Editor/CEditorApplication.h | 5 +- src/Editor/CExportGameDialog.cpp | 12 +- src/Editor/CPakToolDialog.h | 280 ------------------- src/Editor/CProgressDialog.cpp | 91 ++++++ src/Editor/CProgressDialog.h | 83 ++++++ src/Editor/CProgressDialog.ui | 76 +++++ src/Editor/CProjectSettingsDialog.cpp | 20 +- src/Editor/Editor.pro | 19 +- src/Editor/IProgressNotifierUI.h | 30 ++ src/Editor/WorldEditor/CRepackInfoDialog.cpp | 114 -------- src/Editor/WorldEditor/CRepackInfoDialog.h | 34 --- src/Editor/WorldEditor/CRepackInfoDialog.ui | 127 --------- templates/mp1/Script/ActorContraption.xml | 10 +- 23 files changed, 569 insertions(+), 655 deletions(-) create mode 100644 src/Core/IProgressNotifier.h delete mode 100644 src/Editor/CPakToolDialog.h create mode 100644 src/Editor/CProgressDialog.cpp create mode 100644 src/Editor/CProgressDialog.h create mode 100644 src/Editor/CProgressDialog.ui create mode 100644 src/Editor/IProgressNotifierUI.h delete mode 100644 src/Editor/WorldEditor/CRepackInfoDialog.cpp delete mode 100644 src/Editor/WorldEditor/CRepackInfoDialog.h delete mode 100644 src/Editor/WorldEditor/CRepackInfoDialog.ui diff --git a/src/Common/CFourCC.h b/src/Common/CFourCC.h index 499a5f6e..52811304 100644 --- a/src/Common/CFourCC.h +++ b/src/Common/CFourCC.h @@ -13,7 +13,7 @@ class CFourCC { - // Note: mFourCC_Chars isn't really used due to endian issues. It's mostly here for easier readability in the debugger. + // Note: mFourCC_Chars isn't used much due to endianness. union { u32 mFourCC; diff --git a/src/Core/Core.pro b/src/Core/Core.pro index 2aa2884c..17e724fa 100644 --- a/src/Core/Core.pro +++ b/src/Core/Core.pro @@ -227,7 +227,8 @@ HEADERS += \ CompressionUtil.h \ Resource/Animation/CSourceAnimData.h \ Resource/CMapArea.h \ - Resource/CSavedStateID.h + Resource/CSavedStateID.h \ + IProgressNotifier.h # Source Files SOURCES += \ diff --git a/src/Core/GameProject/CGameExporter.cpp b/src/Core/GameProject/CGameExporter.cpp index 6bcbd50b..92703eee 100644 --- a/src/Core/GameProject/CGameExporter.cpp +++ b/src/Core/GameProject/CGameExporter.cpp @@ -23,6 +23,7 @@ CGameExporter::CGameExporter(EGame Game, ERegion Region, const TString& rkGameNa , mGameName(rkGameName) , mGameID(rkGameID) , mBuildVersion(BuildVersion) + , mpProgress(nullptr) { ASSERT(mGame != eUnknownGame); ASSERT(mRegion != eRegion_Unknown); @@ -32,7 +33,7 @@ CGameExporter::CGameExporter(EGame Game, ERegion Region, const TString& rkGameNa #error Fix export directory being cleared! #endif -bool CGameExporter::Export(nod::DiscBase *pDisc, const TString& rkOutputDir, CAssetNameMap *pNameMap, CGameInfo *pGameInfo) +bool CGameExporter::Export(nod::DiscBase *pDisc, const TString& rkOutputDir, CAssetNameMap *pNameMap, CGameInfo *pGameInfo, IProgressNotifier *pProgress) { SCOPED_TIMER(ExportGame); @@ -44,6 +45,10 @@ bool CGameExporter::Export(nod::DiscBase *pDisc, const TString& rkOutputDir, CAs mDiscDir = "Disc/"; mWorldsDirName = "Worlds/"; + // Init progress + mpProgress = pProgress; + mpProgress->SetNumTasks(eES_NumSteps); + // Create project FileUtil::MakeDirectory(mExportDir); FileUtil::ClearDirectory(mExportDir); @@ -72,17 +77,22 @@ bool CGameExporter::Export(nod::DiscBase *pDisc, const TString& rkOutputDir, CAs CResourceStore *pOldStore = gpResourceStore; gpResourceStore = mpStore; - // Export game data + // Export cooked data LoadPaks(); ExportCookedResources(); - mpProject->AudioManager()->LoadAssets(); - ExportResourceEditorData(); + + // Export editor data + if (!mpProgress->ShouldCancel()) + { + mpProject->AudioManager()->LoadAssets(); + ExportResourceEditorData(); + } // Export finished! mProjectPath = mpProject->ProjectPath(); delete mpProject; if (pOldStore) gpResourceStore = pOldStore; - return true; + return !mpProgress->ShouldCancel(); } void CGameExporter::LoadResource(const CAssetID& rkID, std::vector& rBuffer) @@ -97,6 +107,9 @@ bool CGameExporter::ExtractDiscData() // todo: handle dol, apploader, multiple partitions, wii ticket blob SCOPED_TIMER(ExtractDiscData); + // Init progress + mpProgress->SetTask(eES_ExtractDisc, "Extracting disc files"); + // Create Disc output folder TString AbsDiscDir = mExportDir + mDiscDir; FileUtil::MakeDirectory(AbsDiscDir); @@ -106,43 +119,52 @@ bool CGameExporter::ExtractDiscData() nod::ExtractionContext Context; Context.force = false; Context.verbose = false; - Context.progressCB = nullptr; + Context.progressCB = [&](const std::string& rkDesc) { + mpProgress->Report(0, 1, rkDesc); + }; + bool Success = ExtractDiscNodeRecursive(&pDataPartition->getFSTRoot(), AbsDiscDir, Context); if (!Success) return false; - // Extract dol - mDolPath = "boot.dol"; - CFileOutStream DolFile(mExportDir + mDolPath); - if (!DolFile.IsValid()) return false; - - std::unique_ptr pDolBuffer = pDataPartition->getDOLBuf(); - DolFile.WriteBytes(pDolBuffer.get(), (u32) pDataPartition->getDOLSize()); - DolFile.Close(); - - // Extract apploader - mApploaderPath = "apploader.img"; - CFileOutStream ApploaderFile(mExportDir + mApploaderPath); - if (!ApploaderFile.IsValid()) return false; - - std::unique_ptr pApploaderBuffer = pDataPartition->getApploaderBuf(); - ApploaderFile.WriteBytes(pApploaderBuffer.get(), (u32) pDataPartition->getApploaderSize()); - ApploaderFile.Close(); - - // Extract Wii partition header - bool IsWii = (mBuildVersion >= 3.f); - - if (IsWii) + // Extract the remaining disc data + if (!mpProgress->ShouldCancel()) { - mFilesystemAddress = 0; - mPartitionHeaderPath = "partition_header.bin"; - nod::DiscWii *pDiscWii = static_cast(mpDisc); - Success = pDiscWii->writeOutDataPartitionHeader(*TString(mExportDir + mPartitionHeaderPath).ToUTF16()); - if (!Success) return false; + // Extract dol + mDolPath = "boot.dol"; + CFileOutStream DolFile(mExportDir + mDolPath); + if (!DolFile.IsValid()) return false; + + std::unique_ptr pDolBuffer = pDataPartition->getDOLBuf(); + DolFile.WriteBytes(pDolBuffer.get(), (u32) pDataPartition->getDOLSize()); + DolFile.Close(); + + // Extract apploader + mApploaderPath = "apploader.img"; + CFileOutStream ApploaderFile(mExportDir + mApploaderPath); + if (!ApploaderFile.IsValid()) return false; + + std::unique_ptr pApploaderBuffer = pDataPartition->getApploaderBuf(); + ApploaderFile.WriteBytes(pApploaderBuffer.get(), (u32) pDataPartition->getApploaderSize()); + ApploaderFile.Close(); + + // Extract Wii partition header + bool IsWii = (mBuildVersion >= 3.f); + + if (IsWii) + { + mFilesystemAddress = 0; + mPartitionHeaderPath = "partition_header.bin"; + nod::DiscWii *pDiscWii = static_cast(mpDisc); + Success = pDiscWii->writeOutDataPartitionHeader(*TString(mExportDir + mPartitionHeaderPath).ToUTF16()); + if (!Success) return false; + } + else + mFilesystemAddress = (u32) pDataPartition->getFSTMemoryAddr(); + + return true; } else - mFilesystemAddress = (u32) pDataPartition->getFSTMemoryAddr(); - - return true; + return false; } bool CGameExporter::ExtractDiscNodeRecursive(const nod::Node *pkNode, const TString& rkDir, const nod::ExtractionContext& rkContext) @@ -447,9 +469,18 @@ void CGameExporter::ExportCookedResources() SCOPED_TIMER(ExportCookedResources); FileUtil::MakeDirectory(mCookedDir); - for (auto It = mResourceMap.begin(); It != mResourceMap.end(); It++) + mpProgress->SetTask(eES_ExportCooked, "Unpacking cooked assets"); + int ResIndex = 0; + + for (auto It = mResourceMap.begin(); It != mResourceMap.end() && !mpProgress->ShouldCancel(); It++, ResIndex++) { SResourceInstance& rRes = It->second; + + // Update progress + if ((ResIndex & 0x3) == 0) + mpProgress->Report(ResIndex, mResourceMap.size(), TString::Format("Unpacking asset %d/%d", ResIndex, mResourceMap.size()) ); + + // Export resource ExportResource(rRes); } } @@ -462,12 +493,19 @@ void CGameExporter::ExportResourceEditorData() // because we have to load the resource to build its dependency tree and // some resources will fail to load if their dependencies don't exist SCOPED_TIMER(SaveRawResources); + mpProgress->SetTask(eES_GenerateRaw, "Generating editor data"); + int ResIndex = 0; // todo: we're wasting a ton of time loading the same resources over and over because most resources automatically // load all their dependencies and then we just clear it out from memory even though we'll need it again later. we // should really be doing this by dependency order instead of by ID order. - for (CResourceIterator It(mpStore); It; ++It) + for (CResourceIterator It(mpStore); It && !mpProgress->ShouldCancel(); ++It, ++ResIndex) { + // Update progress + if ((ResIndex & 0x3) == 0) + mpProgress->Report(ResIndex, mpStore->NumTotalResources(), TString::Format("Processing asset %d/%d: %s", + ResIndex, mpStore->NumTotalResources(), *It->CookedAssetPath(true).GetFileName()) ); + // Worlds need some info we can only get from the pak at export time; namely, which areas can // have duplicates, as well as the world's internal name. if (It->ResourceType() == eWorld) @@ -496,6 +534,8 @@ void CGameExporter::ExportResourceEditorData() It->UpdateDependencies(); } } + + if (!mpProgress->ShouldCancel()) { // All resources should have dependencies generated, so save the project files SCOPED_TIMER(SaveResourceDatabase); diff --git a/src/Core/GameProject/CGameExporter.h b/src/Core/GameProject/CGameExporter.h index 9a6b844c..0175c611 100644 --- a/src/Core/GameProject/CGameExporter.h +++ b/src/Core/GameProject/CGameExporter.h @@ -57,9 +57,21 @@ class CGameExporter }; std::map mResourceMap; + // Progress + IProgressNotifier *mpProgress; + + enum EExportStep + { + eES_ExtractDisc, + eES_ExportCooked, + eES_GenerateRaw, + // eES_Max must be last + eES_NumSteps + }; + public: CGameExporter(EGame Game, ERegion Region, const TString& rkGameName, const TString& rkGameID, float BuildVersion); - bool Export(nod::DiscBase *pDisc, const TString& rkOutputDir, CAssetNameMap *pNameMap, CGameInfo *pGameInfo); + bool Export(nod::DiscBase *pDisc, const TString& rkOutputDir, CAssetNameMap *pNameMap, CGameInfo *pGameInfo, IProgressNotifier *pProgress); void LoadResource(const CAssetID& rkID, std::vector& rBuffer); inline TString ProjectPath() const { return mProjectPath; } diff --git a/src/Core/GameProject/CGameProject.cpp b/src/Core/GameProject/CGameProject.cpp index fc1d21d9..4069bc7b 100644 --- a/src/Core/GameProject/CGameProject.cpp +++ b/src/Core/GameProject/CGameProject.cpp @@ -95,9 +95,7 @@ void CGameProject::Serialize(IArchive& rArc) } } -void ProgressDummy(size_t, const nod::SystemString&, size_t) {} - -bool CGameProject::BuildISO(const TString& rkIsoPath) +bool CGameProject::BuildISO(const TString& rkIsoPath, IProgressNotifier *pProgress) { ASSERT( FileUtil::IsValidPath(rkIsoPath, false) ); @@ -109,7 +107,13 @@ bool CGameProject::BuildISO(const TString& rkIsoPath) else { - nod::DiscBuilderGCN *pBuilder = new nod::DiscBuilderGCN(*rkIsoPath.ToUTF16(), *mGameID, *mProjectName, mFilesystemAddress, &ProgressDummy); + auto ProgressCallback = [&](size_t, const nod::SystemString& rkInfoString, size_t) + { + pProgress->Report(0, 0, TWideString(rkInfoString).ToUTF8()); + }; + + nod::DiscBuilderGCN *pBuilder = new nod::DiscBuilderGCN(*rkIsoPath.ToUTF16(), *mGameID, *mProjectName, mFilesystemAddress, ProgressCallback); + pProgress->SetTask(0, "Building " + rkIsoPath.GetFileName()); TWideString ProjRoot = ProjectRoot().ToUTF16(); TWideString DiscRoot = DiscDir(false).ToUTF16(); diff --git a/src/Core/GameProject/CGameProject.h b/src/Core/GameProject/CGameProject.h index 79d9e7bf..ac35c843 100644 --- a/src/Core/GameProject/CGameProject.h +++ b/src/Core/GameProject/CGameProject.h @@ -5,6 +5,7 @@ #include "CPackage.h" #include "CResourceStore.h" #include "Core/CAudioManager.h" +#include "Core/IProgressNotifier.h" #include "Core/Resource/Script/CMasterTemplate.h" #include #include @@ -65,7 +66,7 @@ public: bool Save(); void Serialize(IArchive& rArc); - bool BuildISO(const TString& rkIsoPath); + bool BuildISO(const TString& rkIsoPath, IProgressNotifier *pProgress); void GetWorldList(std::list& rOut) const; CAssetID FindNamedResource(const TString& rkName) const; CPackage* FindPackage(const TString& rkName) const; diff --git a/src/Core/GameProject/CPackage.cpp b/src/Core/GameProject/CPackage.cpp index fe2a9ab4..c037d0ad 100644 --- a/src/Core/GameProject/CPackage.cpp +++ b/src/Core/GameProject/CPackage.cpp @@ -59,9 +59,13 @@ void CPackage::UpdateDependencyCache() const mCacheDirty = false; } -void CPackage::Cook() +void CPackage::Cook(IProgressNotifier *pProgress) { + SCOPED_TIMER(CookPackage); + // Build asset list + pProgress->Report(-1, -1, "Building dependency list"); + CPackageDependencyListBuilder Builder(this); std::list AssetList; Builder.BuildDependencyList(true, AssetList); @@ -161,7 +165,7 @@ void CPackage::Cook() u32 ResIdx = 0; u32 ResDataOffset = Pak.Tell(); - for (auto Iter = AssetList.begin(); Iter != AssetList.end(); Iter++, ResIdx++) + for (auto Iter = AssetList.begin(); Iter != AssetList.end() && !pProgress->ShouldCancel(); Iter++, ResIdx++) { // Initialize entry, recook assets if needed u32 AssetOffset = Pak.Tell(); @@ -170,8 +174,18 @@ void CPackage::Cook() ASSERT(pEntry != nullptr); if (pEntry->NeedsRecook()) + { + pProgress->Report(ResIdx, AssetList.size(), "Cooking asset: " + pEntry->Name() + "." + pEntry->CookedExtension()); pEntry->Cook(); + } + // Update progress bar + if (ResIdx & 0x1 || ResIdx == AssetList.size() - 1) + { + pProgress->Report(ResIdx, AssetList.size(), TString::Format("Writing asset %d/%d: %s", ResIdx+1, AssetList.size(), *(pEntry->Name() + "." + pEntry->CookedExtension()))); + } + + // Update table info SResourceTableInfo& rTableInfo = ResourceTableData[ResIdx]; rTableInfo.pEntry = pEntry; rTableInfo.Offset = (Game <= eEchoes ? AssetOffset : AssetOffset - ResDataOffset); @@ -267,40 +281,51 @@ void CPackage::Cook() } ResDataSize = Pak.Tell() - ResDataOffset; - // Write table of contents for real - if (Game >= eCorruption) + // If we cancelled, don't finish writing the pak; delete the file instead and make sure the package is flagged for recook + if (pProgress->ShouldCancel()) { - Pak.Seek(TocOffset, SEEK_SET); - Pak.WriteLong(3); // Always 3 pak sections - Pak.WriteFourCC( FOURCC('STRG') ); - Pak.WriteLong(NamesSize); - Pak.WriteFourCC( FOURCC('RSHD') ); - Pak.WriteLong(ResTableSize); - Pak.WriteFourCC( FOURCC('DATA') ); - Pak.WriteLong(ResDataSize); + Pak.Close(); + FileUtil::DeleteFile(PakPath); + mNeedsRecook = true; } - // Write resource table for real - Pak.Seek(ResTableOffset+4, SEEK_SET); - - for (u32 iRes = 0; iRes < AssetList.size(); iRes++) + else { - const SResourceTableInfo& rkInfo = ResourceTableData[iRes]; - CResourceEntry *pEntry = rkInfo.pEntry; + // Write table of contents for real + if (Game >= eCorruption) + { + Pak.Seek(TocOffset, SEEK_SET); + Pak.WriteLong(3); // Always 3 pak sections + Pak.WriteFourCC( FOURCC('STRG') ); + Pak.WriteLong(NamesSize); + Pak.WriteFourCC( FOURCC('RSHD') ); + Pak.WriteLong(ResTableSize); + Pak.WriteFourCC( FOURCC('DATA') ); + Pak.WriteLong(ResDataSize); + } - Pak.WriteLong( rkInfo.Compressed ? 1 : 0 ); - pEntry->CookedExtension().Write(Pak); - pEntry->ID().Write(Pak); - Pak.WriteLong(rkInfo.Size); - Pak.WriteLong(rkInfo.Offset); + // Write resource table for real + Pak.Seek(ResTableOffset+4, SEEK_SET); + + for (u32 iRes = 0; iRes < AssetList.size(); iRes++) + { + const SResourceTableInfo& rkInfo = ResourceTableData[iRes]; + CResourceEntry *pEntry = rkInfo.pEntry; + + Pak.WriteLong( rkInfo.Compressed ? 1 : 0 ); + pEntry->CookedExtension().Write(Pak); + pEntry->ID().Write(Pak); + Pak.WriteLong(rkInfo.Size); + Pak.WriteLong(rkInfo.Offset); + } + + // Clear recook flag + mNeedsRecook = false; + Log::Write("Finished writing " + PakPath); } - // Clear recook flag - mNeedsRecook = false; Save(); - Log::Write("Finished writing " + PakPath); - // Update resource store in case we recooked any assets mpProject->ResourceStore()->ConditionalSaveStore(); } diff --git a/src/Core/GameProject/CPackage.h b/src/Core/GameProject/CPackage.h index 26156fe5..5884e144 100644 --- a/src/Core/GameProject/CPackage.h +++ b/src/Core/GameProject/CPackage.h @@ -5,6 +5,7 @@ #include #include #include +#include "Core/IProgressNotifier.h" class CGameProject; @@ -57,7 +58,7 @@ public: void AddResource(const TString& rkName, const CAssetID& rkID, const CFourCC& rkType); void UpdateDependencyCache() const; - void Cook(); + void Cook(IProgressNotifier *pProgress); void CompareOriginalAssetList(const std::list& rkNewList); bool ContainsAsset(const CAssetID& rkID) const; diff --git a/src/Core/IProgressNotifier.h b/src/Core/IProgressNotifier.h new file mode 100644 index 00000000..d1065d4e --- /dev/null +++ b/src/Core/IProgressNotifier.h @@ -0,0 +1,48 @@ +#ifndef IPROGRESSNOTIFIER_H +#define IPROGRESSNOTIFIER_H + +#include + +class IProgressNotifier +{ + TString mTaskName; + int mTaskIndex; + int mTaskCount; + +public: + IProgressNotifier() + : mTaskIndex(0) + , mTaskCount(1) + {} + + void SetNumTasks(int NumTasks) + { + mTaskName = ""; + mTaskIndex = 0; + mTaskCount = NumTasks; + } + + void SetTask(int TaskIndex, TString TaskName) + { + mTaskName = TaskName; + mTaskIndex = TaskIndex; + } + + void Report(int StepIndex, int StepCount, const TString& rkStepDesc) + { + ASSERT(mTaskCount >= 1); + + // Calculate percentage + float TaskPercent = 1.f / (float) mTaskCount; + float StepPercent = (StepCount >= 0 ? (float) StepIndex / (float) StepCount : 0.f); + float ProgressPercent = (TaskPercent * mTaskIndex) + (TaskPercent * StepPercent); + UpdateProgress(mTaskName, rkStepDesc, ProgressPercent); + } + + virtual bool ShouldCancel() const = 0; + +protected: + virtual void UpdateProgress(const TString& rkTaskName, const TString& rkStepDesc, float ProgressPercent) = 0; +}; + +#endif // IPROGRESSNOTIFIER_H diff --git a/src/Editor/CEditorApplication.cpp b/src/Editor/CEditorApplication.cpp index b3c55c91..ea016f6b 100644 --- a/src/Editor/CEditorApplication.cpp +++ b/src/Editor/CEditorApplication.cpp @@ -1,6 +1,7 @@ #include "CEditorApplication.h" #include "IEditor.h" #include "CBasicViewport.h" +#include "CProgressDialog.h" #include "CProjectSettingsDialog.h" #include "Editor/CharacterEditor/CCharacterEditor.h" #include "Editor/ModelEditor/CModelEditorWindow.h" @@ -9,7 +10,9 @@ #include #include #include -#include + +#include +#include CEditorApplication::CEditorApplication(int& rArgc, char **ppArgv) : QApplication(rArgc, ppArgv) @@ -141,19 +144,51 @@ void CEditorApplication::NotifyAssetsModified() emit AssetsModified(); } -void CEditorApplication::CookAllDirtyPackages() +bool CEditorApplication::CookPackage(CPackage *pPkg) +{ + return CookPackageList(QList() << pPkg); +} + +bool CEditorApplication::CookAllDirtyPackages() { ASSERT(mpActiveProject != nullptr); + QList PackageList; for (u32 iPkg = 0; iPkg < mpActiveProject->NumPackages(); iPkg++) { CPackage *pPackage = mpActiveProject->PackageByIndex(iPkg); if (pPackage->NeedsRecook()) - pPackage->Cook(); + PackageList << pPackage; } - emit PackagesCooked(); + return CookPackageList(PackageList); +} + +bool CEditorApplication::CookPackageList(QList PackageList) +{ + if (!PackageList.isEmpty()) + { + CProgressDialog Dialog("Cooking package" + QString(PackageList.size() > 1 ? "s" : ""), true, mpWorldEditor); + + QFuture Future = QtConcurrent::run([&]() + { + Dialog.SetNumTasks(PackageList.size()); + + for (int PkgIdx = 0; PkgIdx < PackageList.size() && !Dialog.ShouldCancel(); PkgIdx++) + { + CPackage *pPkg = PackageList[PkgIdx]; + Dialog.SetTask(PkgIdx, "Cooking " + pPkg->Name() + ".pak..."); + pPkg->Cook(&Dialog); + } + }); + + Dialog.WaitForResults(Future); + + emit PackagesCooked(); + return !Dialog.ShouldCancel(); + } + else return true; } // ************ SLOTS ************ diff --git a/src/Editor/CEditorApplication.h b/src/Editor/CEditorApplication.h index 77164e5e..ad40b31a 100644 --- a/src/Editor/CEditorApplication.h +++ b/src/Editor/CEditorApplication.h @@ -37,7 +37,10 @@ public: bool OpenProject(const QString& rkProjPath); void EditResource(CResourceEntry *pEntry); void NotifyAssetsModified(); - void CookAllDirtyPackages(); + + bool CookPackage(CPackage *pPackage); + bool CookAllDirtyPackages(); + bool CookPackageList(QList PackageList); // Accessors inline CGameProject* ActiveProject() const { return mpActiveProject; } diff --git a/src/Editor/CExportGameDialog.cpp b/src/Editor/CExportGameDialog.cpp index 843b74d1..57413dc7 100644 --- a/src/Editor/CExportGameDialog.cpp +++ b/src/Editor/CExportGameDialog.cpp @@ -1,5 +1,6 @@ #include "CExportGameDialog.h" #include "ui_CExportGameDialog.h" +#include "CProgressDialog.h" #include "UICommon.h" #include @@ -13,6 +14,7 @@ #include #include #include +#include #include @@ -383,10 +385,16 @@ void CExportGameDialog::Export() CGameExporter Exporter(mGame, mRegion, mGameTitle, mGameID, mBuildVer); TString StrExportDir = TO_TSTRING(ExportDir); StrExportDir.EnsureEndsWith('/'); - mExportSuccess = Exporter.Export(mpDisc, StrExportDir, &NameMap, &GameInfo); + + CProgressDialog Dialog("Creating new game project", true, parentWidget()); + QFuture Future = QtConcurrent::run(&Exporter, &CGameExporter::Export, mpDisc, StrExportDir, &NameMap, &GameInfo, &Dialog); + mExportSuccess = Dialog.WaitForResults(Future); if (!mExportSuccess) - UICommon::ErrorMsg(this, "Export failed!"); + { + if (!Dialog.ShouldCancel()) + UICommon::ErrorMsg(this, "Export failed!"); + } else mNewProjectPath = TO_QSTRING(Exporter.ProjectPath()); } diff --git a/src/Editor/CPakToolDialog.h b/src/Editor/CPakToolDialog.h deleted file mode 100644 index 123c9c04..00000000 --- a/src/Editor/CPakToolDialog.h +++ /dev/null @@ -1,280 +0,0 @@ -#ifndef CPAKTOOLDIALOG -#define CPAKTOOLDIALOG - -#include -#include -#include -#include "Editor/UICommon.h" - -#include -#include -#include -#include -#include -#include - -class CPakToolDialog : public QProgressDialog -{ - Q_OBJECT - -public: - enum EResult { eSuccess, eError, eUserCancelled }; - -private: - enum { eExtract, eRepack, eListDump } mMode; - QProcess *mpPakTool; - QString mPakFilename; - QString mListFilename; - QString mFolderPath; - EGame mGame; - EResult mResult; - - QString mPartialString; - bool mUpdating; - - int mCur; - int mMax; - bool mSetMax; - - // Private Functions - CPakToolDialog(QWidget *pParent = 0) - : QProgressDialog(pParent) - , mpPakTool(nullptr) - , mSetMax(false) - , mUpdating(false) - { - } - - virtual QSize sizeHint() const - { - QSize Size = QProgressDialog::sizeHint(); - Size.rwidth() *= 3; - return Size; - } - - void Run() - { - mpPakTool = new QProcess(this); - QStringList Args; - - if (mMode == eExtract) - { - Args << "-x" << mPakFilename; - setLabelText("Extracting..."); - } - else if (mMode == eRepack) - { - Args << "-r" << GameString(mGame) << mFolderPath << mPakFilename << mListFilename; - setLabelText("Repacking..."); - } - else if (mMode == eListDump) - Args << "-d" << mPakFilename; - - setWindowTitle(labelText()); - - // List dump is really fast, so showing progress dialog isn't necessary for it. - if (mMode != eListDump) - { - connect(mpPakTool, SIGNAL(readyReadStandardOutput()), this, SLOT(ReadStdOut())); - connect(mpPakTool, SIGNAL(finished(int)), this, SLOT(PakToolFinished(int))); - mpPakTool->start("PakTool.exe", Args); - exec(); - } - - else - { - mpPakTool->start("PakTool.exe", Args); - mpPakTool->waitForFinished(-1); - } - } - - EResult Result() const - { - if (wasCanceled()) return eUserCancelled; - else return (EResult) result(); - } - -private slots: - void ReadStdOut() - { - if (mUpdating) return; - mUpdating = true; - - QString StdOut = mpPakTool->readAllStandardOutput(); - QStringList Strings = StdOut.split(QRegExp("[\\r\\n]"), QString::SkipEmptyParts); - - if (!Strings.isEmpty()) - { - Strings.front().prepend(mPartialString); - - QCharRef LastChar = StdOut[StdOut.size() - 1]; - if (LastChar != '\n' && LastChar != '\r') - { - mPartialString = Strings.back(); - - if (Strings.size() > 1) - Strings.pop_back(); - } - else - mPartialString = ""; - - const QRegExp kExtracting("^Extracting file ([0-9]{1,6}) of ([0-9]{1,6})"); - const QRegExp kRepacking("^Repacking file ([0-9]{1,6}) of ([0-9]{1,6})"); - const QRegExp& rkRegExp = (mMode == eExtract ? kExtracting : kRepacking); - - int Result = rkRegExp.indexIn(Strings.last()); - - if (Result != -1) - { - mCur = rkRegExp.cap(1).toInt(); - mMax = rkRegExp.cap(2).toInt(); - - if (!mSetMax) - { - setMinimum(0); - setMaximum(mMax); - mSetMax = true; - } - - // Deferring UI updates is necessary because trying to do them on this thread causes crashes for a lot of people - QTimer::singleShot(0, this, SLOT(UpdateUI())); - } - } - - mUpdating = false; - } - - void UpdateUI() - { - setLabelText(QString("%1 file %2 of %3...").arg(mMode == eExtract ? "Extracting" : "Repacking").arg(mCur).arg(mMax)); - setWindowTitle(labelText()); - setValue(mCur); - } - - void PakToolFinished(int ExitCode) - { - while (mUpdating) {} - done(ExitCode == 0 ? eSuccess : eError); - } - -public: - static EResult Extract(QString PakFilename, QString *pOutFolder = 0, QWidget *pParent = 0) - { - Log::Write("Extracting pak " + TO_TSTRING(PakFilename)); - - CPakToolDialog Dialog(pParent); - Dialog.mMode = eExtract; - Dialog.mPakFilename = PakFilename; - Dialog.Run(); - EResult Result = Dialog.Result(); - - if (pOutFolder) - { - QFileInfo Pak(PakFilename); - *pOutFolder = Pak.absolutePath() + '/' + Pak.baseName() + "-pak/"; - } - - Log::Write("Result: " + TO_TSTRING(ResultString(Result))); - return Result; - } - - static EResult Repack(EGame Game, QString TargetPak, QString ListFile, QString FolderPath, QString *pOutPak = 0, QWidget *pParent = 0) - { - Log::Write("Repacking folder " + TO_TSTRING(FolderPath) + " into pak " + TO_TSTRING(TargetPak)); - - CPakToolDialog Dialog(pParent); - Dialog.mMode = eRepack; - Dialog.mPakFilename = TargetPak; - Dialog.mListFilename = ListFile; - Dialog.mFolderPath = FolderPath; - Dialog.mGame = Game; - Dialog.Run(); - EResult Result = Dialog.Result(); - - if (pOutPak) - *pOutPak = TargetPak; - - Log::Write("Result: " + TO_TSTRING(ResultString(Result))); - return Result; - } - - static EResult DumpList(QString PakFilename, QString *pOutTxt = 0, QWidget *pParent = 0) - { - Log::Write("Dumping file list for pak " + TO_TSTRING(PakFilename)); - - CPakToolDialog Dialog(pParent); - Dialog.mMode = eListDump; - Dialog.mPakFilename = PakFilename; - Dialog.Run(); - EResult Result = Dialog.Result(); - - if (pOutTxt) - { - QFileInfo Pak(PakFilename); - *pOutTxt = Pak.absolutePath() + '/' + Pak.baseName() + "-pak.txt"; - } - - Log::Write("Result: " + TO_TSTRING(ResultString(Result))); - return Result; - } - - static QString TargetPakForFolder(QString Folder) - { - QDir Dir(Folder); - QString PakName = Dir.dirName(); - if (PakName.endsWith("-pak")) PakName.chop(4); - Dir.cdUp(); - QString DirName = Dir.absolutePath(); - - QString PakPath = DirName + '/' + PakName + ".pak"; - - if (QFile::exists(PakPath)) - return PakPath; - else - return ""; - } - - static QString TargetListForFolder(QString Folder) - { - QDir Dir(Folder); - QString TxtName = Dir.dirName(); - Dir.cdUp(); - QString DirName = Dir.absolutePath(); - - QString ListPath = DirName + '/' + TxtName + ".txt"; - - if (QFile::exists(ListPath)) - return ListPath; - else - return ""; - } - - static QString GameString(EGame Game) - { - switch (Game) - { - case ePrimeDemo: return "mp1demo"; - case ePrime: return "mp1"; - case eEchoesDemo: return "mp2demo"; - case eEchoes: return "mp2"; - case eCorruptionProto: return "mp3proto"; - case eCorruption: return "mp3"; - case eReturns: return "dkcr"; - default: return "INVALID"; - } - } - - static QString ResultString(EResult Result) - { - switch (Result) - { - case eSuccess: return "Success"; - case eError: return "Error"; - case eUserCancelled: return "User Cancelled"; - default: return ""; - } - } -}; - -#endif // CPAKTOOLDIALOG - diff --git a/src/Editor/CProgressDialog.cpp b/src/Editor/CProgressDialog.cpp new file mode 100644 index 00000000..8d5746d8 --- /dev/null +++ b/src/Editor/CProgressDialog.cpp @@ -0,0 +1,91 @@ +#include "CProgressDialog.h" +#include "ui_CProgressDialog.h" +#include "CEditorApplication.h" +#include + +CProgressDialog::CProgressDialog(QString OperationName, bool AlertOnFinish, QWidget *pParent) + : IProgressNotifierUI(pParent) + , mpUI(new Ui::CProgressDialog) + , mAlertOnFinish(AlertOnFinish) + , mFinished(false) + , mCanceled(false) +{ + mpUI->setupUi(this); + mpUI->ProgressBar->setMinimum(0); + mpUI->ProgressBar->setMaximum(10000); + setWindowTitle(OperationName); + +#if WIN32 + QWinTaskbarButton *pButton = new QWinTaskbarButton(this); + QWindow *pWindow = parentWidget()->windowHandle(); + ASSERT(pWindow); + pButton->setWindow(pWindow); + + mpTaskbarProgress = pButton->progress(); + mpTaskbarProgress->setMinimum(0); + mpTaskbarProgress->setMaximum(10000); + mpTaskbarProgress->show(); + + setWindowFlags(windowFlags() | Qt::MSWindowsFixedSizeDialogHint); +#endif + + connect(mpUI->CancelButton, SIGNAL(pressed()), this, SLOT(CancelButtonClicked())); +} + +CProgressDialog::~CProgressDialog() +{ + delete mpUI; +} + +void CProgressDialog::DisallowCanceling() +{ + mpUI->CancelButton->setHidden(true); +} + +bool CProgressDialog::ShouldCancel() const +{ + return mCanceled; +} + +void CProgressDialog::closeEvent(QCloseEvent *pEvent) +{ + if (!mFinished) + { + CancelButtonClicked(); + pEvent->ignore(); + } + else + { + pEvent->accept(); + +#if WIN32 + mpTaskbarProgress->reset(); + mpTaskbarProgress->hide(); +#endif + } +} + +void CProgressDialog::FinishAndClose() +{ + mFinished = true; + close(); +} + +void CProgressDialog::CancelButtonClicked() +{ + mCanceled = true; + mpUI->CancelButton->setEnabled(false); +} + +void CProgressDialog::UpdateUI(const QString& rkTaskDesc, const QString& rkStepDesc, float ProgressPercent) +{ + mpUI->TaskLabel->setText(rkTaskDesc); + mpUI->StepLabel->setText(rkStepDesc); + + int ProgressValue = 10000 * ProgressPercent; + mpUI->ProgressBar->setValue(ProgressValue); + +#if WIN32 + mpTaskbarProgress->setValue(ProgressValue); +#endif +} diff --git a/src/Editor/CProgressDialog.h b/src/Editor/CProgressDialog.h new file mode 100644 index 00000000..297f6332 --- /dev/null +++ b/src/Editor/CProgressDialog.h @@ -0,0 +1,83 @@ +#ifndef CPROGRESSDIALOG_H +#define CPROGRESSDIALOG_H + +#include "IProgressNotifierUI.h" +#include "UICommon.h" +#include +#include + +#include +#include +#include + +#if WIN32 +#include +#include +#endif + +namespace Ui { +class CProgressDialog; +} + +class CProgressDialog : public IProgressNotifierUI +{ + Q_OBJECT + Ui::CProgressDialog *mpUI; + bool mAlertOnFinish; + bool mFinished; + bool mCanceled; + +#if WIN32 + QWinTaskbarProgress *mpTaskbarProgress; +#endif + +public: + explicit CProgressDialog(QString OperationName, bool AlertOnFinish, QWidget *pParent = 0); + ~CProgressDialog(); + + void DisallowCanceling(); + + // IProgressNotifier interface + virtual bool ShouldCancel() const; + + // Slots +public slots: + void closeEvent(QCloseEvent *pEvent); + void FinishAndClose(); + void CancelButtonClicked(); + void UpdateUI(const QString& rkTaskDesc, const QString& rkStepDesc, float ProgressPercent); + + // Results +protected: + template + void InternalWaitForResults(QFuture Future) + { + gpEdApp->SetEditorTicksEnabled(false); + + QFutureWatcher Watcher; + connect(&Watcher, SIGNAL(finished()), this, SLOT(FinishAndClose())); + Watcher.setFuture(Future); + exec(); + + gpEdApp->SetEditorTicksEnabled(true); + + if (mAlertOnFinish) + gpEdApp->alert(parentWidget()); + } + +public: + template + RetType WaitForResults(QFuture Future) + { + InternalWaitForResults(Future); + return Future.result(); + } + + template<> + void WaitForResults(QFuture Future) + { + InternalWaitForResults(Future); + } +}; + +#endif // CPROGRESSDIALOG_H diff --git a/src/Editor/CProgressDialog.ui b/src/Editor/CProgressDialog.ui new file mode 100644 index 00000000..ce190fbc --- /dev/null +++ b/src/Editor/CProgressDialog.ui @@ -0,0 +1,76 @@ + + + CProgressDialog + + + + 0 + 0 + 609 + 110 + + + + Dialog + + + + + + + 10 + + + + + + + + + + + + 8 + + + + + + + + + + + 24 + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + + + + + + + + diff --git a/src/Editor/CProjectSettingsDialog.cpp b/src/Editor/CProjectSettingsDialog.cpp index 28714537..8b6e33d1 100644 --- a/src/Editor/CProjectSettingsDialog.cpp +++ b/src/Editor/CProjectSettingsDialog.cpp @@ -2,12 +2,16 @@ #include "ui_CProjectSettingsDialog.h" #include "CEditorApplication.h" #include "CExportGameDialog.h" +#include "CProgressDialog.h" #include "UICommon.h" #include "Editor/ResourceBrowser/CResourceBrowser.h" #include #include #include +#include +#include #include +#include CProjectSettingsDialog::CProjectSettingsDialog(QWidget *pParent) : QDialog(pParent) @@ -22,6 +26,7 @@ CProjectSettingsDialog::CProjectSettingsDialog(QWidget *pParent) connect(gpEdApp, SIGNAL(ActiveProjectChanged(CGameProject*)), this, SLOT(ActiveProjectChanged(CGameProject*))); connect(gpEdApp, SIGNAL(AssetsModified()), this, SLOT(SetupPackagesList())); + connect(gpEdApp, SIGNAL(PackagesCooked()), this, SLOT(SetupPackagesList())); // Set build ISO button color QPalette Palette = mpUI->BuildIsoButton->palette(); @@ -91,15 +96,13 @@ void CProjectSettingsDialog::CookPackage() if (PackageIdx != -1) { CPackage *pPackage = mpProject->PackageByIndex(PackageIdx); - pPackage->Cook(); - SetupPackagesList(); + gpEdApp->CookPackage(pPackage); } } void CProjectSettingsDialog::CookAllDirtyPackages() { gpEdApp->CookAllDirtyPackages(); - SetupPackagesList(); } void CProjectSettingsDialog::BuildISO() @@ -107,12 +110,17 @@ void CProjectSettingsDialog::BuildISO() CGameProject *pProj = gpEdApp->ActiveProject(); ASSERT(pProj && !pProj->IsWiiBuild()); - QString DefaultPath = TO_QSTRING(pProj->ProjectRoot() + pProj->Name()) + ".gcm"; + QString DefaultPath = TO_QSTRING( pProj->ProjectRoot() + FileUtil::SanitizeName(pProj->Name(), false) + ".gcm" ); QString IsoPath = UICommon::SaveFileDialog(this, "Choose output ISO path", "*.gcm", DefaultPath); if (!IsoPath.isEmpty()) { - if (!pProj->BuildISO( TO_TSTRING(IsoPath) )) - UICommon::ErrorMsg(this, "Failed to build ISO! Check the log for details."); + if (gpEdApp->CookAllDirtyPackages()) + { + CProgressDialog Dialog("Building ISO", true, this); + Dialog.DisallowCanceling(); + QFuture Future = QtConcurrent::run(pProj, &CGameProject::BuildISO, TO_TSTRING(IsoPath), &Dialog); + Dialog.WaitForResults(Future); + } } } diff --git a/src/Editor/Editor.pro b/src/Editor/Editor.pro index 98c88019..61eea53c 100644 --- a/src/Editor/Editor.pro +++ b/src/Editor/Editor.pro @@ -9,7 +9,10 @@ QMAKE_CXXFLAGS += /WX DEFINES += PWE_EDITOR RESOURCES += Icons.qrc -win32: RC_ICONS += icons/AppIcon.ico +win32: { + RC_ICONS += icons/AppIcon.ico + QT += winextras +} TEMPLATE = app DESTDIR = $$PWD/../../bin @@ -156,8 +159,6 @@ HEADERS += \ Undo/CCloneSelectionCommand.h \ CNodeCopyMimeData.h \ Undo/CPasteNodesCommand.h \ - CPakToolDialog.h \ - WorldEditor/CRepackInfoDialog.h \ CAboutDialog.h \ CharacterEditor/CCharacterEditor.h \ CharacterEditor/CCharacterEditorViewport.h \ @@ -179,7 +180,9 @@ HEADERS += \ Widgets/CTimedLineEdit.h \ CProjectSettingsDialog.h \ WorldEditor/CPoiMapSidebar.h \ - WorldEditor/CWorldEditorSidebar.h + WorldEditor/CWorldEditorSidebar.h \ + CProgressDialog.h \ + IProgressNotifierUI.h # Source Files SOURCES += \ @@ -231,7 +234,6 @@ SOURCES += \ Undo/CCreateInstanceCommand.cpp \ Undo/CCloneSelectionCommand.cpp \ Undo/CPasteNodesCommand.cpp \ - WorldEditor/CRepackInfoDialog.cpp \ CAboutDialog.cpp \ CharacterEditor/CCharacterEditor.cpp \ CharacterEditor/CCharacterEditorViewport.cpp \ @@ -246,7 +248,8 @@ SOURCES += \ WorldEditor/CWorldTreeModel.cpp \ CProjectSettingsDialog.cpp \ WorldEditor/CPoiMapSidebar.cpp \ - WorldEditor/CWorldEditorSidebar.cpp + WorldEditor/CWorldEditorSidebar.cpp \ + CProgressDialog.cpp # UI Files FORMS += \ @@ -263,7 +266,6 @@ FORMS += \ WorldEditor/CTemplateEditDialog.ui \ WorldEditor/CLinkDialog.ui \ WorldEditor/CSelectInstanceDialog.ui \ - WorldEditor/CRepackInfoDialog.ui \ CAboutDialog.ui \ CharacterEditor/CCharacterEditor.ui \ WorldEditor/CCollisionRenderSettingsDialog.ui \ @@ -271,4 +273,5 @@ FORMS += \ CExportGameDialog.ui \ WorldEditor/CWorldInfoSidebar.ui \ CProjectSettingsDialog.ui \ - WorldEditor/CPoiMapSidebar.ui + WorldEditor/CPoiMapSidebar.ui \ + CProgressDialog.ui diff --git a/src/Editor/IProgressNotifierUI.h b/src/Editor/IProgressNotifierUI.h new file mode 100644 index 00000000..5651edb4 --- /dev/null +++ b/src/Editor/IProgressNotifierUI.h @@ -0,0 +1,30 @@ +#ifndef IPROGRESSNOTIFIERUI_H +#define IPROGRESSNOTIFIERUI_H + +#include "UICommon.h" +#include +#include + +// IProgressNotifier subclass for UI classes (dialogs, etc) +class IProgressNotifierUI : public QDialog, public IProgressNotifier +{ +public: + explicit IProgressNotifierUI(QWidget *pParent = 0) + : QDialog(pParent) + {} + +public slots: + virtual void UpdateUI(const QString& rkTaskDesc, const QString& rkStepDesc, float ProgressPercent) = 0; + +private: + virtual void UpdateProgress(const TString& rkTaskDesc, const TString& rkStepDesc, float ProgressPercent) final + { + // Defer the function call to make sure UI updates are done on the main thread + QMetaObject::invokeMethod(this, "UpdateUI", Qt::AutoConnection, + Q_ARG(QString, TO_QSTRING(rkTaskDesc)), + Q_ARG(QString, TO_QSTRING(rkStepDesc)), + Q_ARG(float, ProgressPercent) ); + } +}; + +#endif // IPROGRESSNOTIFIERUI_H diff --git a/src/Editor/WorldEditor/CRepackInfoDialog.cpp b/src/Editor/WorldEditor/CRepackInfoDialog.cpp deleted file mode 100644 index 635ad9e4..00000000 --- a/src/Editor/WorldEditor/CRepackInfoDialog.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "CRepackInfoDialog.h" -#include "ui_CRepackInfoDialog.h" -#include "Editor/CPakToolDialog.h" -#include -#include -#include -#include - -CRepackInfoDialog::CRepackInfoDialog(QString TargetFolder, QString ListFile, QString OutputPak, QWidget *pParent /*= 0*/) - : QDialog(pParent) - , ui(new Ui::CRepackInfoDialog) -{ - ui->setupUi(this); - - ui->FolderLineEdit->setText(TargetFolder); - ui->FileListLineEdit->setText(ListFile); - ui->OutputPakLineEdit->setText(OutputPak); - UpdateStatus(); - - connect(ui->FolderToolButton, SIGNAL(clicked()), this, SLOT(BrowseFolderClicked())); - connect(ui->FileListToolButton, SIGNAL(clicked()), this, SLOT(BrowseListClicked())); - connect(ui->OutputPakToolButton, SIGNAL(clicked()), this, SLOT(BrowseOutPakClicked())); - connect(ui->FolderLineEdit, SIGNAL(textChanged(QString)), this, SLOT(UpdateStatus())); - connect(ui->FileListLineEdit, SIGNAL(textChanged(QString)), this, SLOT(UpdateStatus())); - connect(ui->OutputPakLineEdit, SIGNAL(textChanged(QString)), this, SLOT(UpdateStatus())); -} - -CRepackInfoDialog::~CRepackInfoDialog() -{ - delete ui; -} - -bool CRepackInfoDialog::TargetFolderValid() const -{ - return QDir(ui->FolderLineEdit->text()).exists(); -} - -bool CRepackInfoDialog::ListFileValid() const -{ - return QFile::exists(ui->FileListLineEdit->text()); -} - -bool CRepackInfoDialog::OutputPakValid() const -{ - return QFile::exists(ui->OutputPakLineEdit->text()); -} - -QString CRepackInfoDialog::TargetFolder() const -{ - return ui->FolderLineEdit->text(); -} - -QString CRepackInfoDialog::ListFile() const -{ - return ui->FileListLineEdit->text(); -} - -QString CRepackInfoDialog::OutputPak() const -{ - return ui->OutputPakLineEdit->text(); -} - -// ************ PUBLIC SLOTS ************ -void CRepackInfoDialog::BrowseFolderClicked() -{ - QString Folder = UICommon::OpenDirDialog(this, "Choose directory"); - - if (!Folder.isEmpty()) - { - ui->FolderLineEdit->setText(Folder); - ui->FolderLineEdit->setFocus(); - } -} - -void CRepackInfoDialog::BrowseListClicked() -{ - QString List = UICommon::OpenFileDialog(this, "Open list file", "All supported files (*.txt *.pak);;Text file (*.txt);;Pak file (*.pak)"); - - if (!List.isEmpty()) - { - if (List.endsWith(".txt")) - ui->FileListLineEdit->setText(List); - - else if (List.endsWith(".pak")) - { - QString Txt; - CPakToolDialog::DumpList(List, &Txt); - ui->FileListLineEdit->setText(Txt); - } - - ui->FileListLineEdit->setFocus(); - } -} - -void CRepackInfoDialog::BrowseOutPakClicked() -{ - QString Pak = UICommon::SaveFileDialog(this, "Save pak", "Pak File (*.pak)"); - - if (!Pak.isEmpty()) - { - ui->OutputPakLineEdit->setText(Pak); - ui->OutputPakLineEdit->setFocus(); - } -} - -void CRepackInfoDialog::UpdateStatus() -{ - static const QString skInvalidStylesheet = "border: 1px solid red"; - - ui->FolderLineEdit->setStyleSheet(TargetFolderValid() ? "" : skInvalidStylesheet); - ui->FileListLineEdit->setStyleSheet(ListFileValid() ? "" : skInvalidStylesheet); - ui->OutputPakLineEdit->setStyleSheet(OutputPakValid() ? "" : skInvalidStylesheet); - ui->ButtonBox->button(QDialogButtonBox::Ok)->setEnabled(TargetFolderValid() && ListFileValid() && OutputPakValid()); -} diff --git a/src/Editor/WorldEditor/CRepackInfoDialog.h b/src/Editor/WorldEditor/CRepackInfoDialog.h deleted file mode 100644 index bd963937..00000000 --- a/src/Editor/WorldEditor/CRepackInfoDialog.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef CREPACKINFODIALOG_H -#define CREPACKINFODIALOG_H - -#include - -namespace Ui { -class CRepackInfoDialog; -} - -class CRepackInfoDialog : public QDialog -{ - Q_OBJECT - Ui::CRepackInfoDialog *ui; - -public: - explicit CRepackInfoDialog(QString TargetFolder, QString ListFile, QString OutputPak, QWidget *pParent = 0); - ~CRepackInfoDialog(); - - bool TargetFolderValid() const; - bool ListFileValid() const; - bool OutputPakValid() const; - - QString TargetFolder() const; - QString ListFile() const; - QString OutputPak() const; - -public slots: - void BrowseFolderClicked(); - void BrowseListClicked(); - void BrowseOutPakClicked(); - void UpdateStatus(); -}; - -#endif // CREPACKINFODIALOG_H diff --git a/src/Editor/WorldEditor/CRepackInfoDialog.ui b/src/Editor/WorldEditor/CRepackInfoDialog.ui deleted file mode 100644 index b57efdf3..00000000 --- a/src/Editor/WorldEditor/CRepackInfoDialog.ui +++ /dev/null @@ -1,127 +0,0 @@ - - - CRepackInfoDialog - - - - 0 - 0 - 400 - 125 - - - - Set Pak Info - - - - - - - - - - Folder - - - - - - - - - - File List - - - - - - - - - - Output Pak - - - - - - - - - - - - - - ... - - - - - - - ... - - - - - - - ... - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - ButtonBox - accepted() - CRepackInfoDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - ButtonBox - rejected() - CRepackInfoDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/templates/mp1/Script/ActorContraption.xml b/templates/mp1/Script/ActorContraption.xml index d1832e14..4b38f171 100644 --- a/templates/mp1/Script/ActorContraption.xml +++ b/templates/mp1/Script/ActorContraption.xml @@ -6,15 +6,15 @@ - - - - + + + + - +