static void Main(string[] args) { string outputPath; if (args.Length > 0) { outputPath = args[0]; } else { outputPath = AppContext.BaseDirectory; } string libraryName; if (args.Length > 1) { libraryName = args[1]; } else { libraryName = "cimgui"; } string projectNamespace = libraryName switch { "cimgui" => "ImGuiNET", "cimplot" => "ImPlotNET", "cimnodes" => "imnodesNET", "cimguizmo" => "ImGuizmoNET", _ => throw new NotImplementedException($"Library \"{libraryName}\" is not supported.") }; bool referencesImGui = libraryName switch { "cimgui" => false, "cimplot" => true, "cimnodes" => true, "cimguizmo" => true, _ => throw new NotImplementedException($"Library \"{libraryName}\" is not supported.") }; string classPrefix = libraryName switch { "cimgui" => "ImGui", "cimplot" => "ImPlot", "cimnodes" => "imnodes", "cimguizmo" => "ImGuizmo", _ => throw new NotImplementedException($"Library \"{libraryName}\" is not supported.") }; string dllName = libraryName switch { "cimgui" => "cimgui", "cimplot" => "cimplot", "cimnodes" => "cimnodes", "cimguizmo" => "cimguizmo", _ => throw new NotImplementedException() }; string definitionsPath = Path.Combine(AppContext.BaseDirectory, "definitions", libraryName); var defs = new ImguiDefinitions(); defs.LoadFrom(definitionsPath); Console.WriteLine($"Outputting generated code files to {outputPath}."); foreach (EnumDefinition ed in defs.Enums) { using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, ed.FriendlyName + ".gen.cs"))) { writer.PushBlock($"namespace {projectNamespace}"); if (ed.FriendlyName.Contains("Flags")) { writer.WriteLine("[System.Flags]"); } writer.PushBlock($"public enum {ed.FriendlyName}"); foreach (EnumMember member in ed.Members) { string sanitizedName = ed.SanitizeNames(member.Name); string sanitizedValue = ed.SanitizeNames(member.Value); writer.WriteLine($"{sanitizedName} = {sanitizedValue},"); } writer.PopBlock(); writer.PopBlock(); } } foreach (TypeDefinition td in defs.Types) { if (TypeInfo.CustomDefinedTypes.Contains(td.Name)) { continue; } using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, td.Name + ".gen.cs"))) { writer.Using("System"); writer.Using("System.Numerics"); writer.Using("System.Runtime.CompilerServices"); writer.Using("System.Text"); if (referencesImGui) { writer.Using("ImGuiNET"); } writer.WriteLine(string.Empty); writer.PushBlock($"namespace {projectNamespace}"); writer.PushBlock($"public unsafe partial struct {td.Name}"); foreach (TypeReference field in td.Fields) { string typeStr = GetTypeString(field.Type, field.IsFunctionPointer); if (field.ArraySize != 0) { if (TypeInfo.LegalFixedTypes.Contains(typeStr)) { writer.WriteLine($"public fixed {typeStr} {field.Name}[{field.ArraySize}];"); } else { for (int i = 0; i < field.ArraySize; i++) { writer.WriteLine($"public {typeStr} {field.Name}_{i};"); } } } else { writer.WriteLine($"public {typeStr} {field.Name};"); } } writer.PopBlock(); string ptrTypeName = td.Name + "Ptr"; writer.PushBlock($"public unsafe partial struct {ptrTypeName}"); writer.WriteLine($"public {td.Name}* NativePtr {{ get; }}"); writer.WriteLine($"public {ptrTypeName}({td.Name}* nativePtr) => NativePtr = nativePtr;"); writer.WriteLine($"public {ptrTypeName}(IntPtr nativePtr) => NativePtr = ({td.Name}*)nativePtr;"); writer.WriteLine($"public static implicit operator {ptrTypeName}({td.Name}* nativePtr) => new {ptrTypeName}(nativePtr);"); writer.WriteLine($"public static implicit operator {td.Name}* ({ptrTypeName} wrappedPtr) => wrappedPtr.NativePtr;"); writer.WriteLine($"public static implicit operator {ptrTypeName}(IntPtr nativePtr) => new {ptrTypeName}(nativePtr);"); foreach (TypeReference field in td.Fields) { string typeStr = GetTypeString(field.Type, field.IsFunctionPointer); string rawType = typeStr; if (TypeInfo.WellKnownFieldReplacements.TryGetValue(field.Type, out string wellKnownFieldType)) { typeStr = wellKnownFieldType; } if (field.ArraySize != 0) { string addrTarget = TypeInfo.LegalFixedTypes.Contains(rawType) ? $"NativePtr->{field.Name}" : $"&NativePtr->{field.Name}_0"; writer.WriteLine($"public RangeAccessor<{typeStr}> {field.Name} => new RangeAccessor<{typeStr}>({addrTarget}, {field.ArraySize});"); } else if (typeStr.Contains("ImVector")) { string vectorElementType = GetTypeString(field.TemplateType, false); if (TypeInfo.WellKnownTypes.TryGetValue(vectorElementType, out string wellKnown)) { vectorElementType = wellKnown; } if (GetWrappedType(vectorElementType + "*", out string wrappedElementType)) { writer.WriteLine($"public ImPtrVector<{wrappedElementType}> {field.Name} => new ImPtrVector<{wrappedElementType}>(NativePtr->{field.Name}, Unsafe.SizeOf<{vectorElementType}>());"); } else { if (GetWrappedType(vectorElementType, out wrappedElementType)) { vectorElementType = wrappedElementType; } writer.WriteLine($"public ImVector<{vectorElementType}> {field.Name} => new ImVector<{vectorElementType}>(NativePtr->{field.Name});"); } } else { if (typeStr.Contains("*") && !typeStr.Contains("ImVector")) { if (GetWrappedType(typeStr, out string wrappedTypeName)) { writer.WriteLine($"public {wrappedTypeName} {field.Name} => new {wrappedTypeName}(NativePtr->{field.Name});"); } else if (typeStr == "byte*" && IsStringFieldName(field.Name)) { writer.WriteLine($"public NullTerminatedString {field.Name} => new NullTerminatedString(NativePtr->{field.Name});"); } else { writer.WriteLine($"public IntPtr {field.Name} {{ get => (IntPtr)NativePtr->{field.Name}; set => NativePtr->{field.Name} = ({typeStr})value; }}"); } } else { writer.WriteLine($"public ref {typeStr} {field.Name} => ref Unsafe.AsRef<{typeStr}>(&NativePtr->{field.Name});"); } } } foreach (FunctionDefinition fd in defs.Functions) { foreach (OverloadDefinition overload in fd.Overloads) { if (overload.StructName != td.Name) { continue; } if (overload.IsConstructor) { // TODO: Emit a static function on the type that invokes the native constructor. // Also, add a "Dispose" function or similar. continue; } string exportedName = overload.ExportedName; if (exportedName.StartsWith("ig")) { exportedName = exportedName.Substring(2, exportedName.Length - 2); } if (exportedName.Contains("~")) { continue; } if (overload.Parameters.Any(tr => tr.Type.Contains('('))) { continue; } // TODO: Parse function pointer parameters. bool hasVaList = false; for (int i = 0; i < overload.Parameters.Length; i++) { TypeReference p = overload.Parameters[i]; string paramType = GetTypeString(p.Type, p.IsFunctionPointer); if (p.Name == "...") { continue; } if (paramType == "va_list") { hasVaList = true; break; } } if (hasVaList) { continue; } KeyValuePair <string, string>[] orderedDefaults = overload.DefaultValues.OrderByDescending( kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray(); for (int i = overload.DefaultValues.Count; i >= 0; i--) { Dictionary <string, string> defaults = new Dictionary <string, string>(); for (int j = 0; j < i; j++) { defaults.Add(orderedDefaults[j].Key, orderedDefaults[j].Value); } EmitOverload(writer, overload, defaults, "NativePtr", classPrefix); for (int j = 0; j < overload.Parameters.Length; j++) { // We only want to replace enums that are not a default value for this overload if (overload.Parameters[j].IsEnum && !defaults.TryGetValue(overload.Parameters[j].Name, out var unused)) { var primitiveOverload = new OverloadDefinition( overload.ExportedName, overload.FriendlyName, (TypeReference[])overload.Parameters.Clone(), overload.DefaultValues, overload.ReturnType, overload.StructName, overload.Comment, overload.IsConstructor, overload.IsDestructor); var oldParam = primitiveOverload.Parameters[j]; var newParam = new TypeReference(oldParam.Name, "int", oldParam.ArraySize, new EnumDefinition[] {}); primitiveOverload.Parameters[j] = newParam; EmitOverload(writer, primitiveOverload, defaults, "NativePtr", classPrefix, j, oldParam.Type); } } } } } writer.PopBlock(); writer.PopBlock(); } } using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, $"{classPrefix}Native.gen.cs"))) { writer.Using("System"); writer.Using("System.Numerics"); writer.Using("System.Runtime.InteropServices"); if (referencesImGui) { writer.Using("ImGuiNET"); } writer.WriteLine(string.Empty); writer.PushBlock($"namespace {projectNamespace}"); writer.PushBlock($"public static unsafe partial class {classPrefix}Native"); foreach (FunctionDefinition fd in defs.Functions) { foreach (OverloadDefinition overload in fd.Overloads) { string exportedName = overload.ExportedName; if (exportedName.Contains("~")) { continue; } if (exportedName.Contains("ImVector_")) { continue; } if (exportedName.Contains("ImChunkStream_")) { continue; } if (overload.Parameters.Any(tr => tr.Type.Contains('('))) { continue; } // TODO: Parse function pointer parameters. string ret = GetTypeString(overload.ReturnType, false); bool hasVaList = false; List <string> paramParts = new List <string>(); for (int i = 0; i < overload.Parameters.Length; i++) { TypeReference p = overload.Parameters[i]; string paramType = GetTypeString(p.Type, p.IsFunctionPointer); if (p.ArraySize != 0) { paramType = paramType + "*"; } if (p.Name == "...") { continue; } paramParts.Add($"{paramType} {CorrectIdentifier(p.Name)}"); if (paramType == "va_list") { hasVaList = true; break; } } if (hasVaList) { continue; } string parameters = string.Join(", ", paramParts); bool isUdtVariant = exportedName.Contains("nonUDT"); string methodName = isUdtVariant ? exportedName.Substring(0, exportedName.IndexOf("_nonUDT")) : exportedName; if (isUdtVariant) { writer.WriteLine($"[DllImport(\"{dllName}\", CallingConvention = CallingConvention.Cdecl, EntryPoint = \"{exportedName}\")]"); } else { writer.WriteLine($"[DllImport(\"{dllName}\", CallingConvention = CallingConvention.Cdecl)]"); } writer.WriteLine($"public static extern {ret} {methodName}({parameters});"); } } writer.PopBlock(); writer.PopBlock(); } using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, $"{classPrefix}.gen.cs"))) { writer.Using("System"); writer.Using("System.Numerics"); writer.Using("System.Runtime.InteropServices"); writer.Using("System.Text"); if (referencesImGui) { writer.Using("ImGuiNET"); } writer.WriteLine(string.Empty); writer.PushBlock($"namespace {projectNamespace}"); writer.PushBlock($"public static unsafe partial class {classPrefix}"); foreach (FunctionDefinition fd in defs.Functions) { if (TypeInfo.SkippedFunctions.Contains(fd.Name)) { continue; } foreach (OverloadDefinition overload in fd.Overloads) { string exportedName = overload.ExportedName; if (exportedName.StartsWith("ig")) { exportedName = exportedName.Substring(2, exportedName.Length - 2); } if (exportedName.Contains("~")) { continue; } if (overload.Parameters.Any(tr => tr.Type.Contains('('))) { continue; } // TODO: Parse function pointer parameters. bool hasVaList = false; for (int i = 0; i < overload.Parameters.Length; i++) { TypeReference p = overload.Parameters[i]; string paramType = GetTypeString(p.Type, p.IsFunctionPointer); if (p.Name == "...") { continue; } if (paramType == "va_list") { hasVaList = true; break; } } if (hasVaList) { continue; } KeyValuePair <string, string>[] orderedDefaults = overload.DefaultValues.OrderByDescending( kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray(); for (int i = overload.DefaultValues.Count; i >= 0; i--) { if (overload.IsMemberFunction) { continue; } Dictionary <string, string> defaults = new Dictionary <string, string>(); for (int j = 0; j < i; j++) { defaults.Add(orderedDefaults[j].Key, orderedDefaults[j].Value); } EmitOverload(writer, overload, defaults, null, classPrefix); for (int j = 0; j < overload.Parameters.Length; j++) { // We only want to replace enums that are not a default value for this overload if (overload.Parameters[j].IsEnum && !defaults.TryGetValue(overload.Parameters[j].Name, out var unused)) { var primitiveOverload = new OverloadDefinition( overload.ExportedName, overload.FriendlyName, (TypeReference[])overload.Parameters.Clone(), overload.DefaultValues, overload.ReturnType, overload.StructName, overload.Comment, overload.IsConstructor, overload.IsDestructor); var oldParam = primitiveOverload.Parameters[j]; var newParam = new TypeReference(oldParam.Name, "int", oldParam.ArraySize, new EnumDefinition[] {}); primitiveOverload.Parameters[j] = newParam; EmitOverload(writer, primitiveOverload, defaults, null, classPrefix, j, oldParam.Type); } } } } } writer.PopBlock(); writer.PopBlock(); } foreach (var method in defs.Variants) { foreach (var variant in method.Value.Parameters) { if (!variant.Used) { Console.WriteLine($"Error: Variants targetting parameter {variant.Name} with type {variant.OriginalType} could not be applied to method {method.Key}."); } } } }
static void Main(string[] args) { string outputPath; if (args.Length > 0) { outputPath = args[0]; } else { outputPath = AppContext.BaseDirectory; } Console.WriteLine($"Outputting generated code files to {outputPath}."); JObject typesJson; using (StreamReader fs = File.OpenText(Path.Combine(AppContext.BaseDirectory, "structs_and_enums.json"))) using (JsonTextReader jr = new JsonTextReader(fs)) { typesJson = JObject.Load(jr); } JObject functionsJson; using (StreamReader fs = File.OpenText(Path.Combine(AppContext.BaseDirectory, "definitions.json"))) using (JsonTextReader jr = new JsonTextReader(fs)) { functionsJson = JObject.Load(jr); } EnumDefinition[] enums = typesJson["enums"].Select(jt => { JProperty jp = (JProperty)jt; string name = jp.Name; EnumMember[] elements = jp.Values().Select(v => { return(new EnumMember(v["name"].ToString(), v["value"].ToString())); }).ToArray(); return(new EnumDefinition(name, elements)); }).ToArray(); TypeDefinition[] types = typesJson["structs"].Select(jt => { JProperty jp = (JProperty)jt; string name = jp.Name; TypeReference[] fields = jp.Values().Select(v => { return(new TypeReference( v["name"].ToString(), v["type"].ToString(), v["template_type"]?.ToString(), enums)); }).ToArray(); return(new TypeDefinition(name, fields)); }).ToArray(); FunctionDefinition[] functions = functionsJson.Children().Select(jt => { JProperty jp = (JProperty)jt; string name = jp.Name; if (name.Contains("GetMousePos")) { } bool hasNonUdtVariants = jp.Values().Any(val => val["ov_cimguiname"]?.ToString().EndsWith("nonUDT") ?? false); OverloadDefinition[] overloads = jp.Values().Select(val => { string ov_cimguiname = val["ov_cimguiname"]?.ToString(); string cimguiname = val["cimguiname"].ToString(); string friendlyName = val["funcname"].ToString(); string exportedName = ov_cimguiname; if (exportedName == null) { exportedName = cimguiname; } if (hasNonUdtVariants && !exportedName.EndsWith("nonUDT2")) { return(null); } string selfTypeName = null; int underscoreIndex = exportedName.IndexOf('_'); if (underscoreIndex > 0 && !exportedName.StartsWith("ig")) // Hack to exclude some weirdly-named non-instance functions. { selfTypeName = exportedName.Substring(0, underscoreIndex); } List <TypeReference> parameters = new List <TypeReference>(); if (selfTypeName != null) { parameters.Add(new TypeReference("self", selfTypeName + "*", enums)); } foreach (JToken p in val["argsT"]) { string pType = p["type"].ToString(); string pName = p["name"].ToString(); parameters.Add(new TypeReference(pName, pType, enums)); } Dictionary <string, string> defaultValues = new Dictionary <string, string>(); foreach (JToken dv in val["defaults"]) { JProperty dvProp = (JProperty)dv; defaultValues.Add(dvProp.Name, dvProp.Value.ToString()); } string returnType = val["ret"]?.ToString() ?? "void"; string comment = null; string structName = val["stname"].ToString(); return(new OverloadDefinition( exportedName, friendlyName, parameters.ToArray(), defaultValues, returnType, structName, comment, enums)); }).Where(od => od != null).ToArray(); return(new FunctionDefinition(name, overloads)); }).ToArray(); foreach (EnumDefinition ed in enums) { using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, ed.FriendlyName + ".gen.cs"))) { writer.PushBlock("namespace ImGuiNET"); if (ed.FriendlyName.Contains("Flags")) { writer.WriteLine("[System.Flags]"); } writer.PushBlock($"public enum {ed.FriendlyName}"); foreach (EnumMember member in ed.Members) { string sanitizedName = ed.SanitizeNames(member.Name); string sanitizedValue = ed.SanitizeNames(member.Value); writer.WriteLine($"{sanitizedName} = {sanitizedValue},"); } writer.PopBlock(); writer.PopBlock(); } } foreach (TypeDefinition td in types) { if (s_customDefinedTypes.Contains(td.Name)) { continue; } using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, td.Name + ".gen.cs"))) { writer.Using("System"); writer.Using("System.Numerics"); writer.Using("System.Runtime.CompilerServices"); writer.Using("System.Text"); writer.WriteLine(string.Empty); writer.PushBlock("namespace ImGuiNET"); writer.PushBlock($"public unsafe partial struct {td.Name}"); foreach (TypeReference field in td.Fields) { string typeStr = GetTypeString(field.Type, field.IsFunctionPointer); if (field.ArraySize != 0) { if (s_legalFixedTypes.Contains(typeStr)) { writer.WriteLine($"public fixed {typeStr} {field.Name}[{field.ArraySize}];"); } else { for (int i = 0; i < field.ArraySize; i++) { writer.WriteLine($"public {typeStr} {field.Name}_{i};"); } } } else { writer.WriteLine($"public {typeStr} {field.Name};"); } } writer.PopBlock(); string ptrTypeName = td.Name + "Ptr"; writer.PushBlock($"public unsafe partial struct {ptrTypeName}"); writer.WriteLine($"public {td.Name}* NativePtr {{ get; }}"); writer.WriteLine($"public {ptrTypeName}({td.Name}* nativePtr) => NativePtr = nativePtr;"); writer.WriteLine($"public {ptrTypeName}(IntPtr nativePtr) => NativePtr = ({td.Name}*)nativePtr;"); writer.WriteLine($"public static implicit operator {ptrTypeName}({td.Name}* nativePtr) => new {ptrTypeName}(nativePtr);"); writer.WriteLine($"public static implicit operator {td.Name}* ({ptrTypeName} wrappedPtr) => wrappedPtr.NativePtr;"); writer.WriteLine($"public static implicit operator {ptrTypeName}(IntPtr nativePtr) => new {ptrTypeName}(nativePtr);"); foreach (TypeReference field in td.Fields) { string typeStr = GetTypeString(field.Type, field.IsFunctionPointer); string rawType = typeStr; if (s_wellKnownFieldReplacements.TryGetValue(field.Type, out string wellKnownFieldType)) { typeStr = wellKnownFieldType; } if (field.ArraySize != 0) { string addrTarget = s_legalFixedTypes.Contains(rawType) ? $"NativePtr->{field.Name}" : $"&NativePtr->{field.Name}_0"; writer.WriteLine($"public RangeAccessor<{typeStr}> {field.Name} => new RangeAccessor<{typeStr}>({addrTarget}, {field.ArraySize});"); } else if (typeStr.Contains("ImVector")) { string vectorElementType = GetTypeString(field.TemplateType, false); if (s_wellKnownTypes.TryGetValue(vectorElementType, out string wellKnown)) { vectorElementType = wellKnown; } if (GetWrappedType(vectorElementType + "*", out string wrappedElementType)) { writer.WriteLine($"public ImPtrVector<{wrappedElementType}> {field.Name} => new ImPtrVector<{wrappedElementType}>(NativePtr->{field.Name}, Unsafe.SizeOf<{vectorElementType}>());"); } else { if (GetWrappedType(vectorElementType, out wrappedElementType)) { vectorElementType = wrappedElementType; } writer.WriteLine($"public ImVector<{vectorElementType}> {field.Name} => new ImVector<{vectorElementType}>(NativePtr->{field.Name});"); } } else { if (typeStr.Contains("*") && !typeStr.Contains("ImVector")) { if (GetWrappedType(typeStr, out string wrappedTypeName)) { writer.WriteLine($"public {wrappedTypeName} {field.Name} => new {wrappedTypeName}(NativePtr->{field.Name});"); } else if (typeStr == "byte*" && IsStringFieldName(field.Name)) { writer.WriteLine($"public NullTerminatedString {field.Name} => new NullTerminatedString(NativePtr->{field.Name});"); } else { writer.WriteLine($"public IntPtr {field.Name} {{ get => (IntPtr)NativePtr->{field.Name}; set => NativePtr->{field.Name} = ({typeStr})value; }}"); } } else { writer.WriteLine($"public ref {typeStr} {field.Name} => ref Unsafe.AsRef<{typeStr}>(&NativePtr->{field.Name});"); } } } foreach (FunctionDefinition fd in functions) { foreach (OverloadDefinition overload in fd.Overloads) { if (overload.StructName != td.Name) { continue; } if (overload.FriendlyName == overload.StructName) { continue; } string exportedName = overload.ExportedName; if (exportedName.StartsWith("ig")) { exportedName = exportedName.Substring(2, exportedName.Length - 2); } if (exportedName.Contains("~")) { continue; } if (overload.Parameters.Any(tr => tr.Type.Contains('('))) { continue; } // TODO: Parse function pointer parameters. bool hasVaList = false; for (int i = 0; i < overload.Parameters.Length; i++) { TypeReference p = overload.Parameters[i]; string paramType = GetTypeString(p.Type, p.IsFunctionPointer); if (p.Name == "...") { continue; } if (paramType == "va_list") { hasVaList = true; break; } } if (hasVaList) { continue; } KeyValuePair <string, string>[] orderedDefaults = overload.DefaultValues.OrderByDescending( kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray(); for (int i = overload.DefaultValues.Count; i >= 0; i--) { Dictionary <string, string> defaults = new Dictionary <string, string>(); for (int j = 0; j < i; j++) { defaults.Add(orderedDefaults[j].Key, orderedDefaults[j].Value); } EmitOverload(writer, overload, defaults, "NativePtr"); } } } writer.PopBlock(); writer.PopBlock(); } } using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, "ImGuiNative.gen.cs"))) { writer.Using("System"); writer.Using("System.Numerics"); writer.Using("System.Runtime.InteropServices"); writer.WriteLine(string.Empty); writer.PushBlock("namespace ImGuiNET"); writer.PushBlock("public static unsafe partial class ImGuiNative"); foreach (FunctionDefinition fd in functions) { foreach (OverloadDefinition overload in fd.Overloads) { string exportedName = overload.ExportedName; if (exportedName.Contains("~")) { continue; } if (overload.Parameters.Any(tr => tr.Type.Contains('('))) { continue; } // TODO: Parse function pointer parameters. string ret = GetTypeString(overload.ReturnType, false); bool hasVaList = false; List <string> paramParts = new List <string>(); for (int i = 0; i < overload.Parameters.Length; i++) { TypeReference p = overload.Parameters[i]; string paramType = GetTypeString(p.Type, p.IsFunctionPointer); if (p.ArraySize != 0) { paramType = paramType + "*"; } if (p.Name == "...") { continue; } paramParts.Add($"{paramType} {CorrectIdentifier(p.Name)}"); if (paramType == "va_list") { hasVaList = true; break; } } if (hasVaList) { continue; } string parameters = string.Join(", ", paramParts); bool isUdtVariant = exportedName.Contains("nonUDT"); string methodName = isUdtVariant ? exportedName.Substring(0, exportedName.IndexOf("_nonUDT")) : exportedName; if (isUdtVariant) { writer.WriteLine($"[DllImport(\"cimgui\", CallingConvention = CallingConvention.Cdecl, EntryPoint = \"{exportedName}\")]"); } else { writer.WriteLine("[DllImport(\"cimgui\", CallingConvention = CallingConvention.Cdecl)]"); } writer.WriteLine($"public static extern {ret} {methodName}({parameters});"); } } writer.PopBlock(); writer.PopBlock(); } using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, "ImGui.gen.cs"))) { writer.Using("System"); writer.Using("System.Numerics"); writer.Using("System.Runtime.InteropServices"); writer.Using("System.Text"); writer.WriteLine(string.Empty); writer.PushBlock("namespace ImGuiNET"); writer.PushBlock("public static unsafe partial class ImGui"); foreach (FunctionDefinition fd in functions) { foreach (OverloadDefinition overload in fd.Overloads) { string exportedName = overload.ExportedName; if (exportedName.StartsWith("ig")) { exportedName = exportedName.Substring(2, exportedName.Length - 2); } if (exportedName.Contains("~")) { continue; } if (overload.Parameters.Any(tr => tr.Type.Contains('('))) { continue; } // TODO: Parse function pointer parameters. bool hasVaList = false; for (int i = 0; i < overload.Parameters.Length; i++) { TypeReference p = overload.Parameters[i]; string paramType = GetTypeString(p.Type, p.IsFunctionPointer); if (p.Name == "...") { continue; } if (paramType == "va_list") { hasVaList = true; break; } } if (hasVaList) { continue; } KeyValuePair <string, string>[] orderedDefaults = overload.DefaultValues.OrderByDescending( kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray(); for (int i = overload.DefaultValues.Count; i >= 0; i--) { if (overload.IsMemberFunction) { continue; } Dictionary <string, string> defaults = new Dictionary <string, string>(); for (int j = 0; j < i; j++) { defaults.Add(orderedDefaults[j].Key, orderedDefaults[j].Value); } EmitOverload(writer, overload, defaults, null); } } } writer.PopBlock(); writer.PopBlock(); } }
private static void Main(string[] args) { string outputPath; if (args.Length > 0) { outputPath = args[0]; } else { outputPath = Path.Combine(AppContext.BaseDirectory, @"Generated\"); if (!Directory.Exists(outputPath)) { Directory.CreateDirectory(outputPath); } } var defs = new ImguiDefinitions(); defs.LoadFrom(AppContext.BaseDirectory); Console.WriteLine($"Outputting generated code files to {outputPath}."); foreach (var ed in defs.Enums) { using (var writer = new CSharpCodeWriter(Path.Combine(outputPath, ed.FriendlyName + ".gen.cs"))) { writer.PushBlock("namespace ImGuiNET"); if (ed.FriendlyName.Contains("Flags")) { writer.WriteLine("[System.Flags]"); } writer.PushBlock($"public enum {ed.FriendlyName}"); foreach (var member in ed.Members) { var sanitizedName = ed.SanitizeNames(member.Name); var sanitizedValue = ed.SanitizeNames(member.Value); writer.WriteLine($"{sanitizedName} = {sanitizedValue},"); } writer.PopBlock(); writer.PopBlock(); } } foreach (var td in defs.Types) { if (TypeInfo.CustomDefinedTypes.Contains(td.Name)) { continue; } using (var writer = new CSharpCodeWriter(Path.Combine(outputPath, td.Name + ".gen.cs"))) { writer.Using("System"); writer.Using("System.Numerics"); writer.Using("System.Runtime.CompilerServices"); writer.Using("System.Text"); writer.WriteLine(string.Empty); writer.PushBlock("namespace ImGuiNET"); writer.PushBlock($"public unsafe partial struct {td.Name}"); foreach (var field in td.Fields) { var typeStr = GetTypeString(field.Type, field.IsFunctionPointer); if (field.ArraySize != 0) { if (TypeInfo.LegalFixedTypes.Contains(typeStr)) { writer.WriteLine($"public fixed {typeStr} {field.Name}[{field.ArraySize}];"); } else { for (var i = 0; i < field.ArraySize; i++) { writer.WriteLine($"public {typeStr} {field.Name}_{i};"); } } } else { writer.WriteLine($"public {typeStr} {field.Name};"); } } writer.PopBlock(); var ptrTypeName = td.Name + "Ptr"; writer.PushBlock($"public unsafe partial struct {ptrTypeName}"); writer.WriteLine($"public {td.Name}* NativePtr {{ get; }}"); writer.WriteLine($"public {ptrTypeName}({td.Name}* nativePtr) => NativePtr = nativePtr;"); writer.WriteLine($"public {ptrTypeName}(IntPtr nativePtr) => NativePtr = ({td.Name}*)nativePtr;"); writer.WriteLine($"public static implicit operator {ptrTypeName}({td.Name}* nativePtr) => new {ptrTypeName}(nativePtr);"); writer.WriteLine($"public static implicit operator {td.Name}* ({ptrTypeName} wrappedPtr) => wrappedPtr.NativePtr;"); writer.WriteLine($"public static implicit operator {ptrTypeName}(IntPtr nativePtr) => new {ptrTypeName}(nativePtr);"); foreach (var field in td.Fields) { var typeStr = GetTypeString(field.Type, field.IsFunctionPointer); var rawType = typeStr; if (TypeInfo.WellKnownFieldReplacements.TryGetValue(field.Type, out var wellKnownFieldType)) { typeStr = wellKnownFieldType; } if (field.ArraySize != 0) { var addrTarget = TypeInfo.LegalFixedTypes.Contains(rawType) ? $"NativePtr->{field.Name}" : $"&NativePtr->{field.Name}_0"; writer.WriteLine($"public RangeAccessor<{typeStr}> {field.Name} => new RangeAccessor<{typeStr}>({addrTarget}, {field.ArraySize});"); } else if (typeStr.Contains("ImVector")) { var vectorElementType = GetTypeString(field.TemplateType, false); if (TypeInfo.WellKnownTypes.TryGetValue(vectorElementType, out var wellKnown)) { vectorElementType = wellKnown; } if (GetWrappedType(vectorElementType + "*", out var wrappedElementType)) { writer.WriteLine($"public ImPtrVector<{wrappedElementType}> {field.Name} => new ImPtrVector<{wrappedElementType}>(NativePtr->{field.Name}, Unsafe.SizeOf<{vectorElementType}>());"); } else { if (GetWrappedType(vectorElementType, out wrappedElementType)) { vectorElementType = wrappedElementType; } writer.WriteLine($"public ImVector<{vectorElementType}> {field.Name} => new ImVector<{vectorElementType}>(NativePtr->{field.Name});"); } } else { if (typeStr.Contains("*") && !typeStr.Contains("ImVector")) { if (GetWrappedType(typeStr, out var wrappedTypeName)) { writer.WriteLine($"public {wrappedTypeName} {field.Name} => new {wrappedTypeName}(NativePtr->{field.Name});"); } else if (typeStr == "byte*" && IsStringFieldName(field.Name)) { writer.WriteLine($"public NullTerminatedString {field.Name} => new NullTerminatedString(NativePtr->{field.Name});"); } else { writer.WriteLine($"public IntPtr {field.Name} {{ get => (IntPtr)NativePtr->{field.Name}; set => NativePtr->{field.Name} = ({typeStr})value; }}"); } } else { writer.WriteLine($"public ref {typeStr} {field.Name} => ref Unsafe.AsRef<{typeStr}>(&NativePtr->{field.Name});"); } } } foreach (var fd in defs.Functions) { foreach (var overload in fd.Overloads) { if (overload.StructName != td.Name) { continue; } if (overload.IsConstructor) { // TODO: Emit a static function on the type that invokes the native constructor. // Also, add a "Dispose" function or similar. continue; } var exportedName = overload.ExportedName; if (exportedName.StartsWith("ig")) { exportedName = exportedName.Substring(2, exportedName.Length - 2); } if (exportedName.Contains("~")) { continue; } if (overload.Parameters.Any(tr => tr.Type.Contains('('))) { continue; } // TODO: Parse function pointer parameters. var hasVaList = false; for (var i = 0; i < overload.Parameters.Length; i++) { var p = overload.Parameters[i]; var paramType = GetTypeString(p.Type, p.IsFunctionPointer); if (p.Name == "...") { continue; } if (paramType == "va_list") { hasVaList = true; break; } } if (hasVaList) { continue; } var orderedDefaults = overload.DefaultValues.OrderByDescending( kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray(); for (var i = overload.DefaultValues.Count; i >= 0; i--) { var defaults = new Dictionary <string, string>(); for (var j = 0; j < i; j++) { defaults.Add(orderedDefaults[j].Key, orderedDefaults[j].Value); } EmitOverload(writer, overload, defaults, "NativePtr"); } } } writer.PopBlock(); writer.PopBlock(); } } using (var writer = new CSharpCodeWriter(Path.Combine(outputPath, "ImGuiNative.gen.cs"))) { writer.Using("System"); writer.Using("System.Numerics"); writer.Using("System.Runtime.InteropServices"); writer.WriteLine(string.Empty); writer.PushBlock("namespace ImGuiNET"); writer.PushBlock("public static unsafe partial class ImGuiNative"); foreach (var fd in defs.Functions) { foreach (var overload in fd.Overloads) { var exportedName = overload.ExportedName; if (exportedName.Contains("~")) { continue; } if (exportedName.Contains("ImVector_")) { continue; } if (exportedName.Contains("ImChunkStream_")) { continue; } if (overload.Parameters.Any(tr => tr.Type.Contains('('))) { continue; } // TODO: Parse function pointer parameters. var ret = GetTypeString(overload.ReturnType, false); var hasVaList = false; var paramParts = new List <string>(); for (var i = 0; i < overload.Parameters.Length; i++) { var p = overload.Parameters[i]; var paramType = GetTypeString(p.Type, p.IsFunctionPointer); if (p.ArraySize != 0) { paramType = paramType + "*"; } if (p.Name == "...") { continue; } paramParts.Add($"{paramType} {CorrectIdentifier(p.Name)}"); if (paramType == "va_list") { hasVaList = true; break; } } if (hasVaList) { continue; } var parameters = string.Join(", ", paramParts); var isUdtVariant = exportedName.Contains("nonUDT"); var methodName = isUdtVariant ? exportedName.Substring(0, exportedName.IndexOf("_nonUDT")) : exportedName; writer.WriteLine("[SuppressGCTransition]"); if (isUdtVariant) { writer.WriteLine($"[DllImport(\"cimgui\", CallingConvention = CallingConvention.Cdecl, EntryPoint = \"{exportedName}\")]"); } else { writer.WriteLine("[DllImport(\"cimgui\", CallingConvention = CallingConvention.Cdecl)]"); } writer.WriteLine($"public static extern {ret} {methodName}({parameters});"); } } writer.PopBlock(); writer.PopBlock(); } using (var writer = new CSharpCodeWriter(Path.Combine(outputPath, "ImGui.gen.cs"))) { writer.Using("System"); writer.Using("System.Numerics"); writer.Using("System.Runtime.InteropServices"); writer.Using("System.Text"); writer.WriteLine(string.Empty); writer.PushBlock("namespace ImGuiNET"); writer.PushBlock("public static unsafe partial class ImGui"); foreach (var fd in defs.Functions) { if (TypeInfo.SkippedFunctions.Contains(fd.Name)) { continue; } foreach (var overload in fd.Overloads) { var exportedName = overload.ExportedName; if (exportedName.StartsWith("ig")) { exportedName = exportedName.Substring(2, exportedName.Length - 2); } if (exportedName.Contains("~")) { continue; } if (overload.Parameters.Any(tr => tr.Type.Contains('('))) { continue; } // TODO: Parse function pointer parameters. var hasVaList = false; for (var i = 0; i < overload.Parameters.Length; i++) { var p = overload.Parameters[i]; var paramType = GetTypeString(p.Type, p.IsFunctionPointer); if (p.Name == "...") { continue; } if (paramType == "va_list") { hasVaList = true; break; } } if (hasVaList) { continue; } var orderedDefaults = overload.DefaultValues.OrderByDescending( kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray(); for (var i = overload.DefaultValues.Count; i >= 0; i--) { if (overload.IsMemberFunction) { continue; } var defaults = new Dictionary <string, string>(); for (var j = 0; j < i; j++) { defaults.Add(orderedDefaults[j].Key, orderedDefaults[j].Value); } EmitOverload(writer, overload, defaults, null); } } } writer.PopBlock(); writer.PopBlock(); } foreach (var method in defs.Variants) { foreach (var variant in method.Value.Parameters) { if (!variant.Used) { Console.WriteLine($"Error: Variants targetting parameter {variant.Name} with type {variant.OriginalType} could not be applied to method {method.Key}."); } } } }