mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-17 08:57:09 +00:00
Added functionality to generate asset names
This commit is contained in:
438
src/Core/GameProject/AssetNameGeneration.cpp
Normal file
438
src/Core/GameProject/AssetNameGeneration.cpp
Normal file
@@ -0,0 +1,438 @@
|
||||
#include "AssetNameGeneration.h"
|
||||
#include "CGameProject.h"
|
||||
#include "CResourceIterator.h"
|
||||
#include "Core/Resource/CFont.h"
|
||||
#include "Core/Resource/CScan.h"
|
||||
#include "Core/Resource/CWorld.h"
|
||||
#include "Core/Resource/Script/CScriptLayer.h"
|
||||
#include <Math/MathUtil.h>
|
||||
|
||||
#define PROCESS_WORLDS 1
|
||||
#define PROCESS_AREAS 1
|
||||
#define PROCESS_MODELS 1
|
||||
#define PROCESS_AUDIO_GROUPS 1
|
||||
#define PROCESS_ANIM_CHAR_SETS 1
|
||||
#define PROCESS_STRINGS 1
|
||||
#define PROCESS_SCANS 1
|
||||
#define PROCESS_FONTS 1
|
||||
|
||||
void ApplyGeneratedName(CResourceEntry *pEntry, const TWideString& rkDir, const TWideString& rkName)
|
||||
{
|
||||
TWideString SanitizedName = FileUtil::SanitizeName(rkName, false);
|
||||
TWideString SanitizedDir = FileUtil::SanitizePath(rkDir, true);
|
||||
if (SanitizedName.IsEmpty()) return;
|
||||
|
||||
CVirtualDirectory *pNewDir = pEntry->ResourceStore()->GetVirtualDirectory(SanitizedDir, false, true);
|
||||
if (pEntry->Directory() == pNewDir && pEntry->Name() == SanitizedName) return;
|
||||
|
||||
TWideString Name = SanitizedName;
|
||||
int AppendNum = 0;
|
||||
|
||||
while (pNewDir->FindChildResource(Name, pEntry->ResourceType()) != nullptr)
|
||||
{
|
||||
Name = TWideString::Format(L"%s_%d", *SanitizedName, AppendNum);
|
||||
AppendNum++;
|
||||
}
|
||||
|
||||
bool Success = pEntry->Move(SanitizedDir, Name);
|
||||
ASSERT(Success);
|
||||
}
|
||||
|
||||
void GenerateAssetNames(CGameProject *pProj)
|
||||
{
|
||||
// todo: CAUD/CSMP
|
||||
CResourceStore *pStore = pProj->ResourceStore();
|
||||
|
||||
// Generate names for package named resources first
|
||||
for (u32 iPkg = 0; iPkg < pProj->NumPackages(); iPkg++)
|
||||
{
|
||||
CPackage *pPkg = pProj->PackageByIndex(iPkg);
|
||||
|
||||
for (u32 iCol = 0; iCol < pPkg->NumCollections(); iCol++)
|
||||
{
|
||||
CResourceCollection *pCol = pPkg->CollectionByIndex(iCol);
|
||||
|
||||
for (u32 iRes = 0; iRes < pCol->NumResources(); iRes++)
|
||||
{
|
||||
const SNamedResource& rkRes = pCol->ResourceByIndex(iRes);
|
||||
if (rkRes.Name.EndsWith("NODEPEND")) continue;
|
||||
|
||||
CResourceEntry *pRes = pStore->FindEntry(rkRes.ID);
|
||||
ApplyGeneratedName(pRes, pPkg->Name().ToUTF16(), rkRes.Name.ToUTF16());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if PROCESS_WORLDS
|
||||
// Generate world/area names
|
||||
const TWideString kWorldsRoot = L"Worlds\\";
|
||||
|
||||
for (TResourceIterator<eWorld> It(pStore); It; ++It)
|
||||
{
|
||||
// World common stuff
|
||||
TWideString WorldName = It->Name();
|
||||
|
||||
// Remove date from the end of the world name
|
||||
if (WorldName.EndsWith(L"_#SERIAL#"))
|
||||
WorldName = WorldName.ChopBack(9);
|
||||
|
||||
// Verify the second-to-last character is a number to make sure there is actually a date in the world name
|
||||
// note MP2 multiplayer worlds do not have dates in their names
|
||||
else if (WorldName[WorldName.Size() - 2] >= '0' && WorldName[WorldName.Size() - 2] <= '9')
|
||||
{
|
||||
bool StartedDate = false;
|
||||
|
||||
while (!WorldName.IsEmpty())
|
||||
{
|
||||
wchar_t Chr = WorldName.Back();
|
||||
|
||||
if (!StartedDate && Chr >= L'0' && Chr <= L'9')
|
||||
StartedDate = true;
|
||||
else if (StartedDate && Chr != L'_' && (Chr < L'0' || Chr > L'9'))
|
||||
break;
|
||||
|
||||
WorldName = WorldName.ChopBack(1);
|
||||
}
|
||||
}
|
||||
|
||||
TWideString WorldDir = kWorldsRoot + WorldName + L'\\';
|
||||
ApplyGeneratedName(*It, WorldDir, WorldName);
|
||||
|
||||
CWorld *pWorld = (CWorld*) It->Load();
|
||||
CModel *pSkyModel = pWorld->DefaultSkybox();
|
||||
CStringTable *pWorldNameTable = pWorld->WorldName();
|
||||
CStringTable *pDarkWorldNameTable = pWorld->DarkWorldName();
|
||||
CResource *pSaveWorld = pWorld->SaveWorld();
|
||||
CResource *pMapWorld = pWorld->MapWorld();
|
||||
|
||||
if (pSkyModel && !pSkyModel->Entry()->IsCategorized())
|
||||
{
|
||||
CResourceEntry *pSkyEntry = pSkyModel->Entry();
|
||||
ApplyGeneratedName(pSkyEntry, WorldDir, TWideString::Format(L"%s_Sky", *WorldName));
|
||||
}
|
||||
|
||||
if (pWorldNameTable)
|
||||
{
|
||||
CResourceEntry *pNameEntry = pWorldNameTable->Entry();
|
||||
ApplyGeneratedName(pNameEntry, WorldDir, pNameEntry->Name());
|
||||
}
|
||||
if (pDarkWorldNameTable)
|
||||
{
|
||||
CResourceEntry *pDarkNameEntry = pDarkWorldNameTable->Entry();
|
||||
ApplyGeneratedName(pDarkNameEntry, WorldDir, pDarkNameEntry->Name());
|
||||
}
|
||||
if (pSaveWorld)
|
||||
ApplyGeneratedName(pSaveWorld->Entry(), WorldDir, TWideString::Format(L"%s_SaveInfo", *WorldName));
|
||||
|
||||
if (pMapWorld)
|
||||
ApplyGeneratedName(pMapWorld->Entry(), WorldDir, TWideString::Format(L"%s_Map", *WorldName));
|
||||
|
||||
// Areas
|
||||
for (u32 iArea = 0; iArea < pWorld->NumAreas(); iArea++)
|
||||
{
|
||||
// Determine area name
|
||||
CAssetID AreaID = pWorld->AreaResourceID(iArea);
|
||||
TString InternalAreaName = pWorld->AreaInternalName(iArea);
|
||||
CStringTable *pAreaNameTable = pWorld->AreaName(iArea);
|
||||
TWideString AreaName;
|
||||
|
||||
if (pAreaNameTable)
|
||||
AreaName = pAreaNameTable->String("ENGL", 0);
|
||||
else if (!InternalAreaName.IsEmpty())
|
||||
AreaName = L"!!" + InternalAreaName.ToUTF16();
|
||||
else
|
||||
AreaName = L"!!" + AreaID.ToString().ToUTF16();
|
||||
|
||||
TWideString AreaDir = TWideString::Format(L"%s%02d_%s\\", *WorldDir, iArea, *AreaName);
|
||||
|
||||
// Rename area stuff
|
||||
CResourceEntry *pAreaEntry = pStore->FindEntry(AreaID);
|
||||
ASSERT(pAreaEntry != nullptr);
|
||||
ApplyGeneratedName(pAreaEntry, AreaDir, AreaName);
|
||||
|
||||
if (pAreaNameTable)
|
||||
ApplyGeneratedName(pAreaNameTable->Entry(), AreaDir, pAreaNameTable->Entry()->Name());
|
||||
|
||||
if (pMapWorld)
|
||||
{
|
||||
ASSERT(pMapWorld->Type() == eDependencyGroup);
|
||||
CDependencyGroup *pGroup = static_cast<CDependencyGroup*>(pMapWorld);
|
||||
CAssetID MapID = pGroup->DependencyByIndex(iArea);
|
||||
CResourceEntry *pMapEntry = pStore->FindEntry(MapID);
|
||||
ASSERT(pMapEntry != nullptr);
|
||||
|
||||
ApplyGeneratedName(pMapEntry, AreaDir, TWideString::Format(L"%s_Map", *AreaName));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PROCESS_AREAS
|
||||
// Generate area stuff
|
||||
for (TResourceIterator<eArea> It(pStore); It; ++It)
|
||||
{
|
||||
TWideString AreaDir = It->DirectoryPath();
|
||||
TWideString AreaName = It->Name();
|
||||
CGameArea *pArea = (CGameArea*) It->Load();
|
||||
|
||||
// Area lightmaps
|
||||
TWideString LightmapDir = AreaDir + L"Lightmaps\\";
|
||||
CMaterialSet *pMaterials = pArea->Materials();
|
||||
|
||||
for (u32 iMat = 0; iMat < pMaterials->NumMaterials(); iMat++)
|
||||
{
|
||||
CMaterial *pMat = pMaterials->MaterialByIndex(iMat);
|
||||
|
||||
if (pMat->Options().HasFlag(CMaterial::eLightmap))
|
||||
{
|
||||
CTexture *pLightmapTex = pMat->Pass(0)->Texture();
|
||||
CResourceEntry *pTexEntry = pLightmapTex->Entry();
|
||||
|
||||
TWideString TexName = TWideString::Format(L"lit_%s_%dx%d", *pLightmapTex->ID().ToString().ToUTF16(), pLightmapTex->Width(), pLightmapTex->Height());
|
||||
ApplyGeneratedName(pTexEntry, LightmapDir, TexName);
|
||||
pTexEntry->SetHidden(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate names from script instance names
|
||||
for (u32 iLyr = 0; iLyr < pArea->NumScriptLayers(); iLyr++)
|
||||
{
|
||||
CScriptLayer *pLayer = pArea->ScriptLayer(iLyr);
|
||||
|
||||
for (u32 iInst = 0; iInst < pLayer->NumInstances(); iInst++)
|
||||
{
|
||||
CScriptObject *pInst = pLayer->InstanceByIndex(iInst);
|
||||
|
||||
if (pInst->ObjectTypeID() == 0x42 || pInst->ObjectTypeID() == FOURCC("POIN"))
|
||||
{
|
||||
TString Name = pInst->InstanceName();
|
||||
|
||||
if (Name.EndsWith(".scan"))
|
||||
{
|
||||
TIDString ScanIDString = (pProj->Game() <= ePrime ? "0x4:0x0" : "0xBDBEC295:0xB94E9BE7");
|
||||
TAssetProperty *pScanProperty = TPropCast<TAssetProperty>(pInst->PropertyByIDString(ScanIDString));
|
||||
|
||||
if (pScanProperty)
|
||||
{
|
||||
CAssetID ScanID = pScanProperty->Get();
|
||||
CResourceEntry *pEntry = pStore->FindEntry(ScanID);
|
||||
|
||||
if (pEntry && !pEntry->IsNamed())
|
||||
{
|
||||
TWideString ScanName = Name.ToUTF16().ChopBack(5);
|
||||
ApplyGeneratedName(pEntry, pEntry->DirectoryPath(), ScanName);
|
||||
|
||||
CScan *pScan = (CScan*) pEntry->Load();
|
||||
if (pScan && pScan->ScanText())
|
||||
{
|
||||
CResourceEntry *pStringEntry = pScan->ScanText()->Entry();
|
||||
ApplyGeneratedName(pStringEntry, pStringEntry->DirectoryPath(), ScanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Other area assets
|
||||
CResourceEntry *pPathEntry = pStore->FindEntry(pArea->PathID());
|
||||
CResourceEntry *pPoiMapEntry = pArea->PoiToWorldMap() ? pArea->PoiToWorldMap()->Entry() : nullptr;
|
||||
CResourceEntry *pPortalEntry = pStore->FindEntry(pArea->PortalAreaID());
|
||||
|
||||
if (pPathEntry)
|
||||
ApplyGeneratedName(pPathEntry, AreaDir, TWideString::Format(L"%s_Path", *AreaName));
|
||||
|
||||
if (pPoiMapEntry)
|
||||
ApplyGeneratedName(pPoiMapEntry, AreaDir, TWideString::Format(L"%s_EGMC", *AreaName));
|
||||
|
||||
if (pPortalEntry)
|
||||
ApplyGeneratedName(pPortalEntry, AreaDir, TWideString::Format(L"%s_PortalArea", *AreaName));
|
||||
|
||||
pStore->DestroyUnreferencedResources();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PROCESS_MODELS
|
||||
// Generate Model Lightmap names
|
||||
for (TResourceIterator<eModel> It(pStore); It; ++It)
|
||||
{
|
||||
CModel *pModel = (CModel*) It->Load();
|
||||
|
||||
for (u32 iSet = 0; iSet < pModel->GetMatSetCount(); iSet++)
|
||||
{
|
||||
CMaterialSet *pSet = pModel->GetMatSet(iSet);
|
||||
|
||||
for (u32 iMat = 0; iMat < pSet->NumMaterials(); iMat++)
|
||||
{
|
||||
CMaterial *pMat = pSet->MaterialByIndex(iMat);
|
||||
|
||||
if (pMat->Options().HasFlag(CMaterial::eLightmap))
|
||||
{
|
||||
CTexture *pLightmapTex = pMat->Pass(0)->Texture();
|
||||
CResourceEntry *pTexEntry = pLightmapTex->Entry();
|
||||
if (pTexEntry->IsNamed() || pTexEntry->IsCategorized()) continue;
|
||||
|
||||
TWideString TexName = TWideString::Format(L"lit_%s_%dx%d", *pLightmapTex->ID().ToString().ToUTF16(), pLightmapTex->Width(), pLightmapTex->Height());
|
||||
ApplyGeneratedName(pTexEntry, pModel->Entry()->DirectoryPath(), TexName);
|
||||
pTexEntry->SetHidden(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pStore->DestroyUnreferencedResources();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PROCESS_AUDIO_GROUPS
|
||||
// Generate Audio Group names
|
||||
for (TResourceIterator<eAudioGroup> It(pStore); It; ++It)
|
||||
{
|
||||
CAudioGroup *pGroup = (CAudioGroup*) It->Load();
|
||||
TWideString GroupName = pGroup->GroupName().ToUTF16();
|
||||
ApplyGeneratedName(*It, L"AudioGrp\\", GroupName);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PROCESS_ANIM_CHAR_SETS
|
||||
// Generate animation format names
|
||||
for (TResourceIterator<eAnimSet> It(pStore); It; ++It)
|
||||
{
|
||||
TWideString SetDir = It->DirectoryPath();
|
||||
TWideString NewSetName;
|
||||
CAnimSet *pSet = (CAnimSet*) It->Load();
|
||||
|
||||
for (u32 iChar = 0; iChar < pSet->NumCharacters(); iChar++)
|
||||
{
|
||||
const SSetCharacter *pkChar = pSet->Character(iChar);
|
||||
|
||||
TWideString CharName = pkChar->Name.ToUTF16();
|
||||
if (iChar == 0) NewSetName = CharName;
|
||||
|
||||
if (pkChar->pModel) ApplyGeneratedName(pkChar->pModel->Entry(), SetDir, CharName);
|
||||
if (pkChar->pSkeleton) ApplyGeneratedName(pkChar->pSkeleton->Entry(), SetDir, CharName);
|
||||
if (pkChar->pSkin) ApplyGeneratedName(pkChar->pSkin->Entry(), SetDir, CharName);
|
||||
|
||||
if (pkChar->IceModel.IsValid() || pkChar->IceSkin.IsValid())
|
||||
{
|
||||
TWideString IceName = TWideString::Format(L"%s_IceOverlay", *CharName);
|
||||
|
||||
if (pkChar->IceModel.IsValid())
|
||||
{
|
||||
CResourceEntry *pIceModelEntry = pStore->FindEntry(pkChar->IceModel);
|
||||
ApplyGeneratedName(pIceModelEntry, SetDir, IceName);
|
||||
}
|
||||
if (pkChar->IceSkin.IsValid())
|
||||
{
|
||||
CResourceEntry *pIceSkinEntry = pStore->FindEntry(pkChar->IceSkin);
|
||||
ApplyGeneratedName(pIceSkinEntry, SetDir, IceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!NewSetName.IsEmpty())
|
||||
ApplyGeneratedName(*It, SetDir, NewSetName);
|
||||
|
||||
std::set<CAnimPrimitive> AnimPrimitives;
|
||||
pSet->GetUniquePrimitives(AnimPrimitives);
|
||||
|
||||
for (auto It = AnimPrimitives.begin(); It != AnimPrimitives.end(); It++)
|
||||
{
|
||||
const CAnimPrimitive& rkPrim = *It;
|
||||
CAnimation *pAnim = rkPrim.Animation();
|
||||
|
||||
if (pAnim)
|
||||
{
|
||||
ApplyGeneratedName(pAnim->Entry(), SetDir, rkPrim.Name().ToUTF16());
|
||||
CAnimEventData *pEvents = pAnim->EventData();
|
||||
|
||||
if (pEvents)
|
||||
ApplyGeneratedName(pEvents->Entry(), SetDir, rkPrim.Name().ToUTF16());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PROCESS_STRINGS
|
||||
// Generate string names
|
||||
for (TResourceIterator<eStringTable> It(pStore); It; ++It)
|
||||
{
|
||||
CStringTable *pString = (CStringTable*) It->Load();
|
||||
if (pString->Entry()->IsNamed()) continue;
|
||||
TWideString String;
|
||||
|
||||
for (u32 iStr = 0; iStr < pString->NumStrings() && String.IsEmpty(); iStr++)
|
||||
String = CStringTable::StripFormatting( pString->String("ENGL", iStr) ).Trimmed();
|
||||
|
||||
if (!String.IsEmpty())
|
||||
{
|
||||
TWideString Name = String.SubString(0, Math::Min<u32>(String.Size(), 50)).Trimmed();
|
||||
Name.Replace(L"\n", L" ");
|
||||
|
||||
while (Name.EndsWith(L".") || TWideString::IsWhitespace(Name.Back()))
|
||||
Name = Name.ChopBack(1);
|
||||
|
||||
ApplyGeneratedName(pString->Entry(), pString->Entry()->DirectoryPath(), Name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PROCESS_SCANS
|
||||
// Generate scan names
|
||||
for (TResourceIterator<eScan> It(pStore); It; ++It)
|
||||
{
|
||||
if (It->IsNamed()) continue;
|
||||
CScan *pScan = (CScan*) It->Load();
|
||||
TWideString ScanName;
|
||||
|
||||
if (pProj->Game() >= eEchoesDemo)
|
||||
{
|
||||
CAssetID DisplayAsset = pScan->LogbookDisplayAssetID();
|
||||
CResourceEntry *pEntry = pStore->FindEntry(DisplayAsset);
|
||||
if (pEntry && pEntry->IsNamed()) ScanName = pEntry->Name();
|
||||
}
|
||||
|
||||
if (ScanName.IsEmpty())
|
||||
{
|
||||
CStringTable *pString = pScan->ScanText();
|
||||
if (pString) ScanName = pString->Entry()->Name();
|
||||
}
|
||||
|
||||
ApplyGeneratedName(pScan->Entry(), It->DirectoryPath(), ScanName);
|
||||
|
||||
if (!ScanName.IsEmpty() && pProj->Game() <= ePrime)
|
||||
{
|
||||
CAssetID FrameID = pScan->GuiFrame();
|
||||
CResourceEntry *pEntry = pStore->FindEntry(FrameID);
|
||||
if (pEntry) ApplyGeneratedName(pEntry, pEntry->DirectoryPath(), L"ScanFrame");
|
||||
|
||||
for (u32 iImg = 0; iImg < 4; iImg++)
|
||||
{
|
||||
CAssetID ImageID = pScan->ScanImage(iImg);
|
||||
CResourceEntry *pImgEntry = pStore->FindEntry(ImageID);
|
||||
if (pImgEntry) ApplyGeneratedName(pImgEntry, pImgEntry->DirectoryPath(), TWideString::Format(L"%s_Image%d", *ScanName, iImg));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PROCESS_FONTS
|
||||
// Generate font names
|
||||
for (TResourceIterator<eFont> It(pStore); It; ++It)
|
||||
{
|
||||
CFont *pFont = (CFont*) It->Load();
|
||||
|
||||
if (pFont)
|
||||
{
|
||||
ApplyGeneratedName(pFont->Entry(), pFont->Entry()->DirectoryPath(), pFont->FontName().ToUTF16());
|
||||
|
||||
CTexture *pFontTex = pFont->Texture();
|
||||
|
||||
if (pFontTex)
|
||||
ApplyGeneratedName(pFontTex->Entry(), pFont->Entry()->DirectoryPath(), pFont->Entry()->Name());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
pStore->ConditionalSaveStore();
|
||||
}
|
||||
8
src/Core/GameProject/AssetNameGeneration.h
Normal file
8
src/Core/GameProject/AssetNameGeneration.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef ASSETNAMEGENERATION
|
||||
#define ASSETNAMEGENERATION
|
||||
|
||||
class CGameProject;
|
||||
void GenerateAssetNames(CGameProject *pProj);
|
||||
|
||||
#endif // ASSETNAMEGENERATION
|
||||
|
||||
95
src/Core/GameProject/CAssetNameMap.h
Normal file
95
src/Core/GameProject/CAssetNameMap.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#ifndef CASSETNAMEMAP
|
||||
#define CASSETNAMEMAP
|
||||
|
||||
#include "CResourceIterator.h"
|
||||
#include "CResourceStore.h"
|
||||
#include <Common/CAssetID.h>
|
||||
#include <Common/Serialization/XML.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
const TString gkAssetListDir = "..\\resources\\list\\";
|
||||
|
||||
struct SAssetNameInfo
|
||||
{
|
||||
TWideString Name;
|
||||
TWideString Directory;
|
||||
|
||||
void Serialize(IArchive& rArc)
|
||||
{
|
||||
rArc << SERIAL_AUTO(Name) << SERIAL_AUTO(Directory);
|
||||
}
|
||||
};
|
||||
|
||||
class CAssetNameMap
|
||||
{
|
||||
typedef std::map<CAssetID, SAssetNameInfo> TAssetMap;
|
||||
std::shared_ptr<TAssetMap> mpMap;
|
||||
|
||||
void Serialize(IArchive& rArc)
|
||||
{
|
||||
rArc << SERIAL_CONTAINER("AssetNameMap", *mpMap.get(), "Asset");
|
||||
}
|
||||
|
||||
public:
|
||||
CAssetNameMap()
|
||||
{
|
||||
mpMap = std::make_shared<TAssetMap>(TAssetMap());
|
||||
}
|
||||
|
||||
void GetNameInfo(CAssetID ID, TString& rOutDirectory, TString& rOutName)
|
||||
{
|
||||
auto It = mpMap->find(ID);
|
||||
|
||||
if (It != mpMap->end())
|
||||
{
|
||||
SAssetNameInfo& rInfo = It->second;
|
||||
rOutName = rInfo.Name;
|
||||
rOutDirectory = rInfo.Directory;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
rOutDirectory = "Uncategorized\\";
|
||||
rOutName = ID.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
static TString GetAssetListPath(EGame Game)
|
||||
{
|
||||
return gkAssetListDir + "AssetList" + GetGameShortName(Game) + ".xml";
|
||||
}
|
||||
|
||||
static CAssetNameMap LoadAssetNames(EGame Game)
|
||||
{
|
||||
TString ListPath = GetAssetListPath(Game);
|
||||
CXMLReader Reader(ListPath);
|
||||
|
||||
CAssetNameMap Map;
|
||||
Map.Serialize(Reader);
|
||||
return Map;
|
||||
}
|
||||
|
||||
static void SaveAssetNames(CResourceStore *pStore = gpResourceStore)
|
||||
{
|
||||
CAssetNameMap Map;
|
||||
|
||||
for (CResourceIterator It(pStore); It; ++It)
|
||||
{
|
||||
if (It->IsCategorized() || It->IsNamed())
|
||||
{
|
||||
CAssetID ID = It->ID();
|
||||
TWideString Name = It->Name();
|
||||
TWideString Directory = It->Directory()->FullPath();
|
||||
(*Map.mpMap)[ID] = SAssetNameInfo { Name, Directory };
|
||||
}
|
||||
}
|
||||
|
||||
TString ListPath = GetAssetListPath(pStore->Game());
|
||||
CXMLWriter Writer(ListPath, "AssetList", 0, pStore->Game());
|
||||
Map.Serialize(Writer);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CASSETNAMEMAP
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#define COPY_DISC_DATA 1
|
||||
#define LOAD_PAKS 1
|
||||
#define SAVE_PACKAGE_DEFINITIONS 1
|
||||
#define EXPORT_WORLDS 1
|
||||
#define USE_ASSET_NAME_MAP 0
|
||||
#define EXPORT_COOKED 1
|
||||
|
||||
CGameExporter::CGameExporter(const TString& rkInputDir, const TString& rkOutputDir)
|
||||
@@ -50,13 +50,15 @@ bool CGameExporter::Export()
|
||||
mContentDir = mpStore->RawDir(false);
|
||||
mCookedDir = mpStore->CookedDir(false);
|
||||
|
||||
#if USE_ASSET_NAME_MAP
|
||||
mNameMap = CAssetNameMap::LoadAssetNames(mGame);
|
||||
#endif
|
||||
|
||||
// Export game data
|
||||
CResourceStore *pOldStore = gpResourceStore;
|
||||
gpResourceStore = mpStore;
|
||||
|
||||
LoadAssetList();
|
||||
LoadPaks();
|
||||
ExportWorlds();
|
||||
ExportCookedResources();
|
||||
mpProject->AudioManager()->LoadAssets();
|
||||
ExportResourceEditorData();
|
||||
@@ -128,57 +130,6 @@ void CGameExporter::CopyDiscData()
|
||||
ASSERT(mGame != eUnknownGame);
|
||||
}
|
||||
|
||||
void CGameExporter::LoadAssetList()
|
||||
{
|
||||
SCOPED_TIMER(LoadAssetList);
|
||||
|
||||
// Determine the asset list to use
|
||||
TString ListFile = "../resources/list/AssetList";
|
||||
|
||||
switch (mGame)
|
||||
{
|
||||
case ePrimeDemo: ListFile += "MP1Demo"; break;
|
||||
case ePrime: ListFile += "MP1"; break;
|
||||
case eEchoesDemo: ListFile += "MP2Demo"; break;
|
||||
case eEchoes: ListFile += "MP2"; break;
|
||||
case eCorruptionProto: ListFile += "MP3Proto"; break;
|
||||
case eCorruption: ListFile += "MP3"; break;
|
||||
case eReturns: ListFile += "DKCR"; break;
|
||||
default: ASSERT(false);
|
||||
}
|
||||
|
||||
ListFile += ".xml";
|
||||
|
||||
// Load list
|
||||
tinyxml2::XMLDocument List;
|
||||
List.LoadFile(*ListFile);
|
||||
|
||||
if (List.Error())
|
||||
{
|
||||
Log::Error("Couldn't open asset list: " + ListFile);
|
||||
return;
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement *pRoot = List.FirstChildElement("AssetList");
|
||||
tinyxml2::XMLElement *pAsset = pRoot->FirstChildElement("Asset");
|
||||
|
||||
while (pAsset)
|
||||
{
|
||||
CAssetID ResourceID = TString(pAsset->Attribute("ID")).ToInt64(16);
|
||||
|
||||
tinyxml2::XMLElement *pDir = pAsset->FirstChildElement("Dir");
|
||||
TString Dir = pDir ? pDir->GetText() : "";
|
||||
|
||||
tinyxml2::XMLElement *pName = pAsset->FirstChildElement("Name");
|
||||
TString Name = pName ? pName->GetText() : "";
|
||||
|
||||
if (!Dir.EndsWith("/") && !Dir.EndsWith("\\")) Dir.Append("\\");
|
||||
SetResourcePath(ResourceID, Dir.ToUTF16(), Name.ToUTF16());
|
||||
|
||||
pAsset = pAsset->NextSiblingElement("Asset");
|
||||
}
|
||||
}
|
||||
|
||||
// ************ RESOURCE LOADING ************
|
||||
void CGameExporter::LoadPaks()
|
||||
{
|
||||
@@ -221,7 +172,6 @@ void CGameExporter::LoadPaks()
|
||||
u32 NameLen = Pak.ReadLong();
|
||||
TString Name = Pak.ReadString(NameLen);
|
||||
pCollection->AddResource(Name, ResID, ResType);
|
||||
SetResourcePath(ResID, PakName + L"\\", Name.ToUTF16());
|
||||
}
|
||||
|
||||
u32 NumResources = Pak.ReadLong();
|
||||
@@ -296,7 +246,6 @@ void CGameExporter::LoadPaks()
|
||||
CFourCC ResType = Pak.ReadLong();
|
||||
CAssetID ResID(Pak, mGame);
|
||||
pCollection->AddResource(Name, ResID, ResType);
|
||||
SetResourcePath(ResID, PakName + L"\\", Name.ToUTF16());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,84 +397,6 @@ void CGameExporter::LoadResource(const SResourceInstance& rkResource, std::vecto
|
||||
}
|
||||
}
|
||||
|
||||
void CGameExporter::ExportWorlds()
|
||||
{
|
||||
#if EXPORT_WORLDS
|
||||
SCOPED_TIMER(ExportWorlds);
|
||||
|
||||
for (u32 iPak = 0; iPak < mpProject->NumPackages(); 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.
|
||||
TWideString PakPath = pPak->Path();
|
||||
TWideString GameWorldsDir = PakPath.GetParentDirectoryPath(L"Worlds", false);
|
||||
|
||||
if (!GameWorldsDir.IsEmpty())
|
||||
PakPath = FileUtil::MakeRelative(PakPath, GameWorldsDir);
|
||||
|
||||
// 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 = pCollection->ResourceByIndex(iRes);
|
||||
|
||||
if (rkRes.Type == "MLVL" && !rkRes.Name.EndsWith("NODEPEND"))
|
||||
{
|
||||
// Load world
|
||||
CWorld *pWorld = (CWorld*) mpStore->LoadResource(rkRes.ID, rkRes.Type);
|
||||
|
||||
if (!pWorld)
|
||||
{
|
||||
Log::Error("Couldn't load world " + rkRes.Name + " from package " + pPak->Name() + "; unable to export");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Export world
|
||||
TWideString Name = rkRes.Name.ToUTF16();
|
||||
TWideString WorldDir = mWorldsDirName + PakPath + FileUtil::SanitizeName(Name, true) + L"\\";
|
||||
FileUtil::CreateDirectory(mCookedDir + WorldDir);
|
||||
|
||||
SResourceInstance *pInst = FindResourceInstance(rkRes.ID);
|
||||
ASSERT(pInst != nullptr);
|
||||
|
||||
SetResourcePath(rkRes.ID, WorldDir, Name);
|
||||
ExportResource(*pInst);
|
||||
|
||||
// Export areas
|
||||
for (u32 iArea = 0; iArea < pWorld->NumAreas(); iArea++)
|
||||
{
|
||||
// Determine area names
|
||||
TWideString InternalAreaName = pWorld->AreaInternalName(iArea).ToUTF16();
|
||||
bool HasInternalName = !InternalAreaName.IsEmpty();
|
||||
if (!HasInternalName) InternalAreaName = TWideString::FromInt32(iArea, 2, 10);
|
||||
|
||||
TWideString GameAreaName;
|
||||
CStringTable *pTable = pWorld->AreaName(iArea);
|
||||
if (pTable) GameAreaName = pTable->String("ENGL", 0);
|
||||
if (GameAreaName.IsEmpty()) GameAreaName = InternalAreaName;
|
||||
|
||||
// Export area
|
||||
TWideString AreaDir = WorldDir + TWideString::FromInt32(iArea, 2, 10) + L"_" + FileUtil::SanitizeName(GameAreaName, true) + L"\\";
|
||||
FileUtil::CreateDirectory(mCookedDir + AreaDir);
|
||||
|
||||
CAssetID AreaID = pWorld->AreaResourceID(iArea);
|
||||
SResourceInstance *pInst = FindResourceInstance(AreaID);
|
||||
ASSERT(pInst != nullptr);
|
||||
|
||||
SetResourcePath(AreaID, AreaDir, GameAreaName);
|
||||
ExportResource(*pInst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mpStore->DestroyUnreferencedResources();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CGameExporter::ExportCookedResources()
|
||||
{
|
||||
{
|
||||
@@ -597,21 +468,10 @@ void CGameExporter::ExportResource(SResourceInstance& rRes)
|
||||
std::vector<u8> ResourceData;
|
||||
LoadResource(rRes, ResourceData);
|
||||
|
||||
// Determine output path
|
||||
SResourcePath *pPath = FindResourcePath(rRes.ResourceID);
|
||||
TWideString OutName, OutDir;
|
||||
|
||||
if (pPath)
|
||||
{
|
||||
OutName = pPath->Name;
|
||||
OutDir = pPath->Dir;
|
||||
}
|
||||
|
||||
if (OutName.IsEmpty()) OutName = rRes.ResourceID.ToString().ToUTF16();
|
||||
if (OutDir.IsEmpty()) OutDir = L"Uncategorized\\";
|
||||
|
||||
// Register resource and write to file
|
||||
CResourceEntry *pEntry = mpStore->RegisterResource(rRes.ResourceID, CResource::ResTypeForExtension(rRes.ResourceType), OutDir, OutName);
|
||||
TString Directory, Name;
|
||||
mNameMap.GetNameInfo(rRes.ResourceID, Directory, Name);
|
||||
CResourceEntry *pEntry = mpStore->RegisterResource(rRes.ResourceID, CResource::ResTypeForExtension(rRes.ResourceType), Directory, Name);
|
||||
|
||||
#if EXPORT_COOKED
|
||||
// Save cooked asset
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef CGAMEEXPORTER_H
|
||||
#define CGAMEEXPORTER_H
|
||||
|
||||
#include "CAssetNameMap.h"
|
||||
#include "CGameProject.h"
|
||||
#include "CResourceStore.h"
|
||||
#include <Common/CAssetID.h>
|
||||
@@ -28,6 +29,7 @@ class CGameExporter
|
||||
// Resources
|
||||
TWideStringList mPaks;
|
||||
std::map<CAssetID, bool> mAreaDuplicateMap;
|
||||
CAssetNameMap mNameMap;
|
||||
|
||||
struct SResourceInstance
|
||||
{
|
||||
@@ -41,13 +43,6 @@ class CGameExporter
|
||||
};
|
||||
std::map<CAssetID, SResourceInstance> mResourceMap;
|
||||
|
||||
struct SResourcePath
|
||||
{
|
||||
TWideString Dir;
|
||||
TWideString Name;
|
||||
};
|
||||
std::map<CAssetID, SResourcePath> mResourcePaths;
|
||||
|
||||
public:
|
||||
CGameExporter(const TString& rkInputDir, const TString& rkOutputDir);
|
||||
bool Export();
|
||||
@@ -55,10 +50,8 @@ public:
|
||||
|
||||
protected:
|
||||
void CopyDiscData();
|
||||
void LoadAssetList();
|
||||
void LoadPaks();
|
||||
void LoadResource(const SResourceInstance& rkResource, std::vector<u8>& rBuffer);
|
||||
void ExportWorlds();
|
||||
void ExportCookedResources();
|
||||
void ExportResourceEditorData();
|
||||
void ExportResource(SResourceInstance& rRes);
|
||||
@@ -70,18 +63,6 @@ protected:
|
||||
auto Found = mResourceMap.find(IntegralID);
|
||||
return (Found == mResourceMap.end() ? nullptr : &Found->second);
|
||||
}
|
||||
|
||||
inline SResourcePath* FindResourcePath(const CAssetID& rkID)
|
||||
{
|
||||
u64 IntegralID = rkID.ToLongLong();
|
||||
auto Found = mResourcePaths.find(IntegralID);
|
||||
return (Found == mResourcePaths.end() ? nullptr : &Found->second);
|
||||
}
|
||||
|
||||
inline void SetResourcePath(const CAssetID& rkID, const TWideString& rkDir, const TWideString& rkName)
|
||||
{
|
||||
mResourcePaths[rkID] = SResourcePath { rkDir, rkName };
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CGAMEEXPORTER_H
|
||||
|
||||
@@ -7,6 +7,8 @@ CGameProject *CGameProject::mspActiveProject = nullptr;
|
||||
|
||||
CGameProject::~CGameProject()
|
||||
{
|
||||
ASSERT(!mpResourceStore->IsDirty());
|
||||
|
||||
if (IsActive())
|
||||
mspActiveProject = nullptr;
|
||||
|
||||
|
||||
@@ -47,9 +47,9 @@ public:
|
||||
, mProjectRoot(rkProjRootDir)
|
||||
, mResourceDBPath(L"ResourceDB.rdb")
|
||||
{
|
||||
mProjectRoot.Replace(L"/", L"\\");
|
||||
mpResourceStore = new CResourceStore(this);
|
||||
mpAudioManager = new CAudioManager(this);
|
||||
mProjectRoot.Replace(L"/", L"\\");
|
||||
}
|
||||
|
||||
CGameProject(CGameExporter *pExporter, const TWideString& rkProjRootDir, EGame Game)
|
||||
@@ -58,9 +58,9 @@ public:
|
||||
, mProjectRoot(rkProjRootDir)
|
||||
, mResourceDBPath(L"ResourceDB.rdb")
|
||||
{
|
||||
mProjectRoot.Replace(L"/", L"\\");
|
||||
mpResourceStore = new CResourceStore(this, pExporter, L"Content\\", L"Cooked\\", Game);
|
||||
mpAudioManager = new CAudioManager(this);
|
||||
mProjectRoot.Replace(L"/", L"\\");
|
||||
}
|
||||
|
||||
~CGameProject();
|
||||
@@ -95,6 +95,4 @@ public:
|
||||
static inline CGameProject* ActiveProject() { return mspActiveProject; }
|
||||
};
|
||||
|
||||
extern CGameProject *gpProject;
|
||||
|
||||
#endif // CGAMEPROJECT_H
|
||||
|
||||
@@ -196,9 +196,10 @@ bool CResourceEntry::Save(bool SkipCacheSave /*= false*/)
|
||||
|
||||
// Resource has been saved, now update dependencies + cache file
|
||||
UpdateDependencies();
|
||||
mpStore->SetCacheDataDirty();
|
||||
|
||||
if (!SkipCacheSave)
|
||||
mpStore->SaveCacheFile();
|
||||
mpStore->ConditionalSaveStore();
|
||||
|
||||
if (ShouldCollectGarbage)
|
||||
mpStore->DestroyUnreferencedResources();
|
||||
@@ -277,40 +278,92 @@ bool CResourceEntry::Unload()
|
||||
return true;
|
||||
}
|
||||
|
||||
void CResourceEntry::Move(const TWideString& rkDir, const TWideString& rkName)
|
||||
bool CResourceEntry::CanMoveTo(const TWideString& rkDir, const TWideString& rkName)
|
||||
{
|
||||
// Transient resources can't be moved
|
||||
if (IsTransient()) return false;
|
||||
|
||||
// Validate that the path/name are valid file paths
|
||||
if (!FileUtil::IsValidPath(rkDir, true) || !FileUtil::IsValidName(rkName, false)) return false;
|
||||
|
||||
// We need to validate the path isn't taken already - either the directory doesn't exist, or doesn't have a resource by this name
|
||||
CVirtualDirectory *pDir = mpStore->GetVirtualDirectory(rkDir, false, false);
|
||||
if (pDir && pDir->FindChildResource(rkName, mType)) return false;
|
||||
|
||||
// All checks are true
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CResourceEntry::Move(const TWideString& rkDir, const TWideString& rkName)
|
||||
{
|
||||
if (!CanMoveTo(rkDir, rkName)) return false;
|
||||
|
||||
// Store old paths
|
||||
CVirtualDirectory *pOldDir = mpDirectory;
|
||||
TWideString OldName = mName;
|
||||
TString OldCookedPath = CookedAssetPath();
|
||||
TString OldRawPath = RawAssetPath();
|
||||
|
||||
// Set new directory and name
|
||||
bool HasDirectory = mpDirectory != nullptr;
|
||||
CVirtualDirectory *pNewDir = mpStore->GetVirtualDirectory(rkDir, IsTransient(), true);
|
||||
if (pNewDir == mpDirectory && rkName == mName) return false;
|
||||
|
||||
if (pNewDir != mpDirectory)
|
||||
// Check if we can legally move to this spot
|
||||
ASSERT(pNewDir->FindChildResource(rkName, mType) == nullptr); // this check should be guaranteed to pass due to CanMoveTo() having already checked it
|
||||
|
||||
mpDirectory = pNewDir;
|
||||
mName = rkName;
|
||||
TString NewCookedPath = CookedAssetPath();
|
||||
TString NewRawPath = RawAssetPath();
|
||||
|
||||
// If the old/new paths are the same then we should have already exited as CanMoveTo() should have returned false
|
||||
ASSERT(OldCookedPath != NewCookedPath && OldRawPath != NewRawPath);
|
||||
|
||||
// The cooked/raw asset paths should not exist right now!!!
|
||||
bool FSMoveSuccess = false;
|
||||
|
||||
if (!HasRawVersion() && !HasCookedVersion())
|
||||
{
|
||||
if (mpDirectory)
|
||||
mpDirectory->RemoveChildResource(this);
|
||||
mpDirectory = pNewDir;
|
||||
FSMoveSuccess = true;
|
||||
|
||||
if (FileUtil::Exists(OldRawPath))
|
||||
{
|
||||
FSMoveSuccess = FileUtil::CopyFile(OldRawPath, NewRawPath);
|
||||
if (!FSMoveSuccess) FileUtil::DeleteFile(NewRawPath);
|
||||
}
|
||||
|
||||
if (FSMoveSuccess && FileUtil::Exists(OldCookedPath))
|
||||
{
|
||||
FSMoveSuccess = FileUtil::CopyFile(OldCookedPath, NewCookedPath);
|
||||
if (!FSMoveSuccess) FileUtil::DeleteFile(NewCookedPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (mName != rkName)
|
||||
ASSERT(mpDirectory->FindChildResource(rkName) == nullptr);
|
||||
|
||||
mName = rkName;
|
||||
mCachedUppercaseName = rkName.ToUpper();
|
||||
|
||||
// Move files
|
||||
if (HasDirectory)
|
||||
// If we succeeded, finish the move
|
||||
if (FSMoveSuccess)
|
||||
{
|
||||
TString CookedPath = CookedAssetPath();
|
||||
TString RawPath = RawAssetPath();
|
||||
if (mpDirectory != pOldDir)
|
||||
{
|
||||
FSMoveSuccess = pOldDir->RemoveChildResource(this);
|
||||
ASSERT(FSMoveSuccess == true); // this shouldn't be able to fail
|
||||
mpDirectory->AddChild(L"", this);
|
||||
mpStore->ConditionalDeleteDirectory(pOldDir);
|
||||
}
|
||||
|
||||
if (FileUtil::Exists(OldCookedPath) && CookedPath != OldCookedPath)
|
||||
FileUtil::MoveFile(OldCookedPath, CookedPath);
|
||||
mpStore->SetDatabaseDirty();
|
||||
mCachedUppercaseName = rkName.ToUpper();
|
||||
FileUtil::DeleteFile(OldRawPath);
|
||||
FileUtil::DeleteFile(OldCookedPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (FileUtil::Exists(OldRawPath) && RawPath != OldRawPath)
|
||||
FileUtil::MoveFile(OldRawPath, RawPath);
|
||||
// Otherwise, revert changes and let the caller know the move failed
|
||||
else
|
||||
{
|
||||
mpDirectory = pOldDir;
|
||||
mName = OldName;
|
||||
mpStore->ConditionalDeleteDirectory(pNewDir);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,13 @@ class CDependencyTree;
|
||||
|
||||
enum EResEntryFlag
|
||||
{
|
||||
eREF_NeedsRecook = 0x1, // Resource has been updated but not recooked
|
||||
eREF_Transient = 0x2, // Resource is transient (not part of a game project resource DB)
|
||||
eREF_HasThumbnail = 0x4, // Resource has a unique thumbnail
|
||||
eREF_ThumbnailLoaded = 0x8, // Resource thumbnail is currently in memory
|
||||
eREF_NeedsRecook = 0x00000001, // Resource has been updated but not recooked
|
||||
eREF_Transient = 0x00000002, // Resource is transient (not part of a game project resource DB)
|
||||
eREF_HasThumbnail = 0x00000004, // Resource has a unique thumbnail
|
||||
eREF_ThumbnailLoaded = 0x00000008, // Resource thumbnail is currently in memory
|
||||
eREF_Hidden = 0x00000010, // Resource is hidden, doesn't show up in resource browser
|
||||
// Flags that save to the cache file
|
||||
eREF_SavedFlags = eREF_NeedsRecook | eREF_HasThumbnail
|
||||
eREF_SavedFlags = eREF_NeedsRecook | eREF_HasThumbnail | eREF_Hidden
|
||||
};
|
||||
DECLARE_FLAGS(EResEntryFlag, FResEntryFlags)
|
||||
|
||||
@@ -61,24 +62,30 @@ public:
|
||||
CResource* Load();
|
||||
CResource* LoadCooked(IInputStream& rInput);
|
||||
bool Unload();
|
||||
void Move(const TWideString& rkDir, const TWideString& rkName);
|
||||
bool CanMoveTo(const TWideString& rkDir, const TWideString& rkName);
|
||||
bool Move(const TWideString& rkDir, const TWideString& rkName);
|
||||
void AddToProject(const TWideString& rkDir, const TWideString& rkName);
|
||||
void RemoveFromProject();
|
||||
|
||||
// Accessors
|
||||
void SetDirty() { mFlags.SetFlag(eREF_NeedsRecook); }
|
||||
void SetHidden(bool Hidden) { Hidden ? mFlags.SetFlag(eREF_Hidden) : mFlags.ClearFlag(eREF_Hidden); }
|
||||
|
||||
inline bool IsLoaded() const { return mpResource != nullptr; }
|
||||
inline bool IsCategorized() const { return mpDirectory && mpDirectory->FullPath() != L"Uncategorized\\"; }
|
||||
inline bool IsNamed() const { return mName != mID.ToString().ToUTF16(); }
|
||||
inline CResource* Resource() const { return mpResource; }
|
||||
inline CResourceStore* ResourceStore() const { return mpStore; }
|
||||
inline CDependencyTree* Dependencies() const { return mpDependencies; }
|
||||
inline CAssetID ID() const { return mID; }
|
||||
inline EGame Game() const { return mGame; }
|
||||
inline CVirtualDirectory* Directory() const { return mpDirectory; }
|
||||
inline TWideString DirectoryPath() const { return mpDirectory->FullPath(); }
|
||||
inline TWideString Name() const { return mName; }
|
||||
inline const TWideString& UppercaseName() const { return mCachedUppercaseName; }
|
||||
inline EResType ResourceType() const { return mType; }
|
||||
inline bool IsTransient() const { return mFlags.HasFlag(eREF_Transient); }
|
||||
inline bool IsHidden() const { return mFlags.HasFlag(eREF_Hidden); }
|
||||
|
||||
protected:
|
||||
CResource* InternalLoad(IInputStream& rInput);
|
||||
|
||||
@@ -66,14 +66,14 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template<class ResType>
|
||||
template<EResType ResType>
|
||||
class TResourceIterator : public CResourceIterator
|
||||
{
|
||||
public:
|
||||
TResourceIterator(CResourceStore *pStore = gpResourceStore)
|
||||
: CResourceIterator(pStore)
|
||||
{
|
||||
if (mpCurEntry->ResourceType() != ResType::StaticType())
|
||||
if (mpCurEntry->ResourceType() != ResType)
|
||||
Next();
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
{
|
||||
do {
|
||||
CResourceIterator::Next();
|
||||
} while (mpCurEntry && mpCurEntry->ResourceType() != ResType::StaticType());
|
||||
} while (mpCurEntry && mpCurEntry->ResourceType() != ResType);
|
||||
|
||||
return mpCurEntry;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ CResourceStore::CResourceStore(const TWideString& rkDatabasePath)
|
||||
: mpProj(nullptr)
|
||||
, mGame(eUnknownGame)
|
||||
, mpExporter(nullptr)
|
||||
, mDatabaseDirty(false)
|
||||
, mCacheFileDirty(false)
|
||||
{
|
||||
mpDatabaseRoot = new CVirtualDirectory();
|
||||
mDatabasePath = FileUtil::MakeAbsolute(rkDatabasePath.GetFileDirectory());
|
||||
@@ -30,6 +32,8 @@ CResourceStore::CResourceStore(CGameProject *pProject, CGameExporter *pExporter,
|
||||
, mRawDir(rkRawDir)
|
||||
, mCookedDir(rkCookedDir)
|
||||
, mpExporter(pExporter)
|
||||
, mDatabaseDirty(false)
|
||||
, mCacheFileDirty(false)
|
||||
{
|
||||
SetProject(pProject);
|
||||
}
|
||||
@@ -39,6 +43,8 @@ CResourceStore::CResourceStore(CGameProject *pProject)
|
||||
, mGame(eUnknownGame)
|
||||
, mpDatabaseRoot(nullptr)
|
||||
, mpExporter(nullptr)
|
||||
, mDatabaseDirty(false)
|
||||
, mCacheFileDirty(false)
|
||||
{
|
||||
SetProject(pProject);
|
||||
}
|
||||
@@ -122,6 +128,7 @@ void CResourceStore::SaveResourceDatabase()
|
||||
TString Path = DatabasePath().ToUTF8();
|
||||
CXMLWriter Writer(Path, "ResourceDB", 0, mGame);
|
||||
SerializeResourceDatabase(Writer);
|
||||
mDatabaseDirty = false;
|
||||
}
|
||||
|
||||
void CResourceStore::LoadCacheFile()
|
||||
@@ -208,6 +215,13 @@ void CResourceStore::SaveCacheFile()
|
||||
|
||||
CacheFile.Seek(ResCountOffset, SEEK_SET);
|
||||
CacheFile.WriteLong(ResCount);
|
||||
mCacheFileDirty = false;
|
||||
}
|
||||
|
||||
void CResourceStore::ConditionalSaveStore()
|
||||
{
|
||||
if (mDatabaseDirty) SaveResourceDatabase();
|
||||
if (mCacheFileDirty) SaveCacheFile();
|
||||
}
|
||||
|
||||
void CResourceStore::SetProject(CGameProject *pProj)
|
||||
@@ -298,6 +312,21 @@ CVirtualDirectory* CResourceStore::GetVirtualDirectory(const TWideString& rkPath
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
void CResourceStore::ConditionalDeleteDirectory(CVirtualDirectory *pDir)
|
||||
{
|
||||
if (pDir->IsEmpty())
|
||||
{
|
||||
// If this directory is part of the project, then we should delete the corresponding filesystem directories
|
||||
if (pDir->GetRoot() == mpDatabaseRoot)
|
||||
{
|
||||
FileUtil::DeleteDirectory(RawDir(false) + pDir->FullPath());
|
||||
FileUtil::DeleteDirectory(CookedDir(false) + pDir->FullPath());
|
||||
}
|
||||
|
||||
pDir->Parent()->RemoveChildDirectory(pDir);
|
||||
}
|
||||
}
|
||||
|
||||
CResourceEntry* CResourceStore::FindEntry(const CAssetID& rkID) const
|
||||
{
|
||||
if (!rkID.IsValid()) return nullptr;
|
||||
@@ -316,7 +345,7 @@ bool CResourceStore::IsResourceRegistered(const CAssetID& rkID) const
|
||||
return FindEntry(rkID) != nullptr;
|
||||
}
|
||||
|
||||
CResourceEntry* CResourceStore::RegisterResource(const CAssetID& rkID, EResType Type, const TWideString& rkDir, const TWideString& rkFileName)
|
||||
CResourceEntry* CResourceStore::RegisterResource(const CAssetID& rkID, EResType Type, const TWideString& rkDir, const TWideString& rkName)
|
||||
{
|
||||
CResourceEntry *pEntry = FindEntry(rkID);
|
||||
|
||||
@@ -325,16 +354,16 @@ CResourceEntry* CResourceStore::RegisterResource(const CAssetID& rkID, EResType
|
||||
if (pEntry->IsTransient())
|
||||
{
|
||||
ASSERT(pEntry->ResourceType() == Type);
|
||||
pEntry->AddToProject(rkDir, rkFileName);
|
||||
pEntry->AddToProject(rkDir, rkName);
|
||||
}
|
||||
|
||||
else
|
||||
Log::Error("Attempted to register resource that's already tracked in the database: " + rkID.ToString() + " / " + rkDir.ToUTF8() + " / " + rkFileName.ToUTF8());
|
||||
Log::Error("Attempted to register resource that's already tracked in the database: " + rkID.ToString() + " / " + rkDir.ToUTF8() + " / " + rkName.ToUTF8());
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
pEntry = new CResourceEntry(this, rkID, rkDir, rkFileName.GetFileName(false), Type);
|
||||
pEntry = new CResourceEntry(this, rkID, rkDir, rkName, Type);
|
||||
mResourceEntries[rkID] = pEntry;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ class CResourceStore
|
||||
std::vector<CVirtualDirectory*> mTransientRoots;
|
||||
std::map<CAssetID, CResourceEntry*> mResourceEntries;
|
||||
std::map<CAssetID, CResourceEntry*> mLoadedResources;
|
||||
bool mDatabaseDirty;
|
||||
bool mCacheFileDirty;
|
||||
|
||||
// Directory paths
|
||||
TWideString mDatabasePath;
|
||||
@@ -54,12 +56,14 @@ public:
|
||||
void SaveResourceDatabase();
|
||||
void LoadCacheFile();
|
||||
void SaveCacheFile();
|
||||
void ConditionalSaveStore();
|
||||
void SetProject(CGameProject *pProj);
|
||||
void CloseProject();
|
||||
CVirtualDirectory* GetVirtualDirectory(const TWideString& rkPath, bool Transient, bool AllowCreate);
|
||||
void ConditionalDeleteDirectory(CVirtualDirectory *pDir);
|
||||
|
||||
bool IsResourceRegistered(const CAssetID& rkID) const;
|
||||
CResourceEntry* RegisterResource(const CAssetID& rkID, EResType Type, const TWideString& rkDir, const TWideString& rkFileName);
|
||||
CResourceEntry* RegisterResource(const CAssetID& rkID, EResType Type, const TWideString& rkDir, const TWideString& rkName);
|
||||
CResourceEntry* FindEntry(const CAssetID& rkID) const;
|
||||
CResourceEntry* FindEntry(const TWideString& rkPath) const;
|
||||
CResourceEntry* RegisterTransientResource(EResType Type, const TWideString& rkDir = L"", const TWideString& rkFileName = L"");
|
||||
@@ -84,6 +88,10 @@ public:
|
||||
inline CVirtualDirectory* RootDirectory() const { return mpDatabaseRoot; }
|
||||
inline u32 NumTotalResources() const { return mResourceEntries.size(); }
|
||||
inline u32 NumLoadedResources() const { return mLoadedResources.size(); }
|
||||
inline bool IsDirty() const { return mDatabaseDirty || mCacheFileDirty; }
|
||||
|
||||
inline void SetDatabaseDirty() { mDatabaseDirty = true; }
|
||||
inline void SetCacheDataDirty() { mCacheFileDirty = true; }
|
||||
};
|
||||
|
||||
extern CResourceStore *gpResourceStore;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "CVirtualDirectory.h"
|
||||
#include "CResourceEntry.h"
|
||||
#include "CResourceStore.h"
|
||||
#include "Core/Resource/CResource.h"
|
||||
#include <algorithm>
|
||||
|
||||
CVirtualDirectory::CVirtualDirectory()
|
||||
@@ -33,7 +34,7 @@ bool CVirtualDirectory::IsEmpty() const
|
||||
|
||||
TWideString CVirtualDirectory::FullPath() const
|
||||
{
|
||||
return (mpParent && !mpParent->IsRoot() ? mpParent->FullPath() + L'\\' + mName + L"\\" : mName + L"\\");
|
||||
return (mpParent && !mpParent->IsRoot() ? mpParent->FullPath() + mName + L'\\' : mName + L'\\');
|
||||
}
|
||||
|
||||
CVirtualDirectory* CVirtualDirectory::GetRoot()
|
||||
@@ -50,7 +51,7 @@ CVirtualDirectory* CVirtualDirectory::FindChildDirectory(const TWideString& rkNa
|
||||
{
|
||||
CVirtualDirectory *pChild = mSubdirectories[iSub];
|
||||
|
||||
if (pChild->Name() == DirName)
|
||||
if (pChild->Name().CaseInsensitiveCompare(DirName))
|
||||
{
|
||||
if (SlashIdx == -1)
|
||||
return pChild;
|
||||
@@ -81,7 +82,7 @@ CVirtualDirectory* CVirtualDirectory::FindChildDirectory(const TWideString& rkNa
|
||||
CResourceEntry* CVirtualDirectory::FindChildResource(const TWideString& rkPath)
|
||||
{
|
||||
TWideString Dir = rkPath.GetFileDirectory();
|
||||
TWideString Name = rkPath.GetFileName(false);
|
||||
TWideString Name = rkPath.GetFileName();
|
||||
|
||||
if (!Dir.IsEmpty())
|
||||
{
|
||||
@@ -89,13 +90,22 @@ CResourceEntry* CVirtualDirectory::FindChildResource(const TWideString& rkPath)
|
||||
if (pDir) return pDir->FindChildResource(Name);
|
||||
}
|
||||
|
||||
else
|
||||
else if (!Name.IsEmpty())
|
||||
{
|
||||
for (u32 iRes = 0; iRes < mResources.size(); iRes++)
|
||||
{
|
||||
if (mResources[iRes]->Name() == Name)
|
||||
return mResources[iRes];
|
||||
}
|
||||
TWideString Ext = Name.GetFileExtension();
|
||||
EResType Type = CResource::ResTypeForExtension(Ext);
|
||||
return FindChildResource(Name.GetFileName(false), Type);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CResourceEntry* CVirtualDirectory::FindChildResource(const TWideString& rkName, EResType Type)
|
||||
{
|
||||
for (u32 iRes = 0; iRes < mResources.size(); iRes++)
|
||||
{
|
||||
if (rkName.CaseInsensitiveCompare(mResources[iRes]->Name()) && mResources[iRes]->ResourceType() == Type)
|
||||
return mResources[iRes];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@@ -168,10 +178,6 @@ bool CVirtualDirectory::RemoveChildResource(CResourceEntry *pEntry)
|
||||
if (*It == pEntry)
|
||||
{
|
||||
mResources.erase(It);
|
||||
|
||||
if (mpParent && IsEmpty())
|
||||
mpParent->RemoveChildDirectory(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define CVIRTUALDIRECTORY
|
||||
|
||||
/* Virtual directory system used to look up resources by their location in the filesystem. */
|
||||
#include "Core/Resource/EResType.h"
|
||||
#include <Common/AssertMacro.h>
|
||||
#include <Common/TString.h>
|
||||
#include <vector>
|
||||
@@ -26,6 +27,7 @@ public:
|
||||
CVirtualDirectory* GetRoot();
|
||||
CVirtualDirectory* FindChildDirectory(const TWideString& rkName, bool AllowCreate);
|
||||
CResourceEntry* FindChildResource(const TWideString& rkPath);
|
||||
CResourceEntry* FindChildResource(const TWideString& rkName, EResType Type);
|
||||
void AddChild(const TWideString& rkPath, CResourceEntry *pEntry);
|
||||
bool RemoveChildDirectory(CVirtualDirectory *pSubdir);
|
||||
bool RemoveChildResource(CResourceEntry *pEntry);
|
||||
|
||||
Reference in New Issue
Block a user