예제 #1
0
/**
 * 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;
	}
}
예제 #2
0
/**
 * 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;
}
예제 #3
0
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);
	}
}
예제 #4
0
/**
 * 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
}
예제 #5
0
void UArrayProperty::LinkInternal(FArchive& Ar)
{
	ULinkerLoad* MyLinker = GetLinker();
	if( MyLinker )
	{
		MyLinker->Preload(this);
	}
	Ar.Preload(Inner);
	Inner->Link(Ar);
	Super::LinkInternal(Ar);
}
예제 #6
0
//
// 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);
		}
	}

}
예제 #7
0
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)));
			}
		}
	}
}
예제 #8
0
/**
 * 
 * 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 );
		}
	}
}
예제 #9
0
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);
		}
	}
}
예제 #10
0
/**
 * 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;
}
예제 #11
0
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);
    }
}
예제 #12
0
//------------------------------------------------------------------------------
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;
}