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:
Aruki
2017-02-06 09:20:18 -07:00
parent fbdf9023d1
commit eca833cf89
31 changed files with 1115 additions and 285 deletions

View File

@@ -9,6 +9,7 @@
#include <memory>
const TString gkAssetMapPath = "..\\resources\\gameinfo\\AssetNameMap.xml";
const TString gkAssetMapExt = "xml";
class CAssetNameMap
{
@@ -36,6 +37,9 @@ public:
void SaveAssetNames(TString Path = gkAssetMapPath);
bool GetNameInfo(CAssetID ID, TString& rOutDirectory, TString& rOutName);
void CopyFromStore(CResourceStore *pStore);
inline static TString DefaultNameMapPath() { return gkAssetMapPath; }
inline static TString GetExtension() { return gkAssetMapExt; }
};
#endif // CASSETNAMEMAP

View File

@@ -12,65 +12,77 @@
#include <Common/Serialization/CXMLWriter.h>
#include <tinyxml2.h>
#define COPY_DISC_DATA 1
#define LOAD_PAKS 1
#define SAVE_PACKAGE_DEFINITIONS 1
#define USE_ASSET_NAME_MAP 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;
mBuildVersion = 0.f;
mGameDir = FileUtil::MakeAbsolute(rkInputDir);
mExportDir = FileUtil::MakeAbsolute(rkOutputDir);
mpProject = new CGameProject(mExportDir);
mDiscDir = L"Disc\\";
mWorldsDirName = L"Worlds\\";
ASSERT(mGame != eUnknownGame);
ASSERT(mRegion != eRegion_Unknown);
}
#if PUBLIC_RELEASE
#error Fix export directory being cleared!
#endif
bool CGameExporter::Export()
bool CGameExporter::Export(nod::DiscBase *pDisc, const TString& rkOutputDir, CAssetNameMap *pNameMap, CGameInfo *pGameInfo)
{
SCOPED_TIMER(ExportGame);
FileUtil::CreateDirectory(mExportDir);
FileUtil::ClearDirectory(mExportDir);
mpDisc = pDisc;
mpNameMap = pNameMap;
mpGameInfo = pGameInfo;
// Initial analyze/copy of disc data
CopyDiscData();
FindBuildVersion();
mExportDir = FileUtil::MakeAbsolute(rkOutputDir);
mDiscDir = L"Disc\\";
mWorldsDirName = L"Worlds\\";
// Create project
mpProject = new CGameProject(this, mExportDir, mGame, mBuildVersion);
mpProject->SetProjectName(CMasterTemplate::FindGameName(mGame));
FileUtil::MakeDirectory(mExportDir);
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();
mpStore = mpProject->ResourceStore();
mContentDir = mpStore->RawDir(false);
mCookedDir = mpStore->CookedDir(false);
#if USE_ASSET_NAME_MAP
mNameMap.LoadAssetNames();
#endif
// Export game data
CResourceStore *pOldStore = gpResourceStore;
gpResourceStore = mpStore;
LoadPaks();
ExportCookedResources();
mpProject->AudioManager()->LoadAssets();
ExportResourceEditorData();
// Export finished!
mProjectPath = mpProject->ProjectPath();
delete mpProject;
gpResourceStore = pOldStore;
if (pOldActiveProj) pOldActiveProj->SetActive();
return true;
}
@@ -81,100 +93,85 @@ void CGameExporter::LoadResource(const CAssetID& rkID, std::vector<u8>& rBuffer)
}
// ************ PROTECTED ************
void CGameExporter::CopyDiscData()
bool CGameExporter::ExtractDiscData()
{
#if COPY_DISC_DATA
SCOPED_TIMER(CopyDiscData);
// todo: handle dol, apploader, multiple partitions, wii ticket blob
SCOPED_TIMER(ExtractDiscData);
// Create Disc output folder
FileUtil::CreateDirectory(mExportDir + mDiscDir);
#endif
TWideString AbsDiscDir = mExportDir + mDiscDir;
FileUtil::MakeDirectory(AbsDiscDir);
// Copy data
TWideStringList DiscFiles;
FileUtil::GetDirectoryContents(mGameDir, DiscFiles);
// Extract disc filesystem
nod::Partition *pDataPartition = mpDisc->getDataPartition();
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;
TWideString RelPath = FullPath.ChopFront(mGameDir.Size());
// Exclude PakTool files and folders
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
if (mDolPath.IsEmpty() && FullPath.EndsWith(L".dol", false) && FileUtil::FileSize(FullPath) != 0)
mDolPath = FullPath;
// Detect paks
if (FullPath.GetFileExtension().ToLower() == L"pak")
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
mFilesystemAddress = 0;
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();
ASSERT(mGame != eUnknownGame);
return true;
}
void CGameExporter::FindBuildVersion()
bool CGameExporter::ExtractDiscNodeRecursive(const nod::Node *pkNode, const TWideString& rkDir, const nod::ExtractionContext& rkContext)
{
ASSERT(!mDolPath.IsEmpty());
// MP1 demo build doesn't have a build version
if (mGame == ePrimeDemo) return;
// 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++)
for (nod::Node::DirectoryIterator Iter = pkNode->begin(); Iter != pkNode->end(); ++Iter)
{
int Match = 0;
while (FileContents[SearchIdx + Match] == pkSearchText[Match] && Match < SearchTextSize)
Match++;
if (Match == SearchTextSize)
if (Iter->getKind() == nod::Node::Kind::File)
{
// Found the build info string; extract version number
TString BuildInfo = &FileContents[SearchIdx + SearchTextSize];
int BuildVerStart = BuildInfo.IndexOfPhrase("Build v") + 7;
ASSERT(BuildVerStart != 6);
TWideString FilePath = rkDir + TString(Iter->getName()).ToUTF16();
bool Success = Iter->extractToDirectory(*rkDir, rkContext);
if (!Success) return false;
mBuildVersion = BuildInfo.SubString(BuildVerStart, 5).ToFloat();
return;
if (FilePath.GetFileExtension() == L"pak")
mPaks.push_back(FilePath);
}
else
{
TWideString Subdir = rkDir + TString(Iter->getName()).ToUTF16() + L"\\";
bool Success = FileUtil::MakeDirectory(Subdir);
if (!Success) return false;
Success = ExtractDiscNodeRecursive(&*Iter, Subdir, rkContext);
if (!Success) return false;
}
}
Log::Error("Failed to find MetroidBuildInfo string. Build Version will be set to 0. DOL file: " + mDolPath.ToUTF8());
return true;
}
// ************ RESOURCE LOADING ************
@@ -183,10 +180,13 @@ void CGameExporter::LoadPaks()
#if LOAD_PAKS
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++)
{
TWideString PakPath = *It;
TWideString PakName = PakPath.GetFileName(false);
TString CharPak = PakPath.ToUTF8();
CFileInStream Pak(CharPak.ToStdString(), IOUtil::eBigEndian);
@@ -196,7 +196,7 @@ void CGameExporter::LoadPaks()
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");
// MP1-MP3Proto
@@ -448,7 +448,7 @@ void CGameExporter::ExportCookedResources()
{
{
SCOPED_TIMER(ExportCookedResources);
FileUtil::CreateDirectory(mCookedDir);
FileUtil::MakeDirectory(mCookedDir);
for (auto It = mResourceMap.begin(); It != mResourceMap.end(); It++)
{
@@ -517,13 +517,20 @@ void CGameExporter::ExportResource(SResourceInstance& rRes)
// Register resource and write to file
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);
#if EXPORT_COOKED
// Save cooked asset
TWideString OutCookedPath = pEntry->CookedAssetPath();
FileUtil::CreateDirectory(OutCookedPath.GetFileDirectory());
FileUtil::MakeDirectory(OutCookedPath.GetFileDirectory());
CFileOutStream Out(OutCookedPath.ToUTF8().ToStdString(), IOUtil::eBigEndian);
if (Out.IsValid())

View File

@@ -10,17 +10,25 @@
#include <Common/TString.h>
#include <Common/types.h>
#include <map>
#include <nod/nod.hpp>
class CGameExporter
{
// Project
// Project Data
CGameProject *mpProject;
TWideString mProjectPath;
CResourceStore *mpStore;
EGame mGame;
ERegion mRegion;
TString mGameName;
TString mGameID;
float mBuildVersion;
TWideString mDolPath;
TWideString mApploaderPath;
TWideString mPartitionHeaderPath;
u32 mFilesystemAddress;
// Directories
TWideString mGameDir;
TWideString mExportDir;
TWideString mDiscDir;
TWideString mContentDir;
@@ -29,13 +37,13 @@ class CGameExporter
TWideString mWorldsDirName;
// Files
TWideString mDolPath;
nod::DiscBase *mpDisc;
// Resources
TWideStringList mPaks;
std::map<CAssetID, bool> mAreaDuplicateMap;
CAssetNameMap mNameMap;
CGameInfo mGameInfo;
CAssetNameMap *mpNameMap;
CGameInfo *mpGameInfo;
struct SResourceInstance
{
@@ -50,13 +58,15 @@ class CGameExporter
std::map<CAssetID, SResourceInstance> mResourceMap;
public:
CGameExporter(const TString& rkInputDir, const TString& rkOutputDir);
bool Export();
CGameExporter(EGame Game, ERegion Region, const TString& rkGameName, const TString& rkGameID, float BuildVersion);
bool Export(nod::DiscBase *pDisc, const TString& rkOutputDir, CAssetNameMap *pNameMap, CGameInfo *pGameInfo);
void LoadResource(const CAssetID& rkID, std::vector<u8>& rBuffer);
inline TWideString ProjectPath() const { return mProjectPath; }
protected:
void CopyDiscData();
void FindBuildVersion();
bool ExtractDiscData();
bool ExtractDiscNodeRecursive(const nod::Node *pkNode, const TWideString& rkDir, const nod::ExtractionContext& rkContext);
void LoadPaks();
void LoadResource(const SResourceInstance& rkResource, std::vector<u8>& rBuffer);
void ExportCookedResources();

View File

@@ -1,11 +1,14 @@
#include "CGameInfo.h"
#include <Common/FileUtil.h>
void CGameInfo::LoadGameInfo(EGame Game)
{
Game = RoundGame(Game);
mGame = Game;
LoadGameInfo(GetDefaultGameInfoPath(Game));
TString Path = GetDefaultGameInfoPath(Game);
if (FileUtil::Exists(Path))
LoadGameInfo(Path);
}
void CGameInfo::LoadGameInfo(TString Path)
@@ -59,5 +62,5 @@ TString CGameInfo::GetDefaultGameInfoPath(EGame Game)
return "";
TString GameName = GetGameShortName(Game);
return TString::Format("%s\\GameInfo%s.xml", *gkGameInfoDir, *GameName);
return TString::Format("%s\\GameInfo%s.%s", *gkGameInfoDir, *GameName, *gkGameInfoExt);
}

View File

@@ -9,6 +9,7 @@
#include <map>
const TString gkGameInfoDir = "..\\resources\\gameinfo";
const TString gkGameInfoExt = "xml";
class CGameInfo
{
@@ -34,6 +35,8 @@ public:
static CGameInfo* GetGameInfo(EGame Game);
static EGame RoundGame(EGame Game);
static TString GetDefaultGameInfoPath(EGame Game);
inline static TString GetExtension() { return gkGameInfoExt; }
};
#endif // CGAMEINFO

View File

@@ -10,30 +10,16 @@ CGameProject::~CGameProject()
ASSERT(!mpResourceStore->IsDirty());
if (IsActive())
{
mspActiveProject = nullptr;
gpResourceStore = nullptr;
}
delete mpAudioManager;
delete mpGameInfo;
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()
{
TString ProjPath = ProjectPath().ToUTF8();
@@ -44,8 +30,19 @@ void CGameProject::Save()
void CGameProject::Serialize(IArchive& rArc)
{
rArc << SERIAL("Name", mProjectName)
<< SERIAL("Region", mRegion)
<< SERIAL("GameID", mGameID)
<< 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
std::vector<TString> PackageList;
@@ -129,3 +126,52 @@ CAssetID CGameProject::FindNamedResource(const TString& rkName) const
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;
}

View File

@@ -14,9 +14,16 @@
class CGameProject
{
EGame mGame;
float mBuildVersion;
TString mProjectName;
EGame mGame;
ERegion mRegion;
TString mGameID;
float mBuildVersion;
TWideString mDolPath;
TWideString mApploaderPath;
TWideString mPartitionHeaderPath;
u32 mFilesystemAddress;
TWideString mProjectRoot;
TWideString mResourceDBPath;
std::vector<CPackage*> mPackages;
@@ -34,52 +41,45 @@ class CGameProject
static CGameProject *mspActiveProject;
public:
// Private Constructor
CGameProject()
: mGame(eUnknownGame)
, mProjectName("Unnamed Project")
: mProjectName("Unnamed Project")
, mGame(eUnknownGame)
, mRegion(eRegion_Unknown)
, mGameID("000000")
, mBuildVersion(0.f)
, mResourceDBPath(L"ResourceDB.rdb")
, mpResourceStore(nullptr)
{
mpResourceStore = new CResourceStore(this);
mpGameInfo = new CGameInfo();
mpAudioManager = new CAudioManager(this);
}
CGameProject(const TWideString& rkProjRootDir)
: 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);
}
public:
~CGameProject();
bool Load(const TWideString& rkPath);
void Save();
void Serialize(IArchive& rArc);
void SetActive();
void GetWorldList(std::list<CAssetID>& rOut) 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
inline TWideString ProjectRoot() const { return mProjectRoot; }
inline TWideString ResourceDBPath(bool Relative) const { return Relative ? mResourceDBPath : mProjectRoot + mResourceDBPath; }
@@ -101,6 +101,7 @@ public:
inline EGame Game() const { return mGame; }
inline float BuildVersion() const { return mBuildVersion; }
inline bool IsActive() const { return mspActiveProject == this; }
inline bool IsWiiBuild() const { return mBuildVersion >= 3.f; }
static inline CGameProject* ActiveProject() { return mspActiveProject; }
};

View File

@@ -20,7 +20,7 @@ void CPackage::Load()
void CPackage::Save()
{
TWideString DefPath = DefinitionPath(false);
FileUtil::CreateDirectory(DefPath.GetFileDirectory());
FileUtil::MakeDirectory(DefPath.GetFileDirectory());
CXMLWriter Writer(DefPath.ToUTF8(), "PackageDefinition", 0, mpProject ? mpProject->Game() : eUnknownGame);
Serialize(Writer);

View File

@@ -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.
TString Path = RawAssetPath();
TString Dir = Path.GetFileDirectory();
FileUtil::CreateDirectory(Dir.ToUTF16());
FileUtil::MakeDirectory(Dir.ToUTF16());
TString SerialName = mpTypeInfo->TypeName();
SerialName.RemoveWhitespace();
@@ -268,7 +268,8 @@ CResource* CResourceEntry::LoadCooked(IInputStream& rInput)
gpResourceStore = mpStore;
mpResource = CResourceFactory::LoadCookedResource(this, rInput);
mpStore->TrackLoadedResource(this);
if (mpResource)
mpStore->TrackLoadedResource(this);
gpResourceStore = pOldStore;
return mpResource;