blob: 7313fdd3a4869ecabd45f2f5f8bf9b90c8b7581a [file] [log] [blame]
import json
import sys
import operator
from functools import reduce
structlist = {}
typedeflist = {}
enumlist = {}
# These are not exported to the json, so we just maintain the small subset here by hand for now.
structparents = {
'VRTextureWithPose_t' : 'Texture_t',
'VRTextureWithDepth_t' : 'Texture_t',
'VRTextureWithPoseAndDepth_t' : 'VRTextureWithPose_t' }
structindices = {}
def loadfile(filename, ns):
with open(filename) as data_file:
data = json.load(data_file)
i = 0
for struct in data['structs']:
if(len(struct) > 0 and len(struct['struct'])):
structname = struct['struct']
structlist[structname] = 'struct'
structindices[getclasswithoutnamespace(structname)] = i
i += 1
for typedef in data['typedefs']:
if(len(typedef) > 0 and len(typedef['typedef'])):
typedeflist[typedef['typedef']] = typedef['type']
for enum in data['enums']:
enumname = enum['enumname']
namespace = getnamespace(enumname)
if(len(enum) > 0 and len(enum['enumname'])):
enumlist[enum['enumname'].replace('::','_')] = enum['enumname']
return data
def paramshavelength(params, paramname):
checkname = paramname[1:]
for param in params:
name = param['paramname']
name = name[3:len(name)-4]
sys.stdout.write ('// ' + name + ' ' + checkname + '\n');
if(name == checkname):
return True
return False
def calculateinteropmethodname(classname, methodname):
return classname.replace('vr::', 'VR::').replace('::', '_') + '_' + methodname
def unmanagedtype(thetype):
if(thetype == 'float'):
return 'R4'
if(thetype == 'double'):
return 'R8'
if(thetype == 'char'):
return 'I1'
if(thetype == 'byte'):
return 'U1'
if(thetype == 'ulong'):
return 'U8'
if(thetype == 'uint'):
return 'U4'
if(thetype == 'CSteamID'): # Special case for CSteamID which is a complex C def but is 64-bits.
return 'U8'
return 'Struct'
def ctype(thetype):
thetype = thetype.replace('vr::','')
if(thetype[0:6] == 'const '):
thetype = thetype[6:]
if(thetype[0:6] == 'class '): # for C replaces classes by pointers
thetype = 'intptr_t'
if(thetype == 'ulong'):
return 'unsigned long long'
if(thetype == 'uint'):
return 'unsigned long'
return thetype
def striparraysuffix(thetype):
if(thetype[len(thetype) - 1] == ']'):
thetype = thetype[0:thetype.find('[')-1]
if(thetype == '_Bool'):
thetype = 'bool'
return thetype
def converttype(thetype):
if(thetype[0:11] == 'struct vr::'):
thetype = thetype[11:]
if(thetype[0:17] == 'const struct vr::'):
thetype = thetype[17:]
if(thetype[0:10] == 'class vr::'):
thetype = thetype[10:]
if(thetype == 'const char *const'):
thetype = 'string'
#thetype = thetype.replace('::', '_')
thetypewithnamespace = thetype
thetype = getclasswithoutnamespace(thetype)
if(thetype[0:6] == 'const '):
thetype = thetype[6:]
if(thetype == 'class CSteamID'):
thetype = 'ulong'
if(thetype == 'uint8'):
thetype = 'byte'
if(thetype == 'uint8_t'):
thetype = 'byte'
if(thetype == 'unsigned short'):
thetype = 'ushort'
if(thetype == 'unsigned char'):
thetype = 'byte'
#if(thetype == 'unsigned int'):
# thetype = 'uint'
if(thetype == 'uint16'):
thetype = 'ushort'
if(thetype == 'uint16_t'):
thetype = 'ushort'
if(thetype == 'uint32'):
thetype = 'uint'
if(thetype == 'uint32_t'):
thetype = 'uint'
if(thetype == 'uint64'):
thetype = 'ulong'
if(thetype == 'short'):
thetype = 'short'
if(thetype == 'int32'):
thetype = 'int'
if(thetype == 'int32_t'):
thetype = 'int'
if(thetype == 'int64'):
thetype = 'long'
if(thetype == 'uint64_t'):
thetype = 'ulong'
if(thetype == '_Bool'):
thetype = 'bool'
if(thetype == 'char *'):
thetype = 'string'
if(thetype == 'char **'):
thetype = 'string'
if(thetype == 'void *'):
thetype = 'IntPtr'
if(thetype == 'byte *'):
thetype = 'IntPtr'
if(thetype == 'uint8 *'):
thetype = 'IntPtr'
if(thetype == 'T *'):
thetype = 'IntPtr'
if(thetype == 'func_t'):
thetype = 'IntPtr'
if(thetype == 'RenderModel_t *'):
thetype = 'IntPtr'
if(thetype == 'RenderModel_TextureMap_t *'):
thetype = 'IntPtr'
if(thetype == 'struct VkPhysicalDevice_T *'):
thetype = 'IntPtr'
if(thetype == 'struct VkInstance_T *'):
thetype = 'IntPtr'
if(thetype == 'SteamLeaderboard_t'):
thetype = 'ulong'
if(thetype == 'SteamLeaderboardEntries_t'):
thetype = 'ulong'
if(thetype == 'ScreenshotHandle'):
thetype = 'uint'
if(thetype == 'AudioPlayback_Status'):
thetype = 'int'
if(thetype == 'FriendsGroupID_t'):
thetype = 'char'
if(thetype == 'EOverlayToStoreFlag'):
thetype = 'char'
if(thetype == 'ESteamAPICallFailure'):
thetype = 'int'
if(thetype == 'EGamepadTextInputMode'):
thetype = 'int'
if(thetype == 'EGamepadTextInputLineMode'):
thetype = 'int'
if(thetype == 'EUniverse'):
thetype = 'int'
if(thetype == 'ISteamHTMLSurface::EHTMLMouseButton'):
thetype = 'int'
if(thetype == 'ISteamHTMLSurface::EHTMLKeyModifiers'):
thetype = 'int'
if(thetype == 'SteamInventoryResult_t'):
thetype = 'int'
if(thetype == 'RTime32'):
thetype = 'ulong'
if(thetype == 'AccountID_t'):
thetype = 'uint'
if(thetype == 'AppId_t'):
thetype = 'uint'
if(thetype == 'VRComponentProperties'):
thetype = 'uint'
if(thetype == 'SteamAPICall_t'):
thetype = 'ulong'
if(thetype == 'UGCQueryHandle_t'):
thetype = 'ulong'
if(thetype == 'UGCUpdateHandle_t'):
thetype = 'ulong'
if(thetype == 'ClientUnifiedMessageHandle'):
thetype = 'ulong'
if(thetype == 'UGCFileWriteStreamHandle_t'):
thetype = 'ulong'
if(thetype == 'PublishedFileUpdateHandle_t'):
thetype = 'ulong'
if(thetype == 'SteamAPIWarningMessageHook_t'):
thetype = 'IntPtr'
if(thetype == 'SteamAPI_PostAPIResultInProcess_t'):
thetype = 'IntPtr'
if(thetype == 'SteamAPI_CheckCallbackRegistered_t'):
thetype = 'IntPtr'
if(thetype == 'class CGameID'):
thetype = 'ulong'
if(thetype == 'PublishedFileId_t'):
thetype = 'ulong'
if(thetype == 'UGCHandle_t'):
thetype = 'ulong'
if(thetype == 'SNetListenSocket_t'):
thetype = 'uint'
if(thetype == 'SNetSocket_t'):
thetype = 'uint'
if(thetype == 'TrackedDeviceIndex_t'):
thetype = 'uint'
if(thetype == 'VROverlayHandle_t'):
thetype = 'ulong'
if(thetype == 'PropertyTypeTag_t'):
thetype = 'uint'
if(thetype == 'PropertyContainerHandle_t'):
thetype = 'ulong'
if(thetype == 'SpatialAnchorHandle_t'):
thetype = 'uint'
if(thetype == 'DriverHandle_t'):
thetype = 'ulong'
if(thetype == 'VRActionHandle_t'):
thetype = 'ulong'
if(thetype == 'VRActionSetHandle_t'):
thetype = 'ulong'
if(thetype == 'VRInputValueHandle_t'):
thetype = 'ulong'
if(thetype == 'VRInputComponentHandle_t'):
thetype = 'ulong'
if(thetype == 'VRInputOriginHandle_t'):
thetype = 'ulong'
if(thetype == 'PathHandle_t'):
thetype = 'ulong'
if(thetype == 'VRNotificationId'):
thetype = 'uint'
if(thetype == 'TextureID_t'):
thetype = 'int'
if(thetype == 'WebConsoleHandle_t'):
thetype = 'ulong'
if(thetype == 'vrshared_double'):
thetype = 'double'
if(thetype == 'vrshared_uint64_t'):
thetype = 'ulong'
if(thetype[0:7] == 'struct '):
thetype = thetype[7:]
if(thetype[0:6] == 'class '):
thetype = thetype[6:]
if(thetype[0:6] == 'union '):
thetype = thetype[6:]
if(thetype[0] == 'H' and thetype[0:3] != "Hmd" and thetype[0:16] != "HiddenAreaMesh_t"):
thetype = 'uint'
if(thetype[0:5] == 'enum '):
thetype = thetype[5:]
if(thetype[len(thetype) - 1] == '*' and thetype[len(thetype) - 2] == '*'):
thetype = 'IntPtr'
if(thetype[len(thetype) - 1] == '*'):
if("VRControllerState_t" not in thetype):
thetype = converttype(thetypewithnamespace[:len(thetypewithnamespace)-2])
else:
thetype = converttype(thetype[:len(thetype)-2])
# thetype = 'IntPtr'
if(thetype[len(thetype) - 1] == '&'):
thetype = 'IntPtr'
if(thetype[len(thetype) - 1] == ']'):
thetype = thetype[0:thetype.find('[')-1]
if(thetype in typedeflist):
thetype = converttype(typedeflist[thetype])
if(thetypewithnamespace in typedeflist):
thetype = converttype(typedeflist[thetypewithnamespace])
return thetype
def getnamespace(classname):
if(classname.find('::') != -1):
return classname[:classname.find('::')]
else:
return ''
def getclasswithoutnamespace(classname):
if(classname.rfind('::') != -1):
return classname[classname.rfind('::')+2:]
else:
return classname
def outputenums(namespace, data):
for enum in data['enums']:
if(len(enum) > 0 and len(enum['enumname'])):
ns = getnamespace(enum['enumname'])
enumname = getclasswithoutnamespace(enum['enumname'])
if(ns == namespace or (namespace == '' and ns[:1] == 'I')):
print('public enum '+enumname+'\n{')
enumNameWithoutE = enumname
if( enumname.startswith( "E" ) ):
enumNameWithoutE = enumname[1:]
for enumvalue in enum['values']:
entry = enumvalue['name']
if(entry.startswith(enumname)): entry = entry[len(enumname):] # strip off enum name
if(entry.startswith(enumNameWithoutE)): entry = entry[len(enumNameWithoutE):] # strip off enum name
if(entry.startswith('_')): entry = entry[1:] # strip off leading underscore
print('\t'+entry+' = '+enumvalue['value']+',')
print('}')
def outputstructfields(struct, data):
# recursively add base class fields first
basename = getclasswithoutnamespace(struct['struct'])
if basename in structparents:
parentname = structparents[basename]
i = structindices[parentname];
outputstructfields(data['structs'][i], data)
for enumvalue in struct['fields']:
fieldtype = enumvalue['fieldtype']
otype = converttype(fieldtype)
lastchar = fieldtype[len(fieldtype) - 1]
if(lastchar == ']'):
#print('\tpublic fixed '+otype+' '+enumvalue['fieldname']+ fieldtype[fieldtype.find('['):]+';')
dims = map(int, fieldtype[fieldtype.find('[')+1:-1].split(']['))
size = reduce(operator.mul, dims, 1)
utype = unmanagedtype(otype)
if(otype == 'char'):
#print('\t[MarshalAs(UnmanagedType.ByValTStr, SizeConst = '+str(size)+')]')
sys.stdout.write('public byte '+enumvalue['fieldname']+'0')
for i in range(1, size):
sys.stdout.write(',' + enumvalue['fieldname'] + str(i))
print(';')
print('public string ' + enumvalue['fieldname'] + '\n{')
print('get\n{')
print('return new string(new char[] {')
for i in range(0, size-1):
print('(char)' + enumvalue['fieldname'] + str(i) + ',')
print('(char)' + enumvalue['fieldname'] + str(size-1) + '}).TrimEnd(\'\\0\');')
print('}')
print('}')
else:
#print('\t[MarshalAs(UnmanagedType.ByValArray, SizeConst = '+str(size)+', ArraySubType = UnmanagedType.'+utype+')]')
for i in range(size):
print('\tpublic '+otype+' '+enumvalue['fieldname']+str(i)+';'+(' //'+otype+fieldtype[fieldtype.find('['):] if(i==0) else ''))
else:
if(otype == 'bool'): print('\t[MarshalAs(UnmanagedType.I1)]')
if(lastchar == '*'):
print('\tpublic IntPtr '+enumvalue['fieldname']+';'+' // '+fieldtype)
else:
print('\tpublic '+otype+' '+enumvalue['fieldname']+';')
def outputstructs(namespace, data):
for struct in data['structs']:
if(len(struct) > 0 and len(struct['struct'])):
structname = struct['struct']
#print('//'+structname)
ns = getnamespace(structname)
basename = getclasswithoutnamespace(structname)
if(basename != '(anonymous)' and (ns == namespace or (namespace == '' and ns[:1] == 'I') or (namespace == '' and ns[:1] == 'C'))):
for key, value in typedeflist.items():
if(converttype(value) == basename):
basename = getclasswithoutnamespace(key)
break
print('[StructLayout(LayoutKind.Sequential)] public struct '+basename+'\n{')
outputstructfields(struct, data)
if (basename == 'HmdMatrix34_t'):
print('#if UNITY_5_3_OR_NEWER')
print('\n\tpublic Vector3 GetPosition()')
print('\t{')
print('\t\treturn new Vector3(m3, m7, -m11);')
print('\t}')
print('\n\tpublic bool IsRotationValid()')
print('\t{')
print('\t\treturn ((m2 != 0 || m6 != 0 || m10 != 0) && (m1 != 0 || m5 != 0 || m9 != 0));')
print('\t}')
print('\n\tpublic Quaternion GetRotation()')
print('\t{')
print('\t\tif (IsRotationValid())')
print('\t\t{')
print('\t\t\tfloat w = Mathf.Sqrt(Mathf.Max(0, 1 + m0 + m5 + m10)) / 2;')
print('\t\t\tfloat x = Mathf.Sqrt(Mathf.Max(0, 1 + m0 - m5 - m10)) / 2;')
print('\t\t\tfloat y = Mathf.Sqrt(Mathf.Max(0, 1 - m0 + m5 - m10)) / 2;')
print('\t\t\tfloat z = Mathf.Sqrt(Mathf.Max(0, 1 - m0 - m5 + m10)) / 2;')
print('\n\t\t\t_copysign(ref x, -m9 - -m6);')
print('\t\t\t_copysign(ref y, -m2 - -m8);')
print('\t\t\t_copysign(ref z, m4 - m1);')
print('\n\t\t\treturn new Quaternion(x, y, z, w);')
print('\t\t}')
print('\t\treturn Quaternion.identity;')
print('\t}')
print('\n\tprivate static void _copysign(ref float sizeval, float signval)')
print('\t{')
print('\t\tif (signval > 0 != sizeval > 0)')
print('\t\t\tsizeval = -sizeval;')
print('\t}')
print('#endif')
print('}')
else:
print('}')
# The following structures were originally compiled with pack(4) on Linux & OSX by mistake. Generate
# a packed version of the structure so we can workaround this in the C# interop code
if ((basename == 'RenderModel_t') or
(basename == 'VRControllerState_t') or
(basename == 'RenderModel_TextureMap_t') or
(basename == 'VREvent_t')):
print('// This structure is for backwards binary compatibility on Linux and OSX only')
print('[StructLayout(LayoutKind.Sequential, Pack = 4)] public struct '+basename+'_Packed\n{')
outputstructfields(struct, data)
print('\tpublic '+basename+'_Packed('+basename+' unpacked)');
print('\t{')
for enumvalue in struct['fields']:
fieldtype = enumvalue['fieldtype']
otype = converttype(fieldtype)
if fieldtype[-1] == ']':
dims = map(int, fieldtype[fieldtype.find('[')+1:-1].split(']['))
size = reduce(operator.mul, dims, 1)
utype = unmanagedtype(otype)
for i in range(size):
print('\t\tthis.'+enumvalue['fieldname']+str(i)+' = unpacked.'+enumvalue['fieldname']+str(i)+';')
else:
print('\t\tthis.'+enumvalue['fieldname']+' = unpacked.'+enumvalue['fieldname']+';')
print('\t}')
print('\tpublic void Unpack(ref '+basename+' unpacked)');
print('\t{')
for enumvalue in struct['fields']:
fieldtype = enumvalue['fieldtype']
otype = converttype(fieldtype)
if fieldtype[-1] == ']':
dims = map(int, fieldtype[fieldtype.find('[')+1:-1].split(']['))
size = reduce(operator.mul, dims, 1)
utype = unmanagedtype(otype)
for i in range(size):
print('\t\tunpacked.'+enumvalue['fieldname']+str(i)+' = this.'+enumvalue['fieldname']+str(i)+';')
else:
print('\t\tunpacked.'+enumvalue['fieldname']+' = this.'+enumvalue['fieldname']+';')
print('\t}')
print('}')
def outputinterfaces(namespace, data):
lastclass = ''
lastmethod = ''
for method in data['methods']:
if (len(method) > 0):
returntype = converttype(method['returntype'])
if(method['returntype'][len(method['returntype']) - 1] == '*'):
# Native methods which return pointers are cast to IntPtr
returntype = 'IntPtr'
methodname = method['methodname']
if(methodname == lastmethod):
methodname = methodname + repr(count)
count = count + 1
else:
count = 0
lastmethod = method['methodname']
classname = method['classname']
if(namespace != getnamespace(classname)):
continue
classname = getclasswithoutnamespace(classname)
if(classname != lastclass):
if(lastclass != ''):
print("\t}\n");
print("\t[StructLayout(LayoutKind.Sequential)]")
print("\tpublic struct " + classname + "\n\t{")
lastclass = classname
paramlist = []
if('params' in method):
for param in method['params']:
paramtype = converttype(param['paramtype'])
if(param['paramtype'][len(param['paramtype']) - 1] == '*' and param['paramtype'][len(param['paramtype']) - 2] == '*'):
paramlist.append('ref ' + paramtype + ' ' + param['paramname'])
elif(param['paramtype'][len(param['paramtype']) - 1] == '*'):
if('out_array_count' in param) or ('array_call' in param) or ('array_count' in param) or ('out_array_call' in param):
paramlist.append('[In, Out] ' + paramtype + '[] ' + param['paramname'])
elif('out_string_count' in param):
paramlist.append('System.Text.StringBuilder ' + param['paramname'])
elif('out_struct' in param):
paramlist.append('ref '+ paramtype +' ' + param['paramname'])
elif('out_string' in param):
paramlist.append('System.Text.StringBuilder ' + param['paramname'])
# elif(structlist.has_key(paramtype)):
# paramlist.append(paramtype+' ' + param['paramname'])
# elif(paramtype[0:1] == 'I' and paramtype != 'IntPtr'):
# paramlist.append('IntPtr ' + param['paramname'])
elif (paramtype == 'uint' or paramtype == 'int' or paramtype == 'char' or paramtype == 'bool'):
# Output params for ints,uints,char
paramlist.append('ref ' + paramtype + ' ' + param['paramname'])
elif (paramtype == 'string' and param['paramtype'] == 'const char *'):
# Managed strings on Windows need to be converted to Utf8 native ptrs
paramlist.append('IntPtr ' + param['paramname'])
elif (paramtype == 'string' or paramtype == 'IntPtr'):
# Strings and IntPtrs are just straight
paramlist.append(paramtype + ' ' + param['paramname'])
else:
# paramlist.append('ref ' + converttype(paramtype) + ' ' + param['paramname'])
paramlist.append('ref ' + paramtype + ' ' + param['paramname'])
else:
paramlist.append(paramtype + ' ' + param['paramname'])
if(paramtype == 'bool'):
paramlist[-1] = '[MarshalAs(UnmanagedType.I1)] ' + paramlist[-1];
print("\t\t[UnmanagedFunctionPointer(CallingConvention.StdCall)]")
if(returntype == 'bool'):
print("\t\t[return: MarshalAs(UnmanagedType.I1)]")
print("\t\tinternal delegate "+returntype+" _"+methodname+"("+", ".join(paramlist)+");")
print("\t\t[MarshalAs(UnmanagedType.FunctionPtr)]")
print("\t\tinternal _"+methodname+" "+methodname+";\n")
print("\t}\n\n");
def outputfntables(namespace, data):
lastclass = ''
lastmethod = ''
for method in data['methods']:
if (len(method) > 0):
returntype = method['returntype']
if(returntype == 'class CSteamID'):
returntype = 'uint64'
methodname = method['methodname']
if(methodname == lastmethod):
methodname = methodname + repr(count)
count = count + 1
else:
count = 0
lastmethod = method['methodname']
classname = method['classname']
if(namespace != getnamespace(classname)):
continue
classname = getclasswithoutnamespace(classname)
if(classname != lastclass):
if(lastclass != ''):
print("};\n");
fntablename = method['classname'].replace('vr::', 'VR::').replace('::', '_') + '_FnTable'
print("struct " + fntablename + "\n{")
lastclass = classname
sys.stdout.write ('\t'+ ctype(returntype) + ' (OPENVR_FNTABLE_CALLTYPE *' + methodname + ')')
paramlist = []
if('params' in method):
for param in method['params']:
paramlist.append(ctype(param['paramtype']) + ' ' + param['paramname'])
print('('+", ".join(paramlist)+");")
print("};\n");
def outputfntabledecls(namespace, data):
lastclass = ''
lastmethod = ''
fntablename = ''
for method in data['methods']:
if (len(method) > 0):
returntype = method['returntype']
if(returntype == 'class CSteamID'):
returntype = 'uint64'
methodname = method['methodname']
if(methodname == lastmethod):
methodname = methodname + repr(count)
count = count + 1
else:
count = 0
lastmethod = method['methodname']
classname = method['classname']
if(namespace != getnamespace(classname)):
continue
# Skip over destructors, we don't use any aside from dummy ones, and
# destroying our interfaces would be broken anyway.
if(methodname[:8] == 'Destruct'):
continue
classname = getclasswithoutnamespace(classname)
if(classname != lastclass):
if(lastclass != ''):
print("};\n\n");
fntablename = method['classname'].replace('vr::', 'VR::').replace('::', '_') + '_FnTable'
print("static " + fntablename + " g_" + fntablename + " =\n{")
lastclass = classname
print('\t&' + fntablename + '_' + methodname + ',')
print("};\n\n");
def outputfntableinit(namespace, data):
print("extern void *FindInterface( const char *pchInterfaceName );\n")
print("void InitializeInterfaceFnTables()\n{")
lastclass = ''
for method in data['methods']:
if (len(method) > 0):
classname = method['classname']
if(namespace != getnamespace(classname)):
continue
classname = getclasswithoutnamespace(classname)
if(classname != lastclass):
instancename = 'g_p' + classname
print("\t" + instancename + " = ( " + namespace + "::" + classname + " * )FindInterface( " + namespace + "::" + classname + "_Version );")
lastclass = classname
print("}\n")
def outputfntableaccess(namespace, data):
lastclass = ''
for method in data['methods']:
if (len(method) > 0):
classname = method['classname']
if(namespace != getnamespace(classname)):
continue
classname = getclasswithoutnamespace(classname)
if(classname != lastclass):
fntablename = method['classname'].replace('vr::', 'VR::').replace('::', '_') + '_FnTable'
print('FnTableRegistration autoreg_'+fntablename+'( '+classname+'_Version, &g_'+fntablename+' );')
lastclass = classname
def outputfntablefuncs(namespace, data):
lastclass = ''
lastmethod = ''
instancename = ''
for method in data['methods']:
if (len(method) > 0):
returntype = method['returntype']
if(returntype == 'class CSteamID'):
returntype = 'uint64'
methodname = method['methodname']
if(methodname == lastmethod):
methodname = methodname + repr(count)
count = count + 1
else:
count = 0
lastmethod = method['methodname']
classname = method['classname']
if(namespace != getnamespace(classname)):
continue
# Skip over destructors, we don't use any aside from dummy ones, and
# destroying our interfaces would be broken anyway.
if(method['methodname'][:8] == 'Destruct'):
continue
classname = getclasswithoutnamespace(classname)
if(classname != lastclass):
if(lastclass != ''):
print("\n\n");
instancename = 'g_p' + classname
print("/** " + classname + " FnTable functions */\n")
print("static " + namespace + "::" + classname + " *" + instancename + " = nullptr;\n")
lastclass = classname
creturntype = ctype(returntype)
sys.stdout.write ('static '+ creturntype + ' OPENVR_FNTABLE_CALLTYPE '+method['classname'].replace('vr::', 'VR::').replace('::', '_') + '_FnTable_' + methodname)
paramlist = []
if('params' in method):
for param in method['params']:
paramlist.append(ctype(param['paramtype'])+' '+param['paramname'])
print('('+", ".join(paramlist)+")\n{")
paramlist = []
if('params' in method):
for param in method['params']:
paramtype = param['paramtype']
cparamtype = ctype(paramtype)
if paramtype != cparamtype:
if paramtype.startswith('struct'):
paramlist.append('*('+paramtype+'*)&'+param['paramname'])
else:
paramlist.append('('+paramtype+')'+param['paramname'])
else:
paramlist.append(param['paramname'])
# if the method is a destructor
if(method['methodname'][:8] == 'Destruct'):
sys.stdout.write ('delete instance;')
# void returntype does not return
elif (returntype == 'void'):
sys.stdout.write (instancename+'->'+method['methodname']+'(')
if('params' in method):
sys.stdout.write(", ".join(paramlist))
sys.stdout.write (');')
elif (creturntype.startswith('struct') and creturntype != returntype):
sys.stdout.write (returntype+' result = '+instancename+'->'+method['methodname']+'(')
if('params' in method):
sys.stdout.write(", ".join(paramlist))
sys.stdout.write (');\n')
sys.stdout.write ('return *('+creturntype+'*)&result;')
else:
if creturntype != returntype:
sys.stdout.write ('return ('+creturntype+')'+instancename+'->'+method['methodname']+'(')
else:
sys.stdout.write ('return '+instancename+'->'+method['methodname']+'(')
if('params' in method):
sys.stdout.write(", ".join(paramlist))
sys.stdout.write (');')
sys.stdout.write ('\n}\n');
###############################
# OUTPUT CLASSES
###############################
def isparamptr(paramname, paramlist):
for param in paramlist:
if(param['paramname'] == paramname):
return param['paramtype'][len(param['paramtype']) - 1] == '*'
def outputclasses(namespace, data):
print("""public class Utils
{
public static IntPtr ToUtf8(string managedString)
{
if (managedString == null)
{
return IntPtr.Zero;
}
int size = System.Text.Encoding.UTF8.GetByteCount(managedString) + 1;
if (buffer.Length < size) buffer = new byte[size];
int written = System.Text.Encoding.UTF8.GetBytes(managedString, 0, managedString.Length, buffer, 0);
buffer[written] = 0x00; // null terminate
IntPtr nativeUtf8 = Marshal.AllocHGlobal(written+1);
Marshal.Copy(buffer, 0, nativeUtf8, written+1);
return nativeUtf8;
}
private static byte[] buffer = new byte[1024];
}
""")
# the following methods take a mispacked VRControllerState_t on Linux and OSX so we generate
# a special hacky method to account for that on those platforms.
controller_packed_methods = ['GetControllerState', 'GetControllerStateWithPose', 'GetComponentState']
event_packed_methods = ['PollNextEvent', 'PollNextEventWithPose', 'PollNextOverlayEvent', 'PollNextOverlayEventForAny']
if(namespace == ''):
namespaceextra = ''
elif(namespace == 'vr'):
namespaceextra = 'VR_'
else:
namespaceextra = namespace+'_'
lastclass = ''
lastmethod = ''
for method in data['methods']:
if (len(method) > 0):
returntype = converttype(method['returntype'])
interfacename = method['classname']
if(namespace != getnamespace(interfacename)):
continue
interfacename = getclasswithoutnamespace(interfacename)
methodname = method['methodname']
if(methodname == lastmethod):
methodname = methodname + repr(count)
count = count + 1
else:
count = 0
lastmethod = method['methodname']
if(interfacename != lastclass):
if(lastclass != ''):
print("}\n\n");
classname = 'C' + interfacename[1:]
classshort = interfacename[1:]
print("public class " + classname+"\n{")
print(interfacename+" FnTable;")
print("internal " + classname + "(IntPtr pInterface)")
print("{")
print("\tFnTable = ("+interfacename+")Marshal.PtrToStructure(pInterface, typeof("+interfacename+"));")
print("}")
lastclass = interfacename
paramlist = []
# Native params are always ref and include count params
nativeparamlist = []
skipparam = ''
outarray = 0
outstruct = 0
twocalls = 0
skipref = 0
arraytype = ''
arrayname = ''
arrayreplace = ''
skipcount = ''
skiptype = ''
paramtypelist = []
if('params' in method):
for param in method['params']:
paramtype = converttype(param['paramtype'])
if(param['paramname'] != skipparam):
if('out_array_count' in param):
paramlist.append(param['paramname'])
paramtypelist.append('out ' + paramtype + ' [] ' + param['paramname'])
nativeparamlist.append(param['paramname'])
skipparam = param['out_array_count']
arraytype = paramtype
arrayname = param['paramname']
arrayreplace = 'null'
twocalls = 1
outarray = 1
skipref = 1
elif('out_string_count' in param):
paramlist.append(param['paramname'])
paramtypelist.append('out string ' + param['paramname'])
nativeparamlist.append('pStrBuffer')
skipparam = param['out_string_count']
arraytype = paramtype
arrayname = 'pStrBuffer'
arrayreplace = 'null'
twocalls = 1
skipref = 1
elif('out_array_call' in param):
paramlist.append(param['paramname'])
paramtypelist.append('out ' + paramtype + ' [] ' + param['paramname'])
nativeparamlist.append(param['paramname'])
skipparam = param['out_array_call'].split(',')[0]
arraytype = paramtype
arrayname = param['paramname']
arrayreplace = 'null'
outarray = 1
skipref = 0
elif('out_string_count' in param):
paramlist.append(param['paramname'])
paramtypelist.append('out ' + paramtype + ' ' + param['paramname'])
nativeparamlist.append('ref ' + param['paramname'])
skipparam = param['out_string_count']
elif('out_struct' in param):
paramlist.append(param['paramname'])
paramtypelist.append('out ' + paramtype + ' ' + param['paramname'])
nativeparamlist.append('ref ' + param['paramname'])
elif('array_count' in param):
paramlist.append(param['paramname'])
paramtypelist.append(paramtype + ' [] ' + param['paramname'])
nativeparamlist.append(param['paramname'])
skipparam = param['array_count']
skipcount = param['paramname']
# elif(paramtype[0:1] == 'I' and paramtype != 'IntPtr' and not enumlist.has_key(paramtype)):
# #print("//" + paramtype)
# paramlist.append(param['paramname'])
# paramtypelist.append(paramtype + ' ' + param['paramname'])
# #nativeparamlist.append('ptr')
# nativeparamlist.append(param['paramname'] + '.GetIntPtr()')
# elif (structlist.has_key(paramtype)):
# paramlist.append(' ' + param['paramname'])
# paramtypelist.append(' ' + paramtype + ' ' + param['paramname'])
# nativeparamlist.append(' ' + param['paramname'])
elif (param['paramtype'][len(param['paramtype']) - 1] == '*' and param['paramtype'][len(param['paramtype']) - 2] == '*'):
paramlist.append('ref ' + param['paramname'])
paramtypelist.append('ref ' + paramtype + ' ' + param['paramname'])
nativeparamlist.append('ref ' + param['paramname'])
if(paramtype != 'uint' and paramtype != 'int' and paramtype != 'char' and paramtype != 'bool'):
outstruct = 1
arraytype = paramtype
arrayname = param['paramname']
arrayreplace = 'null'
elif (param['paramtype'][len(param['paramtype']) - 1] == '*' and paramtype != 'string' and paramtype != 'IntPtr'):
paramlist.append('ref ' + param['paramname'])
paramtypelist.append('ref ' + paramtype + ' ' + param['paramname'])
nativeparamlist.append('ref ' + param['paramname'])
if(paramtype != 'uint' and paramtype != 'int' and paramtype != 'char' and paramtype != 'bool'):
outstruct = 1
arraytype = paramtype
arrayname = param['paramname']
arrayreplace = 'null'
elif ('out_string' in param):
paramlist.append(param['paramname'])
paramtypelist.append('System.Text.StringBuilder ' + param['paramname'])
nativeparamlist.append(param['paramname'])
elif (paramtype == 'string' and param['paramtype'] == 'const char *'):
paramlist.append(param['paramname'])
paramtypelist.append(paramtype + ' ' + param['paramname'])
nativeparamlist.append(param['paramname']+'Utf8')
else:
paramlist.append(param['paramname'])
paramtypelist.append(paramtype + ' ' + param['paramname'])
nativeparamlist.append(param['paramname'])
else:
skiptype = paramtype
if(skipref):
nativeparamlist.append('ref ' + param['paramname'])
elif(skipcount != ''):
nativeparamlist.append("(" + paramtype +") " + skipcount + ".Length" )
else:
nativeparamlist.append(param['paramname'])
skipparam = 0
if methodname in controller_packed_methods or methodname in event_packed_methods:
if methodname in controller_packed_methods:
packedlist=[w.replace('VRControllerState_t','VRControllerState_t_Packed').replace('string', 'IntPtr') for w in paramtypelist]
else:
packedlist=[w.replace('VREvent_t','VREvent_t_Packed') for w in paramtypelist]
print('// This is a terrible hack to workaround the fact that VRControllerState_t and VREvent_t were ')
print('// originally mis-compiled with the wrong packing for Linux and OSX.')
print('[UnmanagedFunctionPointer(CallingConvention.StdCall)]')
print('internal delegate '+returntype+' _'+methodname+'Packed('+','.join(packedlist)+');')
print('[StructLayout(LayoutKind.Explicit)]')
print('struct '+methodname+'Union\n{')
print('\t[FieldOffset(0)]\n\tpublic '+interfacename+'._'+methodname+' p'+methodname+';')
print('\t[FieldOffset(0)]\n\tpublic _'+methodname+'Packed p'+methodname+'Packed;\n}')
sys.stdout.write ('public '+returntype+' '+methodname+'('+','.join(paramtypelist)+')\n{\n')
# PROCESS THE STUFF BEFORE THE API CALLS
if('params' in method):
for param in method['params']:
if(skipparam != param['paramname']):
paramtype = converttype(param['paramtype'])
if('out_array_count' in param and skipparam != param['out_array_count']):
skipparam = param['out_array_count']
sys.stdout.write( '\t'+skiptype+' '+skipparam+' = 0;\n' )
elif('out_string_count' in param and skipparam != param['out_string_count']):
skipparam = param['out_string_count']
#if(isparamptr(skipparam, param)):
sys.stdout.write( '\t'+skiptype+' '+skipparam+' = 0;\n' )
elif('out_array_call' in param):
vals = param['out_array_call'].split(',')
sys.stdout.write( '\tint '+vals[0]+' = '+vals[1]+' ('+vals[2]+');\n' )
sys.stdout.write( '\t'+param['paramname']+' = new '+paramtype+'['+vals[0]+'];\n' )
elif('out_struct' in param):
sys.stdout.write( '\t'+param['paramname']+' = new '+paramtype+'();\n' )
elif (param['paramtype'][len(param['paramtype']) - 1] == '*' and (paramtype == 'int' or paramtype == 'uint' or paramtype == 'float' or paramtype == 'long' or paramtype == 'ulong' or paramtype == 'double') and not 'array_count' in param):
# Out parameters need to be initialized to zero
sys.stdout.write( '\t'+param['paramname']+' = 0;\n' )
elif (param['paramtype'][len(param['paramtype']) - 2:] == '**' and (paramtype == 'string')):
# Out parameters need to be initialized to zero
sys.stdout.write( '\t'+param['paramname']+' = "";\n' )
elif (param['paramtype'][len(param['paramtype']) - 1] == '*' and (paramtype == 'char')):
# Out parameters need to be initialized to zero
sys.stdout.write( '\t'+param['paramname']+' = (char) 0;\n' )
elif (param['paramtype'][len(param['paramtype']) - 1] == '*' and (paramtype == 'bool')):
# Out parameters need to be initialized to zero
sys.stdout.write( '\t'+param['paramname']+' = false;\n' )
elif (paramtype == 'string' and param['paramtype'] == 'const char *'):
# In strings need to be converted to UTF-8
sys.stdout.write( '\tIntPtr '+param['paramname']+'Utf8 = Utils.ToUtf8('+param['paramname']+');\n')
#elif(paramtype[0:1] == 'I' and paramtype != 'IntPtr' and not enumlist.has_key(paramtype)):
# Returning an interface ptr needs ptr defined
# sys.stdout.write( '\tIntPtr ptr = IntPtr.Zero;\n' )
# PROCESS THE FIRST OPTIONAL API CALL
if(twocalls):
paramsnull = ','.join(map((lambda x: (arrayreplace if (x==arrayname) else x)),nativeparamlist))
sys.stdout.write ('\t'+('' if (returntype=='void') else (returntype+' result = '))+'FnTable.'+methodname+'('+paramsnull+');\n')
# PROCESS IN BETWEEN THE API CALLS
# Allocate the array or buffer
if('params' in method):
for param in method['params']:
if('out_array_count' in param):
sys.stdout.write( '\t'+param['paramname']+'= new ' + converttype(param['paramtype']) + '['+param['out_array_count']+'];\n' )
if('out_string_count' in param):
sys.stdout.write( '\tSystem.Text.StringBuilder pStrBuffer = new System.Text.StringBuilder((int)'+skipparam+');\n' )
# PROCESS THE FINAL API CALL
if methodname in controller_packed_methods:
packedparamlist = [w.replace('pControllerState','state_packed')
.replace('unControllerStateSize','(uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(VRControllerState_t_Packed))')
for w in nativeparamlist]
print('#if !UNITY_METRO')
print('\tif ((System.Environment.OSVersion.Platform == System.PlatformID.MacOSX) ||')
print('\t (System.Environment.OSVersion.Platform == System.PlatformID.Unix))')
print('\t{')
print('\t\t'+methodname+'Union u;')
print('\t\tVRControllerState_t_Packed state_packed = new VRControllerState_t_Packed(pControllerState);')
print('\t\tu.p'+methodname+'Packed = null;')
print('\t\tu.p'+methodname+' = FnTable.'+methodname+';')
print('\t\t'+returntype+' packed_result = u.p'+methodname+'Packed('+','.join(packedparamlist)+');\n')
print('\t\tstate_packed.Unpack(ref pControllerState);')
print('\t\treturn packed_result;')
print('\t}')
print('#endif')
if methodname in event_packed_methods:
packedparamlist = [w.replace('pEvent','event_packed')
.replace('uncbVREvent','(uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(VREvent_t_Packed))')
for w in nativeparamlist]
print('#if !UNITY_METRO')
print('\tif ((System.Environment.OSVersion.Platform == System.PlatformID.MacOSX) ||')
print('\t (System.Environment.OSVersion.Platform == System.PlatformID.Unix))')
print('\t{')
print('\t\t'+methodname+'Union u;')
print('\t\tVREvent_t_Packed event_packed = new VREvent_t_Packed();')
print('\t\tu.p'+methodname+'Packed = null;')
print('\t\tu.p'+methodname+' = FnTable.'+methodname+';')
print('\t\t'+returntype+' packed_result = u.p'+methodname+'Packed('+','.join(packedparamlist)+');\n')
print('\t\tevent_packed.Unpack(ref pEvent);')
print('\t\treturn packed_result;')
print('\t}')
print('#endif')
savereturn = returntype
if(method['returntype'][len(method['returntype']) - 1] == '*'):
returntype = 'IntPtr'
if(returntype == 'void'):
if(len(nativeparamlist) > 0):
sys.stdout.write ('\tFnTable.'+methodname+'('+','.join(nativeparamlist)+');\n')
else:
sys.stdout.write ('\tFnTable.'+methodname+'();\n')
else:
if(len(nativeparamlist) > 0):
#if(returntype[0:1] == 'I' and returntype != 'IntPtr'):
# sys.stdout.write ('\tC'+returntype[1:]+' result = new C'+returntype[1:]+'(FnTable.'+methodname+'('+','.join(nativeparamlist)+'));\n')
if(returntype == 'string'):
sys.stdout.write ('\tstring result = Marshal.PtrToStringAuto(FnTable.'+methodname+'('+','.join(nativeparamlist)+'));\n')
else:
sys.stdout.write ('\t'+('' if twocalls else returntype)+' result = FnTable.'+methodname+'('+','.join(nativeparamlist)+');\n')
else:
if(returntype == 'string'):
sys.stdout.write ('\tstring result = Marshal.PtrToStringAuto(FnTable.'+methodname+'());\n')
else:
sys.stdout.write ('\t'+('' if twocalls else returntype)+' result = FnTable.'+methodname+'();\n')
# PROCESS AFTER THE API CALL
if('params' in method):
for param in method['params']:
paramtype = converttype(param['paramtype'])
if('out_string_count' in param):
sys.stdout.write( '\t'+param['paramname']+' = pStrBuffer.ToString();\n' )
elif (paramtype == 'string' and param['paramtype'] == 'const char *'):
sys.stdout.write( '\tMarshal.FreeHGlobal('+param['paramname']+'Utf8);\n')
#elif(paramtype[0:1] == 'I' and paramtype != 'IntPtr' and not enumlist.has_key(paramtype)):
# Interface. Need to construct a class wrapper
#sys.stdout.write( '\t'+param['paramname']+'= new C' + paramtype[1:] + '(ptr);\n' )
if(method['returntype'][len(method['returntype']) - 1] == '*'):
if(savereturn == 'string'):
sys.stdout.write ('\treturn Marshal.PtrToStringAnsi(result);\n')
else:
sys.stdout.write ('\treturn ('+savereturn+') Marshal.PtrToStructure(result, typeof('+savereturn+'));\n')
elif(returntype != 'void'):
sys.stdout.write ('\treturn result;\n')
sys.stdout.write ('}\n')