void FAssetFixUpRedirectors::LoadReferencingPackages(TArray<FRedirectorRefs>& RedirectorsToFix, TArray<UPackage*>& OutReferencingPackagesToSave) const
{
	FScopedSlowTask SlowTask( RedirectorsToFix.Num(), LOCTEXT( "LoadingReferencingPackages", "Loading Referencing Packages..." ) );
	SlowTask.MakeDialog();

	ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();

	// Load all packages that reference each redirector, if possible
	for ( auto RedirectorRefsIt = RedirectorsToFix.CreateIterator(); RedirectorRefsIt; ++RedirectorRefsIt )
	{
		SlowTask.EnterProgressFrame(1);

		FRedirectorRefs& RedirectorRefs = *RedirectorRefsIt;
		if ( ISourceControlModule::Get().IsEnabled() )
		{
			FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(RedirectorRefs.Redirector->GetOutermost(), EStateCacheUsage::Use);
			const bool bValidSCCState = !SourceControlState.IsValid() || SourceControlState->IsAdded() || SourceControlState->IsCheckedOut() || SourceControlState->CanCheckout() || !SourceControlState->IsSourceControlled() || SourceControlState->IsIgnored();

			if ( !bValidSCCState )
			{
				RedirectorRefs.bRedirectorValidForFixup = false;
				RedirectorRefs.FailureReason = LOCTEXT("RedirectorFixupFailed_BadSCC", "Redirector could not be checked out or marked for delete");
			}
		}

		// Load all referencers
		for ( auto PackageNameIt = RedirectorRefs.ReferencingPackageNames.CreateConstIterator(); PackageNameIt; ++PackageNameIt )
		{
			const FString PackageName = (*PackageNameIt).ToString();

			// Find the package in memory. If it is not in memory, try to load it
			UPackage* Package = FindPackage(NULL, *PackageName);
			if ( !Package )
			{
				Package = LoadPackage(NULL, *PackageName, LOAD_None);
			}

			if ( Package )
			{
				if ( Package->HasAnyPackageFlags(PKG_CompiledIn) )
				{
					// This is a script reference
					RedirectorRefs.bRedirectorValidForFixup = false;
					RedirectorRefs.FailureReason = FText::Format(LOCTEXT("RedirectorFixupFailed_CodeReference", "Redirector is referenced by code. Package: {0}"), FText::FromString(PackageName));
				}
				else
				{
					// If we found a valid package, mark it for save
					OutReferencingPackagesToSave.AddUnique(Package);
				}
			}
		}
	}
}
Beispiel #2
0
/**
 * Helper function designed to determine if the provided package should be backed up or not.
 * The function checks for many conditions, such as if the package is too large to backup,
 * if the package has a particular attribute that should prevent it from being backed up (such
 * as being marked for PIE-use), if cooking is in progress, etc.
 *
 * @param	InPackage		Package which should be checked to see if its valid for backing-up
 * @param	OutFileName		File name of the package on disk if the function determines the package
 *							already existed
 *
 * @return	true if the package is valid for backing-up; false otherwise
 */
bool FAutoPackageBackup::ShouldBackupPackage( const UPackage& InPackage, FString& OutFilename )
{
	// Check various conditions to see if the package is a valid candidate for backing up
	bool bShouldBackup =
		GIsEditor																			// Backing up packages only makes sense in the editor
		&& !IsRunningCommandlet()															// Don't backup saves resulting from commandlets
		&& IsPackageBackupEnabled()															// Ensure that the package backup is enabled in the first place
		&& (InPackage.HasAnyPackageFlags(PKG_PlayInEditor) == false)						// Don't back up PIE packages
		&& (InPackage.HasAnyPackageFlags(PKG_ContainsScript) == false);						// Don't back up script packages

	if( bShouldBackup )
	{
		GWarn->StatusUpdate( -1, -1, NSLOCTEXT("UnrealEd", "PackageBackup_ValidityWarning", "Determining asset backup validity...") );

		bShouldBackup =	FPackageName::DoesPackageExist( InPackage.GetName(), NULL, &OutFilename );	// Make sure the file already exists (no sense in backing up a new package)
	}
	
	// If the package passed the initial backup checks, proceed to check more specific conditions
	// that might disqualify the package from being backed up
	const int32 FileSizeOfBackup = IFileManager::Get().FileSize( *OutFilename );
	if ( bShouldBackup )
	{
		// Ensure that the size the backup would require is less than that of the maximum allowed
		// space for backups
		bShouldBackup = FileSizeOfBackup <= GetMaxAllowedBackupSpace();
	}

	// If all of the prior checks have passed, now see if the package has been backed up
	// too recently to be considered for an additional backup
	if ( bShouldBackup )
	{
		// Ensure that the autosave/backup directory exists
		const FString& BackupSaveDir = GetBackupDirectory();
		IFileManager::Get().MakeDirectory( *BackupSaveDir, 1 );

		// Find all of the files in the backup directory
		TArray<FString> FilesInBackupDir;
		IFileManager::Get().FindFilesRecursive(FilesInBackupDir, *BackupSaveDir, TEXT("*.*"), true, false);

		// Extract the base file name and extension from the passed-in package file name
		FString ExistingBaseFileName = FPaths::GetBaseFilename(OutFilename);
		FString ExistingFileNameExtension = FPaths::GetExtension(OutFilename);

		bool bFoundExistingBackup = false;
		int32 DirectorySize = 0;
		FDateTime LastBackupTimeStamp = FDateTime::MinValue();

		TArray<FBackupFileInfo> BackupFileInfoArray;
		
		// Check every file in the backup directory for matches against the passed-in package
		// (Additionally keep statistics on all backup files for potential maintenance)
		for ( TArray<FString>::TConstIterator FileIter( FilesInBackupDir ); FileIter; ++FileIter )
		{
			const FString CurBackupFileName = FString( *FileIter );
			
			// Create a new backup file info struct for keeping information about each backup file
			const int32 FileInfoIndex = BackupFileInfoArray.AddZeroed();
			FBackupFileInfo& CurBackupFileInfo = BackupFileInfoArray[ FileInfoIndex ];
			
			// Record the backup file's name, size, and timestamp
			CurBackupFileInfo.FileName = CurBackupFileName;
			CurBackupFileInfo.FileSize = IFileManager::Get().FileSize( *CurBackupFileName );
			
			// If we failed to get a timestamp or a valid size, something has happened to the file and it shouldn't be considered
			CurBackupFileInfo.FileTimeStamp = IFileManager::Get().GetTimeStamp(*CurBackupFileName);
			if (CurBackupFileInfo.FileTimeStamp == FDateTime::MinValue() || CurBackupFileInfo.FileSize == -1)
			{
				BackupFileInfoArray.RemoveAt( BackupFileInfoArray.Num() - 1 );
				continue;
			}

			// Calculate total directory size by adding the size of this backup file
			DirectorySize += CurBackupFileInfo.FileSize;

			FString CurBackupBaseFileName =  FPaths::GetBaseFilename(CurBackupFileName);
			FString CurBackupFileNameExtension = FPaths::GetExtension(CurBackupFileName);

			// The base file name of the backup file is going to include an underscore followed by a timestamp, so they must be removed for comparison's sake
			CurBackupBaseFileName = CurBackupBaseFileName.Left( CurBackupBaseFileName.Find( TEXT("_"), ESearchCase::CaseSensitive, ESearchDir::FromEnd ) );
					
			// If the base file names and extensions match, we've found a backup
			if ( CurBackupBaseFileName == ExistingBaseFileName &&  CurBackupFileNameExtension == ExistingFileNameExtension )
			{
				bFoundExistingBackup = true;

				// Keep track of the most recent matching time stamp so we can check if the passed-in package
				// has been backed up too recently
				if ( CurBackupFileInfo.FileTimeStamp > LastBackupTimeStamp )
				{
					LastBackupTimeStamp = CurBackupFileInfo.FileTimeStamp;
				}
			}
		}

		// If there was an existing backup, check to see if it was created too recently to allow another backup
		if ( bFoundExistingBackup )
		{
			// Check the difference in timestamp seconds against the backup interval; if not enough time has elapsed since
			// the last backup, we don't want to make another one
			if ((FDateTime::UtcNow() - LastBackupTimeStamp).GetTotalSeconds() < GetBackupInterval())
			{
				bShouldBackup = false;
			}
		}

		// If every other check against the package has succeeded for backup purposes, ensure there is enough directory space
		// available in the backup directory, as adding the new backup might use more space than the user allowed for backups.
		// If the backup file size + the current directory size exceeds the max allowed space, deleted old backups until there
		// is sufficient space. If enough space can't be freed for whatever reason, then no back-up will be created.
		if ( bShouldBackup && ( FileSizeOfBackup + DirectorySize > GetMaxAllowedBackupSpace() ) )
		{
			bShouldBackup = PerformBackupSpaceMaintenance( BackupFileInfoArray, DirectorySize, FileSizeOfBackup );
		}
	}
	
	return bShouldBackup;
}
//
// 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;
		}
	
		// Allow delegates to resolve this package
		FString PackageName = InOuter->GetName();

		// Do not resolve packages that are in memory
		if (!InOuter->HasAnyPackageFlags(PKG_InMemoryOnly))
		{
			PackageName = FPackageName::GetDelegateResolvedPackagePath(InOuter->GetName());
		}

		// Verify that the file exists.
		const bool DoesPackageExist = DoesPackageExistForGetPackageLinker(PackageName, CompatibleGuid, NewFilename);
		if ( !DoesPackageExist )
		{
			// In memory-only packages have no linker and this is ok.
			if (!(LoadFlags & LOAD_AllowDll) && !InOuter->HasAnyPackageFlags(PKG_InMemoryOnly) && !FLinkerLoad::IsKnownMissingPackage(InOuter->GetFName()))
			{
				FUObjectThreadContext& ThreadContext = FUObjectThreadContext::Get();
				FFormatNamedArguments Arguments;
				Arguments.Add(TEXT("AssetName"), FText::FromString(PackageName));
				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;
		}
	}
	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;
		}

		// Allow delegates to resolve this path
		PackageName = FPackageName::GetDelegateResolvedPackagePath(PackageName);

		UPackage* ExistingPackage = FindObject<UPackage>(nullptr, *PackageName);
		if (ExistingPackage)
		{
			if (!ExistingPackage->GetOuter() && ExistingPackage->HasAnyPackageFlags(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.
		const bool DoesPackageExist = DoesPackageExistForGetPackageLinker(PackageName, CompatibleGuid, NewFilename);
		if( !DoesPackageExist )
		{
			if (!FLinkerLoad::IsKnownMissingPackage(InLongPackageName))
			{
				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 = (ExistingPackage ? ExistingPackage : CreatePackage(nullptr, *PackageName));
		if (FilenamePkg != ExistingPackage && (LoadFlags & LOAD_PackageForPIE))
		{
			FilenamePkg->SetPackageFlags(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;
}