Skip to content

Instantly share code, notes, and snippets.

@actboy168
Last active May 29, 2024 12:45
Show Gist options
  • Save actboy168/7e88d7d5149f18f409ebc7ea0b008661 to your computer and use it in GitHub Desktop.
Save actboy168/7e88d7d5149f18f409ebc7ea0b008661 to your computer and use it in GitHub Desktop.
#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