// Copyright Epic Games, Inc. All Rights Reserved. #include "Misc/Build.h" #if !UE_BUILD_SHIPPING #include "HAL/IConsoleManager.h" #include "HAL/PlatformStackWalk.h" #include "Misc/FileHelper.h" #include "Misc/Paths.h" #include "Misc/SecureHash.h" #include "Serialization/ArchiveUObject.h" #include "Serialization/BufferArchive.h" #include "Serialization/JsonWriter.h" #include "String/ParseTokens.h" #include "UObject/Class.h" #include "UObject/EnumProperty.h" #include "UObject/Field.h" #include "UObject/MetaData.h" #include "UObject/ObjectMacros.h" #include "UObject/Package.h" #include "UObject/UnrealType.h" #include "UObject/UObjectIterator.h" #include "VerseVM/VVMClass.h" #include "VerseVM/VVMEnumeration.h" #include "VerseVM/VVMValue.h" #include "VerseVM/VVMVerseClass.h" #include "VerseVM/VVMVerseEnum.h" #include "VerseVM/VVMVerseFunction.h" #include "VerseVM/VVMVerseStruct.h" namespace UE::JsonUtilities { class FArchiveMD5Generator : public FArchiveUObject { FMD5 HashBuilder; public: FArchiveMD5Generator() { SetIsLoading(false); SetIsSaving(true); //SetIsPersistent(true); } virtual FArchive& operator<<(FName& Name) override { FString SavedString(Name.ToString()); *this << SavedString; return *this; } //~ Begin FArchive Interface virtual void Serialize(void* Data, int64 Length) override { HashBuilder.Update(static_cast(Data), Length); } virtual FString GetArchiveName() const override { return TEXT("UE::JsonUtilities::FArchiveMD5Generator"); } //~ End FArchive Interface FMD5Hash GetMD5() { FMD5Hash Result; Result.Set(HashBuilder); return Result; } }; class FJsonUETypeWriter : public TJsonStringWriter<> { public: explicit FJsonUETypeWriter(FString* Out) : TJsonStringWriter(Out, 0) { } }; template void WriteSortedObjectHelper(const TCHAR* PropertyName, const TArray& Array, FJsonUETypeWriter& Writer, GET_NAME_FUNC && GetNameFunc, WRITE_FUNC && WriteFunc) { if (Array.IsEmpty()) { return; } Writer.WriteObjectStart(PropertyName); TMap NameToIndex; for (int32 Index = 0; Index < Array.Num(); ++Index) { NameToIndex.Add(GetNameFunc(Array[Index]), Index); } TArray Keys; NameToIndex.GetKeys(Keys); Keys.Sort(); for (const FString& Key : Keys) { Writer.WriteObjectStart(Key); const int32 Index = *NameToIndex.Find(Key); WriteFunc(Array[Index]); Writer.WriteObjectEnd(); } Writer.WriteObjectEnd(); } // a simple way to dissect a flag bits array template FString UEnumFlagsToString(EnumType Flags) { //TArray Result; FString Result = UEnum::GetValueAsString(Flags); // only assume flags if we don't match one if (Result.IsEmpty() || Result == TEXT("None")) { for (int8 Idx = 0; Idx < 64; ++Idx) { const EnumType Flag = EnumType(1ull< TArray FindUsedBits(T Flags) { TArray Result; for (int8 Idx = 0; Idx < 64; ++Idx) { if (uint64(Flags) & (1ull< void WriteVerseVClass(const TCHAR* Name, const T& Class, FJsonUETypeWriter& Writer) { // TODO: how to access this } template void WriteVerseVShape(const TCHAR* Name, const T& Shape, FJsonUETypeWriter& Writer) { // TODO: how to access this } void WriteVerseVValue(const TCHAR* Name, const Verse::VValue& Value, FJsonUETypeWriter& Writer) { // TODO: how to access this } void WriteVerseVEnumeration(const TCHAR* Name, const Verse::VEnumeration& Enumeration, FJsonUETypeWriter& Writer) { // TODO: how to access this } #endif void WriteFFieldClass(const FFieldClass& FieldClass, FJsonUETypeWriter& Writer); void WriteFField(const FField* Field, FJsonUETypeWriter& Writer) { FArchiveMD5Generator MD5Archive; const_cast(Field)->Serialize(MD5Archive); // this is a fallback to just make sure that we catch all differences Writer.WriteValue(TEXT("MD5"), LexToString(MD5Archive.GetMD5())); Writer.WriteValue(TEXT("Name"), Field->GetName()); Writer.WriteValue(TEXT("ClassName"), Field->GetClass()->GetName()); Writer.WriteValue(TEXT("Flags"), LexToString(Field->GetFlags())); if (const FProperty* Property = CastField(Field)) { Writer.WriteValue(TEXT("ArrayDim"), Property->ArrayDim); Writer.WriteValue(TEXT("ElementSize"), Property->GetElementSize()); Writer.WriteValue(TEXT("PropertyFlags"), FindUsedBits(Property->PropertyFlags)); Writer.WriteValue(TEXT("RepIndex"), Property->RepIndex); Writer.WriteValue(TEXT("RepNotifyFunc"), Property->RepNotifyFunc.ToString()); Writer.WriteValue(TEXT("Offset_Internal"), Property->GetOffset_ForDebug()); if (const FBoolProperty* BoolProperty = CastField(Property)) { Writer.WriteValue(TEXT("FieldSize"), BoolProperty->GetBoolFieldSize()); Writer.WriteValue(TEXT("FieldMask"), BoolProperty->GetFieldMask()); Writer.WriteValue(TEXT("ByteOffset"), BoolProperty->GetByteOffset()); Writer.WriteValue(TEXT("ByteMask"), BoolProperty->GetByteMask()); } else if (const FEnumProperty* EnumProperty = CastField(Property)) { if (const FNumericProperty* UnderlyingProperty = EnumProperty->GetUnderlyingProperty()) { Writer.WriteObjectStart(TEXT("UnderlyingProperty")); WriteFField(UnderlyingProperty, Writer); Writer.WriteObjectEnd(); } if (const UEnum* Enum = EnumProperty->GetEnum()) { Writer.WriteValue(TEXT("Enum"), Enum->GetPathName()); } } else if (const FObjectPropertyBase* ObjectPropertyBase = CastField(Property)) { if (ObjectPropertyBase->PropertyClass) { Writer.WriteValue(TEXT("PropertyClass"), ObjectPropertyBase->PropertyClass->GetName()); } if (const FClassProperty* ClassProperty = CastField(ObjectPropertyBase)) { if (ClassProperty->MetaClass) { Writer.WriteValue(TEXT("MetaClass"), ClassProperty->MetaClass->GetName()); } } else if (const FSoftClassProperty* SoftClassProperty = CastField(ObjectPropertyBase)) { if (SoftClassProperty->MetaClass) { Writer.WriteValue(TEXT("MetaClass"), SoftClassProperty->MetaClass->GetName()); } } } else if (const FStructProperty* StructProperty = CastField(Property)) { if (StructProperty->Struct) { Writer.WriteValue(TEXT("Struct"), StructProperty->Struct->GetPathName()); } } else if (const FMulticastDelegateProperty* MulticastDelegateProperty = CastField(Property)) { if (MulticastDelegateProperty->SignatureFunction) { Writer.WriteValue(TEXT("SignatureFunction"), MulticastDelegateProperty->SignatureFunction->GetPathName()); } } else if (const FByteProperty* ByteProperty = CastField(Property)) { if (ByteProperty->Enum) { Writer.WriteValue(TEXT("Enum"), ByteProperty->Enum.GetPathName()); } } else if (const FMapProperty* MapProperty = CastField(Property)) { if (MapProperty->KeyProp) { Writer.WriteObjectStart(TEXT("KeyProp")); WriteFField(MapProperty->KeyProp, Writer); Writer.WriteObjectEnd(); } if (MapProperty->ValueProp) { Writer.WriteObjectStart(TEXT("ValueProp")); WriteFField(MapProperty->ValueProp, Writer); Writer.WriteObjectEnd(); } Writer.WriteValue(TEXT("MapFlags"), FindUsedBits(MapProperty->MapFlags)); Writer.WriteObjectStart(TEXT("MapLayout")); WriteFScriptMapLayout(MapProperty->MapLayout, Writer); Writer.WriteObjectEnd(); } else if (const FSetProperty* SetProperty = CastField(Property)) { if (SetProperty->ElementProp) { Writer.WriteObjectStart(TEXT("ElementProp")); WriteFField(SetProperty->ElementProp, Writer); Writer.WriteObjectEnd(); } Writer.WriteObjectStart(TEXT("SetLayout")); WriteFScriptSetLayout(SetProperty->SetLayout, Writer); Writer.WriteObjectEnd(); } else if (const FDelegateProperty* DelegateProperty = CastField(Property)) { if (DelegateProperty->SignatureFunction) { Writer.WriteValue(TEXT("SignatureFunction"), DelegateProperty->SignatureFunction->GetPathName()); } } else if (const FArrayProperty* ArrayProperty = CastField(Property)) { Writer.WriteValue(TEXT("ArrayFlags"), FindUsedBits(ArrayProperty->ArrayFlags)); if (ArrayProperty->Inner) { Writer.WriteObjectStart(TEXT("Inner")); WriteFField(ArrayProperty->Inner, Writer); Writer.WriteObjectEnd(); } } else if (const FOptionalProperty* OptionalProperty = CastField(Property)) { Writer.WriteObjectStart(TEXT("ValueProperty")); WriteFField(OptionalProperty->GetValueProperty(), Writer); Writer.WriteObjectEnd(); } else if (const FInterfaceProperty* InterfaceProperty = CastField(Property)) { if (InterfaceProperty->InterfaceClass) { Writer.WriteValue(TEXT("InterfaceClass"), InterfaceProperty->InterfaceClass->GetPathName()); } } } } void WriteFFieldClass(const FFieldClass& FieldClass, FJsonUETypeWriter& Writer) { Writer.WriteValue(TEXT("Name"), FieldClass.GetName()); Writer.WriteValue(TEXT("Id"), FieldClass.GetId()); Writer.WriteValue(TEXT("CastFlags"), FindUsedBits(FieldClass.GetCastFlags())); // just a way to bypass private restrictions EClassFlags ClassFlags = CLASS_None; for (int32 Bit = 0; Bit < 32; ++Bit) { EClassFlags TestFlag = EClassFlags(1<GetName()); } if (ClassFlags != CLASS_None) { Writer.WriteObjectStart(TEXT("DefaultObject")); WriteFField(const_cast(FieldClass).GetDefaultObject(), Writer); Writer.WriteObjectEnd(); } } void WriteUField(const UField* Field, FJsonUETypeWriter& Writer) { FArchiveMD5Generator MD5Archive; const_cast(Field)->Serialize(MD5Archive); // this is a fallback to just make sure that we catch all differences Writer.WriteValue(TEXT("MD5"), LexToString(MD5Archive.GetMD5())); Writer.WriteObjectStart(TEXT("UObject")); Writer.WriteValue(TEXT("Name"), Field->GetName()); Writer.WriteValue(TEXT("Flags"), LexToString(Field->GetFlags())); Writer.WriteObjectEnd(); #if WITH_METADATA Writer.WriteObjectStart(TEXT("UField")); if (const TMap* MetaData = FMetaData::GetMapForObject(Field)) { if (!MetaData->IsEmpty()) { Writer.WriteObjectStart(TEXT("MetaData")); TArray Keys; MetaData->GetKeys(Keys); Keys.Sort([](const FName& A, const FName& B){ return A.Compare(B) < 0; }); for (const FName& Key : Keys) { Writer.WriteValue(*Key.ToString(), *MetaData->Find(Key)); } Writer.WriteObjectEnd(); } } Writer.WriteObjectEnd(); #endif if (const UEnum* Enum = Cast(Field)) { Writer.WriteObjectStart(TEXT("UEnum")); Writer.WriteValue(TEXT("CppType"), Enum->CppType); Writer.WriteValue(TEXT("CppForm"), int32(Enum->GetCppForm())); Writer.WriteValue(TEXT("EnumFlags_Flags"), Enum->HasAnyEnumFlags(EEnumFlags::Flags)); Writer.WriteValue(TEXT("EnumFlags_NewerVersionExists"), Enum->HasAnyEnumFlags(EEnumFlags::NewerVersionExists)); TArray> EnumValues; for (int32 Index = 0; Index < Enum->NumEnums(); ++Index) { const FName Name = Enum->GetNameByIndex(Index); if (Name == NAME_None) { continue; } const int64 Value = Enum->GetValueByIndex(Index); EnumValues.Add({Name, Value}); } EnumValues.Sort([](const TPair& A, const TPair& B) { return A.Value < B.Value; }); Writer.WriteObjectStart(TEXT("Names")); for (const TPair& Pair : EnumValues) { Writer.WriteObjectStart(Pair.Key.ToString()); Writer.WriteValue(TEXT("Value"), Pair.Value); // this exists to indirectly ensure that the display name fn was set correctly Writer.WriteValue(TEXT("DisplayName"), Enum->GetDisplayNameTextByValue(Pair.Value).ToString()); Writer.WriteObjectEnd(); } Writer.WriteObjectEnd(); if (const UVerseEnum* VerseEnum = Cast(Enum)) { Writer.WriteObjectStart(TEXT("UVerseEnum")); Writer.WriteValue(TEXT("VerseEnumFlags"), UEnumFlagsToString(VerseEnum->VerseEnumFlags)); Writer.WriteValue(TEXT("QualifiedName"), VerseEnum->QualifiedName); #if WITH_VERSE_VM || defined(__INTELLISENSE__) WriteVerseVEnumeration(TEXT("Enumeration"), *VerseEnum->Enumeration, Writer); #endif Writer.WriteObjectEnd(); } Writer.WriteObjectEnd(); } else if (const UStruct* Struct = Cast(Field)) { Writer.WriteObjectStart(TEXT("UStruct")); if (UStruct* SuperStruct = Struct->GetSuperStruct()) { Writer.WriteValue(TEXT("SuperStruct"), SuperStruct->GetName()); } { TArray Children; for (const UField* Current = Struct->Children.Get(); Current; Current = Current->Next) { Children.Add(Current->GetPathName()); } Children.Sort(); Writer.WriteValue(TEXT("Children"), Children); } { TArray ChildProperties; for (const FField* Current = Struct->ChildProperties; Current; Current = Current->Next) { ChildProperties.Add(Current); } WriteSortedObjectHelper( TEXT("ChildProperties"), ChildProperties, Writer, [](const FField* Field) { return Field->GetFullName(); }, [&Writer](const FField* Field) { WriteFField(Field, Writer); } ); } Writer.WriteValue(TEXT("PropertiesSize"), Struct->PropertiesSize); Writer.WriteValue(TEXT("MinAlignment"), Struct->MinAlignment); #if DO_CHECK // TODO: this isn't exported, is it needed? //Writer.WriteValue(TEXT("IsPropertyChainReady"), Struct->DebugIsPropertyChainReady()); #endif TArray PathNames; for (const FProperty* Current = Struct->PropertyLink; Current; Current = Current->PropertyLinkNext) { PathNames.Add(Current->GetFullName()); } PathNames.Sort(); Writer.WriteValue(TEXT("PropertyLinks"), PathNames); PathNames.Reset(); for (const FProperty* Current = Struct->RefLink; Current; Current = Current->NextRef) { PathNames.Add(Current->GetFullName()); } PathNames.Sort(); Writer.WriteValue(TEXT("RefLinks"), PathNames); PathNames.Reset(); for (const FProperty* Current = Struct->DestructorLink; Current; Current = Current->DestructorLinkNext) { PathNames.Add(Current->GetFullName()); } PathNames.Sort(); Writer.WriteValue(TEXT("DestructorLinks"), PathNames); PathNames.Reset(); for (const FProperty* Current = Struct->PostConstructLink; Current; Current = Current->PostConstructLinkNext) { PathNames.Add(Current->GetFullName()); } PathNames.Sort(); Writer.WriteValue(TEXT("PostConstructLinks"), PathNames); PathNames.Reset(); for (const TObjectPtr& Object : Struct->ScriptAndPropertyObjectReferences) { if (Object) { PathNames.Add(Object->GetPathName()); } else { PathNames.Add(TEXT("(null)")); } } PathNames.Sort(); Writer.WriteValue(TEXT("ScriptAndPropertyObjectReferences"), PathNames); if (Struct->UnresolvedScriptProperties) { Writer.WriteArrayStart(TEXT("UnresolvedScriptProperties")); for (const TPair, int32>& UnresolvedScriptProperty : *Struct->UnresolvedScriptProperties) { Writer.WriteObjectStart(); Writer.WriteValue(TEXT("Path"), UnresolvedScriptProperty.Key.ToString()); Writer.WriteValue(TEXT("Value"), UnresolvedScriptProperty.Value); Writer.WriteObjectEnd(); } Writer.WriteArrayEnd(); } #if WITH_EDITORONLY_DATA PathNames.Reset(); for (const TObjectPtr& PropertyWrapper : Struct->PropertyWrappers) { PathNames.Add(PropertyWrapper->GetPathName()); } PathNames.Sort(); Writer.WriteValue(TEXT("PropertyWrappers"), PathNames); Writer.WriteValue(TEXT("TotalFieldCount"), Struct->TotalFieldCount); Writer.WriteValue(TEXT("HasAssetRegistrySearchableProperties"), Struct->HasAssetRegistrySearchableProperties()); #endif Writer.WriteObjectEnd(); // UStruct if (const UScriptStruct* ScriptStruct = Cast(Struct)) { Writer.WriteObjectStart(TEXT("UScriptStruct")); Writer.WriteValue(TEXT("StructFlags"), FindUsedBits(ScriptStruct->StructFlags)); Writer.WriteValue(TEXT("CustomGuid"), ScriptStruct->GetCustomGuid().ToString()); Writer.WriteValue(TEXT("StructCppName"), ScriptStruct->GetStructCPPName()); if (const UScriptStruct::ICppStructOps* StructOps = ScriptStruct->GetCppStructOps()) { Writer.WriteObjectStart(TEXT("StructOps")); Writer.WriteValue(TEXT("Size"), StructOps->GetSize()); Writer.WriteValue(TEXT("Alignment"), StructOps->GetAlignment()); const UScriptStruct::ICppStructOps::FCapabilities Capabilities = StructOps->GetCapabilities(); Writer.WriteObjectStart(TEXT("Capabilities")); Writer.WriteValue(TEXT("ComputedPropertyFlags"), FindUsedBits(Capabilities.ComputedPropertyFlags)); Writer.WriteValue(TEXT("HasSerializerObjectReferences"), LexToString(Capabilities.HasSerializerObjectReferences)); Writer.WriteValue(TEXT("HasNoopConstructor"), Capabilities.HasNoopConstructor); Writer.WriteValue(TEXT("HasZeroConstructor"), Capabilities.HasZeroConstructor); Writer.WriteValue(TEXT("HasDestructor"), Capabilities.HasDestructor); Writer.WriteValue(TEXT("HasSerializer"), Capabilities.HasSerializer); Writer.WriteValue(TEXT("HasStructuredSerializer"), Capabilities.HasStructuredSerializer); Writer.WriteValue(TEXT("HasPostSerialize"), Capabilities.HasPostSerialize); Writer.WriteValue(TEXT("HasNetSerializer"), Capabilities.HasNetSerializer); Writer.WriteValue(TEXT("HasNetSharedSerialization"), Capabilities.HasNetSharedSerialization); Writer.WriteValue(TEXT("HasNetDeltaSerializer"), Capabilities.HasNetDeltaSerializer); Writer.WriteValue(TEXT("HasPostScriptConstruct"), Capabilities.HasPostScriptConstruct); Writer.WriteValue(TEXT("IsPlainOldData"), Capabilities.IsPlainOldData); Writer.WriteValue(TEXT("IsUECoreType"), Capabilities.IsUECoreType); Writer.WriteValue(TEXT("IsUECoreVariant"), Capabilities.IsUECoreVariant); Writer.WriteValue(TEXT("HasCopy"), Capabilities.HasCopy); Writer.WriteValue(TEXT("HasIdentical"), Capabilities.HasIdentical); Writer.WriteValue(TEXT("HasExportTextItem"), Capabilities.HasExportTextItem); Writer.WriteValue(TEXT("HasImportTextItem"), Capabilities.HasImportTextItem); Writer.WriteValue(TEXT("HasAddStructReferencedObjects"), Capabilities.HasAddStructReferencedObjects); Writer.WriteValue(TEXT("HasSerializeFromMismatchedTag"), Capabilities.HasSerializeFromMismatchedTag); Writer.WriteValue(TEXT("HasStructuredSerializeFromMismatchedTag"), Capabilities.HasStructuredSerializeFromMismatchedTag); Writer.WriteValue(TEXT("HasGetTypeHash"), Capabilities.HasGetTypeHash); Writer.WriteValue(TEXT("HasIntrusiveUnsetOptionalState"), Capabilities.HasIntrusiveUnsetOptionalState); Writer.WriteValue(TEXT("IsAbstract"), Capabilities.IsAbstract); Writer.WriteValue(TEXT("HasFindInnerPropertyInstance"), Capabilities.HasFindInnerPropertyInstance); Writer.WriteValue(TEXT("ClearOnFinishDestroy"), Capabilities.ClearOnFinishDestroy); #if WITH_EDITOR Writer.WriteValue(TEXT("HasCanEditChange"), Capabilities.HasCanEditChange); #endif Writer.WriteValue(TEXT("HasVisitor"), Capabilities.HasVisitor); Writer.WriteObjectEnd(); Writer.WriteObjectEnd(); } if (const UVerseStruct* VerseStruct = Cast(Struct)) { Writer.WriteObjectStart(TEXT("VerseStruct")); Writer.WriteValue(TEXT("VerseClassFlags"), FindUsedBits(VerseStruct->VerseClassFlags)); Writer.WriteValue(TEXT("QualifiedName"), VerseStruct->QualifiedName); if (VerseStruct->InitFunction) { Writer.WriteValue(TEXT("InitFunction"), VerseStruct->InitFunction->GetPathName()); } if (VerseStruct->ModuleClass) { Writer.WriteValue(TEXT("ModuleClass"), VerseStruct->ModuleClass->GetPathName()); } Writer.WriteValue(TEXT("Guid"), VerseStruct->Guid.ToString()); if (VerseStruct->FactoryFunction) { Writer.WriteValue(TEXT("FactoryFunction"), VerseStruct->FactoryFunction->GetPathName()); } if (VerseStruct->OverrideFactoryFunction) { Writer.WriteValue(TEXT("OverrideFactoryFunction"), VerseStruct->OverrideFactoryFunction->GetPathName()); } Writer.WriteValue(TEXT("ConstructorEffects"), UEnumFlagsToString(VerseStruct->ConstructorEffects)); #if WITH_VERSE_VM || defined(__INTELLISENSE__) WriteVerseVClass(TEXT("Class"), VerseStruct->Class, Writer); WriteVerseVShape(TEXT("Shape"), VerseStruct->Shape, Writer); #endif Writer.WriteObjectEnd(); } Writer.WriteObjectEnd(); } else if (const UFunction* Function = Cast(Struct)) { Writer.WriteObjectStart(TEXT("UFunction")); Writer.WriteValue(TEXT("FunctionFlags"), FindUsedBits(Function->FunctionFlags)); Writer.WriteValue(TEXT("NumParms"), Function->NumParms); Writer.WriteValue(TEXT("ParmsSize"), Function->ParmsSize); Writer.WriteValue(TEXT("ReturnValueOffset"), Function->ReturnValueOffset); Writer.WriteValue(TEXT("RPCId"), Function->RPCId); Writer.WriteValue(TEXT("RPCResponseId"), Function->RPCResponseId); if (Function->FirstPropertyToInit) { Writer.WriteValue(TEXT("FirstPropertyToInit"), Function->GetFullName()); } #if UE_BLUEPRINT_EVENTGRAPH_FASTCALLS if (Function->EventGraphFunction) { Writer.WriteValue(TEXT("EventGraphFunction"), Function->EventGraphFunction->GetPathName()); } Writer.WriteValue(TEXT("EventGraphCallOffset"), Function->EventGraphCallOffset); #endif #if WITH_LIVE_CODING if (Function->SingletonPtr) { // just a guess that this is what is intended Writer.WriteValue(TEXT("SingletonPtrEqualsThis"), *Function->SingletonPtr == Function); } #endif ANSICHAR NativeFuncName[512] = {0}; if (Function->GetNativeFunc() == nullptr) { Writer.WriteValue(TEXT("NativeFunc"), TEXT("nullptr")); } else { if (FPlatformStackWalk::ProgramCounterToHumanReadableString( 0, uint64(Function->GetNativeFunc()), NativeFuncName, 512, nullptr )) { FString NativeFuncString(ANSI_TO_TCHAR(NativeFuncName)); TArray NamePartStrings; UE::String::ParseTokens(NativeFuncString, TEXT(" "), [&NamePartStrings](FStringView Token) { NamePartStrings.Add(Token); }); if (NamePartStrings.Num() > 1) { // this is stripping off the address prefix Writer.WriteValue(TEXT("NativeFunc"), NamePartStrings[1]); } else { Writer.WriteValue(TEXT("NativeFunc"), NativeFuncString); } } else { Writer.WriteValue(TEXT("NativeFunc"), TEXT("Unknown Set Function")); } } Writer.WriteObjectEnd(); if (const USparseDelegateFunction* SparseDelegateFunction = Cast(Function)) { Writer.WriteObjectStart(TEXT("USparseDelegateFunction")); Writer.WriteValue(TEXT("OwningClassName"), SparseDelegateFunction->OwningClassName.ToString()); Writer.WriteValue(TEXT("DelegateName"), SparseDelegateFunction->DelegateName.ToString()); Writer.WriteObjectEnd(); } if (const UVerseFunction* VerseFunction = Cast(Function)) { Writer.WriteObjectStart(TEXT("UVerseFunction")); Writer.WriteValue(TEXT("AlternateName"), VerseFunction->AlternateName.ToString()); Writer.WriteValue(TEXT("VerseFunctionFlags"), UEnumFlagsToString(VerseFunction->VerseFunctionFlags)); #if WITH_VERSE_VM || defined(__INTELLISENSE__) WriteVerseVValue(TEXT("Callee"), VerseFunction->Callee.Get(), Writer); #endif Writer.WriteObjectEnd(); } } else if (const UClass* Class = Cast(Struct)) { Writer.WriteObjectStart(TEXT("UClass")); Writer.WriteValue(TEXT("bCooked"), Class->bCooked); Writer.WriteValue(TEXT("bLayoutChanging"), Class->bLayoutChanging); Writer.WriteValue(TEXT("ClassFlags"), FindUsedBits(Class->ClassFlags)); Writer.WriteValue(TEXT("ClassCastFlags"), FindUsedBits(Class->ClassCastFlags)); if (Class->ClassWithin) { Writer.WriteValue(TEXT("ClassWithin"), Class->ClassWithin->GetPathName()); } Writer.WriteValue(TEXT("ClassConfigName"), Class->ClassConfigName.ToString()); WriteSortedObjectHelper( TEXT("ClassReps"), Class->ClassReps, Writer, [](const FRepRecord& Record) { return Record.Property->GetFullName(); }, [&Writer](const FRepRecord& Record) { Writer.WriteValue(TEXT("Index"), Record.Index); } ); TArray NetFields; for (const UField* NetField : Class->NetFields) { NetFields.Add(NetField->GetPathName()); } NetFields.Sort(); Writer.WriteValue(TEXT("NetFields"), NetFields); if (const UScriptStruct* SparseClassDataStruct = Class->GetSparseClassDataStruct()) { Writer.WriteValue(TEXT("SparseClassDataStruct"), SparseClassDataStruct->GetPathName()); } #if WITH_EDITOR if (const ICppClassTypeInfo* CppTypeInfo = Class->GetCppTypeInfo()) { Writer.WriteObjectStart(TEXT("CppTypeInfo")); Writer.WriteValue(TEXT("IsAbstract"), CppTypeInfo->IsAbstract()); Writer.WriteObjectEnd(); } #endif if (const UVerseClass* VerseClass = Cast(Class)) { Writer.WriteObjectStart(TEXT("UVerseClass")); Writer.WriteValue(TEXT("SolClassFlags"), FindUsedBits(VerseClass->SolClassFlags)); Writer.WriteArrayStart(TEXT("TaskClasses")); for (const TObjectPtr& TaskClass : VerseClass->TaskClasses) { if (TaskClass) { Writer.WriteValue(TaskClass->GetPathName()); } else { Writer.WriteValue(TEXT("null")); } } Writer.WriteArrayEnd(); if (VerseClass->InitInstanceFunction) { Writer.WriteValue(TEXT("InitInstanceFunction"), VerseClass->InitInstanceFunction->GetPathName()); } Writer.WriteArrayStart(TEXT("PersistentVars")); for (const FVersePersistentVar& PersistentVar : VerseClass->PersistentVars) { Writer.WriteObjectStart(); Writer.WriteValue(TEXT("Path"), PersistentVar.Path); Writer.WriteValue(TEXT("Property"), PersistentVar.Property.ToString()); Writer.WriteObjectEnd(); } Writer.WriteArrayEnd(); Writer.WriteArrayStart(TEXT("SessionVars")); for (const FVerseSessionVar& SessionVar : VerseClass->SessionVars) { Writer.WriteValue(SessionVar.Property.ToString()); } Writer.WriteArrayEnd(); Writer.WriteObjectStart(TEXT("VarAccessors")); for (const TPair& Pair : VerseClass->VarAccessors) { Writer.WriteObjectStart(Pair.Key.ToString()); auto WriteFVerseClassVarAccessorKeyValue = [&Writer](const TPair& AccessorPair) { Writer.WriteObjectStart(FString::Printf(TEXT("%d"), AccessorPair.Key)); if (AccessorPair.Value.Func) { Writer.WriteValue(TEXT("Func"), AccessorPair.Value.Func->GetPathName()); } Writer.WriteValue(TEXT("bIsInstanceMember"), AccessorPair.Value.bIsInstanceMember); Writer.WriteValue(TEXT("bIsFallible"), AccessorPair.Value.bIsFallible); Writer.WriteObjectEnd(); }; Writer.WriteObjectStart(TEXT("Getters")); for (const TPair& Getter : Pair.Value.Getters) { WriteFVerseClassVarAccessorKeyValue(Getter); } Writer.WriteObjectEnd(); Writer.WriteObjectStart(TEXT("Setters")); for (const TPair& Setter : Pair.Value.Setters) { WriteFVerseClassVarAccessorKeyValue(Setter); } Writer.WriteObjectEnd(); Writer.WriteObjectEnd(); } Writer.WriteObjectEnd(); Writer.WriteValue(TEXT("ConstructorEffects"), UEnumFlagsToString(VerseClass->ConstructorEffects)); Writer.WriteValue(TEXT("MangledPackageVersePath"), VerseClass->MangledPackageVersePath.ToString()); Writer.WriteValue(TEXT("PackageRelativeVersePath"), VerseClass->PackageRelativeVersePath); auto WriteNameMap = [&Writer](const TCHAR* ObjectName, const TMap& NameMap) { if (NameMap.IsEmpty()) { return; } Writer.WriteObjectStart(ObjectName); for (const TPair& Pair : NameMap) { Writer.WriteValue(Pair.Key.ToString(), Pair.Value.ToString()); } Writer.WriteObjectEnd(); }; WriteNameMap(TEXT("DisplayNameToUENameFunctionMap"), VerseClass->DisplayNameToUENameFunctionMap); Writer.WriteArrayStart(TEXT("DirectInterfaces")); for (const TObjectPtr& DirectInterface : VerseClass->DirectInterfaces) { if (DirectInterface) { Writer.WriteValue(DirectInterface->GetPathName()); } else { Writer.WriteValue(TEXT("null")); } } Writer.WriteArrayEnd(); if (!VerseClass->PropertiesWrittenByInitCDO.IsEmpty()) { Writer.WriteArrayStart(TEXT("PropertiesWrittenByInitCDO")); for (const TFieldPath& Path : VerseClass->PropertiesWrittenByInitCDO) { Writer.WriteValue(Path.ToString()); } Writer.WriteArrayEnd(); } WriteNameMap(TEXT("FunctionMangledNames"), VerseClass->FunctionMangledNames); if (!VerseClass->PredictsFunctionNames.IsEmpty()) { Writer.WriteArrayStart(TEXT("PredictsFunctionNames")); for (FName Name : VerseClass->PredictsFunctionNames) { Writer.WriteValue(Name.ToString()); } Writer.WriteArrayEnd(); } #if WITH_VERSE_COMPILER && WITH_EDITORONLY_DATA Writer.WriteValue(TEXT("PreviousPathName"), VerseClass->PreviousPathName); #endif #if WITH_VERSE_VM || defined(__INTELLISENSE__) WriteVerseVClass(TEXT("Class"), VerseClass->Class, Writer); WriteVerseVShape(TEXT("Shape"), VerseClass->Shape, Writer); #endif Writer.WriteObjectEnd(); } Writer.WriteObjectEnd(); } } } // if bMonolithic is true, then "Filename" is the base folder name void DumpUETypeInformation(const FString& Filename, bool bMonolithic, bool bOnlyCompiledIn) { TMap> FieldsByPackage; for (TObjectIterator It; It; ++It) { FieldsByPackage.FindOrAdd(It->GetPackage()).Add(*It); } // now write them in a per-package json FString MonolithicOutputString; { FJsonUETypeWriter MonolithicWriter(&MonolithicOutputString); MonolithicWriter.WriteObjectStart(); MonolithicWriter.WriteObjectStart(TEXT("UFields")); for (TPair>& Pair : FieldsByPackage) { if (bOnlyCompiledIn && !Pair.Key->HasAnyPackageFlags(PKG_CompiledIn)) { continue; } FString FileOutputString; FJsonUETypeWriter PackageWriter(&FileOutputString); FJsonUETypeWriter& Writer = bMonolithic ? MonolithicWriter : PackageWriter; if (bMonolithic) { Writer.WriteObjectStart(Pair.Key->GetName()); } else { Writer.WriteObjectStart(); } TArray& AllFields = Pair.Value; AllFields.Sort([](const UField& A, const UField& B){ return A.GetPathName().Compare(B.GetPathName()) < 0; }); for (const UField* Field : AllFields) { Writer.WriteObjectStart(*Field->GetPathName()); WriteUField(Field, Writer); Writer.WriteObjectEnd(); } Writer.WriteObjectEnd(); if (!bMonolithic) { PackageWriter.Close(); FString DumpFilename = FPaths::ProjectSavedDir() / Filename / FString::Printf(TEXT("%s.json"), *Pair.Key->GetPathName()); FFileHelper::SaveStringToFile(FileOutputString, *DumpFilename, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM); } } MonolithicWriter.WriteObjectEnd(); // UFields // dump all field classes to a separate file/chunk { FString FileOutputString; FJsonUETypeWriter SplitWriter(&FileOutputString); FJsonUETypeWriter& Writer = bMonolithic ? MonolithicWriter : SplitWriter; if (bMonolithic) { Writer.WriteObjectStart(TEXT("FFieldClasses")); } else { Writer.WriteObjectStart(); } TArray FieldClasses = FFieldClass::GetAllFieldClasses(); FieldClasses.Sort([](FFieldClass& A, FFieldClass& B) { return A.GetName().Compare(B.GetName()) < 0; }); for (FFieldClass* FieldClass : FieldClasses) { Writer.WriteObjectStart(FieldClass->GetName()); WriteFFieldClass(*FieldClass, Writer); Writer.WriteObjectEnd(); } Writer.WriteObjectEnd(); if (!bMonolithic) { SplitWriter.Close(); FString DumpFilename = FPaths::ProjectSavedDir() / Filename / TEXT("FieldClasses.json"); FFileHelper::SaveStringToFile(FileOutputString, *DumpFilename, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM); } } MonolithicWriter.WriteObjectEnd(); // root object MonolithicWriter.Close(); } if (bMonolithic) { FString DumpFilename = FPaths::ProjectSavedDir() / Filename; FFileHelper::SaveStringToFile(MonolithicOutputString, *DumpFilename, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM); } } FAutoConsoleCommand GDumpUETypeInformation( TEXT("JsonUtilities.DumpUETypeInformation"), TEXT("Dumps all of the type information into a monolithic json file"), FConsoleCommandWithArgsDelegate::CreateLambda( [](const TArray& Args ) { if (Args.Num() == 0) { return; } bool bOnlyCompiledIn = false; if (Args.Num() > 1) { LexFromString(bOnlyCompiledIn, *Args[1]); } DumpUETypeInformation(Args[0], true, bOnlyCompiledIn); }) ); FAutoConsoleCommand GDumpSplitUETypeInformation( TEXT("JsonUtilities.DumpSplitUETypeInformation"), TEXT("Dumps all of the type information into a folder"), FConsoleCommandWithArgsDelegate::CreateLambda( [](const TArray& Args ) { if (Args.Num() == 0) { return; } bool bOnlyCompiledIn = false; if (Args.Num() > 1) { LexFromString(bOnlyCompiledIn, *Args[1]); } DumpUETypeInformation(Args[0], false, bOnlyCompiledIn); }) ); } // namespace UE::JsonUtilities #endif // !UE_BUILD_SHIPPING