UPackage* LoadPackageInternal(UPackage* InOuter, const TCHAR* InLongPackageNameOrFilename, uint32 LoadFlags, FLinkerLoad* ImportLinker, FArchive* InReaderOverride, const FLinkerInstancingContext* InstancingContext)
{
	DECLARE_SCOPE_CYCLE_COUNTER(TEXT("LoadPackageInternal"), STAT_LoadPackageInternal, STATGROUP_ObjectVerbose);
	SCOPED_CUSTOM_LOADTIMER(LoadPackageInternal)
		ADD_CUSTOM_LOADTIMER_META(LoadPackageInternal, PackageName, InLongPackageNameOrFilename);

	checkf(IsInGameThread(), TEXT("Unable to load %s. Objects and Packages can only be loaded from the game thread."), InLongPackageNameOrFilename);

	UPackage* Result = nullptr;

	if (FPlatformProperties::RequiresCookedData() && GEventDrivenLoaderEnabled
		&& EVENT_DRIVEN_ASYNC_LOAD_ACTIVE_AT_RUNTIME
		)
	{
		FString InName;
		FString InPackageName;

		if (FPackageName::IsPackageFilename(InLongPackageNameOrFilename))
		{
			FPackageName::TryConvertFilenameToLongPackageName(InLongPackageNameOrFilename, InPackageName);
		}
		else
		{
			InPackageName = InLongPackageNameOrFilename;
		}

		if (InOuter)
		{
			InName = InOuter->GetPathName();
		}
		else
		{
			InName = InPackageName;
		}

		FName PackageFName(*InPackageName);

		{
			if (FCoreDelegates::OnSyncLoadPackage.IsBound())
			{
				FCoreDelegates::OnSyncLoadPackage.Broadcast(InName);
				PackageInfoCache::OnRecordSyncLoad(InName);
			}

			int32 RequestID = LoadPackageAsync(InName, nullptr, *InPackageName);

			if (RequestID != INDEX_NONE)
			{
				FlushAsyncLoading(RequestID);
			}
		}

		Result = (InOuter ? InOuter : FindObjectFast<UPackage>(nullptr, PackageFName));
		return Result;
	}

	FString FileToLoad;
#if WITH_EDITOR
	FString DiffFileToLoad;
#endif

#if WITH_EDITOR
	if (LoadFlags & LOAD_ForFileDiff)
	{
		FString TempFilenames = InLongPackageNameOrFilename;
		ensure(TempFilenames.Split(TEXT(";"), &FileToLoad, &DiffFileToLoad, ESearchCase::CaseSensitive));
	}
	else
#endif
	if (InLongPackageNameOrFilename && FCString::Strlen(InLongPackageNameOrFilename) > 0)
	{
		FileToLoad = InLongPackageNameOrFilename;
	}
	else if (InOuter)
	{
		FileToLoad = InOuter->GetName();
	}

#if STREAMING_KURO_DEBUG_LOG
	if(CanStreamKuroLog(FStreamingKuroLog::ELogFlags::SyncLoad))
	{
		UE_LOG(LogKuroStreaming,Log, TEXT("LOAD PACKAGE INTERNAL() FileToLoad %s For Package %s"), *FileToLoad, InLongPackageNameOrFilename);
	}
#endif
	
	
	// Make sure we're trying to load long package names only.
	if (FPackageName::IsShortPackageName(FileToLoad))
	{
		FString LongPackageName;
		FName* ScriptPackageName = FPackageName::FindScriptPackageName(*FileToLoad);
		if (ScriptPackageName)
		{
			UE_LOG(LogUObjectGlobals, Warning, TEXT("LoadPackage: %s is a short script package name."), InLongPackageNameOrFilename);
			FileToLoad = ScriptPackageName->ToString();
		}
		else if (!FPackageName::SearchForPackageOnDisk(FileToLoad, &FileToLoad))
		{
			UE_LOG(LogUObjectGlobals, Warning, TEXT("LoadPackage can't find package %s."), *FileToLoad);
			return NULL;
		}
	}
#if WITH_EDITOR
	// In the editor loading cannot be part of a transaction as it cannot be undone, and may result in recording half-loaded objects. So we suppress any active transaction while in this stack, and set the editor loading flag
	TGuardValue<ITransaction*> SuppressTransaction(GUndo, nullptr);
	TGuardValue<bool> IsEditorLoadingPackage(GIsEditorLoadingPackage, GIsEditor || GIsEditorLoadingPackage);
#endif

	TOptional<FScopedSlowTask> SlowTask;
	if (ShouldCreateThrottledSlowTask())
	{
		static const FTextFormat LoadingPackageTextFormat = NSLOCTEXT("Core", "LoadingPackage_Scope", "Loading Package '{0}'");
		SlowTask.Emplace(100, FText::Format(LoadingPackageTextFormat, FText::FromString(FileToLoad)));
		SlowTask->Visibility = ESlowTaskVisibility::Invisible;
		SlowTask->EnterProgressFrame(10);
	}

	if (FCoreDelegates::OnSyncLoadPackage.IsBound())
	{
		FCoreDelegates::OnSyncLoadPackage.Broadcast(FileToLoad);
		PackageInfoCache::OnRecordSyncLoad(FileToLoad);
	}
	
	// Set up a load context
	TRefCountPtr<FUObjectSerializeContext> LoadContext = FUObjectThreadContext::Get().GetSerializeContext();

	// Try to load.
	BeginLoad(LoadContext, InLongPackageNameOrFilename);

	bool bFullyLoadSkipped = false;

	if (SlowTask)
	{
		SlowTask->EnterProgressFrame(30);
	}

	// Declare here so that the linker does not get destroyed before ResetLoaders is called
	FLinkerLoad* Linker = nullptr;
	{
		// Keep track of start time.
		const double StartTime = FPlatformTime::Seconds();

		// Create a new linker object which goes off and tries load the file.
#if WITH_EDITOR
		if (LoadFlags & LOAD_ForFileDiff)
		{
			// Create the package with the provided long package name.
			if (!InOuter)
			{
				InOuter = CreatePackage(*FileToLoad);
			}
			
			new FUnsafeLinkerLoad(InOuter, *FileToLoad, *DiffFileToLoad, LOAD_ForDiff);
		}
#endif

		{
			FUObjectSerializeContext* InOutLoadContext = LoadContext;
			Linker = GetPackageLinker(InOuter, *FileToLoad, LoadFlags, nullptr, nullptr, InReaderOverride, &InOutLoadContext, ImportLinker, InstancingContext);
			if (InOutLoadContext != LoadContext && InOutLoadContext)
			{
				// The linker already existed and was associated with another context
				LoadContext->DecrementBeginLoadCount();
				LoadContext = InOutLoadContext;
				LoadContext->IncrementBeginLoadCount();
			}			
		}

		if (!Linker)
		{
			EndLoad(LoadContext);
			return nullptr;
		}

		Result = Linker->LinkerRoot;
		checkf(Result, TEXT("LinkerRoot is null"));

		auto EndLoadAndCopyLocalizationGatherFlag = [&]
		{
			EndLoad(Linker->GetSerializeContext());
			// Set package-requires-localization flags from archive after loading. This reinforces flagging of packages that haven't yet been resaved.
			Result->ThisRequiresLocalizationGather(Linker->RequiresLocalizationGather());
		};

#if WITH_EDITORONLY_DATA
		if (!(LoadFlags & (LOAD_IsVerifying|LOAD_EditorOnly)))
		{
			bool bIsEditorOnly = false;
			FProperty* SerializingProperty = ImportLinker ? ImportLinker->GetSerializedProperty() : nullptr;
			
			// Check property parent chain
			while (SerializingProperty)
			{
				if (SerializingProperty->IsEditorOnlyProperty())
				{
					bIsEditorOnly = true;
					break;
				}
				SerializingProperty = SerializingProperty->GetOwner<FProperty>();
			}

			if (!bIsEditorOnly)
			{
				// If this package hasn't been loaded as part of import verification and there's no import linker or the
				// currently serialized property is not editor-only mark this package as runtime.
				Result->SetLoadedByEditorPropertiesOnly(false);
			}
		}
#endif

		if (Result->HasAnyFlags(RF_WasLoaded))
		{
			// The linker is associated with a package that has already been loaded.
			// Loading packages that have already been loaded is unsupported.
			EndLoadAndCopyLocalizationGatherFlag();			
			return Result;
		}

		// The time tracker keeps track of time spent in LoadPackage.
		FExclusiveLoadPackageTimeTracker::FScopedPackageTracker Tracker(Result);

		// If we are loading a package for diffing, set the package flag
		if(LoadFlags & LOAD_ForDiff)
		{
			Result->SetPackageFlags(PKG_ForDiffing);
		}

		// Save the filename we load from in Long package name form
		{
			// convert will succeed here, otherwise the linker will have been null
			FString LongPackageFilename;
			FPackageName::TryConvertFilenameToLongPackageName(FileToLoad, LongPackageFilename);
			Result->FileName = FName(*LongPackageFilename);
		}
		
		// is there a script SHA hash for this package?
		uint8 SavedScriptSHA[20];
		bool bHasScriptSHAHash = FSHA1::GetFileSHAHash(*Linker->LinkerRoot->GetName(), SavedScriptSHA, false);
		if (bHasScriptSHAHash)
		{
			// if there is, start generating the SHA for any script code in this package
			Linker->StartScriptSHAGeneration();
		}

		if (SlowTask)
		{
			SlowTask->EnterProgressFrame(30);
		}

		uint32 DoNotLoadExportsFlags = LOAD_Verify;
#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
		// if this linker already has the DeferDependencyLoads flag, then we're
		// already loading it earlier up the load chain (don't let it invoke any
		// deeper loads that may introduce a circular dependency)
		DoNotLoadExportsFlags |= LOAD_DeferDependencyLoads;
#endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING

		if ((LoadFlags & DoNotLoadExportsFlags) == 0)
		{
			// Make sure we pass the property that's currently being serialized by the linker that owns the import 
			// that triggered this LoadPackage call
			FSerializedPropertyScope SerializedProperty(*Linker, ImportLinker ? ImportLinker->GetSerializedProperty() : Linker->GetSerializedProperty());
			Linker->LoadAllObjects(GEventDrivenLoaderEnabled);

			// @todo: remove me when loading can be self-contained (and EndLoad doesn't check for IsInAsyncLoadingThread) or there's just one loading path
			// If we start a non-async loading during async loading and the serialization context is not associated with any other package and
			// doesn't come from an async package, queue this package to be async loaded, otherwise we'll end up not loading its exports
			if (!Linker->AsyncRoot && Linker->GetSerializeContext()->GetBeginLoadCount() == 1 && IsInAsyncLoadingThread())
			{
				LoadPackageAsync(Linker->LinkerRoot->GetName());
			}
		}
		else
		{
			bFullyLoadSkipped = true;
		}

		if (SlowTask)
		{
			SlowTask->EnterProgressFrame(30);
		}

		Linker->FinishExternalReadDependencies(0.0);

		EndLoadAndCopyLocalizationGatherFlag();

#if WITH_EDITOR
		GIsEditorLoadingPackage = *IsEditorLoadingPackage;
#endif

		// if we are calculating the script SHA for a package, do the comparison now
		if (bHasScriptSHAHash)
		{
			// now get the actual hash data
			uint8 LoadedScriptSHA[20];
			Linker->GetScriptSHAKey(LoadedScriptSHA);

			// compare SHA hash keys
			if (FMemory::Memcmp(SavedScriptSHA, LoadedScriptSHA, 20) != 0)
			{
				appOnFailSHAVerification(*Linker->Filename, false);
			}
		}

		// Only set time it took to load package if the above EndLoad is the "outermost" EndLoad.
		if( Result && !LoadContext->HasLoadedObjects() && !(LoadFlags & LOAD_Verify) )
		{
			Result->SetLoadTime( FPlatformTime::Seconds() - StartTime );
		}

		Linker->Flush();

		if (!FPlatformProperties::RequiresCookedData())
		{
			// Flush cache on uncooked platforms to free precache memory
			Linker->FlushCache();
		}

		// With UE4 and single asset per package, we load so many packages that some platforms will run out
		// of file handles. So, this will close the package, but just things like bulk data loading will
		// fail, so we only currently do this when loading on consoles.
		// The only exception here is when we're in the middle of async loading where we can't reset loaders yet. This should only happen when
		// doing synchronous load in the middle of streaming.
		if (FPlatformProperties::RequiresCookedData())
		{
			if (!IsInAsyncLoadingThread())
			{				
				if (GGameThreadLoadCounter == 0)
				{
					// Sanity check to make sure that Linker is the linker that loaded our Result package or the linker has already been detached
					check(!Result || Result->LinkerLoad == Linker || Result->LinkerLoad == nullptr);
					if (Result && Linker->HasLoader())
					{
						ResetLoaders(Result);
					}
					// Reset loaders could have already deleted Linker so guard against deleting stale pointers
					if (Result && Result->LinkerLoad)
					{
						Linker->DestroyLoader();
					}
					// And make sure no one can use it after it's been deleted
					Linker = nullptr;
				}
				// Async loading removes delayed linkers on the game thread after streaming has finished
				else
				{
					check(Linker->GetSerializeContext());
					Linker->GetSerializeContext()->AddDelayedLinkerClosePackage(Linker);
				}
			}
			else
			{
				check(Linker->GetSerializeContext());
				Linker->GetSerializeContext()->AddDelayedLinkerClosePackage(Linker);
			}
		}
	}

	if (!bFullyLoadSkipped)
	{
		// Mark package as loaded.
		Result->SetFlags(RF_WasLoaded);
	}

	return Result;
}