2024-06-24 18:16:10 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "CookMetadataFiles.h"
# include "AssetRegistry/AssetRegistryState.h"
# include "Containers/Array.h"
# include "Containers/UnrealString.h"
# include "CookMetadata.h"
2024-06-26 10:28:38 -04:00
# include "CookedPackageStore.h"
2024-06-24 18:16:10 -04:00
# include "HAL/FileManager.h"
2024-06-26 10:28:38 -04:00
# include "IO/IoBuffer.h"
2024-06-24 18:16:10 -04:00
# include "IO/IoStore.h"
2024-06-26 10:28:38 -04:00
# include "Memory/MemoryView.h"
2024-06-24 18:16:10 -04:00
# include "Misc/Paths.h"
# include "ProfilingDebugging/CountersTrace.h"
# include "Serialization/LargeMemoryReader.h"
2024-06-26 10:28:38 -04:00
# include "ZenStoreHttpClient.h"
2024-06-24 18:16:10 -04:00
// Returns the hash of the development asset registry or 0 on failure.
2024-06-26 10:28:38 -04:00
static uint64 LoadAssetRegistry ( FCookedPackageStore * InPackageStore , const FString & InAssetRegistryFileName , FIoChunkId InAssetRegistryChunkId , FAssetRegistryState & OutAssetRegistry )
2024-06-24 18:16:10 -04:00
{
FAssetRegistryVersion : : Type Version ;
FAssetRegistryLoadOptions Options ( UE : : AssetRegistry : : ESerializationTarget : : ForDevelopment ) ;
2024-06-26 10:28:38 -04:00
if ( InPackageStore & & InPackageStore - > HasZenStoreClient ( ) & & InAssetRegistryChunkId . IsValid ( ) )
2024-06-24 18:16:10 -04:00
{
2024-06-26 10:28:38 -04:00
FIoBuffer Buffer ;
Buffer = InPackageStore - > ReadChunk ( InAssetRegistryChunkId ) . ConsumeValueOrDie ( ) ;
uint64 DevArHash = UE : : Cook : : FCookMetadataState : : ComputeHashOfDevelopmentAssetRegistry ( Buffer . GetView ( ) ) ;
2024-06-24 18:16:10 -04:00
2024-06-26 10:28:38 -04:00
FLargeMemoryReader MemoryReader ( Buffer . GetData ( ) , Buffer . GetSize ( ) ) ;
2024-06-24 18:16:10 -04:00
if ( OutAssetRegistry . Load ( MemoryReader , Options , & Version ) )
{
return DevArHash ;
}
}
2024-06-26 10:28:38 -04:00
else
{
TUniquePtr < FArchive > FileReader ( IFileManager : : Get ( ) . CreateFileReader ( * InAssetRegistryFileName ) ) ;
if ( FileReader )
{
TArray64 < uint8 > Data ;
Data . SetNumUninitialized ( FileReader - > TotalSize ( ) ) ;
FileReader - > Serialize ( Data . GetData ( ) , Data . Num ( ) ) ;
check ( ! FileReader - > IsError ( ) ) ;
2024-06-24 18:16:10 -04:00
2024-06-26 10:28:38 -04:00
uint64 DevArHash = UE : : Cook : : FCookMetadataState : : ComputeHashOfDevelopmentAssetRegistry ( MakeMemoryView ( Data ) ) ;
FLargeMemoryReader MemoryReader ( Data . GetData ( ) , Data . Num ( ) ) ;
if ( OutAssetRegistry . Load ( MemoryReader , Options , & Version ) )
{
return DevArHash ;
}
}
}
return 0 ;
2024-06-24 18:16:10 -04:00
}
ECookMetadataFiles FindAndLoadMetadataFiles (
2024-06-26 10:28:38 -04:00
FCookedPackageStore * InPackageStore ,
2024-06-24 18:16:10 -04:00
const FString & InCookedDir , ECookMetadataFiles InRequiredFiles ,
FAssetRegistryState & OutAssetRegistry , FString * OutAssetRegistryFileName /*optional, set on success*/ ,
UE : : Cook : : FCookMetadataState * OutCookMetadata , FString * OutCookMetadataFileName /*optional, set on success or need*/ )
{
TRACE_CPUPROFILER_EVENT_SCOPE ( LoadingAssetRegistry ) ;
// Look for the development registry. Should be in \\GameName\\Metadata\\DevelopmentAssetRegistry.bin, but we don't know what "GameName" is.
TArray < FString > PossibleAssetRegistryFiles ;
IFileManager : : Get ( ) . FindFilesRecursive ( PossibleAssetRegistryFiles , * InCookedDir , GetDevelopmentAssetRegistryFilename ( ) , true , false ) ;
if ( PossibleAssetRegistryFiles . Num ( ) > 1 )
{
UE_LOG ( LogIoStore , Warning , TEXT ( " Found multiple possible development asset registries: " ) ) ;
for ( FString & Filename : PossibleAssetRegistryFiles )
{
UE_LOG ( LogIoStore , Warning , TEXT ( " %s " ) , * Filename ) ;
}
}
2024-06-26 10:28:38 -04:00
FIoChunkId AssetRegistryChunkId = FIoChunkId : : InvalidChunkId ;
if ( ( PossibleAssetRegistryFiles . Num ( ) = = 0 ) & & InPackageStore )
{
if ( UE : : FZenStoreHttpClient * ZenStoreClient = InPackageStore - > GetZenStoreClient ( ) )
{
TIoStatusOr < FCbObject > ProjectInfoStatus = ZenStoreClient - > GetProjectInfo ( ) . Get ( ) ;
if ( ProjectInfoStatus . IsOk ( ) )
{
FCbObject ProjectInfo = ProjectInfoStatus . ConsumeValueOrDie ( ) ;
FString ProjectFile ( ProjectInfo [ " projectfile " ] . AsString ( ) ) ;
FString ProjectName = FPaths : : GetBaseFilename ( ProjectFile ) ;
if ( ! ProjectName . IsEmpty ( ) )
{
FString CandidateAssetRegistryFilename = FPaths : : Combine ( InCookedDir , ProjectName , " Metadata " , GetDevelopmentAssetRegistryFilename ( ) ) ;
FPaths : : NormalizeFilename ( CandidateAssetRegistryFilename ) ;
AssetRegistryChunkId = InPackageStore - > GetChunkIdFromFileName ( CandidateAssetRegistryFilename ) ;
if ( AssetRegistryChunkId . IsValid ( ) )
{
PossibleAssetRegistryFiles . Add ( MoveTemp ( CandidateAssetRegistryFilename ) ) ;
}
}
}
}
}
2024-06-24 18:16:10 -04:00
if ( PossibleAssetRegistryFiles . Num ( ) = = 0 )
{
if ( EnumHasAnyFlags ( InRequiredFiles , ECookMetadataFiles : : AssetRegistry ) )
{
UE_LOG ( LogIoStore , Error , TEXT ( " No development asset registry file found! " ) ) ;
}
else
{
UE_LOG ( LogIoStore , Display , TEXT ( " No development asset registry file found! " ) ) ;
}
return ECookMetadataFiles : : None ;
}
2024-06-26 10:28:38 -04:00
FPaths : : NormalizeFilename ( PossibleAssetRegistryFiles [ 0 ] ) ;
2024-06-24 18:16:10 -04:00
UE_LOG ( LogIoStore , Display , TEXT ( " Using input asset registry: %s " ) , * PossibleAssetRegistryFiles [ 0 ] ) ;
2024-06-26 10:28:38 -04:00
uint64 LoadedDevArHash = LoadAssetRegistry ( InPackageStore , PossibleAssetRegistryFiles [ 0 ] , AssetRegistryChunkId , OutAssetRegistry ) ;
2024-06-24 18:16:10 -04:00
if ( LoadedDevArHash = = 0 )
{
return ECookMetadataFiles : : None ; // already logged
}
// If we found the asset registry, try and find the cook metadata that should be next to it.
ECookMetadataFiles ResultFiles = ECookMetadataFiles : : AssetRegistry ;
if ( OutCookMetadata )
{
// The cook metadata file should be adjacent to the development asset registry.
FString CookMetadataFileName = FPaths : : GetPath ( PossibleAssetRegistryFiles [ 0 ] ) / UE : : Cook : : GetCookMetadataFilename ( ) ;
2024-06-26 10:28:38 -04:00
auto ValidateAndOutputCookMetadata = [ InRequiredFiles , LoadedDevArHash , & CookMetadataFileName , & OutCookMetadata , & OutCookMetadataFileName , & ResultFiles ] ( )
2024-06-24 18:16:10 -04:00
{
2024-06-26 10:28:38 -04:00
if ( OutCookMetadata - > GetAssociatedDevelopmentAssetRegistryHash ( ) ! = LoadedDevArHash & &
2024-06-24 18:16:10 -04:00
OutCookMetadata - > GetAssociatedDevelopmentAssetRegistryHashPostWriteback ( ) ! = LoadedDevArHash ) // during testing we can repeat stage after cook so we might have already edited it.
{
if ( EnumHasAnyFlags ( InRequiredFiles , ECookMetadataFiles : : CookMetadata ) )
{
UE_LOG ( LogIoStore , Error ,
TEXT ( " Cook metadata file mismatch: Hash of associated development asset registry does not match. [%s] %llx vs %llx (%llx post writeback) " ) ,
* CookMetadataFileName , LoadedDevArHash , OutCookMetadata - > GetAssociatedDevelopmentAssetRegistryHash ( ) , OutCookMetadata - > GetAssociatedDevelopmentAssetRegistryHashPostWriteback ( ) ) ;
2024-06-26 10:28:38 -04:00
return false ;
2024-06-24 18:16:10 -04:00
}
else
{
UE_LOG ( LogIoStore , Display ,
TEXT ( " Cook metadata file mismatch: Hash of associated development asset registry does not match. [%s] %llx vs %llx (%llx post writeback) " ) ,
* CookMetadataFileName , LoadedDevArHash , OutCookMetadata - > GetAssociatedDevelopmentAssetRegistryHash ( ) , OutCookMetadata - > GetAssociatedDevelopmentAssetRegistryHashPostWriteback ( ) ) ;
OutCookMetadata - > Reset ( ) ;
}
}
else
{
EnumAddFlags ( ResultFiles , ECookMetadataFiles : : CookMetadata ) ;
if ( OutCookMetadataFileName )
{
* OutCookMetadataFileName = MoveTemp ( CookMetadataFileName ) ;
}
}
2024-06-26 10:28:38 -04:00
return true ;
} ;
if ( InPackageStore & & InPackageStore - > HasZenStoreClient ( ) & & AssetRegistryChunkId . IsValid ( ) )
{
FIoChunkId CookMetadataChunkId = InPackageStore - > GetChunkIdFromFileName ( CookMetadataFileName ) ;
if ( ! CookMetadataChunkId . IsValid ( ) )
{
UE_LOG ( LogIoStore , Error , TEXT ( " Failed to find cook metadata file - chunk missing from package store. [%s] " ) , * CookMetadataFileName ) ;
if ( EnumHasAnyFlags ( InRequiredFiles , ECookMetadataFiles : : CookMetadata ) )
{
return ECookMetadataFiles : : None ;
}
}
FIoBuffer Buffer ;
Buffer = InPackageStore - > ReadChunk ( CookMetadataChunkId ) . ConsumeValueOrDie ( ) ;
FLargeMemoryReader MemoryReader ( Buffer . GetData ( ) , Buffer . GetSize ( ) ) ;
if ( ! OutCookMetadata - > Serialize ( MemoryReader ) )
{
UE_LOG ( LogIoStore , Error , TEXT ( " Failed to deserialize cook metadata from package store (%s) " ) , * CookMetadataFileName ) ;
if ( EnumHasAnyFlags ( InRequiredFiles , ECookMetadataFiles : : CookMetadata ) )
{
return ECookMetadataFiles : : None ;
}
}
else if ( ! ValidateAndOutputCookMetadata ( ) )
{
return ECookMetadataFiles : : None ;
}
}
else if ( IFileManager : : Get ( ) . FileExists ( * CookMetadataFileName ) )
{
if ( ! OutCookMetadata - > ReadFromFile ( CookMetadataFileName ) )
{
UE_LOG ( LogIoStore , Error , TEXT ( " Failed to deserialize cook metadata file - invalid data. [%s] " ) , * CookMetadataFileName ) ;
if ( EnumHasAnyFlags ( InRequiredFiles , ECookMetadataFiles : : CookMetadata ) )
{
return ECookMetadataFiles : : None ;
}
}
else if ( ! ValidateAndOutputCookMetadata ( ) )
{
return ECookMetadataFiles : : None ;
}
2024-06-24 18:16:10 -04:00
}
else
{
if ( EnumHasAnyFlags ( InRequiredFiles , ECookMetadataFiles : : CookMetadata ) )
{
UE_LOG ( LogIoStore , Error , TEXT ( " Failed to open and read cook metadata file %s " ) , * CookMetadataFileName ) ;
return ECookMetadataFiles : : None ;
}
UE_LOG ( LogIoStore , Display , TEXT ( " No cook metadata file found, checked %s " ) , * CookMetadataFileName ) ;
if ( OutCookMetadataFileName )
{
* OutCookMetadataFileName = FString ( " " ) ;
}
}
}
if ( OutAssetRegistryFileName )
{
* OutAssetRegistryFileName = MoveTemp ( PossibleAssetRegistryFiles [ 0 ] ) ;
}
return ResultFiles ;
}