Added support for saving/loading game projects
This commit is contained in:
parent
f55b3666a0
commit
12bd4eff90
|
@ -1,6 +1,7 @@
|
|||
#include "CGameExporter.h"
|
||||
#include "Core/GameProject/CResourceStore.h"
|
||||
#include "Core/Resource/CWorld.h"
|
||||
#include "Core/Resource/Script/CMasterTemplate.h"
|
||||
#include <FileIO/FileIO.h>
|
||||
#include <Common/AssertMacro.h>
|
||||
#include <Common/CompressionUtil.h>
|
||||
|
@ -8,11 +9,11 @@
|
|||
#include <Common/FileUtil.h>
|
||||
#include <tinyxml2.h>
|
||||
|
||||
#define COPY_DISC_DATA 0
|
||||
#define COPY_DISC_DATA 1
|
||||
#define LOAD_PAKS 1
|
||||
#define SAVE_PACKAGE_DEFINITIONS 1
|
||||
#define EXPORT_WORLDS 0
|
||||
#define EXPORT_COOKED 0
|
||||
#define EXPORT_WORLDS 1
|
||||
#define EXPORT_COOKED 1
|
||||
|
||||
CGameExporter::CGameExporter(const TString& rkInputDir, const TString& rkOutputDir)
|
||||
: mStore(this)
|
||||
|
@ -91,12 +92,7 @@ void CGameExporter::CopyDiscData()
|
|||
|
||||
// Detect paks
|
||||
if (FullPath.GetFileExtension().ToLower() == L"pak")
|
||||
{
|
||||
if (FullPath.GetFileName(false).StartsWith(L"Metroid", false) || RelPath.Contains(L"Worlds", false))
|
||||
mWorldPaks.push_back(FullPath);
|
||||
else
|
||||
mResourcePaks.push_back(FullPath);
|
||||
}
|
||||
mPaks.push_back(FullPath);
|
||||
|
||||
#if COPY_DISC_DATA
|
||||
// Create directory
|
||||
|
@ -110,6 +106,8 @@ void CGameExporter::CopyDiscData()
|
|||
}
|
||||
|
||||
ASSERT(Game() != eUnknownVersion);
|
||||
mpProject->SetGame(Game());
|
||||
mpProject->SetProjectName(CMasterTemplate::FindGameName(Game()));
|
||||
}
|
||||
|
||||
void CGameExporter::LoadAssetList()
|
||||
|
@ -168,139 +166,133 @@ void CGameExporter::LoadPaks()
|
|||
{
|
||||
#if LOAD_PAKS
|
||||
SCOPED_TIMER(LoadPaks);
|
||||
EUIDLength IDLength = (Game() < eCorruptionProto ? e32Bit : e64Bit);
|
||||
|
||||
for (u32 iList = 0; iList < 2; iList++)
|
||||
for (auto It = mPaks.begin(); It != mPaks.end(); It++)
|
||||
{
|
||||
const TWideStringList& rkList = (iList == 0 ? mWorldPaks : mResourcePaks);
|
||||
bool IsWorldPak = (iList == 0);
|
||||
EUIDLength IDLength = (Game() < eCorruptionProto ? e32Bit : e64Bit);
|
||||
TWideString PakPath = *It;
|
||||
TString CharPak = PakPath.ToUTF8();
|
||||
CFileInStream Pak(CharPak.ToStdString(), IOUtil::eBigEndian);
|
||||
|
||||
for (auto It = rkList.begin(); It != rkList.end(); It++)
|
||||
if (!Pak.IsValid())
|
||||
{
|
||||
TWideString PakPath = *It;
|
||||
TString CharPak = PakPath.ToUTF8();
|
||||
CFileInStream Pak(CharPak.ToStdString(), IOUtil::eBigEndian);
|
||||
Log::Error("Couldn't open pak: " + CharPak);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Pak.IsValid())
|
||||
CPackage *pPackage = new CPackage(mpProject, CharPak.GetFileName(false), FileUtil::MakeRelative(PakPath.GetFileDirectory(), mGameDir));
|
||||
CResourceCollection *pCollection = pPackage->AddCollection("Default");
|
||||
|
||||
// MP1-MP3Proto
|
||||
if (Game() < eCorruption)
|
||||
{
|
||||
u32 PakVersion = Pak.ReadLong();
|
||||
Pak.Seek(0x4, SEEK_CUR);
|
||||
ASSERT(PakVersion == 0x00030005);
|
||||
|
||||
// Echoes demo disc has a pak that ends right here.
|
||||
if (!Pak.EoF())
|
||||
{
|
||||
Log::Error("Couldn't open pak: " + CharPak);
|
||||
continue;
|
||||
u32 NumNamedResources = Pak.ReadLong();
|
||||
ASSERT(NumNamedResources > 0);
|
||||
|
||||
for (u32 iName = 0; iName < NumNamedResources; iName++)
|
||||
{
|
||||
CFourCC ResType = Pak.ReadLong();
|
||||
CUniqueID ResID(Pak, IDLength);
|
||||
u32 NameLen = Pak.ReadLong();
|
||||
TString Name = Pak.ReadString(NameLen);
|
||||
pCollection->AddResource(Name, ResID, ResType);
|
||||
}
|
||||
|
||||
u32 NumResources = Pak.ReadLong();
|
||||
|
||||
for (u32 iRes = 0; iRes < NumResources; iRes++)
|
||||
{
|
||||
bool Compressed = (Pak.ReadLong() == 1);
|
||||
CFourCC ResType = Pak.ReadLong();
|
||||
CUniqueID ResID(Pak, IDLength);
|
||||
u32 ResSize = Pak.ReadLong();
|
||||
u32 ResOffset = Pak.ReadLong();
|
||||
|
||||
u64 IntegralID = ResID.ToLongLong();
|
||||
if (mResourceMap.find(IntegralID) == mResourceMap.end())
|
||||
mResourceMap[IntegralID] = SResourceInstance { PakPath, ResID, ResType, ResOffset, ResSize, Compressed, false };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CPackage *pPackage = new CPackage(mpProject, CharPak.GetFileName(false), FileUtil::MakeRelative(PakPath.GetFileDirectory(), mGameDir));
|
||||
CResourceCollection *pCollection = pPackage->AddCollection("Default");
|
||||
// MP3 + DKCR
|
||||
else
|
||||
{
|
||||
u32 PakVersion = Pak.ReadLong();
|
||||
u32 PakHeaderLen = Pak.ReadLong();
|
||||
Pak.Seek(PakHeaderLen - 0x8, SEEK_CUR);
|
||||
ASSERT(PakVersion == 2);
|
||||
|
||||
// MP1-MP3Proto
|
||||
if (Game() < eCorruption)
|
||||
struct SPakSection {
|
||||
CFourCC Type; u32 Size;
|
||||
};
|
||||
std::vector<SPakSection> PakSections;
|
||||
|
||||
u32 NumPakSections = Pak.ReadLong();
|
||||
ASSERT(NumPakSections == 3);
|
||||
|
||||
for (u32 iSec = 0; iSec < NumPakSections; iSec++)
|
||||
{
|
||||
u32 PakVersion = Pak.ReadLong();
|
||||
Pak.Seek(0x4, SEEK_CUR);
|
||||
ASSERT(PakVersion == 0x00030005);
|
||||
CFourCC Type = Pak.ReadLong();
|
||||
u32 Size = Pak.ReadLong();
|
||||
PakSections.push_back(SPakSection { Type, Size });
|
||||
}
|
||||
Pak.SeekToBoundary(64);
|
||||
|
||||
// Echoes demo disc has a pak that ends right here.
|
||||
if (!Pak.EoF())
|
||||
for (u32 iSec = 0; iSec < NumPakSections; iSec++)
|
||||
{
|
||||
u32 Next = Pak.Tell() + PakSections[iSec].Size;
|
||||
|
||||
// Named Resources
|
||||
if (PakSections[iSec].Type == "STRG")
|
||||
{
|
||||
u32 NumNamedResources = Pak.ReadLong();
|
||||
ASSERT(NumNamedResources > 0);
|
||||
|
||||
for (u32 iName = 0; iName < NumNamedResources; iName++)
|
||||
{
|
||||
TString Name = Pak.ReadString();
|
||||
CFourCC ResType = Pak.ReadLong();
|
||||
CUniqueID ResID(Pak, IDLength);
|
||||
u32 NameLen = Pak.ReadLong();
|
||||
TString Name = Pak.ReadString(NameLen);
|
||||
pCollection->AddResource(Name, ResID, ResType);
|
||||
}
|
||||
}
|
||||
|
||||
else if (PakSections[iSec].Type == "RSHD")
|
||||
{
|
||||
ASSERT(PakSections[iSec + 1].Type == "DATA");
|
||||
u32 DataStart = Next;
|
||||
u32 NumResources = Pak.ReadLong();
|
||||
|
||||
for (u32 iRes = 0; iRes < NumResources; iRes++)
|
||||
{
|
||||
bool Compressed = (Pak.ReadLong() == 1);
|
||||
CFourCC ResType = Pak.ReadLong();
|
||||
CFourCC Type = Pak.ReadLong();
|
||||
CUniqueID ResID(Pak, IDLength);
|
||||
u32 ResSize = Pak.ReadLong();
|
||||
u32 ResOffset = Pak.ReadLong();
|
||||
u32 Size = Pak.ReadLong();
|
||||
u32 Offset = DataStart + Pak.ReadLong();
|
||||
|
||||
u64 IntegralID = ResID.ToLongLong();
|
||||
if (mResourceMap.find(IntegralID) == mResourceMap.end())
|
||||
mResourceMap[IntegralID] = SResourceInstance { PakPath, ResID, ResType, ResOffset, ResSize, Compressed, false };
|
||||
mResourceMap[IntegralID] = SResourceInstance { PakPath, ResID, Type, Offset, Size, Compressed, false };
|
||||
}
|
||||
}
|
||||
|
||||
Pak.Seek(Next, SEEK_SET);
|
||||
}
|
||||
|
||||
// MP3 + DKCR
|
||||
else
|
||||
{
|
||||
u32 PakVersion = Pak.ReadLong();
|
||||
u32 PakHeaderLen = Pak.ReadLong();
|
||||
Pak.Seek(PakHeaderLen - 0x8, SEEK_CUR);
|
||||
ASSERT(PakVersion == 2);
|
||||
|
||||
struct SPakSection {
|
||||
CFourCC Type; u32 Size;
|
||||
};
|
||||
std::vector<SPakSection> PakSections;
|
||||
|
||||
u32 NumPakSections = Pak.ReadLong();
|
||||
ASSERT(NumPakSections == 3);
|
||||
|
||||
for (u32 iSec = 0; iSec < NumPakSections; iSec++)
|
||||
{
|
||||
CFourCC Type = Pak.ReadLong();
|
||||
u32 Size = Pak.ReadLong();
|
||||
PakSections.push_back(SPakSection { Type, Size });
|
||||
}
|
||||
Pak.SeekToBoundary(64);
|
||||
|
||||
for (u32 iSec = 0; iSec < NumPakSections; iSec++)
|
||||
{
|
||||
u32 Next = Pak.Tell() + PakSections[iSec].Size;
|
||||
|
||||
// Named Resources
|
||||
if (PakSections[iSec].Type == "STRG")
|
||||
{
|
||||
u32 NumNamedResources = Pak.ReadLong();
|
||||
|
||||
for (u32 iName = 0; iName < NumNamedResources; iName++)
|
||||
{
|
||||
TString Name = Pak.ReadString();
|
||||
CFourCC ResType = Pak.ReadLong();
|
||||
CUniqueID ResID(Pak, IDLength);
|
||||
pCollection->AddResource(Name, ResID, ResType);
|
||||
}
|
||||
}
|
||||
|
||||
else if (PakSections[iSec].Type == "RSHD")
|
||||
{
|
||||
ASSERT(PakSections[iSec + 1].Type == "DATA");
|
||||
u32 DataStart = Next;
|
||||
u32 NumResources = Pak.ReadLong();
|
||||
|
||||
for (u32 iRes = 0; iRes < NumResources; iRes++)
|
||||
{
|
||||
bool Compressed = (Pak.ReadLong() == 1);
|
||||
CFourCC Type = Pak.ReadLong();
|
||||
CUniqueID ResID(Pak, IDLength);
|
||||
u32 Size = Pak.ReadLong();
|
||||
u32 Offset = DataStart + Pak.ReadLong();
|
||||
|
||||
u64 IntegralID = ResID.ToLongLong();
|
||||
if (mResourceMap.find(IntegralID) == mResourceMap.end())
|
||||
mResourceMap[IntegralID] = SResourceInstance { PakPath, ResID, Type, Offset, Size, Compressed, false };
|
||||
}
|
||||
}
|
||||
|
||||
Pak.Seek(Next, SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
// Add package to project and save
|
||||
mpProject->AddPackage(pPackage, IsWorldPak);
|
||||
#if SAVE_PACKAGE_DEFINITIONS
|
||||
pPackage->Save();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Add package to project and save
|
||||
mpProject->AddPackage(pPackage);
|
||||
#if SAVE_PACKAGE_DEFINITIONS
|
||||
pPackage->Save();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -406,9 +398,9 @@ void CGameExporter::ExportWorlds()
|
|||
#if EXPORT_WORLDS
|
||||
SCOPED_TIMER(ExportWorlds);
|
||||
|
||||
for (u32 iPak = 0; iPak < mpProject->NumWorldPaks(); iPak++)
|
||||
for (u32 iPak = 0; iPak < mpProject->NumPackages(); iPak++)
|
||||
{
|
||||
CPackage *pPak = mpProject->WorldPakByIndex(iPak);
|
||||
CPackage *pPak = mpProject->PackageByIndex(iPak);
|
||||
|
||||
// Get output path. DKCR paks are stored in a Worlds folder so we should get the path relative to that so we don't have Worlds\Worlds\.
|
||||
// Other games have all paks in the game root dir so we're fine just taking the original root dir-relative directory.
|
||||
|
@ -460,20 +452,11 @@ void CGameExporter::ExportWorlds()
|
|||
if (pTable) GameAreaName = pTable->String("ENGL", 0);
|
||||
if (GameAreaName.IsEmpty()) GameAreaName = InternalAreaName;
|
||||
|
||||
// Load area
|
||||
CUniqueID AreaID = pWorld->AreaResourceID(iArea);
|
||||
CGameArea *pArea = (CGameArea*) mStore.LoadResource(AreaID, "MREA");
|
||||
|
||||
if (!pArea)
|
||||
{
|
||||
Log::Error("Unable to export area " + GameAreaName.ToUTF8() + " from world " + rkRes.Name + "; couldn't load area");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Export area
|
||||
TWideString AreaDir = WorldDir + TWideString::FromInt32(iArea, 2, 10) + L"_" + FileUtil::SanitizeName(GameAreaName, true) + L"\\";
|
||||
FileUtil::CreateDirectory(mCookedDir + AreaDir);
|
||||
|
||||
CUniqueID AreaID = pWorld->AreaResourceID(iArea);
|
||||
SResourceInstance *pInst = FindResourceInstance(AreaID);
|
||||
ASSERT(pInst != nullptr);
|
||||
|
||||
|
@ -483,11 +466,6 @@ void CGameExporter::ExportWorlds()
|
|||
|
||||
mStore.DestroyUnreferencedResources();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Log::Error("Unexpected named resource type in world pak: " + rkRes.Type.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -506,11 +484,14 @@ void CGameExporter::ExportCookedResources()
|
|||
ExportResource(rRes);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
{
|
||||
SCOPED_TIMER(SaveResourceDatabase);
|
||||
#if EXPORT_COOKED
|
||||
mStore.SaveResourceDatabase(this->mExportDir.ToUTF8() + "ResourceDatabase.rdb");
|
||||
}
|
||||
#endif
|
||||
mpProject->Save();
|
||||
}
|
||||
}
|
||||
|
||||
void CGameExporter::ExportResource(SResourceInstance& rRes)
|
||||
|
|
|
@ -25,8 +25,7 @@ class CGameExporter
|
|||
TWideString mWorldsDirName;
|
||||
|
||||
// Resources
|
||||
TWideStringList mWorldPaks;
|
||||
TWideStringList mResourcePaks;
|
||||
TWideStringList mPaks;
|
||||
|
||||
struct SResourceInstance
|
||||
{
|
||||
|
|
|
@ -1,10 +1,103 @@
|
|||
#include "CGameProject.h"
|
||||
#include "Core/Resource/Script/CMasterTemplate.h"
|
||||
#include <tinyxml2.h>
|
||||
|
||||
void CGameProject::AddPackage(CPackage *pPackage, bool WorldPak)
|
||||
using namespace tinyxml2;
|
||||
|
||||
void CGameProject::Load()
|
||||
{
|
||||
if (WorldPak)
|
||||
mWorldPaks.push_back(pPackage);
|
||||
else
|
||||
mResourcePaks.push_back(pPackage);
|
||||
TString ProjPath = ProjectPath().ToUTF8();
|
||||
XMLDocument Doc;
|
||||
Doc.LoadFile(*ProjPath);
|
||||
|
||||
if (Doc.Error())
|
||||
{
|
||||
Log::Error("Unable to open game project at " + ProjPath);
|
||||
return;
|
||||
}
|
||||
|
||||
XMLElement *pRoot = Doc.FirstChildElement("GameProject");
|
||||
//EProjectVersion Version = (EProjectVersion) TString(pRoot->Attribute("Version")).ToInt32(10);
|
||||
|
||||
// Verify all elements are present
|
||||
XMLElement *pProjName = pRoot->FirstChildElement("Name");
|
||||
XMLElement *pGame = pRoot->FirstChildElement("Game");
|
||||
XMLElement *pResDB = pRoot->FirstChildElement("ResourceDB");
|
||||
XMLElement *pPackages = pRoot->FirstChildElement("Packages");
|
||||
|
||||
if (!pProjName || !pGame || !pResDB || !pPackages)
|
||||
{
|
||||
TString MissingElem = pProjName ? (pGame ? (pResDB ? "Packages" : "ResourceDB") : "Game") : "Name";
|
||||
Log::Error("Unable to load game project at " + ProjPath + "; " + MissingElem + " element is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
mProjectName = pProjName->GetText();
|
||||
mGame = CMasterTemplate::FindGameForName( pGame->GetText() );
|
||||
mResourceDBPath = pResDB->GetText();
|
||||
|
||||
// Load packages
|
||||
XMLElement *pPkgElem = pPackages->FirstChildElement("Package");
|
||||
|
||||
while (pPkgElem)
|
||||
{
|
||||
pPkgElem = pPkgElem->NextSiblingElement("Package");
|
||||
TString Path = pPkgElem->Attribute("Path");
|
||||
|
||||
if (Path.IsEmpty())
|
||||
Log::Error("Failed to load package in game project " + ProjPath + "; Path attribute is missing or empty");
|
||||
|
||||
else
|
||||
{
|
||||
CPackage *pPackage = new CPackage(this, Path.GetFileName(false), TString(Path.GetFileDirectory()).ToUTF16());
|
||||
pPackage->Load();
|
||||
mPackages.push_back(pPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGameProject::Save()
|
||||
{
|
||||
XMLDocument Doc;
|
||||
|
||||
XMLDeclaration *pDecl = Doc.NewDeclaration();
|
||||
Doc.LinkEndChild(pDecl);
|
||||
|
||||
XMLElement *pRoot = Doc.NewElement("GameProject");
|
||||
pRoot->SetAttribute("Version", eVer_Current);
|
||||
Doc.LinkEndChild(pRoot);
|
||||
|
||||
XMLElement *pProjName = Doc.NewElement("Name");
|
||||
pProjName->SetText(*mProjectName);
|
||||
pRoot->LinkEndChild(pProjName);
|
||||
|
||||
XMLElement *pGame = Doc.NewElement("Game");
|
||||
pGame->SetText(*CMasterTemplate::FindGameName(mGame));
|
||||
pRoot->LinkEndChild(pGame);
|
||||
|
||||
XMLElement *pResDB = Doc.NewElement("ResourceDB");
|
||||
pResDB->SetText(*mResourceDBPath.ToUTF8());
|
||||
pRoot->LinkEndChild(pResDB);
|
||||
|
||||
XMLElement *pPackages = Doc.NewElement("Packages");
|
||||
pRoot->LinkEndChild(pPackages);
|
||||
|
||||
for (u32 iPkg = 0; iPkg < mPackages.size(); iPkg++)
|
||||
{
|
||||
CPackage *pPackage = mPackages[iPkg];
|
||||
TWideString FullDefPath = pPackage->DefinitionPath(false);
|
||||
TWideString RelDefPath = FileUtil::MakeRelative(FullDefPath.GetFileDirectory(), PackagesDir(false));
|
||||
TString DefPath = TWideString(RelDefPath + FullDefPath.GetFileName()).ToUTF8();
|
||||
|
||||
XMLElement *pPakElem = Doc.NewElement("Package");
|
||||
pPakElem->SetAttribute("Path", *DefPath);
|
||||
pPackages->LinkEndChild(pPakElem);
|
||||
}
|
||||
|
||||
// Save Project
|
||||
TString ProjPath = ProjectPath().ToUTF8();
|
||||
XMLError Result = Doc.SaveFile(*ProjPath);
|
||||
|
||||
if (Result != XML_SUCCESS)
|
||||
Log::Error("Failed to save game project at: " + ProjPath);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "CPackage.h"
|
||||
#include "CResourceStore.h"
|
||||
#include "Core/Resource/EGame.h"
|
||||
#include <Common/FileUtil.h>
|
||||
#include <Common/CUniqueID.h>
|
||||
#include <Common/TString.h>
|
||||
#include <Common/types.h>
|
||||
|
@ -14,18 +15,25 @@ class CGameProject
|
|||
TString mProjectName;
|
||||
TWideString mProjectRoot;
|
||||
TWideString mResourceDBPath;
|
||||
std::vector<CPackage*> mWorldPaks;
|
||||
std::vector<CPackage*> mResourcePaks;
|
||||
std::vector<CPackage*> mPackages;
|
||||
|
||||
enum EProjectVersion
|
||||
{
|
||||
eVer_Initial,
|
||||
|
||||
eVer_Max,
|
||||
eVer_Current = eVer_Max - 1
|
||||
};
|
||||
public:
|
||||
CGameProject(const TWideString& rkProjRootDir)
|
||||
: mGame(eUnknownVersion)
|
||||
, mProjectName("UnnamedProject")
|
||||
, mProjectName("Unnamed Project")
|
||||
, mProjectRoot(rkProjRootDir)
|
||||
, mResourceDBPath(L"ResourceDB.rdb")
|
||||
{}
|
||||
|
||||
void AddPackage(CPackage *pPackage, bool WorldPak);
|
||||
void Load();
|
||||
void Save();
|
||||
|
||||
// Directory Handling
|
||||
inline TWideString ProjectRoot() const { return mProjectRoot; }
|
||||
|
@ -34,13 +42,15 @@ public:
|
|||
inline TWideString ContentDir(bool Relative) const { return Relative ? L"Content\\" : mProjectRoot + L"Content\\"; }
|
||||
inline TWideString CookedDir(bool Relative) const { return Relative ? L"Cooked\\" : mProjectRoot + L"Cooked\\"; }
|
||||
inline TWideString PackagesDir(bool Relative) const { return Relative ? L"Packages\\" : mProjectRoot + L"Packages\\"; }
|
||||
inline TWideString ProjectPath() const { return mProjectRoot + FileUtil::SanitizeName(mProjectName.ToUTF16(), false) + L".prj"; }
|
||||
|
||||
// Accessors
|
||||
inline void SetGame(EGame Game) { mGame = Game; }
|
||||
inline void SetProjectName(const TString& rkName) { mProjectName = rkName; }
|
||||
|
||||
inline u32 NumWorldPaks() const { return mWorldPaks.size(); }
|
||||
inline CPackage* WorldPakByIndex(u32 Index) const { return mWorldPaks[Index]; }
|
||||
inline u32 NumPackages() const { return mPackages.size(); }
|
||||
inline CPackage* PackageByIndex(u32 Index) const { return mPackages[Index]; }
|
||||
inline void AddPackage(CPackage *pPackage) { mPackages.push_back(pPackage); }
|
||||
|
||||
inline EGame Game() const { return mGame; }
|
||||
};
|
||||
|
|
|
@ -119,6 +119,26 @@ std::list<CMasterTemplate*> CMasterTemplate::MasterList()
|
|||
return list;
|
||||
}
|
||||
|
||||
TString CMasterTemplate::FindGameName(EGame Game)
|
||||
{
|
||||
CMasterTemplate *pMaster = MasterForGame(Game);
|
||||
return pMaster ? pMaster->GameName() : "Unknown Game";
|
||||
}
|
||||
|
||||
EGame CMasterTemplate::FindGameForName(const TString& rkName)
|
||||
{
|
||||
std::list<CMasterTemplate*> Masters = MasterList();
|
||||
|
||||
for (auto It = Masters.begin(); It != Masters.end(); It++)
|
||||
{
|
||||
CMasterTemplate *pMaster = *It;
|
||||
if (pMaster->GameName() == rkName)
|
||||
return pMaster->Game();
|
||||
}
|
||||
|
||||
return eUnknownVersion;
|
||||
}
|
||||
|
||||
TString CMasterTemplate::PropertyName(u32 PropertyID)
|
||||
{
|
||||
auto it = smPropertyNames.find(PropertyID);
|
||||
|
|
|
@ -51,17 +51,20 @@ public:
|
|||
CStructTemplate* StructAtSource(const TString& rkSource);
|
||||
|
||||
// Inline Accessors
|
||||
EGame Game() const { return mGame; }
|
||||
u32 NumGameVersions() const { return mGameVersions.empty() ? 1 : mGameVersions.size(); }
|
||||
u32 NumScriptTemplates() const { return mTemplates.size(); }
|
||||
u32 NumStates() const { return mStates.size(); }
|
||||
u32 NumMessages() const { return mMessages.size(); }
|
||||
bool IsLoadedSuccessfully() { return mFullyLoaded; }
|
||||
TString GetDirectory() const { return mSourceFile.GetFileDirectory(); }
|
||||
inline EGame Game() const { return mGame; }
|
||||
inline TString GameName() const { return mGameName; }
|
||||
inline u32 NumGameVersions() const { return mGameVersions.empty() ? 1 : mGameVersions.size(); }
|
||||
inline u32 NumScriptTemplates() const { return mTemplates.size(); }
|
||||
inline u32 NumStates() const { return mStates.size(); }
|
||||
inline u32 NumMessages() const { return mMessages.size(); }
|
||||
inline bool IsLoadedSuccessfully() { return mFullyLoaded; }
|
||||
inline TString GetDirectory() const { return mSourceFile.GetFileDirectory(); }
|
||||
|
||||
// Static
|
||||
static CMasterTemplate* MasterForGame(EGame Game);
|
||||
static std::list<CMasterTemplate*> MasterList();
|
||||
static TString FindGameName(EGame Game);
|
||||
static EGame FindGameForName(const TString& rkName);
|
||||
static TString PropertyName(u32 PropertyID);
|
||||
static u32 CreatePropertyID(IPropertyTemplate *pTemp);
|
||||
static void AddProperty(IPropertyTemplate *pTemp, const TString& rkTemplateName = "");
|
||||
|
|
|
@ -274,6 +274,14 @@ void CStartWindow::ExportGame()
|
|||
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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue