#include "CAssetNameMap.h" bool CAssetNameMap::LoadAssetNames(TString Path /*= ""*/) { if (Path.IsEmpty()) Path = DefaultNameMapPath(mIDLength); CXMLReader Reader(Path); if (Reader.IsValid()) { EIDLength FileIDLength = CAssetID::GameIDLength(Reader.Game()); if (FileIDLength == mIDLength) { Serialize(Reader); return true; } else { debugf("Failed to load asset names; expected %s IDs, got %s", mIDLength == k32Bit ? "32-bit" : "64-bit", FileIDLength == k32Bit ? "32-bit" : "64-bit" ); return false; } } else { errorf("Failed to load asset names; couldn't open XML."); return false; } } bool CAssetNameMap::SaveAssetNames(TString Path /*= ""*/) { if (Path.IsEmpty()) Path = DefaultNameMapPath(mIDLength); EGame Game = (mIDLength == k32Bit ? EGame::Prime : EGame::Corruption); CXMLWriter Writer(Path, "AssetNameMap", 0, Game); Serialize(Writer); return Writer.Save(); } bool CAssetNameMap::GetNameInfo(CAssetID ID, TString& rOutDirectory, TString& rOutName, bool& rOutAutoGenDir, bool& rOutAutoGenName) { auto It = mMap.find(ID); if (It != mMap.end()) { SAssetNameInfo& rInfo = It->second; rOutName = rInfo.Name; rOutDirectory = rInfo.Directory; rOutAutoGenDir = rInfo.AutoGenDir; rOutAutoGenName = rInfo.AutoGenName; return true; } else { EGame Game = (ID.Length() == k32Bit ? EGame::Prime : EGame::Corruption); rOutDirectory = CResourceStore::StaticDefaultResourceDirPath(Game); rOutName = ID.ToString(); rOutAutoGenDir = true; rOutAutoGenName = true; return false; } } void CAssetNameMap::CopyFromStore(CResourceStore *pStore /*= gpResourceStore*/) { // Do a first pass to remove old paths from used set to prevent false positives from eg. if two resources switch places ASSERT( CAssetID::GameIDLength(pStore->Game()) == mIDLength ); for (CResourceIterator It(pStore); It; ++It) { if (It->IsCategorized() || It->IsNamed()) { auto Find = mMap.find(It->ID()); if (Find != mMap.end()) { SAssetNameInfo& rInfo = Find->second; auto UsedFind = mUsedSet.find(rInfo); ASSERT(UsedFind != mUsedSet.end()); mUsedSet.erase(UsedFind); } } } // Do a second pass to add the new paths to the map for (CResourceIterator It(pStore); It; ++It) { if (It->IsCategorized() || It->IsNamed()) { CAssetID ID = It->ID(); ASSERT(ID.Length() == mIDLength); TString Name = It->Name(); TString Directory = It->Directory()->FullPath(); CFourCC Type = It->CookedExtension(); bool AutoName = It->HasFlag(EResEntryFlag::AutoResName); bool AutoDir = It->HasFlag(EResEntryFlag::AutoResDir); SAssetNameInfo NameInfo { Name, Directory, Type, AutoName, AutoDir }; // Check for conflicts with new name if (mUsedSet.find(NameInfo) != mUsedSet.end()) { SAssetNameInfo NewNameInfo = NameInfo; int NumConflicted = 0; while (mUsedSet.find(NewNameInfo) != mUsedSet.end()) { NewNameInfo.Name = NameInfo.Name + '_' + TString::FromInt32(NumConflicted, 0, 10); NumConflicted++; } TString OldPath = NameInfo.FullPath(); TString NewPath = NewNameInfo.FullPath(); warnf("Detected name conflict when copying asset name from the resource store; renaming."); warnf("\tOld Path: %s", *OldPath); warnf("\tNew Path: %s", *NewPath); NameInfo.Name = NewNameInfo.Name; } // Assign to map mMap[ID] = NameInfo; mUsedSet.insert(NameInfo); } } } // ************ PRIVATE ************ void CAssetNameMap::Serialize(IArchive& rArc) { rArc << SerialParameter("AssetNameMap", mMap); if (rArc.IsReader()) PostLoadValidate(); } void CAssetNameMap::PostLoadValidate() { // Make sure the newly loaded map doesn't contain any errors or name conflicts. bool FoundErrors = false; mIsValid = false; std::set Dupes; for (auto Iter = mMap.begin(); Iter != mMap.end(); Iter++) { const SAssetNameInfo& rkInfo = Iter->second; if (mUsedSet.find(rkInfo) != mUsedSet.end()) Dupes.insert(rkInfo); else { mUsedSet.insert(rkInfo); // Verify the name/path is valid if (!CResourceStore::IsValidResourcePath(rkInfo.Directory, rkInfo.Name)) { errorf("Invalid resource path in asset name map: %s%s.%s", *rkInfo.Directory, *rkInfo.Name, *rkInfo.Type.ToString()); Iter = mMap.erase(Iter); FoundErrors = true; } // Verify correct ID length if (Iter->first.Length() != mIDLength) { errorf("Incorrect asset ID length in asset name map: %s", *Iter->first.ToString()); Iter = mMap.erase(Iter); FoundErrors = true; } } } // If we detected any dupes, then this map can't be used if (!Dupes.empty()) { errorf("Asset name map is invalid and cannot be used! Duplicate asset entries detected:"); for (auto Iter = Dupes.begin(); Iter != Dupes.end(); Iter++) { warnf("\t%s", *Iter->FullPath()); } mMap.clear(); } else mIsValid = !FoundErrors; } TString CAssetNameMap::DefaultNameMapPath(EIDLength IDLength) { ASSERT(IDLength != kInvalidIDLength); TString Suffix = (IDLength == k32Bit ? "32" : "64"); return gDataDir + gkAssetMapPath + Suffix + "." + gkAssetMapExt; } TString CAssetNameMap::DefaultNameMapPath(EGame Game) { return DefaultNameMapPath( CAssetID::GameIDLength(Game) ); }