Example #1
0
        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}.");
                    }
                }
            }
        }
Example #2
0
        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}.");
                    }
                }
            }
        }