diff --git a/Resources/Icon128.png b/Resources/Icon128.png index 0a75ffda..3624a4bc 100644 Binary files a/Resources/Icon128.png and b/Resources/Icon128.png differ diff --git a/SCREENSHOT.jpg b/SCREENSHOT.jpg index 7da4b53f..834c26a3 100644 Binary files a/SCREENSHOT.jpg and b/SCREENSHOT.jpg differ diff --git a/Source/.clang-format b/Source/.clang-format index de124b78..1f0a0a4a 100644 --- a/Source/.clang-format +++ b/Source/.clang-format @@ -16,7 +16,6 @@ BreakConstructorInitializersBeforeComma: true ColumnLimit: 0 PointerAlignment: Left SpacesInAngles: false -... --- Language: ObjC ... diff --git a/Source/VaRestEditorPlugin/Private/VaRest_BreakJson.cpp b/Source/VaRestEditorPlugin/Private/VaRest_BreakJson.cpp index 418e7ebc..1a14290f 100644 --- a/Source/VaRestEditorPlugin/Private/VaRest_BreakJson.cpp +++ b/Source/VaRestEditorPlugin/Private/VaRest_BreakJson.cpp @@ -592,7 +592,12 @@ void UVaRest_MakeJson::CreateProjectionPins(UEdGraphPin* Source) UEdGraphNode::FCreatePinParams InputPinParams; InputPinParams.ContainerType = (*it).bIsArray ? EPinContainerType::Array : EPinContainerType::None; UEdGraphPin* InputPin = CreatePin(EGPD_Input, Type, TEXT(""), Subtype, FName(*(*it).Name), InputPinParams); + +#if ENGINE_MINOR_VERSION >= 20 InputPin->SetSavePinIfOrphaned(false); +#else + InputPin->bSavePinIfOrphaned = false; +#endif } } diff --git a/Source/VaRestPlugin/Classes/VaRestJsonObject.h b/Source/VaRestPlugin/Classes/VaRestJsonObject.h index f2c27573..32ece87d 100644 --- a/Source/VaRestPlugin/Classes/VaRestJsonObject.h +++ b/Source/VaRestPlugin/Classes/VaRestJsonObject.h @@ -176,9 +176,21 @@ class VARESTPLUGIN_API UVaRestJsonObject : public UObject /** Deserialize byte stream from reader */ void DecodeFromArchive(TUniquePtr& Reader); + ////////////////////////////////////////////////////////////////////////// + // Serialize + +public: /** Save json to file */ bool WriteToFile(const FString& Path); + /** + * Blueprint Save json to filepath + * + * @param bIsRelativeToProjectDir If set to 'false' path is treated as absolute + */ + UFUNCTION(BlueprintCallable, Category = "VaRest|Json") + bool WriteToFilePath(const FString& Path, const bool bIsRelativeToProjectDir = true); + static bool WriteStringToArchive(FArchive& Ar, const TCHAR* StrPtr, int64 Len); ////////////////////////////////////////////////////////////////////////// diff --git a/Source/VaRestPlugin/Classes/VaRestLibrary.h b/Source/VaRestPlugin/Classes/VaRestLibrary.h index 96e477ac..bfd5ea9d 100644 --- a/Source/VaRestPlugin/Classes/VaRestLibrary.h +++ b/Source/VaRestPlugin/Classes/VaRestLibrary.h @@ -97,10 +97,10 @@ class VARESTPLUGIN_API UVaRestLibrary : public UBlueprintFunctionLibrary public: /** * Load JSON from formatted text file - * @param Path File name relative to the Content folder + * @param bIsRelativeToContentDir if set to 'false' path is treated as absolute */ UFUNCTION(BlueprintCallable, Category = "VaRest|Utility", meta = (WorldContext = "WorldContextObject")) - static class UVaRestJsonObject* LoadJsonFromFile(UObject* WorldContextObject, const FString& Path); + static class UVaRestJsonObject* LoadJsonFromFile(UObject* WorldContextObject, const FString& Path, const bool bIsRelativeToContentDir = true); ////////////////////////////////////////////////////////////////////////// // Easy URL processing diff --git a/Source/VaRestPlugin/Private/VaRestJsonObject.cpp b/Source/VaRestPlugin/Private/VaRestJsonObject.cpp index 366525c7..3153c2cc 100644 --- a/Source/VaRestPlugin/Private/VaRestJsonObject.cpp +++ b/Source/VaRestPlugin/Private/VaRestJsonObject.cpp @@ -1,10 +1,13 @@ // Copyright 2014 Vladimir Alyamkin. All Rights Reserved. #include "VaRestJsonObject.h" + #include "VaRestJsonParser.h" #include "VaRestJsonValue.h" #include "VaRestPluginPrivatePCH.h" +#include "Runtime/Launch/Resources/Version.h" + typedef TJsonWriterFactory> FCondensedJsonStringWriterFactory; typedef TJsonWriter> FCondensedJsonStringWriter; @@ -543,28 +546,14 @@ int32 UVaRestJsonObject::DeserializeFromUTF8Bytes(const ANSICHAR* Bytes, int32 S { FJSONReader Reader; -#if ENGINE_MINOR_VERSION >= 19 - // Get destLen - int32 DestinationLength = FUTF8ToTCHAR_Convert::ConvertedLength(Bytes, Size); - TCHAR* DestinationBuffer = new TCHAR[DestinationLength]; - - // CONVERT to TCHAR string - FUTF8ToTCHAR_Convert::Convert(DestinationBuffer, DestinationLength, Bytes, Size); - - int32 i = 0; - while (i < DestinationLength) - { - if (!Reader.Read(DestinationBuffer[i++])) - { - break; - } - } -#else const ANSICHAR* EndByte = Bytes + Size; while (Bytes < EndByte) { +#if ENGINE_MINOR_VERSION >= 19 + TCHAR Char = FUtf8Helper::CodepointFromUtf8(Bytes, EndByte - Bytes); +#else TCHAR Char = FUTF8ToTCHAR_Convert::utf8codepoint(&Bytes); - +#endif if (Char > 0xFFFF) { Char = UNICODE_BOGUS_CHAR_CODEPOINT; @@ -575,7 +564,6 @@ int32 UVaRestJsonObject::DeserializeFromUTF8Bytes(const ANSICHAR* Bytes, int32 S break; } } -#endif SetRootObject(Reader.State.Root); return Reader.State.Size; @@ -657,6 +645,9 @@ void UVaRestJsonObject::DecodeFromArchive(TUniquePtr& Reader) } } +////////////////////////////////////////////////////////////////////////// +// Serialize + bool UVaRestJsonObject::WriteToFile(const FString& Path) { TUniquePtr FileWriter(IFileManager::Get().CreateFileWriter(*Path)); @@ -710,6 +701,11 @@ bool UVaRestJsonObject::WriteToFile(const FString& Path) return true; } +bool UVaRestJsonObject::WriteToFilePath(const FString& Path, const bool bIsRelativeToProjectDir) +{ + return WriteToFile(bIsRelativeToProjectDir ? FPaths::ProjectDir() / Path : Path); +} + bool UVaRestJsonObject::WriteStringToArchive(FArchive& Ar, const TCHAR* StrPtr, int64 Len) { auto Src = StringCast(StrPtr, Len); diff --git a/Source/VaRestPlugin/Private/VaRestJsonParser.cpp b/Source/VaRestPlugin/Private/VaRestJsonParser.cpp index 6a83aedc..a3fe4b6c 100644 --- a/Source/VaRestPlugin/Private/VaRestJsonParser.cpp +++ b/Source/VaRestPlugin/Private/VaRestJsonParser.cpp @@ -1,10 +1,238 @@ // Copyright 2015-2019 Mail.Ru Group. All Rights Reserved. #include "VaRestJsonParser.h" + +#include "VaRestJsonObject.h" + #include "Dom/JsonObject.h" #include "Dom/JsonValue.h" #include "Logging/LogMacros.h" -#include "VaRestJsonObject.h" + +uint32 FUtf8Helper::CodepointFromUtf8(const ANSICHAR*& SourceString, const uint32 SourceLengthRemaining) +{ + checkSlow(SourceLengthRemaining > 0); + + const ANSICHAR* OctetPtr = SourceString; + + uint32 Codepoint = 0; + uint32 Octet = (uint32)((uint8)*SourceString); + uint32 Octet2, Octet3, Octet4; + + if (Octet < 128) // one octet char: 0 to 127 + { + ++SourceString; // skip to next possible start of codepoint. + return Octet; + } + else if (Octet < 192) // bad (starts with 10xxxxxx). + { + // Apparently each of these is supposed to be flagged as a bogus + // char, instead of just resyncing to the next valid codepoint. + ++SourceString; // skip to next possible start of codepoint. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + else if (Octet < 224) // two octets + { + // Ensure our string has enough characters to read from + if (SourceLengthRemaining < 2) + { + // Skip to end and write out a single char (we always have room for at least 1 char) + SourceString += SourceLengthRemaining; + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Octet -= (128 + 64); + Octet2 = (uint32)((uint8) * (++OctetPtr)); + if ((Octet2 & (128 + 64)) != 128) // Format isn't 10xxxxxx? + { + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Codepoint = ((Octet << 6) | (Octet2 - 128)); + if ((Codepoint >= 0x80) && (Codepoint <= 0x7FF)) + { + SourceString += 2; // skip to next possible start of codepoint. + return Codepoint; + } + } + else if (Octet < 240) // three octets + { + // Ensure our string has enough characters to read from + if (SourceLengthRemaining < 3) + { + // Skip to end and write out a single char (we always have room for at least 1 char) + SourceString += SourceLengthRemaining; + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Octet -= (128 + 64 + 32); + Octet2 = (uint32)((uint8) * (++OctetPtr)); + if ((Octet2 & (128 + 64)) != 128) // Format isn't 10xxxxxx? + { + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Octet3 = (uint32)((uint8) * (++OctetPtr)); + if ((Octet3 & (128 + 64)) != 128) // Format isn't 10xxxxxx? + { + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Codepoint = (((Octet << 12)) | ((Octet2 - 128) << 6) | ((Octet3 - 128))); + + // UTF-8 characters cannot be in the UTF-16 surrogates range + if (UE4StringConv_Private::IsHighSurrogate(Codepoint) || UE4StringConv_Private::IsLowSurrogate(Codepoint)) + { + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + // 0xFFFE and 0xFFFF are illegal, too, so we check them at the edge. + if ((Codepoint >= 0x800) && (Codepoint <= 0xFFFD)) + { + SourceString += 3; // skip to next possible start of codepoint. + return Codepoint; + } + } + else if (Octet < 248) // four octets + { + // Ensure our string has enough characters to read from + if (SourceLengthRemaining < 4) + { + // Skip to end and write out a single char (we always have room for at least 1 char) + SourceString += SourceLengthRemaining; + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Octet -= (128 + 64 + 32 + 16); + Octet2 = (uint32)((uint8) * (++OctetPtr)); + if ((Octet2 & (128 + 64)) != 128) // Format isn't 10xxxxxx? + { + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Octet3 = (uint32)((uint8) * (++OctetPtr)); + if ((Octet3 & (128 + 64)) != 128) // Format isn't 10xxxxxx? + { + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Octet4 = (uint32)((uint8) * (++OctetPtr)); + if ((Octet4 & (128 + 64)) != 128) // Format isn't 10xxxxxx? + { + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Codepoint = (((Octet << 18)) | ((Octet2 - 128) << 12) | + ((Octet3 - 128) << 6) | ((Octet4 - 128))); + if ((Codepoint >= 0x10000) && (Codepoint <= 0x10FFFF)) + { + SourceString += 4; // skip to next possible start of codepoint. + return Codepoint; + } + } + // Five and six octet sequences became illegal in rfc3629. + // We throw the codepoint away, but parse them to make sure we move + // ahead the right number of bytes and don't overflow the buffer. + else if (Octet < 252) // five octets + { + // Ensure our string has enough characters to read from + if (SourceLengthRemaining < 5) + { + // Skip to end and write out a single char (we always have room for at least 1 char) + SourceString += SourceLengthRemaining; + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Octet = (uint32)((uint8) * (++OctetPtr)); + if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + { + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Octet = (uint32)((uint8) * (++OctetPtr)); + if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + { + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Octet = (uint32)((uint8) * (++OctetPtr)); + if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + { + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Octet = (uint32)((uint8) * (++OctetPtr)); + if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + { + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + SourceString += 5; // skip to next possible start of codepoint. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + else // six octets + { + // Ensure our string has enough characters to read from + if (SourceLengthRemaining < 6) + { + // Skip to end and write out a single char (we always have room for at least 1 char) + SourceString += SourceLengthRemaining; + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Octet = (uint32)((uint8) * (++OctetPtr)); + if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + { + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Octet = (uint32)((uint8) * (++OctetPtr)); + if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + { + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Octet = (uint32)((uint8) * (++OctetPtr)); + if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + { + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Octet = (uint32)((uint8) * (++OctetPtr)); + if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + { + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + Octet = (uint32)((uint8) * (++OctetPtr)); + if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx? + { + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + SourceString += 6; // skip to next possible start of codepoint. + return UNICODE_BOGUS_CHAR_CODEPOINT; + } + + ++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue. + return UNICODE_BOGUS_CHAR_CODEPOINT; // catch everything else. +} FJSONState::FJSONState() : Notation(EJSONNotation::NONE) diff --git a/Source/VaRestPlugin/Private/VaRestJsonParser.h b/Source/VaRestPlugin/Private/VaRestJsonParser.h index ce25ff59..238491ff 100644 --- a/Source/VaRestPlugin/Private/VaRestJsonParser.h +++ b/Source/VaRestPlugin/Private/VaRestJsonParser.h @@ -4,6 +4,12 @@ #include "Json.h" +struct FUtf8Helper +{ + /** @See FUTF8ToTCHAR_Convert::CodepointFromUtf8 */ + static uint32 CodepointFromUtf8(const ANSICHAR*& SourceString, const uint32 SourceLengthRemaining); +}; + class FJsonValueNonConstArray : public FJsonValueArray { public: diff --git a/Source/VaRestPlugin/Private/VaRestLibrary.cpp b/Source/VaRestPlugin/Private/VaRestLibrary.cpp index 610d0db0..13893b64 100644 --- a/Source/VaRestPlugin/Private/VaRestLibrary.cpp +++ b/Source/VaRestPlugin/Private/VaRestLibrary.cpp @@ -43,12 +43,12 @@ bool UVaRestLibrary::Base64DecodeData(const FString& Source, TArray& Dest ////////////////////////////////////////////////////////////////////////// // File system integration -class UVaRestJsonObject* UVaRestLibrary::LoadJsonFromFile(UObject* WorldContextObject, const FString& Path) +class UVaRestJsonObject* UVaRestLibrary::LoadJsonFromFile(UObject* WorldContextObject, const FString& Path, const bool bIsRelativeToContentDir) { UVaRestJsonObject* Json = UVaRestJsonObject::ConstructJsonObject(WorldContextObject); FString JSONString; - if (FFileHelper::LoadFileToString(JSONString, *(FPaths::ProjectContentDir() + Path))) + if (FFileHelper::LoadFileToString(JSONString, *(bIsRelativeToContentDir ? FPaths::ProjectContentDir() / Path : Path))) { if (Json->DecodeJson(JSONString)) { diff --git a/Source/VaRestPlugin/Private/VaRestRequestJSON.cpp b/Source/VaRestPlugin/Private/VaRestRequestJSON.cpp index 168fe713..7e66c018 100644 --- a/Source/VaRestPlugin/Private/VaRestRequestJSON.cpp +++ b/Source/VaRestPlugin/Private/VaRestRequestJSON.cpp @@ -1,6 +1,7 @@ // Copyright 2014 Vladimir Alyamkin. All Rights Reserved. #include "VaRestRequestJSON.h" + #include "VaRestJsonObject.h" #include "VaRestLibrary.h" #include "VaRestPluginPrivatePCH.h" diff --git a/VaRestPlugin.uplugin b/VaRestPlugin.uplugin index 81e067ef..773ad259 100644 --- a/VaRestPlugin.uplugin +++ b/VaRestPlugin.uplugin @@ -2,8 +2,8 @@ "FileVersion" : 3, "FriendlyName" : "VaRest", - "Version" : 25, - "VersionName" : "1.1-r25", + "Version" : 26, + "VersionName" : "1.1-r26", "CreatedBy" : "Vladimir Alyamkin", "CreatedByURL" : "http://alyamkin.com", "EngineVersion" : "4.22.0",