Added progress bars for most major blocking operations

This commit is contained in:
Aruki 2017-05-21 18:01:09 -06:00
parent 31621874a6
commit 0a9b052413
23 changed files with 569 additions and 655 deletions

View File

@ -13,7 +13,7 @@
class CFourCC 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 union
{ {
u32 mFourCC; u32 mFourCC;

View File

@ -227,7 +227,8 @@ HEADERS += \
CompressionUtil.h \ CompressionUtil.h \
Resource/Animation/CSourceAnimData.h \ Resource/Animation/CSourceAnimData.h \
Resource/CMapArea.h \ Resource/CMapArea.h \
Resource/CSavedStateID.h Resource/CSavedStateID.h \
IProgressNotifier.h
# Source Files # Source Files
SOURCES += \ SOURCES += \

View File

@ -23,6 +23,7 @@ CGameExporter::CGameExporter(EGame Game, ERegion Region, const TString& rkGameNa
, mGameName(rkGameName) , mGameName(rkGameName)
, mGameID(rkGameID) , mGameID(rkGameID)
, mBuildVersion(BuildVersion) , mBuildVersion(BuildVersion)
, mpProgress(nullptr)
{ {
ASSERT(mGame != eUnknownGame); ASSERT(mGame != eUnknownGame);
ASSERT(mRegion != eRegion_Unknown); ASSERT(mRegion != eRegion_Unknown);
@ -32,7 +33,7 @@ CGameExporter::CGameExporter(EGame Game, ERegion Region, const TString& rkGameNa
#error Fix export directory being cleared! #error Fix export directory being cleared!
#endif #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); SCOPED_TIMER(ExportGame);
@ -44,6 +45,10 @@ bool CGameExporter::Export(nod::DiscBase *pDisc, const TString& rkOutputDir, CAs
mDiscDir = "Disc/"; mDiscDir = "Disc/";
mWorldsDirName = "Worlds/"; mWorldsDirName = "Worlds/";
// Init progress
mpProgress = pProgress;
mpProgress->SetNumTasks(eES_NumSteps);
// Create project // Create project
FileUtil::MakeDirectory(mExportDir); FileUtil::MakeDirectory(mExportDir);
FileUtil::ClearDirectory(mExportDir); FileUtil::ClearDirectory(mExportDir);
@ -72,17 +77,22 @@ bool CGameExporter::Export(nod::DiscBase *pDisc, const TString& rkOutputDir, CAs
CResourceStore *pOldStore = gpResourceStore; CResourceStore *pOldStore = gpResourceStore;
gpResourceStore = mpStore; gpResourceStore = mpStore;
// Export game data // Export cooked data
LoadPaks(); LoadPaks();
ExportCookedResources(); ExportCookedResources();
mpProject->AudioManager()->LoadAssets();
ExportResourceEditorData(); // Export editor data
if (!mpProgress->ShouldCancel())
{
mpProject->AudioManager()->LoadAssets();
ExportResourceEditorData();
}
// Export finished! // Export finished!
mProjectPath = mpProject->ProjectPath(); mProjectPath = mpProject->ProjectPath();
delete mpProject; delete mpProject;
if (pOldStore) gpResourceStore = pOldStore; if (pOldStore) gpResourceStore = pOldStore;
return true; return !mpProgress->ShouldCancel();
} }
void CGameExporter::LoadResource(const CAssetID& rkID, std::vector<u8>& rBuffer) void CGameExporter::LoadResource(const CAssetID& rkID, std::vector<u8>& rBuffer)
@ -97,6 +107,9 @@ bool CGameExporter::ExtractDiscData()
// todo: handle dol, apploader, multiple partitions, wii ticket blob // todo: handle dol, apploader, multiple partitions, wii ticket blob
SCOPED_TIMER(ExtractDiscData); SCOPED_TIMER(ExtractDiscData);
// Init progress
mpProgress->SetTask(eES_ExtractDisc, "Extracting disc files");
// Create Disc output folder // Create Disc output folder
TString AbsDiscDir = mExportDir + mDiscDir; TString AbsDiscDir = mExportDir + mDiscDir;
FileUtil::MakeDirectory(AbsDiscDir); FileUtil::MakeDirectory(AbsDiscDir);
@ -106,43 +119,52 @@ bool CGameExporter::ExtractDiscData()
nod::ExtractionContext Context; nod::ExtractionContext Context;
Context.force = false; Context.force = false;
Context.verbose = 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); bool Success = ExtractDiscNodeRecursive(&pDataPartition->getFSTRoot(), AbsDiscDir, Context);
if (!Success) return false; if (!Success) return false;
// Extract dol // Extract the remaining disc data
mDolPath = "boot.dol"; if (!mpProgress->ShouldCancel())
CFileOutStream DolFile(mExportDir + mDolPath);
if (!DolFile.IsValid()) return false;
std::unique_ptr<uint8_t[]> 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<uint8_t[]> 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; // Extract dol
mPartitionHeaderPath = "partition_header.bin"; mDolPath = "boot.dol";
nod::DiscWii *pDiscWii = static_cast<nod::DiscWii*>(mpDisc); CFileOutStream DolFile(mExportDir + mDolPath);
Success = pDiscWii->writeOutDataPartitionHeader(*TString(mExportDir + mPartitionHeaderPath).ToUTF16()); if (!DolFile.IsValid()) return false;
if (!Success) return false;
std::unique_ptr<uint8_t[]> 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<uint8_t[]> 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<nod::DiscWii*>(mpDisc);
Success = pDiscWii->writeOutDataPartitionHeader(*TString(mExportDir + mPartitionHeaderPath).ToUTF16());
if (!Success) return false;
}
else
mFilesystemAddress = (u32) pDataPartition->getFSTMemoryAddr();
return true;
} }
else else
mFilesystemAddress = (u32) pDataPartition->getFSTMemoryAddr(); return false;
return true;
} }
bool CGameExporter::ExtractDiscNodeRecursive(const nod::Node *pkNode, const TString& rkDir, const nod::ExtractionContext& rkContext) bool CGameExporter::ExtractDiscNodeRecursive(const nod::Node *pkNode, const TString& rkDir, const nod::ExtractionContext& rkContext)
@ -447,9 +469,18 @@ void CGameExporter::ExportCookedResources()
SCOPED_TIMER(ExportCookedResources); SCOPED_TIMER(ExportCookedResources);
FileUtil::MakeDirectory(mCookedDir); 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; 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); ExportResource(rRes);
} }
} }
@ -462,12 +493,19 @@ void CGameExporter::ExportResourceEditorData()
// because we have to load the resource to build its dependency tree and // 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 // some resources will fail to load if their dependencies don't exist
SCOPED_TIMER(SaveRawResources); 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 // 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 // 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. // 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 // 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. // have duplicates, as well as the world's internal name.
if (It->ResourceType() == eWorld) if (It->ResourceType() == eWorld)
@ -496,6 +534,8 @@ void CGameExporter::ExportResourceEditorData()
It->UpdateDependencies(); It->UpdateDependencies();
} }
} }
if (!mpProgress->ShouldCancel())
{ {
// All resources should have dependencies generated, so save the project files // All resources should have dependencies generated, so save the project files
SCOPED_TIMER(SaveResourceDatabase); SCOPED_TIMER(SaveResourceDatabase);

View File

@ -57,9 +57,21 @@ class CGameExporter
}; };
std::map<CAssetID, SResourceInstance> mResourceMap; std::map<CAssetID, SResourceInstance> mResourceMap;
// Progress
IProgressNotifier *mpProgress;
enum EExportStep
{
eES_ExtractDisc,
eES_ExportCooked,
eES_GenerateRaw,
// eES_Max must be last
eES_NumSteps
};
public: public:
CGameExporter(EGame Game, ERegion Region, const TString& rkGameName, const TString& rkGameID, float BuildVersion); 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<u8>& rBuffer); void LoadResource(const CAssetID& rkID, std::vector<u8>& rBuffer);
inline TString ProjectPath() const { return mProjectPath; } inline TString ProjectPath() const { return mProjectPath; }

View File

@ -95,9 +95,7 @@ void CGameProject::Serialize(IArchive& rArc)
} }
} }
void ProgressDummy(size_t, const nod::SystemString&, size_t) {} bool CGameProject::BuildISO(const TString& rkIsoPath, IProgressNotifier *pProgress)
bool CGameProject::BuildISO(const TString& rkIsoPath)
{ {
ASSERT( FileUtil::IsValidPath(rkIsoPath, false) ); ASSERT( FileUtil::IsValidPath(rkIsoPath, false) );
@ -109,7 +107,13 @@ bool CGameProject::BuildISO(const TString& rkIsoPath)
else 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 ProjRoot = ProjectRoot().ToUTF16();
TWideString DiscRoot = DiscDir(false).ToUTF16(); TWideString DiscRoot = DiscDir(false).ToUTF16();

View File

@ -5,6 +5,7 @@
#include "CPackage.h" #include "CPackage.h"
#include "CResourceStore.h" #include "CResourceStore.h"
#include "Core/CAudioManager.h" #include "Core/CAudioManager.h"
#include "Core/IProgressNotifier.h"
#include "Core/Resource/Script/CMasterTemplate.h" #include "Core/Resource/Script/CMasterTemplate.h"
#include <Common/CAssetID.h> #include <Common/CAssetID.h>
#include <Common/EGame.h> #include <Common/EGame.h>
@ -65,7 +66,7 @@ public:
bool Save(); bool Save();
void Serialize(IArchive& rArc); void Serialize(IArchive& rArc);
bool BuildISO(const TString& rkIsoPath); 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;
CPackage* FindPackage(const TString& rkName) const; CPackage* FindPackage(const TString& rkName) const;

View File

@ -59,9 +59,13 @@ void CPackage::UpdateDependencyCache() const
mCacheDirty = false; mCacheDirty = false;
} }
void CPackage::Cook() void CPackage::Cook(IProgressNotifier *pProgress)
{ {
SCOPED_TIMER(CookPackage);
// Build asset list // Build asset list
pProgress->Report(-1, -1, "Building dependency list");
CPackageDependencyListBuilder Builder(this); CPackageDependencyListBuilder Builder(this);
std::list<CAssetID> AssetList; std::list<CAssetID> AssetList;
Builder.BuildDependencyList(true, AssetList); Builder.BuildDependencyList(true, AssetList);
@ -161,7 +165,7 @@ void CPackage::Cook()
u32 ResIdx = 0; u32 ResIdx = 0;
u32 ResDataOffset = Pak.Tell(); 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 // Initialize entry, recook assets if needed
u32 AssetOffset = Pak.Tell(); u32 AssetOffset = Pak.Tell();
@ -170,8 +174,18 @@ void CPackage::Cook()
ASSERT(pEntry != nullptr); ASSERT(pEntry != nullptr);
if (pEntry->NeedsRecook()) if (pEntry->NeedsRecook())
{
pProgress->Report(ResIdx, AssetList.size(), "Cooking asset: " + pEntry->Name() + "." + pEntry->CookedExtension());
pEntry->Cook(); 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]; SResourceTableInfo& rTableInfo = ResourceTableData[ResIdx];
rTableInfo.pEntry = pEntry; rTableInfo.pEntry = pEntry;
rTableInfo.Offset = (Game <= eEchoes ? AssetOffset : AssetOffset - ResDataOffset); rTableInfo.Offset = (Game <= eEchoes ? AssetOffset : AssetOffset - ResDataOffset);
@ -267,40 +281,51 @@ void CPackage::Cook()
} }
ResDataSize = Pak.Tell() - ResDataOffset; ResDataSize = Pak.Tell() - ResDataOffset;
// Write table of contents for real // If we cancelled, don't finish writing the pak; delete the file instead and make sure the package is flagged for recook
if (Game >= eCorruption) if (pProgress->ShouldCancel())
{ {
Pak.Seek(TocOffset, SEEK_SET); Pak.Close();
Pak.WriteLong(3); // Always 3 pak sections FileUtil::DeleteFile(PakPath);
Pak.WriteFourCC( FOURCC('STRG') ); mNeedsRecook = true;
Pak.WriteLong(NamesSize);
Pak.WriteFourCC( FOURCC('RSHD') );
Pak.WriteLong(ResTableSize);
Pak.WriteFourCC( FOURCC('DATA') );
Pak.WriteLong(ResDataSize);
} }
// Write resource table for real else
Pak.Seek(ResTableOffset+4, SEEK_SET);
for (u32 iRes = 0; iRes < AssetList.size(); iRes++)
{ {
const SResourceTableInfo& rkInfo = ResourceTableData[iRes]; // Write table of contents for real
CResourceEntry *pEntry = rkInfo.pEntry; 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 ); // Write resource table for real
pEntry->CookedExtension().Write(Pak); Pak.Seek(ResTableOffset+4, SEEK_SET);
pEntry->ID().Write(Pak);
Pak.WriteLong(rkInfo.Size); for (u32 iRes = 0; iRes < AssetList.size(); iRes++)
Pak.WriteLong(rkInfo.Offset); {
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(); Save();
Log::Write("Finished writing " + PakPath);
// Update resource store in case we recooked any assets // Update resource store in case we recooked any assets
mpProject->ResourceStore()->ConditionalSaveStore(); mpProject->ResourceStore()->ConditionalSaveStore();
} }

View File

@ -5,6 +5,7 @@
#include <Common/CFourCC.h> #include <Common/CFourCC.h>
#include <Common/TString.h> #include <Common/TString.h>
#include <Common/Serialization/IArchive.h> #include <Common/Serialization/IArchive.h>
#include "Core/IProgressNotifier.h"
class CGameProject; class CGameProject;
@ -57,7 +58,7 @@ public:
void AddResource(const TString& rkName, const CAssetID& rkID, const CFourCC& rkType); void AddResource(const TString& rkName, const CAssetID& rkID, const CFourCC& rkType);
void UpdateDependencyCache() const; void UpdateDependencyCache() const;
void Cook(); void Cook(IProgressNotifier *pProgress);
void CompareOriginalAssetList(const std::list<CAssetID>& rkNewList); void CompareOriginalAssetList(const std::list<CAssetID>& rkNewList);
bool ContainsAsset(const CAssetID& rkID) const; bool ContainsAsset(const CAssetID& rkID) const;

View File

@ -0,0 +1,48 @@
#ifndef IPROGRESSNOTIFIER_H
#define IPROGRESSNOTIFIER_H
#include <Common/Common.h>
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

View File

@ -1,6 +1,7 @@
#include "CEditorApplication.h" #include "CEditorApplication.h"
#include "IEditor.h" #include "IEditor.h"
#include "CBasicViewport.h" #include "CBasicViewport.h"
#include "CProgressDialog.h"
#include "CProjectSettingsDialog.h" #include "CProjectSettingsDialog.h"
#include "Editor/CharacterEditor/CCharacterEditor.h" #include "Editor/CharacterEditor/CCharacterEditor.h"
#include "Editor/ModelEditor/CModelEditorWindow.h" #include "Editor/ModelEditor/CModelEditorWindow.h"
@ -9,7 +10,9 @@
#include <Common/AssertMacro.h> #include <Common/AssertMacro.h>
#include <Common/CTimer.h> #include <Common/CTimer.h>
#include <Core/GameProject/CGameProject.h> #include <Core/GameProject/CGameProject.h>
#include <QMessageBox>
#include <QFuture>
#include <QtConcurrent/QtConcurrentRun>
CEditorApplication::CEditorApplication(int& rArgc, char **ppArgv) CEditorApplication::CEditorApplication(int& rArgc, char **ppArgv)
: QApplication(rArgc, ppArgv) : QApplication(rArgc, ppArgv)
@ -141,19 +144,51 @@ void CEditorApplication::NotifyAssetsModified()
emit AssetsModified(); emit AssetsModified();
} }
void CEditorApplication::CookAllDirtyPackages() bool CEditorApplication::CookPackage(CPackage *pPkg)
{
return CookPackageList(QList<CPackage*>() << pPkg);
}
bool CEditorApplication::CookAllDirtyPackages()
{ {
ASSERT(mpActiveProject != nullptr); ASSERT(mpActiveProject != nullptr);
QList<CPackage*> PackageList;
for (u32 iPkg = 0; iPkg < mpActiveProject->NumPackages(); iPkg++) for (u32 iPkg = 0; iPkg < mpActiveProject->NumPackages(); iPkg++)
{ {
CPackage *pPackage = mpActiveProject->PackageByIndex(iPkg); CPackage *pPackage = mpActiveProject->PackageByIndex(iPkg);
if (pPackage->NeedsRecook()) if (pPackage->NeedsRecook())
pPackage->Cook(); PackageList << pPackage;
} }
emit PackagesCooked(); return CookPackageList(PackageList);
}
bool CEditorApplication::CookPackageList(QList<CPackage*> PackageList)
{
if (!PackageList.isEmpty())
{
CProgressDialog Dialog("Cooking package" + QString(PackageList.size() > 1 ? "s" : ""), true, mpWorldEditor);
QFuture<void> 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 ************ // ************ SLOTS ************

View File

@ -37,7 +37,10 @@ public:
bool OpenProject(const QString& rkProjPath); bool OpenProject(const QString& rkProjPath);
void EditResource(CResourceEntry *pEntry); void EditResource(CResourceEntry *pEntry);
void NotifyAssetsModified(); void NotifyAssetsModified();
void CookAllDirtyPackages();
bool CookPackage(CPackage *pPackage);
bool CookAllDirtyPackages();
bool CookPackageList(QList<CPackage*> PackageList);
// Accessors // Accessors
inline CGameProject* ActiveProject() const { return mpActiveProject; } inline CGameProject* ActiveProject() const { return mpActiveProject; }

View File

@ -1,5 +1,6 @@
#include "CExportGameDialog.h" #include "CExportGameDialog.h"
#include "ui_CExportGameDialog.h" #include "ui_CExportGameDialog.h"
#include "CProgressDialog.h"
#include "UICommon.h" #include "UICommon.h"
#include <Common/AssertMacro.h> #include <Common/AssertMacro.h>
@ -13,6 +14,7 @@
#include <QFileDialog> #include <QFileDialog>
#include <QLabel> #include <QLabel>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QtConcurrent/QtConcurrentRun>
#include <nod/nod.hpp> #include <nod/nod.hpp>
@ -383,10 +385,16 @@ void CExportGameDialog::Export()
CGameExporter Exporter(mGame, mRegion, mGameTitle, mGameID, mBuildVer); CGameExporter Exporter(mGame, mRegion, mGameTitle, mGameID, mBuildVer);
TString StrExportDir = TO_TSTRING(ExportDir); TString StrExportDir = TO_TSTRING(ExportDir);
StrExportDir.EnsureEndsWith('/'); StrExportDir.EnsureEndsWith('/');
mExportSuccess = Exporter.Export(mpDisc, StrExportDir, &NameMap, &GameInfo);
CProgressDialog Dialog("Creating new game project", true, parentWidget());
QFuture<bool> Future = QtConcurrent::run(&Exporter, &CGameExporter::Export, mpDisc, StrExportDir, &NameMap, &GameInfo, &Dialog);
mExportSuccess = Dialog.WaitForResults(Future);
if (!mExportSuccess) if (!mExportSuccess)
UICommon::ErrorMsg(this, "Export failed!"); {
if (!Dialog.ShouldCancel())
UICommon::ErrorMsg(this, "Export failed!");
}
else else
mNewProjectPath = TO_QSTRING(Exporter.ProjectPath()); mNewProjectPath = TO_QSTRING(Exporter.ProjectPath());
} }

View File

@ -1,280 +0,0 @@
#ifndef CPAKTOOLDIALOG
#define CPAKTOOLDIALOG
#include <Common/EGame.h>
#include <Common/Log.h>
#include <Common/types.h>
#include "Editor/UICommon.h"
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QProcess>
#include <QProgressDialog>
#include <QTimer>
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

View File

@ -0,0 +1,91 @@
#include "CProgressDialog.h"
#include "ui_CProgressDialog.h"
#include "CEditorApplication.h"
#include <QCloseEvent>
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
}

View File

@ -0,0 +1,83 @@
#ifndef CPROGRESSDIALOG_H
#define CPROGRESSDIALOG_H
#include "IProgressNotifierUI.h"
#include "UICommon.h"
#include <Core/GameProject/CGameProject.h>
#include <Core/IProgressNotifier.h>
#include <QDialog>
#include <QFuture>
#include <QFutureWatcher>
#if WIN32
#include <QtWinExtras/QWinTaskbarButton>
#include <QtWinExtras/QWinTaskbarProgress>
#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<typename RetType>
void InternalWaitForResults(QFuture<RetType> Future)
{
gpEdApp->SetEditorTicksEnabled(false);
QFutureWatcher<RetType> Watcher;
connect(&Watcher, SIGNAL(finished()), this, SLOT(FinishAndClose()));
Watcher.setFuture(Future);
exec();
gpEdApp->SetEditorTicksEnabled(true);
if (mAlertOnFinish)
gpEdApp->alert(parentWidget());
}
public:
template<typename RetType>
RetType WaitForResults(QFuture<RetType> Future)
{
InternalWaitForResults(Future);
return Future.result();
}
template<>
void WaitForResults(QFuture<void> Future)
{
InternalWaitForResults(Future);
}
};
#endif // CPROGRESSDIALOG_H

View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CProgressDialog</class>
<widget class="QDialog" name="CProgressDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>609</width>
<height>110</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="TaskLabel">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="StepLabel">
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="ProgressBar">
<property name="value">
<number>24</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="CancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -2,12 +2,16 @@
#include "ui_CProjectSettingsDialog.h" #include "ui_CProjectSettingsDialog.h"
#include "CEditorApplication.h" #include "CEditorApplication.h"
#include "CExportGameDialog.h" #include "CExportGameDialog.h"
#include "CProgressDialog.h"
#include "UICommon.h" #include "UICommon.h"
#include "Editor/ResourceBrowser/CResourceBrowser.h" #include "Editor/ResourceBrowser/CResourceBrowser.h"
#include <Common/AssertMacro.h> #include <Common/AssertMacro.h>
#include <Core/GameProject/CGameExporter.h> #include <Core/GameProject/CGameExporter.h>
#include <QFileDialog> #include <QFileDialog>
#include <QFuture>
#include <QFutureWatcher>
#include <QMessageBox> #include <QMessageBox>
#include <QtConcurrent/QtConcurrentRun>
CProjectSettingsDialog::CProjectSettingsDialog(QWidget *pParent) CProjectSettingsDialog::CProjectSettingsDialog(QWidget *pParent)
: QDialog(pParent) : QDialog(pParent)
@ -22,6 +26,7 @@ CProjectSettingsDialog::CProjectSettingsDialog(QWidget *pParent)
connect(gpEdApp, SIGNAL(ActiveProjectChanged(CGameProject*)), this, SLOT(ActiveProjectChanged(CGameProject*))); connect(gpEdApp, SIGNAL(ActiveProjectChanged(CGameProject*)), this, SLOT(ActiveProjectChanged(CGameProject*)));
connect(gpEdApp, SIGNAL(AssetsModified()), this, SLOT(SetupPackagesList())); connect(gpEdApp, SIGNAL(AssetsModified()), this, SLOT(SetupPackagesList()));
connect(gpEdApp, SIGNAL(PackagesCooked()), this, SLOT(SetupPackagesList()));
// Set build ISO button color // Set build ISO button color
QPalette Palette = mpUI->BuildIsoButton->palette(); QPalette Palette = mpUI->BuildIsoButton->palette();
@ -91,15 +96,13 @@ void CProjectSettingsDialog::CookPackage()
if (PackageIdx != -1) if (PackageIdx != -1)
{ {
CPackage *pPackage = mpProject->PackageByIndex(PackageIdx); CPackage *pPackage = mpProject->PackageByIndex(PackageIdx);
pPackage->Cook(); gpEdApp->CookPackage(pPackage);
SetupPackagesList();
} }
} }
void CProjectSettingsDialog::CookAllDirtyPackages() void CProjectSettingsDialog::CookAllDirtyPackages()
{ {
gpEdApp->CookAllDirtyPackages(); gpEdApp->CookAllDirtyPackages();
SetupPackagesList();
} }
void CProjectSettingsDialog::BuildISO() void CProjectSettingsDialog::BuildISO()
@ -107,12 +110,17 @@ void CProjectSettingsDialog::BuildISO()
CGameProject *pProj = gpEdApp->ActiveProject(); CGameProject *pProj = gpEdApp->ActiveProject();
ASSERT(pProj && !pProj->IsWiiBuild()); 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); QString IsoPath = UICommon::SaveFileDialog(this, "Choose output ISO path", "*.gcm", DefaultPath);
if (!IsoPath.isEmpty()) if (!IsoPath.isEmpty())
{ {
if (!pProj->BuildISO( TO_TSTRING(IsoPath) )) if (gpEdApp->CookAllDirtyPackages())
UICommon::ErrorMsg(this, "Failed to build ISO! Check the log for details."); {
CProgressDialog Dialog("Building ISO", true, this);
Dialog.DisallowCanceling();
QFuture<void> Future = QtConcurrent::run(pProj, &CGameProject::BuildISO, TO_TSTRING(IsoPath), &Dialog);
Dialog.WaitForResults(Future);
}
} }
} }

View File

@ -9,7 +9,10 @@ QMAKE_CXXFLAGS += /WX
DEFINES += PWE_EDITOR DEFINES += PWE_EDITOR
RESOURCES += Icons.qrc RESOURCES += Icons.qrc
win32: RC_ICONS += icons/AppIcon.ico win32: {
RC_ICONS += icons/AppIcon.ico
QT += winextras
}
TEMPLATE = app TEMPLATE = app
DESTDIR = $$PWD/../../bin DESTDIR = $$PWD/../../bin
@ -156,8 +159,6 @@ HEADERS += \
Undo/CCloneSelectionCommand.h \ Undo/CCloneSelectionCommand.h \
CNodeCopyMimeData.h \ CNodeCopyMimeData.h \
Undo/CPasteNodesCommand.h \ Undo/CPasteNodesCommand.h \
CPakToolDialog.h \
WorldEditor/CRepackInfoDialog.h \
CAboutDialog.h \ CAboutDialog.h \
CharacterEditor/CCharacterEditor.h \ CharacterEditor/CCharacterEditor.h \
CharacterEditor/CCharacterEditorViewport.h \ CharacterEditor/CCharacterEditorViewport.h \
@ -179,7 +180,9 @@ HEADERS += \
Widgets/CTimedLineEdit.h \ Widgets/CTimedLineEdit.h \
CProjectSettingsDialog.h \ CProjectSettingsDialog.h \
WorldEditor/CPoiMapSidebar.h \ WorldEditor/CPoiMapSidebar.h \
WorldEditor/CWorldEditorSidebar.h WorldEditor/CWorldEditorSidebar.h \
CProgressDialog.h \
IProgressNotifierUI.h
# Source Files # Source Files
SOURCES += \ SOURCES += \
@ -231,7 +234,6 @@ SOURCES += \
Undo/CCreateInstanceCommand.cpp \ Undo/CCreateInstanceCommand.cpp \
Undo/CCloneSelectionCommand.cpp \ Undo/CCloneSelectionCommand.cpp \
Undo/CPasteNodesCommand.cpp \ Undo/CPasteNodesCommand.cpp \
WorldEditor/CRepackInfoDialog.cpp \
CAboutDialog.cpp \ CAboutDialog.cpp \
CharacterEditor/CCharacterEditor.cpp \ CharacterEditor/CCharacterEditor.cpp \
CharacterEditor/CCharacterEditorViewport.cpp \ CharacterEditor/CCharacterEditorViewport.cpp \
@ -246,7 +248,8 @@ SOURCES += \
WorldEditor/CWorldTreeModel.cpp \ WorldEditor/CWorldTreeModel.cpp \
CProjectSettingsDialog.cpp \ CProjectSettingsDialog.cpp \
WorldEditor/CPoiMapSidebar.cpp \ WorldEditor/CPoiMapSidebar.cpp \
WorldEditor/CWorldEditorSidebar.cpp WorldEditor/CWorldEditorSidebar.cpp \
CProgressDialog.cpp
# UI Files # UI Files
FORMS += \ FORMS += \
@ -263,7 +266,6 @@ FORMS += \
WorldEditor/CTemplateEditDialog.ui \ WorldEditor/CTemplateEditDialog.ui \
WorldEditor/CLinkDialog.ui \ WorldEditor/CLinkDialog.ui \
WorldEditor/CSelectInstanceDialog.ui \ WorldEditor/CSelectInstanceDialog.ui \
WorldEditor/CRepackInfoDialog.ui \
CAboutDialog.ui \ CAboutDialog.ui \
CharacterEditor/CCharacterEditor.ui \ CharacterEditor/CCharacterEditor.ui \
WorldEditor/CCollisionRenderSettingsDialog.ui \ WorldEditor/CCollisionRenderSettingsDialog.ui \
@ -271,4 +273,5 @@ FORMS += \
CExportGameDialog.ui \ CExportGameDialog.ui \
WorldEditor/CWorldInfoSidebar.ui \ WorldEditor/CWorldInfoSidebar.ui \
CProjectSettingsDialog.ui \ CProjectSettingsDialog.ui \
WorldEditor/CPoiMapSidebar.ui WorldEditor/CPoiMapSidebar.ui \
CProgressDialog.ui

View File

@ -0,0 +1,30 @@
#ifndef IPROGRESSNOTIFIERUI_H
#define IPROGRESSNOTIFIERUI_H
#include "UICommon.h"
#include <Core/IProgressNotifier.h>
#include <QDialog>
// 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

View File

@ -1,114 +0,0 @@
#include "CRepackInfoDialog.h"
#include "ui_CRepackInfoDialog.h"
#include "Editor/CPakToolDialog.h"
#include <QDir>
#include <QFile>
#include <QFileDialog>
#include <QPushButton>
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());
}

View File

@ -1,34 +0,0 @@
#ifndef CREPACKINFODIALOG_H
#define CREPACKINFODIALOG_H
#include <QDialog>
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

View File

@ -1,127 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CRepackInfoDialog</class>
<widget class="QDialog" name="CRepackInfoDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>125</height>
</rect>
</property>
<property name="windowTitle">
<string>Set Pak Info</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="FolderLabel">
<property name="text">
<string>Folder</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="FolderLineEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="FileListLabel">
<property name="text">
<string>File List</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="FileListLineEdit"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="OutputPakLabel">
<property name="text">
<string>Output Pak</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="OutputPakLineEdit"/>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QToolButton" name="FolderToolButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="FileListToolButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="OutputPakToolButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="ButtonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>ButtonBox</sender>
<signal>accepted()</signal>
<receiver>CRepackInfoDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>ButtonBox</sender>
<signal>rejected()</signal>
<receiver>CRepackInfoDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -6,15 +6,15 @@
<property ID="0x01" name="Position" type="vector3f"/> <property ID="0x01" name="Position" type="vector3f"/>
<property ID="0x02" name="Rotation" type="vector3f"/> <property ID="0x02" name="Rotation" type="vector3f"/>
<property ID="0x03" name="Scale" type="vector3f"/> <property ID="0x03" name="Scale" type="vector3f"/>
<property ID="0x04" name="Unknown 1" type="vector3f"/> <property ID="0x04" name="Collision Extent" type="vector3f"/>
<property ID="0x05" name="Scan Offset" type="vector3f"/> <property ID="0x05" name="Collision/Scan Offset" type="vector3f"/>
<property ID="0x06" name="Unknown 2" type="float"/> <property ID="0x06" name="Mass" type="float"/>
<property ID="0x07" name="Unknown 3" type="float"/> <property ID="0x07" name="Z Momentum" type="float"/>
<struct ID="0x08" name="HealthInfo" template="Structs/HealthInfo.xml"/> <struct ID="0x08" name="HealthInfo" template="Structs/HealthInfo.xml"/>
<struct ID="0x09" name="DamageVulnerability" template="Structs/DamageVulnerability.xml"/> <struct ID="0x09" name="DamageVulnerability" template="Structs/DamageVulnerability.xml"/>
<property ID="0x0A" name="AnimationParameters" type="character"/> <property ID="0x0A" name="AnimationParameters" type="character"/>
<struct ID="0x0B" name="ActorParameters" template="Structs/ActorParameters.xml"/> <struct ID="0x0B" name="ActorParameters" template="Structs/ActorParameters.xml"/>
<property ID="0x0C" name="Particle" type="asset" extensions="PART"/> <property ID="0x0C" name="Flame Particle" type="asset" extensions="PART"/>
<struct ID="0x0D" name="DamageInfo" template="Structs/DamageInfo.xml"/> <struct ID="0x0D" name="DamageInfo" template="Structs/DamageInfo.xml"/>
<property ID="0x0E" name="Active" type="bool"/> <property ID="0x0E" name="Active" type="bool"/>
</properties> </properties>