Files
UnrealEngine/Engine/Source/Editor/BlueprintGraph/Private/BlueprintNodeSignature.cpp
2025-05-18 13:04:45 +08:00

207 lines
6.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "BlueprintNodeSignature.h"
#include "Containers/Array.h"
#include "Containers/StringConv.h"
#include "HAL/Platform.h"
#include "HAL/PlatformCrt.h"
#include "Misc/CString.h"
#include "Misc/SecureHash.h"
#include "Templates/Tuple.h"
/*******************************************************************************
* Static FBlueprintNodeSignature Helpers
******************************************************************************/
namespace BlueprintNodeSignatureImpl
{
static FString const SignatureOpeningStr("(");
static FString const SignatureElementDelim(",");
static FString const SignatureClosingStr(")");
static FString const SignatureKeyDelim("=");
static FName const LegacySubObjSignatureKey(TEXT("FieldName"));
/**
* Iterates through the given KeyMap and looks for name collisions. Will
* append an incrementing value until a collision is not detected.
*
* @param BaseName The base name you want some permutation of.
* @param KeyMap The map you want the name key for.
* @return A unique name key for the supplied map.
*/
static FName FindUniqueKeyName(FName BaseName, TMap<FName, FString>& KeyMap);
}
//------------------------------------------------------------------------------
static FName BlueprintNodeSignatureImpl::FindUniqueKeyName(FName BaseName, TMap<FName, FString>& KeyMap)
{
FName SingatureKey = BaseName;
int32 FNameIndex = 0;
while (KeyMap.Find(SingatureKey) != nullptr)
{
SingatureKey = FName(BaseName, ++FNameIndex);
}
return SingatureKey;
}
/*******************************************************************************
* FBlueprintNodeSignature
******************************************************************************/
//------------------------------------------------------------------------------
FBlueprintNodeSignature::FBlueprintNodeSignature(FString const& UserString)
{
using namespace BlueprintNodeSignatureImpl;
FString SanitizedSignature = UserString;
SanitizedSignature.RemoveFromStart(SignatureOpeningStr);
SanitizedSignature.RemoveFromEnd(SignatureClosingStr);
TArray<FString> SignatureElements;
SanitizedSignature.ParseIntoArray(SignatureElements, *SignatureElementDelim, 1);
for (FString& SignatureElement : SignatureElements)
{
SignatureElement.TrimStartInline();
FString SignatureKey, SignatureValue;
SignatureElement.Split(SignatureKeyDelim, &SignatureKey, &SignatureValue);
// @TODO: look for UObject redirects with SignatureValue
SignatureValue.RemoveFromStart(TEXT("\""), ESearchCase::CaseSensitive);
SignatureValue.RemoveFromEnd(TEXT("\""), ESearchCase::CaseSensitive);
AddNamedValue(FName(*SignatureKey), SignatureValue);
}
}
//------------------------------------------------------------------------------
FBlueprintNodeSignature::FBlueprintNodeSignature(TSubclassOf<UEdGraphNode> NodeClass)
{
SetNodeClass(NodeClass);
}
//------------------------------------------------------------------------------
void FBlueprintNodeSignature::SetNodeClass(TSubclassOf<UEdGraphNode> NodeClass)
{
static const FName NodeClassSignatureKey(TEXT("NodeName"));
if (NodeClass != nullptr)
{
AddNamedValue(NodeClassSignatureKey, NodeClass->GetPathName());
}
else
{
SignatureSet.Remove(NodeClassSignatureKey);
MarkDirty();
}
}
//------------------------------------------------------------------------------
void FBlueprintNodeSignature::AddSubObject(FFieldVariant SignatureObj)
{
// not ideal for generic "objects", but we have to keep in line with the
// old favorites system (for backwards compatibility)
using namespace BlueprintNodeSignatureImpl;
FName SubObjectSignatureKey = FindUniqueKeyName(LegacySubObjSignatureKey, SignatureSet);
AddNamedValue(SubObjectSignatureKey, SignatureObj.GetPathName());
}
//------------------------------------------------------------------------------
void FBlueprintNodeSignature::AddKeyValue(FString const& KeyValue)
{
// not ideal for some arbitrary value, but we have to keep in line with the
// old favorites system (for backwards compatibility)
using namespace BlueprintNodeSignatureImpl;
FName SignatureKey = FindUniqueKeyName(LegacySubObjSignatureKey, SignatureSet);
AddNamedValue(SignatureKey, KeyValue);
}
//------------------------------------------------------------------------------
void FBlueprintNodeSignature::AddNamedValue(FName SignatureKey, FString const& Value)
{
SignatureSet.Add(SignatureKey, Value);
MarkDirty();
}
//------------------------------------------------------------------------------
bool FBlueprintNodeSignature::IsValid() const
{
return (SignatureSet.Num() > 0);
}
//------------------------------------------------------------------------------
FString const& FBlueprintNodeSignature::ToString() const
{
using namespace BlueprintNodeSignatureImpl;
if (CachedSignatureString.IsEmpty() && IsValid())
{
TArray<FString> SignatureElements;
for (auto& SignatureElement : SignatureSet)
{
FString ObjSignature = SignatureElement.Key.ToString() + SignatureKeyDelim + TEXT("\"") + SignatureElement.Value + TEXT("\"");
SignatureElements.Add(ObjSignature);
}
SignatureElements.Sort();
CachedSignatureString = SignatureOpeningStr;
for (FString& SignatureElement : SignatureElements)
{
CachedSignatureString += SignatureElement + SignatureElementDelim;
}
CachedSignatureString.RemoveFromEnd(SignatureElementDelim);
CachedSignatureString += SignatureClosingStr;
}
return CachedSignatureString;
}
//------------------------------------------------------------------------------
FGuid const& FBlueprintNodeSignature::AsGuid() const
{
static const int32 BytesPerMd5Hash = 16;
static const int32 BytesPerGuidVal = 4;
static const int32 MembersPerGuid = 4;
static const int32 BitsPerByte = 8;
if (!CachedSignatureGuid.IsValid() && IsValid())
{
FString const& SignatureString = ToString();
FMD5 Md5Gen;
uint8 HashedBytes[BytesPerMd5Hash];
Md5Gen.Update((unsigned char*)TCHAR_TO_ANSI(*SignatureString), SignatureString.Len());
Md5Gen.Final(HashedBytes);
//FString HashedStr = FMD5::HashAnsiString(*SignatureString); // to check against
for (int32 GuidIndex = 0; GuidIndex < MembersPerGuid; ++GuidIndex)
{
int32 const Md5UpperIndex = (GuidIndex + 1) * BytesPerGuidVal - 1;
int32 const Md5LowerIndex = (Md5UpperIndex - BytesPerGuidVal) + 1;
int32 GuidValue = 0;
int32 BitOffset = 1;
for (int32 Md5ByteIndex = Md5UpperIndex; Md5ByteIndex >= Md5LowerIndex; --Md5ByteIndex)
{
GuidValue += HashedBytes[Md5ByteIndex] * BitOffset;
BitOffset <<= BitsPerByte;
}
CachedSignatureGuid[GuidIndex] = GuidValue;
}
}
return CachedSignatureGuid;
}
//------------------------------------------------------------------------------
void FBlueprintNodeSignature::MarkDirty()
{
CachedSignatureGuid.Invalidate();
CachedSignatureString.Empty();
}