Added support for saving/loading package definitions

This commit is contained in:
parax0 2016-07-05 01:45:42 -06:00
parent 24c5ad5cd7
commit f55b3666a0
5 changed files with 215 additions and 22 deletions

View File

@ -285,4 +285,5 @@ SOURCES += \
GameProject/CGameExporter.cpp \
GameProject/CResourceStore.cpp \
GameProject/CVirtualDirectory.cpp \
GameProject/CResourceEntry.cpp
GameProject/CResourceEntry.cpp \
GameProject/CPackage.cpp

View File

@ -10,8 +10,9 @@
#define COPY_DISC_DATA 0
#define LOAD_PAKS 1
#define EXPORT_WORLDS 1
#define EXPORT_COOKED 1
#define SAVE_PACKAGE_DEFINITIONS 1
#define EXPORT_WORLDS 0
#define EXPORT_COOKED 0
CGameExporter::CGameExporter(const TString& rkInputDir, const TString& rkOutputDir)
: mStore(this)
@ -186,7 +187,8 @@ void CGameExporter::LoadPaks()
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
if (Game() < eCorruption)
@ -207,7 +209,7 @@ void CGameExporter::LoadPaks()
CUniqueID ResID(Pak, IDLength);
u32 NameLen = Pak.ReadLong();
TString Name = Pak.ReadString(NameLen);
pPackage->AddNamedResource(Name, ResID, ResType);
pCollection->AddResource(Name, ResID, ResType);
}
u32 NumResources = Pak.ReadLong();
@ -265,7 +267,7 @@ void CGameExporter::LoadPaks()
TString Name = Pak.ReadString();
CFourCC ResType = Pak.ReadLong();
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);
#if SAVE_PACKAGE_DEFINITIONS
pPackage->Save();
#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\.
// 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);
if (!GameWorldsDir.IsEmpty())
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"))
{
@ -424,7 +432,7 @@ void CGameExporter::ExportWorlds()
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;
}

View File

@ -33,6 +33,7 @@ public:
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 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
inline void SetGame(EGame Game) { mGame = Game; }

View File

@ -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);
}

View File

@ -5,6 +5,8 @@
#include <Common/CUniqueID.h>
#include <Common/TString.h>
class CGameProject;
struct SNamedResource
{
TString Name;
@ -12,29 +14,66 @@ struct SNamedResource
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
{
CGameProject *mpProject;
TString mPakName;
TWideString mPakPath;
std::vector<SNamedResource> mNamedResources;
std::vector<CUniqueID> mPakResources;
std::vector<CResourceCollection*> mCollections;
enum EPackageDefinitionVersion
{
eVer_Initial,
eVer_Max,
eVer_Current = eVer_Max - 1
};
public:
CPackage() {}
CPackage(const TString& rkName, const TWideString& rkPath)
: mPakName(rkName)
CPackage(CGameProject *pProj, const TString& rkName, const TWideString& rkPath)
: mpProject(pProj)
, mPakName(rkName)
, mPakPath(rkPath)
{}
inline TString PakName() const { return mPakName; }
inline TWideString PakPath() const { return mPakPath; }
inline u32 NumNamedResources() const { return mNamedResources.size(); }
inline const SNamedResource& NamedResourceByIndex(u32 Index) const { return mNamedResources[Index]; }
void Load();
void Save();
TWideString DefinitionPath(bool Relative) const;
TWideString CookedPackagePath(bool Relative) const;
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; }
inline void AddNamedResource(TString Name, const CUniqueID& rkID, const CFourCC& rkType) { mNamedResources.push_back( SNamedResource { Name, rkID, rkType } ); }
inline void AddPakResource(const CUniqueID& rkID) { mPakResources.push_back(rkID); }
};
#endif // CPACKAGE