Added support for saving/loading package definitions
This commit is contained in:
parent
24c5ad5cd7
commit
f55b3666a0
|
@ -285,4 +285,5 @@ SOURCES += \
|
||||||
GameProject/CGameExporter.cpp \
|
GameProject/CGameExporter.cpp \
|
||||||
GameProject/CResourceStore.cpp \
|
GameProject/CResourceStore.cpp \
|
||||||
GameProject/CVirtualDirectory.cpp \
|
GameProject/CVirtualDirectory.cpp \
|
||||||
GameProject/CResourceEntry.cpp
|
GameProject/CResourceEntry.cpp \
|
||||||
|
GameProject/CPackage.cpp
|
||||||
|
|
|
@ -10,8 +10,9 @@
|
||||||
|
|
||||||
#define COPY_DISC_DATA 0
|
#define COPY_DISC_DATA 0
|
||||||
#define LOAD_PAKS 1
|
#define LOAD_PAKS 1
|
||||||
#define EXPORT_WORLDS 1
|
#define SAVE_PACKAGE_DEFINITIONS 1
|
||||||
#define EXPORT_COOKED 1
|
#define EXPORT_WORLDS 0
|
||||||
|
#define EXPORT_COOKED 0
|
||||||
|
|
||||||
CGameExporter::CGameExporter(const TString& rkInputDir, const TString& rkOutputDir)
|
CGameExporter::CGameExporter(const TString& rkInputDir, const TString& rkOutputDir)
|
||||||
: mStore(this)
|
: mStore(this)
|
||||||
|
@ -186,7 +187,8 @@ void CGameExporter::LoadPaks()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
CPackage *pPackage = new CPackage(CharPak.GetFileName(false), FileUtil::MakeRelative(PakPath.GetFileDirectory(), mGameDir));
|
CPackage *pPackage = new CPackage(mpProject, CharPak.GetFileName(false), FileUtil::MakeRelative(PakPath.GetFileDirectory(), mGameDir));
|
||||||
|
CResourceCollection *pCollection = pPackage->AddCollection("Default");
|
||||||
|
|
||||||
// MP1-MP3Proto
|
// MP1-MP3Proto
|
||||||
if (Game() < eCorruption)
|
if (Game() < eCorruption)
|
||||||
|
@ -207,7 +209,7 @@ void CGameExporter::LoadPaks()
|
||||||
CUniqueID ResID(Pak, IDLength);
|
CUniqueID ResID(Pak, IDLength);
|
||||||
u32 NameLen = Pak.ReadLong();
|
u32 NameLen = Pak.ReadLong();
|
||||||
TString Name = Pak.ReadString(NameLen);
|
TString Name = Pak.ReadString(NameLen);
|
||||||
pPackage->AddNamedResource(Name, ResID, ResType);
|
pCollection->AddResource(Name, ResID, ResType);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 NumResources = Pak.ReadLong();
|
u32 NumResources = Pak.ReadLong();
|
||||||
|
@ -265,7 +267,7 @@ void CGameExporter::LoadPaks()
|
||||||
TString Name = Pak.ReadString();
|
TString Name = Pak.ReadString();
|
||||||
CFourCC ResType = Pak.ReadLong();
|
CFourCC ResType = Pak.ReadLong();
|
||||||
CUniqueID ResID(Pak, IDLength);
|
CUniqueID ResID(Pak, IDLength);
|
||||||
pPackage->AddNamedResource(Name, ResID, ResType);
|
pCollection->AddResource(Name, ResID, ResType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,8 +295,11 @@ void CGameExporter::LoadPaks()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add package to project
|
// Add package to project and save
|
||||||
mpProject->AddPackage(pPackage, IsWorldPak);
|
mpProject->AddPackage(pPackage, IsWorldPak);
|
||||||
|
#if SAVE_PACKAGE_DEFINITIONS
|
||||||
|
pPackage->Save();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -407,15 +412,18 @@ void CGameExporter::ExportWorlds()
|
||||||
|
|
||||||
// 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\.
|
// 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.
|
// Other games have all paks in the game root dir so we're fine just taking the original root dir-relative directory.
|
||||||
TWideString PakPath = pPak->PakPath();
|
TWideString PakPath = pPak->Path();
|
||||||
TWideString GameWorldsDir = PakPath.GetParentDirectoryPath(L"Worlds", false);
|
TWideString GameWorldsDir = PakPath.GetParentDirectoryPath(L"Worlds", false);
|
||||||
|
|
||||||
if (!GameWorldsDir.IsEmpty())
|
if (!GameWorldsDir.IsEmpty())
|
||||||
PakPath = FileUtil::MakeRelative(PakPath, GameWorldsDir);
|
PakPath = FileUtil::MakeRelative(PakPath, GameWorldsDir);
|
||||||
|
|
||||||
for (u32 iRes = 0; iRes < pPak->NumNamedResources(); iRes++)
|
// Note since there's no collections in the cooked data we're guaranteed that every pak will have exactly one collection.
|
||||||
|
CResourceCollection *pCollection = pPak->CollectionByIndex(0);
|
||||||
|
|
||||||
|
for (u32 iRes = 0; iRes < pCollection->NumResources(); iRes++)
|
||||||
{
|
{
|
||||||
const SNamedResource& rkRes = pPak->NamedResourceByIndex(iRes);
|
const SNamedResource& rkRes = pCollection->ResourceByIndex(iRes);
|
||||||
|
|
||||||
if (rkRes.Type == "MLVL" && !rkRes.Name.EndsWith("NODEPEND"))
|
if (rkRes.Type == "MLVL" && !rkRes.Name.EndsWith("NODEPEND"))
|
||||||
{
|
{
|
||||||
|
@ -424,7 +432,7 @@ void CGameExporter::ExportWorlds()
|
||||||
|
|
||||||
if (!pWorld)
|
if (!pWorld)
|
||||||
{
|
{
|
||||||
Log::Error("Couldn't load world " + rkRes.Name + " from package " + pPak->PakName() + "; unable to export");
|
Log::Error("Couldn't load world " + rkRes.Name + " from package " + pPak->Name() + "; unable to export");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ public:
|
||||||
inline TWideString DiscDir(bool Relative) const { return Relative ? L"Disc\\" : mProjectRoot + L"Disc\\"; }
|
inline TWideString DiscDir(bool Relative) const { return Relative ? L"Disc\\" : mProjectRoot + L"Disc\\"; }
|
||||||
inline TWideString ContentDir(bool Relative) const { return Relative ? L"Content\\" : mProjectRoot + L"Content\\"; }
|
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 CookedDir(bool Relative) const { return Relative ? L"Cooked\\" : mProjectRoot + L"Cooked\\"; }
|
||||||
|
inline TWideString PackagesDir(bool Relative) const { return Relative ? L"Packages\\" : mProjectRoot + L"Packages\\"; }
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
inline void SetGame(EGame Game) { mGame = Game; }
|
inline void SetGame(EGame Game) { mGame = Game; }
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
#include "CPackage.h"
|
||||||
|
#include "CGameProject.h"
|
||||||
|
#include <Common/AssertMacro.h>
|
||||||
|
#include <Common/FileUtil.h>
|
||||||
|
#include <tinyxml2.h>
|
||||||
|
|
||||||
|
using namespace tinyxml2;
|
||||||
|
|
||||||
|
void CPackage::Load()
|
||||||
|
{
|
||||||
|
TWideString DefPath = DefinitionPath(false);
|
||||||
|
|
||||||
|
XMLDocument Doc;
|
||||||
|
Doc.LoadFile(*DefPath.ToUTF8());
|
||||||
|
|
||||||
|
if (Doc.Error())
|
||||||
|
{
|
||||||
|
Log::Error("Couldn't open pak definition at path: " + DefPath.ToUTF8());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
XMLElement *pRoot = Doc.FirstChildElement("PackageDefinition");
|
||||||
|
//EPackageDefinitionVersion Version = (EPackageDefinitionVersion) TString(pRoot->Attribute("Version")).ToInt32(10);
|
||||||
|
|
||||||
|
XMLElement *pColElem = pRoot->FirstChildElement("ResourceCollection");
|
||||||
|
|
||||||
|
while (pColElem)
|
||||||
|
{
|
||||||
|
CResourceCollection *pCollection = AddCollection( pColElem->Attribute("Name") );
|
||||||
|
XMLElement *pResElem = pColElem->FirstChildElement("NamedResource");
|
||||||
|
|
||||||
|
while (pResElem)
|
||||||
|
{
|
||||||
|
XMLElement *pNameElem = pResElem->FirstChildElement("Name");
|
||||||
|
XMLElement *pIDElem = pResElem->FirstChildElement("ID");
|
||||||
|
XMLElement *pTypeElem = pResElem->FirstChildElement("Type");
|
||||||
|
|
||||||
|
if (!pIDElem || !pNameElem || !pTypeElem)
|
||||||
|
{
|
||||||
|
TString ElemName = (pNameElem ? (pIDElem ? "Type" : "ID") : "Name");
|
||||||
|
Log::Error("Can't add named resource from pak definition at " + DefPath.ToUTF8() + "; " + ElemName + " element missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CUniqueID ID = CUniqueID::FromString(pIDElem->GetText());
|
||||||
|
TString Name = pNameElem->GetText();
|
||||||
|
CFourCC Type = CFourCC(pTypeElem->GetText());
|
||||||
|
pCollection->AddResource(Name, ID, Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
pResElem = pResElem->NextSiblingElement("NamedResource");
|
||||||
|
}
|
||||||
|
|
||||||
|
pColElem = pColElem->NextSiblingElement("ResourceCollection");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPackage::Save()
|
||||||
|
{
|
||||||
|
TWideString DefPath = DefinitionPath(false);
|
||||||
|
FileUtil::CreateDirectory(DefPath.GetFileDirectory());
|
||||||
|
|
||||||
|
// Write XML
|
||||||
|
XMLDocument Doc;
|
||||||
|
|
||||||
|
XMLDeclaration *pDecl = Doc.NewDeclaration();
|
||||||
|
Doc.LinkEndChild(pDecl);
|
||||||
|
|
||||||
|
XMLElement *pRoot = Doc.NewElement("PackageDefinition");
|
||||||
|
pRoot->SetAttribute("Version", eVer_Current);
|
||||||
|
Doc.LinkEndChild(pRoot);
|
||||||
|
|
||||||
|
for (u32 iCol = 0; iCol < mCollections.size(); iCol++)
|
||||||
|
{
|
||||||
|
CResourceCollection *pCollection = mCollections[iCol];
|
||||||
|
|
||||||
|
XMLElement *pColElem = Doc.NewElement("ResourceCollection");
|
||||||
|
pColElem->SetAttribute("Name", *pCollection->Name());
|
||||||
|
pRoot->LinkEndChild(pColElem);
|
||||||
|
|
||||||
|
for (u32 iRes = 0; iRes < pCollection->NumResources(); iRes++)
|
||||||
|
{
|
||||||
|
const SNamedResource& rkRes = pCollection->ResourceByIndex(iRes);
|
||||||
|
|
||||||
|
XMLElement *pResElem = Doc.NewElement("NamedResource");
|
||||||
|
pColElem->LinkEndChild(pResElem);
|
||||||
|
|
||||||
|
XMLElement *pName = Doc.NewElement("Name");
|
||||||
|
pName->SetText(*rkRes.Name);
|
||||||
|
pResElem->LinkEndChild(pName);
|
||||||
|
|
||||||
|
XMLElement *pID = Doc.NewElement("ID");
|
||||||
|
pID->SetText(*rkRes.ID.ToString());
|
||||||
|
pResElem->LinkEndChild(pID);
|
||||||
|
|
||||||
|
XMLElement *pType = Doc.NewElement("Type");
|
||||||
|
pType->SetText(*rkRes.Type.ToString());
|
||||||
|
pResElem->LinkEndChild(pType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XMLError Error = Doc.SaveFile(*DefPath.ToUTF8());
|
||||||
|
|
||||||
|
if (Error != XML_SUCCESS)
|
||||||
|
Log::Error("Failed to save pak definition at path: " + DefPath.ToUTF8());
|
||||||
|
}
|
||||||
|
|
||||||
|
TWideString CPackage::DefinitionPath(bool Relative) const
|
||||||
|
{
|
||||||
|
return mpProject->PackagesDir(Relative) + mPakPath + mPakName.ToUTF16() + L".pkd";
|
||||||
|
}
|
||||||
|
|
||||||
|
TWideString CPackage::CookedPackagePath(bool Relative) const
|
||||||
|
{
|
||||||
|
return mpProject->DiscDir(Relative) + mPakPath + mPakName.ToUTF16() + L".pak";
|
||||||
|
}
|
||||||
|
|
||||||
|
CResourceCollection* CPackage::AddCollection(const TString& rkName)
|
||||||
|
{
|
||||||
|
CResourceCollection *pCollection = new CResourceCollection(rkName);
|
||||||
|
mCollections.push_back(pCollection);
|
||||||
|
return pCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPackage::RemoveCollection(CResourceCollection *pCollection)
|
||||||
|
{
|
||||||
|
for (u32 iCol = 0; iCol < mCollections.size(); iCol++)
|
||||||
|
{
|
||||||
|
if (mCollections[iCol] == pCollection)
|
||||||
|
{
|
||||||
|
RemoveCollection(iCol);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPackage::RemoveCollection(u32 Index)
|
||||||
|
{
|
||||||
|
ASSERT(Index < mCollections.size());
|
||||||
|
auto Iter = mCollections.begin() + Index;
|
||||||
|
delete *Iter;
|
||||||
|
mCollections.erase(Iter);
|
||||||
|
}
|
|
@ -5,6 +5,8 @@
|
||||||
#include <Common/CUniqueID.h>
|
#include <Common/CUniqueID.h>
|
||||||
#include <Common/TString.h>
|
#include <Common/TString.h>
|
||||||
|
|
||||||
|
class CGameProject;
|
||||||
|
|
||||||
struct SNamedResource
|
struct SNamedResource
|
||||||
{
|
{
|
||||||
TString Name;
|
TString Name;
|
||||||
|
@ -12,29 +14,66 @@ struct SNamedResource
|
||||||
CFourCC Type;
|
CFourCC Type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CResourceCollection
|
||||||
|
{
|
||||||
|
TString mName;
|
||||||
|
std::vector<SNamedResource> mNamedResources;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CResourceCollection() : mName("UNNAMED") {}
|
||||||
|
CResourceCollection(const TString& rkName) : mName(rkName) {}
|
||||||
|
|
||||||
|
inline TString Name() const { return mName; }
|
||||||
|
inline u32 NumResources() const { return mNamedResources.size(); }
|
||||||
|
inline const SNamedResource& ResourceByIndex(u32 Idx) const { return mNamedResources[Idx]; }
|
||||||
|
|
||||||
|
inline void AddResource(const TString& rkName, const CUniqueID& rkID, const CFourCC& rkType)
|
||||||
|
{
|
||||||
|
mNamedResources.push_back( SNamedResource { rkName, rkID, rkType } );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class CPackage
|
class CPackage
|
||||||
{
|
{
|
||||||
|
CGameProject *mpProject;
|
||||||
TString mPakName;
|
TString mPakName;
|
||||||
TWideString mPakPath;
|
TWideString mPakPath;
|
||||||
std::vector<SNamedResource> mNamedResources;
|
std::vector<CResourceCollection*> mCollections;
|
||||||
std::vector<CUniqueID> mPakResources;
|
|
||||||
|
enum EPackageDefinitionVersion
|
||||||
|
{
|
||||||
|
eVer_Initial,
|
||||||
|
|
||||||
|
eVer_Max,
|
||||||
|
eVer_Current = eVer_Max - 1
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CPackage() {}
|
CPackage() {}
|
||||||
|
|
||||||
CPackage(const TString& rkName, const TWideString& rkPath)
|
CPackage(CGameProject *pProj, const TString& rkName, const TWideString& rkPath)
|
||||||
: mPakName(rkName)
|
: mpProject(pProj)
|
||||||
|
, mPakName(rkName)
|
||||||
, mPakPath(rkPath)
|
, mPakPath(rkPath)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
inline TString PakName() const { return mPakName; }
|
void Load();
|
||||||
inline TWideString PakPath() const { return mPakPath; }
|
void Save();
|
||||||
inline u32 NumNamedResources() const { return mNamedResources.size(); }
|
|
||||||
inline const SNamedResource& NamedResourceByIndex(u32 Index) const { return mNamedResources[Index]; }
|
|
||||||
|
|
||||||
inline void SetPakName(TString NewName) { mPakName = NewName; }
|
TWideString DefinitionPath(bool Relative) const;
|
||||||
inline void AddNamedResource(TString Name, const CUniqueID& rkID, const CFourCC& rkType) { mNamedResources.push_back( SNamedResource { Name, rkID, rkType } ); }
|
TWideString CookedPackagePath(bool Relative) const;
|
||||||
inline void AddPakResource(const CUniqueID& rkID) { mPakResources.push_back(rkID); }
|
|
||||||
|
CResourceCollection* AddCollection(const TString& rkName);
|
||||||
|
void RemoveCollection(CResourceCollection *pCollection);
|
||||||
|
void RemoveCollection(u32 Index);
|
||||||
|
|
||||||
|
// Accessors
|
||||||
|
inline TString Name() const { return mPakName; }
|
||||||
|
inline TWideString Path() const { return mPakPath; }
|
||||||
|
inline u32 NumCollections() const { return mCollections.size(); }
|
||||||
|
inline CResourceCollection* CollectionByIndex(u32 Idx) const { return mCollections[Idx]; }
|
||||||
|
|
||||||
|
inline void SetPakName(TString NewName) { mPakName = NewName; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CPACKAGE
|
#endif // CPACKAGE
|
||||||
|
|
Loading…
Reference in New Issue