예제 #1
0
/**
 * Load a text file to an FString.
 * Supports all combination of ANSI/Unicode files and platforms.
 * @param Result string representation of the loaded file
 * @param Filename name of the file to load
 * @param VerifyFlags flags controlling the hash verification behavior ( see EHashOptions )
 */
bool FFileHelper::LoadFileToString( FString& Result, const TCHAR* Filename, uint32 VerifyFlags )
{
	FArchive* Reader = IFileManager::Get().CreateFileReader( Filename );
	if( !Reader )
	{
		return 0;
	}
	
	int32 Size = Reader->TotalSize();
	uint8* Ch = (uint8*)FMemory::Malloc(Size);
	Reader->Serialize( Ch, Size );
	bool Success = Reader->Close();
	delete Reader;
	BufferToString( Result, Ch, Size );

	// handle SHA verify of the file
	if( (VerifyFlags & EHashOptions::EnableVerify) && ( (VerifyFlags & EHashOptions::ErrorMissingHash) || FSHA1::GetFileSHAHash(Filename, NULL) ) )
	{
		// kick off SHA verify task. this frees the buffer on close
		FBufferReaderWithSHA Ar( Ch, Size, true, Filename, false, true );
	}
	else
	{
		// free manually since not running SHA task
		FMemory::Free(Ch);
	}

	return Success;
}
예제 #2
0
void SVisualLogger::HandleSaveCommandExecute()
{
	TArray<TSharedPtr<class STimeline> > OutTimelines;
	MainView->GetTimelines(OutTimelines, true);
	if (OutTimelines.Num() == 0)
	{
		MainView->GetTimelines(OutTimelines);
	}

	if (OutTimelines.Num())
	{
		// Prompt the user for the filenames
		TArray<FString> SaveFilenames;
		IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
		bool bSaved = false;
		if (DesktopPlatform)
		{
			void* ParentWindowWindowHandle = NULL;

			IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
			const TSharedPtr<SWindow>& MainFrameParentWindow = MainFrameModule.GetParentWindow();
			if (MainFrameParentWindow.IsValid() && MainFrameParentWindow->GetNativeWindow().IsValid())
			{
				ParentWindowWindowHandle = MainFrameParentWindow->GetNativeWindow()->GetOSWindowHandle();
			}

			const FString DefaultBrowsePath = FString::Printf(TEXT("%slogs/"), *FPaths::GameSavedDir());
			bSaved = DesktopPlatform->SaveFileDialog(
				ParentWindowWindowHandle,
				LOCTEXT("NewProjectBrowseTitle", "Choose a project location").ToString(),
				DefaultBrowsePath,
				TEXT(""),
				LogVisualizer::SaveFileTypes,
				EFileDialogFlags::None,
				SaveFilenames
				);
		}

		if (bSaved)
		{
			if (SaveFilenames.Num() > 0)
			{
				TArray<FVisualLogDevice::FVisualLogEntryItem> FrameCache;
				for (auto CurrentItem : OutTimelines)
				{
					FrameCache.Append(CurrentItem->GetEntries());
				}

				if (FrameCache.Num())
				{
					FArchive* FileArchive = IFileManager::Get().CreateFileWriter(*SaveFilenames[0]);
					FVisualLoggerHelpers::Serialize(*FileArchive, FrameCache);
					FileArchive->Close();
					delete FileArchive;
					FileArchive = NULL;
				}
			}
		}
	}
}
bool UParticleSystemAuditCommandlet::DumpSimpleSet(TSet<FString>& InSet, const TCHAR* InShortFilename, const TCHAR* InObjectClassName)
{
	if (InSet.Num() > 0)
	{
		check(InShortFilename != NULL);
		check(InObjectClassName != NULL);

		FArchive* OutputStream = GetOutputFile(InShortFilename);
		if (OutputStream != NULL)
		{
			UE_LOG(LogParticleSystemAuditCommandlet, Log, TEXT("Dumping '%s' results..."), InShortFilename);
			OutputStream->Logf(TEXT("%s,..."), InObjectClassName);
			for (TSet<FString>::TIterator DumpIt(InSet); DumpIt; ++DumpIt)
			{
				FString ObjName = *DumpIt;
				OutputStream->Logf(TEXT("%s"), *ObjName);
			}

			OutputStream->Close();
			delete OutputStream;
		}
		else
		{
			return false;
		}
	}
	return true;
}
예제 #4
0
bool FBuildPatchAppManifest::SaveToFile(const FString& Filename, bool bUseBinary)
{
	bool bSuccess = false;
	FArchive* FileOut = IFileManager::Get().CreateFileWriter(*Filename);
	if (FileOut)
	{
		if (bUseBinary)
		{
			Data->ManifestFileVersion = EBuildPatchAppManifestVersion::GetLatestVersion();
			FManifestWriter ManifestData;
			Serialize(ManifestData);
			ManifestData.Finalize();
			if (!ManifestData.IsError())
			{
				int32 DataSize = ManifestData.TotalSize();
				TArray<uint8> TempCompressed;
				TempCompressed.AddUninitialized(DataSize);
				int32 CompressedSize = DataSize;
				bool bDataIsCompressed = FCompression::CompressMemory(
					static_cast<ECompressionFlags>(COMPRESS_ZLIB | COMPRESS_BiasMemory),
					TempCompressed.GetData(),
					CompressedSize,
					ManifestData.GetBytes().GetData(),
					DataSize);
				TempCompressed.SetNum(CompressedSize);

				TArray<uint8>& FileData = bDataIsCompressed ? TempCompressed : ManifestData.GetBytes();

				FManifestFileHeader Header;
				*FileOut << Header;
				Header.HeaderSize = FileOut->Tell();
				Header.StoredAs = bDataIsCompressed ? EManifestFileHeader::STORED_COMPRESSED : EManifestFileHeader::STORED_RAW;
				Header.DataSize = DataSize;
				Header.CompressedSize = bDataIsCompressed ? CompressedSize : 0;
				FSHA1::HashBuffer(FileData.GetData(), FileData.Num(), Header.SHAHash.Hash);

				FileOut->Seek(0);
				*FileOut << Header;

				FileOut->Serialize(FileData.GetData(), FileData.Num());
				bSuccess = !FileOut->IsError();
			}
		}
		else
		{
			Data->ManifestFileVersion = EBuildPatchAppManifestVersion::GetLatestJsonVersion();
			FString JSONOutput;
			SerializeToJSON(JSONOutput);
			FTCHARToUTF8 JsonUTF8(*JSONOutput);
			FileOut->Serialize((UTF8CHAR*)JsonUTF8.Get(), JsonUTF8.Length() * sizeof(UTF8CHAR));
		}
		FileOut->Close();
		delete FileOut;
		FileOut = nullptr;
	}

	return bSuccess;
}
/** 
 * Write all the data mined from the minidump to a text file
 */
void FLinuxCrashContext::GenerateReport(const FString & DiagnosticsPath) const
{
	FArchive* ReportFile = IFileManager::Get().CreateFileWriter(*DiagnosticsPath);
	if (ReportFile != NULL)
	{
		FString Line;

		WriteLine(ReportFile, "Generating report for minidump");
		WriteLine(ReportFile);

		Line = FString::Printf(TEXT("Application version %d.%d.%d.0" ), FEngineVersion::Current().GetMajor(), FEngineVersion::Current().GetMinor(), FEngineVersion::Current().GetPatch());
		WriteLine(ReportFile, TCHAR_TO_UTF8(*Line));

		Line = FString::Printf(TEXT(" ... built from changelist %d"), FEngineVersion::Current().GetChangelist());
		WriteLine(ReportFile, TCHAR_TO_UTF8(*Line));
		WriteLine(ReportFile);

		utsname UnixName;
		if (uname(&UnixName) == 0)
		{
			Line = FString::Printf(TEXT( "OS version %s %s (network name: %s)" ), ANSI_TO_TCHAR(UnixName.sysname), ANSI_TO_TCHAR(UnixName.release), ANSI_TO_TCHAR(UnixName.nodename));
			WriteLine(ReportFile, TCHAR_TO_UTF8(*Line));	
			Line = FString::Printf( TEXT( "Running %d %s processors (%d logical cores)" ), FPlatformMisc::NumberOfCores(), ANSI_TO_TCHAR(UnixName.machine), FPlatformMisc::NumberOfCoresIncludingHyperthreads());
			WriteLine(ReportFile, TCHAR_TO_UTF8(*Line));
		}
		else
		{
			Line = FString::Printf(TEXT("OS version could not be determined (%d, %s)"), errno, ANSI_TO_TCHAR(strerror(errno)));
			WriteLine(ReportFile, TCHAR_TO_UTF8(*Line));	
			Line = FString::Printf( TEXT( "Running %d unknown processors" ), FPlatformMisc::NumberOfCores());
			WriteLine(ReportFile, TCHAR_TO_UTF8(*Line));
		}
		Line = FString::Printf(TEXT("Exception was \"%s\""), SignalDescription);
		WriteLine(ReportFile, TCHAR_TO_UTF8(*Line));
		WriteLine(ReportFile);

		WriteLine(ReportFile, "<SOURCE START>");
		WriteLine(ReportFile, "<SOURCE END>");
		WriteLine(ReportFile);

		WriteLine(ReportFile, "<CALLSTACK START>");
		WriteLine(ReportFile, MinidumpCallstackInfo);
		WriteLine(ReportFile, "<CALLSTACK END>");
		WriteLine(ReportFile);

		WriteLine(ReportFile, "0 loaded modules");

		WriteLine(ReportFile);

		Line = FString::Printf(TEXT("Report end!"));
		WriteLine(ReportFile, TCHAR_TO_UTF8(*Line));

		ReportFile->Close();
		delete ReportFile;
	}
}
예제 #6
0
void SVisualLogger::HandleLoadCommandExecute()
{
	FArchive Ar;
	TArray<FVisualLogDevice::FVisualLogEntryItem> RecordedLogs;

	TArray<FString> OpenFilenames;
	IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
	bool bOpened = false;
	if (DesktopPlatform)
	{
		void* ParentWindowWindowHandle = NULL;

		IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked<IMainFrameModule>(TEXT("MainFrame"));
		const TSharedPtr<SWindow>& MainFrameParentWindow = MainFrameModule.GetParentWindow();
		if (MainFrameParentWindow.IsValid() && MainFrameParentWindow->GetNativeWindow().IsValid())
		{
			ParentWindowWindowHandle = MainFrameParentWindow->GetNativeWindow()->GetOSWindowHandle();
		}

		const FString DefaultBrowsePath = FString::Printf(TEXT("%slogs/"), *FPaths::GameSavedDir());

		bOpened = DesktopPlatform->OpenFileDialog(
			ParentWindowWindowHandle,
			LOCTEXT("OpenProjectBrowseTitle", "Open Project").ToString(),
			DefaultBrowsePath,
			TEXT(""),
			LogVisualizer::LoadFileTypes,
			EFileDialogFlags::None,
			OpenFilenames
			);
	}

	if (bOpened && OpenFilenames.Num() > 0)
	{
		OnNewWorld(nullptr);
		for (int FilenameIndex = 0; FilenameIndex < OpenFilenames.Num(); ++FilenameIndex)
		{
			FString CurrentFileName = OpenFilenames[FilenameIndex];
			const bool bIsBinaryFile = CurrentFileName.Find(TEXT(".bvlog")) != INDEX_NONE;
			if (bIsBinaryFile)
			{
				FArchive* FileAr = IFileManager::Get().CreateFileReader(*CurrentFileName);
				FVisualLoggerHelpers::Serialize(*FileAr, RecordedLogs);
				FileAr->Close();
				delete FileAr;
				FileAr = NULL;

				for (FVisualLogDevice::FVisualLogEntryItem& CurrentItem : RecordedLogs)
				{
					OnNewLogEntry(CurrentItem);
				}
			}
		}
	}
}
예제 #7
0
bool UJavascriptLibrary::WriteFile(UObject* Object, FString Filename)
{
	FArchive* Writer = IFileManager::Get().CreateFileWriter(*Filename);
	if (!Writer)
	{
		return false;
	}
		
	Writer->Serialize(FArrayBufferAccessor::GetData(), FArrayBufferAccessor::GetSize());
	return Writer->Close();
}
예제 #8
0
uint8 FBuildPatchUtils::VerifyFile(const FString& FileToVerify, const FSHAHashData& Hash1, const FSHAHashData& Hash2, FBuildPatchFloatDelegate ProgressDelegate, FBuildPatchBoolRetDelegate ShouldPauseDelegate, double& TimeSpentPaused)
{
	uint8 ReturnValue = 0;
	FArchive* FileReader = IFileManager::Get().CreateFileReader(*FileToVerify);
	ProgressDelegate.ExecuteIfBound(0.0f);
	if (FileReader != NULL)
	{
		FSHA1 HashState;
		FSHAHashData HashValue;
		const int64 FileSize = FileReader->TotalSize();
		uint8* FileReadBuffer = new uint8[FileBufferSize];
		while (!FileReader->AtEnd() && !FBuildPatchInstallError::HasFatalError())
		{
			// Pause if necessary
			const double PrePauseTime = FPlatformTime::Seconds();
			double PostPauseTime = PrePauseTime;
			bool bShouldPause = ShouldPauseDelegate.IsBound() && ShouldPauseDelegate.Execute();
			while (bShouldPause && !FBuildPatchInstallError::HasFatalError())
			{
				FPlatformProcess::Sleep(0.1f);
				bShouldPause = ShouldPauseDelegate.Execute();
				PostPauseTime = FPlatformTime::Seconds();
			}
			// Count up pause time
			TimeSpentPaused += PostPauseTime - PrePauseTime;
			// Read file and update hash state
			const int64 SizeLeft = FileSize - FileReader->Tell();
			const uint32 ReadLen = FMath::Min< int64 >(FileBufferSize, SizeLeft);
			FileReader->Serialize(FileReadBuffer, ReadLen);
			HashState.Update(FileReadBuffer, ReadLen);
			const double FileSizeTemp = FileSize;
			const float Progress = 1.0f - ((SizeLeft - ReadLen) / FileSizeTemp);
			ProgressDelegate.ExecuteIfBound(Progress);
		}
		delete[] FileReadBuffer;
		HashState.Final();
		HashState.GetHash(HashValue.Hash);
		ReturnValue = (HashValue == Hash1) ? 1 : (HashValue == Hash2) ? 2 : 0;
		if (ReturnValue == 0)
		{
			GLog->Logf(TEXT("BuildDataGenerator: Verify failed on %s"), *FPaths::GetCleanFilename(FileToVerify));
		}
		FileReader->Close();
		delete FileReader;
	}
	else
	{
		GLog->Logf(TEXT("BuildDataGenerator: ERROR VerifyFile cannot open %s"), *FileToVerify);
	}
	ProgressDelegate.ExecuteIfBound(1.0f);
	return ReturnValue;
}
/** 
 * Creates (fake so far) minidump
 */
void GenerateMinidump(const FString & Path)
{
	FArchive* ReportFile = IFileManager::Get().CreateFileWriter(*Path);
	if (ReportFile != NULL)
	{
		// write BOM
		static uint32 Garbage = 0xDEADBEEF;
		ReportFile->Serialize(&Garbage, sizeof(Garbage));

		ReportFile->Close();
		delete ReportFile;
	}
}
예제 #10
0
void G_UnSnapshotLevel (bool hubLoad)
{
	if (level.info->snapshot == NULL)
		return;

	if (level.info->isValid())
	{
		SaveVersion = level.info->snapshotVer;
		level.info->snapshot->Reopen ();
		FArchive arc (*level.info->snapshot);
		if (hubLoad)
			arc.SetHubTravel ();
		G_SerializeLevel (arc, hubLoad);
		arc.Close ();
		level.FromSnapshot = true;

		TThinkerIterator<APlayerPawn> it;
		APlayerPawn *pawn, *next;

		next = it.Next();
		while ((pawn = next) != 0)
		{
			next = it.Next();
			if (pawn->player == NULL || pawn->player->mo == NULL || !playeringame[pawn->player - players])
			{
				int i;

				// If this isn't the unmorphed original copy of a player, destroy it, because it's extra.
				for (i = 0; i < MAXPLAYERS; ++i)
				{
					if (playeringame[i] && players[i].morphTics && players[i].mo->tracer == pawn)
					{
						break;
					}
				}
				if (i == MAXPLAYERS)
				{
					pawn->Destroy ();
				}
			}
		}
	}
	// No reason to keep the snapshot around once the level's been entered.
	level.info->ClearSnapshot();
	if (hubLoad)
	{
		// Unlock ACS global strings that were locked when the snapshot was made.
		FBehavior::StaticUnlockLevelVarStrings();
	}
}
예제 #11
0
void FDesktopPlatformLinux::EnumerateEngineInstallations(TMap<FString, FString> &OutInstallations)
{
	EnumerateLauncherEngineInstallations(OutInstallations);

	FString UProjectPath = FString(FPlatformProcess::ApplicationSettingsDir()) / "Unreal.uproject";
	FArchive* File = IFileManager::Get().CreateFileWriter(*UProjectPath, FILEWRITE_EvenIfReadOnly);
	if (File)
	{
		File->Close();
		delete File;
	}
	else
	{
	    SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Unable to write to Settings Directory", TCHAR_TO_UTF8(*UProjectPath), NULL);
	}

	FConfigFile ConfigFile;
	FString ConfigPath = FString(FPlatformProcess::ApplicationSettingsDir()) / FString(TEXT("UnrealEngine")) / FString(TEXT("Install.ini"));
	ConfigFile.Read(ConfigPath);

	FConfigSection &Section = ConfigFile.FindOrAdd(TEXT("Installations"));

	// @todo: currently we can enumerate only this installation
	FString EngineDir = FPaths::EngineDir();

	FString EngineId;
	const FName* Key = Section.FindKey(EngineDir);
	if (Key)
	{
		EngineId = Key->ToString();
	}
	else
	{
		if (!OutInstallations.FindKey(EngineDir))
		{
			EngineId = FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens);
			Section.AddUnique(*EngineId, EngineDir);
			ConfigFile.Dirty = true;
		}
	}
	if (!EngineId.IsEmpty() && !OutInstallations.Find(EngineId))
	{
		OutInstallations.Add(EngineId, EngineDir);
	}

	ConfigFile.Write(ConfigPath);

	IFileManager::Get().Delete(*UProjectPath);
}
bool FRuntimeAssetCacheBackend::PutCachedData(const FName Bucket, const TCHAR* CacheKey, TArray<uint8>& InData, FCacheEntryMetadata* Metadata)
{
	bool bResult = false;
	FArchive* Ar = CreateWriteArchive(Bucket, CacheKey);
	if (!Ar)
	{
		return bResult;
	}

	*Ar << *Metadata;
	Ar->Serialize(InData.GetData(), InData.Num());
	bResult = Ar->Close();
	delete Ar;
	return bResult;
}
예제 #13
0
bool UJavascriptLibrary::ReadFile(UObject* Object, FString Filename)
{
	FArchive* Reader = IFileManager::Get().CreateFileReader(*Filename);
	if (!Reader)
	{
		return false;
	}

	int32 Size = Reader->TotalSize();
	if (Size != FArrayBufferAccessor::GetSize())
	{
		return false;
	}

	Reader->Serialize(FArrayBufferAccessor::GetData(), Size);
	return Reader->Close();	
}
예제 #14
0
bool FTextLocalizationManager::FLocalizationEntryTracker::ReadFromFile(const FString& FilePath)
{
	FArchive* Reader = IFileManager::Get().CreateFileReader( *FilePath );
	if( !Reader )
	{
		return false;
	}

	Reader->SetForceUnicode(true);

	ReadFromArchive(*Reader, FilePath);

	bool Success = Reader->Close();
	delete Reader;

	return Success;
}
예제 #15
0
/**
 * Load a binary file to a dynamic array.
 */
bool FFileHelper::LoadFileToArray( TArray<uint8>& Result, const TCHAR* Filename, uint32 Flags )
{
	FArchive* Reader = IFileManager::Get().CreateFileReader( Filename, Flags );
	if( !Reader )
	{
		if (!(Flags & FILEREAD_Silent))
		{
			UE_LOG(LogStreaming,Warning,TEXT("Failed to read file '%s' error."),Filename);
		}
		return 0;
	}
	Result.Reset();
	Result.AddUninitialized( Reader->TotalSize() );
	Reader->Serialize(Result.GetData(), Result.Num());
	bool Success = Reader->Close();
	delete Reader;
	return Success;
}
예제 #16
0
bool FPerfCounters::ReportUnplayableCondition(const FString& ConditionDescription)
{
	FString UnplayableConditionFile(FPaths::Combine(*FPaths::GameSavedDir(), *FString::Printf(TEXT("UnplayableConditionForPid_%d.txt"), FPlatformProcess::GetCurrentProcessId())));

	FArchive* ReportFile = IFileManager::Get().CreateFileWriter(*UnplayableConditionFile);
	if (UNLIKELY(ReportFile == nullptr))
	{
		return false;
	}

	// include description for debugging
	FTCHARToUTF8 Converter(*FString::Printf(TEXT("Unplayable condition encountered: %s\n"), *ConditionDescription));
	ReportFile->Serialize(reinterpret_cast<void *>(const_cast<char *>(Converter.Get())), Converter.Length());

	ReportFile->Close();
	delete ReportFile;

	return true;
}
FCacheEntryMetadata* FRuntimeAssetCacheBackend::GetCachedData(const FName Bucket, const TCHAR* CacheKey, TArray<uint8>& OutData)
{
	FCacheEntryMetadata* Result = nullptr;
	FArchive* Ar = CreateReadArchive(Bucket, CacheKey);
	if (!Ar)
	{
		return Result;
	}

	Result = PreloadMetadata(Ar);

	int64 TotalSize = Ar->TotalSize();
	int64 CurrentPosition = Ar->Tell();
	int64 NumberOfBytesToSerialize = TotalSize - CurrentPosition;
	OutData.Reset();
	OutData.AddUninitialized(NumberOfBytesToSerialize);

	Ar->Serialize(OutData.GetData(), NumberOfBytesToSerialize);
	Ar->Close();
	delete Ar;
	return Result;
}
	virtual void UpdateADBPath() override
	{
		FScopeLock PathUpdateLock(&ADBPathCheckLock);
		TCHAR AndroidDirectory[32768] = { 0 };
		FPlatformMisc::GetEnvironmentVariable(TEXT("ANDROID_HOME"), AndroidDirectory, 32768);

		FString ADBPath;
		
#if PLATFORM_MAC
		if (AndroidDirectory[0] == 0)
		{
			// didn't find ANDROID_HOME, so parse the .bash_profile file on MAC
			FArchive* FileReader = IFileManager::Get().CreateFileReader(*FString([@"~/.bash_profile" stringByExpandingTildeInPath]));
			if (FileReader)
			{
				const int64 FileSize = FileReader->TotalSize();
				ANSICHAR* AnsiContents = (ANSICHAR*)FMemory::Malloc(FileSize);
				FileReader->Serialize(AnsiContents, FileSize);
				FileReader->Close();
				delete FileReader;

				TArray<FString> Lines;
				FString(ANSI_TO_TCHAR(AnsiContents)).ParseIntoArrayLines(&Lines);
				FMemory::Free(AnsiContents);

				for (int32 Index = 0; Index < Lines.Num(); Index++)
				{
					if (AndroidDirectory[0] == 0 && Lines[Index].StartsWith(TEXT("export ANDROID_HOME=")))
					{
						FString Directory;
						Lines[Index].Split(TEXT("="), NULL, &Directory);
						Directory = Directory.Replace(TEXT("\""), TEXT(""));
						FCString::Strcpy(AndroidDirectory, *Directory);
						setenv("ANDROID_HOME", TCHAR_TO_ANSI(AndroidDirectory), 1);
					}
				}
			}
		}
int32 UFixupRedirectsCommandlet::Main( const FString& Params )
{
	// Retrieve list of all packages in .ini paths.
	TArray<FString> PackageList;
	FEditorFileUtils::FindAllPackageFiles(PackageList);
	if( !PackageList.Num() )
	{
		return 0;
	}

	// process the commandline
	FString Token;
	const TCHAR* CommandLine	= *Params;
	bool bIsQuietMode			= false;
	bool bIsTestOnly			= false;
	bool bShouldRestoreProgress= false;
	bool bIsSCCDisabled		= false;
	bool bUpdateEnginePackages = false;
	bool bDeleteRedirects		= true;
	bool bDeleteOnlyReferencedRedirects = false;
	bool bAutoSubmit			= false;
	bool bSandboxed			= IFileManager::Get().IsSandboxEnabled();

	while (FParse::Token(CommandLine, Token, 1))
	{
		if (Token == TEXT("-nowarn"))
		{
			bIsQuietMode = true;
		}
		else if (Token == TEXT("-testonly"))
		{
			bIsTestOnly = true;
		}
		else if (Token == TEXT("-restore"))
		{
			bShouldRestoreProgress = true;
		}
		else if (Token == TEXT("-NoAutoCheckout"))
		{
			bIsSCCDisabled = true;
		}
		else if (Token == TEXT("-AutoSubmit"))
		{
			bAutoSubmit = true;
		}
		else if (Token == TEXT("-UpdateEnginePackages"))
		{
			bUpdateEnginePackages = true;
		}
		else if (Token == TEXT("-NoDelete"))
		{
			bDeleteRedirects = false;
		}
		else if (Token == TEXT("-NoDeleteUnreferenced"))
		{
			bDeleteOnlyReferencedRedirects = true;
		}
		else if ( Token.Left(1) != TEXT("-") )
		{
			FPackageName::SearchForPackageOnDisk(Token, &GRedirectCollector.FileToFixup);
		}
	}

	if (bSandboxed)
	{
		UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Running in a sandbox."));
		bIsSCCDisabled = true;
		bAutoSubmit = false;
		bDeleteRedirects = false;
	}


	bool bShouldSkipErrors = false;

	// Setup file Output log in the Saved Directory
	const FString ProjectDir = FPaths::GetPath(FPaths::GetProjectFilePath());
	const FString LogFilename = FPaths::Combine(*ProjectDir, TEXT("Saved"), TEXT("FixupRedirect"), TEXT("FixupRedirect.log"));
	FArchive* LogOutputFile = IFileManager::Get().CreateFileWriter(*LogFilename);
	LogOutputFile->Logf(TEXT("[Redirects]"));

	/////////////////////////////////////////////////////////////////////
	// Display any script code referenced redirects
	/////////////////////////////////////////////////////////////////////

	TArray<FString> UpdatePackages;
	TArray<FString> RedirectorsThatCantBeCleaned;

	if (!bShouldRestoreProgress)
	{
		// load all string asset reference targets, and add fake redirectors for them
		GRedirectCollector.ResolveStringAssetReference();

		for (int32 RedirIndex = 0; RedirIndex < GRedirectCollector.Redirections.Num(); RedirIndex++)
		{
			FRedirection& Redir = GRedirectCollector.Redirections[RedirIndex];
			UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Boot redirector can't be cleaned: %s(%s) -> %s(%s)"), *Redir.RedirectorName, *Redir.RedirectorPackageFilename, *Redir.DestinationObjectName, *Redir.PackageFilename);
			RedirectorsThatCantBeCleaned.AddUnique(Redir.RedirectorName);
		}

		/////////////////////////////////////////////////////////////////////
		// Build a list of packages that need to be updated
		/////////////////////////////////////////////////////////////////////

		SET_WARN_COLOR(COLOR_WHITE);
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(""));
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Generating list of tasks:"));
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("-------------------------"));
		CLEAR_WARN_COLOR();

		int32 GCIndex = 0;

		// process script code first pass, then everything else
		for( int32 PackageIndex = 0; PackageIndex < PackageList.Num(); PackageIndex++ )
		{
			const FString& Filename = PackageList[PackageIndex];

			// already loaded code, skip them, and skip autosave packages
			if (Filename.StartsWith(AutoSaveUtils::GetAutoSaveDir(), ESearchCase::IgnoreCase))
			{
				continue;
			}

			UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Looking for redirects in %s..."), *Filename);

			// Assert if package couldn't be opened so we have no chance of messing up saving later packages.
			UPackage* Package = Cast<UPackage>(LoadPackage(NULL, *Filename, 0));

			// load all string asset reference targets, and add fake redirectors for them
			GRedirectCollector.ResolveStringAssetReference();

			if (!Package)
			{
				SET_WARN_COLOR(COLOR_RED);
				UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("   ... failed to open %s"), *Filename);
				CLEAR_WARN_COLOR();

				// if we are not in quiet mode, and the user wants to continue, go on
				const FText Message = FText::Format( NSLOCTEXT("Unreal", "PackageFailedToOpen", "The package '{0}' failed to open. Should this commandlet continue operation, ignoring further load errors? Continuing may have adverse effects (object references may be lost)."), FText::FromString( Filename ) );
				if (bShouldSkipErrors || (!bIsQuietMode && GWarn->YesNof( Message )))
				{
					bShouldSkipErrors = true;
					continue;
				}
				else
				{
					return 1;
				}
			}

			// all notices here about redirects get this color
			SET_WARN_COLOR(COLOR_DARK_GREEN);

			// look for any redirects that were followed that are referenced by this package
			// (may have been loaded earlier if this package was loaded by script code)
			// any redirectors referenced by script code can't be cleaned up
			for (int32 RedirIndex = 0; RedirIndex < GRedirectCollector.Redirections.Num(); RedirIndex++)
			{
				FRedirection& Redir = GRedirectCollector.Redirections[RedirIndex];

				if (Redir.PackageFilename == Filename)
				{
					UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("   ... references %s [-> %s]"), *Redir.RedirectorName, *Redir.DestinationObjectName);

					// put the source and dest packages in the list to be updated
					UpdatePackages.AddUnique(Redir.PackageFilename);
					UpdatePackages.AddUnique(Redir.RedirectorPackageFilename);
				}
			}

			// clear the flag for needing to collect garbage
			bool bNeedToCollectGarbage = false;

			// if this package has any redirectors, make sure we update it
			if (!GRedirectCollector.FileToFixup.Len() || GRedirectCollector.FileToFixup == FPackageName::FilenameToLongPackageName(Filename))
			{
				TArray<UObject *> ObjectsInOuter;
				GetObjectsWithOuter(Package, ObjectsInOuter);
				for( int32 Index = 0; Index < ObjectsInOuter.Num(); Index++ )
				{
					UObject* Obj = ObjectsInOuter[Index];
					UObjectRedirector* Redir = Cast<UObjectRedirector>(Obj);
					if (Redir)
					{
						// make sure this package is in the list of packages to update
						UpdatePackages.AddUnique(Filename);

						UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("   ... has redirect %s [-> %s]"), *Redir->GetPathName(), *Redir->DestinationObject->GetFullName());
						LogOutputFile->Logf(TEXT("%s = %s"), *Redir->GetPathName(), *Redir->DestinationObject->GetFullName());

						// make sure we GC if we found a redirector
						bNeedToCollectGarbage = true;
					}
				}
			}

			CLEAR_WARN_COLOR();

			// collect garbage every N packages, or if there was any redirectors in the package 
			// (to make sure that redirectors get reloaded properly and followed by the callback)
			// also, GC after loading a map package, to make sure it's unloaded so that we can track
			// other packages loading a map package as a dependency
			if (((++GCIndex) % 50) == 0 || bNeedToCollectGarbage || Package->ContainsMap())
			{
				// collect garbage to close the package
				CollectGarbage(RF_Native);
				UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("GC..."));
				// reset our counter
				GCIndex = 0;
			}
		}

		// save off restore point
		FArchive* Ar = IFileManager::Get().CreateFileWriter(*(FPaths::GameSavedDir() + TEXT("Fixup.bin")));
		*Ar << GRedirectCollector.Redirections;
		*Ar << UpdatePackages;
		*Ar << RedirectorsThatCantBeCleaned;
		delete Ar;
	}
	else
	{
		// load restore point
		FArchive* Ar = IFileManager::Get().CreateFileReader(*(FPaths::GameSavedDir() + TEXT("Fixup.bin")));
		if( Ar != NULL )
		{
			*Ar << GRedirectCollector.Redirections;
			*Ar << UpdatePackages;
			*Ar << RedirectorsThatCantBeCleaned;
			delete Ar;
		}
	}

	// unregister the callback so we stop getting redirections added
	FCoreUObjectDelegates::RedirectorFollowed.RemoveAll(&GRedirectCollector);
	
	/////////////////////////////////////////////////////////////////////
	// Explain to user what is happening
	/////////////////////////////////////////////////////////////////////
	SET_WARN_COLOR(COLOR_YELLOW);
	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(""));
	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Files that need to fixed up:"));
	SET_WARN_COLOR(COLOR_DARK_YELLOW);

	// print out the working set of packages
	for (int32 PackageIndex = 0; PackageIndex < UpdatePackages.Num(); PackageIndex++)
	{
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("   %s"), *UpdatePackages[PackageIndex]);
	}

	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(""));
	CLEAR_WARN_COLOR();

	// if we are only testing, just just quiet before actually doing anything
	if (bIsTestOnly)
	{
		LogOutputFile->Close();
		delete LogOutputFile;
		LogOutputFile = NULL;
		return 0;
	}

	/////////////////////////////////////////////////////////////////////
	// Find redirectors that are referenced by packages we cant check out
	/////////////////////////////////////////////////////////////////////

	bool bEngineRedirectorCantBeCleaned = false;

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

	// initialize source control if it hasn't been initialized yet
	if(!bIsSCCDisabled)
	{
		SourceControlProvider.Init();

		//Make sure we can check out all packages - update their state first
		SourceControlProvider.Execute(ISourceControlOperation::Create<FUpdateStatus>(), UpdatePackages);
	}

	for( int32 PackageIndex = 0; PackageIndex < UpdatePackages.Num(); PackageIndex++ )
	{
		const FString& Filename = UpdatePackages[PackageIndex];

		bool bCanEdit = true;

		if(!bIsSCCDisabled)
		{
			FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Filename, EStateCacheUsage::ForceUpdate);
			if(SourceControlState.IsValid() && !SourceControlState->CanEdit())
			{
				bCanEdit = false;
			}
		}
		else if(IFileManager::Get().IsReadOnly(*Filename))
		{
			bCanEdit = false;
		}

		if(!bCanEdit)
		{
			const bool bAllowCheckout = bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() );

			if (!bIsSCCDisabled && bAllowCheckout)
			{
				FString PackageName(FPackageName::FilenameToLongPackageName(Filename));
				FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Filename, EStateCacheUsage::ForceUpdate);
				if (SourceControlState.IsValid() && SourceControlState->CanCheckout())
				{
					SourceControlProvider.Execute(ISourceControlOperation::Create<FCheckOut>(), SourceControlHelpers::PackageFilename(PackageName));
					FSourceControlStatePtr NewSourceControlState = SourceControlProvider.GetState(Filename, EStateCacheUsage::ForceUpdate);
					bCanEdit = NewSourceControlState.IsValid() && NewSourceControlState->CanEdit();
				}
			}

			// if the checkout failed for any reason, we can't clean it up
			if (bAllowCheckout && !bCanEdit)
			{
				SET_WARN_COLOR(COLOR_RED);
				UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Couldn't check out/edit %s..."), *Filename);
				CLEAR_WARN_COLOR();

				// load all string asset reference targets, and add fake redirectors for them
				GRedirectCollector.ResolveStringAssetReference();

				// any redirectors that are pointed to by this read-only package can't be fixed up
				for (int32 RedirIndex = 0; RedirIndex < GRedirectCollector.Redirections.Num(); RedirIndex++)
				{
					FRedirection& Redir = GRedirectCollector.Redirections[RedirIndex];

					// any redirectors pointed to by this file we don't clean
					if (Redir.PackageFilename == Filename)
					{
						RedirectorsThatCantBeCleaned.AddUnique(Redir.RedirectorName);
						UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("   ... can't fixup references to %s"), *Redir.RedirectorName);

						// if this redirector is in an Engine package, we need to alert the user, because
						// Engine packages are shared between games, so if one game still needs the redirector
						// to be valid, then we can't check in Engine packages after another game has
						// run that could 
						if (!bEngineRedirectorCantBeCleaned)
						{
							FString PackagePath;
							FPackageName::DoesPackageExist(Redir.RedirectorPackageFilename, NULL, &PackagePath);
							if (PackagePath.StartsWith(FPaths::EngineDir()))
							{
								bEngineRedirectorCantBeCleaned = true;
							}
						}
					}
					// any redirectors in this file can't be deleted
					else if (Redir.RedirectorPackageFilename == Filename)
					{
						RedirectorsThatCantBeCleaned.AddUnique(Redir.RedirectorName);
						UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("   ... can't delete %s"), *Redir.RedirectorName);
					}
				}
			} 
			else
			{
				SET_WARN_COLOR(COLOR_DARK_GREEN);
				UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Checked out %s..."), *Filename);
				CLEAR_WARN_COLOR();
			}
		}
	}
	// warn about an engine package that can't be cleaned up (may want to revert engine packages)
	if (bEngineRedirectorCantBeCleaned)
	{
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(""));
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Engine package redirectors are referenced by packages that can't be cleaned. If you have multiple games, it's recommended you revert any checked out Engine packages."));
		bUpdateEnginePackages=false;
	}

	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(""));

	/////////////////////////////////////////////////////////////////////
	// Load and save packages to save out the proper fixed up references
	/////////////////////////////////////////////////////////////////////

	SET_WARN_COLOR(COLOR_WHITE);
	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Fixing references to point to proper objects:"));
	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("---------------------------------------------"));
	CLEAR_WARN_COLOR();

	// keep a list of all packages that have ObjectRedirects in them. This is not needed if we aren't deleting redirects
	TArray<bool> PackagesWithRedirects;

	// Delete these packages entirely because they are empty
	TArray<bool> EmptyPackagesToDelete;

	if ( bDeleteRedirects )
	{
		PackagesWithRedirects.AddZeroed(UpdatePackages.Num());
		EmptyPackagesToDelete.AddZeroed(PackageList.Num());
	}

	bool bSCCEnabled = ISourceControlModule::Get().IsEnabled();

	// Iterate over all packages, loading and saving to remove all references to ObjectRedirectors (can't delete the Redirects yet)
	for( int32 PackageIndex = 0; PackageIndex < UpdatePackages.Num(); PackageIndex++ )
	{
		const FString& Filename = UpdatePackages[PackageIndex];

		// we can't do anything with packages that are read-only or cant be edited due to source control (we don't want to fixup the redirects)
		if(bSCCEnabled)
		{
			FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(Filename), EStateCacheUsage::ForceUpdate);
			if(SourceControlState.IsValid() && !SourceControlState->CanEdit())
			{
				continue;
			}
		}
		else if(IFileManager::Get().IsReadOnly(*Filename))
		{
			continue;
		}
		
		// Assert if package couldn't be opened so we have no chance of messing up saving later packages.
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Cleaning %s"), *Filename);
		UPackage* Package = LoadPackage( NULL, *Filename, LOAD_NoVerify );
		// load all string asset reference targets, and add fake redirectors for them
		GRedirectCollector.ResolveStringAssetReference();

		// if it failed to open, we have already dealt with quitting if we are going to, so just skip it
		if (!Package)
		{
			continue;
		}

		if ( bDeleteOnlyReferencedRedirects && bDeleteRedirects )
		{
			TArray<UObject *> ObjectsInOuter;
			GetObjectsWithOuter(Package, ObjectsInOuter);
			for( int32 Index = 0; Index < ObjectsInOuter.Num(); Index++ )
			{
				UObject* Obj = ObjectsInOuter[Index];
				UObjectRedirector* Redir = Cast<UObjectRedirector>(Obj);
				if (Redir)
				{
					PackagesWithRedirects[PackageIndex] = 1;
					break;
				}
			}
		}

		// Don't save packages in the engine directory if we did not opt to update engine packages
		if( bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() ) )
		{
			// save out the package
			UWorld* World = UWorld::FindWorldInPackage(Package);
			GEditor->SavePackage( Package, World, World ? RF_NoFlags : RF_Standalone, *Filename, GWarn );
		}
		
		// collect garbage to close the package
		UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Collecting Garbage..."));
		CollectGarbage(RF_Native);
	}

	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(""));

	if ( bDeleteRedirects )
	{
		/////////////////////////////////////////////////////////////////////
		// Delete all redirects that are no longer referenced
		/////////////////////////////////////////////////////////////////////

		SET_WARN_COLOR(COLOR_WHITE);
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Deleting redirector objects:"));
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("----------------------------"));
		CLEAR_WARN_COLOR();

		const TArray<FString>& PackagesToCleaseOfRedirects = bDeleteOnlyReferencedRedirects ? UpdatePackages : PackageList;

		int32 GCIndex = 0;

		// Again, iterate over all packages, loading and saving to and this time deleting all 
		for( int32 PackageIndex = 0; PackageIndex < PackagesToCleaseOfRedirects.Num(); PackageIndex++ )
		{
			const FString& Filename = PackagesToCleaseOfRedirects[PackageIndex];
		
			if (bDeleteOnlyReferencedRedirects && PackagesWithRedirects[PackageIndex] == 0)
			{
				continue;
			}

			// The file should have already been checked out/be writable
			// If it is read only that means the checkout failed or we are not using source control and we can not write this file.
			if(bSCCEnabled)
			{
				FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(Filename), EStateCacheUsage::ForceUpdate);
				if(SourceControlState.IsValid() && !SourceControlState->CanEdit())
				{
					UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Skipping file: source control says we cant edit the file %s..."), *Filename);
					continue;
				}
			}
			else if( IFileManager::Get().IsReadOnly( *Filename ))
			{
				UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Skipping read-only file %s..."), *Filename);
				continue;
			}

			UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Wiping %s..."), *Filename);
		
			UPackage* Package = LoadPackage( NULL, *Filename, 0 );
			// load all string asset reference targets, and add fake redirectors for them
			GRedirectCollector.ResolveStringAssetReference();
			// if it failed to open, we have already dealt with quitting if we are going to, so just skip it
			if (!Package)
			{
				continue;
			}

			// assume that all redirs in this file are still-referenced
			bool bIsDirty = false;

			// see if this package is completely empty other than purged redirectors and metadata
			bool bIsEmpty = true;

			// delete all ObjectRedirects now
			TArray<UObjectRedirector*> Redirs;
			// find all redirectors, put into an array so delete doesn't mess up the iterator
			TArray<UObject *> ObjectsInOuter;
			GetObjectsWithOuter(Package, ObjectsInOuter);
			for( int32 Index = 0; Index < ObjectsInOuter.Num(); Index++ )
			{
				UObject* Obj = ObjectsInOuter[Index];
				UObjectRedirector *Redir = Cast<UObjectRedirector>(Obj);

				if (Redir)
				{
					int32 Dummy;
					// if the redirector was marked as uncleanable, skip it
					if (RedirectorsThatCantBeCleaned.Find(Redir->GetFullName(), Dummy))
					{
						SET_WARN_COLOR(COLOR_DARK_RED);
						UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("   ... skipping still-referenced %s"), *Redir->GetFullName());
						CLEAR_WARN_COLOR();
						bIsEmpty = false;
						continue;
					}

					bIsDirty = true;
					SET_WARN_COLOR(COLOR_DARK_GREEN);
					UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("   ... deleting %s"), *Redir->GetFullName());
					CLEAR_WARN_COLOR();
					Redirs.Add(Redir);
				}
				else if ( (!Obj->IsA(UMetaData::StaticClass()) && !Obj->IsA(UField::StaticClass()))
					|| Obj->IsA(UUserDefinedEnum::StaticClass())) // UserDefinedEnum should not be deleted
				{
					// This package has a real object
					bIsEmpty = false;
				}
			}

			if (bIsEmpty && !bDeleteOnlyReferencedRedirects)
			{
				EmptyPackagesToDelete[PackageIndex] = 1;
			}

			// mark them for deletion.
			for (int32 RedirIndex = 0; RedirIndex < Redirs.Num(); RedirIndex++)
			{
				UObjectRedirector* Redirector = Redirs[RedirIndex];
				// Remove standalone flag and mark as pending kill.
				Redirector->ClearFlags( RF_Standalone | RF_Public );
			
				// We don't need redirectors in the root set, the references should already be fixed up
				if ( Redirector->IsRooted() )
				{
					Redirector->RemoveFromRoot();
				}

				Redirector->MarkPendingKill();
			}
			Redirs.Empty();

			if (bIsDirty)
			{
				// Don't save packages in the engine directory if we did not opt to update engine packages
				if( bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() ) )
				{
					// save the package
					UWorld* World = UWorld::FindWorldInPackage(Package);
					GEditor->SavePackage( Package, World, World ? RF_NoFlags : RF_Standalone, *Filename, GWarn );
				}
			}

			// collect garbage every N packages, or if there was any redirectors in the package 
			if (((++GCIndex) % 50) == 0 || bIsDirty || Package->ContainsMap())
			{
				// collect garbage to close the package
				CollectGarbage(RF_Native);
				UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("GC..."));
				// reset our counter
				GCIndex = 0;
			}
		}

		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(""));
	}
	else
	{
		SET_WARN_COLOR(COLOR_WHITE);
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("----------------------------"));
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Not deleting any redirector objects:"));
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("----------------------------"));
		UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(""));
		CLEAR_WARN_COLOR();
	}

	/////////////////////////////////////////////////////////////////////
	// Clean up any unused trash
	/////////////////////////////////////////////////////////////////////

	SET_WARN_COLOR(COLOR_WHITE);
	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Deleting empty and unused packages:"));
	UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("---------------------------------------"));
	CLEAR_WARN_COLOR();

	LogOutputFile->Logf(TEXT(""));
	LogOutputFile->Logf(TEXT("[Deleted]"));

	// Iterate over packages, attempting to delete unreferenced packages
	for( int32 PackageIndex = 0; PackageIndex < PackageList.Num(); PackageIndex++ )
	{
		bool bDelete = false;
		const FString& Filename = PackageList[PackageIndex];
		
		FString PackageName(FPackageName::FilenameToLongPackageName(Filename));

		const bool bAllowDelete = bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() );
		if (bDeleteRedirects && !bDeleteOnlyReferencedRedirects && EmptyPackagesToDelete[PackageIndex] && bAllowDelete)
		{
			// this package is completely empty, delete it
			bDelete = true;
		}

		if (bDelete == true)
		{
			struct Local
			{
				static bool DeleteFromDisk(const FString& InFilename, const FString& InMessage)
				{
					SET_WARN_COLOR(COLOR_GREEN);
					UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("%s"), *InMessage);
					if (!IFileManager::Get().Delete(*InFilename, false, true))
					{
						SET_WARN_COLOR(COLOR_RED);
						UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("  ... failed to delete from disk."), *InFilename);
						CLEAR_WARN_COLOR();
						return false;
					}
					CLEAR_WARN_COLOR();
					return true;
				}
			};

			if (!bIsSCCDisabled)
			{
				// get file SCC status
				FString FileName = SourceControlHelpers::PackageFilename(PackageName);
				FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(FileName, EStateCacheUsage::ForceUpdate);

				if(SourceControlState.IsValid() && (SourceControlState->IsCheckedOut() || SourceControlState->IsAdded()) )
				{
					UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Revert '%s' from source control..."), *Filename);
					SourceControlProvider.Execute(ISourceControlOperation::Create<FRevert>(), FileName);

					SET_WARN_COLOR(COLOR_GREEN);
					UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Deleting '%s' from source control..."), *Filename);
					CLEAR_WARN_COLOR();
					SourceControlProvider.Execute(ISourceControlOperation::Create<FDelete>(), FileName);
				}
				else if(SourceControlState.IsValid() && SourceControlState->CanCheckout())
				{
					SET_WARN_COLOR(COLOR_GREEN);
					UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Deleting '%s' from source control..."), *Filename);
					CLEAR_WARN_COLOR();
					SourceControlProvider.Execute(ISourceControlOperation::Create<FDelete>(), FileName);
				}
				else if(SourceControlState.IsValid() && SourceControlState->IsCheckedOutOther())
				{
					SET_WARN_COLOR(COLOR_RED);
					UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Couldn't delete '%s' from source control, someone has it checked out, skipping..."), *Filename);
					CLEAR_WARN_COLOR();
				}
				else if(SourceControlState.IsValid() && !SourceControlState->IsSourceControlled())
				{
					if(Local::DeleteFromDisk(Filename, FString::Printf(TEXT("'%s' is not in source control, attempting to delete from disk..."), *Filename)))
					{
						LogOutputFile->Logf(*Filename);
					}
				}
				else
				{
					if(Local::DeleteFromDisk(Filename, FString::Printf(TEXT("'%s' is in an unknown source control state, attempting to delete from disk..."), *Filename)))
					{
						LogOutputFile->Logf(*Filename);
					}
				}
			}
			else
			{
				if(Local::DeleteFromDisk(Filename, FString::Printf(TEXT("source control disabled while deleting '%s', attempting to delete from disk..."), *Filename)))
				{
					LogOutputFile->Logf(*Filename);
				}
			}
		}
	}
	LogOutputFile->Close();
	delete LogOutputFile;
	LogOutputFile = NULL;

	if (!bIsSCCDisabled && bAutoSubmit)
	{
		/////////////////////////////////////////////////////////////////////
		// Submit the results to source control
		/////////////////////////////////////////////////////////////////////
		UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Submiting the results to source control"));

		// Work out the list of file to check in
		TArray <FString> FilesToSubmit;

		for( int32 PackageIndex = 0; PackageIndex < PackageList.Num(); PackageIndex++ )
		{
			const FString& Filename = PackageList[PackageIndex];

			FString PackageName(FPackageName::FilenameToLongPackageName(Filename));
			FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(Filename), EStateCacheUsage::ForceUpdate);

			if( SourceControlState.IsValid() && (SourceControlState->IsCheckedOut() || SourceControlState->IsAdded() || SourceControlState->IsDeleted()) )
			{
				// Only submit engine packages if we're requested to
				if( bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() ) )
				{
					FilesToSubmit.Add(*PackageName);
				}
			}
		}

		// Check in all changed files
		const FText Description = NSLOCTEXT("FixupRedirectsCmdlet", "ChangelistDescription", "Fixed up Redirects");
		TSharedRef<FCheckIn, ESPMode::ThreadSafe> CheckInOperation = ISourceControlOperation::Create<FCheckIn>();
		CheckInOperation->SetDescription( Description );
		SourceControlProvider.Execute(CheckInOperation, SourceControlHelpers::PackageFilenames(FilesToSubmit));

		// toss the SCC manager
		ISourceControlModule::Get().GetProvider().Close();
	}
	return 0;
}
bool FBuildPatchFileConstructor::ConstructFileFromChunks( const FString& Filename, bool bResumeExisting )
{
	const bool bIsFileData = BuildManifest->IsFileDataManifest();
	bResumeExisting = bResumeExisting && !bIsFileData;
	bool bSuccess = true;
	FString ErrorString;
	FString NewFilename = StagingDirectory / Filename;

	// Calculate the hash as we write the data
	FSHA1 HashState;
	FSHAHashData HashValue;

	// First make sure we can get the file manifest
	const FFileManifestData* FileManifest = BuildManifest->GetFileManifest(Filename);
	bSuccess = FileManifest != nullptr;
	if( bSuccess )
	{
		if( !FileManifest->SymlinkTarget.IsEmpty() )
		{
#if PLATFORM_MAC
			bSuccess = symlink(TCHAR_TO_UTF8(*FileManifest->SymlinkTarget), TCHAR_TO_UTF8(*NewFilename)) == 0;
#else
			const bool bSymlinkNotImplemented = false;
			check(bSymlinkNotImplemented);
			bSuccess = false;
#endif
			return bSuccess;
		}

		// Check for resuming of existing file
		int64 StartPosition = 0;
		int32 StartChunkPart = 0;
		if( bResumeExisting )
		{
			// We have to read in the existing file so that the hash check can still be done.
			FArchive* NewFileReader = IFileManager::Get().CreateFileReader( *NewFilename );
			if( NewFileReader != NULL )
			{
				// Read buffer
				uint8* ReadBuffer = new uint8[ FBuildPatchData::ChunkDataSize ];
				// Reuse a certain amount of the file
				StartPosition = FMath::Max<int64>( 0, NewFileReader->TotalSize() - NUM_BYTES_RESUME_IGNORE );
				// We'll also find the correct chunkpart to start writing from
				int64 ByteCounter = 0;
				for( int32 ChunkPartIdx = StartChunkPart; ChunkPartIdx < FileManifest->FileChunkParts.Num() && !FBuildPatchInstallError::HasFatalError(); ++ChunkPartIdx )
				{
					const FChunkPartData& ChunkPart = FileManifest->FileChunkParts[ ChunkPartIdx ];
					const int64 NextBytePosition = ByteCounter + ChunkPart.Size;
					if( NextBytePosition <= StartPosition )
					{
						// Read data for hash check
						NewFileReader->Serialize( ReadBuffer, ChunkPart.Size );
						HashState.Update( ReadBuffer, ChunkPart.Size );
						// Count bytes read from file
						ByteCounter = NextBytePosition;
						// Set to resume from next chunk part
						StartChunkPart = ChunkPartIdx + 1;
						// Inform the chunk cache of the chunk part skip
						FBuildPatchChunkCache::Get().SkipChunkPart( ChunkPart );
						// Wait if paused
						BuildProgress->WaitWhilePaused();
					}
					else
					{
						// No more parts on disk
						break;
					}
				}
				// Set start position to the byte we got up to
				StartPosition = ByteCounter;
				// Clean read buffer
				delete[] ReadBuffer;
				// Close file
				NewFileReader->Close();
				delete NewFileReader;
			}
		}

		// Now we can make sure the chunk cache knows to start downloading chunks
		if( !bIsFileData && !bIsDownloadStarted && !FBuildPatchInstallError::HasFatalError() )
		{
			bIsDownloadStarted = true;
			FBuildPatchChunkCache::Get().BeginDownloads();
		}

		// Attempt to create the file
		FArchive* NewFile = IFileManager::Get().CreateFileWriter( *NewFilename, bResumeExisting ? EFileWrite::FILEWRITE_Append : 0 );
		bSuccess = NewFile != NULL;
		if( bSuccess )
		{
			// Whenever we start writing again, there's no more resuming to be done
			BuildProgress->SetStateProgress( EBuildPatchProgress::Resuming, 1.0f );

			// Seek to file write position
			NewFile->Seek( StartPosition );

			// For each chunk, load it, and place it's data into the file
			for( int32 ChunkPartIdx = StartChunkPart; ChunkPartIdx < FileManifest->FileChunkParts.Num() && bSuccess && !FBuildPatchInstallError::HasFatalError(); ++ChunkPartIdx )
			{
				const FChunkPartData& ChunkPart = FileManifest->FileChunkParts[ChunkPartIdx];
				if( bIsFileData )
				{
					bSuccess = InsertFileData( ChunkPart, *NewFile, HashState );
				}
				else
				{
					bSuccess = InsertChunkData( ChunkPart, *NewFile, HashState );
				}
				if( bSuccess )
				{
					CountBytesProcessed( ChunkPart.Size );
					// Wait if paused
					BuildProgress->WaitWhilePaused();
				}
				else
				{
					ErrorString = TEXT( "Failed to construct file " );
					ErrorString += Filename;
					ErrorString += TEXT( " because of chunk " );
					ErrorString += ChunkPart.Guid.ToString();
					GWarn->Logf( TEXT( "BuildPatchFileConstructor: ERROR: %s" ), *ErrorString );
					FBuildPatchInstallError::SetFatalError( EBuildPatchInstallError::FileConstructionFail, ErrorString );
				}
			}

			// Close the file writer
			NewFile->Close();
			delete NewFile;
		}
		else
		{
			FBuildPatchAnalytics::RecordConstructionError( Filename, FPlatformMisc::GetLastError(), TEXT( "Could Not Create File" ) );
			ErrorString = TEXT( "Could not create new file " );
			ErrorString += Filename;
			GWarn->Logf( TEXT( "BuildPatchFileConstructor: ERROR: %s" ), *ErrorString );
			FBuildPatchInstallError::SetFatalError( EBuildPatchInstallError::FileConstructionFail, ErrorString );
		}
	}
	else
	{
		FBuildPatchAnalytics::RecordConstructionError( Filename, INDEX_NONE, TEXT( "Missing File Manifest" ) );
		ErrorString = TEXT( "Build manifest does not contain a file manifest for " );
		ErrorString += Filename;
		FBuildPatchInstallError::SetFatalError( EBuildPatchInstallError::FileConstructionFail, ErrorString );
	}

	// Verify the hash for the file that we created
	if( bSuccess )
	{
		HashState.Final();
		HashState.GetHash( HashValue.Hash );
		bSuccess = HashValue == FileManifest->FileHash;
		if( !bSuccess )
		{
			FBuildPatchAnalytics::RecordConstructionError( Filename, INDEX_NONE, TEXT( "Serialised Verify Fail" ) );
			ErrorString = TEXT( "Verify failed after constructing file " );
			ErrorString += Filename;
			GWarn->Logf( TEXT( "BuildDataGenerator: ERROR: %s" ), *ErrorString );
			FBuildPatchInstallError::SetFatalError( EBuildPatchInstallError::FileConstructionFail, ErrorString );
		}
	}

#if PLATFORM_MAC
	if( bSuccess && FileManifest->bIsUnixExecutable )
	{
		// Enable executable permission bit
		struct stat FileInfo;
		if (stat(TCHAR_TO_UTF8(*NewFilename), &FileInfo) == 0)
		{
			bSuccess = chmod(TCHAR_TO_UTF8(*NewFilename), FileInfo.st_mode | S_IXUSR | S_IXGRP | S_IXOTH) == 0;
		}
	}
#endif
	
	// Delete the staging file if unsuccessful by means of construction fail (i.e. keep if canceled or download issue)
	if( !bSuccess && FBuildPatchInstallError::GetErrorState() == EBuildPatchInstallError::FileConstructionFail )
	{
		IFileManager::Get().Delete( *NewFilename, false, true );
	}

	return bSuccess;
}
예제 #21
0
const bool FChunkWriter::FQueuedChunkWriter::WriteChunkData(const FString& ChunkFilename, FChunkFile* ChunkFile, const FGuid& ChunkGuid)
{
	// Chunks are saved with GUID, so if a file already exists it will never be different.
	// Skip with return true if already exists
	if( FPaths::FileExists( ChunkFilename ) )
	{
		const int64 ChunkFilesSize = IFileManager::Get().FileSize(*ChunkFilename);
		ChunkFileSizesCS.Lock();
		ChunkFileSizes.Add(ChunkGuid, ChunkFilesSize);
		ChunkFileSizesCS.Unlock();
		return true;
	}
	FArchive* FileOut = IFileManager::Get().CreateFileWriter( *ChunkFilename );
	bool bSuccess = FileOut != NULL;
	if( bSuccess )
	{
		// Setup to handle compression
		bool bDataIsCompressed = true;
		uint8* ChunkDataSource = ChunkFile->ChunkData;
		int32 ChunkDataSourceSize = FBuildPatchData::ChunkDataSize;
		TArray< uint8 > TempCompressedData;
		TempCompressedData.Empty( ChunkDataSourceSize );
		TempCompressedData.AddUninitialized( ChunkDataSourceSize );
		int32 CompressedSize = ChunkDataSourceSize;

		// Compressed can increase in size, but the function will return as failure in that case
		// we can allow that to happen since we would not keep larger compressed data anyway.
		bDataIsCompressed = FCompression::CompressMemory(
			static_cast< ECompressionFlags >( COMPRESS_ZLIB | COMPRESS_BiasMemory ),
			TempCompressedData.GetData(),
			CompressedSize,
			ChunkFile->ChunkData,
			FBuildPatchData::ChunkDataSize );

		// If compression succeeded, set data vars
		if( bDataIsCompressed )
		{
			ChunkDataSource = TempCompressedData.GetData();
			ChunkDataSourceSize = CompressedSize;
		}

		// Setup Header
		FChunkHeader& Header = ChunkFile->ChunkHeader;
		*FileOut << Header;
		Header.HeaderSize = FileOut->Tell();
		Header.StoredAs = bDataIsCompressed ? FChunkHeader::STORED_COMPRESSED : FChunkHeader::STORED_RAW;
		Header.DataSize = ChunkDataSourceSize;
		Header.HashType = FChunkHeader::HASH_ROLLING;

		// Write out files
		FileOut->Seek( 0 );
		*FileOut << Header;
		FileOut->Serialize( ChunkDataSource, ChunkDataSourceSize );
		const int64 ChunkFilesSize = FileOut->TotalSize();
		FileOut->Close();

		ChunkFileSizesCS.Lock();
		ChunkFileSizes.Add(ChunkGuid, ChunkFilesSize);
		ChunkFileSizesCS.Unlock();

		bSuccess = !FileOut->GetError();

		delete FileOut;
	}
	// Log errors
	if( !bSuccess )
	{
		GLog->Logf( TEXT( "BuildPatchServices: Error: Could not save out generated chunk file %s" ), *ChunkFilename );
	}
	return bSuccess;
}
bool FBuildPatchInstaller::RunBackupAndMove()
{
    GLog->Logf(TEXT("BuildPatchServices: Running backup and stage relocation"));
    // If there's no error, move all complete files
    bool bMoveSuccess = FBuildPatchInstallError::HasFatalError() == false;
    if (bMoveSuccess)
    {
        // First handle files that should be removed for patching
        TArray< FString > FilesToRemove;
        if (CurrentBuildManifest.IsValid())
        {
            FBuildPatchAppManifest::GetRemovableFiles(CurrentBuildManifest.ToSharedRef(), NewBuildManifest, FilesToRemove);
        }
        // Add to build stats
        ThreadLock.Lock();
        BuildStats.NumFilesToRemove = FilesToRemove.Num();
        ThreadLock.Unlock();
        for (const FString& OldFilename : FilesToRemove)
        {
            BackupFileIfNecessary(OldFilename);
            const bool bDeleteSuccess = IFileManager::Get().Delete(*(InstallDirectory / OldFilename), false, true, true);
            const uint32 LastError = FPlatformMisc::GetLastError();
            GLog->Logf(TEXT("BuildPatchServices: Removed (%u,%u) %s"), bDeleteSuccess ? 1 : 0, LastError, *OldFilename);
        }

        // Now handle files that have been constructed
        bool bSavedMoveMarkerFile = false;
        TArray< FString > ConstructionFiles;
        NewBuildManifest->GetFileList(ConstructionFiles);
        BuildProgress.SetStateProgress(EBuildPatchProgress::MovingToInstall, 0.0f);
        const float NumConstructionFilesFloat = ConstructionFiles.Num();
        for (auto ConstructionFilesIt = ConstructionFiles.CreateConstIterator(); ConstructionFilesIt && bMoveSuccess && !FBuildPatchInstallError::HasFatalError(); ++ConstructionFilesIt)
        {
            const FString& ConstructionFile = *ConstructionFilesIt;
            const FString SrcFilename = InstallStagingDir / ConstructionFile;
            const FString DestFilename = InstallDirectory / ConstructionFile;
            const float FileIndexFloat = ConstructionFilesIt.GetIndex();
            // Skip files not constructed
            if (!FPaths::FileExists(SrcFilename))
            {
                BuildProgress.SetStateProgress(EBuildPatchProgress::MovingToInstall, FileIndexFloat / NumConstructionFilesFloat);
                continue;
            }
            // Create the move marker file
            if (!bSavedMoveMarkerFile)
            {
                bSavedMoveMarkerFile = true;
                GLog->Logf(TEXT("BuildPatchServices: Create MM"));
                FArchive* MoveMarkerFile = IFileManager::Get().CreateFileWriter(*PreviousMoveMarker, FILEWRITE_EvenIfReadOnly);
                if (MoveMarkerFile != NULL)
                {
                    MoveMarkerFile->Close();
                    delete MoveMarkerFile;
                }
                // Make sure we have some progress if we do some work
                if (BuildProgress.GetStateWeight(EBuildPatchProgress::MovingToInstall) <= 0.0f)
                {
                    BuildProgress.SetStateWeight(EBuildPatchProgress::MovingToInstall, 0.1f);
                }
            }
            // Backup file if need be
            BackupFileIfNecessary(ConstructionFile);
            // Move the file to the installation directory
            int32 MoveRetries = NUM_FILE_MOVE_RETRIES;
            bMoveSuccess = IFileManager::Get().Move(*DestFilename, *SrcFilename, true, true);
            uint32 ErrorCode = FPlatformMisc::GetLastError();
            while (!bMoveSuccess && MoveRetries > 0)
            {
                --MoveRetries;
                FBuildPatchAnalytics::RecordConstructionError(ConstructionFile, ErrorCode, TEXT("Failed To Move"));
                GWarn->Logf(TEXT("BuildPatchServices: ERROR: Failed to move file %s (%d), trying copy"), *ConstructionFile, ErrorCode);
                bMoveSuccess = IFileManager::Get().Copy(*DestFilename, *SrcFilename, true, true) == COPY_OK;
                ErrorCode = FPlatformMisc::GetLastError();
                if (!bMoveSuccess)
                {
                    GWarn->Logf(TEXT("BuildPatchServices: ERROR: Failed to copy file %s (%d), retying after 0.5 sec"), *ConstructionFile, ErrorCode);
                    FPlatformProcess::Sleep(0.5f);
                    --MoveRetries;
                    bMoveSuccess = IFileManager::Get().Move(*DestFilename, *SrcFilename, true, true);
                    ErrorCode = FPlatformMisc::GetLastError();
                }
                else
                {
                    IFileManager::Get().Delete(*SrcFilename, false, true, false);
                }
            }
            if (!bMoveSuccess)
            {
                GWarn->Logf(TEXT("BuildPatchServices: ERROR: Failed to move file %s"), *FPaths::GetCleanFilename(ConstructionFile));
                FBuildPatchInstallError::SetFatalError(EBuildPatchInstallError::MoveFileToInstall);
            }
            else
            {
                FilesInstalled.Add(ConstructionFile);
                BuildProgress.SetStateProgress(EBuildPatchProgress::MovingToInstall, FileIndexFloat / NumConstructionFilesFloat);
            }
        }

        bMoveSuccess = bMoveSuccess && (FBuildPatchInstallError::HasFatalError() == false);
        if (bMoveSuccess)
        {
            BuildProgress.SetStateProgress(EBuildPatchProgress::MovingToInstall, 1.0f);
        }
    }
    GLog->Logf(TEXT("BuildPatchServices: Relocation complete %d"), bMoveSuccess?1:0);
    return bMoveSuccess;
}
/** 
 * Mimics Windows WER format
 */
void GenerateWindowsErrorReport(const FString & WERPath, bool bReportingNonCrash)
{
	FArchive* ReportFile = IFileManager::Get().CreateFileWriter(*WERPath);
	if (ReportFile != NULL)
	{
		// write BOM
		static uint16 ByteOrderMarker = 0xFEFF;
		ReportFile->Serialize(&ByteOrderMarker, sizeof(ByteOrderMarker));

		WriteLine(ReportFile, TEXT("<?xml version=\"1.0\" encoding=\"UTF-16\"?>"));
		WriteLine(ReportFile, TEXT("<WERReportMetadata>"));
		
		WriteLine(ReportFile, TEXT("\t<OSVersionInformation>"));
		WriteLine(ReportFile, TEXT("\t\t<WindowsNTVersion>0.0</WindowsNTVersion>"));
		WriteLine(ReportFile, TEXT("\t\t<Build>No Build</Build>"));
		WriteLine(ReportFile, TEXT("\t\t<Product>Linux</Product>"));
		WriteLine(ReportFile, TEXT("\t\t<Edition>No Edition</Edition>"));
		WriteLine(ReportFile, TEXT("\t\t<BuildString>No BuildString</BuildString>"));
		WriteLine(ReportFile, TEXT("\t\t<Revision>0</Revision>"));
		WriteLine(ReportFile, TEXT("\t\t<Flavor>No Flavor</Flavor>"));
		WriteLine(ReportFile, TEXT("\t\t<Architecture>Unknown Architecture</Architecture>"));
		WriteLine(ReportFile, TEXT("\t\t<LCID>0</LCID>"));
		WriteLine(ReportFile, TEXT("\t</OSVersionInformation>"));
		
		WriteLine(ReportFile, TEXT("\t<ParentProcessInformation>"));
		WriteLine(ReportFile, *FString::Printf(TEXT("\t\t<ParentProcessId>%d</ParentProcessId>"), getppid()));
		WriteLine(ReportFile, TEXT("\t\t<ParentProcessPath>C:\\Windows\\explorer.exe</ParentProcessPath>"));			// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<ParentProcessCmdLine>C:\\Windows\\Explorer.EXE</ParentProcessCmdLine>"));	// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t</ParentProcessInformation>"));

		WriteLine(ReportFile, TEXT("\t<ProblemSignatures>"));
		WriteLine(ReportFile, TEXT("\t\t<EventType>APPCRASH</EventType>"));
		WriteLine(ReportFile, *FString::Printf(TEXT("\t\t<Parameter0>UE4-%s</Parameter0>"), FApp::GetGameName()));
		WriteLine(ReportFile, *FString::Printf(TEXT("\t\t<Parameter1>%d.%d.%d</Parameter1>"), FEngineVersion::Current().GetMajor(), FEngineVersion::Current().GetMinor(), FEngineVersion::Current().GetPatch()));
		WriteLine(ReportFile, TEXT("\t\t<Parameter2>0</Parameter2>"));													// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<Parameter3>Unknown Fault Module</Parameter3>"));										// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<Parameter4>0.0.0.0</Parameter4>"));													// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<Parameter5>00000000</Parameter5>"));													// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<Parameter6>00000000</Parameter6>"));													// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<Parameter7>0000000000000000</Parameter7>"));											// FIXME: supply valid?
		WriteLine(ReportFile, *FString::Printf(TEXT("\t\t<Parameter8>!%s!</Parameter8>"), FCommandLine::Get()));				// FIXME: supply valid? Only partially valid
		WriteLine(ReportFile, *FString::Printf(TEXT("\t\t<Parameter9>%s!%s!%s!%d</Parameter9>"), *FApp::GetBranchName(), FPlatformProcess::BaseDir(), FPlatformMisc::GetEngineMode(), FEngineVersion::Current().GetChangelist()));
		WriteLine(ReportFile, TEXT("\t</ProblemSignatures>"));

		WriteLine(ReportFile, TEXT("\t<DynamicSignatures>"));
		WriteLine(ReportFile, TEXT("\t\t<Parameter1>6.1.7601.2.1.0.256.48</Parameter1>"));
		WriteLine(ReportFile, TEXT("\t\t<Parameter2>1033</Parameter2>"));
		WriteLine(ReportFile, *FString::Printf(TEXT("\t\t<DeploymentName>%s</DeploymentName>"), FApp::GetDeploymentName()));
		WriteLine(ReportFile, *FString::Printf(TEXT("\t\t<IsEnsure>%s</IsEnsure>"), bReportingNonCrash ? TEXT("1") : TEXT("0")));
		WriteLine(ReportFile, TEXT("\t</DynamicSignatures>"));

		WriteLine(ReportFile, TEXT("\t<SystemInformation>"));
		WriteLine(ReportFile, TEXT("\t\t<MID>11111111-2222-3333-4444-555555555555</MID>"));							// FIXME: supply valid?
		
		WriteLine(ReportFile, TEXT("\t\t<SystemManufacturer>Unknown.</SystemManufacturer>"));						// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<SystemProductName>Linux machine</SystemProductName>"));					// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<BIOSVersion>A02</BIOSVersion>"));											// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t</SystemInformation>"));

		WriteLine(ReportFile, TEXT("</WERReportMetadata>"));

		ReportFile->Close();
		delete ReportFile;
	}
}
void FBuildPatchChunkCache::ReserveChunkInventorySlotForce( const FGuid& ChunkGuid )
{
	// If already reserved, return immediate
	if( ChunkCache.HasReservation( ChunkGuid ) || ChunkCache.Contains( ChunkGuid ) )
	{
		return;
	}

	// Begin by checking if any slots can be freed
	ChunkCache.PurgeUnreferenced();

	// Try to add the reservation
	bool bReservationAccepted = ChunkCache.TryAddReservation( ChunkGuid );

	// If we couldn't reserve, we need to boot out a chunk for this required one
	if( bReservationAccepted == false )
	{
		// We create a unique ref array from the use order so that chunks not needed
		// for longer times end up nearer the bottom of the array
		TArray< FGuid > ChunkPriorityList;
		ChunkInfoLock.Lock();
		for( int32 ChunkUseOrderStackIdx = ChunkUseOrderStack.Num() - 1; ChunkUseOrderStackIdx >= 0 ; --ChunkUseOrderStackIdx )
		{
			ChunkPriorityList.AddUnique( ChunkUseOrderStack[ ChunkUseOrderStackIdx ] );
		}
		ChunkInfoLock.Unlock();

		// Starting at the bottom of the list, we look for a chunk that is contained in the cache
		for( int32 ChunkPriorityListIdx = ChunkPriorityList.Num() - 1; ChunkPriorityListIdx >= 0 && !bReservationAccepted; --ChunkPriorityListIdx )
		{
			const FGuid& LowPriChunk = ChunkPriorityList[ ChunkPriorityListIdx ];
			BuildProgress->WaitWhilePaused();
			// Check if there were any errors while paused, like canceling
			if( FBuildPatchInstallError::HasFatalError() )
			{
				return;
			}
			if( ChunkCache.Contains( LowPriChunk ) )
			{
				GWarn->Logf( TEXT( "FBuildPatchChunkCache: Booting chunk %s" ), *LowPriChunk.ToString() );

				// Save chunk to disk so we don't have to download again
				bool bSuccess = true;
				const FString NewChunkFilename = FBuildPatchUtils::GetChunkOldFilename( ChunkCacheStage, LowPriChunk );
				FChunkFile* LowPriChunkFile = ChunkCache.Get( LowPriChunk );
				FChunkHeader* LowPriChunkHeader;
				uint8* LowPriChunkData;
				LowPriChunkFile->GetDataLock( &LowPriChunkData, &LowPriChunkHeader );
				FArchive* FileOut = IFileManager::Get().CreateFileWriter( *NewChunkFilename );
				bSuccess = FileOut != NULL;
				const int32 LastError = FPlatformMisc::GetLastError();
				if( bSuccess )
				{
					// Setup Header
					*FileOut << *LowPriChunkHeader;
					LowPriChunkHeader->HeaderSize = FileOut->Tell();
					LowPriChunkHeader->StoredAs = FChunkHeader::STORED_RAW;
					LowPriChunkHeader->DataSize = FBuildPatchData::ChunkDataSize; // This would change if compressing/encrypting

					// Write out file
					FileOut->Seek( 0 );
					*FileOut << *LowPriChunkHeader;
					FileOut->Serialize( LowPriChunkData, FBuildPatchData::ChunkDataSize );
					FileOut->Close();

					delete FileOut;
				}
				LowPriChunkFile->ReleaseDataLock();

				// Setup new chunk origin
				if( bSuccess )
				{
					ChunkOrigins[ LowPriChunk ] = EChunkOrigin::Harddisk;
				}
				else
				{
					// Queue download if save failed
					ChunkOrigins[ LowPriChunk ] = EChunkOrigin::Download;
					FBuildPatchDownloader::Get().AddChunkToDownload( LowPriChunk );
					FBuildPatchAnalytics::RecordChunkCacheError( ChunkGuid, NewChunkFilename, LastError, TEXT( "ChunkBooting" ), TEXT( "Chunk Save Failed" ) );
				}

				// Boot this chunk
				ChunkCache.Remove( LowPriChunk );

				// Try get the reservation again!
				bReservationAccepted = ChunkCache.TryAddReservation( ChunkGuid );

				// Count the boot
				NumChunksCacheBooted.Increment();
			}
		}

		// We must have been able to make room
		check( bReservationAccepted );
	}
}
예제 #25
0
// cooked package asset registry saves information about all the cooked packages and assets contained within for stats purposes
// in json format
bool FChunkManifestGenerator::SaveCookedPackageAssetRegistry( const FString& SandboxCookedRegistryFilename, const bool Append )
{
	bool bSuccess = false;
	for ( const auto& Platform : Platforms )
	{
		TSet<FName> CookedPackages;

		// save the file 
		const FString CookedAssetRegistryFilename = SandboxCookedRegistryFilename.Replace(TEXT("[Platform]"), *Platform->PlatformName());

		FString JsonOutString;
		JsonWriter Json = TJsonWriterFactory<TCHAR, TPrettyJsonPrintPolicy<TCHAR> >::Create(&JsonOutString);

		Json->WriteObjectStart();
		Json->WriteArrayStart(TEXT("Packages"));

		for ( const auto& Package : AllCookedPackages )
		{
			Json->WriteObjectStart(); // unnamed package start
			const FName& PackageName = Package.Key;
			const FString& SandboxPath = Package.Value;

			CookedPackages.Add( PackageName );

			FString PlatformSandboxPath = SandboxPath.Replace(TEXT("[Platform]"), *Platform->PlatformName());
			
			FDateTime TimeStamp = IFileManager::Get().GetTimeStamp( *PlatformSandboxPath );

			Json->WriteValue( "SourcePackageName", PackageName.ToString() );
			Json->WriteValue( "CookedPackageName", PlatformSandboxPath );
			Json->WriteValue( "CookedPackageTimeStamp", TimeStamp.ToString() );
			
			Json->WriteArrayStart("AssetData");
			for (const auto& AssetData : AssetRegistryData)
			{	// Add only assets that have actually been cooked and belong to any chunk
				if (AssetData.ChunkIDs.Num() > 0 && (AssetData.PackageName == PackageName))
				{
					Json->WriteObjectStart();
					// save all their infos 
					Json->WriteValue(TEXT("ObjectPath"), AssetData.ObjectPath.ToString() );
					Json->WriteValue(TEXT("PackageName"), AssetData.PackageName.ToString() );
					Json->WriteValue(TEXT("PackagePath"), AssetData.PackagePath.ToString() );
					Json->WriteValue(TEXT("GroupNames"), AssetData.GroupNames.ToString() );
					Json->WriteValue(TEXT("AssetName"), AssetData.AssetName.ToString() );
					Json->WriteValue(TEXT("AssetClass"), AssetData.AssetClass.ToString() );
					Json->WriteObjectStart("TagsAndValues");
					for ( const auto& Tag : AssetData.TagsAndValues )
					{
						Json->WriteValue( Tag.Key.ToString(), Tag.Value );
					}
					Json->WriteObjectEnd(); // end tags and values object
					Json->WriteObjectEnd(); // end unnamed array object
				}
			}
			Json->WriteArrayEnd();
			Json->WriteObjectEnd(); // unnamed package
		}

		if ( Append )
		{
			FString JsonInString;
			if ( FFileHelper::LoadFileToString(JsonInString, *CookedAssetRegistryFilename) )
			{
				// load up previous package asset registry and fill in any packages which weren't recooked on this run
				JsonReader Reader = TJsonReaderFactory<TCHAR>::Create(JsonInString);
				TSharedPtr<FJsonObject> JsonObject;
				bool shouldRead = FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid() && JsonObject->HasTypedField<EJson::Array>(TEXT("Packages"));
				if ( shouldRead )
				{
					TArray<TSharedPtr<FJsonValue>> PackageList = JsonObject->GetArrayField(TEXT("Packages"));
					for (auto PackageListIt = PackageList.CreateConstIterator(); PackageListIt && shouldRead; ++PackageListIt)
					{
						const TSharedPtr<FJsonValue>& JsonValue = *PackageListIt;
						shouldRead = JsonValue->Type == EJson::Object;
						if ( shouldRead )
						{
							const TSharedPtr<FJsonObject>& JsonPackage = JsonValue->AsObject();

							// get the package name and see if we have already written it out this run
							
							FString CookedPackageName;
							verify( JsonPackage->TryGetStringField(TEXT("SourcePackageName"), CookedPackageName) );

							const FName CookedPackageFName(*CookedPackageName);
							if ( CookedPackages.Contains(CookedPackageFName))
							{
								// don't need to process this package
								continue;
							}


							// check that the on disk version is still valid
							FString SourcePackageName;
							check( JsonPackage->TryGetStringField( TEXT("SourcePackageName"), SourcePackageName) );

							// if our timestamp is different then don't copy the information over
							FDateTime CurrentTimeStamp = IFileManager::Get().GetTimeStamp( *CookedPackageName );

							FString SavedTimeString;
							check( JsonPackage->TryGetStringField(TEXT("CookedPackageTimeStamp"), SavedTimeString) );
							FDateTime SavedTimeStamp;
							FDateTime::Parse(SavedTimeString, SavedTimeStamp);

							if ( SavedTimeStamp != CurrentTimeStamp )
							{
								continue;
							}



							CopyJsonValueToWriter(Json, FString(), JsonValue);
							// read in all the other stuff and copy it over to the new registry
							/*Json->WriteObjectStart(); // open package

							// copy all the values over
							for ( const auto& JsonPackageValue : JsonPackage->Values)
							{
								CopyJsonValueToWriter(Json, JsonPackageValue.Key, JsonPackageValue.Value);
							}

							Json->WriteObjectEnd();*/
						}
						
					}
				}
				else
				{
					UE_LOG(LogChunkManifestGenerator, Warning, TEXT("Unable to read or json is invalid format %s"), *CookedAssetRegistryFilename);
				}
			}
		}


		Json->WriteArrayEnd();
		Json->WriteObjectEnd();

		if (Json->Close())
		{
			FArchive* ItemTemplatesFile = IFileManager::Get().CreateFileWriter(*CookedAssetRegistryFilename);
			if (ItemTemplatesFile)
			{
				// serialize the file contents
				TStringConversion<FTCHARToUTF8_Convert> Convert(*JsonOutString);
				ItemTemplatesFile->Serialize(const_cast<ANSICHAR*>(Convert.Get()), Convert.Length());
				ItemTemplatesFile->Close();
				if ( !ItemTemplatesFile->IsError() )
				{
					bSuccess = true;
				}
				else
				{
					UE_LOG(LogChunkManifestGenerator, Error, TEXT("Unable to write to %s"), *CookedAssetRegistryFilename);
				}
				delete ItemTemplatesFile;
			}
			else
			{
				UE_LOG(LogChunkManifestGenerator, Error, TEXT("Unable to open %s for writing."), *CookedAssetRegistryFilename);
			}
		}
		else
		{
			UE_LOG(LogChunkManifestGenerator, Error, TEXT("Error closing Json Writer"));
		}
	}
	return bSuccess;
}
bool FBuildPatchChunkCache::RecycleChunkFromBuild( const FGuid& ChunkGuid )
{
	// Must never double acquire
	check( ChunkCache.Contains( ChunkGuid ) == false );

	// Debug leaving any files open
	bool bSuccess = true;

	// Get the app manifest that this chunk can be sourced from
	FBuildPatchAppManifestPtr ChunkSourceAppManifest = InstallationInfo.GetManifestContainingChunk(ChunkGuid);
	if (!ChunkSourceAppManifest.IsValid())
	{
		return false;
	}

	// Get the install directory for this manifest
	const FString ChunkSourceInstallDir = InstallationInfo.GetManifestInstallDir(ChunkSourceAppManifest);
	if(ChunkSourceInstallDir.Len() <= 0)
	{
		return false;
	}

	// We need to generate an inventory of all chunk parts in this build that refer to the chunk that we require
	TMap< FGuid, TArray< FFileChunkPart > > ChunkPartInventory;
	TArray< FGuid > Array;
	Array.Add( ChunkGuid );
	ChunkSourceAppManifest->EnumerateChunkPartInventory(Array, ChunkPartInventory);

	// Attempt construction of the chunk from the parts
	FArchive* BuildFileIn = NULL;
	FString  BuildFileOpened;
	int64 BuildFileInSize = 0;

	// We must have a hash for this chunk or else we cant verify it
	uint8 HashType = 0;
	uint64 ChunkHash = 0;
	FSHAHashData ChunkShaHash;
	if (InstallManifet->GetChunkShaHash(ChunkGuid, ChunkShaHash))
	{
		HashType = FChunkHeader::HASH_SHA1;
	}
	else if (ChunkSourceAppManifest->GetChunkHash(ChunkGuid, ChunkHash))
	{
		HashType = FChunkHeader::HASH_ROLLING;
	}
	TArray< FFileChunkPart >* FileChunkPartsPtr = ChunkPartInventory.Find( ChunkGuid );
	bSuccess = (FileChunkPartsPtr != NULL && HashType != 0);
	if( bSuccess )
	{
		const TArray< FFileChunkPart >& FileChunkParts = *FileChunkPartsPtr;
		TArray< uint8 > TempArray;
		TempArray.AddUninitialized( FBuildPatchData::ChunkDataSize );
		uint8* TempChunkConstruction = TempArray.GetData();
		FMemory::Memzero( TempChunkConstruction, FBuildPatchData::ChunkDataSize );
		bSuccess = FileChunkParts.Num() > 0;
		for( auto FileChunkPartIt = FileChunkParts.CreateConstIterator(); FileChunkPartIt && bSuccess && !FBuildPatchInstallError::HasFatalError(); ++FileChunkPartIt )
		{
			const FFileChunkPart& FileChunkPart = *FileChunkPartIt;
			FString FullFilename = ChunkSourceInstallDir / FileChunkPart.Filename;
			// Close current build file ?
			if( BuildFileIn != NULL && BuildFileOpened != FullFilename )
			{
				BuildFileIn->Close();
				delete BuildFileIn;
				BuildFileIn = NULL;
				BuildFileOpened = TEXT( "" );
				BuildFileInSize = 0;
			}
			// Open build file ?
			if( BuildFileIn == NULL )
			{
				BuildFileIn = IFileManager::Get().CreateFileReader( *FullFilename );
				bSuccess = BuildFileIn != NULL;
				if( !bSuccess )
				{
					BuildFileOpened = TEXT( "" );
					FBuildPatchAnalytics::RecordChunkCacheError( ChunkGuid, FileChunkPart.Filename, FPlatformMisc::GetLastError(), TEXT( "ChunkRecycle" ), TEXT( "Source File Missing" ) );
					GWarn->Logf( TEXT( "BuildPatchChunkConstruction: Warning: Failed to load source file for chunk. %s" ), *FullFilename );
				}
				else
				{
					BuildFileOpened = FullFilename;
					BuildFileInSize = BuildFileIn->TotalSize();
				}
			}
			// Grab the section of the chunk
			if( BuildFileIn != NULL )
			{
				// Make sure we don't attempt to read off the end of the file
				const int64 LastRequiredByte = FileChunkPart.FileOffset + FileChunkPart.ChunkPart.Size;
				if( BuildFileInSize >= LastRequiredByte )
				{
					BuildFileIn->Seek( FileChunkPart.FileOffset );
					BuildFileIn->Serialize( TempChunkConstruction + FileChunkPart.ChunkPart.Offset, FileChunkPart.ChunkPart.Size );
				}
				else
				{
					bSuccess = false;
					FBuildPatchAnalytics::RecordChunkCacheError( ChunkGuid, FileChunkPart.Filename, INDEX_NONE, TEXT( "ChunkRecycle" ), TEXT( "Source File Too Small" ) );
					GWarn->Logf( TEXT( "BuildPatchChunkConstruction: Warning: Source file too small for chunk position. %s" ), *FullFilename );
				}
			}
		}

		// Check no other fatal errors were registered in the meantime
		bSuccess = bSuccess && !FBuildPatchInstallError::HasFatalError();

		// Check chunk hash
		if( bSuccess )
		{
			FSHAHashData ShaHashCheck;
			switch (HashType)
			{
			case FChunkHeader::HASH_ROLLING:
				bSuccess = FRollingHash< FBuildPatchData::ChunkDataSize >::GetHashForDataSet(TempChunkConstruction) == ChunkHash;
				break;
			case FChunkHeader::HASH_SHA1:
				FSHA1::HashBuffer(TempChunkConstruction, FBuildPatchData::ChunkDataSize, ShaHashCheck.Hash);
				bSuccess = ShaHashCheck == ChunkShaHash;
				break;
			default:
				bSuccess = false;
			}
			if( !bSuccess )
			{
				FBuildPatchAnalytics::RecordChunkCacheError( ChunkGuid, TEXT( "" ), INDEX_NONE, TEXT( "ChunkRecycle" ), TEXT( "Chunk Hash Fail" ) );
				GWarn->Logf( TEXT( "BuildPatchChunkConstruction: Warning: Hash check failed for recycled chunk %s" ), *ChunkGuid.ToString() );
			}
		}

		// Save the chunk to cache if all went well
		if( bSuccess )
		{
			// It was added asynchronously!!
			check( ChunkCache.Contains( ChunkGuid ) == false );

			// Create the ChunkFile data structure
			FChunkFile* NewChunkFile = new FChunkFile( GetRemainingReferenceCount( ChunkGuid ), true );

			// Lock data
			FChunkHeader* ChunkHeader;
			uint8* ChunkData;
			NewChunkFile->GetDataLock( &ChunkData, &ChunkHeader );

			// Copy the data
			FMemoryReader MemReader( TempArray );
			MemReader.Serialize( ChunkData, FBuildPatchData::ChunkDataSize );

			// Setup the header
			ChunkHeader->Guid = ChunkGuid;
			ChunkHeader->StoredAs = FChunkHeader::STORED_RAW;
			ChunkHeader->DataSize = FBuildPatchData::ChunkDataSize; // This would change if compressing/encrypting
			ChunkHeader->HashType = HashType;
			ChunkHeader->RollingHash = ChunkHash;
			ChunkHeader->SHAHash = ChunkShaHash;

			// Release data
			NewChunkFile->ReleaseDataLock();

			// Count chunk
			NumChunksRecycled.Increment();

			// Add it to our cache.
			ChunkCache.Add( ChunkGuid, NewChunkFile );
		}

		// Close any open file
		if( BuildFileIn != NULL )
		{
			BuildFileIn->Close();
			delete BuildFileIn;
			BuildFileIn = NULL;
		}
	}
	
	return bSuccess;
}
bool FBuildPatchChunkCache::ReadChunkFromDriveCache( const FGuid& ChunkGuid )
{
	bool bSuccess = true;

	// Get the chunk filename
	const FString Filename = FBuildPatchUtils::GetChunkOldFilename( ChunkCacheStage, ChunkGuid );

	// Read the chunk
	FArchive* FileReader = IFileManager::Get().CreateFileReader( *Filename );
	bSuccess = FileReader != NULL;
	if( bSuccess )
	{
		// Get file size
		const int64 FileSize = FileReader->TotalSize();

		// Create the ChunkFile data structure
		FChunkFile* NewChunkFile = new FChunkFile( GetRemainingReferenceCount( ChunkGuid ), true );

		// Lock data
		FChunkHeader* ChunkHeader;
		uint8* ChunkData;
		NewChunkFile->GetDataLock( &ChunkData, &ChunkHeader );

		// Read the header
		*FileReader << *ChunkHeader;

		// Check header magic
		bSuccess = ChunkHeader->IsValidMagic();
		if ( bSuccess )
		{
			// Check the right data size
			bSuccess = ChunkHeader->DataSize == FBuildPatchData::ChunkDataSize;
			if( bSuccess )
			{
				// Check Header and data size
				bSuccess = ( ChunkHeader->HeaderSize + ChunkHeader->DataSize ) == FileSize;
				if( bSuccess )
				{
					// Read the data
					FileReader->Serialize( ChunkData, FBuildPatchData::ChunkDataSize );
					// Verify the data hash
					FSHAHashData ShaHashCheck;
					switch (ChunkHeader->HashType)
					{
					case FChunkHeader::HASH_ROLLING:
						bSuccess = ChunkHeader->RollingHash == FRollingHash< FBuildPatchData::ChunkDataSize >::GetHashForDataSet(ChunkData);
						break;
					case FChunkHeader::HASH_SHA1:
						FSHA1::HashBuffer(ChunkData, FBuildPatchData::ChunkDataSize, ShaHashCheck.Hash);
						bSuccess = ShaHashCheck == ChunkHeader->SHAHash;
						break;
					default:
						bSuccess = false;
					}
					if( !bSuccess )
					{
						FBuildPatchAnalytics::RecordChunkCacheError( ChunkGuid, Filename, INDEX_NONE, TEXT( "DriveCache" ), TEXT( "Hash Check Failed" ) );
						GLog->Logf( TEXT( "FBuildPatchChunkCache: ERROR: ReadChunkFromDriveCache chunk failed hash check %s" ), *ChunkGuid.ToString() );
					}
					else
					{
						// Count loads
						NumDriveCacheChunkLoads.Increment();
						GLog->Logf( TEXT( "FBuildPatchChunkCache: ReadChunkFromDriveCache loaded chunk %s" ), *ChunkGuid.ToString() );
					}
				}
				else
				{
					FBuildPatchAnalytics::RecordChunkCacheError( ChunkGuid, Filename, INDEX_NONE, TEXT( "DriveCache" ), TEXT( "Incorrect File Size" ) );
					GLog->Logf( TEXT( "FBuildPatchChunkCache: ERROR: ReadChunkFromDriveCache header info does not match file size %s" ), *ChunkGuid.ToString() );
				}
			}
			else
			{
				FBuildPatchAnalytics::RecordChunkCacheError( ChunkGuid, Filename, INDEX_NONE, TEXT( "DriveCache" ), TEXT( "Datasize/Hashtype Mismatch" ) );
				GLog->Logf( TEXT( "FBuildPatchChunkCache: ERROR: ReadChunkFromDriveCache mismatch datasize/hashtype combination %s" ), *ChunkGuid.ToString() );
			}
		}
		else
		{
			FBuildPatchAnalytics::RecordChunkCacheError( ChunkGuid, Filename, INDEX_NONE, TEXT( "DriveCache" ), TEXT( "Corrupt Header" ) );
			GLog->Logf( TEXT( "FBuildPatchChunkCache: ERROR: ReadChunkFromDriveCache corrupt header %s" ), *ChunkGuid.ToString() );
		}

		// Release data
		NewChunkFile->ReleaseDataLock();

		// Add the newly filled data to the cache if successful
		if( bSuccess )
		{
			ChunkCache.Add( ChunkGuid, NewChunkFile );
		}
		// If there was a problem, remove from cache and reservation
		else
		{
			ChunkCache.Remove( ChunkGuid );
		}

		// Close the file
		FileReader->Close();
		delete FileReader;

	}
	else
	{
		FBuildPatchAnalytics::RecordChunkCacheError( ChunkGuid, Filename, FPlatformMisc::GetLastError(), TEXT( "DriveCache" ), TEXT( "Open File Fail" ) );
		GLog->Logf( TEXT( "BuildPatchServices: ERROR: GetChunkData could not open chunk file %s" ), *ChunkGuid.ToString() );
	}

	return bSuccess;
}
예제 #28
0
/** 
 * Mimics Windows WER format
 */
void GenerateWindowsErrorReport(const FString & WERPath)
{
	FArchive* ReportFile = IFileManager::Get().CreateFileWriter(*WERPath);
	if (ReportFile != NULL)
	{
		// write BOM
		static uint16 ByteOrderMarker = 0xFEFF;
		ReportFile->Serialize(&ByteOrderMarker, sizeof(ByteOrderMarker));

		WriteLine(ReportFile, TEXT("<?xml version=\"1.0\" encoding=\"UTF-16\"?>"));
		WriteLine(ReportFile, TEXT("<WERReportMetadata>"));
		
		WriteLine(ReportFile, TEXT("\t<OSVersionInformation>"));
		WriteLine(ReportFile, TEXT("\t\t<WindowsNTVersion>6.1</WindowsNTVersion>"));
		WriteLine(ReportFile, TEXT("\t\t<Build>7601 Service Pack 1</Build>"));
		WriteLine(ReportFile, TEXT("\t\t<Product>(0x30): Windows 7 Professional</Product>"));
		WriteLine(ReportFile, TEXT("\t\t<Edition>Professional</Edition>"));
		WriteLine(ReportFile, TEXT("\t\t<BuildString>7601.18044.amd64fre.win7sp1_gdr.130104-1431</BuildString>"));
		WriteLine(ReportFile, TEXT("\t\t<Revision>1130</Revision>"));
		WriteLine(ReportFile, TEXT("\t\t<Flavor>Multiprocessor Free</Flavor>"));
		WriteLine(ReportFile, TEXT("\t\t<Architecture>X64</Architecture>"));
		WriteLine(ReportFile, TEXT("\t\t<LCID>1033</LCID>"));
		WriteLine(ReportFile, TEXT("\t</OSVersionInformation>"));
		
		WriteLine(ReportFile, TEXT("\t<ParentProcessInformation>"));
		WriteLine(ReportFile, *FString::Printf(TEXT("\t\t<ParentProcessId>%d</ParentProcessId>"), getppid()));
		WriteLine(ReportFile, TEXT("\t\t<ParentProcessPath>C:\\Windows\\explorer.exe</ParentProcessPath>"));			// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<ParentProcessCmdLine>C:\\Windows\\Explorer.EXE</ParentProcessCmdLine>"));	// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t</ParentProcessInformation>"));

		WriteLine(ReportFile, TEXT("\t<ProblemSignatures>"));
		WriteLine(ReportFile, TEXT("\t\t<EventType>APPCRASH</EventType>"));
		WriteLine(ReportFile, *FString::Printf(TEXT("\t\t<Parameter0>UE4-%s</Parameter0>"), FApp::GetGameName()));
		WriteLine(ReportFile, *FString::Printf(TEXT("\t\t<Parameter1>1.0.%d.%d</Parameter1>"), ENGINE_VERSION_HIWORD, ENGINE_VERSION_LOWORD));
		WriteLine(ReportFile, TEXT("\t\t<Parameter2>528f2d37</Parameter2>"));													// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<Parameter3>KERNELBASE.dll</Parameter3>"));												// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<Parameter4>6.1.7601.18015</Parameter4>"));												// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<Parameter5>50b8479b</Parameter5>"));													// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<Parameter6>00000001</Parameter6>"));													// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<Parameter7>0000000000009E5D</Parameter7>"));											// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<Parameter8>!!</Parameter8>"));															// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<Parameter9>UE4!D:/ShadowArtSync/UE4/Engine/Binaries/Win64/!Editor!0</Parameter9>"));	// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t</ProblemSignatures>"));

		WriteLine(ReportFile, TEXT("\t<DynamicSignatures>"));
		WriteLine(ReportFile, TEXT("\t\t<Parameter1>6.1.7601.2.1.0.256.48</Parameter1>"));
		WriteLine(ReportFile, TEXT("\t\t<Parameter2>1033</Parameter2>"));
		WriteLine(ReportFile, TEXT("\t</DynamicSignatures>"));

		WriteLine(ReportFile, TEXT("\t<SystemInformation>"));
		WriteLine(ReportFile, TEXT("\t\t<MID>11111111-2222-3333-4444-555555555555</MID>"));							// FIXME: supply valid?
		
		WriteLine(ReportFile, TEXT("\t\t<SystemManufacturer>Unknown.</SystemManufacturer>"));						// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<SystemProductName>Linux machine</SystemProductName>"));					// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t\t<BIOSVersion>A02</BIOSVersion>"));											// FIXME: supply valid?
		WriteLine(ReportFile, TEXT("\t</SystemInformation>"));

		WriteLine(ReportFile, TEXT("</WERReportMetadata>"));

		ReportFile->Close();
		delete ReportFile;
	}
}
예제 #29
0
void FCrashInfo::GenerateReport( const FString& DiagnosticsPath )
{
	FArchive* ReportFile = IFileManager::Get().CreateFileWriter( *DiagnosticsPath );
	if( ReportFile != NULL )
	{
		FString Line;

		WriteLine( ReportFile, TCHAR_TO_UTF8( TEXT( "Generating report for minidump" ) ) );
		WriteLine( ReportFile );

		if ( EngineVersion.Len() > 0 )
		{
			Line = FString::Printf( TEXT( "Application version %s" ), *EngineVersion );
			WriteLine( ReportFile, TCHAR_TO_UTF8( *Line ) );
		}
		else if( Modules.Num() > 0 )
		{
			Line = FString::Printf( TEXT( "Application version %d.%d.%d" ), Modules[0].Major, Modules[0].Minor, Modules[0].Patch );
			WriteLine( ReportFile, TCHAR_TO_UTF8( *Line ) );
		}

		Line = FString::Printf( TEXT( " ... built from changelist %d" ), BuiltFromCL );
		WriteLine( ReportFile, TCHAR_TO_UTF8( *Line ) );
		if( LabelName.Len() > 0 )
		{
			Line = FString::Printf( TEXT( " ... based on label %s" ), *LabelName );
			WriteLine( ReportFile, TCHAR_TO_UTF8( *Line ) );
		}
		WriteLine( ReportFile );

		Line = FString::Printf( TEXT( "OS version %d.%d.%d.%d" ), SystemInfo.OSMajor, SystemInfo.OSMinor, SystemInfo.OSBuild, SystemInfo.OSRevision );
		WriteLine( ReportFile, TCHAR_TO_UTF8( *Line ) );

		Line = FString::Printf( TEXT( "Running %d %s processors" ), SystemInfo.ProcessorCount, GetProcessorArchitecture( SystemInfo.ProcessorArchitecture ) );
		WriteLine( ReportFile, TCHAR_TO_UTF8( *Line ) );

		Line = FString::Printf( TEXT( "Exception was \"%s\"" ), *Exception.ExceptionString );
		WriteLine( ReportFile, TCHAR_TO_UTF8( *Line ) );
		WriteLine( ReportFile );

		Line = FString::Printf( TEXT( "Source context from \"%s\"" ), *SourceFile );
		WriteLine( ReportFile, TCHAR_TO_UTF8( *Line ) );
		WriteLine( ReportFile );

		Line = FString::Printf( TEXT( "<SOURCE START>" ) );
		WriteLine( ReportFile, TCHAR_TO_UTF8( *Line ) );
		for( int32 LineIndex = 0; LineIndex < SourceContext.Num(); LineIndex++ )
		{
			Line = FString::Printf( TEXT( "%s" ), *SourceContext[LineIndex] );
			WriteLine( ReportFile, TCHAR_TO_UTF8( *Line ) );
		}
		Line = FString::Printf( TEXT( "<SOURCE END>" ) );
		WriteLine( ReportFile, TCHAR_TO_UTF8( *Line ) );
		WriteLine( ReportFile );

		Line = FString::Printf( TEXT( "<CALLSTACK START>" ) );
		WriteLine( ReportFile, TCHAR_TO_UTF8( *Line ) );

		for( int32 StackIndex = 0; StackIndex < Exception.CallStackString.Num(); StackIndex++ )
		{
			Line = FString::Printf( TEXT( "%s" ), *Exception.CallStackString[StackIndex] );
			WriteLine( ReportFile, TCHAR_TO_UTF8( *Line ) );
		}

		Line = FString::Printf( TEXT( "<CALLSTACK END>" ) );
		WriteLine( ReportFile, TCHAR_TO_UTF8( *Line ) );
		WriteLine( ReportFile );

		Line = FString::Printf( TEXT( "%d loaded modules" ), Modules.Num() );
		WriteLine( ReportFile, TCHAR_TO_UTF8( *Line ) );

		for( int32 ModuleIndex = 0; ModuleIndex < Modules.Num(); ModuleIndex++ )
		{
			FCrashModuleInfo& Module = Modules[ModuleIndex];

			FString ModuleDirectory = FPaths::GetPath(Module.Name);
			FString ModuleName = FPaths::GetBaseFilename( Module.Name, true ) + FPaths::GetExtension( Module.Name, true );

			FString ModuleDetail = FString::Printf( TEXT( "%40s" ), *ModuleName );
			FString Version = FString::Printf( TEXT( " (%d.%d.%d.%d)" ), Module.Major, Module.Minor, Module.Patch, Module.Revision );
			ModuleDetail += FString::Printf( TEXT( " %22s" ), *Version );
			ModuleDetail += FString::Printf( TEXT( " 0x%016x 0x%08x" ), Module.BaseOfImage, Module.SizeOfImage );
			ModuleDetail += FString::Printf( TEXT( " %s" ), *ModuleDirectory );

			WriteLine( ReportFile, TCHAR_TO_UTF8( *ModuleDetail ) );
		}

		WriteLine( ReportFile );

		// Write out the processor debugging log
		WriteLine( ReportFile, TCHAR_TO_UTF8( *Report ) );

		Line = FString::Printf( TEXT( "Report end!" ) );
		WriteLine( ReportFile, TCHAR_TO_UTF8( *Line )  );

		ReportFile->Close();
		delete ReportFile;
	}
}
예제 #30
0
void USetupDefinition::ProcessCopy( FString Key, FString Value, UBOOL Selected, FInstallPoll* Poll )
{
	guard(USetupDefinition::ProcessCopy);
	BYTE Buffer[4096];
	if( Selected && Key==TEXT("File") )
	{
		// Get source and dest filenames.
		FFileInfo Info(*Value);
		if( Info.Lang==TEXT("") || Info.Lang==UObject::GetLanguage() )
		{
			if( Info.Dest==TEXT("") )
				Info.Dest = Info.Src;
			if( !LocateSourceFile(Info.Src) )
				LocalizedFileError( TEXT("MissingInstallerFile"), Patch ? TEXT("AdviseBadDownload") : TEXT("AdviseBadMedia"), *Info.Src );
			FString FullDest  = DestPath * Info.Dest;
			FString FullSrc   = Info.Ref==TEXT("") ? Info.Src : GetFullRef(*Info.Ref);
			FString FullPatch = FullDest + TEXT("_tmp");

			// Update uninstallation log.
			UninstallLogAdd( TEXT("File"), *Info.Dest, 0, 1 );

			// Make destination directory.
			if( !GFileManager->MakeDirectory( *BasePath(FullDest), 1 ) )
				LocalizedFileError( TEXT("FailedMakeDir"), TEXT("AdviseBadDest"), *FullDest );

			// Status display.
			if( !Poll->Poll(*FullDest,0,0,RunningBytes,TotalBytes) )
				DidCancel();

			// Copy SrcAr -> DestAr.
			INT CalcOldCRC = 0;
			guard(CopyFile);
			FString ThisDest = Info.Ref==TEXT("") ? FullDest : FullPatch;
			debugf( TEXT("Copying %s to %s"), *FullSrc, *ThisDest);
			FArchive* SrcAr = GFileManager->CreateFileReader( *FullSrc );
			if( !SrcAr )
				LocalizedFileError( TEXT("FailedOpenSource"), Patch ? TEXT("AdviseBadDownload") : TEXT("AdviseBadMedia"), *FullSrc );
			INT Size = SrcAr->TotalSize();
			FArchive* DestAr = GFileManager->CreateFileWriter( *ThisDest, FILEWRITE_EvenIfReadOnly );
			if( !DestAr )
				LocalizedFileError( TEXT("FailedOpenDest"), TEXT("AdviseBadDest"), *ThisDest );

			if( FullSrc.Right(3).Caps() == TEXT(".UZ") && ThisDest.Right(3).Caps() != TEXT(".UZ"))
			{
				INT Signature;
				FString OrigFilename;
				*SrcAr << Signature;
				if( Signature != 5678 )
					LocalizedFileError( TEXT("FailedOpenSource"), TEXT("AdviseBadMedia"), *FullSrc );
				else
				{
					*SrcAr << OrigFilename;
					FCodecFull Codec;
					Codec.AddCodec(new FCodecRLE);
					Codec.AddCodec(new FCodecBWT);
					Codec.AddCodec(new FCodecMTF);
					Codec.AddCodec(new FCodecRLE);
					Codec.AddCodec(new FCodecHuffman);
					Codec.Decode( *SrcAr, *DestAr );
					if( !Poll->Poll(*FullDest,Size,Size,RunningBytes+=Size,TotalBytes) )					{
						delete SrcAr;
						delete DestAr;
						DidCancel();
					}
				}
			}
			else
			{
				for( SQWORD Pos=0; Pos<Size; Pos+=sizeof(Buffer) )
				{
					INT Count = Min( Size-Pos, (SQWORD)sizeof(Buffer) );
					SrcAr->Serialize( Buffer, Count );
					if( SrcAr->IsError() )
					{
						delete SrcAr;
						delete DestAr;
						LocalizedFileError( TEXT("FailedReadingSource"), Patch ? TEXT("AdviseBadDownload") : TEXT("AdviseBadMedia"), *FullSrc );
					}
					if( Info.Ref!=TEXT("") )
					{
						CalcOldCRC = appMemCrc( Buffer, Count, CalcOldCRC );
					}
					DestAr->Serialize( Buffer, Count );
					if( DestAr->IsError() )
					{
						delete SrcAr;
						delete DestAr;
						LocalizedFileError( TEXT("FailedWritingDest"), TEXT("AdviseBadDest"), *ThisDest );
					}
					if( !Poll->Poll(*FullDest,Pos,Size,RunningBytes+=Count,TotalBytes) )
					{
						delete SrcAr;
						delete DestAr;
						DidCancel();
					}
				}
			}
			delete SrcAr;
			if( !DestAr->Close() )
				LocalizedFileError( TEXT("FailedClosingDest"), TEXT("AdviseBadDest"), *ThisDest );
			delete DestAr;
			unguard;

			// Patch SrcAr + DeltaFile -> DestAr.
			if( Info.Ref!=TEXT("") )
			{
				guard(PatchFile);
				BYTE Buffer[4096];

				// Open files.
				FString ThisSrc = FullPatch;
				FArchive* SrcAr = GFileManager->CreateFileReader( *ThisSrc );
				if( !SrcAr )
					LocalizedFileError( TEXT("FailedOpenSource"), Patch ? TEXT("AdviseBadDownload") : TEXT("AdviseBadMedia"), *ThisSrc );
				INT Size = SrcAr->TotalSize();
				FArchive* DestAr = GFileManager->CreateFileWriter(*FullDest,FILEWRITE_EvenIfReadOnly);
				if( !DestAr )
					LocalizedFileError( TEXT("FailedOpenDest"), TEXT("AdviseBadDest"), *FullDest );

				// Load delta file.
				TArray<BYTE> Delta;
				FString DeltaName = Info.Src;
				if( !appLoadFileToArray( Delta, *DeltaName ) )
					LocalizedFileError( TEXT("FailedLoadingUpdate"), TEXT("AdviseBadDownload"), *Info.Src );
				debugf( TEXT("Patching %s to %s with %s"), *ThisSrc, *FullDest, *DeltaName );

				// Decompress variables.
				INT PrevSpot=0, CountSize=0, CRC=0;
				INT Magic=0, OldSize=0, OldCRC=0, NewSize=0, NewCRC;
				FBufferReader Reader( Delta );
				Reader << Magic << OldSize << OldCRC << NewSize << NewCRC;

				// Validate.
				if( Magic!=0x92f92912 )
					appErrorf( LineFormat(LocalizeError("PatchCorrupt")), *DeltaName, LocalizeError("AdviseBadDownload") );
				if( OldSize!=Size || OldCRC!=CalcOldCRC )
					appErrorf( LocalizeError("CdFileMismatch"), *Info.Ref, *LocalProduct );

				// Delta decode it.
				INT OldCountSize=0;
				while( !Reader.AtEnd() )
				{
					INT Index;
					Reader << AR_INDEX(Index);
					if( Index<0 )
					{
						CRC = appMemCrc( &Delta(Reader.Tell()), -Index, CRC );
						DestAr->Serialize( &Delta(Reader.Tell()), -Index );
						if( DestAr->IsError() )
							LocalizedFileError( TEXT("FailedWritingDest"), TEXT("AdviseBadDest"), *FullDest );
						Reader.Seek( Reader.Tell() - Index );
						CountSize -= Index;
					}
					else
					{
						INT CopyPos;
						Reader << AR_INDEX(CopyPos);
						CopyPos += PrevSpot;
						check(CopyPos>=0);
						check(CopyPos+Index<=Size);
						SrcAr->Seek( CopyPos );
						for( INT Base=Index; Base>0; Base-=sizeof(Buffer) )
						{
							INT Move = Min(Base,(INT)sizeof(Buffer));
							SrcAr->Serialize( Buffer, Move );
							if( SrcAr->IsError() )
								LocalizedFileError( TEXT("FailedReadingSource"), Patch ? TEXT("AdviseBadDownload") : TEXT("AdviseBadDownload"), *ThisSrc );
							CRC = appMemCrc( Buffer, Move, CRC );
							DestAr->Serialize( Buffer, Move );
							if( DestAr->IsError() )
								LocalizedFileError( TEXT("FailedWritingDest"), TEXT("AdviseBadDest"), *FullDest );
						}
						CountSize += Index;
						PrevSpot = CopyPos + Index;
					}
					if( ((CountSize^OldCountSize)&~(sizeof(Buffer)-1)) || Reader.AtEnd() )
					{
						if( !Poll->Poll(*FullDest,CountSize,Info.Size,RunningBytes+=(CountSize-OldCountSize),TotalBytes) )
						{
							delete SrcAr;
							delete DestAr;
							DidCancel();
						}
						OldCountSize = CountSize;
					}
				}
				if( NewSize!=CountSize || NewCRC!=CRC )
					appErrorf( LineFormat(LocalizeError("PatchCorrupt")), *DeltaName, LocalizeError("AdviseBadDownload") );
				delete SrcAr;
				if( !DestAr->Close() )
					LocalizedFileError( TEXT("FailedClosingDest"), TEXT("AdviseBadDest"), *FullDest );
				delete DestAr;
				GFileManager->Delete( *ThisSrc );
				unguard;
			}
		}
	}
	unguard;
}