soloud-sys 1.1.1

Rust bindings for the soloud audio engine
Documentation
#!/usr/bin/env python3
""" SoLoud C# (cs) wrapper generator """

import soloud_codegen

fo = open("../glue/soloud.cs", "w")


C_TO_CS_TYPES = {
    "string":"string",
    "int":"int",
    "void":"void",
    "const char *":"string",
    "char *":"string",
    "unsigned int":"uint",
    "float":"float",
    "double":"double",
    "float *":"float[]",
    "File *":"SoloudObject",
    "unsigned char *":"IntPtr",
    "const unsigned char *":"IntPtr",
    "unsigned char":"byte",
    "short *":"IntPtr"
}

for soloud_type in soloud_codegen.soloud_type:
    C_TO_CS_TYPES[soloud_type + " *"] = "SoloudObject"


def has_ex_variant(funcname):
    """ Checks if this function has an "Ex" variant """    
    if funcname[-2::] == "Ex":
        # Already an Ex..
        return False
    for func in soloud_codegen.soloud_func:
        if func[1] == (funcname + "Ex"):
            return True
    return False

fo.write("""
// SoLoud wrapper for C# (cs)
// This file is autogenerated; any changes will be overwritten

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace SoLoud
{

public class SoloudObject
{
    public IntPtr objhandle;
}

""")


# Since there's no reason to use the "raw" data anymore,
# skip generating the enum dictionary
#
#fo.write("# Enumerations\n")
#fo.write("soloud_enum = {\n")
#first = True
#for x in soloud_codegen.soloud_enum:
#    if first:
#        first = False
#    else:
#        fo.write(",\n")
#    fo.write('"' + x + '": ' + str(soloud_codegen.soloud_enum[x]))
#fo.write("\n}\n")

fo.write("\n")
# fo.write("# Raw DLL functions\n")
# for x in soloud_codegen.soloud_func:
#     fo.write(x[1] + ' = soloud_dll.' + x[1] + '\n')
#     fo.write(x[1] + '.restype = ' + C_TO_CS_TYPES[x[0]] + '\n')
#     fo.write(x[1] + '.argtypes = [')
#     first = True
#     for y in x[2]:
#         if len(y) > 0:
#             if first:
#                 first = False
#             else:
#                 fo.write(", ")
#             fo.write(fudge_types(y[0]))
#     fo.write(']\n')
#     fo.write('\n')
# 
#################################################################
#
# oop
#

#    class classname : SoloudObject
#    {
#    public const int CONSTANT = 3;
#    [DllImport("soloud_x86.dll", CallingConvention = CallingConvention.Cdecl)]
#    internal static extern int classname_foobar(...
#    public int foobar(...
#    {
#        classname_foobar(...
#    }
#


def fix_default_param(defparam, classname):
    """ 'fixes' default parameters from C to what python expectes """
    if (classname + '::') == defparam[0:len(classname)+2:]:
        return defparam[len(classname)+2::]

    # The C API doesn't use BOOL, so the generated source will use int too,
    # using a boolean as the default param for an int won't work.
    if defparam == "true":
        return "1"
    elif defparam == "false":
        return "0"

    # C# uses . as namespace/class separator
    return defparam.replace("::", ".")

def external_pointer_fix(param):
    if param == "SoloudObject":
        return "IntPtr"
    if param == "string":
        return "[MarshalAs(UnmanagedType.LPStr)] string"
    return param

for x in soloud_codegen.soloud_type:
    first = True
    for y in soloud_codegen.soloud_func:
        if (x + "_") == y[1][0:len(x)+1:]:
            if first:
                fo.write('\n')
                fo.write('public class %s : SoloudObject, IDisposable\n{\n'%(x))
                for z in soloud_codegen.soloud_enum:
                    if z[0:len(x)+1] == x.upper()+'_':
                        s = str(soloud_codegen.soloud_enum[z])
                        fo.write('\tpublic const int %s = %s;\n'%(z[len(x)+1::], s))
                fo.write('\n\t[DllImport("soloud_x86.dll", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern IntPtr %s_create();\n'%(x))
                fo.write('\tpublic %s()\n\t{\n'%(x))
                fo.write('\t\tobjhandle = %s_create();\n'%(x))
                fo.write('\t}\n')
                fo.write('\n\t[DllImport("soloud_x86.dll", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern IntPtr %s_destroy(IntPtr aObjHandle);\n'%(x))
                fo.write('\t~%s()\n\t{\n'%(x))
                fo.write('\t\t%s_destroy(objhandle);\n'%(x))
                fo.write('\t}\n')
                fo.write('\tpublic void Dispose()\n\t{\n')
                fo.write('\t\tif (objhandle != IntPtr.Zero) {\n')
                fo.write('\t\t\t%s_destroy(objhandle);\n'%(x))
                fo.write('\t\t\tobjhandle = IntPtr.Zero;\n')
                fo.write('\t\t\tGC.SuppressFinalize(this);\n')
                fo.write('\t\t}\n')
                fo.write('\t}\n')
                
                first = False
            funcname = y[1][len(x)+1::]
            # If the function has the name "Ex", remove the subfix
            if funcname[-2::] == "Ex":
                funcname = funcname[:len(funcname)-2]
            # Skip generating functions that have an Ex variant            
            if funcname == "create" or funcname == "destroy" or has_ex_variant(y[1]):
                pass # omit create/destroy, handled by __exit__ / close
            else:
                charptr = False
                floatptr = False
                ret = C_TO_CS_TYPES[y[0]]
                if y[0] == 'const char *':                    
                    charptr = True
                    ret = 'IntPtr'
                if y[0] == 'const unsigned char *':                    
                    charptr = True
                    ret = 'IntPtr'
                if y[0] == 'float *':
                    floatptr = True
                    ret = 'IntPtr'
                fo.write('\n\t[DllImport("soloud_x86.dll", CallingConvention = CallingConvention.Cdecl)]\n\tinternal static extern %s %s(IntPtr aObjHandle'%(ret, y[1]))
                for z in y[2]:
                    if len(z) > 1:
                        if z[1] == 'a'+x:
                            pass # skip the 'self' pointer
                        else:
                            fo.write(', ')
                            fo.write(external_pointer_fix(C_TO_CS_TYPES[z[0]]) + ' ' + z[1])
                fo.write(');\n')
                
                fo.write('\tpublic %s %s('%(C_TO_CS_TYPES[y[0]], funcname))
                firstparm = True
                for z in y[2]:
                    if len(z) > 1:
                        if z[1] == 'a'+x:
                            pass # skip the 'self' pointer
                        else:
                            if firstparm:
                                firstparm = False
                            else:
                                fo.write(', ')
                            fo.write(C_TO_CS_TYPES[z[0]] + ' ' + z[1])
                            if len(z) > 2:
                                fo.write(' = ' + fix_default_param(z[2], x))
                fo.write(')\n\t{\n')
                if floatptr:
                    fo.write('\t\tfloat[] ret = new float[256];\n')
                fo.write('\t\t')
                if y[0] == 'void':
                    pass
                elif charptr or floatptr:
                    fo.write('IntPtr p = ')
                else:
                    fo.write('return ')
                fo.write(y[1] + '(objhandle')
                for z in y[2]:
                    if len(z) > 1:
                        if z[1] == 'a'+x:
                            pass # skip the 'self' pointer
                        else:
                            fo.write(', ')
                            fudged_type = C_TO_CS_TYPES[z[0]]
                            if fudged_type == 'SoloudObject':
                                fo.write(z[1] + '.objhandle')
                            else:
                                fo.write(z[1])
                fo.write(');\n')
                if charptr:
                    fo.write('\t\treturn Marshal.PtrToStringAnsi(p);\n')
                if floatptr:
                    fo.write("""
\t\tbyte[] buffer = new byte[4];
\t\tfor (int i = 0; i < ret.Length; ++i)
\t\t{
\t\t\tint f_bits = Marshal.ReadInt32(p, i * 4);
\t\t\tbuffer[0] = (byte)((f_bits >> 0) & 0xff);
\t\t\tbuffer[1] = (byte)((f_bits >> 8) & 0xff);
\t\t\tbuffer[2] = (byte)((f_bits >> 16) & 0xff);
\t\t\tbuffer[3] = (byte)((f_bits >> 24) & 0xff);
\t\t\tret[i] = BitConverter.ToSingle(buffer, 0);
\t\t}
\t\treturn ret;
""")
                fo.write('\t}\n')
    if not first:
        fo.write('}\n')

fo.write('}\n')

print("soloud.cs generated")

fo.close()