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}."); } } } }
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}."); } } } }