bool ULevelDBPluginBPLibrary::WriteObjectToLevelDB(ULevelDBObject* DatabaseObject, FString Key, UObject* Value)
{
	if (IsValid(Value))
	{
		//Vars
		TArray<uint8> ObjectBytes;
		FMemoryWriter MemoryWriter(ObjectBytes, true);

		//Write Object Class name so we can deserialize it one day
		FString ObjectClassName = Value->GetClass()->GetName();
		MemoryWriter << ObjectClassName;

		//Serialize Object and put into the byte array as well
		FObjectAndNameAsStringProxyArchive Ar(MemoryWriter, false);
		Value->Serialize(Ar);

		//marshall pointer to first array entry
		auto ptr = reinterpret_cast<const char*>(ObjectBytes.GetData());

		//level db options
		leveldb::WriteOptions writeOptions;

		//create a slice to store
		leveldb::Slice data = leveldb::Slice(ptr, ObjectBytes.Num());

		//Smash it into the db
		if (DatabaseObject->db != NULL)
			DatabaseObject->db->Put(writeOptions, TCHAR_TO_UTF8(*Key), data);
		else
			return false;
		return true;
	}
	return false;
}
Ejemplo n.º 2
0
void UDataTable::CleanBeforeStructChange()
{
    RowsSerializedWithTags.Reset();
    TemporarilyReferencedObjects.Empty();
    {
        class FRawStructWriter : public FObjectWriter
        {
            TSet<UObject*>& TemporarilyReferencedObjects;
        public:
            FRawStructWriter(TArray<uint8>& InBytes, TSet<UObject*>& InTemporarilyReferencedObjects)
                : FObjectWriter(InBytes), TemporarilyReferencedObjects(InTemporarilyReferencedObjects) {}
            virtual FArchive& operator<<(class UObject*& Res) override
            {
                FObjectWriter::operator<<(Res);
                TemporarilyReferencedObjects.Add(Res);
                return *this;
            }
        };

        FRawStructWriter MemoryWriter(RowsSerializedWithTags, TemporarilyReferencedObjects);
        SaveStructData(MemoryWriter);
    }
    EmptyTable();
    Modify();
}
Ejemplo n.º 3
0
	MemoryWriter Image::encode(ImageFormat format) const
	{
		if (isEmpty())
		{
			return MemoryWriter();
		}

		if (format == ImageFormat::Unspecified)
		{
			format = ImageFormat::PNG;
		}

		return Siv3DEngine::GetImageFormat()->encode(*this, format);
	}
Ejemplo n.º 4
0
bool CloudySaveManagerImpl::Cloudy_SaveGameToSlot(USaveGame* SaveGameObject, const FString& SlotName, 
                                                  const int32 UserIndex, const int32 PCID)
{
    bool bSuccess = false;

    // 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);

    ISaveGameSystem* SaveSystem = IPlatformFeaturesModule::Get().GetSaveGameSystem();
    // If we have a system and an object to save and a save name...
    if (SaveSystem && (SaveGameObject != NULL) && (NewSlotName.Len() > 0))
    {
        TArray<uint8> ObjectBytes;
        FMemoryWriter MemoryWriter(ObjectBytes, true);

        // write file type tag. identifies this file type and indicates it's using proper versioning
        // since older UE4 versions did not version this data.
        int32 FileTypeTag = UE4_SAVEGAME_FILE_TYPE_TAG;
        MemoryWriter << FileTypeTag;

        // Write version for this file format
        int32 SavegameFileVersion = UE4_SAVEGAME_FILE_VERSION;
        MemoryWriter << SavegameFileVersion;

        // Write out engine and UE4 version information
        MemoryWriter << GPackageFileUE4Version;
        FEngineVersion SavedEngineVersion = GEngineVersion;
        MemoryWriter << SavedEngineVersion;

        // Write the class name so we know what class to load to
        FString SaveGameClassName = SaveGameObject->GetClass()->GetName();
        MemoryWriter << SaveGameClassName;
        
        // Then save the object state, replacing object refs and names with strings
        FObjectAndNameAsStringProxyArchive Ar(MemoryWriter, false);
        SaveGameObject->Serialize(Ar);

        // Stuff that data into the save system with the desired file name
        SaveSystem->SaveGame(false, *NewSlotName, UserIndex, ObjectBytes);

        bSuccess = ICloudyWebAPI::Get().UploadFile(NewSlotName, PCID);
    }

    return bSuccess;
}
Ejemplo n.º 5
0
void FStatsWriteFile::WriteHeader( bool bIsRawStatsFile )
{
	FMemoryWriter MemoryWriter( OutData, false, true );
	FArchive& Ar = File ? *File : MemoryWriter;

	uint32 Magic = EStatMagicWithHeader::MAGIC;
	// Serialize magic value.
	Ar << Magic;

	// Serialize dummy header, overwritten in Finalize.
	Header.Version = EStatMagicWithHeader::VERSION_4;
	Header.PlatformName = FPlatformProperties::PlatformName();
	Header.bRawStatsFile = bIsRawStatsFile;
	Ar << Header;

	// Serialize metadata.
	WriteMetadata( Ar );
	Ar.Flush();
}
Ejemplo n.º 6
0
/** Saves the global shader map as a file for the target platform. */
FString SaveGlobalShaderFile(EShaderPlatform Platform, FString SavePath)
{
	TShaderMap<FGlobalShaderType>* GlobalShaderMap = GetGlobalShaderMap(Platform);

	// Wait until all global shaders are compiled
	if (GShaderCompilingManager)
	{
		GShaderCompilingManager->ProcessAsyncResults(false, true);
	}

	TArray<uint8> GlobalShaderData;
	FMemoryWriter MemoryWriter(GlobalShaderData);
	SerializeGlobalShaders(MemoryWriter, GlobalShaderMap);

	// make the final name
	FString FullPath = SavePath / GetGlobalShaderCacheFilename(Platform);
	verify(FFileHelper::SaveArrayToFile(GlobalShaderData, *FullPath));

	return FullPath;
}
Ejemplo n.º 7
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 );
			}
		}
	}
}
Ejemplo n.º 8
0
void FCrashUpload::CompressAndSendData()
{
	UE_LOG(CrashReportClientLog, Log, TEXT("CompressAndSendData have %d pending files"), PendingFiles.Num());

	// Compress all files into one archive.
	const int32 BufferSize = 16*1024*1024;

	TArray<uint8> UncompressedData;
	UncompressedData.Reserve( BufferSize );
	FMemoryWriter MemoryWriter( UncompressedData, false, true );

	int32 CurrentFileIndex = 0;

	// Loop to keep trying files until a send succeeds or we run out of files
	while (PendingFiles.Num() != 0)
	{
		const FString PathOfFileToUpload = PendingFiles.Pop();
		
		if (FPlatformFileManager::Get().GetPlatformFile().FileSize(*PathOfFileToUpload) > MaxFileSizeToUpload)
		{
			UE_LOG(CrashReportClientLog, Warning, TEXT("Skipping large crash report file"));
			continue;
		}

		if (!FFileHelper::LoadFileToArray(PostData, *PathOfFileToUpload))
		{
			UE_LOG(CrashReportClientLog, Warning, TEXT("Failed to load crash report file"));
			continue;
		}

		const bool bSkipLogFile = !FCrashReportClientConfig::Get().GetSendLogFile() && PathOfFileToUpload.EndsWith( TEXT( ".log" ) );
		if (bSkipLogFile)
		{
			UE_LOG( CrashReportClientLog, Warning, TEXT( "Skipping the log file" ) );
			continue;
		}

		UE_LOG(CrashReportClientLog, Log, TEXT("CompressAndSendData compressing %d bytes ('%s')"), PostData.Num(), *PathOfFileToUpload);
		FString Filename = FPaths::GetCleanFilename(PathOfFileToUpload);
		if (Filename == "diagnostics.txt")
		{
			// Ensure diagnostics file is capitalized for server
			Filename[0] = 'D';
		}

		FCompressedCrashFile FileToCompress( CurrentFileIndex, Filename, PostData );
		CurrentFileIndex++;

		MemoryWriter << FileToCompress;
	}

	
	uint8* CompressedDataRaw = new uint8[BufferSize];

	int32 CompressedSize = BufferSize;
	int32 UncompressedSize = UncompressedData.Num();
	const bool bResult = FCompression::CompressMemory( COMPRESS_ZLIB, CompressedDataRaw, CompressedSize, UncompressedData.GetData(), UncompressedSize );
	if( !bResult )
	{
		UE_LOG(CrashReportClientLog, Warning, TEXT("Couldn't compress the crash report files"));
		SetCurrentState(EUploadState::Cancelled);
		return;
	}
	
	const FString Filename = ErrorReport.GetReportDirectoryLeafName() + TEXT(".ue4crash");

	// Copy compressed data into the array.
	TArray<uint8> CompressedData;
	CompressedData.Append( CompressedDataRaw, CompressedSize );
	delete [] CompressedDataRaw;
	CompressedDataRaw = nullptr;

	// Set up request for upload
	auto Request = CreateHttpRequest();
	Request->SetVerb(TEXT("POST"));
	Request->SetHeader(TEXT("Content-Type"), TEXT("application/octet-stream"));
	Request->SetURL(UrlPrefix / TEXT("UploadReportFile"));
	Request->SetContent(CompressedData);
	Request->SetHeader(TEXT("DirectoryName"), *ErrorReport.GetReportDirectoryLeafName());
	Request->SetHeader(TEXT("FileName"), Filename);
	Request->SetHeader(TEXT("FileLength"), TTypeToString<int32>::ToString(CompressedData.Num()) );
	Request->SetHeader(TEXT("CompressedSize"), TTypeToString<int32>::ToString(CompressedSize) );
	Request->SetHeader(TEXT("UncompressedSize"), TTypeToString<int32>::ToString(UncompressedSize) );
	Request->SetHeader(TEXT("NumberOfFiles"), TTypeToString<int32>::ToString(CurrentFileIndex) );
	UE_LOG( CrashReportClientLog, Log, TEXT( "Sending HTTP request: %s" ), *Request->GetURL() );

	if (Request->ProcessRequest())
	{
		return;
	}
	else
	{
		UE_LOG(CrashReportClientLog, Warning, TEXT("Failed to send file upload request"));
		SetCurrentState(EUploadState::Cancelled);
	}	
}
Ejemplo n.º 9
0
PRAGMA_ENABLE_OPTIMIZATION
#endif


/**
 * Handles Byte-swapping outgoing animation data to an array of BYTEs
 *
 * @param	Seq					An Animation Sequence to write.
 * @param	SerializedData		The output buffer.
 * @param	ForceByteSwapping	true is byte swapping is not optional.
 */
void AnimEncodingLegacyBase::ByteSwapOut(
	UAnimSequence& Seq,
	TArray<uint8>& SerializedData, 
	bool ForceByteSwapping)
{
	FMemoryWriter MemoryWriter( SerializedData, true );
	MemoryWriter.SetByteSwapping( ForceByteSwapping );

	uint8* StreamBase		= Seq.CompressedByteStream.GetData();
	const int32 NumTracks		= Seq.CompressedTrackOffsets.Num()/4;

	bool bHasValidScale = Seq.CompressedScaleOffsets.IsValid();

	for ( int32 TrackIndex = 0; TrackIndex < NumTracks; ++TrackIndex )
	{
		const int32 OffsetTrans	= Seq.CompressedTrackOffsets[TrackIndex*4];
		const int32 NumKeysTrans	= Seq.CompressedTrackOffsets[TrackIndex*4+1];
		const int32 OffsetRot		= Seq.CompressedTrackOffsets[TrackIndex*4+2];
		const int32 NumKeysRot	= Seq.CompressedTrackOffsets[TrackIndex*4+3];

		// Translation data.
		checkSlow( (OffsetTrans % 4) == 0 && "CompressedByteStream not aligned to four bytes" );
		uint8* TransTrackData = StreamBase + OffsetTrans;
		if (Seq.TranslationCodec != NULL)
		{
			((AnimEncodingLegacyBase*)Seq.TranslationCodec)->ByteSwapTranslationOut(Seq, MemoryWriter, TransTrackData, NumKeysTrans);
		}
		else
		{
			UE_LOG(LogAnimationCompression, Fatal, TEXT("%i: unknown or unsupported animation format"), (int32)Seq.KeyEncodingFormat );
		};

		// Like the compressed byte stream, pad the serialization stream to four bytes.
		PadMemoryWriter(&MemoryWriter, TransTrackData, 4);

		// Rotation data.
		checkSlow( (OffsetRot % 4) == 0 && "CompressedByteStream not aligned to four bytes" );
		uint8* RotTrackData = StreamBase + OffsetRot;
		checkSlow(Seq.RotationCodec != NULL);
		((AnimEncodingLegacyBase*)Seq.RotationCodec)->ByteSwapRotationOut(Seq, MemoryWriter, RotTrackData, NumKeysRot);

		// Like the compressed byte stream, pad the serialization stream to four bytes.
		PadMemoryWriter(&MemoryWriter, RotTrackData, 4);

		if (bHasValidScale)
		{
			const int32 OffsetScale	= Seq.CompressedScaleOffsets.GetOffsetData(TrackIndex, 0);
			const int32 NumKeysScale	= Seq.CompressedScaleOffsets.GetOffsetData(TrackIndex, 1);

			// Scale data.
			checkSlow( (OffsetScale % 4) == 0 && "CompressedByteStream not aligned to four bytes" );
			uint8* ScaleTrackData = StreamBase + OffsetScale;
			if (Seq.ScaleCodec != NULL)
			{
				((AnimEncodingLegacyBase*)Seq.ScaleCodec)->ByteSwapScaleOut(Seq, MemoryWriter, ScaleTrackData, NumKeysScale);
			}
			else
			{
				UE_LOG(LogAnimationCompression, Fatal, TEXT("%i: unknown or unsupported animation format"), (int32)Seq.KeyEncodingFormat );
			};

			// Like the compressed byte stream, pad the serialization stream to four bytes.
			PadMemoryWriter(&MemoryWriter, ScaleTrackData, 4);
		}
	}
}
Ejemplo n.º 10
0
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);
}
Ejemplo n.º 11
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;
}