Initial implementation of resource database (mainly creation and read/write), and added resource registration system with a registrant for every format from every game

This commit is contained in:
parax0 2016-05-31 00:45:30 -06:00
parent 5f2064178c
commit f15aca3f99
16 changed files with 622 additions and 176 deletions

43
src/Common/CScopedTimer.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef CSCOPEDTIMER
#define CSCOPEDTIMER
#include "CTimer.h"
#include "Log.h"
#include "TString.h"
// Runs a timer and automatically stops + prints the time to the log when it goes out of scope.
class CScopedTimer
{
CTimer mTimer;
TString mTimerName;
bool mStopped;
public:
CScopedTimer(const TString& rkTimeoutMessage)
: mTimerName(rkTimeoutMessage)
, mStopped(false)
{
mTimer.Start();
}
~CScopedTimer()
{
Stop();
}
void Stop()
{
if (!mStopped)
{
Log::Write(mTimerName + " finished in " + TString::FromFloat((float) mTimer.Stop()) + "s");
mStopped = true;
}
}
};
#define SCOPED_TIMER(TimerName) \
CScopedTimer TimerName(#TimerName); \
(void) TimerName; // This avoids "unused local variable" compiler warnings
#endif // CSCOPEDTIMER

View File

@ -73,7 +73,8 @@ HEADERS += \
types.h \
Log.h \
FileUtil.h \
AssertMacro.h
AssertMacro.h \
CScopedTimer.h
# Source Files
SOURCES += \

View File

@ -75,6 +75,7 @@ bool DeleteFile(const TWideString& rkFilePath)
bool DeleteDirectory(const TWideString& rkDirPath)
{
// This is an extremely destructive function, be careful using it!
if (!IsDirectory(rkDirPath)) return false;
// Sanity check - don't delete root
@ -94,6 +95,7 @@ bool DeleteDirectory(const TWideString& rkDirPath)
bool ClearDirectory(const TWideString& rkDirPath)
{
// This is an extremely destructive function, be careful using it!
if (!IsDirectory(rkDirPath)) return false;
// Sanity check - don't clear root
@ -126,9 +128,14 @@ bool ClearDirectory(const TWideString& rkDirPath)
return true;
}
int FileSize(const TWideString &rkFilePath)
u64 FileSize(const TWideString &rkFilePath)
{
return (int) (Exists(*rkFilePath) ? file_size(*rkFilePath) : -1);
return (u64) (Exists(*rkFilePath) ? file_size(*rkFilePath) : -1);
}
u64 LastModifiedTime(const TWideString& rkFilePath)
{
return (u64) last_write_time(*rkFilePath);
}
TWideString WorkingDirectory()

View File

@ -17,9 +17,10 @@ bool CopyDirectory(const TWideString& rkOrigPath, const TWideString& rkNewPath);
bool MoveFile(const TWideString& rkOldPath, const TWideString& rkNewPath);
bool MoveDirectory(const TWideString& rkOldPath, const TWideString& rkNewPath);
bool DeleteFile(const TWideString& rkFilePath);
bool DeleteDirectory(const TWideString& rkDirPath);
bool ClearDirectory(const TWideString& rkDirPath);
int FileSize(const TWideString& rkFilePath);
bool DeleteDirectory(const TWideString& rkDirPath); // This is an extremely destructive function, be careful using it!
bool ClearDirectory(const TWideString& rkDirPath); // This is an extremely destructive function, be careful using it!
u64 FileSize(const TWideString& rkFilePath);
u64 LastModifiedTime(const TWideString& rkFilePath);
TWideString WorkingDirectory();
TWideString MakeAbsolute(TWideString Path);
TWideString MakeRelative(const TWideString& rkPath, const TWideString& rkRelativeTo = WorkingDirectory());

View File

@ -2,6 +2,7 @@
#include <FileIO/FileIO.h>
#include <Common/AssertMacro.h>
#include <Common/CompressionUtil.h>
#include <Common/CScopedTimer.h>
#include <Common/FileUtil.h>
#define COPY_DISC_DATA 1
@ -9,20 +10,24 @@
#define EXPORT_COOKED 1
CGameExporter::CGameExporter(const TString& rkInputDir, const TString& rkOutputDir)
: mGameDir( FileUtil::MakeAbsolute(rkInputDir) )
, mExportDir( FileUtil::MakeAbsolute(rkOutputDir) )
, mDiscDir(mExportDir + L"Disc\\")
, mCookedResDir(mExportDir + L"Cooked\\Resources\\")
, mCookedWorldsDir(mExportDir + L"Cooked\\Worlds\\")
, mRawResDir(mExportDir + L"Raw\\Resources\\")
, mRawWorldsDir(mExportDir + L"Raw\\Worlds\\")
, mpProject(new CGameProject)
{
mGameDir = FileUtil::MakeAbsolute(rkInputDir);
mExportDir = FileUtil::MakeAbsolute(rkOutputDir);
mpProject = new CGameProject(mExportDir);
mDiscDir = mpProject->DiscDir();
mResDir = mpProject->ResourcesDir();
mWorldsDir = mpProject->WorldsDir();
mCookedDir = mpProject->CookedDir();
mCookedResDir = mpProject->CookedResourcesDir();
mCookedWorldsDir = mpProject->CookedWorldsDir();
}
bool CGameExporter::Export()
{
SCOPED_TIMER(ExportGame);
FileUtil::CreateDirectory(mExportDir);
FileUtil::ClearDirectory(mExportDir);
CopyDiscData();
LoadPaks();
ExportCookedResources();
@ -33,6 +38,8 @@ bool CGameExporter::Export()
void CGameExporter::CopyDiscData()
{
#if COPY_DISC_DATA
SCOPED_TIMER(CopyDiscData);
// Create Disc output folder
FileUtil::CreateDirectory(mDiscDir);
#endif
@ -64,7 +71,7 @@ void CGameExporter::CopyDiscData()
}
// Detect paks
if (FullPath.GetFileExtension() == L"pak")
if (FullPath.GetFileExtension().ToLower() == L"pak")
{
if (FullPath.GetFileName(false).StartsWith(L"Metroid", false) || RelPath.Contains(L"Worlds", false))
mWorldPaks.push_back(FullPath);
@ -90,6 +97,8 @@ void CGameExporter::CopyDiscData()
void CGameExporter::LoadPaks()
{
#if LOAD_PAKS
SCOPED_TIMER(LoadPaks);
for (u32 iList = 0; iList < 2; iList++)
{
const TWideStringList& rkList = (iList == 0 ? mWorldPaks : mResourcePaks);
@ -117,31 +126,35 @@ void CGameExporter::LoadPaks()
Pak.Seek(0x4, SEEK_CUR);
ASSERT(PakVersion == 0x00030005);
u32 NumNamedResources = Pak.ReadLong();
ASSERT(NumNamedResources > 0);
for (u32 iName = 0; iName < NumNamedResources; iName++)
// Echoes demo disc has a pak that ends right here.
if (!Pak.EoF())
{
Pak.Seek(0x4, SEEK_CUR); // Skip resource type
CUniqueID ResID(Pak, IDLength);
u32 NameLen = Pak.ReadLong();
TString Name = Pak.ReadString(NameLen);
pPackage->AddNamedResource(Name, ResID);
}
u32 NumNamedResources = Pak.ReadLong();
ASSERT(NumNamedResources > 0);
u32 NumResources = Pak.ReadLong();
for (u32 iName = 0; iName < NumNamedResources; iName++)
{
Pak.Seek(0x4, SEEK_CUR); // Skip resource type
CUniqueID ResID(Pak, IDLength);
u32 NameLen = Pak.ReadLong();
TString Name = Pak.ReadString(NameLen);
pPackage->AddNamedResource(Name, ResID);
}
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();
u32 NumResources = Pak.ReadLong();
u64 IntegralID = ResID.ToLongLong();
if (mResourceMap.find(IntegralID) == mResourceMap.end())
mResourceMap[IntegralID] = SResourceInstance { PakPath, ResID, ResType, ResOffset, ResSize, Compressed };
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 };
}
}
}
@ -317,20 +330,32 @@ void CGameExporter::LoadPakResource(const SResourceInstance& rkResource, std::ve
void CGameExporter::ExportCookedResources()
{
#if EXPORT_COOKED
FileUtil::CreateDirectory(mCookedResDir);
for (auto It = mResourceMap.begin(); It != mResourceMap.end(); It++)
CResourceDatabase *pResDB = mpProject->ResourceDatabase();
{
const SResourceInstance& rkRes = It->second;
std::vector<u8> ResourceData;
LoadPakResource(rkRes, ResourceData);
SCOPED_TIMER(ExportCookedResources);
FileUtil::CreateDirectory(mCookedResDir);
TString OutName = rkRes.ResourceID.ToString() + "." + rkRes.ResourceType.ToString();
TString OutPath = mCookedResDir.ToUTF8() + "/" + OutName;
CFileOutStream Out(OutPath.ToStdString(), IOUtil::eBigEndian);
for (auto It = mResourceMap.begin(); It != mResourceMap.end(); It++)
{
const SResourceInstance& rkRes = It->second;
std::vector<u8> ResourceData;
LoadPakResource(rkRes, ResourceData);
if (Out.IsValid())
Out.WriteBytes(ResourceData.data(), ResourceData.size());
TString OutName = rkRes.ResourceID.ToString() + "." + rkRes.ResourceType.ToString();
TString OutDir = mCookedResDir.ToUTF8() + "\\";
TString OutPath = OutDir + OutName;
CFileOutStream Out(OutPath.ToStdString(), IOUtil::eBigEndian);
if (Out.IsValid())
Out.WriteBytes(ResourceData.data(), ResourceData.size());
// Add to resource DB
pResDB->RegisterResource(rkRes.ResourceID, FileUtil::MakeRelative(OutDir, mCookedDir), OutName, CResource::ResTypeForExtension(rkRes.ResourceType));
}
}
{
SCOPED_TIMER(SaveResourceDatabase);
pResDB->Save(this->mExportDir.ToUTF8() + "ResourceDatabase.rdb");
}
#endif
}

View File

@ -17,10 +17,11 @@ class CGameExporter
TWideString mGameDir;
TWideString mExportDir;
TWideString mDiscDir;
TWideString mResDir;
TWideString mWorldsDir;
TWideString mCookedDir;
TWideString mCookedResDir;
TWideString mCookedWorldsDir;
TWideString mRawResDir;
TWideString mRawWorldsDir;
// Resources
TWideStringList mWorldPaks;

View File

@ -12,20 +12,31 @@ class CGameProject
{
EGame mGame;
TString mProjectName;
TString mProjectRoot;
TWideString mProjectRoot;
CResourceDatabase *mpResourceDatabase;
std::vector<CPackage*> mWorldPaks;
std::vector<CPackage*> mResourcePaks;
public:
CGameProject()
CGameProject(const TWideString& rkProjRootDir)
: mGame(eUnknownVersion)
, mProjectName("UnnamedProject")
, mpResourceDatabase(new CResourceDatabase)
, mProjectRoot(rkProjRootDir)
, mpResourceDatabase(new CResourceDatabase(this))
{}
void AddPackage(CPackage *pPackage, bool WorldPak);
// Directory Handling
inline TWideString ProjectRoot() const { return mProjectRoot; }
inline TWideString DiscDir() const { return mProjectRoot + L"Disc\\"; }
inline TWideString ResourcesDir() const { return mProjectRoot + L"Resources\\"; }
inline TWideString WorldsDir() const { return mProjectRoot + L"Worlds\\"; }
inline TWideString CookedDir() const { return mProjectRoot + L"Cooked\\"; }
inline TWideString CookedResourcesDir() const { return CookedDir() + L"Resources\\"; }
inline TWideString CookedWorldsDir() const { return CookedDir() + L"Worlds\\"; }
// Accessors
inline void SetGame(EGame Game) { mGame = Game; }
inline void SetProjectName(const TString& rkName) { mProjectName = rkName; }

View File

@ -1 +1,215 @@
#include "CResourceDatabase.h"
#include "CGameProject.h"
#include "Core/Resource/CResCache.h"
#include <Common/AssertMacro.h>
#include <Common/FileUtil.h>
#include <Common/Log.h>
#include <tinyxml2.h>
using namespace tinyxml2;
// ************ CResourceEntry ************
bool CResourceEntry::HasRawVersion() const
{
return FileUtil::Exists(RawAssetPath());
}
bool CResourceEntry::HasCookedVersion() const
{
return FileUtil::Exists(CookedAssetPath());
}
TString CResourceEntry::RawAssetPath() const
{
TWideString Ext = GetRawExtension(mResourceType, mpDatabase->GameProject()->Game()).ToUTF16();
return mpDatabase->GameProject()->ProjectRoot() + mFileDir + mFileName + L"." + Ext;
}
TString CResourceEntry::CookedAssetPath() const
{
TWideString Ext = GetCookedExtension(mResourceType, mpDatabase->GameProject()->Game()).ToUTF16();
return mpDatabase->GameProject()->CookedDir() + mFileDir + mFileName + L"." + Ext;
}
bool CResourceEntry::NeedsRecook() const
{
// Assets that do not have a raw version can't be recooked since they will always just be saved cooked to begin with.
// We will recook any asset where the raw version has been updated but not recooked yet. mNeedsRecook can also be
// toggled to arbitrarily flag any asset for recook.
if (!HasRawVersion()) return false;
if (!HasCookedVersion()) return true;
if (mNeedsRecook) return true;
return (FileUtil::LastModifiedTime(CookedAssetPath()) < FileUtil::LastModifiedTime(RawAssetPath()));
}
// ************ CResourceDatabase ************
CResourceDatabase::CResourceDatabase(CGameProject *pProj)
: mpProj(pProj)
{}
CResourceDatabase::~CResourceDatabase()
{
}
void CResourceDatabase::Load(const TString& rkPath)
{
XMLDocument Doc;
Doc.LoadFile(*rkPath);
if (!Doc.Error())
{
XMLElement *pRoot = Doc.FirstChildElement("ResourceDatabase");
//EVersion DatabaseVersion = (EVersion) TString(pRoot->Attribute("Version")).ToInt32(10); // Version currently unused
XMLElement *pResources = pRoot->FirstChildElement("Resources");
XMLElement *pRes = pResources->FirstChildElement("Resource");
u32 ResIndex = 0;
while (pRes)
{
XMLElement *pChild = pRes->FirstChildElement();
bool HasID = false, HasType = false, HasDir = false, HasName = false;
CUniqueID ID;
EResType Type;
TWideString FileDir;
TWideString FileName;
while (pChild)
{
TString NodeName = pChild->Name();
if (NodeName == "ID")
{
ID = CUniqueID::FromString(pChild->GetText());
HasID = true;
}
else if (NodeName == "Type")
{
Type = CResource::ResTypeForExtension(pChild->GetText());
HasType = true;
ASSERT(Type != eInvalidResType);
}
else if (NodeName == "FileDir")
{
FileDir = pChild->GetText();
HasDir = true;
}
else if (NodeName == "FileName")
{
FileName = pChild->GetText();
HasName = true;
}
pChild = pChild->NextSiblingElement();
}
if (HasID && HasType && HasDir && HasName)
RegisterResource(ID, FileDir, FileName, Type);
else
Log::Error("Error reading " + rkPath + ": Resource entry " + TString::FromInt32(ResIndex, 0, 10) + " is missing one or more components");
ResIndex++;
pRes = pRes->NextSiblingElement("Resource");
}
}
}
void CResourceDatabase::Save(const TString& rkPath) const
{
XMLDocument Doc;
XMLDeclaration *pDecl = Doc.NewDeclaration();
Doc.LinkEndChild(pDecl);
XMLElement *pRoot = Doc.NewElement("ResourceDatabase");
pRoot->SetAttribute("Version", eVer_Current);
Doc.LinkEndChild(pRoot);
XMLElement *pResources = Doc.NewElement("Resources");
pRoot->LinkEndChild(pResources);
for (auto It = mResourceMap.begin(); It != mResourceMap.end(); It++)
{
CResourceEntry *pEntry = It->second;
XMLElement *pRes = Doc.NewElement("Resource");
pResources->LinkEndChild(pRes);
XMLElement *pID = Doc.NewElement("ID");
pID->SetText(*pEntry->ID().ToString());
pRes->LinkEndChild(pID);
XMLElement *pType = Doc.NewElement("Type");
pType->SetText(*GetCookedExtension(pEntry->ResourceType(), mpProj->Game()));
pRes->LinkEndChild(pType);
XMLElement *pDir = Doc.NewElement("FileDir");
pDir->SetText(*pEntry->FileDirectory());
pRes->LinkEndChild(pDir);
XMLElement *pName = Doc.NewElement("FileName");
pName->SetText(*pEntry->FileName());
pRes->LinkEndChild(pName);
XMLElement *pRecook = Doc.NewElement("NeedsRecook");
pRecook->SetText(pEntry->NeedsRecook() ? "true" : "false");
pRes->LinkEndChild(pRecook);
}
Doc.SaveFile(*rkPath);
}
CResourceEntry* CResourceDatabase::FindResourceEntry(const CUniqueID& rkID) const
{
auto Found = mResourceMap.find(rkID);
if (Found == mResourceMap.end()) return nullptr;
else return Found->second;
}
CResource* CResourceDatabase::LoadResource(const CUniqueID& rkID) const
{
// todo: no handling for raw assets yet
CResourceEntry *pEntry = FindResourceEntry(rkID);
if (pEntry)
{
TString AssetPath = pEntry->CookedAssetPath();
if (FileUtil::Exists(AssetPath))
return gResCache.GetResource(pEntry->CookedAssetPath());
}
return nullptr;
}
bool CResourceDatabase::RegisterResource(const CUniqueID& rkID, const TWideString& rkDir, const TWideString& rkFileName, EResType Type)
{
CResourceEntry *pEntry = FindResourceEntry(rkID);
if (pEntry)
{
Log::Error("Attempted to register resource that's already tracked in the database: " + rkID.ToString() + " / " + rkDir.ToUTF8() + " / " + rkFileName.ToUTF8());
return false;
}
else
{
pEntry = new CResourceEntry(this, rkID, rkDir, rkFileName.GetFileName(false), Type);
if (!pEntry->HasCookedVersion() && !pEntry->HasRawVersion())
{
Log::Error("Attempted to register a resource that doesn't exist: " + rkID.ToString() + " | " + rkDir.ToUTF8() + " | " + rkFileName.ToUTF8());
delete pEntry;
return false;
}
else
{
mResourceMap[rkID] = pEntry;
return true;
}
}
}

View File

@ -1,29 +1,74 @@
#ifndef CRESOURCEDATABASE_H
#define CRESOURCEDATABASE_H
#include "Core/Resource/CResource.h"
#include <Common/CUniqueID.h>
#include <Common/TString.h>
#include <Common/types.h>
#include <map>
class CGameProject;
class CResourceDatabase;
class CResourceEntry
{
CUniqueID ID;
TString DataPath;
CResourceDatabase *mpDatabase;
CUniqueID mID;
TWideString mFileDir;
TWideString mFileName;
EResType mResourceType;
bool mNeedsRecook;
public:
CResourceEntry(CResourceDatabase *pDatabase, const CUniqueID& rkID,
const TWideString& rkDir, const TWideString& rkFilename, EResType Type)
: mpDatabase(pDatabase)
, mID(rkID)
, mFileDir(rkDir)
, mFileName(rkFilename)
, mResourceType(Type)
, mNeedsRecook(false)
{}
bool HasRawVersion() const;
bool HasCookedVersion() const;
TString RawAssetPath() const;
TString CookedAssetPath() const;
bool NeedsRecook() const;
// Accessors
void SetDirty() { mNeedsRecook = true; }
inline CUniqueID ID() const { return mID; }
inline TString FileDirectory() const { return mFileDir; }
inline TString FileName() const { return mFileName; }
inline EResType ResourceType() const { return mResourceType; }
};
class CResourceDatabase
{
struct SResEntry
CGameProject *mpProj;
std::map<CUniqueID, CResourceEntry*> mResourceMap;
enum EVersion
{
CUniqueID ID;
TString DataPath;
eVer_Initial,
eVer_Max,
eVer_Current = eVer_Max - 1
};
public:
CResourceDatabase() {}
~CResourceDatabase() {}
CResourceDatabase(CGameProject *pProj);
~CResourceDatabase();
void Load(const TString& rkPath);
void Save(const TString& rkPath) const;
CResourceEntry* FindResourceEntry(const CUniqueID& rkID) const;
CResource* LoadResource(const CUniqueID& rkID) const;
bool RegisterResource(const CUniqueID& rkID, const TWideString& rkDir, const TWideString& rkFileName, EResType Type);
inline CGameProject* GameProject() const { return mpProj; }
};
#endif // CRESOURCEDATABASE_H

View File

@ -8,7 +8,7 @@
class CCollisionMeshGroup : public CResource
{
DECLARE_RESOURCE_TYPE(eCollisionMeshGroup)
DECLARE_RESOURCE_TYPE(eDynamicCollision)
std::vector<CCollisionMesh*> mMeshes;
public:

View File

@ -8,7 +8,7 @@
class CPoiToWorld : public CResource
{
DECLARE_RESOURCE_TYPE(ePoiToWorld)
DECLARE_RESOURCE_TYPE(eStaticGeometryMap)
public:
struct SPoiMap

View File

@ -1,69 +1,140 @@
#include "CResource.h"
#include <Common/AssertMacro.h>
#include <map>
std::map<u32, EResType> gExtensionTypeMap;
std::map<u32, TString> gTypeExtensionMap;
u32 GetGameTypeID(EGame Game, EResType ResType)
{
return ((Game & 0xFFFF) << 16) | (ResType & 0xFFFF);
}
// ************ STATIC ************
EResType CResource::ResTypeForExtension(CFourCC Extension)
{
Extension = Extension.ToUpper();
auto Find = gExtensionTypeMap.find(Extension.ToLong());
if (Extension < "FONT")
if (Find == gExtensionTypeMap.end())
{
if (Extension < "CSKR")
{
if (Extension == "AFSM") return eStateMachine;
if (Extension == "AGSC") return eAudioGrp;
if (Extension == "ANCS") return eAnimSet;
if (Extension == "ANIM") return eAnimation;
if (Extension == "ATBL") return eAudioTable;
if (Extension == "CAUD") return eAudioData;
if (Extension == "CHAR") return eAnimSet;
if (Extension == "CINF") return eSkeleton;
if (Extension == "CMDL") return eModel;
if (Extension == "CRSC") return eCollisionResponse;
}
else
{
if (Extension == "CSKR") return eSkin;
if (Extension == "CSMP") return eAudioSample;
if (Extension == "CSNG") return eMidi;
if (Extension == "CTWK") return eTweak;
if (Extension == "DCLN") return eCollisionMeshGroup;
if (Extension == "DGRP") return eDependencyGroup;
if (Extension == "DSP ") return eMusicTrack;
if (Extension == "DUMB") return eDataDump;
if (Extension == "ELSC") return eParticleElectric;
if (Extension == "EVNT") return eAnimEventData;
}
}
else
{
if (Extension < "PAK ")
{
if (Extension == "FONT") return eFont;
if (Extension == "FRME") return eGuiFrame;
if (Extension == "FSM2") return eStateMachine;
if (Extension == "HINT") return eHintSystem;
if (Extension == "MAPA") return eMapArea;
if (Extension == "MAPW") return eMapWorld;
if (Extension == "MAPU") return eMapUniverse;
if (Extension == "MLVL") return eWorld;
if (Extension == "MREA") return eArea;
if (Extension == "NTWK") return eTweak;
}
else
{
if (Extension == "PAK ") return ePackFile;
if (Extension == "PART") return eParticle;
if (Extension == "PATH") return eNavMesh;
if (Extension == "SAVW") return eSaveWorld;
if (Extension == "SCAN") return eScan;
if (Extension == "STRG") return eStringTable;
if (Extension == "STRM") return eAudioStream;
if (Extension == "SWHC") return eParticleSwoosh;
if (Extension == "THP ") return eVideo;
if (Extension == "TXTR") return eTexture;
if (Extension == "WPSC") return eProjectile;
}
Log::Error("Couldn't find resource type for requested cooked extension: " + Extension.ToString());
return eInvalidResType;
}
return eInvalidResType;
return Find->second;
}
// Implementation of functions declared in EResType.h
TString GetRawExtension(EResType /*Type*/, EGame /*Game*/)
{
return "";
}
TString GetCookedExtension(EResType Type, EGame Game)
{
u32 GameTypeID = GetGameTypeID(Game, Type);
auto Find = gTypeExtensionMap.find(GameTypeID);
if (Find != gTypeExtensionMap.end()) return Find->second;
else return "";
}
// ************ TYPE REGISTRATIONS ************
/* This macro registers a resource's cooked extension with an EResType enum, which allows for
* a resource type to be looked up via its extension, and vice versa. Because certain EResType
* enumerators are reused with different extensions between games, it's possible to set up a
* registration to be valid for only a specific range of games, and then tie a different
* extension to the same enumerator for a different game. This allows you to always look up the
* correct extension for a given resource type with a combination of an EResType and an EGame.
*
* You shouldn't need to add any new resource types, as the currently registered ones cover every
* resource type for every game from the MP1 demo up to DKCR. However, if you do, simply add an
* extra REGISTER_RESOURCE_TYPE line to the list below.
*/
#define REGISTER_RESOURCE_TYPE(CookedExtension, TypeEnum, FirstGame, LastGame) \
class CResourceTypeRegistrant__##CookedExtension \
{ \
public: \
CResourceTypeRegistrant__##CookedExtension() \
{ \
ASSERT(FirstGame != eUnknownVersion); \
\
/* Register extension with resource type (should be consistent across all games) */ \
u32 IntExt = CFourCC(#CookedExtension).ToLong(); \
auto ExtFind = gExtensionTypeMap.find(IntExt); \
if (ExtFind != gExtensionTypeMap.end()) \
ASSERT(ExtFind->second == TypeEnum); \
\
gExtensionTypeMap[IntExt] = TypeEnum; \
\
/* Register resource type with extension for the specified game range */ \
EGame Game = FirstGame; \
\
while (Game <= LastGame) \
{ \
u32 GameTypeID = GetGameTypeID(Game, TypeEnum); \
auto Find = gTypeExtensionMap.find(GameTypeID); \
ASSERT(Find == gTypeExtensionMap.end()); \
gTypeExtensionMap[GameTypeID] = #CookedExtension; \
Game = (EGame) ((int) Game + 1); \
} \
} \
}; \
CResourceTypeRegistrant__##CookedExtension gResourceTypeRegistrant__##CookedExtension;
REGISTER_RESOURCE_TYPE(AFSM, eStateMachine, ePrimeDemo, eEchoes)
REGISTER_RESOURCE_TYPE(AGSC, eAudioGroupSet, ePrimeDemo, eCorruptionProto)
REGISTER_RESOURCE_TYPE(ANCS, eAnimSet, ePrimeDemo, eEchoes)
REGISTER_RESOURCE_TYPE(ANIM, eAnimation, ePrimeDemo, eReturns)
REGISTER_RESOURCE_TYPE(ATBL, eAudioLookupTable, ePrimeDemo, eCorruption)
REGISTER_RESOURCE_TYPE(BFRC, eBurstFireData, eCorruptionProto, eCorruption)
REGISTER_RESOURCE_TYPE(CAAD, eUnknown_CAAD, eCorruption, eCorruption)
REGISTER_RESOURCE_TYPE(CAUD, eAudioMacro, eCorruptionProto, eReturns)
REGISTER_RESOURCE_TYPE(CHAR, eCharacter, eCorruptionProto, eReturns)
REGISTER_RESOURCE_TYPE(CINF, eSkeleton, ePrimeDemo, eReturns)
REGISTER_RESOURCE_TYPE(CMDL, eModel, ePrimeDemo, eReturns)
REGISTER_RESOURCE_TYPE(CRSC, eParticleCollisionResponse, ePrimeDemo, eCorruption)
REGISTER_RESOURCE_TYPE(CPRM, eAnimCollisionPrimData, eReturns, eReturns)
REGISTER_RESOURCE_TYPE(CSKR, eSkin, ePrimeDemo, eReturns)
REGISTER_RESOURCE_TYPE(CSMP, eAudioSample, eCorruptionProto, eReturns)
REGISTER_RESOURCE_TYPE(CSNG, eMidi, ePrimeDemo, eEchoes)
REGISTER_RESOURCE_TYPE(CSPP, eSpatialPrimitive, eEchoesDemo, eEchoes)
REGISTER_RESOURCE_TYPE(CTWK, eTweak, ePrimeDemo, ePrime)
REGISTER_RESOURCE_TYPE(DCLN, eDynamicCollision, ePrimeDemo, eReturns)
REGISTER_RESOURCE_TYPE(DGRP, eDependencyGroup, ePrimeDemo, eReturns)
REGISTER_RESOURCE_TYPE(DPSC, eParticleDecal, ePrimeDemo, eCorruption)
REGISTER_RESOURCE_TYPE(DUMB, eBinaryData, ePrimeDemo, eCorruption)
REGISTER_RESOURCE_TYPE(EGMC, eStaticGeometryMap, eEchoesDemo, eCorruption)
REGISTER_RESOURCE_TYPE(ELSC, eParticleElectric, ePrimeDemo, eCorruption)
REGISTER_RESOURCE_TYPE(EVNT, eAnimEventData, ePrimeDemo, ePrime)
REGISTER_RESOURCE_TYPE(FONT, eFont, ePrimeDemo, eReturns)
REGISTER_RESOURCE_TYPE(FRME, eGuiFrame, ePrimeDemo, eReturns)
REGISTER_RESOURCE_TYPE(FSM2, eStateMachine2, eEchoesDemo, eCorruption)
REGISTER_RESOURCE_TYPE(FSMC, eStateMachine, eReturns, eReturns)
REGISTER_RESOURCE_TYPE(HINT, eHintSystem, ePrime, eCorruption)
REGISTER_RESOURCE_TYPE(KFAM, eGuiKeyFrame, ePrimeDemo, ePrimeDemo)
REGISTER_RESOURCE_TYPE(MAPA, eMapArea, ePrimeDemo, eCorruption)
REGISTER_RESOURCE_TYPE(MAPU, eMapUniverse, ePrimeDemo, eEchoes)
REGISTER_RESOURCE_TYPE(MAPW, eMapWorld, ePrimeDemo, eCorruption)
REGISTER_RESOURCE_TYPE(MLVL, eWorld, ePrimeDemo, eReturns)
REGISTER_RESOURCE_TYPE(MREA, eArea, ePrimeDemo, eReturns)
REGISTER_RESOURCE_TYPE(NTWK, eTweak, eEchoesDemo, eReturns)
REGISTER_RESOURCE_TYPE(PAK , ePackage, ePrimeDemo, eReturns)
REGISTER_RESOURCE_TYPE(PART, eParticle, ePrimeDemo, eReturns)
REGISTER_RESOURCE_TYPE(PATH, eNavMesh, ePrimeDemo, eCorruption)
REGISTER_RESOURCE_TYPE(PTLA, ePortalArea, eEchoesDemo, eCorruption)
REGISTER_RESOURCE_TYPE(RULE, eRuleSet, eEchoesDemo, eReturns)
REGISTER_RESOURCE_TYPE(SAND, eSourceAnimData, eCorruptionProto, eCorruption)
REGISTER_RESOURCE_TYPE(SAVA, eSaveArea, eCorruptionProto, eCorruption)
REGISTER_RESOURCE_TYPE(SAVW, eSaveWorld, ePrime, eReturns)
REGISTER_RESOURCE_TYPE(SCAN, eScan, ePrimeDemo, eCorruption)
REGISTER_RESOURCE_TYPE(SPSC, eParticleSpawn, eEchoesDemo, eReturns)
REGISTER_RESOURCE_TYPE(SRSC, eParticleSorted, eEchoesDemo, eEchoes)
REGISTER_RESOURCE_TYPE(STLC, eStringList, eEchoesDemo, eCorruptionProto)
REGISTER_RESOURCE_TYPE(STRG, eStringTable, ePrimeDemo, eReturns)
REGISTER_RESOURCE_TYPE(STRM, eStreamedAudio, eCorruptionProto, eReturns)
REGISTER_RESOURCE_TYPE(SWHC, eParticleSwoosh, ePrimeDemo, eReturns)
REGISTER_RESOURCE_TYPE(THP , eVideo, ePrimeDemo, eReturns)
REGISTER_RESOURCE_TYPE(TXTR, eTexture, ePrimeDemo, eReturns)
REGISTER_RESOURCE_TYPE(USRC, eUserEvaluatorData, eCorruptionProto, eCorruption)
REGISTER_RESOURCE_TYPE(XFSC, eParticleTransform, eReturns, eReturns)
REGISTER_RESOURCE_TYPE(WPSC, eParticleWeapon, ePrimeDemo, eCorruption)

View File

@ -4,14 +4,14 @@
// Global version enum that can be easily shared between loaders
enum EGame
{
ePrimeDemo = 0,
ePrime = 1,
eEchoesDemo = 2,
eEchoes = 3,
eCorruptionProto = 4,
eCorruption = 5,
eReturns = 6,
eUnknownVersion = -1
ePrimeDemo,
ePrime,
eEchoesDemo,
eEchoes,
eCorruptionProto,
eCorruption,
eReturns,
eUnknownVersion = -1
};
#endif // EGAME_H

View File

@ -1,52 +1,75 @@
#ifndef ERESTYPE
#define ERESTYPE
#include "EGame.h"
#include <Common/TString.h>
enum EResType
{
eAnimation = 0,
eAnimEventData = 1,
eAnimSet = 2,
eArea = 3,
eAudioData = 4,
eAudioGrp = 5,
eAudioSample = 6,
eAudioStream = 7,
eAudioTable = 8,
eCharacter = 9,
eCollisionMeshGroup = 10,
eCollisionResponse = 11,
eDataDump = 12,
eDecal = 13,
eDependencyGroup = 14,
eFont = 15,
eGuiFrame = 16,
eHintSystem = 17,
eInvalidResType = 18,
eMapArea = 19,
eMapWorld = 20,
eMapUniverse = 21,
eMidi = 22,
eModel = 23,
eMusicTrack = 24,
eNavMesh = 25,
ePackFile = 26,
eParticle = 27,
eParticleElectric = 28,
eParticleSwoosh = 29,
ePoiToWorld = 30,
eProjectile = 31,
eResource = 32,
eSaveWorld = 33,
eScan = 34,
eSkeleton = 35,
eSkin = 36,
eStateMachine = 37,
eStringTable = 38,
eTexture = 39,
eTweak = 40,
eVideo = 41,
eWorld = 42
eAnimation,
eAnimCollisionPrimData,
eAnimEventData,
eAnimSet,
eArea,
eAudioMacro,
eAudioGroupSet,
eAudioSample,
eStreamedAudio,
eAudioLookupTable,
eBinaryData,
eBurstFireData,
eCharacter,
eDependencyGroup,
eDynamicCollision,
eFont,
eGuiFrame,
eGuiKeyFrame,
eHintSystem,
eInvalidResType,
eMapArea,
eMapWorld,
eMapUniverse,
eMidi,
eModel,
eMusicTrack,
eNavMesh,
ePackage,
eParticle,
eParticleCollisionResponse,
eParticleDecal,
eParticleElectric,
eParticleSorted,
eParticleSpawn,
eParticleSwoosh,
eParticleTransform,
eParticleWeapon,
ePortalArea,
eResource,
eRuleSet,
eSaveArea,
eSaveWorld,
eScan,
eSkeleton,
eSkin,
eSourceAnimData,
eSpatialPrimitive,
eStateMachine,
eStateMachine2, // For distinguishing AFSM/FSM2
eStaticGeometryMap,
eStringList,
eStringTable,
eTexture,
eTweak,
eUnknown_CAAD,
eUserEvaluatorData,
eVideo,
eWorld
};
// defined in CResource.cpp
TString GetTypeName(EResType Type);
TString GetRawExtension(EResType Type, EGame Game);
TString GetCookedExtension(EResType Type, EGame Game);
#endif // ERESTYPE

View File

@ -232,7 +232,7 @@ CCollisionMeshGroup* CScriptTemplate::FindCollision(CPropertyStruct *pProperties
}
// Verify resource exists + is correct type
if (pRes && (pRes->Type() == eCollisionMeshGroup))
if (pRes && (pRes->Type() == eDynamicCollision))
return static_cast<CCollisionMeshGroup*>(pRes);
}

View File

@ -258,15 +258,19 @@ void CStartWindow::About()
void CStartWindow::ExportGame()
{
// TEMP - hardcoded names for convenience. will remove later!
#define USE_HARDCODED_NAMES 1
#define USE_HARDCODED_GAME_ROOT 0
#define USE_HARDCODED_EXPORT_DIR 1
#if USE_HARDCODED_NAMES
QString GameRoot = "E:/Unpacked/DKCR Dolphin";
QString ExportDir = "E:/Unpacked/ExportTest";
#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