Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ValveSoftware/Proton.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'lsteamclient/gen_wrapper.py')
-rwxr-xr-xlsteamclient/gen_wrapper.py543
1 files changed, 543 insertions, 0 deletions
diff --git a/lsteamclient/gen_wrapper.py b/lsteamclient/gen_wrapper.py
new file mode 100755
index 00000000..833489d2
--- /dev/null
+++ b/lsteamclient/gen_wrapper.py
@@ -0,0 +1,543 @@
+#!/usr/bin/python2
+
+#NOTE: If you make modifications here, consider whether they should
+#be duplicated in ../vrclient/gen_wrapper.py
+
+import pprint
+import sys
+import clang.cindex
+import os
+import re
+
+sdk_versions = [
+ "142",
+ "141",
+ "140",
+ "139",
+ "138a",
+ "138",
+ "137",
+ "136",
+ "135a",
+ "135",
+ "134",
+ "133b",
+ "133a",
+ "133",
+ "132",
+ "131",
+ "130",
+ "129a",
+ "129",
+ "128",
+ "127",
+ "126a",
+ "126",
+ "125",
+ "124",
+ "123a",
+ "123",
+ "122",
+ "121",
+ "120",
+ "119",
+ "118",
+ "117",
+ "116",
+ "115",
+ "114",
+ "113",
+ "112",
+ "111",
+ "110",
+ "109",
+ "108",
+ "107",
+ "106",
+ "105",
+ "104",
+ "103",
+ "102",
+ "101",
+ "100",
+]
+
+files = [
+ ("steam_api.h", [
+ "ISteamApps",
+ "ISteamAppList",
+ "ISteamClient",
+ "ISteamController",
+ "ISteamFriends",
+ "ISteamHTMLSurface",
+ "ISteamHTTP",
+ "ISteamInventory",
+ "ISteamMatchmaking",
+ "ISteamMatchmakingServers",
+ "ISteamMusic",
+ "ISteamMusicRemote",
+ "ISteamNetworking",
+ "ISteamRemoteStorage",
+ "ISteamScreenshots",
+ "ISteamUGC",
+ "ISteamUnifiedMessages",
+ "ISteamUser",
+ "ISteamUserStats",
+ "ISteamUtils",
+ "ISteamVideo"
+ ]),
+ ("isteamappticket.h", [
+ "ISteamAppTicket"
+ ]),
+ ("isteamgameserver.h", [
+ "ISteamGameServer"
+ ]),
+ ("isteamgameserverstats.h", [
+ "ISteamGameServerStats"
+ ]),
+ ("isteammasterserverupdater.h", [
+ "ISteamMasterServerUpdater"
+ ]),
+ ("isteamgamecoordinator.h", [
+ "ISteamGameCoordinator"
+ ]),
+ ("isteamparentalsettings.h", [
+ "ISteamParentalSettings"
+ ]),
+]
+
+aliases = {
+ #Some interface versions are not present in the public SDK
+ #headers, but are actually requested by games. It would be nice
+ #to verify that these interface versions are actually binary
+ #compatible. Lacking that, we hope the next highest version
+ #is compatible.
+ "SteamClient012":["SteamClient013"],
+ "SteamUtils004":["SteamUtils003"], # TimeShift uses SteamUtils003
+
+
+ #leaving these commented-out. let's see if they turn up in practice,
+ #and handle them correctly if so.
+
+# "SteamFriends011":["SteamFriends010"],
+# "SteamFriends013":["SteamFriends012"],
+# "SteamGameServer008":["SteamGameServer007", "SteamGameServer006"],
+# "SteamMatchMaking004":["SteamMatchMaking003"],
+# "SteamMatchMaking006":["SteamMatchMaking005"],
+# "STEAMREMOTESTORAGE_INTERFACE_VERSION004":["STEAMREMOTESTORAGE_INTERFACE_VERSION003"],
+# "STEAMREMOTESTORAGE_INTERFACE_VERSION008":["STEAMREMOTESTORAGE_INTERFACE_VERSION007"],
+# "STEAMREMOTESTORAGE_INTERFACE_VERSION010":["STEAMREMOTESTORAGE_INTERFACE_VERSION009"],
+# "STEAMUGC_INTERFACE_VERSION005":["STEAMUGC_INTERFACE_VERSION004"],
+# "STEAMUGC_INTERFACE_VERSION007":["STEAMUGC_INTERFACE_VERSION006"],
+# "SteamUser016":["SteamUser015"],
+# "STEAMUSERSTATS_INTERFACE_VERSION009":["STEAMUSERSTATS_INTERFACE_VERSION008"],
+}
+
+# TODO: we could do this automatically by creating temp files and
+# having clang parse those and detect when the MS-style padding results
+# in identical struct widths. But there's only a couple, so let's cheat...
+skip_structs = [
+ "RemoteStorageGetPublishedFileDetailsResult_t_9740",
+ "SteamUGCQueryCompleted_t_20",
+ "SteamUGCRequestUGCDetailsResult_t_9764"
+]
+
+print_sizes = []
+
+class_versions = {}
+
+def handle_method(cfile, classname, winclassname, cppname, method, cpp, cpp_h, existing_methods):
+ used_name = method.spelling
+ idx = '2'
+ while used_name in existing_methods:
+ used_name = "%s_%s" % (method.spelling, idx)
+ idx = chr(ord(idx) + 1)
+ returns_record = method.result_type.get_canonical().kind == clang.cindex.TypeKind.RECORD
+ if returns_record:
+ parambytes = 8 #_this + return pointer
+ else:
+ parambytes = 4 #_this
+ for param in list(method.get_children()):
+ if param.kind == clang.cindex.CursorKind.PARM_DECL:
+ parambytes += param.type.get_size()
+ cfile.write("DEFINE_THISCALL_WRAPPER(%s_%s, %s)\n" % (winclassname, used_name, parambytes))
+ cpp_h.write("extern ")
+ if method.result_type.spelling.startswith("ISteam"):
+ cfile.write("win%s " % (method.result_type.spelling))
+ cpp.write("void *")
+ cpp_h.write("void *")
+ elif returns_record:
+ cfile.write("%s *" % method.result_type.spelling)
+ cpp.write("%s " % (method.result_type.spelling))
+ cpp_h.write("%s " % (method.result_type.spelling))
+ else:
+ cfile.write("%s " % (method.result_type.spelling))
+ cpp.write("%s " % (method.result_type.spelling))
+ cpp_h.write("%s " % (method.result_type.spelling))
+ cfile.write('__thiscall %s_%s(%s *_this' % (winclassname, used_name, winclassname))
+ cpp.write("%s_%s(void *linux_side" % (cppname, used_name))
+ cpp_h.write("%s_%s(void *" % (cppname, used_name))
+ if returns_record:
+ cfile.write(", %s *_r" % method.result_type.spelling)
+ unnamed = 'a'
+ for param in list(method.get_children()):
+ if param.kind == clang.cindex.CursorKind.PARM_DECL:
+ if param.type.kind == clang.cindex.TypeKind.POINTER and \
+ param.type.get_pointee().kind == clang.cindex.TypeKind.UNEXPOSED:
+ #unspecified function pointer
+ typename = "void *"
+ else:
+ typename = param.type.spelling.split("::")[-1];
+ if param.spelling == "":
+ cfile.write(", %s _%s" % (typename, unnamed))
+ cpp.write(", %s _%s" % (typename, unnamed))
+ cpp_h.write(", %s" % typename)
+ unnamed = chr(ord(unnamed) + 1)
+ else:
+ cfile.write(", %s %s" % (typename, param.spelling))
+ cpp.write(", %s %s" % (typename, param.spelling))
+ cpp_h.write(", %s" % (typename))
+ cfile.write(")\n{\n")
+ cpp.write(")\n{\n")
+ cpp_h.write(");\n")
+
+ char_param_is_unix_path = "GetAppInstallDir" in used_name or \
+ "GetItemInstallInfo" in used_name
+ path_param_name = None
+ path_size_param_name = None
+
+ if char_param_is_unix_path:
+ cfile.write(" uint32 path_result;\n")
+
+ cfile.write(" TRACE(\"%p\\n\", _this);\n")
+
+ if method.result_type.kind == clang.cindex.TypeKind.VOID:
+ cfile.write(" ")
+ cpp.write(" ")
+ elif char_param_is_unix_path:
+ cfile.write(" path_result = ")
+ cpp.write(" return ")
+ elif returns_record:
+ cfile.write(" *_r = ")
+ cpp.write(" return ")
+ else:
+ cfile.write(" return ")
+ cpp.write(" return ")
+
+ should_gen_wrapper = method.result_type.spelling.startswith("ISteam") or \
+ used_name.startswith("GetISteamGenericInterface")
+ if should_gen_wrapper:
+ cfile.write("create_win_interface(pchVersion,\n ")
+
+ cfile.write("%s_%s(_this->linux_side" % (cppname, used_name))
+ cpp.write("((%s*)linux_side)->%s(" % (classname, method.spelling))
+ unnamed = 'a'
+ first = True
+ for param in list(method.get_children()):
+ if param.kind == clang.cindex.CursorKind.PARM_DECL:
+ if not first:
+ cpp.write(", ")
+ else:
+ first = False
+ if char_param_is_unix_path:
+ if param.type.spelling == "char *":
+ path_param_name = param.spelling
+ elif not path_param_name is None and \
+ (param.type.spelling == "uint32" or
+ param.type.spelling == "int"):
+ path_size_param_name = param.spelling
+ if param.spelling == "":
+ cfile.write(", _%s" % unnamed)
+ cpp.write("(%s)_%s" % (param.type.spelling, unnamed))
+ unnamed = chr(ord(unnamed) + 1)
+ elif "ISteamMatchmakingServerListResponse" in param.type.spelling:
+ cfile.write(", create_LinuxMatchmakingServerListResponse(%s)" % param.spelling)
+ cpp.write("(%s)%s" % (param.type.spelling, param.spelling))
+ else:
+ cfile.write(", %s" % param.spelling)
+ cpp.write("(%s)%s" % (param.type.spelling, param.spelling))
+ if should_gen_wrapper:
+ cfile.write(")")
+ cfile.write(");\n")
+ cpp.write(");\n")
+ if returns_record:
+ cfile.write(" return _r;\n")
+ if char_param_is_unix_path and not path_param_name is None and not path_size_param_name is None:
+ cfile.write(" return steamclient_unix_path_to_dos_path(path_result, %s, %s);\n" % (path_param_name, path_size_param_name))
+ cfile.write("}\n\n")
+ cpp.write("}\n\n")
+ return used_name
+
+def get_iface_version(classname):
+ # ISteamClient -> STEAMCLIENT_INTERFACE_VERSION
+ defname = "%s_INTERFACE_VERSION" % classname[1:].upper()
+ if defname in iface_versions.keys():
+ ver = iface_versions[defname]
+ else:
+ ver = "UNVERSIONED"
+ if classname in class_versions.keys() and ver in class_versions[classname]:
+ return (ver, True)
+ if not classname in class_versions.keys():
+ class_versions[classname] = []
+ class_versions[classname].append(ver)
+ return (ver, False)
+
+def handle_class(sdkver, classnode):
+ children = list(classnode.get_children())
+ if len(children) == 0:
+ return
+ (iface_version, already_generated) = get_iface_version(classnode.spelling)
+ if already_generated:
+ return
+ winname = "win%s" % classnode.spelling
+ if not winname in generated_c_files:
+ generated_c_files.append(winname)
+ cppname = "cpp%s_%s" % (classnode.spelling, iface_version)
+ generated_cpp_files.append(cppname)
+
+ file_exists = os.path.isfile("%s.c" % winname)
+ cfile = open("%s.c" % winname, "a")
+ if not file_exists:
+ cfile.write("""/* This file is auto-generated, do not edit. */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wine/debug.h"
+
+#include "cxx.h"
+
+#include "steam_defs.h"
+
+#include "steamclient_private.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(steamclient);
+
+""")
+
+ cpp = open("%s.cpp" % cppname, "w")
+ cpp.write("#include \"steamclient_private.h\"\n")
+ cpp.write("#include \"steam_defs.h\"\n")
+ cpp.write("#include \"steamworks_sdk_%s/steam_api.h\"\n" % sdkver)
+ if not fname == "steam_api.h":
+ cpp.write("#include \"steamworks_sdk_%s/%s\"\n" % (sdkver, fname))
+ cpp.write("#include \"%s.h\"\n" % cppname)
+ cpp.write("#ifdef __cplusplus\nextern \"C\" {\n#endif\n")
+
+ cpp_h = open("%s.h" % cppname, "w")
+ cpp_h.write("#ifdef __cplusplus\nextern \"C\" {\n#endif\n")
+
+ winclassname = "win%s_%s" % (classnode.spelling, iface_version)
+ cfile.write("#include \"%s.h\"\n\n" % cppname)
+ cfile.write("typedef struct __%s {\n" % winclassname)
+ cfile.write(" vtable_ptr *vtable;\n")
+ cfile.write(" void *linux_side;\n")
+ cfile.write("} %s;\n\n" % winclassname)
+ methods = []
+ for child in children:
+ if child.kind == clang.cindex.CursorKind.CXX_METHOD:
+ methods.append(handle_method(cfile, classnode.spelling, winclassname, cppname, child, cpp, cpp_h, methods))
+
+ cfile.write("extern vtable_ptr %s_vtable;\n\n" % winclassname)
+ cfile.write("#ifndef __GNUC__\n")
+ cfile.write("void __asm_dummy_vtables(void) {\n")
+ cfile.write("#endif\n")
+ cfile.write(" __ASM_VTABLE(%s,\n" % winclassname)
+ for method in methods:
+ cfile.write(" VTABLE_ADD_FUNC(%s_%s)\n" % (winclassname, method))
+ cfile.write(" );\n")
+ cfile.write("#ifndef __GNUC__\n")
+ cfile.write("}\n")
+ cfile.write("#endif\n\n")
+ cfile.write("%s *create_%s(void *linux_side)\n{\n" % (winclassname, winclassname))
+ cfile.write(" %s *r = HeapAlloc(GetProcessHeap(), 0, sizeof(%s));\n" % (winclassname, winclassname))
+ cfile.write(" TRACE(\"-> %p\\n\", r);\n")
+ cfile.write(" r->vtable = &%s_vtable;\n" % winclassname)
+ cfile.write(" r->linux_side = linux_side;\n")
+ cfile.write(" return r;\n}\n\n")
+
+ cpp.write("#ifdef __cplusplus\n}\n#endif\n")
+ cpp_h.write("#ifdef __cplusplus\n}\n#endif\n")
+
+ constructors = open("win_constructors.h", "a")
+ constructors.write("extern void *create_%s(void *);\n" % winclassname)
+
+ constructors = open("win_constructors_table.dat", "a")
+ constructors.write(" {\"%s\", &create_%s},\n" % (iface_version, winclassname))
+ if iface_version in aliases.keys():
+ for alias in aliases[iface_version]:
+ constructors.write(" {\"%s\", &create_%s}, /* alias */\n" % (alias, winclassname))
+
+
+generated_cb_handlers = []
+generated_cb_ids = []
+cpp_files_need_close_brace = []
+cb_table = {}
+
+#because of struct packing differences between win32 and linux, we
+#need to convert these structs from their linux layout to the win32
+#layout.
+#TODO: could we optimize this by detecting if the structs are the
+#same layout at generation-time?
+def handle_callback_struct(sdkver, callback, cb_num):
+ handler_name = "%s_%s" % (callback.displayname, callback.type.get_size())
+
+ if handler_name in generated_cb_handlers:
+ # we already have a handler for the callback struct of this size
+ return
+
+ if handler_name in skip_structs:
+ # due to padding, some structs have the same width across versions of
+ # the SDK. since we key our lin->win conversion on the win struct size,
+ # we can skip the smaller structs and just write into the padding on
+ # older versions
+ # TODO: we could automate this. see comment near skip_structs declaration
+ return
+
+ cb_id = cb_num | (callback.type.get_size() << 16)
+ if cb_id in generated_cb_ids:
+ # either this cb changed name, or steam used the same ID for different structs
+ return
+
+ generated_cb_ids.append(cb_id)
+
+ filename_base = "cb_converters_%s" % sdkver
+ cppname = "%s.cpp" % filename_base
+ file_exists = os.path.isfile(cppname)
+ cppfile = open(cppname, "a")
+ if not file_exists:
+ generated_cpp_files.append(filename_base)
+ cppfile.write("#include \"steamclient_private.h\"\n")
+ cppfile.write("#include \"steam_defs.h\"\n")
+ cppfile.write("#include \"steamworks_sdk_%s/steam_api.h\"\n" % sdkver)
+ cppfile.write("#include \"steamworks_sdk_%s/isteamgameserver.h\"\n" % (sdkver))
+ if os.path.isfile("steamworks_sdk_%s/isteamgameserverstats.h" % sdkver):
+ cppfile.write("#include \"steamworks_sdk_%s/isteamgameserverstats.h\"\n" % (sdkver))
+ if os.path.isfile("steamworks_sdk_%s/isteamgamecoordinator.h" % sdkver):
+ cppfile.write("#include \"steamworks_sdk_%s/isteamgamecoordinator.h\"\n" % sdkver)
+ cppfile.write("extern \"C\" {\n")
+ cpp_files_need_close_brace.append(cppname)
+
+ datfile = open("cb_converters.dat", "a")
+ datfile.write("case 0x%08x: win_msg->m_cubParam = sizeof(struct win%s); win_msg->m_pubParam = HeapAlloc(GetProcessHeap(), 0, win_msg->m_cubParam); cb_%s(lin_msg.m_pubParam, win_msg->m_pubParam); break;\n" % (cb_id, handler_name, handler_name))
+
+ hfile = open("cb_converters.h", "a")
+ hfile.write("struct win%s {\n" % handler_name)
+ for m in callback.get_children():
+ if m.kind == clang.cindex.CursorKind.FIELD_DECL:
+ if m.type.kind == clang.cindex.TypeKind.CONSTANTARRAY:
+ hfile.write(" %s %s[%u];\n" % (m.type.element_type.spelling, m.displayname, m.type.element_count))
+ else:
+ hfile.write(" %s %s;\n" % (m.type.spelling, m.displayname))
+ hfile.write("} __attribute__ ((ms_struct));\n")
+ hfile.write("extern void cb_%s(void *l, void *w);\n" % handler_name)
+
+ cppfile.write("struct win%s {\n" % handler_name)
+ for m in callback.get_children():
+ if m.kind == clang.cindex.CursorKind.FIELD_DECL:
+ if m.type.kind == clang.cindex.TypeKind.CONSTANTARRAY:
+ cppfile.write(" %s %s[%u];\n" % (m.type.element_type.spelling, m.displayname, m.type.element_count))
+ else:
+ cppfile.write(" %s %s;\n" % (m.type.spelling, m.displayname))
+ cppfile.write("} __attribute__ ((ms_struct));\n")
+
+ cppfile.write("void cb_%s(void *l, void *w)\n{\n" % handler_name)
+ cppfile.write(" %s *lin = (%s *)l;\n" % (callback.displayname, callback.displayname))
+ cppfile.write(" struct win%s *win = (struct win%s *)w;\n" % (handler_name, handler_name))
+ for m in callback.get_children():
+ if m.kind == clang.cindex.CursorKind.FIELD_DECL:
+ if m.type.kind == clang.cindex.TypeKind.CONSTANTARRAY:
+ #TODO: if this is a struct, or packed differently, we'll have to
+ # copy each element in a for-loop
+ cppfile.write(" memcpy(win->%s, lin->%s, sizeof(win->%s));\n" % (m.displayname, m.displayname, m.displayname))
+ else:
+ cppfile.write(" win->%s = lin->%s;\n" % (m.displayname, m.displayname))
+ cppfile.write("}\n\n")
+
+ generated_cb_handlers.append(handler_name)
+
+ if not cb_num in cb_table.keys():
+ # latest SDK linux size, list of windows struct names
+ cb_table[cb_num] = (callback.type.get_size(), [])
+ cb_table[cb_num][1].append(handler_name)
+
+
+def handle_callback_maybe(sdkver, callback):
+ members = callback.get_children()
+ for c in members:
+ if c.kind == clang.cindex.CursorKind.ENUM_DECL:
+ enums = c.get_children()
+ for e in enums:
+ if e.displayname == "k_iCallback":
+ handle_callback_struct(sdkver, callback, e.enum_value)
+
+
+
+
+#clang.cindex.Config.set_library_file("/usr/lib/llvm-3.8/lib/libclang-3.8.so.1");
+
+generated_c_files = []
+generated_cpp_files = []
+
+prog = re.compile("^#define\s*(\w*)\s*\"(.*)\"")
+for sdkver in sdk_versions:
+ iface_versions = {}
+ for f in os.listdir("steamworks_sdk_%s" % sdkver):
+ x = open("steamworks_sdk_%s/%s" % (sdkver, f), "r")
+ for l in x:
+ if "INTERFACE_VERSION" in l:
+ result = prog.match(l)
+ if result:
+ iface, version = result.group(1, 2)
+ iface_versions[iface] = version
+
+ for fname, classes in files:
+ # Parse as 32-bit C++
+ input_name = "steamworks_sdk_%s/%s" % (sdkver, fname)
+ sys.stdout.write("about to parse %s\n" % input_name)
+ if not os.path.isfile(input_name):
+ continue
+ index = clang.cindex.Index.create()
+ tu = index.parse(input_name, args=['-x', 'c++', '-m32', '-Isteamworks_sdk_%s/' % sdkver, '-I/usr/lib/clang/5.0.1/include/'])
+
+ diagnostics = list(tu.diagnostics)
+ if len(diagnostics) > 0:
+ print 'There were parse errors'
+ pprint.pprint(diagnostics)
+ else:
+ children = list(tu.cursor.get_children())
+ for child in children:
+ if child.kind == clang.cindex.CursorKind.CLASS_DECL and child.displayname in classes:
+ handle_class(sdkver, child)
+ if child.kind == clang.cindex.CursorKind.STRUCT_DECL or \
+ child.kind == clang.cindex.CursorKind.CLASS_DECL:
+ handle_callback_maybe(sdkver, child)
+ if child.displayname in print_sizes:
+ sys.stdout.write("size of %s is %u\n" % (child.displayname, child.type.get_size()))
+
+for f in cpp_files_need_close_brace:
+ m = open(f, "a")
+ m.write("\n}\n")
+
+getapifile = open("cb_getapi_table.dat", "w")
+cbsizefile = open("cb_getapi_sizes.dat", "w")
+for cb in cb_table.keys():
+ cbsizefile.write("case %u: /* %s */\n" % (cb, cb_table[cb][1][0]))
+ cbsizefile.write(" return %u;\n" % cb_table[cb][0])
+ getapifile.write("case %u:\n" % cb)
+ getapifile.write(" switch(callback_len){\n")
+ getapifile.write(" default:\n") # the first one should be the latest, should best support future SDK versions
+ for struct in cb_table[cb][1]:
+ getapifile.write(" case sizeof(struct win%s): cb_%s(lin_callback, callback); break;\n" % (struct, struct))
+ getapifile.write(" }\n break;\n")
+
+m = open("Makefile.in", "a")
+for f in generated_c_files:
+ m.write("\t%s.c \\\n" % f)
+m.write("\nCPP_SRCS = \\\n")
+for f in generated_cpp_files:
+ m.write("\t%s.cpp \\\n" % f)