mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-05-25 00:31:24 +00:00
Changed game exporter to export from a full disc image using nod instead of a pre-extracted disc filesystem; also fixed issue with tabbing in/out with a file dialog open, and fixed a memory leak in CAnimSet
This commit is contained in:
parent
fbdf9023d1
commit
eca833cf89
@ -9,6 +9,9 @@
|
|||||||
|
|
||||||
#define FOURCC(Text) (Text[0] << 24 | Text[1] << 16 | Text[2] << 8 | Text[3])
|
#define FOURCC(Text) (Text[0] << 24 | Text[1] << 16 | Text[2] << 8 | Text[3])
|
||||||
#define FOURCC_CONSTEXPR(A, B, C, D) (A << 24 | B << 16 | C << 8 | D)
|
#define FOURCC_CONSTEXPR(A, B, C, D) (A << 24 | B << 16 | C << 8 | D)
|
||||||
|
// todo: replace usages of FOURCC and FOURCC_CONSTEXPR with this macro
|
||||||
|
// (it should be renamed to just FOURCC when the others aren't in use anymore)
|
||||||
|
#define IFOURCC(Value) Value
|
||||||
|
|
||||||
class CFourCC
|
class CFourCC
|
||||||
{
|
{
|
||||||
|
@ -65,3 +65,27 @@ void Serialize(IArchive& rArc, EGame& rGame)
|
|||||||
rArc.SerializePrimitive(GameID);
|
rArc.SerializePrimitive(GameID);
|
||||||
if (rArc.IsReader()) rGame = GetGameForID(GameID);
|
if (rArc.IsReader()) rGame = GetGameForID(GameID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ERegion
|
||||||
|
void Serialize(IArchive& rArc, ERegion& rRegion)
|
||||||
|
{
|
||||||
|
static const TString skRegionNames[] = { "NTSC", "PAL", "JPN" };
|
||||||
|
TString Name;
|
||||||
|
|
||||||
|
if (rArc.IsWriter())
|
||||||
|
Name = skRegionNames[rRegion];
|
||||||
|
|
||||||
|
rArc.SerializePrimitive(Name);
|
||||||
|
|
||||||
|
if (rArc.IsReader())
|
||||||
|
{
|
||||||
|
for (u32 iReg = 0; iReg < 3; iReg++)
|
||||||
|
{
|
||||||
|
if (skRegionNames[iReg] == Name)
|
||||||
|
{
|
||||||
|
rRegion = (ERegion) iReg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -28,4 +28,14 @@ TString GetGameName(EGame Game);
|
|||||||
TString GetGameShortName(EGame Game);
|
TString GetGameShortName(EGame Game);
|
||||||
void Serialize(IArchive& rArc, EGame& rGame);
|
void Serialize(IArchive& rArc, EGame& rGame);
|
||||||
|
|
||||||
|
// ERegion
|
||||||
|
enum ERegion
|
||||||
|
{
|
||||||
|
eRegion_NTSC,
|
||||||
|
eRegion_PAL,
|
||||||
|
eRegion_JPN,
|
||||||
|
eRegion_Unknown = -1
|
||||||
|
};
|
||||||
|
void Serialize(IArchive& rArc, ERegion& rRegion);
|
||||||
|
|
||||||
#endif // EGAME_H
|
#endif // EGAME_H
|
||||||
|
@ -54,7 +54,7 @@ bool IsEmpty(const TWideString& rkDirPath)
|
|||||||
return is_empty(*rkDirPath);
|
return is_empty(*rkDirPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CreateDirectory(const TWideString& rkNewDir)
|
bool MakeDirectory(const TWideString& rkNewDir)
|
||||||
{
|
{
|
||||||
if (!IsValidPath(rkNewDir, true))
|
if (!IsValidPath(rkNewDir, true))
|
||||||
{
|
{
|
||||||
@ -73,7 +73,7 @@ bool CopyFile(const TWideString& rkOrigPath, const TWideString& rkNewPath)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateDirectory(rkNewPath.GetFileDirectory());
|
MakeDirectory(rkNewPath.GetFileDirectory());
|
||||||
boost::system::error_code Error;
|
boost::system::error_code Error;
|
||||||
copy(*rkOrigPath, *rkNewPath, Error);
|
copy(*rkOrigPath, *rkNewPath, Error);
|
||||||
return (Error == boost::system::errc::success);
|
return (Error == boost::system::errc::success);
|
||||||
@ -87,7 +87,7 @@ bool CopyDirectory(const TWideString& rkOrigPath, const TWideString& rkNewPath)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateDirectory(rkNewPath.GetFileDirectory());
|
MakeDirectory(rkNewPath.GetFileDirectory());
|
||||||
boost::system::error_code Error;
|
boost::system::error_code Error;
|
||||||
copy_directory(*rkOrigPath, *rkNewPath, Error);
|
copy_directory(*rkOrigPath, *rkNewPath, Error);
|
||||||
return (Error == boost::system::errc::success);
|
return (Error == boost::system::errc::success);
|
||||||
|
@ -14,7 +14,7 @@ bool IsDirectory(const TWideString& rkDirPath);
|
|||||||
bool IsAbsolute(const TWideString& rkDirPath);
|
bool IsAbsolute(const TWideString& rkDirPath);
|
||||||
bool IsRelative(const TWideString& rkDirPath);
|
bool IsRelative(const TWideString& rkDirPath);
|
||||||
bool IsEmpty(const TWideString& rkDirPath);
|
bool IsEmpty(const TWideString& rkDirPath);
|
||||||
bool CreateDirectory(const TWideString& rkNewDir);
|
bool MakeDirectory(const TWideString& rkNewDir);
|
||||||
bool CopyFile(const TWideString& rkOrigPath, const TWideString& rkNewPath);
|
bool CopyFile(const TWideString& rkOrigPath, const TWideString& rkNewPath);
|
||||||
bool CopyDirectory(const TWideString& rkOrigPath, const TWideString& rkNewPath);
|
bool CopyDirectory(const TWideString& rkOrigPath, const TWideString& rkNewPath);
|
||||||
bool MoveFile(const TWideString& rkOldPath, const TWideString& rkNewPath);
|
bool MoveFile(const TWideString& rkOldPath, const TWideString& rkNewPath);
|
||||||
|
@ -29,6 +29,8 @@ CONFIG (debug, debug|release) {
|
|||||||
-L$$BUILD_DIR/Math/ -lMathd \
|
-L$$BUILD_DIR/Math/ -lMathd \
|
||||||
-L$$EXTERNALS_DIR/assimp/lib/ -lassimp-vc140-mtd \
|
-L$$EXTERNALS_DIR/assimp/lib/ -lassimp-vc140-mtd \
|
||||||
-L$$EXTERNALS_DIR/lzo-2.09/lib/ -llzo2d \
|
-L$$EXTERNALS_DIR/lzo-2.09/lib/ -llzo2d \
|
||||||
|
-L$$EXTERNALS_DIR/nodtool/build/debug/lib/ -lnod \
|
||||||
|
-L$$EXTERNALS_DIR/nodtool/build/debug/logvisor/ -llogvisor \
|
||||||
-L$$EXTERNALS_DIR/tinyxml2/lib/ -ltinyxml2d \
|
-L$$EXTERNALS_DIR/tinyxml2/lib/ -ltinyxml2d \
|
||||||
-L$$EXTERNALS_DIR/zlib/lib/ -lzlibd
|
-L$$EXTERNALS_DIR/zlib/lib/ -lzlibd
|
||||||
|
|
||||||
@ -51,6 +53,8 @@ CONFIG (release, debug|release) {
|
|||||||
-L$$BUILD_DIR/Math/ -lMath \
|
-L$$BUILD_DIR/Math/ -lMath \
|
||||||
-L$$EXTERNALS_DIR/assimp/lib/ -lassimp-vc140-mt \
|
-L$$EXTERNALS_DIR/assimp/lib/ -lassimp-vc140-mt \
|
||||||
-L$$EXTERNALS_DIR/lzo-2.09/lib/ -llzo2 \
|
-L$$EXTERNALS_DIR/lzo-2.09/lib/ -llzo2 \
|
||||||
|
-L$$EXTERNALS_DIR/nodtool/build/release/lib/ -lnod \
|
||||||
|
-L$$EXTERNALS_DIR/nodtool/build/release/logvisor -llogvisor \
|
||||||
-L$$EXTERNALS_DIR/tinyxml2/lib/ -ltinyxml2 \
|
-L$$EXTERNALS_DIR/tinyxml2/lib/ -ltinyxml2 \
|
||||||
-L$$EXTERNALS_DIR/zlib/lib/ -lzlib
|
-L$$EXTERNALS_DIR/zlib/lib/ -lzlib
|
||||||
|
|
||||||
@ -72,6 +76,8 @@ INCLUDEPATH += $$PWE_MAIN_INCLUDE \
|
|||||||
$$EXTERNALS_DIR/glew-2.0.0/include \
|
$$EXTERNALS_DIR/glew-2.0.0/include \
|
||||||
$$EXTERNALS_DIR/glm/glm \
|
$$EXTERNALS_DIR/glm/glm \
|
||||||
$$EXTERNALS_DIR/lzo-2.09/include \
|
$$EXTERNALS_DIR/lzo-2.09/include \
|
||||||
|
$$EXTERNALS_DIR/nodtool/include \
|
||||||
|
$$EXTERNALS_DIR/nodtool/logvisor/include \
|
||||||
$$EXTERNALS_DIR/tinyxml2/include \
|
$$EXTERNALS_DIR/tinyxml2/include \
|
||||||
$$EXTERNALS_DIR/zlib/include
|
$$EXTERNALS_DIR/zlib/include
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
const TString gkAssetMapPath = "..\\resources\\gameinfo\\AssetNameMap.xml";
|
const TString gkAssetMapPath = "..\\resources\\gameinfo\\AssetNameMap.xml";
|
||||||
|
const TString gkAssetMapExt = "xml";
|
||||||
|
|
||||||
class CAssetNameMap
|
class CAssetNameMap
|
||||||
{
|
{
|
||||||
@ -36,6 +37,9 @@ public:
|
|||||||
void SaveAssetNames(TString Path = gkAssetMapPath);
|
void SaveAssetNames(TString Path = gkAssetMapPath);
|
||||||
bool GetNameInfo(CAssetID ID, TString& rOutDirectory, TString& rOutName);
|
bool GetNameInfo(CAssetID ID, TString& rOutDirectory, TString& rOutName);
|
||||||
void CopyFromStore(CResourceStore *pStore);
|
void CopyFromStore(CResourceStore *pStore);
|
||||||
|
|
||||||
|
inline static TString DefaultNameMapPath() { return gkAssetMapPath; }
|
||||||
|
inline static TString GetExtension() { return gkAssetMapExt; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CASSETNAMEMAP
|
#endif // CASSETNAMEMAP
|
||||||
|
@ -12,65 +12,77 @@
|
|||||||
#include <Common/Serialization/CXMLWriter.h>
|
#include <Common/Serialization/CXMLWriter.h>
|
||||||
#include <tinyxml2.h>
|
#include <tinyxml2.h>
|
||||||
|
|
||||||
#define COPY_DISC_DATA 1
|
|
||||||
#define LOAD_PAKS 1
|
#define LOAD_PAKS 1
|
||||||
#define SAVE_PACKAGE_DEFINITIONS 1
|
#define SAVE_PACKAGE_DEFINITIONS 1
|
||||||
#define USE_ASSET_NAME_MAP 1
|
#define USE_ASSET_NAME_MAP 1
|
||||||
#define EXPORT_COOKED 1
|
#define EXPORT_COOKED 1
|
||||||
|
|
||||||
CGameExporter::CGameExporter(const TString& rkInputDir, const TString& rkOutputDir)
|
CGameExporter::CGameExporter(EGame Game, ERegion Region, const TString& rkGameName, const TString& rkGameID, float BuildVersion)
|
||||||
|
: mGame(Game)
|
||||||
|
, mRegion(Region)
|
||||||
|
, mGameName(rkGameName)
|
||||||
|
, mGameID(rkGameID)
|
||||||
|
, mBuildVersion(BuildVersion)
|
||||||
{
|
{
|
||||||
mGame = eUnknownGame;
|
ASSERT(mGame != eUnknownGame);
|
||||||
mBuildVersion = 0.f;
|
ASSERT(mRegion != eRegion_Unknown);
|
||||||
|
|
||||||
mGameDir = FileUtil::MakeAbsolute(rkInputDir);
|
|
||||||
mExportDir = FileUtil::MakeAbsolute(rkOutputDir);
|
|
||||||
|
|
||||||
mpProject = new CGameProject(mExportDir);
|
|
||||||
mDiscDir = L"Disc\\";
|
|
||||||
mWorldsDirName = L"Worlds\\";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if PUBLIC_RELEASE
|
#if PUBLIC_RELEASE
|
||||||
#error Fix export directory being cleared!
|
#error Fix export directory being cleared!
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool CGameExporter::Export()
|
bool CGameExporter::Export(nod::DiscBase *pDisc, const TString& rkOutputDir, CAssetNameMap *pNameMap, CGameInfo *pGameInfo)
|
||||||
{
|
{
|
||||||
SCOPED_TIMER(ExportGame);
|
SCOPED_TIMER(ExportGame);
|
||||||
|
|
||||||
FileUtil::CreateDirectory(mExportDir);
|
mpDisc = pDisc;
|
||||||
FileUtil::ClearDirectory(mExportDir);
|
mpNameMap = pNameMap;
|
||||||
|
mpGameInfo = pGameInfo;
|
||||||
|
|
||||||
// Initial analyze/copy of disc data
|
mExportDir = FileUtil::MakeAbsolute(rkOutputDir);
|
||||||
CopyDiscData();
|
mDiscDir = L"Disc\\";
|
||||||
FindBuildVersion();
|
mWorldsDirName = L"Worlds\\";
|
||||||
|
|
||||||
// Create project
|
// Create project
|
||||||
mpProject = new CGameProject(this, mExportDir, mGame, mBuildVersion);
|
FileUtil::MakeDirectory(mExportDir);
|
||||||
mpProject->SetProjectName(CMasterTemplate::FindGameName(mGame));
|
FileUtil::ClearDirectory(mExportDir);
|
||||||
|
|
||||||
|
// Extract disc
|
||||||
|
if (!ExtractDiscData())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Create project
|
||||||
|
CGameProject *pOldActiveProj = CGameProject::ActiveProject();
|
||||||
|
|
||||||
|
mpProject = CGameProject::CreateProjectForExport(
|
||||||
|
this,
|
||||||
|
mExportDir,
|
||||||
|
mGame,
|
||||||
|
mRegion,
|
||||||
|
mGameID,
|
||||||
|
mBuildVersion,
|
||||||
|
mDolPath,
|
||||||
|
mApploaderPath,
|
||||||
|
mPartitionHeaderPath,
|
||||||
|
mFilesystemAddress);
|
||||||
|
|
||||||
|
mpProject->SetProjectName(mGameName);
|
||||||
mpProject->SetActive();
|
mpProject->SetActive();
|
||||||
mpStore = mpProject->ResourceStore();
|
mpStore = mpProject->ResourceStore();
|
||||||
mContentDir = mpStore->RawDir(false);
|
mContentDir = mpStore->RawDir(false);
|
||||||
mCookedDir = mpStore->CookedDir(false);
|
mCookedDir = mpStore->CookedDir(false);
|
||||||
|
|
||||||
#if USE_ASSET_NAME_MAP
|
|
||||||
mNameMap.LoadAssetNames();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Export game data
|
// Export game data
|
||||||
CResourceStore *pOldStore = gpResourceStore;
|
|
||||||
gpResourceStore = mpStore;
|
|
||||||
|
|
||||||
LoadPaks();
|
LoadPaks();
|
||||||
ExportCookedResources();
|
ExportCookedResources();
|
||||||
mpProject->AudioManager()->LoadAssets();
|
mpProject->AudioManager()->LoadAssets();
|
||||||
ExportResourceEditorData();
|
ExportResourceEditorData();
|
||||||
|
|
||||||
// Export finished!
|
// Export finished!
|
||||||
|
mProjectPath = mpProject->ProjectPath();
|
||||||
delete mpProject;
|
delete mpProject;
|
||||||
gpResourceStore = pOldStore;
|
if (pOldActiveProj) pOldActiveProj->SetActive();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,100 +93,85 @@ void CGameExporter::LoadResource(const CAssetID& rkID, std::vector<u8>& rBuffer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ************ PROTECTED ************
|
// ************ PROTECTED ************
|
||||||
void CGameExporter::CopyDiscData()
|
bool CGameExporter::ExtractDiscData()
|
||||||
{
|
{
|
||||||
#if COPY_DISC_DATA
|
// todo: handle dol, apploader, multiple partitions, wii ticket blob
|
||||||
SCOPED_TIMER(CopyDiscData);
|
SCOPED_TIMER(ExtractDiscData);
|
||||||
|
|
||||||
// Create Disc output folder
|
// Create Disc output folder
|
||||||
FileUtil::CreateDirectory(mExportDir + mDiscDir);
|
TWideString AbsDiscDir = mExportDir + mDiscDir;
|
||||||
#endif
|
FileUtil::MakeDirectory(AbsDiscDir);
|
||||||
|
|
||||||
// Copy data
|
// Extract disc filesystem
|
||||||
TWideStringList DiscFiles;
|
nod::Partition *pDataPartition = mpDisc->getDataPartition();
|
||||||
FileUtil::GetDirectoryContents(mGameDir, DiscFiles);
|
nod::ExtractionContext Context;
|
||||||
|
Context.force = false;
|
||||||
|
Context.verbose = false;
|
||||||
|
Context.progressCB = nullptr;
|
||||||
|
bool Success = ExtractDiscNodeRecursive(&pDataPartition->getFSTRoot(), AbsDiscDir, Context);
|
||||||
|
if (!Success) return false;
|
||||||
|
|
||||||
for (auto It = DiscFiles.begin(); It != DiscFiles.end(); It++)
|
// Extract dol
|
||||||
|
mDolPath = L"boot.dol";
|
||||||
|
CFileOutStream DolFile(TWideString(mExportDir + mDolPath).ToUTF8().ToStdString());
|
||||||
|
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 = L"apploader.img";
|
||||||
|
CFileOutStream ApploaderFile(TWideString(mExportDir + mApploaderPath).ToUTF8().ToStdString());
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
TWideString FullPath = *It;
|
mFilesystemAddress = 0;
|
||||||
TWideString RelPath = FullPath.ChopFront(mGameDir.Size());
|
mPartitionHeaderPath = L"partition_header.bin";
|
||||||
|
nod::DiscWii *pDiscWii = static_cast<nod::DiscWii*>(mpDisc);
|
||||||
|
Success = pDiscWii->writeOutDataPartitionHeader(*(mExportDir + mPartitionHeaderPath));
|
||||||
|
if (!Success) return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mFilesystemAddress = (u32) pDataPartition->getFSTMemoryAddr();
|
||||||
|
|
||||||
// Exclude PakTool files and folders
|
return true;
|
||||||
if (FullPath.GetFileName(false) == L"PakTool" || FullPath.GetFileName(false) == L"zlib1" || RelPath.Contains(L"-pak"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Hack to determine game
|
|
||||||
if (mGame == eUnknownGame)
|
|
||||||
{
|
|
||||||
TWideString Name = FullPath.GetFileName(false);
|
|
||||||
if (Name == L"MetroidCWP") mGame = ePrimeDemo;
|
|
||||||
else if (Name == L"NESemu") mGame = ePrime;
|
|
||||||
else if (Name == L"PirateGun") mGame = eEchoesDemo;
|
|
||||||
else if (Name == L"AtomicBeta") mGame = eEchoes;
|
|
||||||
else if (Name == L"InGameAudio") mGame = eCorruptionProto;
|
|
||||||
else if (Name == L"GuiDVD") mGame = eCorruption;
|
|
||||||
else if (Name == L"PreloadData") mGame = eReturns;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark dol path. Note size != 0 check is needed because some ISO unpackers (*cough* GCRebuilder) can export bad dol files
|
bool CGameExporter::ExtractDiscNodeRecursive(const nod::Node *pkNode, const TWideString& rkDir, const nod::ExtractionContext& rkContext)
|
||||||
if (mDolPath.IsEmpty() && FullPath.EndsWith(L".dol", false) && FileUtil::FileSize(FullPath) != 0)
|
{
|
||||||
mDolPath = FullPath;
|
for (nod::Node::DirectoryIterator Iter = pkNode->begin(); Iter != pkNode->end(); ++Iter)
|
||||||
|
{
|
||||||
|
if (Iter->getKind() == nod::Node::Kind::File)
|
||||||
|
{
|
||||||
|
TWideString FilePath = rkDir + TString(Iter->getName()).ToUTF16();
|
||||||
|
bool Success = Iter->extractToDirectory(*rkDir, rkContext);
|
||||||
|
if (!Success) return false;
|
||||||
|
|
||||||
// Detect paks
|
if (FilePath.GetFileExtension() == L"pak")
|
||||||
if (FullPath.GetFileExtension().ToLower() == L"pak")
|
mPaks.push_back(FilePath);
|
||||||
mPaks.push_back(FullPath);
|
|
||||||
|
|
||||||
#if COPY_DISC_DATA
|
|
||||||
// Create directory
|
|
||||||
TWideString OutFile = mExportDir + mDiscDir + RelPath;
|
|
||||||
FileUtil::CreateDirectory(OutFile.GetFileDirectory());
|
|
||||||
|
|
||||||
// Copy file
|
|
||||||
if (FileUtil::IsFile(FullPath))
|
|
||||||
FileUtil::CopyFile(FullPath, OutFile);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(mGame != eUnknownGame);
|
else
|
||||||
}
|
|
||||||
|
|
||||||
void CGameExporter::FindBuildVersion()
|
|
||||||
{
|
{
|
||||||
ASSERT(!mDolPath.IsEmpty());
|
TWideString Subdir = rkDir + TString(Iter->getName()).ToUTF16() + L"\\";
|
||||||
|
bool Success = FileUtil::MakeDirectory(Subdir);
|
||||||
|
if (!Success) return false;
|
||||||
|
|
||||||
// MP1 demo build doesn't have a build version
|
Success = ExtractDiscNodeRecursive(&*Iter, Subdir, rkContext);
|
||||||
if (mGame == ePrimeDemo) return;
|
if (!Success) return false;
|
||||||
|
|
||||||
// Read entire file into a big buffer
|
|
||||||
CFileInStream File(mDolPath.ToUTF8().ToStdString(), IOUtil::eBigEndian);
|
|
||||||
std::vector<char> FileContents(File.Size());
|
|
||||||
File.ReadBytes(FileContents.data(), FileContents.size());
|
|
||||||
File.Close();
|
|
||||||
|
|
||||||
// Find build info string
|
|
||||||
const char *pkSearchText = "!#$MetroidBuildInfo!#$";
|
|
||||||
const int SearchTextSize = strlen(pkSearchText);
|
|
||||||
|
|
||||||
for (u32 SearchIdx = 0; SearchIdx < FileContents.size() - SearchTextSize + 1; SearchIdx++)
|
|
||||||
{
|
|
||||||
int Match = 0;
|
|
||||||
|
|
||||||
while (FileContents[SearchIdx + Match] == pkSearchText[Match] && Match < SearchTextSize)
|
|
||||||
Match++;
|
|
||||||
|
|
||||||
if (Match == SearchTextSize)
|
|
||||||
{
|
|
||||||
// Found the build info string; extract version number
|
|
||||||
TString BuildInfo = &FileContents[SearchIdx + SearchTextSize];
|
|
||||||
int BuildVerStart = BuildInfo.IndexOfPhrase("Build v") + 7;
|
|
||||||
ASSERT(BuildVerStart != 6);
|
|
||||||
|
|
||||||
mBuildVersion = BuildInfo.SubString(BuildVerStart, 5).ToFloat();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::Error("Failed to find MetroidBuildInfo string. Build Version will be set to 0. DOL file: " + mDolPath.ToUTF8());
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ************ RESOURCE LOADING ************
|
// ************ RESOURCE LOADING ************
|
||||||
@ -183,10 +180,13 @@ void CGameExporter::LoadPaks()
|
|||||||
#if LOAD_PAKS
|
#if LOAD_PAKS
|
||||||
SCOPED_TIMER(LoadPaks);
|
SCOPED_TIMER(LoadPaks);
|
||||||
|
|
||||||
|
mPaks.sort([](const TWideString& rkLeft, const TWideString& rkRight) -> bool {
|
||||||
|
return rkLeft.ToUpper() < rkRight.ToUpper();
|
||||||
|
});
|
||||||
|
|
||||||
for (auto It = mPaks.begin(); It != mPaks.end(); It++)
|
for (auto It = mPaks.begin(); It != mPaks.end(); It++)
|
||||||
{
|
{
|
||||||
TWideString PakPath = *It;
|
TWideString PakPath = *It;
|
||||||
TWideString PakName = PakPath.GetFileName(false);
|
|
||||||
TString CharPak = PakPath.ToUTF8();
|
TString CharPak = PakPath.ToUTF8();
|
||||||
CFileInStream Pak(CharPak.ToStdString(), IOUtil::eBigEndian);
|
CFileInStream Pak(CharPak.ToStdString(), IOUtil::eBigEndian);
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ void CGameExporter::LoadPaks()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
CPackage *pPackage = new CPackage(mpProject, CharPak.GetFileName(false), FileUtil::MakeRelative(PakPath.GetFileDirectory(), mGameDir));
|
CPackage *pPackage = new CPackage(mpProject, CharPak.GetFileName(false), FileUtil::MakeRelative(PakPath.GetFileDirectory(), mExportDir + mDiscDir));
|
||||||
CResourceCollection *pCollection = pPackage->AddCollection("Default");
|
CResourceCollection *pCollection = pPackage->AddCollection("Default");
|
||||||
|
|
||||||
// MP1-MP3Proto
|
// MP1-MP3Proto
|
||||||
@ -448,7 +448,7 @@ void CGameExporter::ExportCookedResources()
|
|||||||
{
|
{
|
||||||
{
|
{
|
||||||
SCOPED_TIMER(ExportCookedResources);
|
SCOPED_TIMER(ExportCookedResources);
|
||||||
FileUtil::CreateDirectory(mCookedDir);
|
FileUtil::MakeDirectory(mCookedDir);
|
||||||
|
|
||||||
for (auto It = mResourceMap.begin(); It != mResourceMap.end(); It++)
|
for (auto It = mResourceMap.begin(); It != mResourceMap.end(); It++)
|
||||||
{
|
{
|
||||||
@ -517,13 +517,20 @@ void CGameExporter::ExportResource(SResourceInstance& rRes)
|
|||||||
|
|
||||||
// Register resource and write to file
|
// Register resource and write to file
|
||||||
TString Directory, Name;
|
TString Directory, Name;
|
||||||
mNameMap.GetNameInfo(rRes.ResourceID, Directory, Name);
|
|
||||||
|
#if USE_ASSET_NAME_MAP
|
||||||
|
mpNameMap->GetNameInfo(rRes.ResourceID, Directory, Name);
|
||||||
|
#else
|
||||||
|
Directory = "Uncategorized";
|
||||||
|
Name = rRes.ResourceID.ToString();
|
||||||
|
#endif
|
||||||
|
|
||||||
CResourceEntry *pEntry = mpStore->RegisterResource(rRes.ResourceID, CResTypeInfo::TypeForCookedExtension(mGame, rRes.ResourceType)->Type(), Directory, Name);
|
CResourceEntry *pEntry = mpStore->RegisterResource(rRes.ResourceID, CResTypeInfo::TypeForCookedExtension(mGame, rRes.ResourceType)->Type(), Directory, Name);
|
||||||
|
|
||||||
#if EXPORT_COOKED
|
#if EXPORT_COOKED
|
||||||
// Save cooked asset
|
// Save cooked asset
|
||||||
TWideString OutCookedPath = pEntry->CookedAssetPath();
|
TWideString OutCookedPath = pEntry->CookedAssetPath();
|
||||||
FileUtil::CreateDirectory(OutCookedPath.GetFileDirectory());
|
FileUtil::MakeDirectory(OutCookedPath.GetFileDirectory());
|
||||||
CFileOutStream Out(OutCookedPath.ToUTF8().ToStdString(), IOUtil::eBigEndian);
|
CFileOutStream Out(OutCookedPath.ToUTF8().ToStdString(), IOUtil::eBigEndian);
|
||||||
|
|
||||||
if (Out.IsValid())
|
if (Out.IsValid())
|
||||||
|
@ -10,17 +10,25 @@
|
|||||||
#include <Common/TString.h>
|
#include <Common/TString.h>
|
||||||
#include <Common/types.h>
|
#include <Common/types.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <nod/nod.hpp>
|
||||||
|
|
||||||
class CGameExporter
|
class CGameExporter
|
||||||
{
|
{
|
||||||
// Project
|
// Project Data
|
||||||
CGameProject *mpProject;
|
CGameProject *mpProject;
|
||||||
|
TWideString mProjectPath;
|
||||||
CResourceStore *mpStore;
|
CResourceStore *mpStore;
|
||||||
EGame mGame;
|
EGame mGame;
|
||||||
|
ERegion mRegion;
|
||||||
|
TString mGameName;
|
||||||
|
TString mGameID;
|
||||||
float mBuildVersion;
|
float mBuildVersion;
|
||||||
|
TWideString mDolPath;
|
||||||
|
TWideString mApploaderPath;
|
||||||
|
TWideString mPartitionHeaderPath;
|
||||||
|
u32 mFilesystemAddress;
|
||||||
|
|
||||||
// Directories
|
// Directories
|
||||||
TWideString mGameDir;
|
|
||||||
TWideString mExportDir;
|
TWideString mExportDir;
|
||||||
TWideString mDiscDir;
|
TWideString mDiscDir;
|
||||||
TWideString mContentDir;
|
TWideString mContentDir;
|
||||||
@ -29,13 +37,13 @@ class CGameExporter
|
|||||||
TWideString mWorldsDirName;
|
TWideString mWorldsDirName;
|
||||||
|
|
||||||
// Files
|
// Files
|
||||||
TWideString mDolPath;
|
nod::DiscBase *mpDisc;
|
||||||
|
|
||||||
// Resources
|
// Resources
|
||||||
TWideStringList mPaks;
|
TWideStringList mPaks;
|
||||||
std::map<CAssetID, bool> mAreaDuplicateMap;
|
std::map<CAssetID, bool> mAreaDuplicateMap;
|
||||||
CAssetNameMap mNameMap;
|
CAssetNameMap *mpNameMap;
|
||||||
CGameInfo mGameInfo;
|
CGameInfo *mpGameInfo;
|
||||||
|
|
||||||
struct SResourceInstance
|
struct SResourceInstance
|
||||||
{
|
{
|
||||||
@ -50,13 +58,15 @@ class CGameExporter
|
|||||||
std::map<CAssetID, SResourceInstance> mResourceMap;
|
std::map<CAssetID, SResourceInstance> mResourceMap;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CGameExporter(const TString& rkInputDir, const TString& rkOutputDir);
|
CGameExporter(EGame Game, ERegion Region, const TString& rkGameName, const TString& rkGameID, float BuildVersion);
|
||||||
bool Export();
|
bool Export(nod::DiscBase *pDisc, const TString& rkOutputDir, CAssetNameMap *pNameMap, CGameInfo *pGameInfo);
|
||||||
void LoadResource(const CAssetID& rkID, std::vector<u8>& rBuffer);
|
void LoadResource(const CAssetID& rkID, std::vector<u8>& rBuffer);
|
||||||
|
|
||||||
|
inline TWideString ProjectPath() const { return mProjectPath; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void CopyDiscData();
|
bool ExtractDiscData();
|
||||||
void FindBuildVersion();
|
bool ExtractDiscNodeRecursive(const nod::Node *pkNode, const TWideString& rkDir, const nod::ExtractionContext& rkContext);
|
||||||
void LoadPaks();
|
void LoadPaks();
|
||||||
void LoadResource(const SResourceInstance& rkResource, std::vector<u8>& rBuffer);
|
void LoadResource(const SResourceInstance& rkResource, std::vector<u8>& rBuffer);
|
||||||
void ExportCookedResources();
|
void ExportCookedResources();
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
#include "CGameInfo.h"
|
#include "CGameInfo.h"
|
||||||
|
#include <Common/FileUtil.h>
|
||||||
|
|
||||||
void CGameInfo::LoadGameInfo(EGame Game)
|
void CGameInfo::LoadGameInfo(EGame Game)
|
||||||
{
|
{
|
||||||
Game = RoundGame(Game);
|
Game = RoundGame(Game);
|
||||||
mGame = Game;
|
mGame = Game;
|
||||||
|
|
||||||
LoadGameInfo(GetDefaultGameInfoPath(Game));
|
TString Path = GetDefaultGameInfoPath(Game);
|
||||||
|
if (FileUtil::Exists(Path))
|
||||||
|
LoadGameInfo(Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameInfo::LoadGameInfo(TString Path)
|
void CGameInfo::LoadGameInfo(TString Path)
|
||||||
@ -59,5 +62,5 @@ TString CGameInfo::GetDefaultGameInfoPath(EGame Game)
|
|||||||
return "";
|
return "";
|
||||||
|
|
||||||
TString GameName = GetGameShortName(Game);
|
TString GameName = GetGameShortName(Game);
|
||||||
return TString::Format("%s\\GameInfo%s.xml", *gkGameInfoDir, *GameName);
|
return TString::Format("%s\\GameInfo%s.%s", *gkGameInfoDir, *GameName, *gkGameInfoExt);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
const TString gkGameInfoDir = "..\\resources\\gameinfo";
|
const TString gkGameInfoDir = "..\\resources\\gameinfo";
|
||||||
|
const TString gkGameInfoExt = "xml";
|
||||||
|
|
||||||
class CGameInfo
|
class CGameInfo
|
||||||
{
|
{
|
||||||
@ -34,6 +35,8 @@ public:
|
|||||||
static CGameInfo* GetGameInfo(EGame Game);
|
static CGameInfo* GetGameInfo(EGame Game);
|
||||||
static EGame RoundGame(EGame Game);
|
static EGame RoundGame(EGame Game);
|
||||||
static TString GetDefaultGameInfoPath(EGame Game);
|
static TString GetDefaultGameInfoPath(EGame Game);
|
||||||
|
|
||||||
|
inline static TString GetExtension() { return gkGameInfoExt; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CGAMEINFO
|
#endif // CGAMEINFO
|
||||||
|
@ -10,30 +10,16 @@ CGameProject::~CGameProject()
|
|||||||
ASSERT(!mpResourceStore->IsDirty());
|
ASSERT(!mpResourceStore->IsDirty());
|
||||||
|
|
||||||
if (IsActive())
|
if (IsActive())
|
||||||
|
{
|
||||||
mspActiveProject = nullptr;
|
mspActiveProject = nullptr;
|
||||||
|
gpResourceStore = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
delete mpAudioManager;
|
delete mpAudioManager;
|
||||||
delete mpGameInfo;
|
delete mpGameInfo;
|
||||||
delete mpResourceStore;
|
delete mpResourceStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CGameProject::Load(const TWideString& rkPath)
|
|
||||||
{
|
|
||||||
mProjectRoot = rkPath.GetFileDirectory();
|
|
||||||
mProjectRoot.Replace(L"/", L"\\");
|
|
||||||
|
|
||||||
TString ProjPath = rkPath.ToUTF8();
|
|
||||||
CXMLReader Reader(ProjPath);
|
|
||||||
mGame = Reader.Game();
|
|
||||||
Serialize(Reader);
|
|
||||||
CTemplateLoader::LoadGameTemplates(mGame);
|
|
||||||
|
|
||||||
mpResourceStore->LoadResourceDatabase();
|
|
||||||
mpGameInfo->LoadGameInfo(mGame);
|
|
||||||
mpAudioManager->LoadAssets();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CGameProject::Save()
|
void CGameProject::Save()
|
||||||
{
|
{
|
||||||
TString ProjPath = ProjectPath().ToUTF8();
|
TString ProjPath = ProjectPath().ToUTF8();
|
||||||
@ -44,8 +30,19 @@ void CGameProject::Save()
|
|||||||
void CGameProject::Serialize(IArchive& rArc)
|
void CGameProject::Serialize(IArchive& rArc)
|
||||||
{
|
{
|
||||||
rArc << SERIAL("Name", mProjectName)
|
rArc << SERIAL("Name", mProjectName)
|
||||||
|
<< SERIAL("Region", mRegion)
|
||||||
|
<< SERIAL("GameID", mGameID)
|
||||||
<< SERIAL("BuildVersion", mBuildVersion)
|
<< SERIAL("BuildVersion", mBuildVersion)
|
||||||
<< SERIAL("ResourceDB", mResourceDBPath);
|
<< SERIAL("DolPath", mDolPath)
|
||||||
|
<< SERIAL("ApploaderPath", mApploaderPath);
|
||||||
|
|
||||||
|
if (rArc.Game() >= eCorruption)
|
||||||
|
rArc << SERIAL("PartitionHeaderPath", mPartitionHeaderPath);
|
||||||
|
|
||||||
|
if (!IsWiiBuild())
|
||||||
|
rArc << SERIAL("FstAddress", mFilesystemAddress);
|
||||||
|
|
||||||
|
rArc << SERIAL("ResourceDB", mResourceDBPath);
|
||||||
|
|
||||||
// Packages
|
// Packages
|
||||||
std::vector<TString> PackageList;
|
std::vector<TString> PackageList;
|
||||||
@ -129,3 +126,52 @@ CAssetID CGameProject::FindNamedResource(const TString& rkName) const
|
|||||||
|
|
||||||
return CAssetID::InvalidID(mGame);
|
return CAssetID::InvalidID(mGame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CGameProject* CGameProject::CreateProjectForExport(
|
||||||
|
CGameExporter *pExporter,
|
||||||
|
const TWideString& rkProjRootDir,
|
||||||
|
EGame Game,
|
||||||
|
ERegion Region,
|
||||||
|
const TString& rkGameID,
|
||||||
|
float BuildVer,
|
||||||
|
const TWideString& rkDolPath,
|
||||||
|
const TWideString& rkApploaderPath,
|
||||||
|
const TWideString& rkPartitionHeaderPath,
|
||||||
|
u32 FstAddress
|
||||||
|
)
|
||||||
|
{
|
||||||
|
CGameProject *pProj = new CGameProject;
|
||||||
|
pProj->mGame = Game;
|
||||||
|
pProj->mRegion = Region;
|
||||||
|
pProj->mGameID = rkGameID;
|
||||||
|
pProj->mBuildVersion = BuildVer;
|
||||||
|
pProj->mDolPath = rkDolPath;
|
||||||
|
pProj->mApploaderPath = rkApploaderPath;
|
||||||
|
pProj->mPartitionHeaderPath = rkPartitionHeaderPath;
|
||||||
|
pProj->mFilesystemAddress = FstAddress;
|
||||||
|
|
||||||
|
pProj->mProjectRoot = rkProjRootDir;
|
||||||
|
pProj->mProjectRoot.Replace(L"/", L"\\");
|
||||||
|
pProj->mpResourceStore = new CResourceStore(pProj, pExporter, L"Content\\", L"Cooked\\", Game);
|
||||||
|
pProj->mpGameInfo->LoadGameInfo(Game);
|
||||||
|
return pProj;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGameProject* CGameProject::LoadProject(const TWideString& rkProjPath)
|
||||||
|
{
|
||||||
|
CGameProject *pProj = new CGameProject;
|
||||||
|
pProj->mProjectRoot = rkProjPath.GetFileDirectory();
|
||||||
|
pProj->mProjectRoot.Replace(L"/", L"\\");
|
||||||
|
|
||||||
|
TString ProjPath = rkProjPath.ToUTF8();
|
||||||
|
CXMLReader Reader(ProjPath);
|
||||||
|
pProj->mGame = Reader.Game();
|
||||||
|
pProj->Serialize(Reader);
|
||||||
|
CTemplateLoader::LoadGameTemplates(pProj->mGame);
|
||||||
|
|
||||||
|
pProj->mpResourceStore = new CResourceStore(pProj);
|
||||||
|
pProj->mpResourceStore->LoadResourceDatabase();
|
||||||
|
pProj->mpGameInfo->LoadGameInfo(pProj->mGame);
|
||||||
|
pProj->mpAudioManager->LoadAssets();
|
||||||
|
return pProj;
|
||||||
|
}
|
||||||
|
@ -14,9 +14,16 @@
|
|||||||
|
|
||||||
class CGameProject
|
class CGameProject
|
||||||
{
|
{
|
||||||
EGame mGame;
|
|
||||||
float mBuildVersion;
|
|
||||||
TString mProjectName;
|
TString mProjectName;
|
||||||
|
EGame mGame;
|
||||||
|
ERegion mRegion;
|
||||||
|
TString mGameID;
|
||||||
|
float mBuildVersion;
|
||||||
|
TWideString mDolPath;
|
||||||
|
TWideString mApploaderPath;
|
||||||
|
TWideString mPartitionHeaderPath;
|
||||||
|
u32 mFilesystemAddress;
|
||||||
|
|
||||||
TWideString mProjectRoot;
|
TWideString mProjectRoot;
|
||||||
TWideString mResourceDBPath;
|
TWideString mResourceDBPath;
|
||||||
std::vector<CPackage*> mPackages;
|
std::vector<CPackage*> mPackages;
|
||||||
@ -34,52 +41,45 @@ class CGameProject
|
|||||||
|
|
||||||
static CGameProject *mspActiveProject;
|
static CGameProject *mspActiveProject;
|
||||||
|
|
||||||
public:
|
// Private Constructor
|
||||||
CGameProject()
|
CGameProject()
|
||||||
: mGame(eUnknownGame)
|
: mProjectName("Unnamed Project")
|
||||||
, mProjectName("Unnamed Project")
|
, mGame(eUnknownGame)
|
||||||
|
, mRegion(eRegion_Unknown)
|
||||||
|
, mGameID("000000")
|
||||||
|
, mBuildVersion(0.f)
|
||||||
, mResourceDBPath(L"ResourceDB.rdb")
|
, mResourceDBPath(L"ResourceDB.rdb")
|
||||||
|
, mpResourceStore(nullptr)
|
||||||
{
|
{
|
||||||
mpResourceStore = new CResourceStore(this);
|
|
||||||
mpGameInfo = new CGameInfo();
|
mpGameInfo = new CGameInfo();
|
||||||
mpAudioManager = new CAudioManager(this);
|
mpAudioManager = new CAudioManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
CGameProject(const TWideString& rkProjRootDir)
|
public:
|
||||||
: mGame(eUnknownGame)
|
|
||||||
, mProjectName("Unnamed Project")
|
|
||||||
, mProjectRoot(rkProjRootDir)
|
|
||||||
, mResourceDBPath(L"ResourceDB.rdb")
|
|
||||||
{
|
|
||||||
mProjectRoot.Replace(L"/", L"\\");
|
|
||||||
mpResourceStore = new CResourceStore(this);
|
|
||||||
mpGameInfo = new CGameInfo();
|
|
||||||
mpAudioManager = new CAudioManager(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
CGameProject(CGameExporter *pExporter, const TWideString& rkProjRootDir, EGame Game, float BuildVer)
|
|
||||||
: mGame(Game)
|
|
||||||
, mBuildVersion(BuildVer)
|
|
||||||
, mProjectName(CMasterTemplate::FindGameName(Game))
|
|
||||||
, mProjectRoot(rkProjRootDir)
|
|
||||||
, mResourceDBPath(L"ResourceDB.rdb")
|
|
||||||
{
|
|
||||||
mProjectRoot.Replace(L"/", L"\\");
|
|
||||||
mpResourceStore = new CResourceStore(this, pExporter, L"Content\\", L"Cooked\\", Game);
|
|
||||||
mpGameInfo = new CGameInfo();
|
|
||||||
mpGameInfo->LoadGameInfo(mGame);
|
|
||||||
mpAudioManager = new CAudioManager(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
~CGameProject();
|
~CGameProject();
|
||||||
|
|
||||||
bool Load(const TWideString& rkPath);
|
|
||||||
void Save();
|
void Save();
|
||||||
void Serialize(IArchive& rArc);
|
void Serialize(IArchive& rArc);
|
||||||
void SetActive();
|
void SetActive();
|
||||||
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;
|
||||||
|
|
||||||
|
// Static
|
||||||
|
static CGameProject* CreateProjectForExport(
|
||||||
|
CGameExporter *pExporter,
|
||||||
|
const TWideString& rkProjRootDir,
|
||||||
|
EGame Game,
|
||||||
|
ERegion Region,
|
||||||
|
const TString& rkGameID,
|
||||||
|
float BuildVer,
|
||||||
|
const TWideString& rkDolPath,
|
||||||
|
const TWideString& rkApploaderPath,
|
||||||
|
const TWideString& rkPartitionHeaderPath,
|
||||||
|
u32 FstAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
static CGameProject* LoadProject(const TWideString& rkProjPath);
|
||||||
|
|
||||||
// Directory Handling
|
// Directory Handling
|
||||||
inline TWideString ProjectRoot() const { return mProjectRoot; }
|
inline TWideString ProjectRoot() const { return mProjectRoot; }
|
||||||
inline TWideString ResourceDBPath(bool Relative) const { return Relative ? mResourceDBPath : mProjectRoot + mResourceDBPath; }
|
inline TWideString ResourceDBPath(bool Relative) const { return Relative ? mResourceDBPath : mProjectRoot + mResourceDBPath; }
|
||||||
@ -101,6 +101,7 @@ public:
|
|||||||
inline EGame Game() const { return mGame; }
|
inline EGame Game() const { return mGame; }
|
||||||
inline float BuildVersion() const { return mBuildVersion; }
|
inline float BuildVersion() const { return mBuildVersion; }
|
||||||
inline bool IsActive() const { return mspActiveProject == this; }
|
inline bool IsActive() const { return mspActiveProject == this; }
|
||||||
|
inline bool IsWiiBuild() const { return mBuildVersion >= 3.f; }
|
||||||
|
|
||||||
static inline CGameProject* ActiveProject() { return mspActiveProject; }
|
static inline CGameProject* ActiveProject() { return mspActiveProject; }
|
||||||
};
|
};
|
||||||
|
@ -20,7 +20,7 @@ void CPackage::Load()
|
|||||||
void CPackage::Save()
|
void CPackage::Save()
|
||||||
{
|
{
|
||||||
TWideString DefPath = DefinitionPath(false);
|
TWideString DefPath = DefinitionPath(false);
|
||||||
FileUtil::CreateDirectory(DefPath.GetFileDirectory());
|
FileUtil::MakeDirectory(DefPath.GetFileDirectory());
|
||||||
|
|
||||||
CXMLWriter Writer(DefPath.ToUTF8(), "PackageDefinition", 0, mpProject ? mpProject->Game() : eUnknownGame);
|
CXMLWriter Writer(DefPath.ToUTF8(), "PackageDefinition", 0, mpProject ? mpProject->Game() : eUnknownGame);
|
||||||
Serialize(Writer);
|
Serialize(Writer);
|
||||||
|
@ -190,7 +190,7 @@ bool CResourceEntry::Save(bool SkipCacheSave /*= false*/)
|
|||||||
// Note: We call Serialize directly for resources to avoid having a redundant resource root node in the output file.
|
// Note: We call Serialize directly for resources to avoid having a redundant resource root node in the output file.
|
||||||
TString Path = RawAssetPath();
|
TString Path = RawAssetPath();
|
||||||
TString Dir = Path.GetFileDirectory();
|
TString Dir = Path.GetFileDirectory();
|
||||||
FileUtil::CreateDirectory(Dir.ToUTF16());
|
FileUtil::MakeDirectory(Dir.ToUTF16());
|
||||||
|
|
||||||
TString SerialName = mpTypeInfo->TypeName();
|
TString SerialName = mpTypeInfo->TypeName();
|
||||||
SerialName.RemoveWhitespace();
|
SerialName.RemoveWhitespace();
|
||||||
@ -268,6 +268,7 @@ CResource* CResourceEntry::LoadCooked(IInputStream& rInput)
|
|||||||
gpResourceStore = mpStore;
|
gpResourceStore = mpStore;
|
||||||
|
|
||||||
mpResource = CResourceFactory::LoadCookedResource(this, rInput);
|
mpResource = CResourceFactory::LoadCookedResource(this, rInput);
|
||||||
|
if (mpResource)
|
||||||
mpStore->TrackLoadedResource(this);
|
mpStore->TrackLoadedResource(this);
|
||||||
|
|
||||||
gpResourceStore = pOldStore;
|
gpResourceStore = pOldStore;
|
||||||
|
@ -81,20 +81,31 @@ class CAnimSet : public CResource
|
|||||||
std::vector<CAnimEventData*> mAnimEvents; // note: these are for MP2, where event data isn't a standalone resource; these are owned by the animset
|
std::vector<CAnimEventData*> mAnimEvents; // note: these are for MP2, where event data isn't a standalone resource; these are owned by the animset
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CAnimSet(CResourceEntry *pEntry = 0) : CResource(pEntry) {}
|
CAnimSet(CResourceEntry *pEntry = 0)
|
||||||
|
: CResource(pEntry)
|
||||||
|
, mpDefaultTransition(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
~CAnimSet()
|
~CAnimSet()
|
||||||
{
|
{
|
||||||
|
for (u32 iAnim = 0; iAnim < mAnimations.size(); iAnim++)
|
||||||
|
delete mAnimations[iAnim].pMetaAnim;
|
||||||
|
|
||||||
|
for (u32 iTrans = 0; iTrans < mTransitions.size(); iTrans++)
|
||||||
|
delete mTransitions[iTrans].pMetaTrans;
|
||||||
|
|
||||||
|
for (u32 iHalf = 0; iHalf < mHalfTransitions.size(); iHalf++)
|
||||||
|
delete mHalfTransitions[iHalf].pMetaTrans;
|
||||||
|
|
||||||
|
delete mpDefaultTransition;
|
||||||
|
|
||||||
// For MP2, anim events need to be cleaned up manually
|
// For MP2, anim events need to be cleaned up manually
|
||||||
if (Game() >= eEchoesDemo)
|
|
||||||
{
|
|
||||||
for (u32 iEvent = 0; iEvent < mAnimEvents.size(); iEvent++)
|
for (u32 iEvent = 0; iEvent < mAnimEvents.size(); iEvent++)
|
||||||
{
|
{
|
||||||
ASSERT(mAnimEvents[iEvent] && !mAnimEvents[iEvent]->Entry());
|
ASSERT(mAnimEvents[iEvent] && !mAnimEvents[iEvent]->Entry());
|
||||||
delete mAnimEvents[iEvent];
|
delete mAnimEvents[iEvent];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
CDependencyTree* BuildDependencyTree() const
|
CDependencyTree* BuildDependencyTree() const
|
||||||
{
|
{
|
||||||
|
@ -42,7 +42,7 @@ void CTemplateWriter::SaveAllTemplates()
|
|||||||
{
|
{
|
||||||
// Create directory
|
// Create directory
|
||||||
std::list<CMasterTemplate*> MasterList = CMasterTemplate::MasterList();
|
std::list<CMasterTemplate*> MasterList = CMasterTemplate::MasterList();
|
||||||
FileUtil::CreateDirectory(smTemplatesDir);
|
FileUtil::MakeDirectory(smTemplatesDir);
|
||||||
|
|
||||||
// Resave property list
|
// Resave property list
|
||||||
SavePropertyList();
|
SavePropertyList();
|
||||||
@ -95,7 +95,7 @@ void CTemplateWriter::SaveGameTemplates(CMasterTemplate *pMaster)
|
|||||||
// Create directory
|
// Create directory
|
||||||
TString OutFile = smTemplatesDir + pMaster->mSourceFile;
|
TString OutFile = smTemplatesDir + pMaster->mSourceFile;
|
||||||
TString OutDir = OutFile.GetFileDirectory();
|
TString OutDir = OutFile.GetFileDirectory();
|
||||||
FileUtil::CreateDirectory(OutDir);
|
FileUtil::MakeDirectory(OutDir);
|
||||||
|
|
||||||
// Resave script templates
|
// Resave script templates
|
||||||
for (auto it = pMaster->mTemplates.begin(); it != pMaster->mTemplates.end(); it++)
|
for (auto it = pMaster->mTemplates.begin(); it != pMaster->mTemplates.end(); it++)
|
||||||
@ -226,7 +226,7 @@ void CTemplateWriter::SaveScriptTemplate(CScriptTemplate *pTemp)
|
|||||||
// Create directory
|
// Create directory
|
||||||
TString OutFile = smTemplatesDir + pMaster->GetDirectory() + pTemp->mSourceFile;
|
TString OutFile = smTemplatesDir + pMaster->GetDirectory() + pTemp->mSourceFile;
|
||||||
TString OutDir = OutFile.GetFileDirectory();
|
TString OutDir = OutFile.GetFileDirectory();
|
||||||
FileUtil::CreateDirectory(*OutDir);
|
FileUtil::MakeDirectory(*OutDir);
|
||||||
|
|
||||||
// Create new document
|
// Create new document
|
||||||
XMLDocument ScriptXML;
|
XMLDocument ScriptXML;
|
||||||
@ -432,7 +432,7 @@ void CTemplateWriter::SaveStructTemplate(CStructTemplate *pTemp)
|
|||||||
TString OutFile = smTemplatesDir + pMaster->GetDirectory() + pTemp->mSourceFile;
|
TString OutFile = smTemplatesDir + pMaster->GetDirectory() + pTemp->mSourceFile;
|
||||||
TString OutDir = OutFile.GetFileDirectory();
|
TString OutDir = OutFile.GetFileDirectory();
|
||||||
TString Name = OutFile.GetFileName(false);
|
TString Name = OutFile.GetFileName(false);
|
||||||
FileUtil::CreateDirectory(OutDir);
|
FileUtil::MakeDirectory(OutDir);
|
||||||
|
|
||||||
// Create new document and write struct properties to it
|
// Create new document and write struct properties to it
|
||||||
XMLDocument StructXML;
|
XMLDocument StructXML;
|
||||||
@ -456,7 +456,7 @@ void CTemplateWriter::SaveEnumTemplate(CEnumTemplate *pTemp)
|
|||||||
TString OutFile = smTemplatesDir + pMaster->GetDirectory() + pTemp->mSourceFile;
|
TString OutFile = smTemplatesDir + pMaster->GetDirectory() + pTemp->mSourceFile;
|
||||||
TString OutDir = OutFile.GetFileDirectory();
|
TString OutDir = OutFile.GetFileDirectory();
|
||||||
TString Name = OutFile.GetFileName(false);
|
TString Name = OutFile.GetFileName(false);
|
||||||
FileUtil::CreateDirectory(OutDir);
|
FileUtil::MakeDirectory(OutDir);
|
||||||
|
|
||||||
// Create new document and write enumerators to it
|
// Create new document and write enumerators to it
|
||||||
XMLDocument EnumXML;
|
XMLDocument EnumXML;
|
||||||
@ -479,7 +479,7 @@ void CTemplateWriter::SaveBitfieldTemplate(CBitfieldTemplate *pTemp)
|
|||||||
TString OutFile = smTemplatesDir + pMaster->GetDirectory() + pTemp->mSourceFile;
|
TString OutFile = smTemplatesDir + pMaster->GetDirectory() + pTemp->mSourceFile;
|
||||||
TString OutDir = OutFile.GetFileDirectory();
|
TString OutDir = OutFile.GetFileDirectory();
|
||||||
TString Name = pTemp->mSourceFile.GetFileName(false);
|
TString Name = pTemp->mSourceFile.GetFileName(false);
|
||||||
FileUtil::CreateDirectory(OutDir);
|
FileUtil::MakeDirectory(OutDir);
|
||||||
|
|
||||||
// Create new document and write enumerators to it
|
// Create new document and write enumerators to it
|
||||||
XMLDocument BitfieldXML;
|
XMLDocument BitfieldXML;
|
||||||
|
@ -162,6 +162,7 @@ void CAnimSetLoader::ProcessPrimitives()
|
|||||||
for (u32 iTrans = 0; iTrans < pSet->mTransitions.size(); iTrans++)
|
for (u32 iTrans = 0; iTrans < pSet->mTransitions.size(); iTrans++)
|
||||||
pSet->mTransitions[iTrans].pMetaTrans->GetUniquePrimitives(UniquePrimitives);
|
pSet->mTransitions[iTrans].pMetaTrans->GetUniquePrimitives(UniquePrimitives);
|
||||||
|
|
||||||
|
if (pSet->mpDefaultTransition)
|
||||||
pSet->mpDefaultTransition->GetUniquePrimitives(UniquePrimitives);
|
pSet->mpDefaultTransition->GetUniquePrimitives(UniquePrimitives);
|
||||||
|
|
||||||
for (u32 iTrans = 0; iTrans < pSet->mHalfTransitions.size(); iTrans++)
|
for (u32 iTrans = 0; iTrans < pSet->mHalfTransitions.size(); iTrans++)
|
||||||
@ -181,6 +182,8 @@ void CAnimSetLoader::ProcessPrimitives()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add animations referenced by default transition
|
// Add animations referenced by default transition
|
||||||
|
if (pSet->mpDefaultTransition)
|
||||||
|
{
|
||||||
std::set<CAnimPrimitive> DefaultTransPrimitives;
|
std::set<CAnimPrimitive> DefaultTransPrimitives;
|
||||||
pSet->mpDefaultTransition->GetUniquePrimitives(DefaultTransPrimitives);
|
pSet->mpDefaultTransition->GetUniquePrimitives(DefaultTransPrimitives);
|
||||||
|
|
||||||
@ -194,6 +197,7 @@ void CAnimSetLoader::ProcessPrimitives()
|
|||||||
rChar.UsedAnimationIndices.insert(rkPrim.ID());
|
rChar.UsedAnimationIndices.insert(rkPrim.ID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add animations referenced by used transitions
|
// Add animations referenced by used transitions
|
||||||
for (u32 iChar = 0; iChar < pSet->mCharacters.size(); iChar++)
|
for (u32 iChar = 0; iChar < pSet->mCharacters.size(); iChar++)
|
||||||
|
@ -12,6 +12,8 @@ class CResourceEntry;
|
|||||||
class CWorldEditor;
|
class CWorldEditor;
|
||||||
class IEditor;
|
class IEditor;
|
||||||
|
|
||||||
|
const int gkTickFrequencyMS = 8;
|
||||||
|
|
||||||
class CEditorApplication : public QApplication
|
class CEditorApplication : public QApplication
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -35,6 +37,9 @@ public:
|
|||||||
inline CResourceBrowser* ResourceBrowser() const { return mpResourceBrowser; }
|
inline CResourceBrowser* ResourceBrowser() const { return mpResourceBrowser; }
|
||||||
inline CProjectOverviewDialog* ProjectDialog() const { return mpProjectDialog; }
|
inline CProjectOverviewDialog* ProjectDialog() const { return mpProjectDialog; }
|
||||||
|
|
||||||
|
inline void SetEditorTicksEnabled(bool Enabled) { Enabled ? mRefreshTimer.start(gkTickFrequencyMS) : mRefreshTimer.stop(); }
|
||||||
|
inline bool AreEditorTicksEnabled() const { return mRefreshTimer.isActive(); }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void AddEditor(IEditor *pEditor);
|
void AddEditor(IEditor *pEditor);
|
||||||
void TickEditors();
|
void TickEditors();
|
||||||
|
360
src/Editor/CExportGameDialog.cpp
Normal file
360
src/Editor/CExportGameDialog.cpp
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
#include "CExportGameDialog.h"
|
||||||
|
#include "ui_CExportGameDialog.h"
|
||||||
|
#include "UICommon.h"
|
||||||
|
|
||||||
|
#include <Common/AssertMacro.h>
|
||||||
|
#include <Core/GameProject/CAssetNameMap.h>
|
||||||
|
#include <Core/GameProject/CGameExporter.h>
|
||||||
|
#include <Core/GameProject/CGameInfo.h>
|
||||||
|
#include <Core/Resource/Script/CMasterTemplate.h>
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include <nod/nod.hpp>
|
||||||
|
|
||||||
|
CExportGameDialog::CExportGameDialog(const QString& rkIsoPath, const QString& rkExportDir, QWidget *pParent /*= 0*/)
|
||||||
|
: QDialog(pParent)
|
||||||
|
, mpUI(new Ui::CExportGameDialog)
|
||||||
|
, mGame(eUnknownGame)
|
||||||
|
, mRegion(eRegion_Unknown)
|
||||||
|
, mTrilogy(false)
|
||||||
|
, mExportSuccess(true)
|
||||||
|
{
|
||||||
|
mpUI->setupUi(this);
|
||||||
|
|
||||||
|
// Set up disc
|
||||||
|
TWideString StrPath = TO_TWIDESTRING(rkIsoPath);
|
||||||
|
mpDisc = nod::OpenDiscFromImage(*StrPath).release();
|
||||||
|
|
||||||
|
if (ValidateGame())
|
||||||
|
{
|
||||||
|
mBuildVer = FindBuildVersion();
|
||||||
|
InitUI(rkExportDir);
|
||||||
|
|
||||||
|
TString IsoName = TO_TSTRING(rkIsoPath).GetFileName();
|
||||||
|
setWindowTitle(QString("Export Settings - %1").arg( TO_QSTRING(IsoName) ));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!mTrilogy)
|
||||||
|
UICommon::ErrorMsg(this, "Invalid ISO!");
|
||||||
|
|
||||||
|
delete mpDisc;
|
||||||
|
mpDisc = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CExportGameDialog::~CExportGameDialog()
|
||||||
|
{
|
||||||
|
delete mpUI;
|
||||||
|
delete mpDisc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecursiveAddToTree(const nod::Node *pkNode, QTreeWidgetItem *pParent);
|
||||||
|
|
||||||
|
void CExportGameDialog::InitUI(QString ExportDir)
|
||||||
|
{
|
||||||
|
ASSERT(mpDisc != nullptr);
|
||||||
|
|
||||||
|
// Export settings
|
||||||
|
ExportDir.replace('/', '\\');
|
||||||
|
|
||||||
|
TWideString DefaultNameMapPath = CAssetNameMap::DefaultNameMapPath();
|
||||||
|
if (!FileUtil::Exists(DefaultNameMapPath)) DefaultNameMapPath = L"";
|
||||||
|
|
||||||
|
TWideString DefaultGameInfoPath = CGameInfo::GetDefaultGameInfoPath(mGame);
|
||||||
|
if (!FileUtil::Exists(DefaultGameInfoPath)) DefaultGameInfoPath = L"";
|
||||||
|
|
||||||
|
mpUI->OutputDirectoryLineEdit->setText(ExportDir);
|
||||||
|
mpUI->AssetNameMapLineEdit->setText(TO_QSTRING(DefaultNameMapPath));
|
||||||
|
mpUI->GameEditorInfoLineEdit->setText(TO_QSTRING(DefaultGameInfoPath));
|
||||||
|
|
||||||
|
// Info boxes
|
||||||
|
mpUI->GameTitleLineEdit->setText( TO_QSTRING(mGameTitle) );
|
||||||
|
mpUI->GameIdLineEdit->setText( TO_QSTRING(mGameID) );
|
||||||
|
mpUI->BuildVersionLineEdit->setText( QString::number(mBuildVer) );
|
||||||
|
mpUI->RegionLineEdit->setText( mRegion == eRegion_NTSC ? "NTSC" :
|
||||||
|
mRegion == eRegion_PAL ? "PAL" : "JPN" );
|
||||||
|
|
||||||
|
// Disc tree widget
|
||||||
|
nod::Partition *pPartition = mpDisc->getDataPartition();
|
||||||
|
ASSERT(pPartition);
|
||||||
|
|
||||||
|
const nod::Node *pkDiscRoot = &pPartition->getFSTRoot();
|
||||||
|
if (mTrilogy)
|
||||||
|
pkDiscRoot = &*pkDiscRoot->find( GetGameShortName(mGame).ToStdString() );
|
||||||
|
|
||||||
|
QTreeWidgetItem *pTreeRoot = new QTreeWidgetItem((QTreeWidgetItem*) nullptr, QStringList(QString("Disc")));
|
||||||
|
mpUI->DiscFstTreeWidget->addTopLevelItem(pTreeRoot);
|
||||||
|
RecursiveAddToTree(pkDiscRoot, pTreeRoot);
|
||||||
|
pTreeRoot->setExpanded(true);
|
||||||
|
|
||||||
|
// Signals and slots
|
||||||
|
connect(mpUI->OutputDirectoryBrowseButton, SIGNAL(pressed()), this, SLOT(BrowseOutputDirectory()));
|
||||||
|
connect(mpUI->AssetNameMapBrowseButton, SIGNAL(pressed()), this, SLOT(BrowseAssetNameMap()));
|
||||||
|
connect(mpUI->GameEditorInfoBrowseButton, SIGNAL(pressed()), this, SLOT(BrowseGameEditorInfo()));
|
||||||
|
connect(mpUI->CancelButton, SIGNAL(pressed()), this, SLOT(close()));
|
||||||
|
connect(mpUI->ExportButton, SIGNAL(pressed()), this, SLOT(Export()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CExportGameDialog::ValidateGame()
|
||||||
|
{
|
||||||
|
if (!mpDisc) return false;
|
||||||
|
|
||||||
|
const nod::Header& rkHeader = mpDisc->getHeader();
|
||||||
|
mGameTitle = rkHeader.m_gameTitle;
|
||||||
|
mGameID = TString(6, 0);
|
||||||
|
memcpy(&mGameID[0], rkHeader.m_gameID, 6);
|
||||||
|
|
||||||
|
// Check region byte
|
||||||
|
switch (mGameID[3])
|
||||||
|
{
|
||||||
|
case 'E':
|
||||||
|
mRegion = eRegion_NTSC;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'P':
|
||||||
|
mRegion = eRegion_PAL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'J':
|
||||||
|
mRegion = eRegion_JPN;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set region byte to X so we don't need to compare every regional variant of the ID
|
||||||
|
// Then figure out what game this is
|
||||||
|
CFourCC GameID(&mGameID[0]);
|
||||||
|
GameID[3] = 'X';
|
||||||
|
|
||||||
|
switch (GameID.ToLong())
|
||||||
|
{
|
||||||
|
case IFOURCC('GM8X'):
|
||||||
|
// This ID is normally MP1, but it's used by the MP1 NTSC demo and the MP2 bonus disc demo as well
|
||||||
|
if (strcmp(rkHeader.m_gameTitle, "Long Game Name") == 0)
|
||||||
|
{
|
||||||
|
// todo - not handling demos yet
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mGame = ePrime;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IFOURCC('G2MX'):
|
||||||
|
// Echoes, but also appears in the MP3 proto
|
||||||
|
if (mGameID[4] == 'A' && mGameID[5] == 'B')
|
||||||
|
mGame = eCorruptionProto;
|
||||||
|
else
|
||||||
|
mGame = eEchoes;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IFOURCC('RM3X'):
|
||||||
|
mGame = eCorruption;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IFOURCC('SF8X'):
|
||||||
|
mGame = eReturns;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IFOURCC('R3MX'):
|
||||||
|
// Trilogy
|
||||||
|
mTrilogy = true;
|
||||||
|
if (!RequestTrilogyGame()) return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IFOURCC('R3IX'):
|
||||||
|
// MP1 Wii de Asobu
|
||||||
|
case IFOURCC('R32X'):
|
||||||
|
// MP2 Wii de Asobu
|
||||||
|
default:
|
||||||
|
// Unrecognized game ID
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CExportGameDialog::RequestTrilogyGame()
|
||||||
|
{
|
||||||
|
QDialog Dialog;
|
||||||
|
Dialog.setWindowTitle("Select Trilogy Game");
|
||||||
|
|
||||||
|
QLabel Label("You have selected a Metroid Prime: Trilogy ISO. Please pick a game to export:", &Dialog);
|
||||||
|
QComboBox ComboBox(&Dialog);
|
||||||
|
ComboBox.addItem("Metroid Prime");
|
||||||
|
ComboBox.addItem("Metroid Prime 2: Echoes");
|
||||||
|
ComboBox.addItem("Metroid Prime 3: Corruption");
|
||||||
|
QDialogButtonBox ButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &Dialog);
|
||||||
|
connect(&ButtonBox, SIGNAL(accepted()), &Dialog, SLOT(accept()));
|
||||||
|
connect(&ButtonBox, SIGNAL(rejected()), &Dialog, SLOT(reject()));
|
||||||
|
|
||||||
|
QVBoxLayout Layout;
|
||||||
|
Layout.addWidget(&Label);
|
||||||
|
Layout.addWidget(&ComboBox);
|
||||||
|
Layout.addWidget(&ButtonBox);
|
||||||
|
Dialog.setLayout(&Layout);
|
||||||
|
|
||||||
|
int Result = Dialog.exec();
|
||||||
|
|
||||||
|
if (Result == QDialog::Accepted)
|
||||||
|
{
|
||||||
|
switch (ComboBox.currentIndex())
|
||||||
|
{
|
||||||
|
case 0: mGame = ePrime; break;
|
||||||
|
case 1: mGame = eEchoes; break;
|
||||||
|
case 2: mGame = eCorruption; break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float CExportGameDialog::FindBuildVersion()
|
||||||
|
{
|
||||||
|
ASSERT(mpDisc != nullptr);
|
||||||
|
|
||||||
|
// MP1 demo build doesn't have a build version
|
||||||
|
if (mGame == ePrimeDemo) return 0.f;
|
||||||
|
|
||||||
|
// Get DOL buffer
|
||||||
|
std::unique_ptr<uint8_t[]> pDolData = mpDisc->getDataPartition()->getDOLBuf();
|
||||||
|
u32 DolSize = (u32) mpDisc->getDataPartition()->getDOLSize();
|
||||||
|
|
||||||
|
// Find build info string
|
||||||
|
const char *pkSearchText = "!#$MetroidBuildInfo!#$";
|
||||||
|
const int SearchTextSize = strlen(pkSearchText);
|
||||||
|
|
||||||
|
for (u32 SearchIdx = 0; SearchIdx < DolSize - SearchTextSize + 1; SearchIdx++)
|
||||||
|
{
|
||||||
|
int Match = 0;
|
||||||
|
|
||||||
|
while (pDolData[SearchIdx + Match] == pkSearchText[Match] && Match < SearchTextSize)
|
||||||
|
Match++;
|
||||||
|
|
||||||
|
if (Match == SearchTextSize)
|
||||||
|
{
|
||||||
|
// Found the build info string; extract version number
|
||||||
|
TString BuildInfo = (char*) &pDolData[SearchIdx + SearchTextSize];
|
||||||
|
int BuildVerStart = BuildInfo.IndexOfPhrase("Build v") + 7;
|
||||||
|
ASSERT(BuildVerStart != 6);
|
||||||
|
|
||||||
|
return BuildInfo.SubString(BuildVerStart, 5).ToFloat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::Error("Failed to find MetroidBuildInfo string. Build Version will be set to 0.");
|
||||||
|
return 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecursiveAddToTree(const nod::Node *pkNode, QTreeWidgetItem *pParent)
|
||||||
|
{
|
||||||
|
// Get sorted list of nodes
|
||||||
|
std::list<const nod::Node*> NodeList;
|
||||||
|
for (nod::Node::DirectoryIterator Iter = pkNode->begin(); Iter != pkNode->end(); ++Iter)
|
||||||
|
NodeList.push_back(&*Iter);
|
||||||
|
|
||||||
|
NodeList.sort([](const nod::Node *pkLeft, const nod::Node *pkRight) -> bool
|
||||||
|
{
|
||||||
|
if (pkLeft->getKind() != pkRight->getKind())
|
||||||
|
return pkLeft->getKind() == nod::Node::Kind::Directory;
|
||||||
|
else
|
||||||
|
return TString(pkLeft->getName()).ToUpper() < TString(pkRight->getName()).ToUpper();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add nodes to tree
|
||||||
|
static const QIcon skFileIcon = QIcon(":/icons/New.png");
|
||||||
|
static const QIcon skDirIcon = QIcon(":/icons/Open_24px.png");
|
||||||
|
|
||||||
|
for (auto Iter = NodeList.begin(); Iter != NodeList.end(); Iter++)
|
||||||
|
{
|
||||||
|
const nod::Node *pkNode = *Iter;
|
||||||
|
bool IsDir = pkNode->getKind() == nod::Node::Kind::Directory;
|
||||||
|
|
||||||
|
QTreeWidgetItem *pItem = new QTreeWidgetItem(pParent, QStringList(QString::fromStdString(pkNode->getName())) );
|
||||||
|
pItem->setIcon(0, QIcon(IsDir ? skDirIcon : skFileIcon));
|
||||||
|
|
||||||
|
if (IsDir)
|
||||||
|
RecursiveAddToTree(pkNode, pItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CExportGameDialog::BrowseOutputDirectory()
|
||||||
|
{
|
||||||
|
QString NewOutputDir = UICommon::OpenDirDialog(this, "Choose export directory");
|
||||||
|
if (!NewOutputDir.isEmpty()) mpUI->OutputDirectoryLineEdit->setText(NewOutputDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CExportGameDialog::BrowseAssetNameMap()
|
||||||
|
{
|
||||||
|
QString Filter = "*." + TO_QSTRING(CAssetNameMap::GetExtension());
|
||||||
|
QString NewNameMap = UICommon::OpenFileDialog(this, "Choose Asset Name Map", Filter);
|
||||||
|
if (!NewNameMap.isEmpty()) mpUI->AssetNameMapLineEdit->setText(NewNameMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CExportGameDialog::BrowseGameEditorInfo()
|
||||||
|
{
|
||||||
|
QString Filter = "*." + TO_QSTRING(CGameInfo::GetExtension());
|
||||||
|
QString NewGameInfo = UICommon::OpenFileDialog(this, "Choose Game Editor Info", Filter);
|
||||||
|
if (!NewGameInfo.isEmpty()) mpUI->GameEditorInfoLineEdit->setText(NewGameInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CExportGameDialog::Export()
|
||||||
|
{
|
||||||
|
QString ExportDir = mpUI->OutputDirectoryLineEdit->text();
|
||||||
|
QString NameMapPath = mpUI->AssetNameMapLineEdit->text();
|
||||||
|
QString GameInfoPath = mpUI->GameEditorInfoLineEdit->text();
|
||||||
|
|
||||||
|
// Validate export dir
|
||||||
|
if (ExportDir.isEmpty())
|
||||||
|
{
|
||||||
|
UICommon::ErrorMsg(this, "Please specify an output directory!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (!FileUtil::IsEmpty( TO_TSTRING(ExportDir) ))
|
||||||
|
{
|
||||||
|
QMessageBox::Button Button = QMessageBox::warning(this, "Warning", "<b>Warning:</b> The specified directory is not empty. Export anyway?", QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::NoButton);
|
||||||
|
if (Button != QMessageBox::Ok) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify name map path and game info path
|
||||||
|
if (!NameMapPath.isEmpty() && !FileUtil::Exists(TO_TSTRING(NameMapPath)))
|
||||||
|
{
|
||||||
|
UICommon::ErrorMsg(this, "The Asset Name Map path is invalid!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GameInfoPath.isEmpty() && !FileUtil::Exists(TO_TSTRING(GameInfoPath)))
|
||||||
|
{
|
||||||
|
UICommon::ErrorMsg(this, "The Game Editor Info path is invalid!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do export
|
||||||
|
close();
|
||||||
|
|
||||||
|
CAssetNameMap NameMap;
|
||||||
|
if (!NameMapPath.isEmpty())
|
||||||
|
NameMap.LoadAssetNames( TO_TSTRING(NameMapPath) );
|
||||||
|
|
||||||
|
CGameInfo GameInfo;
|
||||||
|
if (!GameInfoPath.isEmpty())
|
||||||
|
GameInfo.LoadGameInfo( TO_TSTRING(GameInfoPath) );
|
||||||
|
|
||||||
|
CGameExporter Exporter(mGame, mRegion, mGameTitle, mGameID, mBuildVer);
|
||||||
|
TString StrExportDir = TO_TSTRING(ExportDir);
|
||||||
|
StrExportDir.EnsureEndsWith('\\');
|
||||||
|
mExportSuccess = Exporter.Export(mpDisc, StrExportDir, &NameMap, &GameInfo);
|
||||||
|
|
||||||
|
if (!mExportSuccess)
|
||||||
|
UICommon::ErrorMsg(this, "Export failed!");
|
||||||
|
else
|
||||||
|
mNewProjectPath = TO_QSTRING(Exporter.ProjectPath());
|
||||||
|
}
|
52
src/Editor/CExportGameDialog.h
Normal file
52
src/Editor/CExportGameDialog.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#ifndef CEXPORTGAMEDIALOG_H
|
||||||
|
#define CEXPORTGAMEDIALOG_H
|
||||||
|
|
||||||
|
#include <Common/EGame.h>
|
||||||
|
#include <Core/GameProject/CGameProject.h>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QString>
|
||||||
|
#include <nod/DiscBase.hpp>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class CExportGameDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CExportGameDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Ui::CExportGameDialog *mpUI;
|
||||||
|
nod::DiscBase *mpDisc;
|
||||||
|
|
||||||
|
TString mGameTitle;
|
||||||
|
TString mGameID;
|
||||||
|
EGame mGame;
|
||||||
|
ERegion mRegion;
|
||||||
|
float mBuildVer;
|
||||||
|
bool mTrilogy;
|
||||||
|
|
||||||
|
bool mExportSuccess;
|
||||||
|
QString mNewProjectPath;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CExportGameDialog(const QString& rkIsoPath, const QString& rkExportDir, QWidget *pParent = 0);
|
||||||
|
~CExportGameDialog();
|
||||||
|
|
||||||
|
void InitUI(QString ExportDir);
|
||||||
|
bool ValidateGame();
|
||||||
|
bool RequestTrilogyGame();
|
||||||
|
float FindBuildVersion();
|
||||||
|
|
||||||
|
// Accessors
|
||||||
|
inline bool HasValidDisc() const { return mpDisc != nullptr; }
|
||||||
|
inline bool ExportSucceeded() const { return mExportSuccess; }
|
||||||
|
inline QString ProjectPath() const { return mNewProjectPath; }
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void BrowseOutputDirectory();
|
||||||
|
void BrowseAssetNameMap();
|
||||||
|
void BrowseGameEditorInfo();
|
||||||
|
void Export();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CEXPORTGAMEDIALOG_H
|
271
src/Editor/CExportGameDialog.ui
Normal file
271
src/Editor/CExportGameDialog.ui
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>CExportGameDialog</class>
|
||||||
|
<widget class="QDialog" name="CExportGameDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>339</width>
|
||||||
|
<height>642</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Export Settings</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="HeaderLabel">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>14</pointsize>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Game Export Settings</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="OutputDirectoryLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Output Directory:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="OutputDirectoryLineEdit">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>1</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QPushButton" name="OutputDirectoryBrowseButton">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="AssetNameMapLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Asset Name Map:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="AssetNameMapLineEdit">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>1</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QPushButton" name="AssetNameMapBrowseButton">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="GameEditorInfoLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Game Editor Info:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="GameEditorInfoLineEdit">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>1</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="2">
|
||||||
|
<widget class="QPushButton" name="GameEditorInfoBrowseButton">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="GameInfoGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Game</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="GameTitleLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Game Title:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="GameTitleLineEdit">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="GameIdLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Game ID:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="GameIdLineEdit">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="RegionLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Region:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="RegionLineEdit">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="BuildVersionLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Build Version:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="BuildVersionLineEdit">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeWidget" name="DiscFstTreeWidget">
|
||||||
|
<property name="editTriggers">
|
||||||
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
|
</property>
|
||||||
|
<property name="alternatingRowColors">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="verticalScrollMode">
|
||||||
|
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="indentation">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<attribute name="headerVisible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">1</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</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="ExportButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Export</string>
|
||||||
|
</property>
|
||||||
|
<property name="default">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="CancelButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Cancel</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
@ -1,6 +1,7 @@
|
|||||||
#include "CProjectOverviewDialog.h"
|
#include "CProjectOverviewDialog.h"
|
||||||
#include "ui_CProjectOverviewDialog.h"
|
#include "ui_CProjectOverviewDialog.h"
|
||||||
#include "CEditorApplication.h"
|
#include "CEditorApplication.h"
|
||||||
|
#include "CExportGameDialog.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>
|
||||||
@ -28,17 +29,13 @@ CProjectOverviewDialog::~CProjectOverviewDialog()
|
|||||||
delete mpUI;
|
delete mpUI;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CProjectOverviewDialog::OpenProject()
|
void CProjectOverviewDialog::InternalLoadProject(const QString& rkPath)
|
||||||
{
|
{
|
||||||
// Open project file
|
|
||||||
QString ProjPath = QFileDialog::getOpenFileName(this, "Open Project", "", "Game Project (*.prj)");
|
|
||||||
if (ProjPath.isEmpty()) return;
|
|
||||||
|
|
||||||
// Load project
|
// Load project
|
||||||
TWideString Path = TO_TWIDESTRING(ProjPath);
|
TWideString Path = TO_TWIDESTRING(rkPath);
|
||||||
CGameProject *pNewProj = new CGameProject(Path.GetFileDirectory());
|
CGameProject *pNewProj = CGameProject::LoadProject(Path);
|
||||||
|
|
||||||
if (pNewProj->Load(Path))
|
if (pNewProj)
|
||||||
{
|
{
|
||||||
if (mpProject) delete mpProject;
|
if (mpProject) delete mpProject;
|
||||||
mpProject = pNewProj;
|
mpProject = pNewProj;
|
||||||
@ -49,49 +46,34 @@ void CProjectOverviewDialog::OpenProject()
|
|||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
Log::Error("Failed to load project");
|
Log::Error("Failed to load project");
|
||||||
delete pNewProj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CProjectOverviewDialog::OpenProject()
|
||||||
|
{
|
||||||
|
// Open project file
|
||||||
|
QString ProjPath = UICommon::OpenFileDialog(this, "Open Project", "Game Project (*.prj)");
|
||||||
|
if (!ProjPath.isEmpty()) InternalLoadProject(ProjPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CProjectOverviewDialog::ExportGame()
|
void CProjectOverviewDialog::ExportGame()
|
||||||
{
|
{
|
||||||
// TEMP - hardcoded names for convenience. will remove later!
|
QString IsoPath = UICommon::OpenFileDialog(this, "Select ISO", "*.iso *.gcm *.tgc *.wbfs");
|
||||||
#define USE_HARDCODED_GAME_ROOT 0
|
if (IsoPath.isEmpty()) return;
|
||||||
#define USE_HARDCODED_EXPORT_DIR 0
|
|
||||||
|
|
||||||
#if USE_HARDCODED_GAME_ROOT
|
QString ExportDir = UICommon::OpenDirDialog(this, "Select output export directory");
|
||||||
QString GameRoot = "E:/Unpacked/Metroid Prime";
|
|
||||||
#else
|
|
||||||
QString GameRoot = QFileDialog::getExistingDirectory(this, "Select game root directory");
|
|
||||||
if (GameRoot.isEmpty()) return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if USE_HARDCODED_EXPORT_DIR
|
|
||||||
QString ExportDir = "E:/Unpacked/ExportTest";
|
|
||||||
#else
|
|
||||||
QString ExportDir = QFileDialog::getExistingDirectory(this, "Select output export directory");
|
|
||||||
if (ExportDir.isEmpty()) return;
|
if (ExportDir.isEmpty()) return;
|
||||||
#endif
|
|
||||||
|
|
||||||
// Verify valid game root by checking if opening.bnr exists
|
CExportGameDialog ExportDialog(IsoPath, ExportDir, this);
|
||||||
TString OpeningBNR = TO_TSTRING(GameRoot) + "/opening.bnr";
|
if (ExportDialog.HasValidDisc()) ExportDialog.exec();
|
||||||
if (!FileUtil::Exists(OpeningBNR.ToUTF16()))
|
|
||||||
|
if (ExportDialog.ExportSucceeded())
|
||||||
{
|
{
|
||||||
QMessageBox::warning(this, "Error", "Error; this is not a valid game root directory!");
|
int OpenChoice = QMessageBox::information(this, "Export complete", "Export finished successfully! Open new project?", QMessageBox::Yes, QMessageBox::No);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify export directory is empty
|
if (OpenChoice == QMessageBox::Yes)
|
||||||
if (!FileUtil::IsEmpty(TO_TSTRING(ExportDir)))
|
InternalLoadProject(ExportDialog.ProjectPath());
|
||||||
{
|
|
||||||
QMessageBox::Button Button = QMessageBox::warning(this, "Warning", "<b>Warning:</b> The specified directory is not empty. Export anyway?", QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::NoButton);
|
|
||||||
if (Button != QMessageBox::Ok) return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CGameExporter Exporter(TO_TSTRING(GameRoot), TO_TSTRING(ExportDir));
|
|
||||||
Exporter.Export();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CProjectOverviewDialog::SetupWorldsList()
|
void CProjectOverviewDialog::SetupWorldsList()
|
||||||
|
@ -24,6 +24,9 @@ public:
|
|||||||
explicit CProjectOverviewDialog(QWidget *pParent = 0);
|
explicit CProjectOverviewDialog(QWidget *pParent = 0);
|
||||||
~CProjectOverviewDialog();
|
~CProjectOverviewDialog();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void InternalLoadProject(const QString& rkPath);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void OpenProject();
|
void OpenProject();
|
||||||
void ExportGame();
|
void ExportGame();
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include "Editor/ModelEditor/CModelEditorWindow.h"
|
#include "Editor/ModelEditor/CModelEditorWindow.h"
|
||||||
#include "Editor/WorldEditor/CWorldEditor.h"
|
#include "Editor/WorldEditor/CWorldEditor.h"
|
||||||
#include <Core/GameProject/CGameExporter.h>
|
|
||||||
#include <Core/GameProject/CResourceStore.h>
|
#include <Core/GameProject/CResourceStore.h>
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
@ -26,7 +25,6 @@ CStartWindow::CStartWindow(QWidget *parent)
|
|||||||
|
|
||||||
connect(ui->ActionAbout, SIGNAL(triggered()), this, SLOT(About()));
|
connect(ui->ActionAbout, SIGNAL(triggered()), this, SLOT(About()));
|
||||||
connect(ui->ActionCharacterEditor, SIGNAL(triggered()), this, SLOT(LaunchCharacterEditor()));
|
connect(ui->ActionCharacterEditor, SIGNAL(triggered()), this, SLOT(LaunchCharacterEditor()));
|
||||||
connect(ui->ActionExportGame, SIGNAL(triggered()), this, SLOT(ExportGame()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CStartWindow::~CStartWindow()
|
CStartWindow::~CStartWindow()
|
||||||
@ -245,35 +243,3 @@ void CStartWindow::About()
|
|||||||
CAboutDialog Dialog(this);
|
CAboutDialog Dialog(this);
|
||||||
Dialog.exec();
|
Dialog.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStartWindow::ExportGame()
|
|
||||||
{
|
|
||||||
// TEMP - hardcoded names for convenience. will remove later!
|
|
||||||
#define USE_HARDCODED_GAME_ROOT 0
|
|
||||||
#define USE_HARDCODED_EXPORT_DIR 1
|
|
||||||
|
|
||||||
#if USE_HARDCODED_GAME_ROOT
|
|
||||||
QString GameRoot = "E:/Unpacked/Metroid Prime 2";
|
|
||||||
#else
|
|
||||||
QString GameRoot = QFileDialog::getExistingDirectory(this, "Select game root directory");
|
|
||||||
if (GameRoot.isEmpty()) return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if USE_HARDCODED_EXPORT_DIR
|
|
||||||
QString ExportDir = "E:/Unpacked/ExportTest";
|
|
||||||
#else
|
|
||||||
QString ExportDir = QFileDialog::getExistingDirectory(this, "Select output export directory");
|
|
||||||
if (ExportDir.isEmpty()) return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Verify valid game root by checking if opening.bnr exists
|
|
||||||
TString OpeningBNR = TO_TSTRING(GameRoot) + "/opening.bnr";
|
|
||||||
if (!FileUtil::Exists(OpeningBNR.ToUTF16()))
|
|
||||||
{
|
|
||||||
QMessageBox::warning(this, "Error", "Error; this is not a valid game root directory!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CGameExporter Exporter(TO_TSTRING(GameRoot), TO_TSTRING(ExportDir));
|
|
||||||
Exporter.Export();
|
|
||||||
}
|
|
||||||
|
@ -40,7 +40,6 @@ private slots:
|
|||||||
|
|
||||||
void LaunchCharacterEditor();
|
void LaunchCharacterEditor();
|
||||||
void About();
|
void About();
|
||||||
void ExportGame();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void FillWorldUI();
|
void FillWorldUI();
|
||||||
|
@ -35,6 +35,8 @@ CONFIG(debug, debug|release) {
|
|||||||
-L$$EXTERNALS_DIR/assimp/lib/ -lassimp-vc140-mtd \
|
-L$$EXTERNALS_DIR/assimp/lib/ -lassimp-vc140-mtd \
|
||||||
-L$$EXTERNALS_DIR/boost_1_63_0/lib64-msvc-14.0 -llibboost_filesystem-vc140-mt-gd-1_63 \
|
-L$$EXTERNALS_DIR/boost_1_63_0/lib64-msvc-14.0 -llibboost_filesystem-vc140-mt-gd-1_63 \
|
||||||
-L$$EXTERNALS_DIR/lzo-2.09/lib/ -llzo2d \
|
-L$$EXTERNALS_DIR/lzo-2.09/lib/ -llzo2d \
|
||||||
|
-L$$EXTERNALS_DIR/nodtool/build/debug/lib/ -lnod \
|
||||||
|
-L$$EXTERNALS_DIR/nodtool/build/debug/logvisor/ -llogvisor \
|
||||||
-L$$EXTERNALS_DIR/tinyxml2/lib/ -ltinyxml2d \
|
-L$$EXTERNALS_DIR/tinyxml2/lib/ -ltinyxml2d \
|
||||||
-L$$EXTERNALS_DIR/zlib/lib/ -lzlibd
|
-L$$EXTERNALS_DIR/zlib/lib/ -lzlibd
|
||||||
|
|
||||||
@ -62,6 +64,8 @@ CONFIG(release, debug|release) {
|
|||||||
-L$$EXTERNALS_DIR/assimp/lib/ -lassimp-vc140-mt \
|
-L$$EXTERNALS_DIR/assimp/lib/ -lassimp-vc140-mt \
|
||||||
-L$$EXTERNALS_DIR/boost_1_63_0/lib64-msvc-14.0 -llibboost_filesystem-vc140-mt-1_63 \
|
-L$$EXTERNALS_DIR/boost_1_63_0/lib64-msvc-14.0 -llibboost_filesystem-vc140-mt-1_63 \
|
||||||
-L$$EXTERNALS_DIR/lzo-2.09/lib/ -llzo2 \
|
-L$$EXTERNALS_DIR/lzo-2.09/lib/ -llzo2 \
|
||||||
|
-L$$EXTERNALS_DIR/nodtool/build/release/lib/ -lnod \
|
||||||
|
-L$$EXTERNALS_DIR/nodtool/build/release/logvisor -llogvisor \
|
||||||
-L$$EXTERNALS_DIR/tinyxml2/lib/ -ltinyxml2 \
|
-L$$EXTERNALS_DIR/tinyxml2/lib/ -ltinyxml2 \
|
||||||
-L$$EXTERNALS_DIR/zlib/lib/ -lzlib
|
-L$$EXTERNALS_DIR/zlib/lib/ -lzlib
|
||||||
|
|
||||||
@ -84,6 +88,8 @@ INCLUDEPATH += $$PWE_MAIN_INCLUDE \
|
|||||||
$$EXTERNALS_DIR/glew-2.0.0/include \
|
$$EXTERNALS_DIR/glew-2.0.0/include \
|
||||||
$$EXTERNALS_DIR/glm/glm \
|
$$EXTERNALS_DIR/glm/glm \
|
||||||
$$EXTERNALS_DIR/lzo-2.09/include \
|
$$EXTERNALS_DIR/lzo-2.09/include \
|
||||||
|
$$EXTERNALS_DIR/nodtool/include \
|
||||||
|
$$EXTERNALS_DIR/nodtool/logvisor/include \
|
||||||
$$EXTERNALS_DIR/tinyxml2/include \
|
$$EXTERNALS_DIR/tinyxml2/include \
|
||||||
$$EXTERNALS_DIR/zlib/include
|
$$EXTERNALS_DIR/zlib/include
|
||||||
|
|
||||||
@ -174,7 +180,8 @@ HEADERS += \
|
|||||||
ResourceBrowser/CVirtualDirectoryModel.h \
|
ResourceBrowser/CVirtualDirectoryModel.h \
|
||||||
CEditorApplication.h \
|
CEditorApplication.h \
|
||||||
IEditor.h \
|
IEditor.h \
|
||||||
Widgets/CResourceSelector.h
|
Widgets/CResourceSelector.h \
|
||||||
|
CExportGameDialog.h
|
||||||
|
|
||||||
# Source Files
|
# Source Files
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
@ -238,7 +245,8 @@ SOURCES += \
|
|||||||
CProjectOverviewDialog.cpp \
|
CProjectOverviewDialog.cpp \
|
||||||
ResourceBrowser/CResourceBrowser.cpp \
|
ResourceBrowser/CResourceBrowser.cpp \
|
||||||
CEditorApplication.cpp \
|
CEditorApplication.cpp \
|
||||||
Widgets/CResourceSelector.cpp
|
Widgets/CResourceSelector.cpp \
|
||||||
|
CExportGameDialog.cpp
|
||||||
|
|
||||||
# UI Files
|
# UI Files
|
||||||
FORMS += \
|
FORMS += \
|
||||||
@ -262,4 +270,5 @@ FORMS += \
|
|||||||
CharacterEditor/CCharacterEditor.ui \
|
CharacterEditor/CCharacterEditor.ui \
|
||||||
WorldEditor/CCollisionRenderSettingsDialog.ui \
|
WorldEditor/CCollisionRenderSettingsDialog.ui \
|
||||||
CProjectOverviewDialog.ui \
|
CProjectOverviewDialog.ui \
|
||||||
ResourceBrowser/CResourceBrowser.ui
|
ResourceBrowser/CResourceBrowser.ui \
|
||||||
|
CExportGameDialog.ui
|
||||||
|
@ -292,7 +292,7 @@ void CResourceBrowser::OnResourceSelectionChanged(const QModelIndex& rkNewIndex,
|
|||||||
|
|
||||||
void CResourceBrowser::OnImportPakContentsTxt()
|
void CResourceBrowser::OnImportPakContentsTxt()
|
||||||
{
|
{
|
||||||
QStringList PathList = QFileDialog::getOpenFileNames(this, "Open pak contents list", "", "*.pak.contents.txt");
|
QStringList PathList = UICommon::OpenFilesDialog(this, "Open pak contents list", "*.pak.contents.txt");
|
||||||
if (PathList.isEmpty()) return;
|
if (PathList.isEmpty()) return;
|
||||||
|
|
||||||
foreach(const QString& rkPath, PathList)
|
foreach(const QString& rkPath, PathList)
|
||||||
@ -326,7 +326,7 @@ void CResourceBrowser::OnImportNamesFromAssetNameMap()
|
|||||||
|
|
||||||
void CResourceBrowser::ExportAssetNames()
|
void CResourceBrowser::ExportAssetNames()
|
||||||
{
|
{
|
||||||
QString OutFile = QFileDialog::getSaveFileName(this, "Export asset name map", "../resources/gameinfo/", "*.xml");
|
QString OutFile = UICommon::SaveFileDialog(this, "Export asset name map", "*.xml", "../resources/gameinfo/");
|
||||||
if (OutFile.isEmpty()) return;
|
if (OutFile.isEmpty()) return;
|
||||||
|
|
||||||
CAssetNameMap NameMap;
|
CAssetNameMap NameMap;
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
#ifndef UICOMMON
|
#ifndef UICOMMON
|
||||||
#define UICOMMON
|
#define UICOMMON
|
||||||
|
|
||||||
|
#include "CEditorApplication.h"
|
||||||
#include <Common/TString.h>
|
#include <Common/TString.h>
|
||||||
|
#include <QFileDialog>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
#include <QMessageBox>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
// App string variable handling - automatically fill in application name/version
|
// App string variable handling - automatically fill in application name/version
|
||||||
@ -57,7 +60,54 @@ inline TWideString ToTWideString(const QString& rkStr)
|
|||||||
{
|
{
|
||||||
return TWideString(rkStr.toStdWString());
|
return TWideString(rkStr.toStdWString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QFileDialog wrappers
|
||||||
|
// Note: pause editor ticks while file dialogs are open because otherwise there's a bug that makes it really difficult to tab out and back in
|
||||||
|
#define PUSH_TICKS_ENABLED \
|
||||||
|
bool TicksEnabled = gpEdApp->AreEditorTicksEnabled(); \
|
||||||
|
gpEdApp->SetEditorTicksEnabled(false);
|
||||||
|
#define POP_TICKS_ENABLED \
|
||||||
|
gpEdApp->SetEditorTicksEnabled(TicksEnabled);
|
||||||
|
|
||||||
|
inline QString OpenFileDialog(QWidget *pParent, const QString& rkCaption, const QString& rkFilter, const QString& rkStartingDir = "")
|
||||||
|
{
|
||||||
|
PUSH_TICKS_ENABLED;
|
||||||
|
QString Result = QFileDialog::getOpenFileName(pParent, rkCaption, rkStartingDir, rkFilter);
|
||||||
|
POP_TICKS_ENABLED;
|
||||||
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline QStringList OpenFilesDialog(QWidget *pParent, const QString& rkCaption, const QString& rkFilter, const QString& rkStartingDir = "")
|
||||||
|
{
|
||||||
|
PUSH_TICKS_ENABLED;
|
||||||
|
QStringList Result = QFileDialog::getOpenFileNames(pParent, rkCaption, rkStartingDir, rkFilter);
|
||||||
|
POP_TICKS_ENABLED;
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QString SaveFileDialog(QWidget *pParent, const QString& rkCaption, const QString& rkFilter, const QString& rkStartingDir = "")
|
||||||
|
{
|
||||||
|
PUSH_TICKS_ENABLED;
|
||||||
|
QString Result = QFileDialog::getSaveFileName(pParent, rkCaption, rkStartingDir, rkFilter);
|
||||||
|
POP_TICKS_ENABLED;
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QString OpenDirDialog(QWidget *pParent, const QString& rkCaption, const QString& rkStartingDir = "")
|
||||||
|
{
|
||||||
|
PUSH_TICKS_ENABLED;
|
||||||
|
QString Result = QFileDialog::getExistingDirectory(pParent, rkCaption, rkStartingDir);
|
||||||
|
POP_TICKS_ENABLED;
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// QMessageBox wrappers
|
||||||
|
inline void ErrorMsg(QWidget *pParent, QString ErrorText)
|
||||||
|
{
|
||||||
|
QMessageBox::warning(pParent, "Error", ErrorText);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // UICommon Namespace End
|
||||||
|
|
||||||
#endif // UICOMMON
|
#endif // UICOMMON
|
||||||
|
|
||||||
|
@ -215,7 +215,7 @@ void WResourceSelector::OnBrowseButtonClicked()
|
|||||||
Filter += UICommon::ExtensionFilterString(mSupportedExtensions[iExt]);
|
Filter += UICommon::ExtensionFilterString(mSupportedExtensions[iExt]);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString NewRes = QFileDialog::getOpenFileName(this, "Select resource", "", Filter);
|
QString NewRes = UICommon::OpenFileDialog(this, "Select resource", Filter);
|
||||||
|
|
||||||
if (!NewRes.isEmpty())
|
if (!NewRes.isEmpty())
|
||||||
{
|
{
|
||||||
|
@ -63,7 +63,7 @@ QString CRepackInfoDialog::OutputPak() const
|
|||||||
// ************ PUBLIC SLOTS ************
|
// ************ PUBLIC SLOTS ************
|
||||||
void CRepackInfoDialog::BrowseFolderClicked()
|
void CRepackInfoDialog::BrowseFolderClicked()
|
||||||
{
|
{
|
||||||
QString Folder = QFileDialog::getExistingDirectory(this, "Choose directory");
|
QString Folder = UICommon::OpenDirDialog(this, "Choose directory");
|
||||||
|
|
||||||
if (!Folder.isEmpty())
|
if (!Folder.isEmpty())
|
||||||
{
|
{
|
||||||
@ -74,7 +74,7 @@ void CRepackInfoDialog::BrowseFolderClicked()
|
|||||||
|
|
||||||
void CRepackInfoDialog::BrowseListClicked()
|
void CRepackInfoDialog::BrowseListClicked()
|
||||||
{
|
{
|
||||||
QString List = QFileDialog::getOpenFileName(this, "Open list file", "", "All supported files (*.txt *.pak);;Text file (*.txt);;Pak file (*.pak)");
|
QString List = UICommon::OpenFileDialog(this, "Open list file", "All supported files (*.txt *.pak);;Text file (*.txt);;Pak file (*.pak)");
|
||||||
|
|
||||||
if (!List.isEmpty())
|
if (!List.isEmpty())
|
||||||
{
|
{
|
||||||
@ -94,7 +94,7 @@ void CRepackInfoDialog::BrowseListClicked()
|
|||||||
|
|
||||||
void CRepackInfoDialog::BrowseOutPakClicked()
|
void CRepackInfoDialog::BrowseOutPakClicked()
|
||||||
{
|
{
|
||||||
QString Pak = QFileDialog::getSaveFileName(this, "Save pak", "", "Pak File (*.pak)");
|
QString Pak = UICommon::SaveFileDialog(this, "Save pak", "Pak File (*.pak)");
|
||||||
|
|
||||||
if (!Pak.isEmpty())
|
if (!Pak.isEmpty())
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user