/** * Returns the licensee version of the linker for this object. * * @return the licensee version of the engine's package file when this object * was last saved, or GPackageFileLicenseeVersion (current version) if * this object does not have a linker, which indicates that * a) this object is a native only class, or * b) this object's linker has been detached, in which case it is already fully loaded */ int32 UObjectBaseUtility::GetLinkerLicenseeUE4Version() const { ULinkerLoad* Loader = GetLinker(); // No linker. if( Loader == NULL ) { // the _Linker reference is never set for the top-most UPackage of a package (the linker root), so if this object // is the linker root, find our loader in the global list. if( GetOutermost() == this ) { Loader = ULinkerLoad::FindExistingLinkerForPackage(const_cast<UPackage*>(CastChecked<UPackage>((const UObject*)this))); } } if ( Loader != NULL ) { // We have a linker so we can return its version. return Loader->LicenseeUE4Ver(); } else { // We don't have a linker associated as we e.g. might have been saved or had loaders reset, ... return GPackageFileLicenseeUE4Version; } }
/** * Return the path name of the UObject represented by the specified import. * (can be used with StaticFindObject) * * @param ImportIndex index into the ImportMap for the resource to get the name for * * @return the path name of the UObject represented by the resource at ImportIndex */ FString ULinker::GetImportPathName(int32 ImportIndex) { ULinkerLoad* Loader = dynamic_cast<ULinkerLoad*>(this); FString Result; for (FPackageIndex LinkerIndex = FPackageIndex::FromImport(ImportIndex); !LinkerIndex.IsNull();) { FObjectResource Resource = ImpExp(LinkerIndex); bool bSubobjectDelimiter=false; if (Result.Len() > 0 && Loader != NULL && Loader->GetClassName(LinkerIndex) != NAME_Package && (Resource.OuterIndex.IsNull() || Loader->GetClassName(Resource.OuterIndex) == NAME_Package) ) { bSubobjectDelimiter = true; } // don't append a dot in the first iteration if ( Result.Len() > 0 ) { if ( bSubobjectDelimiter ) { Result = FString(SUBOBJECT_DELIMITER) + Result; } else { Result = FString(TEXT(".")) + Result; } } Result = Resource.ObjectName.ToString() + Result; LinkerIndex = Resource.OuterIndex; } return Result; }
void FAssetFixUpRedirectors::DeleteRedirectors(TArray<FRedirectorRefs>& RedirectorsToFix) const { TArray<UObject*> ObjectsToDelete; for ( auto RedirectorIt = RedirectorsToFix.CreateIterator(); RedirectorIt; ++RedirectorIt ) { FRedirectorRefs& RedirectorRefs = *RedirectorIt; if ( RedirectorRefs.bRedirectorValidForFixup ) { // Add all redirectors found in this package to the redirectors to delete list. // All redirectors in this package should be fixed up. UPackage* RedirectorPackage = RedirectorRefs.Redirector->GetOutermost(); TArray<UObject*> AssetsInRedirectorPackage; GetObjectsWithOuter(RedirectorPackage, AssetsInRedirectorPackage, /*bIncludeNestedObjects=*/false); UMetaData* PackageMetaData = NULL; bool bContainsAtLeastOneOtherAsset = false; for ( auto ObjIt = AssetsInRedirectorPackage.CreateConstIterator(); ObjIt; ++ObjIt ) { if ( UObjectRedirector* Redirector = Cast<UObjectRedirector>(*ObjIt) ) { Redirector->RemoveFromRoot(); ObjectsToDelete.Add(Redirector); } else if ( UMetaData* MetaData = Cast<UMetaData>(*ObjIt) ) { PackageMetaData = MetaData; } else { bContainsAtLeastOneOtherAsset = true; } } if ( !bContainsAtLeastOneOtherAsset ) { RedirectorPackage->RemoveFromRoot(); ULinkerLoad* Linker = ULinkerLoad::FindExistingLinkerForPackage(RedirectorPackage); if ( Linker ) { Linker->RemoveFromRoot(); } // @todo we shouldnt be worrying about metadata objects here, ObjectTools::CleanUpAfterSuccessfulDelete should if ( PackageMetaData ) { PackageMetaData->RemoveFromRoot(); ObjectsToDelete.Add(PackageMetaData); } } // This redirector will be deleted, NULL the reference here RedirectorRefs.Redirector = NULL; } } if ( ObjectsToDelete.Num() > 0 ) { ObjectTools::DeleteObjects(ObjectsToDelete); } }
/** * Loads the data from disk into the specified memory block. This requires us still being attached to an * archive we can use for serialization. * * @param Dest Memory to serialize data into */ void FUntypedBulkData::LoadDataIntoMemory( void* Dest ) { #if WITH_EDITOR checkf( AttachedAr, TEXT( "Attempted to load bulk data without an attached archive. Most likely the bulk data was loaded twice on console, which is not supported" ) ); // Keep track of current position in file so we can restore it later. int64 PushedPos = AttachedAr->Tell(); // Seek to the beginning of the bulk data in the file. AttachedAr->Seek( BulkDataOffsetInFile ); SerializeBulkData( *AttachedAr, Dest ); // Restore file pointer. AttachedAr->Seek( PushedPos ); #else bool bWasLoadedSuccessfully = false; if (IsInGameThread() && Linker.IsValid()) { ULinkerLoad* LinkerLoad = Linker.Get(); if ( LinkerLoad && LinkerLoad->Loader && !LinkerLoad->IsCompressed() ) { FArchive* Ar = LinkerLoad; // keep track of current position in this archive int64 CurPos = Ar->Tell(); // Seek to the beginning of the bulk data in the file. Ar->Seek( BulkDataOffsetInFile ); // serialize the bulk data SerializeBulkData( *Ar, Dest ); // seek back to the position the archive was before Ar->Seek(CurPos); // note that we loaded it bWasLoadedSuccessfully = true; } } // if we weren't able to load via linker, load directly by filename if (!bWasLoadedSuccessfully) { // load from the specied filename when the linker has been cleared checkf( Filename != TEXT(""), TEXT( "Attempted to load bulk data without a proper filename." ) ); FArchive* Ar = IFileManager::Get().CreateFileReader(*Filename, FILEREAD_Silent); checkf( Ar != NULL, TEXT( "Attempted to load bulk data from an invalid filename '%s'." ), *Filename ); // Seek to the beginning of the bulk data in the file. Ar->Seek( BulkDataOffsetInFile ); SerializeBulkData( *Ar, Dest ); delete Ar; } #endif // WITH_EDITOR }
void UArrayProperty::LinkInternal(FArchive& Ar) { ULinkerLoad* MyLinker = GetLinker(); if( MyLinker ) { MyLinker->Preload(this); } Ar.Preload(Inner); Inner->Link(Ar); Super::LinkInternal(Ar); }
// // Empty the loaders. // void ResetLoaders( UObject* InPkg ) { // Make sure we're not in the middle of loading something in the background. FlushAsyncLoading(); // Top level package to reset loaders for. UObject* TopLevelPackage = InPkg ? InPkg->GetOutermost() : NULL; // Find loader/ linker associated with toplevel package. We do this upfront as Detach resets LinkerRoot. if( TopLevelPackage ) { // Linker to reset/ detach. ULinkerLoad* LinkerToReset = ULinkerLoad::FindExistingLinkerForPackage(CastChecked<UPackage>(TopLevelPackage)); if ( LinkerToReset ) { for (TMap<UPackage*, ULinkerLoad*>::TIterator It(GObjLoaders); It; ++It) { ULinkerLoad* Linker = It.Value(); // Detach LinkerToReset from other linker's import table. if( Linker->LinkerRoot != TopLevelPackage ) { for( int32 j=0; j<Linker->ImportMap.Num(); j++ ) { if( Linker->ImportMap[j].SourceLinker == LinkerToReset ) { Linker->ImportMap[j].SourceLinker = NULL; Linker->ImportMap[j].SourceIndex = INDEX_NONE; } } } else { check(Linker == LinkerToReset); } } // Detach linker, also removes from array and sets LinkerRoot to NULL. LinkerToReset->Detach(true); } } else { TArray<ULinkerLoad *> LinkersToDetach; GObjLoaders.GenerateValueArray(LinkersToDetach); for (int32 Index = 0; Index < LinkersToDetach.Num(); Index++) { ULinkerLoad* Linker = LinkersToDetach[Index]; // Detach linker, also removes from array and sets LinkerRoot to NULL. Linker->Detach(true); } } }
void FAssetDeleteModel::PrepareToDelete(UObject* InObject) { if ( InObject->IsA<UObjectRedirector>() ) { // Add all redirectors found in this package to the redirectors to delete list. // All redirectors in this package should be fixed up. UPackage* RedirectorPackage = InObject->GetOutermost(); TArray<UObject*> AssetsInRedirectorPackage; GetObjectsWithOuter(RedirectorPackage, AssetsInRedirectorPackage, /*bIncludeNestedObjects=*/false); UMetaData* PackageMetaData = NULL; bool bContainsAtLeastOneOtherAsset = false; for ( auto ObjIt = AssetsInRedirectorPackage.CreateConstIterator(); ObjIt; ++ObjIt ) { if ( UObjectRedirector* Redirector = Cast<UObjectRedirector>(*ObjIt) ) { Redirector->RemoveFromRoot(); } else if ( UMetaData* MetaData = Cast<UMetaData>(*ObjIt) ) { PackageMetaData = MetaData; } else { bContainsAtLeastOneOtherAsset = true; } } if ( !bContainsAtLeastOneOtherAsset ) { RedirectorPackage->RemoveFromRoot(); ULinkerLoad* Linker = ULinkerLoad::FindExistingLinkerForPackage(RedirectorPackage); if ( Linker ) { Linker->RemoveFromRoot(); } // @todo we shouldnt be worrying about metadata objects here, ObjectTools::CleanUpAfterSuccessfulDelete should if ( PackageMetaData ) { PackageMetaData->RemoveFromRoot(); PendingDeletes.AddUnique(MakeShareable(new FPendingDelete(PackageMetaData))); } } } }
/** * * Ensure thumbnails are loaded and then reset the loader in preparation for a package save * * @param InOuter The outer for the package we are saving * @param Filename The filename we are saving too */ void ResetLoadersForSave(UObject* InOuter, const TCHAR *Filename) { UPackage* Package = dynamic_cast<UPackage*>(InOuter); // If we have a loader for the package, unload it to prevent conflicts if we are resaving to the same filename ULinkerLoad* Loader = ULinkerLoad::FindExistingLinkerForPackage(Package); // This is the loader corresponding to the package we're saving. if( Loader ) { // Before we save the package, make sure that we load up any thumbnails that aren't already // in memory so that they won't be wiped out during this save Loader->SerializeThumbnails(); // Compare absolute filenames to see whether we're trying to save over an existing file. if( FPaths::ConvertRelativePathToFull(Filename) == FPaths::ConvertRelativePathToFull( Loader->Filename ) ) { // Detach all exports from the linker and dissociate the linker. ResetLoaders( InOuter ); } } }
void UUserDefinedStruct::RecursivelyPreload() { ULinkerLoad* Linker = GetLinker(); if( Linker && (NULL == PropertyLink) ) { TArray<UObject*> AllChildMembers; GetObjectsWithOuter(this, AllChildMembers); for (int32 Index = 0; Index < AllChildMembers.Num(); ++Index) { UObject* Member = AllChildMembers[Index]; Linker->Preload(Member); } Linker->Preload(this); if (NULL == PropertyLink) { StaticLink(true); } } }
/** * Return the path name of the UObject represented by the specified export. * (can be used with StaticFindObject) * * @param ExportIndex index into the ExportMap for the resource to get the name for * @param FakeRoot Optional name to replace use as the root package of this object instead of the linker * @param bResolveForcedExports if true, the package name part of the return value will be the export's original package, * not the name of the package it's currently contained within. * * @return the path name of the UObject represented by the resource at ExportIndex */ FString ULinker::GetExportPathName(int32 ExportIndex, const TCHAR* FakeRoot,bool bResolveForcedExports/*=false*/) { FString Result; ULinkerLoad* Loader = dynamic_cast<ULinkerLoad*>(this); bool bForcedExport = false; for ( FPackageIndex LinkerIndex = FPackageIndex::FromExport(ExportIndex); !LinkerIndex.IsNull(); LinkerIndex = Exp(LinkerIndex).OuterIndex ) { const FObjectExport Export = Exp(LinkerIndex); // don't append a dot in the first iteration if ( Result.Len() > 0 ) { // if this export is not a UPackage but this export's Outer is a UPackage, we need to use subobject notation if (Loader != NULL && ( Export.OuterIndex.IsNull() || Loader->GetExportClassName(Export.OuterIndex) == NAME_Package) && Loader->GetExportClassName(LinkerIndex) != NAME_Package) { Result = FString(SUBOBJECT_DELIMITER) + Result; } else { Result = FString(TEXT(".")) + Result; } } Result = Export.ObjectName.ToString() + Result; bForcedExport = bForcedExport || Export.bForcedExport; } if ( bForcedExport && FakeRoot == NULL && bResolveForcedExports ) { // Result already contains the correct path name for this export return Result; } return (FakeRoot ? FakeRoot : LinkerRoot->GetPathName()) + TEXT(".") + Result; }
void FPackageDependencyInfo::RecursiveDeterminePackageDependentTimeStamp(const TCHAR* InPackageName, FDateTime& OutNewestTime, bool& bOutHadCircularReferences) { // Find the package info... FPackageDependencyTrackingInfo** pPkgInfo = PackageInformation.Find(InPackageName); if ((pPkgInfo != NULL) && (*pPkgInfo != NULL)) { FPackageDependencyTrackingInfo* PkgInfo = *pPkgInfo; if (PkgInfo->bBeingProcessed == true) { // Circular reference?? bOutHadCircularReferences = true; return; } checkf((PkgInfo->DependentTimeStamp == FDateTime::MinValue()), TEXT("RecursiveDeterminePackageDependentTimeStamp: Package already processed: %s"), InPackageName); // We have the package info, so process the actual package. BeginLoad(); ULinkerLoad* Linker = GetPackageLinker(NULL, InPackageName, LOAD_NoVerify, NULL, NULL); EndLoad(); if (Linker != NULL) { PkgInfo->bBeingProcessed = true; // Start off with setting the dependent time to the package itself PkgInfo->DependentTimeStamp = PkgInfo->TimeStamp; // Map? Code (ie blueprint)? PkgInfo->bContainsMap = Linker->ContainsMap(); PkgInfo->bContainsBlueprints = Linker->ContainsCode(); FName CheckMaterial = FName(TEXT("Material")); FName CheckMIC = FName(TEXT("MaterialInstanceConstant")); FName CheckMID = FName(TEXT("MaterialInstanceDynamic")); FName CheckLMIC = FName(TEXT("LandscapeMaterialInstanceConstant")); FName CheckWorld = FName(TEXT("World")); FName CheckBlueprint = FName(TEXT("Blueprint")); FName CheckAnimBlueprint = FName(TEXT("AnimBlueprint")); // Check the export map for material interfaces for (int32 ExpIdx = 0; ExpIdx < Linker->ExportMap.Num(); ExpIdx++) { FObjectExport& ObjExp = Linker->ExportMap[ExpIdx]; FName ExpClassName = Linker->GetExportClassName(ExpIdx); if ((ExpClassName == CheckMaterial) || (ExpClassName == CheckMIC) || (ExpClassName == CheckMID) || (ExpClassName == CheckLMIC)) { PkgInfo->bContainsShaders = true; if (PkgInfo->DependentTimeStamp < ShaderSourceTimeStamp) { PkgInfo->DependentTimeStamp = ShaderSourceTimeStamp; } PkgInfo->DependentPackages.Add(ShaderSourcePkgName, ShaderSourcePkgInfo); AllPackages.Add(ShaderSourcePkgInfo); } else if (ExpClassName == CheckWorld) { PkgInfo->bContainsMap = true; } else if ((ExpClassName == CheckBlueprint) || (ExpClassName == CheckAnimBlueprint)) { PkgInfo->bContainsBlueprints = true; if (PkgInfo->DependentTimeStamp < ScriptSourceTimeStamp) { PkgInfo->DependentTimeStamp = ScriptSourceTimeStamp; } PkgInfo->DependentPackages.Add(ScriptSourcePkgName, ScriptSourcePkgInfo); AllPackages.Add(ScriptSourcePkgInfo); } } // Check the dependencies //@todo. Make this a function of the linker? Almost the exact same code is used in PkgInfo commandlet... FName LinkerName = Linker->LinkerRoot->GetFName(); TArray<FName> DependentPackages; for (int32 ImpIdx = 0; ImpIdx < Linker->ImportMap.Num(); ImpIdx++) { FObjectImport& ObjImp = Linker->ImportMap[ImpIdx]; FName PackageName = NAME_None; FName OuterName = NAME_None; if (!ObjImp.OuterIndex.IsNull()) { // Find the package which contains this import. import.SourceLinker is cleared in EndLoad, so we'll need to do this manually now. FPackageIndex OutermostLinkerIndex = ObjImp.OuterIndex; for (FPackageIndex LinkerIndex = ObjImp.OuterIndex; !LinkerIndex.IsNull();) { OutermostLinkerIndex = LinkerIndex; LinkerIndex = Linker->ImpExp(LinkerIndex).OuterIndex; } PackageName = Linker->ImpExp(OutermostLinkerIndex).ObjectName; } if (PackageName == NAME_None && ObjImp.ClassName == NAME_Package) { PackageName = ObjImp.ObjectName; } if ((PackageName != NAME_None) && (PackageName != LinkerName)) { DependentPackages.AddUnique(PackageName); } if ((ObjImp.ClassPackage != NAME_None) && (ObjImp.ClassPackage != LinkerName)) { DependentPackages.AddUnique(ObjImp.ClassPackage); } } for (int32 DependentIdx = 0; DependentIdx < DependentPackages.Num(); DependentIdx++) { FString PkgName = DependentPackages[DependentIdx].ToString(); FText Reason; if (!FPackageName::IsValidLongPackageName(PkgName, true, &Reason)) { //UE_LOG(LogPackageDependencyInfo, Display, TEXT("%s --> %s"), *PkgName, *Reason.ToString()); continue; } FString LongName = FPackageName::LongPackageNameToFilename(PkgName); //UE_LOG(LogPackageDependencyInfo, Display, TEXT("%s --> %s"), *PkgName, *LongName); // Is it already in the list FPackageDependencyTrackingInfo** pDepPkgInfo = PackageInformation.Find(LongName); if ((pDepPkgInfo == NULL) || (*pDepPkgInfo == NULL)) { continue; } FPackageDependencyTrackingInfo* DepPkgInfo = *pDepPkgInfo; if (DepPkgInfo->bBeingProcessed == true) { bOutHadCircularReferences = true; // Circular reference // For now, store it off and we will resolve when completed PkgInfo->DependentPackages.Add(LongName, DepPkgInfo); PkgInfo->bHasCircularReferences = true; AllPackages.Add(PkgInfo); continue; } if (DepPkgInfo->DependentTimeStamp == FDateTime::MinValue()) { FDateTime TempTime; RecursiveDeterminePackageDependentTimeStamp(*LongName, TempTime, bOutHadCircularReferences); } PkgInfo->DependentPackages.Add(LongName, DepPkgInfo); AllPackages.Add(DepPkgInfo); if (DepPkgInfo->DependentTimeStamp != FDateTime::MinValue()) { if (PkgInfo->DependentTimeStamp < DepPkgInfo->DependentTimeStamp) { PkgInfo->DependentTimeStamp = DepPkgInfo->DependentTimeStamp; } } } PkgInfo->bBeingProcessed = false; OutNewestTime = PkgInfo->DependentTimeStamp; } else { UE_LOG(LogPackageDependencyInfo, Display, TEXT("RecursiveDeterminePackageDependentTimeStamp: Failed to find linker for %s"), InPackageName); } } else { UE_LOG(LogPackageDependencyInfo, Display, TEXT("RecursiveDeterminePackageDependentTimeStamp: Failed to find package info for %s"), InPackageName); } }
//------------------------------------------------------------------------------ bool FStructScriptLoader::LoadStructWithScript(UStruct* DestScriptContainer, FArchive& Ar, bool bAllowDeferredSerialization) { if (!Ar.IsLoading() || !IsPrimed() || GIsDuplicatingClassForReinstancing) { return false; } bool const bIsLinkerLoader = Ar.IsPersistent() && (Ar.GetLinker() != nullptr); int32 const ScriptEndOffset = ScriptSerializationOffset + SerializedScriptSize; // to help us move development forward (and not have to support ancient // script code), we define a minimum script version bool bSkipScriptSerialization = (Ar.UE4Ver() < VER_MIN_SCRIPTVM_UE4) || (Ar.LicenseeUE4Ver() < VER_MIN_SCRIPTVM_LICENSEEUE4); #if WITH_EDITOR static const FBoolConfigValueHelper SkipByteCodeHelper(TEXT("StructSerialization"), TEXT("SkipByteCodeSerialization")); // in editor builds, we're going to regenerate the bytecode anyways, so it // is a waste of cycles to try and serialize it in bSkipScriptSerialization |= (bool)SkipByteCodeHelper; #endif // WITH_EDITOR bSkipScriptSerialization &= bIsLinkerLoader; // to keep consistent with old UStruct::Serialize() functionality if (bSkipScriptSerialization) { int32 TrackedBufferSize = BytecodeBufferSize; BytecodeBufferSize = 0; // temporarily clear so that ClearScriptCode() doesn't leave Class->Script with anything allocated ClearScriptCode(DestScriptContainer); BytecodeBufferSize = TrackedBufferSize; // we have to at least move the archiver forward, so it is positioned // where it expects to be (as if we read in the script) Ar.Seek(ScriptEndOffset); return false; } bAllowDeferredSerialization &= bIsLinkerLoader; if (bAllowDeferredSerialization && ShouldDeferScriptSerialization(Ar)) { ULinkerLoad* Linker = CastChecked<ULinkerLoad>(Ar.GetLinker()); FDeferredScriptTracker::Get().AddDeferredScriptObject(Linker, DestScriptContainer, *this); // we have to at least move the archiver forward, so it is positioned // where it expects to be (as if we read in the script) Ar.Seek(ScriptEndOffset); return false; } Ar.Seek(ScriptSerializationOffset); if (bIsLinkerLoader) { ULinkerLoad* LinkerLoad = CastChecked<ULinkerLoad>(Ar.GetLinker()); TArray<uint8> ShaScriptBuffer; ShaScriptBuffer.AddUninitialized(SerializedScriptSize); Ar.Serialize(ShaScriptBuffer.GetData(), SerializedScriptSize); ensure(ScriptEndOffset == Ar.Tell()); LinkerLoad->UpdateScriptSHAKey(ShaScriptBuffer); Ar.Seek(ScriptSerializationOffset); } DestScriptContainer->Script.Empty(BytecodeBufferSize); DestScriptContainer->Script.AddUninitialized(BytecodeBufferSize); int32 BytecodeIndex = 0; while (BytecodeIndex < BytecodeBufferSize) { DestScriptContainer->SerializeExpr(BytecodeIndex, Ar); } ensure(ScriptEndOffset == Ar.Tell()); checkf(BytecodeIndex == BytecodeBufferSize, TEXT("'%s' script expression-count mismatch; Expected: %i, Got: %i"), *DestScriptContainer->GetName(), BytecodeBufferSize, BytecodeIndex); if (!GUObjectArray.IsDisregardForGC(DestScriptContainer)) { DestScriptContainer->ScriptObjectReferences.Empty(); FArchiveScriptReferenceCollector ObjRefCollector(DestScriptContainer->ScriptObjectReferences); BytecodeIndex = 0; while (BytecodeIndex < BytecodeBufferSize) { DestScriptContainer->SerializeExpr(BytecodeIndex, ObjRefCollector); } } // success! (we filled the target with serialized script code) return true; }