EM_Task/CoreUObject/Public/UObject/LinkerLoad.h
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

1286 lines
50 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Serialization/ArchiveUObject.h"
#include "UObject/LazyObjectPtr.h"
#include "UObject/SoftObjectPtr.h"
#include "UObject/ObjectResource.h"
#include "UObject/Linker.h"
class FLinkerPlaceholderBase;
class IPakFile;
class ULinkerPlaceholderExportObject;
struct FScopedSlowTask;
struct FUntypedBulkData;
/*----------------------------------------------------------------------------
FLinkerLoad.
----------------------------------------------------------------------------*/
/**
* Helper struct to keep track of all objects needed by an export (recursive dependency caching)
*/
struct FDependencyRef
{
/** The Linker the export lives in */
FLinkerLoad* Linker;
/** Index into Linker's ExportMap for this object */
int32 ExportIndex;
/**
* Comparison operator
*/
bool operator==(const FDependencyRef& Other) const
{
return Linker == Other.Linker && ExportIndex == Other.ExportIndex;
}
/**
* Type hash implementation. Export indices are usually less than 100k, so are linker indices.
*
* @param Ref Reference to hash
* @return hash value
*/
friend uint32 GetTypeHash(const FDependencyRef& Ref);
};
/** Helper struct to keep track of the first time CreateImport() is called in the current callstack. */
struct FScopedCreateImportCounter
{
/**
* Constructor. Called upon CreateImport() entry.
* @param Linker - Current Linker
* @param Index - Index of the current Import
*/
FScopedCreateImportCounter(FLinkerLoad* Linker, int32 Index);
/** Destructor. Called upon CreateImport() exit. */
~FScopedCreateImportCounter();
/** Current load context object */
FUObjectSerializeContext* LoadContext;
/** Previously stored linker */
FLinkerLoad* PreviousLinker;
/** Previously stored index */
int32 PreviousIndex;
};
/**
* Handles loading Unreal package files, including reading UObject data from disk.
*/
class FAsyncArchive;
class FLinkerLoad
#if !WITH_EDITOR
final
#endif
: public FLinker
, public FArchiveUObject
{
// Friends.
friend class UObject;
friend class UPackageMap;
friend struct FAsyncPackage;
friend struct FAsyncPackage2;
friend struct FResolvingExportTracker;
protected:
/** Linker loading status. */
enum ELinkerStatus
{
/** Error occured when loading. */
LINKER_Failed = 0,
/** Operation completed successfully. */
LINKER_Loaded = 1,
/** Operation took more time than allowed. */
LINKER_TimedOut = 2
};
/** Verify result. */
enum EVerifyResult
{
/** Error occured when verifying import (can be fatal). */
VERIFY_Failed = 0,
/** Verify completed successfully. */
VERIFY_Success = 1,
/** Verify completed successfully and followed a redirector. */
VERIFY_Redirected = 2
};
// Variables.
public:
FORCEINLINE static ELinkerType::Type StaticType()
{
return ELinkerType::Load;
}
virtual ~FLinkerLoad();
/** Flags determining loading behavior. */
uint32 LoadFlags;
/** Indicates whether the imports for this loader have been verified */
bool bHaveImportsBeenVerified;
/** Indicates that this linker was created for a dynamic class package and will not use Loader */
bool bDynamicClassLinker;
UObject* TemplateForGetArchetypeFromLoader;
bool bForceSimpleIndexToObject;
bool bLockoutLegacyOperations;
/** True if Loader is FAsyncArchive */
bool bIsAsyncLoader;
FORCEINLINE FAsyncArchive* GetAsyncLoader()
{
return bIsAsyncLoader ? (FAsyncArchive*)Loader : nullptr;
}
FORCEINLINE const FLinkerInstancingContext& GetInstancingContext() const
{
return InstancingContext;
}
private:
/** True if the linker is currently deleting loader */
bool bIsDestroyingLoader;
/** Structured archive interface. Wraps underlying loader to provide contextual metadata to the values being written
* which ultimately allows text based serialization of the data
*/
FStructuredArchive* StructuredArchive;
FArchiveFormatterType* StructuredArchiveFormatter;
TOptional<FStructuredArchive::FRecord> StructuredArchiveRootRecord;
TArray<FStructuredArchiveChildReader*> ExportReaders;
/** The archive that actually reads the raw data from disk. */
FArchive* Loader;
/** The linker instancing context. */
FLinkerInstancingContext InstancingContext;
// Helper function to access the InstancingContext IsInstanced,
// returns false if WITH_EDITOR isn't defined.
bool IsContextInstanced() const;
// Helper function to access the InstancingContext,
// return ObjectName directly if WITH_EDITOR isn't defined.
FName InstancingContextRemap(FName ObjectName) const;
protected:
void SetLoader(FArchive* InLoader);
FArchive* GetLoader() const { return Loader; }
public:
/** Access the underlying archive. Note that if this is a text archive, the loader will point to a generic text file, and not a binary archive as
* in the past. General access to the underlying file format is discouraged and should only be done after checking that the linker package is what
* you expect it to be (i.e. !IsTextFormat())
*/
FArchive* GetLoader_Unsafe() const
{
return Loader;
}
bool HasLoader() const
{
return Loader != nullptr;
}
void DestroyLoader();
FORCEINLINE bool IsDestroyingLoader() const
{
return bIsDestroyingLoader;
}
/** The async package associated with this linker */
struct FAsyncPackage* AsyncRoot;
#if WITH_EDITOR
/** Bulk data that does not need to be loaded when the linker is loaded. */
TArray<FUntypedBulkData*> BulkDataLoaders;
#endif // WITH_EDITOR
/** Hash table for exports. */
static constexpr int32 ExportHashCount = 256;
TUniquePtr<int32[]> ExportHash;
/**
* List of imports and exports that must be serialized before other exports...all packed together, see FirstExportDependency
*/
TArray<FPackageIndex> PreloadDependencies;
/**
* List of external read dependencies that must be finished to load this package
*/
TArray<FExternalReadCallback> ExternalReadDependencies;
/**
* Utility functions to query the object name redirects list for previous names for a class
* @param CurrentClassPath The current name of the class, with a full path
* @param bIsInstance If true, we're an instance, so check instance only maps as well
* @return Names without path of all classes that were redirected to this name. Empty if none found.
*/
COREUOBJECT_API static TArray<FName> FindPreviousNamesForClass(FString CurrentClassPath, bool bIsInstance);
/**
* Utility functions to query the object name redirects list for the current name for a class
* @param OldClassName An old class name, without path
* @return Current full path of the class. It will be None if no redirect found
*/
COREUOBJECT_API static FName FindNewNameForClass(FName OldClassName, bool bIsInstance);
/**
* Utility functions to query the enum name redirects list for the current name for an enum
* @param OldEnumName An old enum name, without path
* @return Current full path of the enum. It will be None if no redirect found
*/
COREUOBJECT_API static FName FindNewNameForEnum(FName OldEnumName);
/**
* Utility functions to query the struct name redirects list for the current name for a struct
* @param OldStructName An old struct name, without path
* @return Current full path of the struct. It will be None if no redirect found
*/
COREUOBJECT_API static FName FindNewNameForStruct(FName OldStructName);
/**
* Utility functions to check the list of known missing packages and silence any warnings
* that may have occurred on load.
* @return true if the provided package is in the KnownMissingPackage list
*/
COREUOBJECT_API static bool IsKnownMissingPackage(FName PackageName);
/**
* Register that a package is now known missing and that it should silence future warnings/issues
*/
COREUOBJECT_API static void AddKnownMissingPackage(FName PackageName);
/**
* Register that a package is no longer known missing and that it should be searched for again in the future
* @return true if the provided package was removed from the KnownMissingPackage list
*/
COREUOBJECT_API static bool RemoveKnownMissingPackage(FName PackageName);
/**
*
*/
COREUOBJECT_API static void OnNewFileAdded(const FString& Filename);
COREUOBJECT_API static void OnPakFileMounted(const IPakFile& PakFile);
/**
* Checks if the linker has any objects in the export table that require loading.
*/
COREUOBJECT_API bool HasAnyObjectsPendingLoad() const;
/**
* Add a new redirect from old game name to new game name for ImportMap
*/
COREUOBJECT_API static void AddGameNameRedirect(const FName OldName, const FName NewName);
private:
// Variables used during async linker creation.
/** Current index into gatherable text data map, used by async linker creation for spreading out serializing text entries. */
int32 GatherableTextDataMapIndex;
/** Current index into import map, used by async linker creation for spreading out serializing importmap entries. */
int32 ImportMapIndex;
/** Current index into export map, used by async linker creation for spreading out serializing exportmap entries. */
int32 ExportMapIndex;
/** Current index into depends map, used by async linker creation for spreading out serializing dependsmap entries. */
int32 DependsMapIndex;
/** Current index into export hash map, used by async linker creation for spreading out hashing exports. */
int32 ExportHashIndex;
/** Whether we already serialized the package file summary. */
bool bHasSerializedPackageFileSummary : 1;
/** Whether we have already reconstructed the import/export tables for a text asset */
bool bHasReconstructedImportAndExportMap : 1;
/** Whether we already serialized preload dependencies. */
bool bHasSerializedPreloadDependencies : 1;
/** Whether we already fixed up import map. */
bool bHasFixedUpImportMap : 1;
/** Whether we already fixed up import map. */
bool bHasPopulatedInstancingContext : 1;
/** Used for ActiveClassRedirects functionality */
bool bFixupExportMapDone : 1;
/** Whether we already matched up existing exports. */
bool bHasFoundExistingExports : 1;
/** Whether we are already fully initialized. */
bool bHasFinishedInitialization : 1;
/** Whether we are gathering dependencies, can be used to streamline VerifyImports, etc */
bool bIsGatheringDependencies : 1;
/** Whether time limit is/ has been exceeded in current/ last tick. */
bool bTimeLimitExceeded : 1;
/** Whether to use a time limit for async linker creation. */
bool bUseTimeLimit : 1;
/** Whether to use the full time limit, even if we're blocked on I/O */
bool bUseFullTimeLimit : 1;
/** Call count of IsTimeLimitExceeded. */
int32 IsTimeLimitExceededCallCount;
/** Current time limit to use if bUseTimeLimit is true. */
float TimeLimit;
/** Time at begin of Tick function. Used for time limit determination. */
double TickStartTime;
#if WITH_EDITOR
/** Check to avoid multiple export duplicate fixups in case we don't save asset. */
bool bExportsDuplicatesFixed;
#endif // WITH_EDITOR
/** Id of the thread that created this linker. This is to guard against using this linker on other threads than the one it was created on **/
int32 OwnerThread;
/**
* Helper struct to keep track of background file reads
*/
struct FPackagePrecacheInfo
{
/** Synchronization object used to wait for completion of async read. Pointer so it can be copied around, etc */
FThreadSafeCounter* SynchronizationObject;
/** Memory that contains the package data read off disk */
void* PackageData;
/** Size of the buffer pointed to by PackageData */
int64 PackageDataSize;
/**
* Basic constructor
*/
FPackagePrecacheInfo()
: SynchronizationObject(NULL), PackageData(NULL), PackageDataSize(0)
{
}
/**
* Destructor that will free the sync object
*/
~FPackagePrecacheInfo()
{
delete SynchronizationObject;
}
};
private:
/** Allows access to UTexture2D::StaticClass() without linking Core with Engine */
static UClass* UTexture2DStaticClass;
static FName NAME_LoadErrors;
/** Makes sure the deprecated active redirects inis have been read */
static bool bActiveRedirectsMapInitialized;
#if WITH_EDITOR
/** Feedback scope that is created to house the slow task of an asynchronous linker load. Raw ptr so we don't pull in TUniquePtr for everything. */
FScopedSlowTask* LoadProgressScope;
/** Test whether we should report progress or not */
FORCEINLINE bool ShouldReportProgress() const
{
return !IsAsyncLoading() && (LoadFlags & (LOAD_Quiet | LOAD_Async)) == 0;
}
/** Test whether we should report progress or not based on how recently we last created a progress task */
bool ShouldCreateThrottledSlowTask() const;
virtual void PushDebugDataString(const FName& DebugData) override
{
FArchiveUObject::PushDebugDataString(DebugData);
if (Loader)
Loader->PushDebugDataString(DebugData);
}
virtual void PopDebugDataString() override
{
FArchiveUObject::PopDebugDataString();
if (Loader)
Loader->PopDebugDataString();
}
#endif
public:
/**
* Initialize the static variables
*/
COREUOBJECT_API static void StaticInit(UClass* InUTexture2DStaticClass);
/**
* Add redirects to FLinkerLoad static map
*/
static void CreateActiveRedirectsMap(const FString& GEngineIniName);
/**
* Test whether the given package index is a valid import or export in this package
*/
bool IsValidPackageIndex(FPackageIndex InIndex);
/**
* Locates package index for a UPackage import
*/
COREUOBJECT_API bool FindImportPackage(FName PackageName, FPackageIndex& PackageIdx);
/**
* Locates the class adjusted index and its package adjusted index for a given class name in the import map
*/
COREUOBJECT_API bool FindImportClassAndPackage(FName ClassName, FPackageIndex& ClassIdx, FPackageIndex& PackageIdx);
/**
* Attempts to find the index for the given class object in the import list and adds it + its package if it does not exist
*/
COREUOBJECT_API bool CreateImportClassAndPackage(FName ClassName, FName PackageName, FPackageIndex& ClassIdx, FPackageIndex& PackageIdx);
/**
* Allows object instances to be converted to other classes upon loading a package
*/
COREUOBJECT_API ELinkerStatus FixupExportMap();
/**
* Returns whether linker has finished (potentially) async initialization.
*
* @return true if initialized, false if pending.
*/
FORCEINLINE bool HasFinishedInitialization() const
{
return bHasFinishedInitialization;
}
/** Returns ID of the thread that created this linker */
FORCEINLINE int32 GetOwnerThreadId() const
{
return OwnerThread;
}
/**
* If this archive is a FLinkerLoad or FLinkerSave, returns a pointer to the FLinker portion.
*/
virtual FLinker* GetLinker() override { return this; }
/** Flush Loader Cache */
virtual void FlushCache() override;
/**
* Creates and returns a FLinkerLoad object.
*
* @param Parent Parent object to load into, can be NULL (most likely case)
* @param Filename Name of file on disk to load
* @param LoadFlags Load flags determining behavior
* @param InLoader Loader archive override
* @param InstancingContext Context to remap package name when loading a package on disk into a package with a different name
*
* @return new FLinkerLoad object for Parent/ Filename
*/
COREUOBJECT_API static FLinkerLoad* CreateLinker(FUObjectSerializeContext* LoadContext, UPackage* Parent, const TCHAR* Filename, uint32 LoadFlags, FArchive* InLoader = nullptr, const FLinkerInstancingContext* InstancingContext = nullptr);
void Verify();
COREUOBJECT_API FName GetExportClassPackage(int32 i);
virtual FString GetArchiveName() const override;
#if WITH_EDITORONLY_DATA
/**
* Recursively gathers the dependencies of a given export (the recursive chain of imports
* and their imports, and so on)
* @param ExportIndex Index into the linker's ExportMap that we are checking dependencies
* @param Dependencies Set of all dependencies needed
* @param bSkipLoadedObjects Whether to skip already loaded objects when gathering dependencies
*/
COREUOBJECT_API void GatherExportDependencies(int32 ExportIndex, TSet<FDependencyRef>& Dependencies, bool bSkipLoadedObjects = true);
/**
* Recursively gathers the dependencies of a given import (the recursive chain of imports
* and their imports, and so on)
* @param ImportIndex Index into the linker's ImportMap that we are checking dependencies
* @param Dependencies Set of all dependencies needed
* @param bSkipLoadedObjects Whether to skip already loaded objects when gathering dependencies
*/
COREUOBJECT_API void GatherImportDependencies(int32 ImportIndex, TSet<FDependencyRef>& Dependencies, bool bSkipLoadedObjects = true);
#endif
/**
* A wrapper around VerifyImportInner. If the VerifyImportInner (previously VerifyImport) fails, this function
* will look for a UObjectRedirector that will point to the real location of the object. You will see this if
* an object was renamed to a different package or group, but something that was referencing the object was not
* not currently open. (Rename fixes up references of all loaded objects, but naturally not for ones that aren't
* loaded).
*
* @param ImportIndex The index into this package's ImportMap to verify
* @return Verify import result
*/
EVerifyResult VerifyImport(int32 ImportIndex);
/**
* Loads all objects in package.
*
* @param bForcePreload Whether to explicitly call Preload (serialize) right away instead of being
* called from EndLoad()
*/
COREUOBJECT_API void LoadAllObjects(bool bForcePreload);
/**
* Returns the ObjectName associated with the resource indicated.
*
* @param ResourceIndex location of the object resource
*
* @return ObjectName for the FObjectResource at ResourceIndex, or NAME_None if not found
*/
FName ResolveResourceName(FPackageIndex ResourceIndex);
int32 FindExportIndex(FName ClassName, FName ClassPackage, FName ObjectName, FPackageIndex ExportOuterIndex);
/**
* Function to create the instance of, or verify the presence of, an object as found in this Linker.
*
* @param ObjectClass The class of the object
* @param ObjectName The name of the object
* @param Outer Optional outer that this object must be in (for finding objects in a specific group when there are multiple groups with the same name)
* @param LoadFlags Flags used to determine if the object is being verified or should be created
* @param Checked Whether or not a failure will throw an error
* @return The created object, or (UObject*)-1 if this is just verifying
*/
UObject* Create(UClass* ObjectClass, FName ObjectName, UObject* Outer, uint32 InLoadFlags, bool Checked);
/**
* Serialize the object data for the specified object from the unreal package file. Loads any
* additional resources required for the object to be in a valid state to receive the loaded
* data, such as the object's Outer, Class, or ObjectArchetype.
*
* When this function exits, Object is guaranteed to contain the data stored that was stored on disk.
*
* @param Object The object to load data for. If the data for this object isn't stored in this
* FLinkerLoad, routes the call to the appropriate linker. Data serialization is
* skipped if the object has already been loaded (as indicated by the RF_NeedLoad flag
* not set for the object), so safe to call on objects that have already been loaded.
* Note that this function assumes that Object has already been initialized against
* its template object.
* If Object is a UClass and the class default object has already been created, calls
* Preload for the class default object as well.
*/
COREUOBJECT_API void Preload(UObject* Object) override;
/**
* Before loading a persistent object from disk, this function can be used to discover
* the object in memory. This could happen in the editor when you save a package (which
* destroys the linker) and then play PIE, which would cause the Linker to be
* recreated. However, the objects are still in memory, so there is no need to reload
* them.
*
* @param ExportIndex The index of the export to hunt down
* @return The object that was found, or null if it wasn't found
*/
UObject* FindExistingExport(int32 ExportIndex);
/**
* @param ImportIndex The index of the import to hunt down
* @return The object that was found, or null if it wasn't found
*/
UObject* FindExistingImport(int32 ImportIndex);
/**
* Builds a string containing the full path for a resource in the export table.
*
* @param OutPathName [out] Will contain the full path for the resource
* @param ResourceIndex Index of a resource in the export table
*/
void BuildPathName(FString& OutPathName, FPackageIndex ExportIndex) const;
/**
* Checks if the specified export should be loaded or not.
* Performs similar checks as CreateExport().
*
* @param ExportIndex Index of the export to check
* @return true of the export should be loaded
*/
COREUOBJECT_API bool WillTextureBeLoaded(UClass* Class, int32 ExportIndex);
/**
* Called when an object begins serializing property data using script serialization.
*/
virtual void MarkScriptSerializationStart(const UObject* Obj) override;
/**
* Called when an object stops serializing property data using script serialization.
*/
virtual void MarkScriptSerializationEnd(const UObject* Obj) override;
virtual UObject* GetArchetypeFromLoader(const UObject* Obj) override;
/**
* Looks for an existing linker for the given package, without trying to make one if it doesn't exist
*/
COREUOBJECT_API static FLinkerLoad* FindExistingLinkerForPackage(const UPackage* Package);
/**
* Replaces OldObject's entry in its linker with NewObject, so that all subsequent loads of OldObject will return NewObject.
* This is used to update instanced components that were serialized out, but regenerated during compile-on-load
*
* OldObject will be consigned to oblivion, and NewObject will take its place.
*
* WARNING!!! This function is potentially very dangerous! It should only be used at very specific times, and in very specific cases.
* If you're unsure, DON'T TRY TO USE IT!!!
*/
COREUOBJECT_API static void PRIVATE_PatchNewObjectIntoExport(UObject* OldObject, UObject* NewObject);
/**
* Wraps a call to the package linker's ResolveAllImports().
*
* @param Package The package whose imports you want all loaded.
*
* WARNING!!! This function shouldn't be used carelessly, and serves as a
* hacky entrypoint to FLinkerLoad's privates. It should only
* be used at very specific times, and in very specific cases.
* If you're unsure, DON'T TRY TO USE IT!!!
*/
COREUOBJECT_API static void PRIVATE_ForceLoadAllDependencies(UPackage* Package);
/**
* Invalidates the future loading of a specific object, so that subsequent loads will fail
* This is used to invalidate sub objects of a replaced object that may no longer be valid
*/
COREUOBJECT_API static void InvalidateExport(UObject* OldObject);
/** Used by Matinee to fixup component renaming */
COREUOBJECT_API static FName FindSubobjectRedirectName(const FName& Name, UClass* Class);
#if WITH_EDITOR
COREUOBJECT_API static bool GetPreloadingEnabled();
COREUOBJECT_API static void SetPreloadingEnabled(bool bEnabled);
COREUOBJECT_API static bool TryGetPreloadedLoader(FArchive*& OutLoader, const TCHAR* FileName);
private:
static bool bPreloadingEnabled;
public:
#endif
/**
* Adds external read dependency
*
* @return true if dependency has been added
*/
virtual bool AttachExternalReadDependency(FExternalReadCallback& ReadCallback) override;
/**
* Finalizes external dependencies till time limit is exceeded
*
* @return true if all dependencies are finished, false otherwise
*/
bool FinishExternalReadDependencies(double TimeLimit);
private:
#if WITH_EDITOR
/**
* Nulls duplicated exports and fixes indexes in ExportMap to point to original objects instead of duplicates.
*/
void FixupDuplicateExports();
/**
* Replaces all instances of OldIndex in ExportMap with NewIndex.
*/
void ReplaceExportIndexes(const FPackageIndex& OldIndex, const FPackageIndex& NewIndex);
#endif // WITH_EDITOR
UObject* CreateExport(int32 Index);
/**
* Creates export and preload if requested.
*
* @param Index Index of the export in export map.
* @param bForcePreload Whether to explicitly call Preload (serialize)
* right away instead of being called from EndLoad().
*
* @return Created object.
*/
UObject* CreateExportAndPreload(int32 ExportIndex, bool bForcePreload = false);
/**
* Utility function for easily retrieving the specified export's UClass.
*
* @param ExportIndex Index of the export you want a class for.
* @return The class that the specified export's ClassIndex references.
*/
UClass* GetExportLoadClass(int32 ExportIndex);
#if WITH_EDITORONLY_DATA
/**
* Looks for and loads meta data object from export map.
*
* @param bForcePreload Whether to explicitly call Preload (serialize)
* right away instead of being called from EndLoad().
*
* @return If found returns index of meta data object in the export map,
* INDEX_NONE otherwise.
*/
int32 LoadMetaDataFromExportMap(bool bForcePreload);
#endif
UObject* CreateImport(int32 Index);
/**
* Determines if the specified import belongs to a native "compiled in"
* package (as opposed to an asset-file package). Recursive if the
* specified import is not a package itself.
*
* @param ImportIndex An index into the ImportMap, defining the import you wish to check.
* @return True if the specified import comes from (or is) a "compiled in" package, otherwise false (it is an asset import).
*/
bool IsImportNative(const int32 ImportIndex) const;
/**
* Attempts to lookup and return the corresponding FLinkerLoad object for
* the specified import WITHOUT invoking a load, or continuing to load
* the import package (will only return one if it has already been
* created... could still be in the process of loading).
*
* @param ImportIndex Specifies the import that you would like a linker for.
* @return The imports associated linker (null if it hasn't been created yet).
*/
FLinkerLoad* FindExistingLinkerForImport(int32 ImportIndex) const;
UObject* IndexToObject(FPackageIndex Index);
void DetachExport(int32 i);
// FArchive interface.
/**
* Hint the archive that the region starting at passed in offset and spanning the passed in size
* is going to be read soon and should be precached.
*
* The function returns whether the precache operation has completed or not which is an important
* hint for code knowing that it deals with potential async I/O. The archive is free to either not
* implement this function or only partially precache so it is required that given sufficient time
* the function will return true. Archives not based on async I/O should always return true.
*
* This function will not change the current archive position.
*
* @param PrecacheOffset Offset at which to begin precaching.
* @param PrecacheSize Number of bytes to precache
* @return false if precache operation is still pending, true otherwise
*/
FORCEINLINE virtual bool Precache(int64 PrecacheOffset, int64 PrecacheSize) override
{
return bDynamicClassLinker || Loader->Precache(PrecacheOffset, PrecacheSize);
}
#if WITH_EDITOR
/**
* Attaches/ associates the passed in bulk data object with the linker.
*
* @param Owner UObject owning the bulk data
* @param BulkData Bulk data object to associate
*/
virtual void AttachBulkData(UObject* Owner, FUntypedBulkData* BulkData) override;
/**
* Detaches the passed in bulk data object from the linker.
*
* @param BulkData Bulk data object to detach
* @param bEnsureBulkDataIsLoaded Whether to ensure that the bulk data is loaded before detaching
*/
virtual void DetachBulkData(FUntypedBulkData* BulkData, bool bEnsureBulkDataIsLoaded) override;
/**
* Detaches all attached bulk data objects.
*
* @param bEnsureBulkDataIsLoaded Whether to ensure that the bulk data is loaded before detaching
*/
#endif
void DetachAllBulkData(bool bEnsureBulkDataIsLoaded);
public:
/**
* Detaches linker from bulk data.
*/
COREUOBJECT_API void LoadAndDetachAllBulkData();
/**
* Detaches linker from bulk data/ exports and removes itself from array of loaders.
*/
COREUOBJECT_API void Detach();
private:
FORCEINLINE virtual void Seek(int64 InPos) override
{
Loader->Seek(InPos);
}
FORCEINLINE virtual int64 Tell() override
{
return Loader->Tell();
}
FORCEINLINE virtual int64 TotalSize() override
{
return Loader->TotalSize();
}
// this fixes the warning : 'ULinkerSave::Serialize' hides overloaded virtual function
using FLinker::Serialize;
FORCEINLINE virtual void Serialize(void* V, int64 Length) override
{
checkSlow(FPlatformTLS::GetCurrentThreadId() == OwnerThread);
#if WITH_EDITOR
Loader->SetSerializedProperty(GetSerializedProperty());
#endif
Loader->Serialize(V, Length);
}
using FArchiveUObject::operator<<; // For visibility of the overloads we don't override
virtual FArchive& operator<<(UObject*& Object) override;
FORCEINLINE virtual FArchive& operator<<(FLazyObjectPtr& LazyObjectPtr) override
{
FArchive& Ar = *this;
FUniqueObjectGuid ID;
Ar << ID;
LazyObjectPtr = ID;
return Ar;
}
FORCEINLINE virtual FArchive& operator<<(FSoftObjectPtr& Value) override
{
FArchive& Ar = *this;
FSoftObjectPath ID;
ID.Serialize(Ar);
Value = ID;
return Ar;
}
void BadNameIndexError(int32 NameIndex);
FORCEINLINE virtual FArchive& operator<<(FName& Name) override
{
FArchive& Ar = *this;
int32 NameIndex;
Ar << NameIndex;
int32 Number = 0;
Ar << Number;
if (NameMap.IsValidIndex(NameIndex))
{
// if the name wasn't loaded (because it wasn't valid in this context)
FNameEntryId MappedName = NameMap[NameIndex];
// simply create the name from the NameMap's name and the serialized instance number
Name = FName::CreateFromDisplayId(MappedName, Number);
}
else
{
Name = FName();
BadNameIndexError(NameIndex);
SetCriticalError();
}
return *this;
}
/**
* Safely verify that an import in the ImportMap points to a good object. This decides whether or not
* a failure to load the object redirector in the wrapper is a fatal error or not (return value)
*
* @param i The index into this packages ImportMap to verify
* @param WarningSuffix [out] additional information about the load failure that should be appended to
* the name of the object in the load failure dialog.
*
* @return true if the wrapper should crash if it can't find a good object redirector to load
*/
bool VerifyImportInner(const int32 ImportIndex, FString& WarningSuffix);
//
// FLinkerLoad creation helpers BEGIN
//
/**
* Creates a FLinkerLoad object for async creation. Tick has to be called manually till it returns
* true in which case the returned linker object has finished the async creation process.
*
* @param Parent Parent object to load into, can be NULL (most likely case)
* @param Filename Name of file on disk to load
* @param LoadFlags Load flags determining behavior
* @param InstancingContext Context to remap package name when loading a package on disk into a package with a different name
*
* @return new FLinkerLoad object for Parent/ Filename
*/
COREUOBJECT_API static FLinkerLoad* CreateLinkerAsync(FUObjectSerializeContext* LoadContext, UPackage* Parent, const TCHAR* Filename, uint32 LoadFlags, const FLinkerInstancingContext* InstancingContext, TFunction<void()>&& InSummaryReadyCallback);
protected: // Daniel L: Made this protected so I can override the constructor and create a custom loader to load the header of the linker in the DiffFilesCommandlet
/**
* Ticks an in-flight linker and spends InTimeLimit seconds on creation. This is a soft time limit used
* if bInUseTimeLimit is true.
*
* @param InTimeLimit Soft time limit to use if bInUseTimeLimit is true
* @param bInUseTimeLimit Whether to use a (soft) timelimit
* @param bInUseFullTimeLimit Whether to use the entire time limit, even if blocked on I/O
*
* @return true if linker has finished creation, false if it is still in flight
*/
ELinkerStatus Tick(float InTimeLimit, bool bInUseTimeLimit, bool bInUseFullTimeLimit, TMap<TPair<FName, FPackageIndex>, FPackageIndex>* ObjectNameWithOuterToExportMap);
/**
* Private constructor, passing arguments through from CreateLinker.
*
* @param Parent Parent object to load into, can be NULL (most likely case)
* @param Filename Name of file on disk to load
* @param LoadFlags Load flags determining behavior
* @param InstancingContext The instancing context for remapping imports if needed.
*/
FLinkerLoad(UPackage* InParent, const TCHAR* InFilename, uint32 InLoadFlags, FLinkerInstancingContext InstancingContext = FLinkerInstancingContext());
private:
/**
* Returns whether the time limit allotted has been exceeded, if enabled.
*
* @param CurrentTask description of current task performed for logging spilling over time limit
* @param Granularity Granularity on which to check timing, useful in cases where FPlatformTime::Seconds is slow (e.g. PC)
*
* @return true if time limit has been exceeded (and is enabled), false otherwise (including if time limit is disabled)
*/
COREUOBJECT_API bool IsTimeLimitExceeded(const TCHAR* CurrentTask, int32 Granularity = 1);
protected: // Daniel L: Made this protected so I can override the constructor and create a custom loader to load the header of the linker in the DiffFilesCommandlet
/**
* Creates loader used to serialize content.
*/
ELinkerStatus CreateLoader(
TFunction<void()>&& InSummaryReadyCallback);
private:
/**
* Start the process of serializing the package file summary if needed
*/
ELinkerStatus SerializePackageFileSummary();
/**
* Does the actual serialization of the package file summary.
*/
ELinkerStatus SerializePackageFileSummaryInternal();
/**
* Updates the linker, loader and root package with data from the package file summary.
* */
ELinkerStatus UpdateFromPackageFileSummary();
/**
* Serializes the name map.
*/
ELinkerStatus SerializeNameMap();
/**
* Serializes the import map.
*/
ELinkerStatus SerializeImportMap();
/**
* Fixes up the import map, performing remapping for backward compatibility and such.
*/
ELinkerStatus FixupImportMap();
/**
* Generate remapping for the instancing context if this is an instanced package.
*/
ELinkerStatus PopulateInstancingContext();
/**
* Serializes the export map.
*/
ELinkerStatus SerializeExportMap();
#if WITH_TEXT_ARCHIVE_SUPPORT
/**
* Create an import and export table when loading a text asset.
*/
ELinkerStatus ReconstructImportAndExportMap();
#endif
ELinkerStatus SerializeDependsMap();
ELinkerStatus SerializePreloadDependencies();
/** Sets the basic linker archive info */
void ResetStatusInfo();
/** For a given full object path, find or create the associated import or export table record. Used when loading text assets which only store object paths */
FPackageIndex FindOrCreateImportOrExport(const FString& InFullPath);
/** For the given object and class info, find or create an associated import record. Used when loading text assets which only store object paths */
FPackageIndex FindOrCreateImport(const FName InObjectName, const FName InClassName, const FName InClassPackageName);
public:
/**
* Serializes the gatherable text data container.
*/
COREUOBJECT_API ELinkerStatus SerializeGatherableTextDataMap(bool bForceEnableForCommandlet = false);
/**
* Serializes thumbnails
*/
COREUOBJECT_API ELinkerStatus SerializeThumbnails(bool bForceEnableForCommandlet = false);
/** Inform the archive that blueprint finalization is pending. */
virtual void ForceBlueprintFinalization() override;
/**
* Query method to help handle recursive behavior. When this returns true,
* this linker is in the middle of, or is about to call FinalizeBlueprint()
* (for a blueprint class somewhere in the current callstack). Needed when
* we get to finalizing a sub-class before we've finished finalizing its
* super (so we know we need to finish finalizing the super first).
*
* @return True if FinalizeBlueprint() is currently being ran (or about to be ran) for an export (Blueprint) class.
*/
bool IsBlueprintFinalizationPending() const;
/**
* Gives external code the ability to create FLinkerPlaceholderBase objects
* in place of loads that may violate the LOAD_DeferDependencyLoads state.
* This will only produce a placeholder if LOAD_DeferDependencyLoads is set
* for this linker.
*
* NOTE: For now, this will only produce UClass placeholders, as that is the
* only type we've identified needing.
*
* @param ObjectType The expected type of the object you want to defer loading of.
* @param ObjectPath The full object/package path for the expected object.
* @return A FLinkerPlaceholderBase UObject that can be used in place of the import dependency.
*/
UObject* RequestPlaceholderValue(UClass* ObjectType, const TCHAR* ObjectPath);
private:
/**
* Regenerates/Refreshes a blueprint class
*
* @param LoadClass Instance of the class currently being loaded and which is the parent for the blueprint
* @param ExportObject Current object bein exported
* @return Returns true if regeneration was successful, otherwise false
*/
bool RegenerateBlueprintClass(UClass* LoadClass, UObject* ExportObject);
/**
* Determines if the specified import should be deferred. If so, it will
* instantiate a placeholder object in its place.
*
* @param ImportIndex An index into this linker's ImportMap, specifying which import to check.
* @return True if the specified import was deferred, other wise false (it is ok to load it).
*/
bool DeferPotentialCircularImport(const int32 ImportIndex);
#if WITH_EDITOR
/**
* Determines if the Object Import error should be suppressed
*
* @param ImportIndex Internal index into this linker's ImportMap, references the import to check for suppression.
* @return True if the import error should be suppressed
*/
bool IsSuppressableBlueprintImportError(int32 ImportIndex) const;
#endif // WITH_EDITOR
/**
* Stubs in a ULinkerPlaceholderExportObject for the specified export (if
* one is required, meaning: the export's LoadClass is not fully formed).
* This should rarely happen, but has been seen in cyclic Blueprint
* scenarios involving Blueprinted components.
*
* @param ExportIndex Identifies the export you want deferred.
* @param Outer The outer of the export to potentially defer
* @return True if the export has been deferred (and should not be loaded).
*/
bool DeferExportCreation(const int32 ExportIndex, UObject* Outer);
/**
* Iterates through this linker's ExportMap, looking for the corresponding
* class-default-object for the specified class (assumes that the supplied
* class is an export itself, making this a Blueprint package).
*
* @param LoadClass The Blueprint class that this linker is in charge of loading (also belonging to its ExportMap).
* @return An index into this linker's ExportMap array (INDEX_NONE if the CDO wasn't found).
*/
int32 FindCDOExportIndex(UClass* LoadClass);
/**
* Combs the ImportMap for any imports that were deferred, and then creates
* them (via CreateImport).
*
* @param LoadStruct The (Blueprint) class or struct that you want resolved (so that it no longer contains dependency placeholders).
*/
void ResolveDeferredDependencies(UStruct* LoadStruct);
/**
* Loads the import that the Placeholder was initially stubbed in for (NOTE:
* this could cause recursive behavior), and then replaces all known
* placeholder references with the proper class.
*
* @param Placeholder A ULinkerPlaceholderClass that was substituted in place of a deferred dependency.
* @param ReferencingClass The (Blueprint) class that was loading, while we deferred dependencies (now referencing the placeholder).
* @param ObjectPath Optional param that denotes the full object/package path for the object the placeholder is supposed to represent
* (used when the passed placeholder is not tied to an import in the linker's ImportMap).
* @return The number of placeholder references replaced (could be none, if this was recursively resolved).
*/
int32 ResolveDependencyPlaceholder(class FLinkerPlaceholderBase* Placeholder, UClass* ReferencingClass = nullptr, const FName ObjectPath = NAME_None);
/**
* Query method to help catch recursive behavior. When this returns true, a
* dependency placeholder is in the middle of being resolved by
* ResolveDependencyPlaceholder(). Used so a nested call would know to
* complete that placeholder before continuing.
*
* @return True if ResolveDependencyPlaceholder() is being ran on a placeholder that has yet to be resolved.
*/
bool HasUnresolvedDependencies() const;
/**
* Iterates through the ImportMap and calls CreateImport() for every entry,
* creating/loading each import as we go. This also makes sure that class
* imports have had ResolveDeferredDependencies() completely executed for
* them (even those already running through it earlier in the callstack).
*/
void ResolveAllImports();
/**
* Takes the supplied serialized class and serializes in its CDO, then
* regenerates both.
*
* @param LoadClass The loaded blueprint class (assumes that it has been fully loaded/serialized).
*/
void FinalizeBlueprint(UClass* LoadClass);
/**
* Combs the ExportMap for any stubbed in ULinkerPlaceholderExportObjects,
* and finalizes the real export's class before actually creating it
* (exports are deferred when their class isn't fully formed at the time
* CreateExport() is called). Also, this function ensures that deferred CDO
* serialization is executed (expects its class to be fully resolved at this
* point).
*
* @param LoadClass A fully loaded/serialized class that may have property references to placeholder export objects (in need of fix-up).
*/
void ResolveDeferredExports(UClass* LoadClass);
/** Called when a class is loaded and its now safe to load deferred instances */
void ResolvePlaceholder(ULinkerPlaceholderExportObject* Placeholder);
/** Helper function to recursively resolve placeholders that were waiting for their outer */
void ResolvedDeferredSubobjects(ULinkerPlaceholderExportObject* OwningPlaceholder);
/**
* Sometimes we have to instantiate an export object that is of an imported
* type, and sometimes in those scenarios (thanks to cyclic dependencies)
* the type class could be a Blueprint type that is half resolved. To avoid
* having to re-instance objects on load, we have to ensure that the class
* is fully regenerated before we spawn any instances of it. That's where
* this function comes in. It will make sure that the specified class is
* fully loaded, finalized, and regenerated.
*
* NOTE: be wary, if called in the wrong place, then this could introduce
* nasty infinite recursion!
*
* @param ImportClass The class you want to make sure is fully regenerated.
* @return True if the class could be regenerated (false if it didn't have its linker set).
*/
bool ForceRegenerateClass(UClass* ImportClass);
/**
* Checks to see if an export (or one up its outer chain) is currently
* in the middle of having its class dependency force-regenerated. This
* function is meant to help avoid unnecessary recursion, as
* ForceRegenerateClass() does nothing itself to stave off infinite
* recursion.
*
* @param ExportIndex Identifies the export you're about to call CreateExport() on.
* @return True if the specified export's class (or one up its outer chain) is currently being force-regenerated.
*/
bool IsExportBeingResolved(int32 ExportIndex);
void ResetDeferredLoadingState();
bool HasPerformedFullExportResolvePass();
/** Finds import, tries to fall back to dynamic class if the object could not be found */
UObject* FindImport(UClass* ImportClass, UObject* ImportOuter, const TCHAR* Name);
/** Finds import, tries to fall back to dynamic class if the object could not be found */
static UObject* FindImportFast(UClass* ImportClass, UObject* ImportOuter, FName Name, bool bAnyPackage = false);
/** Fills all necessary information for constructing dynamic type package linker */
void CreateDynamicTypeLoader();
#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
/**
* For deferring dependency loads, we block CDO serialization until the
* class if complete. If we attempt to serialize the CDO while that is
* happening, we instead defer it and record the export's index here (so we
* can return to it later).
*/
bool bForceBlueprintFinalization;
/**
* Index of the CDO that should be used for blueprint finalization, may be INDEX_NONE
* in the case of some legacy content.
*/
int32 DeferredCDOIndex;
/**
* Used to track dependency placeholders currently being resolved inside of
* ResolveDependencyPlaceholder()... utilized for nested reentrant behavior,
* to make sure this placeholder is completely resolved before continuing on
* to the next.
*/
TArray<class FLinkerPlaceholderBase*> ResolvingPlaceholderStack;
/**
* Internal list to track imports that were deferred, but don't belong to
* the ImportMap (thinks ones loaded through config files via FProperty::ImportText).
*/
TMap<FName, FLinkerPlaceholderBase*> ImportPlaceholders;
#endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
/**
* Creates the export hash.
*/
ELinkerStatus CreateExportHash();
/**
* Finds existing exports in memory and matches them up with this linker. This is required for PIE to work correctly
* and also for script compilation as saving a package will reset its linker and loading will reload/ replace existing
* objects without a linker.
*/
ELinkerStatus FindExistingExports();
/**
* Finalizes linker creation, adding linker to loaders array and potentially verifying imports.
*/
ELinkerStatus FinalizeCreation(TMap<TPair<FName, FPackageIndex>, FPackageIndex>* ObjectNameWithOuterToExportMap);
//
// FLinkerLoad creation helpers END
//
private:
#if WITH_TEXT_ARCHIVE_SUPPORT
// Cache of the export names in a text asset. Allows us to enter those export slots by index rather than needing to reconstruct the name
TArray<FName> OriginalExportNames;
// Function to get a slot for a given export
FStructuredArchiveSlot GetExportSlot(FPackageIndex InExportIndex);
#endif
public:
//~ FArchive interface
COREUOBJECT_API virtual void SetSerializeContext(FUObjectSerializeContext* InLoadContext) override;
COREUOBJECT_API virtual FUObjectSerializeContext* GetSerializeContext() override;
};
// used by the EDL at boot time to coordinate loading with what is going on with the deferred registration stuff
enum class ENotifyRegistrationType
{
NRT_Class,
NRT_ClassCDO,
NRT_Struct,
NRT_Enum,
NRT_Package,
};
enum class ENotifyRegistrationPhase
{
NRP_Added,
NRP_Started,
NRP_Finished,
};
COREUOBJECT_API void NotifyRegistrationEvent(const TCHAR* PackageName, const TCHAR* Name, ENotifyRegistrationType NotifyRegistrationType, ENotifyRegistrationPhase NotifyRegistrationPhase, UObject* (*InRegister)() = nullptr, bool InbDynamic = false);
COREUOBJECT_API void NotifyRegistrationComplete();