Esempio n. 1
0
void UDataTable::RestoreAfterStructChange()
{
    EmptyTable();
    {
        class FRawStructReader : public FObjectReader
        {
        public:
            FRawStructReader(TArray<uint8>& InBytes) : FObjectReader(InBytes) {}
            virtual FArchive& operator<<(class UObject*& Res) override
            {
                UObject* Object = NULL;
                FObjectReader::operator<<(Object);
                FWeakObjectPtr WeakObjectPtr = Object;
                Res = WeakObjectPtr.Get();
                return *this;
            }
        };

        FRawStructReader MemoryReader(RowsSerializedWithTags);
        LoadStructData(MemoryReader);
    }
    TemporarilyReferencedObjects.Empty();
    RowsSerializedWithTags.Empty();
}
Esempio n. 2
0
TShaderMap<FGlobalShaderType>* GetGlobalShaderMap(EShaderPlatform Platform, bool bRefreshShaderMap)
{
	// No global shaders needed on dedicated server
	if (FPlatformProperties::IsServerOnly())
	{
		if (!GGlobalShaderMap[Platform])
		{
			GGlobalShaderMap[Platform] = new TShaderMap<FGlobalShaderType>();
			return GGlobalShaderMap[Platform];
		}
		return NULL;
	}

	if (bRefreshShaderMap)
	{
		// delete the current global shader map
		delete GGlobalShaderMap[Platform];
		GGlobalShaderMap[Platform] = NULL;

		// make sure we look for updated shader source files
		FlushShaderFileCache();
	}

	// If the global shader map hasn't been created yet, create it.
	if(!GGlobalShaderMap[Platform])
	{
		// GetGlobalShaderMap is called the first time during startup in the main thread.
		check(IsInGameThread());

		FScopedSlowTask SlowTask(70);

		// verify that all shader source files are intact
		SlowTask.EnterProgressFrame(20);
		VerifyShaderSourceFiles();

		GGlobalShaderMap[Platform] = new TShaderMap<FGlobalShaderType>();

		bool bLoadedFromCacheFile = false;

		// Try to load the global shaders from a local cache file if it exists
		// This method is used exclusively with cooked content, since the DDC is not present
		if (FPlatformProperties::RequiresCookedData())
		{
			SlowTask.EnterProgressFrame(50);

			TArray<uint8> GlobalShaderData;
			FString GlobalShaderCacheFilename = FPaths::GetRelativePathToRoot() / GetGlobalShaderCacheFilename(Platform);
			FPaths::MakeStandardFilename(GlobalShaderCacheFilename);
			bLoadedFromCacheFile = FFileHelper::LoadFileToArray(GlobalShaderData, *GlobalShaderCacheFilename, FILEREAD_Silent);

			if (!bLoadedFromCacheFile)
			{
				// Handle this gracefully and exit.
				FString SandboxPath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*GlobalShaderCacheFilename);				
				// This can be too early to localize in some situations.
				const FText Message = FText::Format( NSLOCTEXT("Engine", "GlobalShaderCacheFileMissing", "The global shader cache file '{0}' is missing.\n\nYou're running a version of the application built to load COOKED content only, however no COOKED content was found. Consider cooking content for this build, or build and run the UNCOOKED version of the application instead."), FText::FromString( SandboxPath ) );
				if (FPlatformProperties::SupportsWindowedMode())
				{
					UE_LOG(LogMaterial, Error, TEXT("%s"), *Message.ToString());
					FMessageDialog::Open(EAppMsgType::Ok, Message);
					FPlatformMisc::RequestExit(false);
					return NULL;
				}
				else
				{
					UE_LOG(LogMaterial, Fatal, TEXT("%s"), *Message.ToString());
				}
			}

			FMemoryReader MemoryReader(GlobalShaderData);
			SerializeGlobalShaders(MemoryReader, GGlobalShaderMap[Platform]);
		}
		// Uncooked platform
		else
		{
			FGlobalShaderMapId ShaderMapId(Platform);

			TArray<uint8> CachedData;
			SlowTask.EnterProgressFrame(40);
			const FString DataKey = GetGlobalShaderMapKeyString(ShaderMapId, Platform);

			// Find the shader map in the derived data cache
			SlowTask.EnterProgressFrame(10);
			if (GetDerivedDataCacheRef().GetSynchronous(*DataKey, CachedData))
			{
				FMemoryReader Ar(CachedData, true);

				// Deserialize from the cached data
				SerializeGlobalShaders(Ar, GGlobalShaderMap[Platform]);
			}
		}

		// If any shaders weren't loaded, compile them now.
		VerifyGlobalShaders(Platform, bLoadedFromCacheFile);

		extern int32 GCreateShadersOnLoad;
		if (GCreateShadersOnLoad && Platform == GMaxRHIShaderPlatform)
		{
			for (TMap<FShaderType*, TRefCountPtr<FShader> >::TConstIterator ShaderIt(GGlobalShaderMap[Platform]->GetShaders()); ShaderIt; ++ShaderIt)
			{
				FShader* Shader = ShaderIt.Value();

				if (Shader)
				{
					Shader->InitializeResource();
				}
			}
		}
	}
	return GGlobalShaderMap[Platform];
}
Esempio n. 3
0
/**
 * Serialize just the bulk data portion to/ from the passed in memory.
 *
 * @param	Ar					Archive to serialize with
 * @param	Data				Memory to serialize either to or from
 */
void FUntypedBulkData::SerializeBulkData( FArchive& Ar, void* Data )
{
	// skip serializing of unused data
	if( BulkDataFlags & BULKDATA_Unused )
	{
		return;
	}

	// Skip serialization for bulk data of zero length
	const int32 BulkDataSize = GetBulkDataSize();
	if(BulkDataSize == 0)
	{
		return;
	}

	// Allow backward compatible serialization by forcing bulk serialization off if required. Saving also always uses single
	// element serialization so errors or oversight when changing serialization code is recoverable.
	bool bSerializeInBulk = true;
	if( RequiresSingleElementSerialization( Ar ) 
	// Set when serialized like a lazy array.
	|| (BulkDataFlags & BULKDATA_ForceSingleElementSerialization) 
	// We use bulk serialization even when saving 1 byte types (texture & sound bulk data) as an optimization for those.
	|| (Ar.IsSaving() && (GetElementSize() > 1) ) )
	{
		bSerializeInBulk = false;
	}

	// Raw serialize the bulk data without any possiblity for potential endian conversion.
	if( bSerializeInBulk )
	{
		// Serialize data compressed.
		if( BulkDataFlags & BULKDATA_SerializeCompressed )
		{
			Ar.SerializeCompressed( Data, GetBulkDataSize(), GetDecompressionFlags());
		}
		// Uncompressed/ regular serialization.
		else
		{
			Ar.Serialize( Data, GetBulkDataSize() );
		}
	}
	// Serialize an element at a time via the virtual SerializeElement function potentialy allowing and dealing with 
	// endian conversion. Dealing with compression makes this a bit more complex as SerializeCompressed expects the 
	// full data to be compresed en block and not piecewise.
	else
	{
		// Serialize data compressed.
		if( BulkDataFlags & BULKDATA_SerializeCompressed )
		{
			// Placeholder for to be serialized data.
			TArray<uint8> SerializedData;
			
			// Loading, data is compressed in archive and needs to be decompressed.
			if( Ar.IsLoading() )
			{
				// Create space for uncompressed data.
				SerializedData.Empty( GetBulkDataSize() );
				SerializedData.AddUninitialized( GetBulkDataSize() );

				// Serialize data with passed in archive and compress.
				Ar.SerializeCompressed( SerializedData.GetData(), SerializedData.Num(), GetDecompressionFlags());
				
				// Initialize memory reader with uncompressed data array and propagate forced byte swapping
				FMemoryReader MemoryReader( SerializedData, true );
				MemoryReader.SetByteSwapping( Ar.ForceByteSwapping() );

				// Serialize each element individually via memory reader.				
				for( int32 ElementIndex=0; ElementIndex<ElementCount; ElementIndex++ )
				{
					SerializeElement( MemoryReader, Data, ElementIndex );
				}
			}
			// Saving, data is uncompressed in memory and needs to be compressed.
			else if( Ar.IsSaving() )
			{			
				// Initialize memory writer with blank data array and propagate forced byte swapping
				FMemoryWriter MemoryWriter( SerializedData, true );
				MemoryWriter.SetByteSwapping( Ar.ForceByteSwapping() );

				// Serialize each element individually via memory writer.				
				for( int32 ElementIndex=0; ElementIndex<ElementCount; ElementIndex++ )
				{
					SerializeElement( MemoryWriter, Data, ElementIndex );
				}

				// Serialize data with passed in archive and compress.
				Ar.SerializeCompressed( SerializedData.GetData(), SerializedData.Num(), GetDecompressionFlags() );
			}
		}
		// Uncompressed/ regular serialization.
		else
		{
			// We can use the passed in archive if we're not compressing the data.
			for( int32 ElementIndex=0; ElementIndex<ElementCount; ElementIndex++ )
			{
				SerializeElement( Ar, Data, ElementIndex );
			}
		}
	}
}
Esempio n. 4
0
void FStatsMemoryDumpCommand::InternalRun()
{
	FParse::Value( FCommandLine::Get(), TEXT( "-INFILE=" ), SourceFilepath );

	const int64 Size = IFileManager::Get().FileSize( *SourceFilepath );
	if( Size < 4 )
	{
		UE_LOG( LogStats, Error, TEXT( "Could not open: %s" ), *SourceFilepath );
		return;
	}
	TAutoPtr<FArchive> FileReader( IFileManager::Get().CreateFileReader( *SourceFilepath ) );
	if( !FileReader )
	{
		UE_LOG( LogStats, Error, TEXT( "Could not open: %s" ), *SourceFilepath );
		return;
	}

	if( !Stream.ReadHeader( *FileReader ) )
	{
		UE_LOG( LogStats, Error, TEXT( "Could not open, bad magic: %s" ), *SourceFilepath );
		return;
	}

	UE_LOG( LogStats, Warning, TEXT( "Reading a raw stats file for memory profiling: %s" ), *SourceFilepath );

	const bool bIsFinalized = Stream.Header.IsFinalized();
	check( bIsFinalized );
	check( Stream.Header.Version == EStatMagicWithHeader::VERSION_5 );
	StatsThreadStats.MarkAsLoaded();

	TArray<FStatMessage> Messages;
	if( Stream.Header.bRawStatsFile )
	{
		FScopeLogTime SLT( TEXT( "FStatsMemoryDumpCommand::InternalRun" ), nullptr, FScopeLogTime::ScopeLog_Seconds );

		// Read metadata.
		TArray<FStatMessage> MetadataMessages;
		Stream.ReadFNamesAndMetadataMessages( *FileReader, MetadataMessages );
		StatsThreadStats.ProcessMetaDataOnly( MetadataMessages );

		// Find all UObject metadata messages.
		for( const auto& Meta : MetadataMessages )
		{
			FName LongName = Meta.NameAndInfo.GetRawName();
			const FString Desc = FStatNameAndInfo::GetShortNameFrom( LongName ).GetPlainNameString();
			const bool bContainsUObject = Desc.Contains( TEXT( "//" ) );
			if( bContainsUObject )
			{
				UObjectNames.Add( LongName );
			}
		}

		const int64 CurrentFilePos = FileReader->Tell();

		// Update profiler's metadata.
		CreateThreadsMapping();

		// Read frames offsets.
		Stream.ReadFramesOffsets( *FileReader );

		// Buffer used to store the compressed and decompressed data.
		TArray<uint8> SrcArray;
		TArray<uint8> DestArray;
		const bool bHasCompressedData = Stream.Header.HasCompressedData();
		check( bHasCompressedData );

		TMap<int64, FStatPacketArray> CombinedHistory;
		int64 TotalDataSize = 0;
		int64 TotalStatMessagesNum = 0;
		int64 MaximumPacketSize = 0;
		int64 TotalPacketsNum = 0;
		// Read all packets sequentially, force by the memory profiler which is now a part of the raw stats.
		// !!CAUTION!! Frame number in the raw stats is pointless, because it is time based, not frame based.
		// Background threads usually execute time consuming operations, so the frame number won't be valid.
		// Needs to be combined by the thread and the time, not by the frame number.
		{
			// Display log information once per 5 seconds to avoid spamming.
			double PreviousSeconds = FPlatformTime::Seconds();
			const int64 FrameOffset0 = Stream.FramesInfo[0].FrameFileOffset;
			FileReader->Seek( FrameOffset0 );

			const int64 FileSize = FileReader->TotalSize();

			while( FileReader->Tell() < FileSize )
			{
				// Read the compressed data.
				FCompressedStatsData UncompressedData( SrcArray, DestArray );
				*FileReader << UncompressedData;
				if( UncompressedData.HasReachedEndOfCompressedData() )
				{
					break;
				}

				FMemoryReader MemoryReader( DestArray, true );

				FStatPacket* StatPacket = new FStatPacket();
				Stream.ReadStatPacket( MemoryReader, *StatPacket );

				const int64 StatPacketFrameNum = StatPacket->Frame;
				FStatPacketArray& Frame = CombinedHistory.FindOrAdd( StatPacketFrameNum );

				// Check if we need to combine packets from the same thread.
				FStatPacket** CombinedPacket = Frame.Packets.FindByPredicate( [&]( FStatPacket* Item ) -> bool
				{
					return Item->ThreadId == StatPacket->ThreadId;
				} );

				const int64 PacketSize = StatPacket->StatMessages.GetAllocatedSize();
				TotalStatMessagesNum += StatPacket->StatMessages.Num();

				if( CombinedPacket )
				{
					TotalDataSize -= (*CombinedPacket)->StatMessages.GetAllocatedSize();
					(*CombinedPacket)->StatMessages += StatPacket->StatMessages;
					TotalDataSize += (*CombinedPacket)->StatMessages.GetAllocatedSize();

					delete StatPacket;
				}
				else
				{
					Frame.Packets.Add( StatPacket );
					TotalDataSize += PacketSize;
				}

				const double CurrentSeconds = FPlatformTime::Seconds();
				if( CurrentSeconds > PreviousSeconds + NumSecondsBetweenLogs )
				{
					const int32 PctPos = int32( 100.0*FileReader->Tell() / FileSize );
					UE_LOG( LogStats, Log, TEXT( "%3i%% %10llu (%.1f MB) read messages, last read frame %4i" ), PctPos, TotalStatMessagesNum, TotalDataSize / 1024.0f / 1024.0f, StatPacketFrameNum );
					PreviousSeconds = CurrentSeconds;
				}
			
				MaximumPacketSize = FMath::Max( MaximumPacketSize, PacketSize );			
				TotalPacketsNum++;
			}
		}

		// Dump frame stats
		for( const auto& It : CombinedHistory )
		{
			const int64 FrameNum = It.Key;
			int64 FramePacketsSize = 0;
			int64 FrameStatMessages = 0;
			int64 FramePackets = It.Value.Packets.Num(); // Threads
			for( const auto& It2 : It.Value.Packets )
			{
				FramePacketsSize += It2->StatMessages.GetAllocatedSize();
				FrameStatMessages += It2->StatMessages.Num();
			}

			UE_LOG( LogStats, Warning, TEXT( "Frame: %10llu/%3lli Size: %.1f MB / %10lli" ), 
					FrameNum, 
					FramePackets, 
					FramePacketsSize / 1024.0f / 1024.0f,
					FrameStatMessages );
		}

		UE_LOG( LogStats, Warning, TEXT( "TotalPacketSize: %.1f MB, Max: %1f MB" ),
				TotalDataSize / 1024.0f / 1024.0f,
				MaximumPacketSize / 1024.0f / 1024.0f );

		TArray<int64> Frames;
		CombinedHistory.GenerateKeyArray( Frames );
		Frames.Sort();
		const int64 MiddleFrame = Frames[Frames.Num() / 2];

		ProcessMemoryOperations( CombinedHistory );
	}
}
void FTextLocalizationManager::RegenerateResources( const FString& ConfigFilePath, IInternationalizationArchiveSerializer& ArchiveSerializer, IInternationalizationManifestSerializer& ManifestSerializer )
{
	// Add one to the revision index, so all FText's refresh.
	++HeadCultureRevisionIndex;
	
	FInternationalization& I18N = FInternationalization::Get();

	FString SectionName = TEXT("RegenerateResources");

	// Get source path.
	FString SourcePath;
	if( !( GConfig->GetString( *SectionName, TEXT("SourcePath"), SourcePath, ConfigFilePath ) ) )
	{
		UE_LOG(LogTextLocalizationManager, Error, TEXT("No source path specified."));
		return;
	}

	// Get destination path.
	FString DestinationPath;
	if( !( GConfig->GetString( *SectionName, TEXT("DestinationPath"), DestinationPath, ConfigFilePath ) ) )
	{
		UE_LOG(LogTextLocalizationManager, Error, TEXT("No destination path specified."));
		return;
	}

	// Get manifest name.
	FString ManifestName;
	if( !( GConfig->GetString( *SectionName, TEXT("ManifestName"), ManifestName, ConfigFilePath ) ) )
	{
		UE_LOG(LogTextLocalizationManager, Error, TEXT("No manifest name specified."));
		return;
	}

	// Get resource name.
	FString ResourceName;
	if( !( GConfig->GetString( *SectionName, TEXT("ResourceName"), ResourceName, ConfigFilePath ) ) )
	{
		UE_LOG(LogTextLocalizationManager, Error, TEXT("No resource name specified."));
		return;
	}

	TArray<FString> LocaleNames;
	{
		const FString CultureName = I18N.GetCurrentCulture()->GetName();
		LocaleNames.Add(CultureName);
		const FString BaseLanguageName = I18N.GetCurrentCulture()->GetTwoLetterISOLanguageName();
		if(BaseLanguageName != CultureName)
		{
			LocaleNames.Add(BaseLanguageName);
		}
	}

	// Source path needs to be relative to Engine or Game directory
	FString ConfigFullPath = FPaths::ConvertRelativePathToFull(ConfigFilePath);
	FString EngineFullPath = FPaths::ConvertRelativePathToFull(FPaths::EngineConfigDir());
	bool IsEngineManifest = false;
	
	if (ConfigFullPath.StartsWith(EngineFullPath))
	{
		IsEngineManifest = true;
	}

	if (IsEngineManifest)
	{
		SourcePath = FPaths::Combine(*(FPaths::EngineDir()), *SourcePath);
		DestinationPath = FPaths::Combine(*(FPaths::EngineDir()), *DestinationPath);
	}
	else
	{
		SourcePath = FPaths::Combine(*(FPaths::GameDir()), *SourcePath);
		DestinationPath = FPaths::Combine(*(FPaths::GameDir()), *DestinationPath);
	}

	TArray<TArray<uint8>> BackingBuffers;
	BackingBuffers.SetNum(LocaleNames.Num());

	for(int32 i = 0; i < BackingBuffers.Num(); ++i)
	{
		TArray<uint8>& BackingBuffer = BackingBuffers[i];
		FMemoryWriter MemoryWriter(BackingBuffer, true);

		// Read the manifest file from the source path.
		FString ManifestFilePath = (SourcePath / ManifestName);
		ManifestFilePath = FPaths::ConvertRelativePathToFull(ManifestFilePath);
		TSharedRef<FInternationalizationManifest> InternationalizationManifest = MakeShareable(new FInternationalizationManifest);

#if 0 // @todo Json: Serializing from FArchive is currently broken
		FArchive* ManifestFile = IFileManager::Get().CreateFileReader(*ManifestFilePath);

		if (ManifestFile == nullptr)
		{
			UE_LOG(LogTextLocalizationManager, Error, TEXT("No manifest found at %s."), *ManifestFilePath);
			return;
		}

		ManifestSerializer.DeserializeManifest(*ManifestFile, InternationalizationManifest);
#else
		FString ManifestContent;

		if (!FFileHelper::LoadFileToString(ManifestContent, *ManifestFilePath))
		{
			UE_LOG(LogTextLocalizationManager, Error, TEXT("Failed to load file %s."), *ManifestFilePath);
			continue;
		}

		ManifestSerializer.DeserializeManifest(ManifestContent, InternationalizationManifest);
#endif

		// Write resource.
		FTextLocalizationResourceGenerator::Generate(SourcePath, InternationalizationManifest, LocaleNames[i], &(MemoryWriter), ArchiveSerializer);

		MemoryWriter.Close();
	}

	// Prioritized array of localization entry trackers.
	TArray<FLocalizationEntryTracker> LocalizationEntryTrackers;
	for(int32 i = 0; i < BackingBuffers.Num(); ++i)
	{
		TArray<uint8>& BackingBuffer = BackingBuffers[i];
		FMemoryReader MemoryReader(BackingBuffer, true);
		const FString CulturePath = DestinationPath / LocaleNames[i];
		const FString ResourceFilePath = FPaths::ConvertRelativePathToFull(CulturePath / ResourceName);

		FLocalizationEntryTracker& CultureTracker = LocalizationEntryTrackers[LocalizationEntryTrackers.Add(FLocalizationEntryTracker())];
		CultureTracker.ReadFromArchive(MemoryReader, ResourceFilePath);
		CultureTracker.ReportCollisions();

		MemoryReader.Close();
	}

	// Don't filter updates by table name, or we can't live preview strings that had no translation originally
	UpdateLiveTable(LocalizationEntryTrackers);
}
USaveGame* CloudySaveManagerImpl::Cloudy_LoadGameFromSlot(const FString& SlotName, 
                                                          const int32 UserIndex,
                                                          const int32 PCID)
{
    // Load from CloudyWeb, write it to default save location.
    ICloudyWebAPI::Get().DownloadFile(SlotName, PCID);

    // Appends the player controller ID to the save file, 
    // so that the split screen players do not overwrite 1 save file.
    FString NewSlotName = SlotName + "-" + FString::FromInt(PCID);
    
    USaveGame* OutSaveGameObject = NULL;
    
    ISaveGameSystem* SaveSystem = IPlatformFeaturesModule::Get().GetSaveGameSystem();
    // If we have a save system and a valid name..
    if (SaveSystem && (NewSlotName.Len() > 0))
    {
        // Load raw data from slot
        TArray<uint8> ObjectBytes;
        bool bSuccess = SaveSystem->LoadGame(false, *NewSlotName, UserIndex, ObjectBytes);
        if (bSuccess)
        {
            FMemoryReader MemoryReader(ObjectBytes, true);

            int32 FileTypeTag;
            MemoryReader << FileTypeTag;

            int32 SavegameFileVersion;
            if (FileTypeTag != UE4_SAVEGAME_FILE_TYPE_TAG)
            {
                // this is an old saved game, back up the file pointer to the beginning and assume version 1
                MemoryReader.Seek(0);
                SavegameFileVersion = 1;

                // Note for 4.8 and beyond: if you get a crash loading a pre-4.8 version of your savegame file and 
                // you don't want to delete it, try uncommenting these lines and changing them to use the version 
                // information from your previous build. Then load and resave your savegame file.
                //MemoryReader.SetUE4Ver(MyPreviousUE4Version);				// @see GPackageFileUE4Version
                //MemoryReader.SetEngineVer(MyPreviousEngineVersion);		// @see FEngineVersion::Current()
            }
            else
            {
                // Read version for this file format
                MemoryReader << SavegameFileVersion;

                // Read engine and UE4 version information
                int32 SavedUE4Version;
                MemoryReader << SavedUE4Version;

                FEngineVersion SavedEngineVersion;
                MemoryReader << SavedEngineVersion;

                MemoryReader.SetUE4Ver(SavedUE4Version);
                MemoryReader.SetEngineVer(SavedEngineVersion);
            }

            // Get the class name
            FString SaveGameClassName;
            MemoryReader << SaveGameClassName;

            // Try and find it, and failing that, load it
            UClass* SaveGameClass = FindObject<UClass>(ANY_PACKAGE, *SaveGameClassName);
            if (SaveGameClass == NULL)
            {
                SaveGameClass = LoadObject<UClass>(NULL, *SaveGameClassName);
            }

            // If we have a class, try and load it.
            if (SaveGameClass != NULL)
            {
                OutSaveGameObject = NewObject<USaveGame>(GetTransientPackage(), SaveGameClass);

                FObjectAndNameAsStringProxyArchive Ar(MemoryReader, true);
                OutSaveGameObject->Serialize(Ar);
            }

            UE_LOG(CloudySaveManagerLog, Warning, TEXT("Game loaded successfully."));
        }
    }
    
    return OutSaveGameObject;
}
Esempio n. 7
0
bool FTextPropertyTest::RunTest (const FString& Parameters)
{
	UClass* const TextPropertyTestObjectClass = UTextPropertyTestObject::StaticClass();
	UTextProperty* const DefaultedTextProperty = FindField<UTextProperty>(TextPropertyTestObjectClass, "DefaultedText");
	UTextProperty* const UndefaultedTextProperty = FindField<UTextProperty>(TextPropertyTestObjectClass, "UndefaultedText");
	UTextPropertyTestObject* const TextPropertyTestCDO = Cast<UTextPropertyTestObject>( TextPropertyTestObjectClass->ClassDefaultObject );

	{
		UTextPropertyTestObject* NewObject = Cast<UTextPropertyTestObject>( StaticConstructObject( TextPropertyTestObjectClass ) );

		// Test Identical - Newly constructed object properties should be identical to class default object properties.
		if(	(DefaultedTextProperty->Identical(&(NewObject->DefaultedText), &(TextPropertyTestCDO->DefaultedText), 0) != true)
			||
			(UndefaultedTextProperty->Identical(&(NewObject->UndefaultedText), &(TextPropertyTestCDO->UndefaultedText), 0) != true) )
		{
			AddError(TEXT("UTextProperty::Identical failed to return true comparing a newly constructed object and the class default object."));
		}

		// Test ExportText - Export text should provide the localized form of the text.
		{
			FString ExportedStringValue;
			DefaultedTextProperty->ExportTextItem(ExportedStringValue, &(NewObject->DefaultedText), NULL, NULL, 0, NULL);
			if( ExportedStringValue != NewObject->DefaultedText.ToString() )
			{
				AddError(TEXT("UTextProperty::ExportTextItem failed to provide the display string."));
			}
		}

		// Test ImportText - Import text should set the source string to the input string.
		{
			FString ImportedStringValue = TEXT("ImportValue");
			DefaultedTextProperty->ImportText(*ImportedStringValue, &(NewObject->DefaultedText), 0, NULL);
			const FString* const SourceString = FTextInspector::GetSourceString(NewObject->DefaultedText);
			if( !SourceString || ImportedStringValue != *SourceString )
			{
				AddError(TEXT("UTextProperty::ImportText failed to alter the source string to the provided value."));
			}
		}
	}

	// Test Identical - Altered text properties should not be identical to class default object properties.
	{
		UTextPropertyTestObject* NewObject = Cast<UTextPropertyTestObject>( StaticConstructObject( TextPropertyTestObjectClass ) );

		NewObject->DefaultedText = LOCTEXT("ModifiedDefaultedText", "Modified DefaultedText Value");
		NewObject->UndefaultedText = LOCTEXT("ModifiedUndefaultedText", "Modified UndefaultedText Value");
		if( 
			DefaultedTextProperty->Identical(&(NewObject->DefaultedText), &(TextPropertyTestCDO->DefaultedText), 0)
			||
			UndefaultedTextProperty->Identical(&(NewObject->UndefaultedText), &(TextPropertyTestCDO->UndefaultedText), 0)
		  )
		{
			AddError(TEXT("UTextProperty::Identical failed to return false comparing a modified object and the class default object."));
		}
	}

	{
		TArray<uint8> BackingStore;
		
		UTextPropertyTestObject* SavedObject = Cast<UTextPropertyTestObject>(StaticConstructObject(UTextPropertyTestObject::StaticClass()));

		FText::FindText( TEXT("TextPropertyTest"), TEXT("DefaultedText"), /*OUT*/SavedObject->DefaultedText );
		SavedObject->UndefaultedText = LOCTEXT("ModifiedUndefaultedText", "Modified UndefaultedText Value");
		const FText TransientText = FText::Format( LOCTEXT("TransientTest", "{0}"), LOCTEXT("TransientTestMessage", "Testing Transient serialization detection") );
		SavedObject->TransientText = TransientText;

		// Test Identical - Text properties with the same source as class default object properties should be considered identical. 
		if( !( DefaultedTextProperty->Identical(&(SavedObject->DefaultedText), &(TextPropertyTestCDO->DefaultedText), 0) ) )
		{
			AddError(TEXT("UTextProperty::Identical failed to return true comparing an FText with an identical source string to the class default object."));
		}

		// Save.
		{
			FMemoryWriter MemoryWriter(BackingStore, true);
			SavedObject->Serialize(MemoryWriter);
		}

		UTextPropertyTestObject* LoadedObject = Cast<UTextPropertyTestObject>(StaticConstructObject(UTextPropertyTestObject::StaticClass()));

		// Load.
		{
			FMemoryReader MemoryReader(BackingStore, true);
			LoadedObject->Serialize(MemoryReader);
		}

		// Test Serialization - Loaded object should be identical to saved object. 
		if( 
			!( DefaultedTextProperty->Identical(&(LoadedObject->DefaultedText), &(SavedObject->DefaultedText), 0) )
			||
			!( UndefaultedTextProperty->Identical(&(LoadedObject->UndefaultedText), &(SavedObject->UndefaultedText), 0) )
		  )
		{
			AddError(TEXT("Saving and loading a serialized object containing FText properties failed to maintain FText values."));
		}

		// Test Identical - Text properties with the same source as the class default object property should save and load as the class default object property.
		if( !( DefaultedTextProperty->Identical(&(LoadedObject->DefaultedText), &(TextPropertyTestCDO->DefaultedText), 0) ) )
		{
			AddError(TEXT("UTextProperty::Identical failed to collapse identical source strings into the same namespace and key during serialization."));
		}

		// Test Transient - Transient text properties should save out an error message instead of their actual string value
		const FString* const LoadedTransientTextString = FTextInspector::GetSourceString(LoadedObject->TransientText);
		const FString* const TransientTextString = FTextInspector::GetSourceString(TransientText);
		if ( GIsEditor && LoadedTransientTextString && TransientTextString && *(LoadedTransientTextString) != *(TransientTextString) )
		{
			AddError(TEXT("Transient Texts should not exist in the editor."));
		}
		else if ( !GIsEditor && LoadedObject->TransientText.ToString() != FText::Format( FText::SerializationFailureError, TransientText ).ToString() )
		{
			//AddError(TEXT("Transient Texts should persist an error message when they are serialized."));
		}
	}

	return true;
}
void FRawProfilerSession::PrepareLoading()
{
	SCOPE_LOG_TIME_FUNC();

	const FString Filepath = DataFilepath + FStatConstants::StatsFileRawExtension;
	const int64 Size = IFileManager::Get().FileSize( *Filepath );
	if( Size < 4 )
	{
		UE_LOG( LogStats, Error, TEXT( "Could not open: %s" ), *Filepath );
		return;
	}
	TAutoPtr<FArchive> FileReader( IFileManager::Get().CreateFileReader( *Filepath ) );
	if( !FileReader )
	{
		UE_LOG( LogStats, Error, TEXT( "Could not open: %s" ), *Filepath );
		return;
	}

	if( !Stream.ReadHeader( *FileReader ) )
	{
		UE_LOG( LogStats, Error, TEXT( "Could not open, bad magic: %s" ), *Filepath );
		return;
	}

	const bool bIsFinalized = Stream.Header.IsFinalized();
	check( bIsFinalized );
	check( Stream.Header.Version == EStatMagicWithHeader::VERSION_5 );
	StatsThreadStats.MarkAsLoaded();

	TArray<FStatMessage> Messages;
	if( Stream.Header.bRawStatsFile )
	{
		// Read metadata.
		TArray<FStatMessage> MetadataMessages;
		Stream.ReadFNamesAndMetadataMessages( *FileReader, MetadataMessages );
		StatsThreadStats.ProcessMetaDataOnly( MetadataMessages );

		const FName F00245 = FName(245, 245, 0);
		
		const FName F11602 = FName(11602, 11602, 0);
		const FName F06394 = FName(6394, 6394, 0);

		const int64 CurrentFilePos = FileReader->Tell();

		// Update profiler's metadata.
		StatMetaData->UpdateFromStatsState( StatsThreadStats );
		const uint32 GameThreadID = GetMetaData()->GetGameThreadID();

		// Read frames offsets.
		Stream.ReadFramesOffsets( *FileReader );

		// Buffer used to store the compressed and decompressed data.
		TArray<uint8> SrcArray;
		TArray<uint8> DestArray;
		const bool bHasCompressedData = Stream.Header.HasCompressedData();
		check(bHasCompressedData);

		TMap<int64, FStatPacketArray> CombinedHistory;
		int64 TotalPacketSize = 0;
		int64 MaximumPacketSize = 0;
		// Read all packets sequentially, force by the memory profiler which is now a part of the raw stats.
		// !!CAUTION!! Frame number in the raw stats is pointless, because it is time based, not frame based.
		// Background threads usually execute time consuming operations, so the frame number won't be valid.
		// Needs to be combined by the thread and the time, not by the frame number.
		{
			int64 FrameOffset0 = Stream.FramesInfo[0].FrameFileOffset;
			FileReader->Seek( FrameOffset0 );

			const int64 FileSize = FileReader->TotalSize();

			while( FileReader->Tell() < FileSize )
			{
				// Read the compressed data.
				FCompressedStatsData UncompressedData( SrcArray, DestArray );
				*FileReader << UncompressedData;
				if( UncompressedData.HasReachedEndOfCompressedData() )
				{
					break;
				}

				FMemoryReader MemoryReader( DestArray, true );

				FStatPacket* StatPacket = new FStatPacket();
				Stream.ReadStatPacket( MemoryReader, *StatPacket );
				
				const int64 FrameNum = StatPacket->Frame;
				FStatPacketArray& Frame = CombinedHistory.FindOrAdd(FrameNum);
			
				// Check if we need to combine packets from the same thread.
				FStatPacket** CombinedPacket = Frame.Packets.FindByPredicate([&](FStatPacket* Item) -> bool
				{
					return Item->ThreadId == StatPacket->ThreadId;
				});
				
				if( CombinedPacket )
				{
					(*CombinedPacket)->StatMessages += StatPacket->StatMessages;
				}
				else
				{
					Frame.Packets.Add(StatPacket);
				}

				const int64 CurrentPos = FileReader->Tell();
				const int32 PctPos = int32(100.0f*CurrentPos/FileSize);

				UE_LOG( LogStats, Log, TEXT( "%3i Processing FStatPacket: Frame %5i for thread %5i with %6i messages (%.1f MB)" ), 
					PctPos, 
					StatPacket->Frame, 
					StatPacket->ThreadId, 
					StatPacket->StatMessages.Num(), 
					StatPacket->StatMessages.GetAllocatedSize()/1024.0f/1024.0f );

				const int64 PacketSize = StatPacket->StatMessages.GetAllocatedSize();
				TotalPacketSize += PacketSize;
				MaximumPacketSize = FMath::Max( MaximumPacketSize, PacketSize );
			}
		}

		UE_LOG( LogStats, Log, TEXT( "TotalPacketSize: %.1f MB, Max: %1f MB" ), 
			TotalPacketSize/1024.0f/1024.0f, 
			MaximumPacketSize/1024.0f/1024.0f );

		TArray<int64> Frames;
		CombinedHistory.GenerateKeyArray(Frames);
		Frames.Sort();
		const int64 MiddleFrame = Frames[Frames.Num()/2];


		// Remove all frames without the game thread messages.
		for (int32 FrameIndex = 0; FrameIndex < Frames.Num(); ++FrameIndex)
		{
			const int64 TargetFrame = Frames[FrameIndex];
			const FStatPacketArray& Frame = CombinedHistory.FindChecked( TargetFrame );

			const double GameThreadTimeMS = GetMetaData()->ConvertCyclesToMS( GetFastThreadFrameTimeInternal( Frame, EThreadType::Game ) );

			if (GameThreadTimeMS == 0.0f)
			{
				CombinedHistory.Remove( TargetFrame );
				Frames.RemoveAt( FrameIndex );
				FrameIndex--;
			}
		}
		
	
		StatMetaData->SecondsPerCycle = GetSecondsPerCycle( CombinedHistory.FindChecked(MiddleFrame) );
		check( StatMetaData->GetSecondsPerCycle() > 0.0 );

		//const int32 FirstGameThreadFrame = FindFirstFrameWithGameThread( CombinedHistory, Frames );

		// Prepare profiler frame.
		{
			SCOPE_LOG_TIME( TEXT( "Preparing profiler frames" ), nullptr );

			// Prepare profiler frames.
			double ElapsedTimeMS = 0;

			for( int32 FrameIndex = 0; FrameIndex < Frames.Num(); ++FrameIndex )
			{
				const int64 TargetFrame = Frames[FrameIndex];
				const FStatPacketArray& Frame = CombinedHistory.FindChecked(TargetFrame);

				const double GameThreadTimeMS = GetMetaData()->ConvertCyclesToMS( GetFastThreadFrameTimeInternal(Frame,EThreadType::Game) );

				if( GameThreadTimeMS == 0.0f )
				{
					continue;
				}

				const double RenderThreadTimeMS = GetMetaData()->ConvertCyclesToMS( GetFastThreadFrameTimeInternal(Frame,EThreadType::Renderer) );

				// Update mini-view, convert from cycles to ms.
				TMap<uint32, float> ThreadTimesMS;
				ThreadTimesMS.Add( GameThreadID, GameThreadTimeMS );
				ThreadTimesMS.Add( GetMetaData()->GetRenderThreadID()[0], RenderThreadTimeMS );

				// Pass the reference to the stats' metadata.
				OnAddThreadTime.ExecuteIfBound( FrameIndex, ThreadTimesMS, StatMetaData );

				// Create a new profiler frame and add it to the stream.
				ElapsedTimeMS += GameThreadTimeMS;
				FProfilerFrame* ProfilerFrame = new FProfilerFrame( TargetFrame, GameThreadTimeMS, ElapsedTimeMS );
				ProfilerFrame->ThreadTimesMS = ThreadTimesMS;
				ProfilerStream.AddProfilerFrame( TargetFrame, ProfilerFrame );
			}
		}
	
		// Process the raw stats data.
		{
			SCOPE_LOG_TIME( TEXT( "Processing the raw stats" ), nullptr );

			double CycleCounterAdjustmentMS = 0.0f;

			// Read the raw stats messages.
			for( int32 FrameIndex = 0; FrameIndex < Frames.Num()-1; ++FrameIndex )
			{
				const int64 TargetFrame = Frames[FrameIndex];
				const FStatPacketArray& Frame = CombinedHistory.FindChecked(TargetFrame);

				FProfilerFrame* ProfilerFrame = ProfilerStream.GetProfilerFrame( FrameIndex );

				UE_CLOG( FrameIndex % 8 == 0, LogStats, Log, TEXT( "Processing raw stats frame: %4i/%4i" ), FrameIndex, Frames.Num() );

				ProcessStatPacketArray( Frame, *ProfilerFrame, FrameIndex ); // or ProfilerFrame->TargetFrame

				// Find the first cycle counter for the game thread.
				if( CycleCounterAdjustmentMS == 0.0f )
				{
					CycleCounterAdjustmentMS = ProfilerFrame->Root->CycleCounterStartTimeMS;
				}

				// Update thread time and mark profiler frame as valid and ready for use.
				ProfilerFrame->MarkAsValid();
			}

			// Adjust all profiler frames.
			ProfilerStream.AdjustCycleCounters( CycleCounterAdjustmentMS );
		}
	}

	const int64 AllocatedSize = ProfilerStream.GetAllocatedSize();

	// We have the whole metadata and basic information about the raw stats file, start ticking the profiler session.
	//OnTickHandle = FTicker::GetCoreTicker().AddTicker( OnTick, 0.25f );

#if	0
	if( SessionType == EProfilerSessionTypes::OfflineRaw )
	{
		// Broadcast that a capture file has been fully processed.
		OnCaptureFileProcessed.ExecuteIfBound( GetInstanceID() );
	}
#endif // 0
}