213 lines
6.7 KiB
C++
213 lines
6.7 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "UObject/ObjectMacros.h"
|
|
#include "UObject/PropertyPortFlags.h"
|
|
#include "UObject/UnrealType.h"
|
|
#include "Misc/StringBuilder.h"
|
|
#include "Misc/AsciiSet.h"
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
FStrProperty.
|
|
-----------------------------------------------------------------------------*/
|
|
IMPLEMENT_FIELD(FStrProperty)
|
|
|
|
EConvertFromTypeResult FStrProperty::ConvertFromType(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot, uint8* Data, UStruct* DefaultsStruct)
|
|
{
|
|
// Convert serialized text to string.
|
|
if (Tag.Type == NAME_TextProperty)
|
|
{
|
|
FText Text;
|
|
Slot << Text;
|
|
const FString String = FTextInspector::GetSourceString(Text) ? *FTextInspector::GetSourceString(Text) : TEXT("");
|
|
SetPropertyValue_InContainer(Data, String, Tag.ArrayIndex);
|
|
|
|
return EConvertFromTypeResult::Converted;
|
|
}
|
|
|
|
return EConvertFromTypeResult::UseSerializeItem;
|
|
}
|
|
|
|
FString FStrProperty::GetCPPTypeForwardDeclaration() const
|
|
{
|
|
return FString();
|
|
}
|
|
|
|
// Necessary to fix Compiler Error C2026 and C1091
|
|
FString FStrProperty::ExportCppHardcodedText(const FString& InSource, const FString& Indent)
|
|
{
|
|
constexpr FAsciiSet EscapableCharacters("\\\"\n\r\t");
|
|
|
|
auto EstimateExportedStringLength = [&EscapableCharacters](const UTF16CHAR* InStr)
|
|
{
|
|
int32 EstimatedLen = 0;
|
|
while (const UTF16CHAR Char = *InStr++)
|
|
{
|
|
if (EscapableCharacters.Contains(Char))
|
|
{
|
|
// Exported escaped
|
|
EstimatedLen += 2;
|
|
}
|
|
else if (Char > 0x7f)
|
|
{
|
|
// Exported as a UTF-16 sequence
|
|
EstimatedLen += 4;
|
|
}
|
|
else
|
|
{
|
|
// Exported verbatim
|
|
EstimatedLen += 1;
|
|
}
|
|
}
|
|
return EstimatedLen;
|
|
};
|
|
|
|
const int32 PreferredLineSize = 256;
|
|
const int32 LinesPerString = 16;
|
|
|
|
// Note: This is a no-op on platforms that are using a 16-bit TCHAR
|
|
FTCHARToUTF16 UTF16SourceString(*InSource, InSource.Len() + 1); // include the null terminator
|
|
|
|
const bool bUseSubStrings = EstimateExportedStringLength(UTF16SourceString.Get()) > (LinesPerString * PreferredLineSize);
|
|
int32 LineNum = 0;
|
|
|
|
TStringBuilder<1024> Result;
|
|
|
|
if (bUseSubStrings)
|
|
{
|
|
Result << TEXT("*(FString(");
|
|
}
|
|
|
|
const UTF16CHAR* SourceBegin = UTF16SourceString.Get();
|
|
const UTF16CHAR* SourceIt = SourceBegin;
|
|
do
|
|
{
|
|
if (SourceIt > SourceBegin)
|
|
{
|
|
Result << TEXT("\n");
|
|
Result << Indent;
|
|
}
|
|
|
|
++LineNum;
|
|
if (bUseSubStrings && (LineNum % LinesPerString) == 0)
|
|
{
|
|
Result << TEXT(") + FString(");
|
|
}
|
|
|
|
Result << TEXT("TEXT(\"");
|
|
{
|
|
int32 ResultStartLen = Result.Len();
|
|
while (*SourceIt && (Result.Len() - ResultStartLen) < PreferredLineSize)
|
|
{
|
|
if (EscapableCharacters.Contains(*SourceIt))
|
|
{
|
|
const TCHAR CharToEscape = (TCHAR)*SourceIt++;
|
|
|
|
Result << TEXT('\\');
|
|
switch (CharToEscape)
|
|
{
|
|
case TEXT('\n'):
|
|
Result << TEXT('n');
|
|
break;
|
|
case TEXT('\r'):
|
|
Result << TEXT('r');
|
|
break;
|
|
case TEXT('\t'):
|
|
Result << TEXT('t');
|
|
break;
|
|
default:
|
|
Result << CharToEscape;
|
|
break;
|
|
}
|
|
}
|
|
else if (*SourceIt > 0x7f)
|
|
{
|
|
// If this character is part of a surrogate pair, then combine them and write them as a single UTF-32 sequence
|
|
// Otherwise just write out the character as a UTF-16 sequence
|
|
if (StringConv::IsHighSurrogate(*SourceIt) && StringConv::IsLowSurrogate(*(SourceIt + 1)))
|
|
{
|
|
const UTF32CHAR Codepoint = StringConv::EncodeSurrogate(*SourceIt, *(SourceIt + 1));
|
|
Result.Appendf(TEXT("\\U%08x"), Codepoint);
|
|
SourceIt += 2;
|
|
}
|
|
else
|
|
{
|
|
Result.Appendf(TEXT("\\u%04x"), *SourceIt++);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Result << (TCHAR)*SourceIt++;
|
|
}
|
|
}
|
|
}
|
|
Result << TEXT("\")");
|
|
} while (*SourceIt);
|
|
|
|
if (bUseSubStrings)
|
|
{
|
|
Result << TEXT("))");
|
|
}
|
|
|
|
return Result.ToString();
|
|
}
|
|
|
|
void FStrProperty::ExportTextItem(FString& ValueStr, const void* PropertyValue, const void* DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope) const
|
|
{
|
|
FString& StringValue = *(FString*)PropertyValue;
|
|
if (0 != (PortFlags & PPF_ExportCpp))
|
|
{
|
|
ValueStr += FString::Printf(TEXT("FString(%s)"), *ExportCppHardcodedText(StringValue, FString()));
|
|
}
|
|
else if (!(PortFlags & PPF_Delimited))
|
|
{
|
|
ValueStr += StringValue;
|
|
}
|
|
else if (StringValue.Len() > 0)
|
|
{
|
|
ValueStr += FString::Printf(TEXT("\"%s\""), *(StringValue.ReplaceCharWithEscapedChar()));
|
|
}
|
|
else
|
|
{
|
|
ValueStr += TEXT("\"\"");
|
|
}
|
|
}
|
|
const TCHAR* FStrProperty::ImportText_Internal(const TCHAR* Buffer, void* Data, int32 PortFlags, UObject* Parent, FOutputDevice* ErrorText) const
|
|
{
|
|
if (!(PortFlags & PPF_Delimited))
|
|
{
|
|
*(FString*)Data = Buffer;
|
|
|
|
// in order to indicate that the value was successfully imported, advance the buffer past the last character that was imported
|
|
Buffer += FCString::Strlen(Buffer);
|
|
}
|
|
else
|
|
{
|
|
// require quoted string here
|
|
if (*Buffer != TCHAR('"'))
|
|
{
|
|
ErrorText->Logf(TEXT("Missing opening '\"' in string property value: %s"), Buffer);
|
|
return NULL;
|
|
}
|
|
const TCHAR* Start = Buffer;
|
|
FString Temp;
|
|
Buffer = FPropertyHelpers::ReadToken(Buffer, Temp);
|
|
if (Buffer == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
if (Buffer > Start && Buffer[-1] != TCHAR('"'))
|
|
{
|
|
ErrorText->Logf(TEXT("Missing terminating '\"' in string property value: %s"), Start);
|
|
return NULL;
|
|
}
|
|
*(FString*)Data = MoveTemp(Temp);
|
|
}
|
|
return Buffer;
|
|
}
|
|
|
|
uint32 FStrProperty::GetValueTypeHashInternal(const void* Src) const
|
|
{
|
|
return GetTypeHash(*(const FString*)Src);
|
|
}
|