2023-10-06 05:54:04 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "AssetHeaderPatcher.h"
# include "Containers/ContainersFwd.h"
# include "UObject/PackageFileSummary.h"
# include "UObject/NameTypes.h"
# include "UObject/ObjectResource.h"
# include "AssetRegistry/IAssetRegistry.h"
# include "Misc/EnumerateRange.h"
# include "Misc/FileHelper.h"
# include "Misc/PackageName.h"
# include "Misc/PathViews.h"
# include "UObject/Package.h"
# include "Serialization/LargeMemoryReader.h"
# include "WorldPartition/WorldPartitionActorDesc.h"
# include "WorldPartition/WorldPartitionActorDescUtils.h"
# include "AssetRegistry/AssetData.h"
DEFINE_LOG_CATEGORY_STATIC ( LogAssetHeaderPatcher , Log , All ) ;
2023-12-04 06:05:10 -05:00
namespace
2023-10-06 05:54:04 -04:00
{
// To override writing of FName's to ensure they have been patched
class FNamePatchingWriter final : public FArchiveProxy
{
public :
FNamePatchingWriter ( FArchive & InAr , const TMap < FNameEntryId , int32 > & InNameToIndexMap )
: FArchiveProxy ( InAr )
, NameToIndexMap ( InNameToIndexMap )
{
}
2023-12-04 06:05:10 -05:00
virtual ~ FNamePatchingWriter ( )
2023-10-06 05:54:04 -04:00
{
}
virtual FArchive & operator < < ( FName & Name ) override
{
FNameEntryId EntryId = Name . GetDisplayIndex ( ) ;
const int32 * MaybeIndex = NameToIndexMap . Find ( EntryId ) ;
if ( MaybeIndex = = nullptr )
{
UE_LOG ( LogAssetHeaderPatcher , Error , TEXT ( " Cannot serialize FName %s because it is not in the name table for %s " ) , * Name . ToString ( ) , * GetArchiveName ( ) ) ;
SetCriticalError ( ) ;
return * this ;
}
int32 Index = * MaybeIndex ;
int32 Number = Name . GetNumber ( ) ;
FArchive & Ar = * this ;
Ar < < Index ;
Ar < < Number ;
return * this ;
}
private :
const TMap < FNameEntryId , int32 > & NameToIndexMap ;
} ;
2023-12-04 06:05:10 -05:00
enum class EPatchedSection
2023-10-06 05:54:04 -04:00
{
Summary ,
NameTable ,
2023-11-15 04:52:12 -05:00
SoftPathTable ,
2023-10-26 14:54:24 -04:00
ImportTable ,
2023-10-06 05:54:04 -04:00
ExportTable ,
2023-11-15 04:52:12 -05:00
SoftPackageReferencesTable ,
2023-10-06 05:54:04 -04:00
ThumbnailTable ,
AssetRegistryData
} ;
struct FSectionData
{
EPatchedSection Section = EPatchedSection : : Summary ;
int64 Offset = 0 ;
int64 Size = 0 ;
bool bRequired = false ;
} ;
enum class ESummaryOffset
{
NameTable ,
SoftObjectPathList ,
GatherableTextDataOffset ,
ImportTable ,
ExportTable ,
DependsTable ,
SoftPackageReferenceList ,
SearchableNamesTable ,
ThumbnailTable ,
AssetRegistryData ,
WorldTileInfoData ,
PreloadDependency , // Should not be present - only for cooked data
BulkData ,
PayloadToc
} ;
// To override MemoryReaders FName method
class FReadFNameAs2IntFromMemoryReader final : public FLargeMemoryReader
{
public :
FReadFNameAs2IntFromMemoryReader ( TArray < FName > & InNameTable , const uint8 * InData , const int64 Num , ELargeMemoryReaderFlags InFlags = ELargeMemoryReaderFlags : : None , const FName InArchiveName = NAME_None )
: FLargeMemoryReader ( InData , Num , InFlags , InArchiveName )
, NameTable ( InNameTable )
{
}
// FLargeMemoryReader falls back to FMemoryArchive's imp of this method.
// which uses strings as the format for FName.
// We need the 2xint32 version when decoding the current file formats.
virtual FArchive & operator < < ( FName & OutName ) override
{
int32 NameIndex ;
int32 Number ;
FArchive & Ar = * this ;
Ar < < NameIndex ;
Ar < < Number ;
if ( NameTable . IsValidIndex ( NameIndex ) )
{
FNameEntryId MappedName = NameTable [ NameIndex ] . GetDisplayIndex ( ) ;
OutName = FName : : CreateFromDisplayId ( MappedName , MappedName ? Number : 0 ) ;
}
else
{
OutName = FName ( ) ;
SetCriticalError ( ) ;
}
return * this ;
}
2023-12-04 06:05:10 -05:00
virtual FString GetArchiveName ( ) const override
2023-10-06 05:54:04 -04:00
{
2023-12-04 06:05:10 -05:00
return TEXT ( " FReadFNameAs2IntFromMemoryReader " ) ;
2023-10-06 05:54:04 -04:00
}
private :
TArray < FName > & NameTable ;
} ;
struct FSummaryOffsetMeta
{
// NOTE: The offsets in Summary get to a max of 312 bytes.
// So we could drop this to a uint16 but that is probably overkill at this point.
uint32 Offset : 31 ;
uint32 bIs64Bit : 1 ;
int64 Value ( FPackageFileSummary & Summary ) const
{
intptr_t Ptr = reinterpret_cast < intptr_t > ( & Summary ) + Offset ;
if ( bIs64Bit )
{
return * reinterpret_cast < int64 * > ( Ptr ) ;
}
else
{
return * reinterpret_cast < int32 * > ( Ptr ) ;
}
}
void PatchOffsetValue ( FPackageFileSummary & Summary , int64 Value ) const
{
intptr_t Ptr = reinterpret_cast < intptr_t > ( & Summary ) + Offset ;
if ( bIs64Bit )
{
int64 & Dst = * reinterpret_cast < int64 * > ( Ptr ) ;
Dst + = Value ;
}
else
{
int32 & Dst = * reinterpret_cast < int32 * > ( Ptr ) ;
* reinterpret_cast < int32 * > ( Ptr ) = IntCastChecked < int32 > ( ( int64 ) Dst + Value ) ;
}
}
} ;
const FSummaryOffsetMeta OffsetTable [ ] = {
# define UE_POPULATE_OFFSET_INFO(NAME) \
( uint32 ) STRUCT_OFFSET ( FPackageFileSummary , NAME ) , \
std : : is_same_v < decltype ( ( ( FPackageFileSummary * ) 0 ) - > NAME ) , int64 >
{ UE_POPULATE_OFFSET_INFO ( NameOffset ) } ,
{ UE_POPULATE_OFFSET_INFO ( SoftObjectPathsOffset ) } ,
{ UE_POPULATE_OFFSET_INFO ( GatherableTextDataOffset ) } ,
{ UE_POPULATE_OFFSET_INFO ( ImportOffset ) } ,
{ UE_POPULATE_OFFSET_INFO ( ExportOffset ) } ,
{ UE_POPULATE_OFFSET_INFO ( DependsOffset ) } ,
{ UE_POPULATE_OFFSET_INFO ( SoftPackageReferencesOffset ) } ,
{ UE_POPULATE_OFFSET_INFO ( SearchableNamesOffset ) } ,
{ UE_POPULATE_OFFSET_INFO ( ThumbnailTableOffset ) } ,
{ UE_POPULATE_OFFSET_INFO ( AssetRegistryDataOffset ) } ,
{ UE_POPULATE_OFFSET_INFO ( BulkDataStartOffset ) } ,
{ UE_POPULATE_OFFSET_INFO ( WorldTileInfoDataOffset ) } ,
{ UE_POPULATE_OFFSET_INFO ( PreloadDependencyOffset ) } ,
{ UE_POPULATE_OFFSET_INFO ( PayloadTocOffset ) } ,
# undef UE_POPULATE_OFFSET_INFO
} ;
void PatchSummaryOffsets ( FPackageFileSummary & Dst , int64 OffsetFrom , int64 OffsetDelta )
{
if ( ! OffsetDelta )
{
return ;
}
for ( const FSummaryOffsetMeta & OffsetData : OffsetTable )
{
if ( OffsetData . Value ( Dst ) > OffsetFrom )
{
OffsetData . PatchOffsetValue ( Dst , OffsetDelta ) ;
}
}
} ;
FAssetDataTagMap MakeTagMap ( const TArray < UE : : AssetRegistry : : FDeserializeTagData > & TagData )
{
FAssetDataTagMap Out ;
Out . Reserve ( TagData . Num ( ) ) ;
for ( const UE : : AssetRegistry : : FDeserializeTagData & Tag : TagData )
{
if ( ! Tag . Key . IsEmpty ( ) & & ! Tag . Value . IsEmpty ( ) )
{
Out . Add ( * Tag . Key , Tag . Value ) ;
}
}
return Out ;
}
2023-10-26 14:54:24 -04:00
FStringView Find ( const TMap < FString , FString > & Table , FStringView Needle )
{
uint32 NeedleHash = TMap < FString , FString > : : KeyFuncsType : : GetKeyHash < FStringView > ( Needle ) ;
const FString * MaybeNewItem = Table . FindByHash < FStringView > ( NeedleHash , Needle ) ;
if ( MaybeNewItem )
{
return * MaybeNewItem ;
}
return { } ;
}
2023-10-06 05:54:04 -04:00
}
// The information we need in the task to do patching.
class FAssetHeaderPatcherInner
{
public :
using EResult = FAssetHeaderPatcher : : EResult ;
2024-01-12 12:06:14 -05:00
FAssetHeaderPatcherInner ( const FString & InSrcAsset , const FString & InDstAsset , const TMap < FString , FString > & InSearchAndReplace , FArchive * InDstArchive = nullptr , bool bInBespokeSearchInUse = false )
2024-01-11 06:57:01 -05:00
: SrcAsset ( InSrcAsset )
, DstAsset ( InDstAsset )
, SearchAndReplace ( InSearchAndReplace )
2024-01-12 12:06:14 -05:00
, bBespokeSearchInUse ( bInBespokeSearchInUse )
2023-10-06 05:54:04 -04:00
, DstArchive ( InDstArchive )
{
}
bool DoPatch ( FString & InOutString ) ;
bool DoPatch ( FName & InOutName ) ;
2023-12-06 11:52:23 -05:00
bool DoPatch ( FSoftObjectPath & InOutSoft ) ;
2023-10-06 05:54:04 -04:00
bool DoPatch ( FTopLevelAssetPath & InOutPath ) ;
FAssetHeaderPatcher : : EResult PatchHeader ( ) ;
FAssetHeaderPatcher : : EResult PatchHeader_Deserialize ( ) ;
void PatchHeader_PatchSections ( ) ;
FAssetHeaderPatcher : : EResult PatchHeader_WriteDestinationFile ( ) ;
2024-01-11 06:57:01 -05:00
const FString & SrcAsset ;
const FString & DstAsset ;
const TMap < FString , FString > & SearchAndReplace ;
2024-01-12 12:06:14 -05:00
bool bBespokeSearchInUse ;
2023-10-06 05:54:04 -04:00
FArchive * DstArchive = nullptr ;
TUniquePtr < FArchive > DstArchiveOwner ;
2024-01-11 06:57:01 -05:00
2023-10-06 05:54:04 -04:00
TArray64 < uint8 > SrcBuffer ;
struct FHeaderInformation
{
int64 SummarySize = - 1 ;
int64 NameTableSize = - 1 ;
int64 SoftObjectPathListSize = - 1 ;
int64 SoftPackageReferencesListSize = - 1 ;
int64 ImportTableSize = - 1 ;
int64 ExportTableSize = - 1 ;
int64 ThumbnailTableSize = - 1 ;
int64 AssetRegistryDataSize = - 1 ;
int64 PackageTrailerSize = - 1 ;
} ;
struct FThumbnailEntry
{
FString ObjectShortClassName ;
FString ObjectPathWithoutPackageName ;
int32 FileOffset = 0 ;
} ;
FHeaderInformation HeaderInformation ;
FPackageFileSummary Summary ;
TArray < FName > NameTable ;
2023-11-15 04:52:12 -05:00
TArray < FSoftObjectPath > SoftObjectPathTable ;
TArray < FName > SoftPackageReferencesTable ;
2023-10-06 05:54:04 -04:00
TMap < FNameEntryId , int32 > NameToIndexMap ;
TArray < FObjectImport > ImportTable ;
TArray < FObjectExport > ExportTable ;
TArray < FThumbnailEntry > ThumbnailTable ;
// Asset registry data information
2023-12-04 06:05:10 -05:00
struct FAssetRegistryObjectData
2023-10-06 05:54:04 -04:00
{
UE : : AssetRegistry : : FDeserializeObjectPackageData ObjectData ;
TArray < UE : : AssetRegistry : : FDeserializeTagData > TagData ;
} ;
2023-12-06 11:52:23 -05:00
struct FAssetRegistryData
2023-10-06 05:54:04 -04:00
{
int64 SectionSize = - 1 ;
UE : : AssetRegistry : : FDeserializePackageData PkgData ;
2023-12-04 06:05:10 -05:00
TArray < FAssetRegistryObjectData > ObjectData ;
2023-10-06 05:54:04 -04:00
} ;
2023-12-06 11:52:23 -05:00
FAssetRegistryData AssetRegistryData ;
2023-10-06 05:54:04 -04:00
} ;
2024-01-12 12:06:14 -05:00
FAssetHeaderPatcher : : EResult FAssetHeaderPatcher : : DoPatch ( const FString & InSrcAsset , const FString & InDstAsset , const TMap < FString , FString > & InSearchAndReplace , bool bInStarRestrictionInUse )
2023-10-06 05:54:04 -04:00
{
2024-01-12 12:06:14 -05:00
FAssetHeaderPatcherInner Inner ( InSrcAsset , InDstAsset , InSearchAndReplace , nullptr , bInStarRestrictionInUse ) ;
2023-10-06 05:54:04 -04:00
2024-01-11 06:57:01 -05:00
if ( ! FFileHelper : : LoadFileToArray ( Inner . SrcBuffer , * Inner . SrcAsset ) )
2023-10-06 05:54:04 -04:00
{
2024-01-11 06:57:01 -05:00
UE_LOG ( LogAssetHeaderPatcher , Error , TEXT ( " Failed to load %s " ) , * Inner . SrcAsset ) ;
return FAssetHeaderPatcherInner : : EResult : : ErrorFailedToLoadSourceAsset ;
}
else
{
return Inner . PatchHeader ( ) ;
2023-10-06 05:54:04 -04:00
}
}
2024-01-11 06:57:01 -05:00
2023-10-06 05:54:04 -04:00
FAssetHeaderPatcher : : EResult FAssetHeaderPatcher : : Test_DoPatch ( FArchive & InReader , FArchive & InWriter ,
2024-01-11 06:57:01 -05:00
const TMap < FString , FString > & InSearchAndReplace )
2023-10-06 05:54:04 -04:00
{
2024-01-11 06:57:01 -05:00
FAssetHeaderPatcherInner Inner ( InReader . GetArchiveName ( ) , InWriter . GetArchiveName ( ) , InSearchAndReplace , & InWriter ) ;
2023-10-06 05:54:04 -04:00
InReader . Seek ( 0 ) ;
Inner . SrcBuffer . SetNumUninitialized ( InReader . TotalSize ( ) ) ;
InReader . Serialize ( Inner . SrcBuffer . GetData ( ) , Inner . SrcBuffer . Num ( ) ) ;
if ( InReader . IsError ( ) )
{
UE_LOG ( LogAssetHeaderPatcher , Error , TEXT ( " Failed to load %s " ) , * Inner . SrcAsset ) ;
return FAssetHeaderPatcherInner : : EResult : : ErrorFailedToLoadSourceAsset ;
}
else
{
return Inner . PatchHeader ( ) ;
}
}
FAssetHeaderPatcher : : EResult FAssetHeaderPatcherInner : : PatchHeader ( )
{
FAssetHeaderPatcher : : EResult Result = PatchHeader_Deserialize ( ) ;
2023-10-26 14:54:24 -04:00
if ( Result ! = EResult : : Success )
2023-10-06 05:54:04 -04:00
{
return Result ;
}
PatchHeader_PatchSections ( ) ;
return PatchHeader_WriteDestinationFile ( ) ;
}
FAssetHeaderPatcher : : EResult FAssetHeaderPatcherInner : : PatchHeader_Deserialize ( )
{
FReadFNameAs2IntFromMemoryReader MemAr ( NameTable , SrcBuffer . GetData ( ) , SrcBuffer . Num ( ) ) ;
MemAr < < Summary ;
HeaderInformation . SummarySize = MemAr . Tell ( ) ;
// set version numbers so components branch correctly
MemAr . SetUEVer ( Summary . GetFileVersionUE ( ) ) ;
MemAr . SetLicenseeUEVer ( Summary . GetFileVersionLicenseeUE ( ) ) ;
MemAr . SetEngineVer ( Summary . SavedByEngineVersion ) ;
MemAr . SetCustomVersions ( Summary . GetCustomVersionContainer ( ) ) ;
2023-10-26 14:54:24 -04:00
if ( Summary . GetPackageFlags ( ) & PKG_FilterEditorOnly )
{
MemAr . SetFilterEditorOnly ( true ) ;
}
2023-10-06 05:54:04 -04:00
if ( Summary . DataResourceOffset > 0 )
{
// Should only be set in cooked data. If that changes, we need to add code to patch it
UE_LOG ( LogAssetHeaderPatcher , Error , TEXT ( " Asset %s has an unexpected DataResourceOffset " ) , * SrcAsset ) ;
return EResult : : ErrorUnexpectedSectionOrder ;
}
if ( Summary . NameCount > 0 )
{
MemAr . Seek ( Summary . NameOffset ) ;
NameTable . Reserve ( Summary . NameCount ) ;
for ( int32 NameMapIdx = 0 ; NameMapIdx < Summary . NameCount ; + + NameMapIdx )
{
FNameEntrySerialized NameEntry ( ENAME_LinkerConstructor ) ;
MemAr < < NameEntry ;
NameTable . Add ( FName ( NameEntry ) ) ;
}
HeaderInformation . NameTableSize = MemAr . Tell ( ) - HeaderInformation . SummarySize ;
}
2023-11-15 04:52:12 -05:00
if ( Summary . SoftObjectPathsCount > 0 )
{
MemAr . Seek ( Summary . SoftObjectPathsOffset ) ;
SoftObjectPathTable . Reserve ( Summary . SoftObjectPathsCount ) ;
for ( int32 Idx = 0 ; Idx < Summary . SoftObjectPathsCount ; + + Idx )
{
FSoftObjectPath & PathRef = SoftObjectPathTable . AddDefaulted_GetRef ( ) ;
PathRef . SerializePath ( MemAr ) ;
}
HeaderInformation . SoftObjectPathListSize = MemAr . Tell ( ) - Summary . SoftObjectPathsOffset ;
}
else
{
HeaderInformation . SoftObjectPathListSize = 0 ;
}
2023-10-06 05:54:04 -04:00
# define UE_CHECK_AND_SET_ERROR_AND_RETURN(EXP) \
do \
{ \
if ( EXP ) \
{ \
UE_LOG ( LogAssetHeaderPatcher , Log , TEXT ( " Asset %s fails %s " ) , * SrcAsset , TEXT ( # EXP ) ) ; \
return EResult : : ErrorBadOffset ; \
} \
} \
while ( 0 )
if ( Summary . ImportCount > 0 )
{
UE_CHECK_AND_SET_ERROR_AND_RETURN ( Summary . ImportOffset > = Summary . TotalHeaderSize ) ;
UE_CHECK_AND_SET_ERROR_AND_RETURN ( Summary . ImportOffset < 0 ) ;
MemAr . Seek ( Summary . ImportOffset ) ;
ImportTable . Reserve ( Summary . ImportCount ) ;
for ( int32 ImportIndex = 0 ; ImportIndex < Summary . ImportCount ; + + ImportIndex )
{
FObjectImport & Import = ImportTable . Emplace_GetRef ( ) ;
MemAr < < Import ;
}
HeaderInformation . ImportTableSize = MemAr . Tell ( ) - Summary . ImportOffset ;
}
2023-12-04 06:05:10 -05:00
else
2023-10-06 05:54:04 -04:00
{
HeaderInformation . ImportTableSize = 0 ;
}
if ( Summary . ExportCount > 0 )
{
UE_CHECK_AND_SET_ERROR_AND_RETURN ( Summary . ExportOffset > = Summary . TotalHeaderSize ) ;
UE_CHECK_AND_SET_ERROR_AND_RETURN ( Summary . ExportOffset < 0 ) ;
MemAr . Seek ( Summary . ExportOffset ) ;
ExportTable . Reserve ( Summary . ExportCount ) ;
for ( int32 ExportIndex = 0 ; ExportIndex < Summary . ExportCount ; + + ExportIndex )
{
FObjectExport & Export = ExportTable . Emplace_GetRef ( ) ;
MemAr < < Export ;
}
HeaderInformation . ExportTableSize = MemAr . Tell ( ) - Summary . ExportOffset ;
}
else
{
HeaderInformation . ExportTableSize = 0 ;
}
# undef UE_CHECK_AND_SET_ERROR_AND_RETURN
2023-11-15 04:52:12 -05:00
if ( Summary . SoftPackageReferencesCount )
{
MemAr . Seek ( Summary . SoftPackageReferencesOffset ) ;
SoftPackageReferencesTable . Reserve ( Summary . SoftPackageReferencesCount ) ;
for ( int32 Idx = 0 ; Idx < Summary . SoftPackageReferencesCount ; + + Idx )
{
FName & Reference = SoftPackageReferencesTable . Emplace_GetRef ( ) ;
MemAr < < Reference ;
}
HeaderInformation . SoftPackageReferencesListSize = MemAr . Tell ( ) - Summary . SoftPackageReferencesOffset ;
}
else
{
HeaderInformation . SoftPackageReferencesListSize = 0 ;
}
2023-10-06 05:54:04 -04:00
if ( Summary . ThumbnailTableOffset )
{
MemAr . Seek ( Summary . ThumbnailTableOffset ) ;
int32 ThumbnailCount = 0 ;
MemAr < < ThumbnailCount ;
ThumbnailTable . Reserve ( ThumbnailCount ) ;
for ( int32 Index = 0 ; Index < ThumbnailCount ; + + Index )
{
FThumbnailEntry & Entry = ThumbnailTable . Emplace_GetRef ( ) ;
MemAr < < Entry . ObjectShortClassName ;
MemAr < < Entry . ObjectPathWithoutPackageName ;
MemAr < < Entry . FileOffset ;
}
HeaderInformation . ThumbnailTableSize = MemAr . Tell ( ) - Summary . ThumbnailTableOffset ;
}
// Load AR data
if ( Summary . AssetRegistryDataOffset )
{
MemAr . Seek ( Summary . AssetRegistryDataOffset ) ;
UE : : AssetRegistry : : EReadPackageDataMainErrorCode ErrorCode ;
2023-12-06 11:52:23 -05:00
if ( ! AssetRegistryData . PkgData . DoSerialize ( MemAr , Summary , ErrorCode ) )
2023-10-06 05:54:04 -04:00
{
UE_LOG ( LogAssetHeaderPatcher , Error , TEXT ( " Failed to deserialize asset registry data for %s " ) , * SrcAsset ) ;
return EResult : : ErrorFailedToDeserializeSourceAsset ;
}
2023-12-06 11:52:23 -05:00
AssetRegistryData . ObjectData . Reserve ( AssetRegistryData . PkgData . ObjectCount ) ;
for ( int32 i = 0 ; i < AssetRegistryData . PkgData . ObjectCount ; + + i )
2023-10-06 05:54:04 -04:00
{
2023-12-06 11:52:23 -05:00
FAssetRegistryObjectData & ObjData = AssetRegistryData . ObjectData . Emplace_GetRef ( ) ;
2023-10-06 05:54:04 -04:00
if ( ! ObjData . ObjectData . DoSerialize ( MemAr , ErrorCode ) )
{
UE_LOG ( LogAssetHeaderPatcher , Error , TEXT ( " Failed to deserialize asset registry data for %s " ) , * SrcAsset ) ;
return EResult : : ErrorFailedToDeserializeSourceAsset ;
}
ObjData . TagData . Reserve ( ObjData . ObjectData . TagCount ) ;
for ( int32 j = 0 ; j < ObjData . ObjectData . TagCount ; + + j )
{
if ( ! ObjData . TagData . Emplace_GetRef ( ) . DoSerialize ( MemAr , ErrorCode ) )
{
UE_LOG ( LogAssetHeaderPatcher , Error , TEXT ( " Failed to deserialize asset registry data for %s " ) , * SrcAsset ) ;
return EResult : : ErrorFailedToDeserializeSourceAsset ;
}
}
}
2023-12-06 11:52:23 -05:00
AssetRegistryData . SectionSize = MemAr . Tell ( ) - Summary . AssetRegistryDataOffset ;
2023-10-06 05:54:04 -04:00
}
return EResult : : Success ;
}
bool FAssetHeaderPatcherInner : : DoPatch ( FString & InOutString )
{
{
2023-10-26 14:54:24 -04:00
// Find a Path, change a Path.
FStringView MaybeReplacement = Find ( SearchAndReplace , InOutString ) ;
if ( ! MaybeReplacement . IsEmpty ( ) )
2023-10-06 05:54:04 -04:00
{
2023-10-26 14:54:24 -04:00
InOutString = MaybeReplacement ;
2023-10-06 05:54:04 -04:00
return true ;
}
}
2023-10-26 14:54:24 -04:00
{
// Patch Object paths.
// Path occurs to the left of a ":"
int Idx { } ;
if ( InOutString . FindChar ( TCHAR ( ' : ' ) , Idx ) )
{
if ( InOutString [ Idx + 1 ] = = TCHAR ( ' : ' ) )
{
// "::" is not a path delim
return false ;
}
FStringView MaybeReplacement = Find ( SearchAndReplace , FStringView ( * InOutString , Idx ) ) ;
if ( ! MaybeReplacement . IsEmpty ( ) )
{
2023-12-04 06:05:10 -05:00
InOutString = MaybeReplacement + InOutString . RightChop ( Idx ) ;
2023-10-26 14:54:24 -04:00
return true ;
}
}
}
2023-12-04 06:05:10 -05:00
2023-10-26 14:54:24 -04:00
{
// Patch quoted paths.
// Path occurs to the right of the first "'"
int Idx { } ;
if ( InOutString . FindChar ( TCHAR ( ' \' ' ) , Idx ) & & InOutString . EndsWith ( TEXT ( " ' " ) ) )
{
FStringView MaybeReplacement = Find ( SearchAndReplace , FStringView ( * InOutString + Idx + 1 , InOutString . Len ( ) - ( Idx + 2 ) ) ) ;
if ( ! MaybeReplacement . IsEmpty ( ) )
{
2023-12-04 06:05:10 -05:00
InOutString = InOutString . LeftChop ( Idx + 1 ) + MaybeReplacement + TCHAR ( ' \' ' ) ;
2023-10-26 14:54:24 -04:00
return true ;
}
}
}
2023-10-06 05:54:04 -04:00
return false ;
}
bool FAssetHeaderPatcherInner : : DoPatch ( FName & InOutName )
{
FString Value = InOutName . GetPlainNameString ( ) ;
2023-12-06 11:52:23 -05:00
if ( DoPatch ( Value ) )
2023-10-06 05:54:04 -04:00
{
// Use the same Number as the original Name for consistency.
// Otherwise different files with the same name, generate different numbers, and this breaks linking.
InOutName = FName ( Value , InOutName . GetNumber ( ) ) ;
2023-12-06 11:52:23 -05:00
return true ;
2023-10-06 05:54:04 -04:00
}
2023-12-06 11:52:23 -05:00
return false ;
}
bool FAssetHeaderPatcherInner : : DoPatch ( FSoftObjectPath & InOutSoft )
{
FTopLevelAssetPath TmpPath = InOutSoft . GetAssetPath ( ) ;
if ( DoPatch ( TmpPath ) )
{
InOutSoft . SetPath ( TmpPath , InOutSoft . GetSubPathString ( ) ) ;
return true ;
}
return false ;
2023-10-06 05:54:04 -04:00
}
bool FAssetHeaderPatcherInner : : DoPatch ( FTopLevelAssetPath & InOutPath )
{
2023-12-04 06:05:10 -05:00
FName PackageName = InOutPath . GetPackageName ( ) ;
FName AssetName = InOutPath . GetAssetName ( ) ;
bool bPatching = DoPatch ( PackageName ) ;
bPatching | = DoPatch ( AssetName ) ;
2023-10-06 05:54:04 -04:00
if ( bPatching )
{
2023-12-04 06:05:10 -05:00
InOutPath = FTopLevelAssetPath ( PackageName , AssetName ) ;
2023-10-06 05:54:04 -04:00
}
return bPatching ;
}
void FAssetHeaderPatcherInner : : PatchHeader_PatchSections ( )
{
DoPatch ( Summary . PackageName ) ;
2023-10-26 14:54:24 -04:00
{ // Patch the Name table
// This data is used by all FGNames in the file.
// So if we patch a Identifier we append it to the name table.
// This is because there may be FNames in data we dont want to patch in structures we dont look at.
// If we patch the ident at inplace, then we would change those names.
TArray < FName > ToAppend ;
for ( FName & Name : NameTable )
{
2023-11-15 04:52:12 -05:00
FName TmpName = Name ;
2023-12-04 06:05:10 -05:00
if ( DoPatch ( TmpName ) )
2023-10-26 14:54:24 -04:00
{
2023-11-15 04:52:12 -05:00
FString TmpNameStr = Name . GetPlainNameString ( ) ;
// If the string does not contain any path separators, then call it an Identifier.
if ( ! ( TmpNameStr . Contains ( TEXT ( " / " ) ) | | TmpNameStr . Contains ( TEXT ( " \\ " ) ) ) )
2023-10-26 14:54:24 -04:00
{
2023-11-15 04:52:12 -05:00
ToAppend . Add ( TmpName ) ;
2023-12-04 06:05:10 -05:00
}
2023-10-26 14:54:24 -04:00
else
{
2023-11-15 04:52:12 -05:00
Name = TmpName ;
2023-10-26 14:54:24 -04:00
}
}
}
NameTable . Append ( ToAppend ) ;
Summary . NameCount = NameTable . Num ( ) ;
2023-10-06 05:54:04 -04:00
}
2023-10-26 14:54:24 -04:00
// Build Name Path table
// This is used to generate the indices saved by the FNamePatchingWriter
NameToIndexMap . Reserve ( NameTable . Num ( ) ) ;
for ( TConstEnumerateRef < FName > Name : EnumerateRange ( NameTable ) )
{
NameToIndexMap . Add ( Name - > GetDisplayIndex ( ) ) = Name . GetIndex ( ) ;
}
2023-11-15 04:52:12 -05:00
// Soft paths
for ( FSoftObjectPath & PathRef : SoftObjectPathTable )
{
2023-12-06 11:52:23 -05:00
DoPatch ( PathRef ) ;
2023-11-15 04:52:12 -05:00
}
2023-10-26 14:54:24 -04:00
// Import table
2023-10-06 05:54:04 -04:00
for ( FObjectImport & Import : ImportTable )
{
DoPatch ( Import . ObjectName ) ;
# if WITH_EDITORONLY_DATA
DoPatch ( Import . OldClassName ) ;
# endif
DoPatch ( Import . ClassPackage ) ;
DoPatch ( Import . ClassName ) ;
# if WITH_EDITORONLY_DATA
DoPatch ( Import . PackageName ) ;
# endif
}
2023-11-15 04:52:12 -05:00
// Export Table
2023-10-06 05:54:04 -04:00
for ( FObjectExport & Export : ExportTable )
{
DoPatch ( Export . ObjectName ) ;
# if WITH_EDITORONLY_DATA
DoPatch ( Export . OldClassName ) ;
# endif
}
2023-11-15 04:52:12 -05:00
// Soft Package Reference's
for ( FName & Reference : SoftPackageReferencesTable )
{
DoPatch ( Reference ) ;
}
2023-10-06 05:54:04 -04:00
// Asset Register Data
2023-12-06 11:52:23 -05:00
for ( FAssetRegistryObjectData & ObjData : AssetRegistryData . ObjectData )
2023-10-06 05:54:04 -04:00
{
DoPatch ( ObjData . ObjectData . ObjectPath ) ;
2023-12-04 06:05:10 -05:00
{ // ObjData.ObjectData.ObjectClassName is stored as a string, but is used as a FTopLevelAssetPath
FTopLevelAssetPath Tmp ( ObjData . ObjectData . ObjectClassName ) ;
if ( DoPatch ( Tmp ) )
{
FString TS = Tmp . ToString ( ) ;
ObjData . ObjectData . ObjectClassName = TS ;
}
}
2023-10-06 05:54:04 -04:00
for ( UE : : AssetRegistry : : FDeserializeTagData & TagData : ObjData . TagData )
{
2023-12-04 06:05:10 -05:00
if ( TagData . Key = = FWorldPartitionActorDescUtils : : ActorMetaDataTagName ( ) )
2023-10-06 05:54:04 -04:00
{
2023-12-06 11:52:23 -05:00
const FString LongPackageName ( SrcAsset ) ;
const FString ObjectPath ( ObjData . ObjectData . ObjectPath ) ;
const FTopLevelAssetPath AssetClassPathName ( ObjData . ObjectData . ObjectClassName ) ;
const FAssetDataTagMap Tags ( MakeTagMap ( ObjData . TagData ) ) ;
const FAssetData AssetData ( LongPackageName , ObjectPath , AssetClassPathName , Tags ) ;
2023-10-06 05:54:04 -04:00
2023-12-06 11:52:23 -05:00
struct FWorldPartitionAssetDataPatcherInner : FWorldPartitionAssetDataPatcher
2023-10-06 05:54:04 -04:00
{
2023-12-06 11:52:23 -05:00
FWorldPartitionAssetDataPatcherInner ( FAssetHeaderPatcherInner * InInner ) : Inner ( InInner ) { }
virtual bool DoPatch ( FString & InOutString ) override { return Inner - > DoPatch ( InOutString ) ; }
virtual bool DoPatch ( FName & InOutName ) override { return Inner - > DoPatch ( InOutName ) ; }
virtual bool DoPatch ( FSoftObjectPath & InOutSoft ) override { return Inner - > DoPatch ( InOutSoft ) ; }
virtual bool DoPatch ( FTopLevelAssetPath & InOutPath ) override { return Inner - > DoPatch ( InOutPath ) ; }
FAssetHeaderPatcherInner * Inner ;
} ;
FString PatchedAssetData ;
FWorldPartitionAssetDataPatcherInner Patcher ( this ) ;
if ( FWorldPartitionActorDescUtils : : GetPatchedAssetDataFromAssetData ( AssetData , PatchedAssetData , & Patcher ) )
2023-12-04 06:05:10 -05:00
{
2023-12-06 11:52:23 -05:00
TagData . Value = PatchedAssetData ;
2023-12-04 06:05:10 -05:00
}
}
2024-01-12 12:06:14 -05:00
else if ( bBespokeSearchInUse & &
FPathViews : : GetBaseFilename ( ObjData . ObjectData . ObjectPath ) = = TEXT ( " GameFeatureData " ) & &
TagData . Key = = TEXT ( " PrimaryAssetName " ) )
{
FString BespokeSearchValue = TEXT ( " <GameFeatureData.PrimaryAssetName> " ) + TagData . Value ;
if ( DoPatch ( BespokeSearchValue ) )
{
TagData . Value = BespokeSearchValue ;
}
}
2023-12-04 06:05:10 -05:00
else
{
DoPatch ( TagData . Value ) ;
2023-10-06 05:54:04 -04:00
}
}
}
}
FAssetHeaderPatcher : : EResult FAssetHeaderPatcherInner : : PatchHeader_WriteDestinationFile ( )
{
// Serialize modified sections and reconstruct the file
// Original offsets and sizes of any sections that will be patched
2023-11-15 04:52:12 -05:00
// Tag Offset Size bRequired
2023-10-06 05:54:04 -04:00
const FSectionData SourceSections [ ] = {
2023-11-15 04:52:12 -05:00
{ EPatchedSection : : Summary , 0 , HeaderInformation . SummarySize , true } ,
{ EPatchedSection : : NameTable , Summary . NameOffset , HeaderInformation . NameTableSize , true } ,
{ EPatchedSection : : SoftPathTable , Summary . SoftObjectPathsOffset , HeaderInformation . SoftObjectPathListSize , false } ,
{ EPatchedSection : : ImportTable , Summary . ImportOffset , HeaderInformation . ImportTableSize , true } ,
{ EPatchedSection : : ExportTable , Summary . ExportOffset , HeaderInformation . ExportTableSize , true } ,
{ EPatchedSection : : SoftPackageReferencesTable , Summary . SoftPackageReferencesOffset , HeaderInformation . SoftPackageReferencesListSize , false } ,
{ EPatchedSection : : ThumbnailTable , Summary . ThumbnailTableOffset , HeaderInformation . ThumbnailTableSize , false } ,
2023-12-06 11:52:23 -05:00
{ EPatchedSection : : AssetRegistryData , Summary . AssetRegistryDataOffset , AssetRegistryData . SectionSize , true } ,
2023-10-06 05:54:04 -04:00
} ;
const int32 SourceTotalHeaderSize = Summary . TotalHeaderSize ;
// Ensure the sections are in the expected order.
for ( int SectionIdx = 1 ; SectionIdx < UE_ARRAY_COUNT ( SourceSections ) ; + + SectionIdx )
{
const FSectionData & SourceSection = SourceSections [ SectionIdx ] ;
const FSectionData & PrevSection = SourceSections [ SectionIdx - 1 ] ;
// Verify sections are ordered as expected
if ( SourceSection . Offset < 0 | | ( SourceSection . bRequired & & ( SourceSection . Offset < PrevSection . Offset ) ) )
{
UE_LOG ( LogAssetHeaderPatcher , Error , TEXT ( " Unexpected section order for %s (%d %zd < %zd) " ) , * SrcAsset , SectionIdx , SourceSection . Offset , PrevSection . Offset ) ;
return EResult : : ErrorUnexpectedSectionOrder ;
}
}
// Ensure the required sections have data
for ( const FSectionData & SourceSection : SourceSections )
{
// skip processing empty non required chunks.
if ( SourceSection . bRequired & & SourceSection . Size < = 0 )
{
UE_LOG ( LogAssetHeaderPatcher , Error , TEXT ( " Unexpected section order for %s " ) , * SrcAsset ) ;
return EResult : : ErrorEmptyRequireSection ;
}
}
// Create the destination file if not open already
if ( ! DstArchive )
{
TUniquePtr < FArchive > FileWriter ( IFileManager : : Get ( ) . CreateFileWriter ( * DstAsset , FILEWRITE_EvenIfReadOnly ) ) ;
if ( ! FileWriter )
{
UE_LOG ( LogAssetHeaderPatcher , Error , TEXT ( " Failed to open %s for write " ) , * DstAsset ) ;
return EResult : : ErrorFailedToOpenDestinationFile ;
}
DstArchiveOwner = MoveTemp ( FileWriter ) ;
DstArchive = DstArchiveOwner . Get ( ) ;
}
FNamePatchingWriter Writer ( * DstArchive , NameToIndexMap ) ;
// set version numbers so components branch correctly
Writer . SetUEVer ( Summary . GetFileVersionUE ( ) ) ;
Writer . SetLicenseeUEVer ( Summary . GetFileVersionLicenseeUE ( ) ) ;
Writer . SetEngineVer ( Summary . SavedByEngineVersion ) ;
Writer . SetCustomVersions ( Summary . GetCustomVersionContainer ( ) ) ;
2023-10-26 14:54:24 -04:00
if ( Summary . GetPackageFlags ( ) & PKG_FilterEditorOnly )
{
Writer . SetFilterEditorOnly ( true ) ;
}
2023-10-06 05:54:04 -04:00
int64 LastSectionEndedAt = 0 ;
2023-12-04 06:05:10 -05:00
2023-10-06 05:54:04 -04:00
for ( int SectionIdx = 0 ; SectionIdx < UE_ARRAY_COUNT ( SourceSections ) ; + + SectionIdx )
{
const FSectionData & SourceSection = SourceSections [ SectionIdx ] ;
// skip processing empty non required chunks.
// really the only option section is the Thumbnails, and its annoying its ion the middle.
2023-12-04 06:05:10 -05:00
if ( ! SourceSection . bRequired & & SourceSection . Size < = 0 )
2023-10-06 05:54:04 -04:00
{
continue ;
}
// copy the blob from the end of the last section, to the start of this one
if ( LastSectionEndedAt )
{
int64 SizeToCopy = SourceSection . Offset - LastSectionEndedAt ;
checkf ( SizeToCopy > = 0 , TEXT ( " Section %d of %s \n %zd -> %zd %zd " ) , SectionIdx , * SrcAsset , SourceSection . Offset , LastSectionEndedAt , SizeToCopy ) ;
Writer . Serialize ( SrcBuffer . GetData ( ) + LastSectionEndedAt , SizeToCopy ) ;
}
LastSectionEndedAt = SourceSection . Offset + SourceSection . Size ;
// Serialize the current patched section and patch summary offsets
switch ( SourceSection . Section )
{
2023-12-04 06:05:10 -05:00
case EPatchedSection : : Summary :
{
// We will write the Summary twice.
// The first is so we get its new size (if the name was changed in patching)
// The second is done after the loop, to patch up all the offsets.
check ( Writer . Tell ( ) = = 0 ) ;
Writer < < Summary ;
const int64 SummarySize = Writer . Tell ( ) ;
const int64 Delta = SummarySize - SourceSection . Size ;
PatchSummaryOffsets ( Summary , 0 , Delta ) ;
Summary . TotalHeaderSize + = ( int32 ) Delta ;
2023-10-06 05:54:04 -04:00
2023-12-04 06:05:10 -05:00
break ;
}
case EPatchedSection : : NameTable :
{
const int64 NameTableStartOffset = Writer . Tell ( ) ;
for ( FName & Name : NameTable )
{
const FNameEntry * Entry = FName : : GetEntry ( Name . GetDisplayIndex ( ) ) ;
check ( Entry ) ;
Entry - > Write ( Writer ) ;
}
const int64 NameTableSize = Writer . Tell ( ) - NameTableStartOffset ;
const int64 Delta = NameTableSize - SourceSection . Size ;
PatchSummaryOffsets ( Summary , NameTableStartOffset , Delta ) ;
Summary . TotalHeaderSize + = ( int32 ) Delta ;
check ( Summary . NameCount = = NameTable . Num ( ) ) ;
check ( Summary . NameOffset = = NameTableStartOffset ) ;
break ;
}
case EPatchedSection : : SoftPathTable :
{
const int64 TableStartOffset = Writer . Tell ( ) ;
for ( FSoftObjectPath & PathRef : SoftObjectPathTable )
{
PathRef . SerializePath ( Writer ) ;
}
const int64 TableSize = Writer . Tell ( ) - TableStartOffset ;
const int64 Delta = TableSize - SourceSection . Size ;
checkf ( Delta = = 0 , TEXT ( " Delta should be Zero. is %d " ) , ( int ) Delta ) ;
check ( Summary . SoftObjectPathsCount = = SoftObjectPathTable . Num ( ) ) ;
check ( Summary . SoftObjectPathsOffset = = TableStartOffset ) ;
break ;
}
case EPatchedSection : : ImportTable :
{
const int64 ImportTableStartOffset = Writer . Tell ( ) ;
for ( FObjectImport & Import : ImportTable )
{
Writer < < Import ;
}
const int64 ImportTableSize = Writer . Tell ( ) - ImportTableStartOffset ;
const int64 Delta = ImportTableSize - SourceSection . Size ;
check ( Delta = = 0 ) ;
checkf ( ImportTableSize = = SourceSection . Size , TEXT ( " %d == %d " ) , ( int ) ImportTableSize , ( int ) SourceSection . Size ) ; // We only patch export table offsets, we should not be patching size
checkf ( Summary . ImportCount = = ImportTable . Num ( ) , TEXT ( " %d == %d " ) , Summary . ImportCount , ImportTable . Num ( ) ) ;
checkf ( Summary . ImportOffset = = ImportTableStartOffset , TEXT ( " %d == %d " ) , Summary . ImportOffset , ImportTableStartOffset ) ;
break ;
}
case EPatchedSection : : ExportTable :
{
// The export table offsets aren't correct yet.
// Once we know them, we will seek back and write it a second time.
const int64 ExportTableStartOffset = Writer . Tell ( ) ;
for ( FObjectExport & Export : ExportTable )
{
Writer < < Export ;
}
const int64 ExportTableSize = Writer . Tell ( ) - ExportTableStartOffset ;
const int64 Delta = ExportTableSize - SourceSection . Size ;
check ( Delta = = 0 ) ;
checkf ( ExportTableSize = = SourceSection . Size , TEXT ( " %d == %d " ) , ( int ) ExportTableSize , ( int ) SourceSection . Size ) ; // We only patch export table offsets, we should not be patching size
checkf ( Summary . ExportCount = = ExportTable . Num ( ) , TEXT ( " %d == %d " ) , Summary . ExportCount , ExportTable . Num ( ) ) ;
checkf ( Summary . ExportOffset = = ExportTableStartOffset , TEXT ( " %d == %d " ) , Summary . ExportOffset , ExportTableStartOffset ) ;
break ;
}
case EPatchedSection : : SoftPackageReferencesTable :
{
const int64 TableStartOffset = Writer . Tell ( ) ;
for ( FName & Reference : SoftPackageReferencesTable )
{
Writer < < Reference ;
}
const int64 TableSize = Writer . Tell ( ) - TableStartOffset ;
const int64 Delta = TableSize - SourceSection . Size ;
checkf ( Delta = = 0 , TEXT ( " Delta should be Zero. is %d " ) , ( int ) Delta ) ;
check ( Summary . SoftPackageReferencesCount = = SoftPackageReferencesTable . Num ( ) ) ;
check ( Summary . SoftPackageReferencesOffset = = TableStartOffset ) ;
break ;
}
case EPatchedSection : : ThumbnailTable :
{
// Luckily the strings in the thumbnail table don't include the package, so only the offsets need to be patched
const int64 ThumbnailTableStartOffset = Writer . Tell ( ) ;
const int64 ThumbnailTableDeltaOffset = ThumbnailTableStartOffset - SourceSection . Offset ;
int32 ThumbnailCount = ThumbnailTable . Num ( ) ;
Writer < < ThumbnailCount ;
for ( FThumbnailEntry & Entry : ThumbnailTable )
{
Writer < < Entry . ObjectShortClassName ;
Writer < < Entry . ObjectPathWithoutPackageName ;
Entry . FileOffset + = ( int32 ) ThumbnailTableDeltaOffset ; // Thumbnail payloads immediately follow the table, so we can just apply the section offset delta here
Writer < < Entry . FileOffset ;
}
const int64 ThumbnailTableSize = Writer . Tell ( ) - ThumbnailTableStartOffset ;
checkf ( ThumbnailTableStartOffset = = Summary . ThumbnailTableOffset , TEXT ( " %zd == %zd " ) , ThumbnailTableStartOffset , Summary . ThumbnailTableOffset ) ;
check ( ThumbnailTableSize = = SourceSection . Size ) ; // We only patch thumbnail table offsets, we should not be patching size
break ;
}
case EPatchedSection : : AssetRegistryData :
{
2023-12-06 11:52:23 -05:00
const int64 AssetRegistryDataStartOffset = Writer . Tell ( ) ;
checkf ( AssetRegistryDataStartOffset = = Summary . AssetRegistryDataOffset , TEXT ( " %zd == %zd " ) , AssetRegistryDataStartOffset , Summary . AssetRegistryDataOffset ) ;
2023-12-04 06:05:10 -05:00
// Manually write this back out, there isn't a nicely factored function to call for this
2023-12-06 11:52:23 -05:00
if ( AssetRegistryData . PkgData . DependencyDataOffset ! = INDEX_NONE )
2023-12-04 06:05:10 -05:00
{
2023-12-06 11:52:23 -05:00
Writer < < AssetRegistryData . PkgData . DependencyDataOffset ;
2023-12-04 06:05:10 -05:00
}
2023-12-06 11:52:23 -05:00
Writer < < AssetRegistryData . PkgData . ObjectCount ;
2023-12-04 06:05:10 -05:00
2023-12-06 11:52:23 -05:00
check ( AssetRegistryData . PkgData . ObjectCount = = AssetRegistryData . ObjectData . Num ( ) ) ;
for ( FAssetRegistryObjectData & ObjData : AssetRegistryData . ObjectData )
2023-12-04 06:05:10 -05:00
{
Writer < < ObjData . ObjectData . ObjectPath ;
Writer < < ObjData . ObjectData . ObjectClassName ;
Writer < < ObjData . ObjectData . TagCount ;
check ( ObjData . ObjectData . TagCount = = ObjData . TagData . Num ( ) ) ;
for ( UE : : AssetRegistry : : FDeserializeTagData & TagData : ObjData . TagData )
{
Writer < < TagData . Key ;
Writer < < TagData . Value ;
}
2023-10-06 05:54:04 -04:00
}
2023-12-06 11:52:23 -05:00
const int64 AssetRegistryDataSize = Writer . Tell ( ) - AssetRegistryDataStartOffset ;
const int64 Delta = AssetRegistryDataSize - SourceSection . Size ;
PatchSummaryOffsets ( Summary , AssetRegistryDataStartOffset , Delta ) ;
2023-12-04 06:05:10 -05:00
Summary . TotalHeaderSize + = ( int32 ) Delta ;
2023-10-06 05:54:04 -04:00
2023-12-06 11:52:23 -05:00
if ( AssetRegistryData . PkgData . DependencyDataOffset ! = INDEX_NONE )
2023-12-04 06:05:10 -05:00
{
// DependencyDataOffset is not relative but points to just after the rest of the AR data
// We will seek back and write this later
2023-12-06 11:52:23 -05:00
const int64 DependencyDataDelta = AssetRegistryDataStartOffset - SourceSection . Offset + Delta ;
AssetRegistryData . PkgData . DependencyDataOffset + = DependencyDataDelta ;
2023-10-06 05:54:04 -04:00
}
2023-12-04 06:05:10 -05:00
break ;
}
2023-11-15 04:52:12 -05:00
2023-12-04 06:05:10 -05:00
default :
UE_LOG ( LogAssetHeaderPatcher , Error , TEXT ( " Unexpected section for %s " ) , * SrcAsset ) ;
return EResult : : ErrorUnkownSection ;
2023-10-06 05:54:04 -04:00
}
if ( Writer . IsError ( ) )
{
UE_LOG ( LogAssetHeaderPatcher , Error , TEXT ( " Failed to write to %s " ) , * DstAsset ) ;
return EResult : : ErrorFailedToWriteToDestinationFile ;
}
}
{ // copy the last blob
int64 SizeToCopy = SrcBuffer . Num ( ) - LastSectionEndedAt ;
checkf ( SizeToCopy > = 0 , TEXT ( " Section last of %s \n %zd -> %zd %zd " ) , * SrcAsset , SrcBuffer . Num ( ) , LastSectionEndedAt , SizeToCopy ) ;
Writer . Serialize ( SrcBuffer . GetData ( ) + LastSectionEndedAt , SizeToCopy ) ;
}
if ( Writer . IsError ( ) )
{
UE_LOG ( LogAssetHeaderPatcher , Error , TEXT ( " Failed to write to %s " ) , * DstAsset ) ;
return EResult : : ErrorFailedToWriteToDestinationFile ;
}
// Re-write summary with patched offsets
Writer . Seek ( 0 ) ;
Writer < < Summary ;
{
// Re-write export table with patched offsets
// Patch Export table offsets now that we have patched all the header sections
Writer . Seek ( Summary . ExportOffset ) ;
const int64 ExportOffsetDelta = static_cast < int64 > ( Summary . TotalHeaderSize ) - SourceTotalHeaderSize ;
for ( FObjectExport & Export : ExportTable )
{
Export . SerialOffset + = ExportOffsetDelta ;
Writer < < Export ;
}
}
if ( Writer . IsError ( ) )
{
UE_LOG ( LogAssetHeaderPatcher , Error , TEXT ( " Failed to write to %s " ) , * DstAsset ) ;
return EResult : : ErrorFailedToWriteToDestinationFile ;
}
2023-12-06 11:52:23 -05:00
if ( AssetRegistryData . PkgData . DependencyDataOffset ! = INDEX_NONE )
2023-10-06 05:54:04 -04:00
{
// Re-write asset registry dependency data offset
Writer . Seek ( Summary . AssetRegistryDataOffset ) ;
2023-12-06 11:52:23 -05:00
Writer < < AssetRegistryData . PkgData . DependencyDataOffset ;
2023-10-06 05:54:04 -04:00
if ( Writer . IsError ( ) )
{
UE_LOG ( LogAssetHeaderPatcher , Error , TEXT ( " Failed to write to %s " ) , * DstAsset ) ;
return EResult : : ErrorFailedToWriteToDestinationFile ;
}
}
return EResult : : Success ;
}