/// <summary> /// Generates an enum. /// </summary> /// <param name="enumObj">The enum object.</param> private async Task GenerateEnum(GenericTypes.UEEnum enumObj) { var e = new Enum { Name = await NameValidator.MakeUniqueCppName(enumObj) }; if (e.Name.Contains("Default__") || e.Name.Contains("PLACEHOLDER-CLASS")) { return; } e.FullName = await enumObj.GetFullName(); var conflicts = new Dictionary <string, int>(); foreach (var s in await enumObj.GetNames()) { var clean = NameValidator.MakeValidName(s); if (!conflicts.ContainsKey(clean)) { e.Values.Add(clean); conflicts[clean] = 1; } else { e.Values.Add($"{clean}{conflicts[clean]:D2}"); conflicts[clean]++; } } Enums.Add(e); }
/// <summary> /// Generates the methods of a class. /// </summary> /// <param name="classObj">The class object.</param> /// <returns>Return The methods of the class.</returns> private async Task <List <Method> > GenerateMethods(GenericTypes.UEClass classObj) { var methods = new List <Method>(); //some classes (AnimBlueprintGenerated...) have multiple members with the same name, so filter them out var uniqueMethods = new List <string>(); // prop can be UEObject, UEField, UEProperty for (var prop = (await classObj.GetChildren()).Cast <GenericTypes.UEField>(); prop.IsValid(); prop = await prop.GetNext()) { if (!await prop.IsA <GenericTypes.UEFunction>()) { continue; } var function = prop.Cast <GenericTypes.UEFunction>(); var m = new Method { Index = function.GetIndex(), FullName = await function.GetFullName(), Name = Generator.GetSafeKeywordsName(NameValidator.MakeValidName(await function.GetName())) }; if (uniqueMethods.Contains(m.FullName)) { continue; } uniqueMethods.Add(m.FullName); m.IsNative = (await function.GetFunctionFlags()).HasFlag(UEFunctionFlags.Native); m.IsStatic = (await function.GetFunctionFlags()).HasFlag(UEFunctionFlags.Static); m.FlagsString = FunctionFlags.StringifyFlags(await function.GetFunctionFlags()); var parameters = new List <KeyValuePair <GenericTypes.UEProperty, Method.Parameter> >(); var unique = new Dictionary <string, int>(); for (var param = (await function.GetChildren()).Cast <GenericTypes.UEProperty>(); param.IsValid(); param = (await param.GetNext()).Cast <GenericTypes.UEProperty>()) { if (await param.GetElementSize() == 0) { continue; } var info = await param.GetInfo(); if (info.Type == GenericTypes.UEProperty.PropertyType.Unknown) { continue; } var p = new Method.Parameter(UnrealVersion.Unreal4); if (!p.MakeType(await param.GetPropertyFlags(), out p.ParamType)) { //child isn't a parameter continue; } p.PassByReference = false; p.Name = NameValidator.MakeValidName(await param.GetName()); if (!unique.ContainsKey(p.Name)) { unique[p.Name] = 1; } else { unique[p.Name]++; p.Name += unique[p.Name]; } p.FlagsString = PropertyFlags.StringifyFlags(await param.GetPropertyFlags()); p.CppType = info.CppType; if (await param.IsA <GenericTypes.UEBoolProperty>()) { p.CppType = Generator.GetOverrideType("bool"); } if (p.ParamType == Method.Parameter.Type.Default) { if (await param.GetArrayDim() > 1) { p.CppType += "*"; } else if (info.CanBeReference) { p.PassByReference = true; } } p.Name = Generator.GetSafeKeywordsName(p.Name); parameters.Add(new KeyValuePair <GenericTypes.UEProperty, Method.Parameter>(param, p)); } parameters.Sort((lhs, rhs) => ComparePropertyLess(lhs.Key, rhs.Key).Result ? 0 : 1); foreach (var param in parameters) { m.Parameters.Add(param.Value); } methods.Add(m); } return(methods); }
/// <summary> /// Generates the members of a struct or class. /// </summary> /// <param name="structObj">The structure object.</param> /// <param name="offset">The start offset.</param> /// <param name="properties">The properties describing the members.</param> /// <returns>The members of the struct or class.</returns> private async Task <List <Member> > GenerateMembers(GenericTypes.UEStruct structObj, int offset, List <GenericTypes.UEProperty> properties) { var members = new List <Member>(); var uniqueMemberNames = new Dictionary <string, int>(); int unknownDataCounter = 0; var previousBitfieldProperty = new GenericTypes.UEBoolProperty(); foreach (var prop in properties) { if (offset < await prop.GetOffset()) { int size = await prop.GetOffset() - offset; members.Add(CreatePadding(unknownDataCounter++, offset, size, "MISSED OFFSET")); } var info = await prop.GetInfo(); if (info.Type != GenericTypes.UEProperty.PropertyType.Unknown) { var sp = new Member { Offset = await prop.GetOffset(), Size = info.Size, Type = info.CppType, Name = NameValidator.MakeValidName(await prop.GetName()) }; if (!uniqueMemberNames.ContainsKey(sp.Name)) { uniqueMemberNames[sp.Name] = 1; } else { uniqueMemberNames[sp.Name]++; sp.Name += uniqueMemberNames[sp.Name]; } if (await prop.GetArrayDim() > 1) { sp.Name += $"[0x{await prop.GetArrayDim():X}]"; } if (await prop.IsA <GenericTypes.UEBoolProperty>() && await prop.Cast <GenericTypes.UEBoolProperty>().IsBitfield()) { var boolProp = prop.Cast <GenericTypes.UEBoolProperty>(); var missingBits = await boolProp.GetMissingBitsCount(previousBitfieldProperty); if (missingBits[1] != -1) { if (missingBits[0] > 0) { members.Add(CreateBitfieldPadding(unknownDataCounter++, await previousBitfieldProperty.GetOffset(), info.CppType, missingBits[0])); } if (missingBits[1] > 0) { members.Add(CreateBitfieldPadding(unknownDataCounter++, sp.Offset, info.CppType, missingBits[1])); } } else if (missingBits[0] > 0) { members.Add(CreateBitfieldPadding(unknownDataCounter++, sp.Offset, info.CppType, missingBits[0])); } previousBitfieldProperty = boolProp; sp.Name += " : 1"; } sp.Name = Generator.GetSafeKeywordsName(sp.Name); sp.Flags = (int)await prop.GetPropertyFlags(); sp.FlagsString = PropertyFlags.StringifyFlags(await prop.GetPropertyFlags()); members.Add(sp); int sizeMismatch = (await prop.GetElementSize() * await prop.GetArrayDim()) - (info.Size * await prop.GetArrayDim()); if (sizeMismatch > 0) { members.Add(CreatePadding(unknownDataCounter++, offset, sizeMismatch, "FIX WRONG TYPE SIZE OF PREVIOUS PROPERTY")); } } else { var size = await prop.GetElementSize() * await prop.GetArrayDim(); members.Add(CreatePadding(unknownDataCounter++, offset, size, "UNKNOWN PROPERTY: " + await prop.GetFullName())); } offset = await prop.GetOffset() + await prop.GetElementSize() * await prop.GetArrayDim(); } if (offset < await structObj.GetPropertySize()) { int size = await structObj.GetPropertySize() - offset; members.Add(CreatePadding(unknownDataCounter, offset, size, "MISSED OFFSET")); } return(members); }
/// <summary> /// Generates the class. /// </summary> /// <param name="classObj">The class object.</param> private async Task GenerateClass(GenericTypes.UEClass classObj) { var c = new Class { Name = await classObj.GetName(), FullName = await classObj.GetFullName() }; var logTask = Logger.Log($"Class: {await GetName() + "." + c.Name,-85} - instance: 0x{classObj.GetAddress().ToInt64():X8}"); c.NameCpp = NameValidator.MakeValidName(await classObj.GetNameCpp()); c.NameCppFull = $"class {c.NameCpp}"; c.Size = await classObj.GetPropertySize(); c.InheritedSize = 0; int offset = 0; var super = await classObj.GetSuper(); if (super.IsValid() && super != classObj) { c.InheritedSize = offset = await super.GetPropertySize(); c.NameCppFull += $" : public {NameValidator.MakeValidName(await super.GetNameCpp())}"; } var predefinedStaticMembers = new List <PredefinedMember>(); if (Generator.GetPredefinedClassStaticMembers(c.FullName, ref predefinedStaticMembers)) { foreach (var prop in predefinedStaticMembers) { var p = new Member { Offset = 0, Size = 0, Name = prop.Name, Type = prop.Type, IsStatic = true }; c.Members.Add(p); } } var predefinedMembers = new List <PredefinedMember>(); if (Generator.GetPredefinedClassMembers(c.FullName, ref predefinedMembers)) { foreach (var prop in predefinedMembers) { var p = new Member { Offset = 0, Size = 0, Name = prop.Name, Type = prop.Type, IsStatic = false, Comment = "NOT AUTO-GENERATED PROPERTY" }; c.Members.Add(p); } } else { var properties = new List <GenericTypes.UEProperty>(); for (var prop = (await classObj.GetChildren()).Cast <GenericTypes.UEProperty>(); prop.IsValid(); prop = (await prop.GetNext()).Cast <GenericTypes.UEProperty>()) { var elementSizeT = prop.GetElementSize(); var isScriptStruct = prop.IsA <GenericTypes.UEScriptStruct>(); var isFunction = prop.IsA <GenericTypes.UEFunction>(); var isEnum = prop.IsA <GenericTypes.UEEnum>(); var isConst = prop.IsA <GenericTypes.UEConst>(); if (await elementSizeT > 0 && !await isScriptStruct && !await isFunction && !await isEnum && !await isConst && (!super.IsValid() || (super != classObj && await prop.GetOffset() >= await super.GetPropertySize()))) { properties.Add(prop); } } // As C# sort not same as C++ version, that's not work // Anyway after some testes it's not needed !! // properties.Sort((x, y) => ComparePropertyLess(x, y).Result ? 0 : 1); c.Members = await GenerateMembers(classObj, offset, properties); } Generator.GetPredefinedClassMethods(c.FullName, ref c.PredefinedMethods); if (Generator.SdkType == SdkType.External) { // ToDO: Add external Read/Write here for external } else { if (Generator.ShouldUseStrings) { string classStr = Generator.ShouldXorStrings ? $"_xor_(\"{c.FullName}\")" : $"\"{c.FullName}\""; c.PredefinedMethods.Add(PredefinedMethod.Inline($@" static UClass* StaticClass() {{ static auto ptr = UObject::FindClass({classStr}); return ptr; }}" ) ); } else { c.PredefinedMethods.Add(PredefinedMethod.Inline($@" static UClass* StaticClass() {{ static auto ptr = UObject::GetObjectCasted<UClass>({classObj.GetIndex()}); return ptr; }}" ) ); } c.Methods = await GenerateMethods(classObj); //search virtual functions var patterns = new VirtualFunctionPatterns(); if (Generator.GetVirtualFunctionPatterns(c.FullName, ref patterns)) { int ptrSize = Utils.GamePointerSize(); IntPtr vTableAddress = classObj.Object.VfTable; var vTable = new List <IntPtr>(); int methodCount = 0; while (methodCount < 150) { // Dereference Pointer IntPtr vAddress = Utils.MemObj.ReadAddress(vTableAddress + (methodCount * ptrSize)); // Check valid address int res = Win32.VirtualQueryEx(Utils.MemObj.ProcessHandle, vAddress, out var info, (uint)Marshal.SizeOf <Win32.MemoryBasicInformation>()); if (res == 0 || info.Protect.HasFlag(Win32.MemoryProtection.PageNoAccess)) { break; } vTable.Add(vAddress); methodCount++; } foreach (var(pattern, funcStr) in patterns) { for (int i = 0; i < methodCount; i++) { if (vTable[i].IsNull()) { continue; } var scanResult = await PatternScanner.FindPattern(Utils.MemObj, vTable[i], vTable[i] + 0x300, new List <PatternScanner.Pattern> { pattern }, true); if (!scanResult.ContainsKey(pattern.Name) || scanResult[pattern.Name].Empty()) { continue; } c.PredefinedMethods.Add(PredefinedMethod.Inline($@"{funcStr.Replace("%d", i.ToString())}")); break; } } } } // Wait logger await logTask; Classes.Add(c); }
/// <summary> /// Generates a script structure. /// </summary> /// <param name="scriptStructObj">The script structure object.</param> private async Task GenerateScriptStruct(GenericTypes.UEScriptStruct scriptStructObj) { var ss = new ScriptStruct { Name = await scriptStructObj.GetName(), FullName = await scriptStructObj.GetFullName() }; var logTask = Logger.Log($"Struct: {await GetName() + "." + ss.Name, -85} - instance: 0x{scriptStructObj.GetAddress().ToInt64():X8}"); ss.NameCpp = NameValidator.MakeValidName(await scriptStructObj.GetNameCpp()); ss.NameCppFull = "struct "; //some classes need special alignment var alignment = Generator.GetClassAlignas(ss.FullName); if (alignment == 0) { ss.NameCppFull += $"alignas({alignment}) "; } ss.NameCppFull += await NameValidator.MakeUniqueCppName(scriptStructObj); ss.Size = await scriptStructObj.GetPropertySize(); ss.InheritedSize = 0; int offset = 0; var super = await scriptStructObj.GetSuper(); if (super.IsValid() && super != scriptStructObj) { ss.InheritedSize = offset = await scriptStructObj.GetPropertySize(); ss.NameCppFull += $" : public {await NameValidator.MakeUniqueCppName(super.Cast<GenericTypes.UEScriptStruct>())}"; } var properties = new List <GenericTypes.UEProperty>(); for (var prop = (await scriptStructObj.GetChildren()).Cast <GenericTypes.UEProperty>(); prop.IsValid(); prop = (await prop.GetNext()).Cast <GenericTypes.UEProperty>()) { var isScriptStruct = prop.IsA <GenericTypes.UEScriptStruct>(); var isFunction = prop.IsA <GenericTypes.UEFunction>(); var isEnum = prop.IsA <GenericTypes.UEEnum>(); var isConst = prop.IsA <GenericTypes.UEConst>(); if (await prop.GetElementSize() > 0 && !await isScriptStruct && !await isFunction && !await isEnum && !await isConst) { properties.Add(prop); } } // As C# sort not same as C++ version, that's not work // Anyway after some testes it's not needed !! // properties.Sort((x, y) => ComparePropertyLess(x, y).Result ? 0 : 1); var memberT = GenerateMembers(scriptStructObj, offset, properties); if (Generator.SdkType == SdkType.External) { // ToDO: Add external Read/Write here for external } Generator.GetPredefinedClassMethods(await scriptStructObj.GetFullName(), ref ss.PredefinedMethods); ss.Members = await memberT; ScriptStructs.Add(ss); // wait logger await logTask; }