Last active
May 29, 2024 12:45
-
-
Save actboy168/7e88d7d5149f18f409ebc7ea0b008661 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <format> | |
#include "UObject/EnumProperty.h" | |
#include "UObject/UObjectIterator.h" | |
template <> | |
struct std::formatter<FString, char> : public std::formatter<std::string, char> { | |
template <class FmtContext> | |
FmtContext::iterator format(const FString& s, FmtContext& ctx) const noexcept { | |
auto conv = FTCHARToUTF8(*s); | |
std::string utf8 { conv.Get(), (size_t)conv.Length() }; | |
return std::ranges::copy(utf8, ctx.out()).out; | |
} | |
}; | |
template <typename... T> | |
static void FileWrite(FILE* f, std::format_string<T...> fmt, T&&... args) noexcept { | |
auto str = std::format(fmt, std::forward<T>(args)...); | |
fwrite(str.data(), sizeof(char), str.size(), f); | |
} | |
static FString StrToIdent(const FString& str) noexcept { | |
FString retval = str; | |
FString postfix = TEXT(""); | |
for (auto& c : retval) { | |
if (!::IsValidCPPIdentifierChar(c)) { | |
postfix.Append(::ToValidCPPIdentifierChars(c)); | |
c = TCHAR('x'); | |
} | |
} | |
return retval + postfix; | |
} | |
static FString PathToIdent(const FString& str) noexcept { | |
FString retval = str; | |
for (auto& c : retval) { | |
if (!::IsValidCPPIdentifierChar(c)) { | |
c = TCHAR('.'); | |
} | |
} | |
return retval; | |
} | |
static FString GetPropClassName(FProperty* base_prop) noexcept; | |
static FString GetStructName(UStruct* u) noexcept { | |
if (auto us = Cast<UScriptStruct>(u)) { | |
return TEXT("UE") + PathToIdent(u->GetPathName()); | |
} | |
return TEXT("UE.") + StrToIdent(u->GetName()); | |
} | |
static FString GetEnumName(UEnum* u) noexcept { | |
FString r = u->CppType; | |
int32 pos = r.Find(TEXT("::"), ESearchCase::CaseSensitive); | |
if (pos != INDEX_NONE) { | |
r = r.Left(pos); | |
} | |
return TEXT("UE.") + r; | |
} | |
static FString GetFunctionName(UFunction* u) noexcept { | |
TArray<FString> parmArray; | |
FString retval; | |
for (auto prop : TFieldRange<FProperty> { u }) { | |
if (!(prop->PropertyFlags & CPF_Parm)) { | |
break; | |
} | |
if (prop->PropertyFlags & CPF_ReturnParm) { | |
retval = GetPropClassName(prop); | |
} else { | |
parmArray.Add(prop->GetName() + ":" + GetPropClassName(prop)); | |
} | |
} | |
FString r = TEXT("fun(") + FString::Join(parmArray, TEXT(", ")) + TEXT(")"); | |
if (!retval.IsEmpty()) { | |
r += TEXT(":") + retval; | |
} | |
return r; | |
} | |
static FString GetPropClassName(FProperty* base_prop) noexcept { | |
if (auto prop = CastField<FEnumProperty>(base_prop)) { | |
return GetEnumName(prop->GetEnum()); | |
} | |
if (auto prop = CastField<FArrayProperty>(base_prop)) { | |
return GetPropClassName(prop->Inner) + TEXT("[]"); | |
} | |
if (auto prop = CastField<FMapProperty>(base_prop)) { | |
return TEXT("UE.MAP<") + GetPropClassName(prop->KeyProp) + TEXT(", ") + GetPropClassName(prop->ValueProp) + TEXT(">"); | |
} | |
if (auto prop = CastField<FSetProperty>(base_prop)) { | |
return TEXT("UE.SET<") + GetPropClassName(prop->ElementProp) + TEXT(">"); | |
} | |
if (auto prop = CastField<FObjectProperty>(base_prop)) { | |
return GetStructName(prop->PropertyClass); | |
} | |
if (auto prop = CastField<FObjectPtrProperty>(base_prop)) { | |
return GetStructName(prop->PropertyClass); | |
} | |
if (auto prop = CastField<FLazyObjectProperty>(base_prop)) { | |
return GetStructName(prop->PropertyClass); | |
} | |
if (auto prop = CastField<FWeakObjectProperty>(base_prop)) { | |
return GetStructName(prop->PropertyClass); | |
} | |
if (auto prop = CastField<FSoftObjectProperty>(base_prop)) { | |
return GetStructName(prop->PropertyClass); | |
} | |
if (auto prop = CastField<FClassProperty>(base_prop)) { | |
return GetStructName(prop->MetaClass); | |
} | |
if (auto prop = CastField<FClassPtrProperty>(base_prop)) { | |
return GetStructName(prop->MetaClass); | |
} | |
if (auto prop = CastField<FSoftClassProperty>(base_prop)) { | |
return GetStructName(prop->MetaClass); | |
} | |
if (auto prop = CastField<FInterfaceProperty>(base_prop)) { | |
return GetStructName(prop->InterfaceClass); | |
} | |
if (auto prop = CastField<FStructProperty>(base_prop)) { | |
return GetStructName(prop->Struct); | |
} | |
if (auto prop = CastField<FDelegateProperty>(base_prop)) { | |
return GetFunctionName(prop->SignatureFunction); | |
} | |
if (auto prop = CastField<FMulticastDelegateProperty>(base_prop)) { | |
return GetFunctionName(prop->SignatureFunction); | |
} | |
if (auto prop = CastField<FMulticastInlineDelegateProperty>(base_prop)) { | |
return GetFunctionName(prop->SignatureFunction); | |
} | |
if (auto prop = CastField<FMulticastSparseDelegateProperty>(base_prop)) { | |
return GetFunctionName(prop->SignatureFunction); | |
} | |
if (auto prop = CastField<FByteProperty>(base_prop)) { | |
if (prop->Enum) { | |
return GetEnumName(prop->Enum); | |
} | |
return TEXT("UE.UInt8"); | |
} | |
if (auto prop = CastField<FInt8Property>(base_prop)) { | |
return TEXT("UE.Int8"); | |
} | |
if (auto prop = CastField<FUInt16Property>(base_prop)) { | |
return TEXT("UE.UInt16"); | |
} | |
if (auto prop = CastField<FInt16Property>(base_prop)) { | |
return TEXT("UE.Int16"); | |
} | |
if (auto prop = CastField<FUInt32Property>(base_prop)) { | |
return TEXT("UE.UInt32"); | |
} | |
if (auto prop = CastField<FIntProperty>(base_prop)) { | |
return TEXT("UE.Int32"); | |
} | |
if (auto prop = CastField<FUInt64Property>(base_prop)) { | |
return TEXT("UE.UInt64"); | |
} | |
if (auto prop = CastField<FInt64Property>(base_prop)) { | |
return TEXT("UE.Int64"); | |
} | |
if (auto prop = CastField<FFloatProperty>(base_prop)) { | |
return TEXT("UE.Float"); | |
} | |
if (auto prop = CastField<FDoubleProperty>(base_prop)) { | |
return TEXT("UE.Double"); | |
} | |
if (auto prop = CastField<FBoolProperty>(base_prop)) { | |
return TEXT("UE.Bool"); | |
} | |
if (auto prop = CastField<FTextProperty>(base_prop)) { | |
return TEXT("UE.Text"); | |
} | |
if (auto prop = CastField<FStrProperty>(base_prop)) { | |
return TEXT("UE.String"); | |
} | |
if (auto prop = CastField<FNameProperty>(base_prop)) { | |
return TEXT("UE.Name"); | |
} | |
if (auto prop = CastField<FFieldPathProperty>(base_prop)) { | |
return TEXT("UE.FieldPath"); | |
} | |
return TEXT("UE.") + base_prop->GetClass()->GetName(); | |
} | |
static void GenerateUProperty(const std::string& path) noexcept { | |
FILE* f = fopen(path.c_str(), "wb"); | |
if (!f) { | |
return; | |
} | |
FileWrite(f, "---@meta UE.UProperty\n"); | |
FileWrite(f, "\n"); | |
FileWrite(f, "---@alias UE.UInt8 integer\n"); | |
FileWrite(f, "---@alias UE.Int8 integer\n"); | |
FileWrite(f, "---@alias UE.UInt16 integer\n"); | |
FileWrite(f, "---@alias UE.Int16 integer\n"); | |
FileWrite(f, "---@alias UE.UInt32 integer\n"); | |
FileWrite(f, "---@alias UE.Int32 integer\n"); | |
FileWrite(f, "---@alias UE.UInt64 integer\n"); | |
FileWrite(f, "---@alias UE.Int64 integer\n"); | |
FileWrite(f, "---@alias UE.Float number\n"); | |
FileWrite(f, "---@alias UE.Double number\n"); | |
FileWrite(f, "---@alias UE.Bool boolean\n"); | |
FileWrite(f, "---@alias UE.Text string\n"); | |
FileWrite(f, "---@alias UE.String string\n"); | |
FileWrite(f, "---@alias UE.Name string\n"); | |
FileWrite(f, "---@alias UE.FieldPath string\n"); | |
FileWrite(f, "\n"); | |
FileWrite(f, "---@class UE.MAP<K, V>: {{ [K]: V }}\n"); | |
FileWrite(f, "---@class UE.SET<K>: {{ [K]: true }}\n"); | |
FileWrite(f, "\n"); | |
fclose(f); | |
} | |
static void GenerateUEnum(const std::string& path) noexcept { | |
FILE* f = fopen(path.c_str(), "wb"); | |
if (!f) { | |
return; | |
} | |
FileWrite(f, "---@meta UE.UEnum\n"); | |
FileWrite(f, "\n"); | |
for (UEnum* u : TObjectRange<UEnum> {}) { | |
auto name = GetEnumName(u); | |
FileWrite(f, "---@alias {}\n", name); | |
for (int32_t i = 0; i < u->NumEnums(); ++i) { | |
FileWrite(f, "---| `{}.{}`\n", name, u->GetNameStringByIndex(i)); | |
} | |
FileWrite(f, "\n"); | |
} | |
fclose(f); | |
} | |
static void GenerateUStruct(const std::string& path) noexcept { | |
FILE* f = fopen(path.c_str(), "wb"); | |
if (!f) { | |
return; | |
} | |
FileWrite(f, "---@meta UE.UStruct\n"); | |
FileWrite(f, "\n"); | |
FileWrite(f, "local UE = {{}}\n"); | |
FileWrite(f, "\n"); | |
for (UStruct* cls : TObjectRange<UStruct> {}) { | |
if (Cast<UClass>(cls)) { | |
continue; | |
} | |
if (Cast<UFunction>(cls)) { | |
continue; | |
} | |
auto className = GetStructName(cls); | |
FileWrite(f, "---@class {}\n", className); | |
TSet<FString> has; | |
for (FProperty* prop = cls->PropertyLink; prop != nullptr; prop = prop->PropertyLinkNext) { | |
auto propName = prop->GetName(); | |
bool contains = false; | |
has.Add(propName, &contains); | |
if (!contains) { | |
FileWrite(f, "---@field {} {}\n", propName, GetPropClassName(prop)); | |
} | |
} | |
FileWrite(f, "{} = {{}}\n", className); | |
FileWrite(f, "\n"); | |
}; | |
fclose(f); | |
} | |
static void GenerateUClass(const std::string& path) noexcept { | |
FILE* f = fopen(path.c_str(), "wb"); | |
if (!f) { | |
return; | |
} | |
FileWrite(f, "---@meta UE.UClass\n"); | |
FileWrite(f, "\n"); | |
FileWrite(f, "local UE = {{}}\n"); | |
FileWrite(f, "\n"); | |
for (UClass* cls : TObjectRange<UClass> {}) { | |
auto className = GetStructName(cls); | |
TArray<FString> supperArray; | |
auto superClass = cls->GetSuperClass(); | |
if (superClass) { | |
supperArray.Add(GetStructName(superClass)); | |
} | |
for (const FImplementedInterface& Inter : cls->Interfaces) { | |
if (Inter.Class) { | |
supperArray.Add(GetStructName(Inter.Class)); | |
} | |
} | |
if (supperArray.IsEmpty()) { | |
FileWrite(f, "---@class {}\n", className); | |
} else { | |
FileWrite(f, "---@class {}: {}\n", className, FString::Join(supperArray, TEXT(", "))); | |
} | |
TSet<FString> has; | |
for (FProperty* prop = cls->PropertyLink; prop != nullptr; prop = prop->PropertyLinkNext) { | |
auto propName = prop->GetName(); | |
bool contains = false; | |
has.Add(propName, &contains); | |
if (!contains) { | |
FileWrite(f, "---@field {} {}\n", propName, GetPropClassName(prop)); | |
} | |
} | |
FileWrite(f, "{} = {{}}\n", className); | |
TArray<FName> names; | |
cls->GenerateFunctionList(names); | |
for (auto name : names) { | |
UFunction* func = cls->FindFunctionByName(name); | |
TArray<FString> parmArray; | |
for (auto prop : TFieldRange<FProperty> { func }) { | |
if (!(prop->PropertyFlags & CPF_Parm)) { | |
break; | |
} | |
auto propClassName = GetPropClassName(prop); | |
if (prop->PropertyFlags & CPF_ReturnParm) { | |
if (prop->GetName() == "ReturnValue") { | |
FileWrite(f, "---@return {}\n", propClassName); | |
} else { | |
FileWrite(f, "---@return {} {}\n", propClassName, prop->GetName()); | |
} | |
} else { | |
parmArray.Add(prop->GetName()); | |
FileWrite(f, "---@param {} {}\n", prop->GetName(), propClassName); | |
} | |
} | |
if (func->HasAnyFunctionFlags(FUNC_Static)) { | |
FileWrite(f, "function {}.{}({}) end\n", className, name.ToString(), FString::Join(parmArray, TEXT(", "))); | |
} else { | |
FileWrite(f, "function {}:{}({}) end\n", className, name.ToString(), FString::Join(parmArray, TEXT(", "))); | |
} | |
} | |
FileWrite(f, "\n"); | |
} | |
fclose(f); | |
} | |
void GenerateUnrealMetaLua(const std::string& path) noexcept { | |
GenerateUProperty(path + "/property.lua"); | |
GenerateUEnum(path + "/enum.lua"); | |
GenerateUStruct(path + "/struct.lua"); | |
GenerateUClass(path + "/class.lua"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment