/**
	 * A helper method that will reset a package for reload, and flag it as 
	 * unsavable (meant to be used after you've messed with a package for testing 
	 * purposes, leaving it in a questionable state).
	 * 
	 * @param  Package	The package you wish to invalidate.
	 */
	static void InvalidatePackage(UPackage* const Package)
	{
		// reset the blueprint's original package/linker so that we can get by
		// any early returns (in the load code), and reload its exports as if new 
		ResetLoaders(Package);
		Package->ClearFlags(RF_WasLoaded);	
		Package->bHasBeenFullyLoaded = false;

		Package->GetMetaData()->RemoveMetaDataOutsidePackage();
		// we've mucked around with the package manually, we should probably prevent 
		// people from saving it in this state (this means you won't be able to save 
		// the blueprints that these tests were run on until you relaunch the editor)
		DontSavePackage(Package);
	}
Exemple #2
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 );
		}
	}
}
Exemple #3
0
//
// Find or create the linker for a package.
//
ULinkerLoad* GetPackageLinker
(
	UPackage*		InOuter,
	const TCHAR*	InLongPackageName,
	uint32			LoadFlags,
	UPackageMap*	Sandbox,
	FGuid*			CompatibleGuid
)
{
	// See if there is already a linker for this package.
	ULinkerLoad* Result = ULinkerLoad::FindExistingLinkerForPackage(InOuter);

	// Try to load the linker.
	// See if the linker is already loaded.
	if( Result )
	{
		return Result;
	}

	FString NewFilename;
	if( !InLongPackageName )
	{
		// Resolve filename from package name.
		if( !InOuter )
		{
			// try to recover from this instead of throwing, it seems recoverable just by doing this
			FText ErrorText(LOCTEXT("PackageResolveFailed", "Can't resolve asset name"));
			LogGetPackageLinkerError(Result, InLongPackageName, ErrorText, ErrorText, InOuter, LoadFlags);
			return nullptr;
		}

		if( !FPackageName::DoesPackageExist(InOuter->GetName(), CompatibleGuid, &NewFilename) )
		{
			// Compiled in packages have no linker and this is ok
			if (!(LoadFlags & LOAD_AllowDll) && !(InOuter->PackageFlags & PKG_CompiledIn))
			{
				FFormatNamedArguments Arguments;
				Arguments.Add(TEXT("AssetName"), FText::FromString(InOuter->GetName()));
				Arguments.Add(TEXT("PackageName"), FText::FromString(GSerializedPackageLinker ? *(GSerializedPackageLinker->Filename) : TEXT("NULL")));
				LogGetPackageLinkerError(Result, GSerializedPackageLinker ? *GSerializedPackageLinker->Filename : nullptr,
											FText::Format(LOCTEXT("PackageNotFound", "Can't find file for asset '{AssetName}' while loading {PackageName}."), Arguments),
											LOCTEXT("PackageNotFoundShort", "Can't find file for asset."),
											InOuter,
											LoadFlags);
			}

			return nullptr;
		}
	}
	else
	{
		FString PackageName(InLongPackageName);
		if (!FPackageName::TryConvertFilenameToLongPackageName(InLongPackageName, PackageName))
		{
			// try to recover from this instead of throwing, it seems recoverable just by doing this
			FText ErrorText(LOCTEXT("PackageResolveFailed", "Can't resolve asset name"));
			LogGetPackageLinkerError(Result, InLongPackageName, ErrorText, ErrorText, InOuter, LoadFlags);
			return nullptr;
		}

		if (UPackage* ExistingPackage = FindObject<UPackage>(nullptr, *PackageName))
		{
			if (!ExistingPackage->GetOuter() && (ExistingPackage->PackageFlags & PKG_CompiledIn))
			{
				// this is a compiled in package and so it has no linker and this is ok
				return nullptr;
			}
		}

		// Verify that the file exists.
		if( !FPackageName::DoesPackageExist( PackageName, CompatibleGuid, &NewFilename ) )
		{
			FFormatNamedArguments Arguments;
			Arguments.Add(TEXT("Filename"), FText::FromString(InLongPackageName));

			// try to recover from this instead of throwing, it seems recoverable just by doing this
			LogGetPackageLinkerError(Result, InLongPackageName, FText::Format(LOCTEXT("FileNotFound", "Can't find file '{Filename}'"), Arguments), LOCTEXT("FileNotFoundShort", "Can't find file"), InOuter, LoadFlags);
			return nullptr;
		}

		// Create the package with the provided long package name.
		UPackage* FilenamePkg = CreatePackage(nullptr, *PackageName);
		if (LoadFlags & LOAD_PackageForPIE)
		{
			FilenamePkg->PackageFlags |= PKG_PlayInEditor;
		}

		// If no package specified, use package from file.
		if (!InOuter)
		{
			if( !FilenamePkg )
			{
				FFormatNamedArguments Arguments;
				Arguments.Add(TEXT("Filename"), FText::FromString(InLongPackageName));
				LogGetPackageLinkerError(Result, InLongPackageName, FText::Format(LOCTEXT("FilenameToPackage", "Can't convert filename '{Filename}' to asset name"), Arguments), LOCTEXT("FilenameToPackageShort", "Can't convert filename to asset name"), InOuter, LoadFlags);
				return nullptr;
			}
			InOuter = FilenamePkg;
			Result = ULinkerLoad::FindExistingLinkerForPackage(InOuter);
		}
		else if (InOuter != FilenamePkg) //!!should be tested and validated in new UnrealEd
		{
			// Loading a new file into an existing package, so reset the loader.
			//UE_LOG(LogLinker, Log,  TEXT("New File, Existing Package (%s, %s)"), *InOuter->GetFullName(), *FilenamePkg->GetFullName() );
			ResetLoaders( InOuter );
		}
	}

#if 0
	// Make sure the package is accessible in the sandbox.
	if( Sandbox && !Sandbox->SupportsPackage(InOuter) )
	{
		FFormatNamedArguments Arguments;
		Arguments.Add(TEXT("AssetName"), FText::FromString(InOuter->GetName()));

		LogGetPackageLinkerError(Result, InLongPackageName, FText::Format(LOCTEXT("Sandbox", "Asset '{AssetName}' is not accessible in this sandbox"), Arguments), LOCTEXT("SandboxShort", "Asset is not accessible in this sandbox"), InOuter, LoadFlags);
		return nullptr;
	}
#endif

	// Create new linker.
	if( !Result )
	{
		check(IsLoading());

		// we will already have found the filename above
		check(NewFilename.Len() > 0);

		Result = ULinkerLoad::CreateLinker( InOuter, *NewFilename, LoadFlags );
	}

	// Verify compatibility.
	if( CompatibleGuid && Result->Summary.Guid!=*CompatibleGuid )
	{
		FFormatNamedArguments Arguments;
		Arguments.Add(TEXT("AssetName"), FText::FromString(InOuter->GetName()));

		// This should never fire, because FindPackageFile should never return an incompatible file
		LogGetPackageLinkerError(Result, InLongPackageName, FText::Format(LOCTEXT("PackageVersion", "Asset '{AssetName}' version mismatch"), Arguments), LOCTEXT("PackageVersionShort", "Asset version mismatch"), InOuter, LoadFlags);
		return nullptr;
	}

	return Result;
}
Exemple #4
0
//
// Find or create the linker for a package.
//
FLinkerLoad* GetPackageLinker
(
	UPackage*		InOuter,
	const TCHAR*	InLongPackageName,
	uint32			LoadFlags,
	UPackageMap*	Sandbox,
	FGuid*			CompatibleGuid
)
{
	// See if there is already a linker for this package.
	auto Result = FLinkerLoad::FindExistingLinkerForPackage(InOuter);

	// Try to load the linker.
	// See if the linker is already loaded.
	if (Result)
	{
		return Result;
	}

	FString NewFilename;
	if( !InLongPackageName )
	{
		// Resolve filename from package name.
		if( !InOuter )
		{
			// try to recover from this instead of throwing, it seems recoverable just by doing this
			FText ErrorText(LOCTEXT("PackageResolveFailed", "Can't resolve asset name"));
			LogGetPackageLinkerError(Result, InLongPackageName, ErrorText, ErrorText, InOuter, LoadFlags);
			return nullptr;
		}
	
		FString NativeFilename;
		FString LocalizedFilename;
		const bool DoesNativePackageExist = FPackageName::DoesPackageExist(InOuter->GetName(), CompatibleGuid, &NativeFilename, false);
		const bool DoesLocalizedPackageExist = FPackageName::DoesPackageExist(InOuter->GetName(), CompatibleGuid, &LocalizedFilename, true);

		// If we are the editor, we must have a native package. If we are the game, we must have a localized package or a native package.
		if ( (GIsEditor && !DoesNativePackageExist) || (!GIsEditor && !DoesLocalizedPackageExist && !DoesNativePackageExist) )
		{
			// In memory-only packages have no linker and this is ok.
			if (!(LoadFlags & LOAD_AllowDll) && !(InOuter->PackageFlags & PKG_InMemoryOnly))
			{
				FUObjectThreadContext& ThreadContext = FUObjectThreadContext::Get();
				FFormatNamedArguments Arguments;
				Arguments.Add(TEXT("AssetName"), FText::FromString(InOuter->GetName()));
				Arguments.Add(TEXT("PackageName"), FText::FromString(ThreadContext.SerializedPackageLinker ? *(ThreadContext.SerializedPackageLinker->Filename) : TEXT("NULL")));
				LogGetPackageLinkerError(Result, ThreadContext.SerializedPackageLinker ? *ThreadContext.SerializedPackageLinker->Filename : nullptr,
											FText::Format(LOCTEXT("PackageNotFound", "Can't find file for asset '{AssetName}' while loading {PackageName}."), Arguments),
											LOCTEXT("PackageNotFoundShort", "Can't find file for asset."),
											InOuter,
											LoadFlags);
			}

			return nullptr;
		}

		// The editor must not redirect packages for localization.
		if (GIsEditor)
		{
			NewFilename = NativeFilename;
		}
		else
		{
			if (DoesLocalizedPackageExist)
			{
				NewFilename = LocalizedFilename;
			}
			// If we are the game, we can fallback to the native package, but must issue a warning.
			else
			{
				// In memory-only packages have no linker and this is ok.
				if (!(LoadFlags & LOAD_AllowDll) && !(InOuter->PackageFlags & PKG_InMemoryOnly))
				{
					FUObjectThreadContext& ThreadContext = FUObjectThreadContext::Get();
					FFormatNamedArguments Arguments;
					Arguments.Add(TEXT("AssetName"), FText::FromString(InOuter->GetName()));
					Arguments.Add(TEXT("PackageName"), FText::FromString(ThreadContext.SerializedPackageLinker ? *(ThreadContext.SerializedPackageLinker->Filename) : TEXT("NULL")));
					LogGetPackageLinkerError(Result, ThreadContext.SerializedPackageLinker ? *ThreadContext.SerializedPackageLinker->Filename : nullptr,
						FText::Format(LOCTEXT("PackageNotFound", "Can't find localized file for asset '{AssetName}' while loading {PackageName}."), Arguments),
						LOCTEXT("PackageNotFoundShort", "Can't find localized file for asset."),
						InOuter,
						LoadFlags);
				}

				NewFilename = NativeFilename;
			}
		}
	}
	else
	{
		FString PackageName(InLongPackageName);
		if (!FPackageName::TryConvertFilenameToLongPackageName(InLongPackageName, PackageName))
		{
			// try to recover from this instead of throwing, it seems recoverable just by doing this
			FText ErrorText(LOCTEXT("PackageResolveFailed", "Can't resolve asset name"));
			LogGetPackageLinkerError(Result, InLongPackageName, ErrorText, ErrorText, InOuter, LoadFlags);
			return nullptr;
		}

		UPackage* ExistingPackage = FindObject<UPackage>(nullptr, *PackageName);
		if (ExistingPackage)
		{
			if (!ExistingPackage->GetOuter() && (ExistingPackage->PackageFlags & PKG_InMemoryOnly))
			{
				// This is a memory-only in package and so it has no linker and this is ok.
				return nullptr;
			}
		}

		// Verify that the file exists.
		FString NativeFilename;
		FString LocalizedFilename;
		const bool DoesNativePackageExist = FPackageName::DoesPackageExist(PackageName, CompatibleGuid, &NativeFilename, false);
		const bool DoesLocalizedPackageExist = FPackageName::DoesPackageExist(PackageName, CompatibleGuid, &LocalizedFilename, true);

		if( (GIsEditor && !DoesNativePackageExist) || (!GIsEditor && !DoesLocalizedPackageExist && !DoesNativePackageExist) )
		{
			FFormatNamedArguments Arguments;
			Arguments.Add(TEXT("Filename"), FText::FromString(InLongPackageName));

			// try to recover from this instead of throwing, it seems recoverable just by doing this
			LogGetPackageLinkerError(Result, InLongPackageName, FText::Format(LOCTEXT("FileNotFound", "Can't find file '{Filename}'"), Arguments), LOCTEXT("FileNotFoundShort", "Can't find file"), InOuter, LoadFlags);
			return nullptr;
		}

		// The editor must not redirect packages for localization.
		if (GIsEditor)
		{
			NewFilename = NativeFilename;
		}
		else
		{
			// Use the localized package if possible.
			if (DoesLocalizedPackageExist)
			{
				NewFilename = LocalizedFilename;
			}
			// If we are the game, we can fallback to the native package.
			else
			{
				NewFilename = NativeFilename;
			}
		}

		// Create the package with the provided long package name.
		UPackage* FilenamePkg = (ExistingPackage ? ExistingPackage : CreatePackage(nullptr, *PackageName));
		if (FilenamePkg != ExistingPackage && (LoadFlags & LOAD_PackageForPIE))
		{
			FilenamePkg->PackageFlags |= PKG_PlayInEditor;
		}

		// If no package specified, use package from file.
		if (!InOuter)
		{
			if( !FilenamePkg )
			{
				FFormatNamedArguments Arguments;
				Arguments.Add(TEXT("Filename"), FText::FromString(InLongPackageName));
				LogGetPackageLinkerError(Result, InLongPackageName, FText::Format(LOCTEXT("FilenameToPackage", "Can't convert filename '{Filename}' to asset name"), Arguments), LOCTEXT("FilenameToPackageShort", "Can't convert filename to asset name"), InOuter, LoadFlags);
				return nullptr;
			}
			InOuter = FilenamePkg;
			Result = FLinkerLoad::FindExistingLinkerForPackage(InOuter);
		}
		else if (InOuter != FilenamePkg) //!!should be tested and validated in new UnrealEd
		{
			// Loading a new file into an existing package, so reset the loader.
			//UE_LOG(LogLinker, Log,  TEXT("New File, Existing Package (%s, %s)"), *InOuter->GetFullName(), *FilenamePkg->GetFullName() );
			ResetLoaders( InOuter );
		}
	}

#if 0
	// Make sure the package is accessible in the sandbox.
	if( Sandbox && !Sandbox->SupportsPackage(InOuter) )
	{
		FFormatNamedArguments Arguments;
		Arguments.Add(TEXT("AssetName"), FText::FromString(InOuter->GetName()));

		LogGetPackageLinkerError(Result, InLongPackageName, FText::Format(LOCTEXT("Sandbox", "Asset '{AssetName}' is not accessible in this sandbox"), Arguments), LOCTEXT("SandboxShort", "Asset is not accessible in this sandbox"), InOuter, LoadFlags);
		return nullptr;
	}
#endif

	// Create new linker.
	if( !Result )
	{
		check(IsLoading());

		// we will already have found the filename above
		check(NewFilename.Len() > 0);

		Result = FLinkerLoad::CreateLinker( InOuter, *NewFilename, LoadFlags );
	}

	// Verify compatibility.
	if (Result && CompatibleGuid && Result->Summary.Guid != *CompatibleGuid)
	{
		FFormatNamedArguments Arguments;
		Arguments.Add(TEXT("AssetName"), FText::FromString(InOuter->GetName()));

		// This should never fire, because FindPackageFile should never return an incompatible file
		LogGetPackageLinkerError(Result, InLongPackageName, FText::Format(LOCTEXT("PackageVersion", "Asset '{AssetName}' version mismatch"), Arguments), LOCTEXT("PackageVersionShort", "Asset version mismatch"), InOuter, LoadFlags);
		return nullptr;
	}

	return Result;
}