mirror of
https://github.com/ValveSoftware/Proton.git
synced 2025-05-10 13:49:37 +03:00
This avoids returning unix-side server info structures from the PE-side. For the v2 API requests are wrapped entirely and for v1 API as there is no explicit release function, results are cached forvever using the buffer cache.
1747 lines
58 KiB
Python
Executable File
1747 lines
58 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
#NOTE: If you make modifications here, consider whether they should
|
|
#be duplicated in ../vrclient/gen_wrapper.py
|
|
|
|
CLANG_PATH='/usr/lib/clang/15'
|
|
|
|
from clang.cindex import Cursor, CursorKind, Index, TypeKind
|
|
import concurrent.futures
|
|
import os
|
|
import re
|
|
|
|
SDK_VERSIONS = [
|
|
"162",
|
|
"161",
|
|
"160",
|
|
"159",
|
|
"158",
|
|
"157",
|
|
"156",
|
|
"155",
|
|
"154",
|
|
"153a",
|
|
"152",
|
|
"151",
|
|
"150",
|
|
"149",
|
|
"148a",
|
|
"147",
|
|
"146",
|
|
"145",
|
|
"144",
|
|
"143y",
|
|
"143x",
|
|
"143",
|
|
"142",
|
|
"141",
|
|
"140",
|
|
"139",
|
|
"138a",
|
|
"138",
|
|
"137",
|
|
"136",
|
|
"135a",
|
|
"135",
|
|
"134",
|
|
"133x",
|
|
"133b",
|
|
"133a",
|
|
"133",
|
|
"132x",
|
|
"132",
|
|
"131",
|
|
"130x",
|
|
"130",
|
|
"129a",
|
|
"129",
|
|
"128x",
|
|
"128",
|
|
"127",
|
|
"126a",
|
|
"126",
|
|
"125",
|
|
"124",
|
|
"123a",
|
|
"123",
|
|
"122",
|
|
"121x",
|
|
"121",
|
|
"120",
|
|
"119x",
|
|
"119",
|
|
"118",
|
|
"117",
|
|
"116x",
|
|
"116",
|
|
"115",
|
|
"114",
|
|
"113",
|
|
"112x",
|
|
"112",
|
|
"111x",
|
|
"111",
|
|
"110",
|
|
"109",
|
|
"108",
|
|
"107",
|
|
"106",
|
|
"105",
|
|
"104",
|
|
"103",
|
|
"102x",
|
|
"102",
|
|
"101x",
|
|
"101",
|
|
"100",
|
|
"099y",
|
|
"099x",
|
|
"099w",
|
|
"099v",
|
|
"099u",
|
|
]
|
|
|
|
ABIS = ['u32', 'u64', 'w32', 'w64']
|
|
|
|
SDK_SOURCES = {
|
|
"steam_api.h": [
|
|
"ISteamApps",
|
|
"ISteamAppList",
|
|
"ISteamClient",
|
|
"ISteamController",
|
|
"ISteamGameSearch",
|
|
"ISteamFriends",
|
|
"ISteamHTMLSurface",
|
|
"ISteamHTTP",
|
|
"ISteamInput",
|
|
"ISteamInventory",
|
|
"ISteamMatchmaking",
|
|
"ISteamMatchmakingServers",
|
|
"ISteamMusic",
|
|
"ISteamMusicRemote",
|
|
"ISteamNetworking",
|
|
"ISteamParties",
|
|
"ISteamRemotePlay",
|
|
"ISteamRemoteStorage",
|
|
"ISteamScreenshots",
|
|
"ISteamUGC",
|
|
"ISteamUnifiedMessages",
|
|
"ISteamUser",
|
|
"ISteamUserStats",
|
|
"ISteamUtils",
|
|
"ISteamVideo"
|
|
],
|
|
"isteamappticket.h": [
|
|
"ISteamAppTicket"
|
|
],
|
|
"isteamgameserver.h": [
|
|
"ISteamGameServer"
|
|
],
|
|
"isteamgameserverstats.h": [
|
|
"ISteamGameServerStats"
|
|
],
|
|
"isteamgamestats.h": [
|
|
"ISteamGameStats"
|
|
],
|
|
"isteammasterserverupdater.h": [
|
|
"ISteamMasterServerUpdater"
|
|
],
|
|
"isteamgamecoordinator.h": [
|
|
"ISteamGameCoordinator"
|
|
],
|
|
"isteamparentalsettings.h": [
|
|
"ISteamParentalSettings"
|
|
],
|
|
"isteamnetworkingmessages.h": [
|
|
"ISteamNetworkingMessages"
|
|
],
|
|
"isteamnetworkingsockets.h": [
|
|
"ISteamNetworkingSockets"
|
|
],
|
|
"isteamnetworkingsocketsserialized.h": [
|
|
"ISteamNetworkingSocketsSerialized"
|
|
],
|
|
"isteamnetworkingutils.h": [
|
|
"ISteamNetworkingUtils"
|
|
],
|
|
"steamnetworkingfakeip.h": [
|
|
"ISteamNetworkingFakeUDPPort"
|
|
],
|
|
"isteamtimeline.h": [
|
|
"ISteamTimeline"
|
|
],
|
|
}
|
|
|
|
SDK_CLASSES = {klass: source for source, klasses in SDK_SOURCES.items()
|
|
for klass in klasses}
|
|
|
|
VERSION_ALIASES = {
|
|
#these interfaces are undocumented and binary compatible
|
|
#"target interface": ["alias 1", "alias 2"],
|
|
"SteamUtils004":["SteamUtils003"],
|
|
"SteamUtils002":["SteamUtils001"],
|
|
"SteamGameServer008":["SteamGameServer007","SteamGameServer006"],
|
|
"SteamNetworkingSocketsSerialized002":["SteamNetworkingSocketsSerialized001"],
|
|
"STEAMAPPS_INTERFACE_VERSION001":["SteamApps001"],
|
|
"SteamNetworkingSockets002":["SteamNetworkingSockets003"],
|
|
}
|
|
|
|
# these structs are manually confirmed to be equivalent
|
|
EXEMPT_STRUCTS = {
|
|
"CSteamID",
|
|
"CGameID",
|
|
"CCallbackBase",
|
|
"SteamIPAddress_t",
|
|
"SteamPS3Params_t",
|
|
"ValvePackingSentinel_t"
|
|
}
|
|
|
|
# structs for which the size is important, either because of arrays or size parameters
|
|
SIZED_STRUCTS = {
|
|
"SteamPartyBeaconLocation_t",
|
|
"SteamItemDetails_t",
|
|
}
|
|
|
|
# we have converters for these written by hand because they're too complicated to generate
|
|
MANUAL_STRUCTS = [
|
|
"SteamNetworkingMessage_t"
|
|
]
|
|
|
|
UNIX_FUNCS = [
|
|
'steamclient_init',
|
|
'steamclient_init_registry',
|
|
'steamclient_next_callback',
|
|
'steamclient_get_unix_buffer',
|
|
'steamclient_CreateInterface',
|
|
'steamclient_Steam_GetAPICallResult',
|
|
'steamclient_Steam_BGetCallback',
|
|
'steamclient_callback_message_receive',
|
|
'steamclient_Steam_FreeLastCallback',
|
|
'steamclient_Steam_ReleaseThreadLocalMemory',
|
|
'steamclient_Steam_IsKnownInterface',
|
|
'steamclient_Steam_NotifyMissingInterface',
|
|
'steamclient_networking_messages_receive_144',
|
|
'steamclient_networking_messages_receive_147',
|
|
'steamclient_networking_messages_receive_153a',
|
|
'steamclient_networking_message_release_147',
|
|
'steamclient_networking_message_release_153a',
|
|
]
|
|
|
|
MANUAL_METHODS = {
|
|
#TODO: 001 005 007
|
|
#NOTE: 003 never appeared in a public SDK, but is an alias for 002 (the version in SDK 1.45 is actually 004 but incorrectly versioned as 003)
|
|
"ISteamNetworkingSockets_ReceiveMessagesOnConnection": True,
|
|
"ISteamNetworkingSockets_ReceiveMessagesOnListenSocket": True,
|
|
"ISteamNetworkingSockets_ReceiveMessagesOnPollGroup": True,
|
|
"ISteamNetworkingSockets_SendMessages": True,
|
|
"ISteamNetworkingSockets_ConnectP2PCustomSignaling": lambda ver, abi: abi == 'u' and ver <= 8,
|
|
"ISteamNetworkingSockets_ReceivedP2PCustomSignal": lambda ver, abi: abi == 'u' and ver <= 8,
|
|
|
|
"ISteamMatchmakingServers_CancelQuery": lambda ver, abi: abi == 'u' and ver >= 2,
|
|
"ISteamMatchmakingServers_GetServerCount": lambda ver, abi: abi == 'u' and ver >= 2,
|
|
"ISteamMatchmakingServers_GetServerDetails": lambda ver, abi: abi == 'u' and ver >= 2,
|
|
"ISteamMatchmakingServers_IsRefreshing": lambda ver, abi: abi == 'u' and ver >= 2,
|
|
"ISteamMatchmakingServers_PingServer": lambda ver, abi: abi == 'u',
|
|
"ISteamMatchmakingServers_PlayerDetails": lambda ver, abi: abi == 'u',
|
|
"ISteamMatchmakingServers_RefreshQuery": lambda ver, abi: abi == 'u' and ver >= 2,
|
|
"ISteamMatchmakingServers_RefreshServer": lambda ver, abi: abi == 'u' and ver >= 2,
|
|
"ISteamMatchmakingServers_ReleaseRequest": lambda ver, abi: abi == 'w' or abi == 'u',
|
|
"ISteamMatchmakingServers_RequestFavoritesServerList": lambda ver, abi: abi == 'u',
|
|
"ISteamMatchmakingServers_RequestFriendsServerList": lambda ver, abi: abi == 'u',
|
|
"ISteamMatchmakingServers_RequestHistoryServerList": lambda ver, abi: abi == 'u',
|
|
"ISteamMatchmakingServers_RequestInternetServerList": lambda ver, abi: abi == 'u',
|
|
"ISteamMatchmakingServers_RequestLANServerList": lambda ver, abi: abi == 'u',
|
|
"ISteamMatchmakingServers_RequestSpectatorServerList": lambda ver, abi: abi == 'u',
|
|
"ISteamMatchmakingServers_ServerRules": lambda ver, abi: abi == 'u',
|
|
|
|
"ISteamNetworkingUtils_AllocateMessage": True,
|
|
"ISteamNetworkingUtils_SetConfigValue": lambda ver, abi: abi == 'u' and ver >= 3,
|
|
|
|
"ISteamNetworkingMessages_ReceiveMessagesOnChannel": True,
|
|
|
|
"ISteamInput_EnableActionEventCallbacks": lambda ver, abi: abi == 'u',
|
|
"ISteamInput_GetGlyphForActionOrigin": True,
|
|
"ISteamInput_GetGlyphPNGForActionOrigin": True,
|
|
"ISteamInput_GetGlyphSVGForActionOrigin": True,
|
|
"ISteamInput_GetGlyphForActionOrigin_Legacy": True,
|
|
"ISteamInput_GetGlyphForXboxOrigin": True,
|
|
|
|
"ISteamController_GetGlyphForActionOrigin": True,
|
|
"ISteamController_GetGlyphForXboxOrigin": True,
|
|
|
|
"ISteamNetworkingFakeUDPPort_DestroyFakeUDPPort": lambda ver, abi: abi == 'w',
|
|
"ISteamNetworkingFakeUDPPort_ReceiveMessages": True,
|
|
|
|
"ISteamClient_Set_SteamAPI_CCheckCallbackRegisteredInProcess": lambda ver, abi: abi == 'u' and ver >= 20,
|
|
|
|
"ISteamUtils_GetAPICallResult": lambda ver, abi: abi == 'u',
|
|
}
|
|
|
|
|
|
DEFINE_INTERFACE_VERSION = re.compile(r'^#define\s*(?P<name>STEAM(?:\w*)_VERSION(?:\w*))\s*"(?P<version>.*)"')
|
|
|
|
|
|
def is_manual_method(klass, method, abi):
|
|
version = re.search(r'(\d+)$', klass.version)
|
|
|
|
key = f'{klass.name}_{method.name}'
|
|
needs_manual = MANUAL_METHODS.get(key, False)
|
|
|
|
if callable(needs_manual) and version:
|
|
return needs_manual(int(version[0]), abi)
|
|
return needs_manual
|
|
|
|
|
|
# manual converters for simple types (function pointers)
|
|
MANUAL_TYPES = [
|
|
"FSteamNetworkingSocketsDebugOutput",
|
|
"SteamAPIWarningMessageHook_t",
|
|
"SteamAPI_CheckCallbackRegistered_t",
|
|
"SteamAPI_PostAPIResultInProcess_t",
|
|
"void ()",
|
|
]
|
|
|
|
# manual converters for specific parameters
|
|
MANUAL_PARAMS = [
|
|
"nNativeKeyCode"
|
|
]
|
|
|
|
converted_structs = []
|
|
|
|
all_classes = {}
|
|
all_records = {}
|
|
all_sources = {}
|
|
all_structs = {}
|
|
all_versions = {}
|
|
unique_structs = []
|
|
|
|
|
|
PATH_CONV_METHODS_UTOW = {
|
|
"ISteamAppList_GetAppInstallDir": {
|
|
"pchDirectory": {"len": "cchNameMax", "url": False},
|
|
"ret_size": True,
|
|
},
|
|
"ISteamApps_GetAppInstallDir": {
|
|
"pchFolder": {"len": "cchFolderBufferSize", "url": False},
|
|
"ret_size": True,
|
|
},
|
|
"ISteamUGC_GetQueryUGCAdditionalPreview": {
|
|
"pchURLOrVideoID": {"len": "cchURLSize", "url": True},
|
|
},
|
|
"ISteamUGC_GetItemInstallInfo": {
|
|
"pchFolder": {"len": "cchFolderSize", "url": False},
|
|
},
|
|
"ISteamUser_GetUserDataFolder": {
|
|
"pchBuffer": {"len": "cubBuffer", "url": False},
|
|
},
|
|
}
|
|
|
|
PATH_CONV_METHODS_WTOU = {
|
|
"ISteamApps_GetFileDetails": {
|
|
"pszFileName": {"array": False, "url": False},
|
|
},
|
|
### ISteamGameServer::SetModDir - "Just the folder name, not the whole path. I.e. "Spacewar"."
|
|
"ISteamHTMLSurface_LoadURL": {
|
|
"pchURL": {"array": False, "url": True},
|
|
},
|
|
"ISteamHTMLSurface_FileLoadDialogResponse": {
|
|
"pchSelectedFiles": {"array": True, "url": False},
|
|
},
|
|
"ISteamRemoteStorage_PublishWorkshopFile": {
|
|
"pchFile": {"array": False, "url": False},
|
|
"pchPreviewFile": {"array": False, "url": False},
|
|
},
|
|
"ISteamRemoteStorage_UpdatePublishedFileFile": {
|
|
"pchFile": {"array": False, "url": False},
|
|
},
|
|
"ISteamRemoteStorage_UpdatePublishedFilePreviewFile": {
|
|
"pchPreviewFile": {"array": False, "url": False},
|
|
},
|
|
"ISteamRemoteStorage_PublishVideo": {
|
|
"pchPreviewFile": {"array": False, "url": False},
|
|
},
|
|
"ISteamScreenshots_AddScreenshotToLibrary": {
|
|
"pchFilename": {"array": False, "url": False},
|
|
"pchThumbnailFilename": {"array": False, "url": False},
|
|
},
|
|
"ISteamScreenshots_AddVRScreenshotToLibrary": {
|
|
"pchFilename": {"array": False, "url": False},
|
|
"pchVRFilename": {"array": False, "url": False},
|
|
},
|
|
"ISteamRemoteStorage_UGCDownloadToLocation": {
|
|
"pchLocation": {"array": False, "url": False},
|
|
},
|
|
"ISteamUGC_SetItemContent": {
|
|
"pszContentFolder": {"array": False, "url": False},
|
|
},
|
|
"ISteamUGC_SetItemPreview": {
|
|
"pszPreviewFile": {"array": False, "url": False},
|
|
},
|
|
"ISteamUGC_AddItemPreviewFile": {
|
|
"pszPreviewFile": {"array": False, "url": False},
|
|
},
|
|
"ISteamUGC_UpdateItemPreviewFile": {
|
|
"pszPreviewFile": {"array": False, "url": False},
|
|
},
|
|
"ISteamUGC_BInitWorkshopForGameServer": {
|
|
"pszFolder": {"array": False, "url": False},
|
|
},
|
|
"ISteamUtils_CheckFileSignature": {
|
|
"szFileName": {"array": False, "url": False},
|
|
},
|
|
"ISteamController_Init": {
|
|
"pchAbsolutePathToControllerConfigVDF": {"array": False, "url": False},
|
|
},
|
|
"ISteamInput_SetInputActionManifestFilePath": {
|
|
"pchInputActionManifestAbsolutePath": {"array": False, "url": False},
|
|
},
|
|
"ISteamFriends_ActivateGameOverlayToWebPage": {
|
|
"pchURL": {"array": False, "url": True},
|
|
},
|
|
}
|
|
|
|
PATH_CONV_STRUCTS = {
|
|
"HTML_StartRequest_t": {
|
|
"pchURL": True,
|
|
},
|
|
"HTML_URLChanged_t": {
|
|
"pchURL": True,
|
|
},
|
|
"HTML_FinishedRequest_t": {
|
|
"pchURL": True,
|
|
},
|
|
"HTML_OpenLinkInNewTab_t": {
|
|
"pchURL": True,
|
|
},
|
|
"HTML_LinkAtPosition_t": {
|
|
"pchURL": True,
|
|
},
|
|
"HTML_FileOpenDialog_t": {
|
|
"pchInitialFile": True,
|
|
},
|
|
"HTML_NewWindow_t": {
|
|
"pchURL": True,
|
|
},
|
|
}
|
|
|
|
PRETOUCH_TYPES = {
|
|
"const char *": " IsBadStringPtrA({0}, -1);\n",
|
|
}
|
|
|
|
OUTSTR_PARAMS = {
|
|
"GetUGCDetails": "ppchName",
|
|
"GetConfigValueInfo": "pOutName",
|
|
}
|
|
|
|
class Padding:
|
|
def __init__(self, offset, size):
|
|
self.offset = offset
|
|
self.size = size
|
|
|
|
|
|
class Field:
|
|
def __init__(self, cursor, struct, type, offset, name=None):
|
|
self._cursor = cursor
|
|
self._type = type
|
|
|
|
self.name = cursor.spelling if not name else name
|
|
self.type = cursor.type
|
|
self.size = self.type.get_size()
|
|
self.offset = offset
|
|
|
|
def needs_conversion(self, other):
|
|
return self._type.needs_conversion(other._type)
|
|
|
|
|
|
class BasicType:
|
|
def __init__(self, type, abi):
|
|
self._type = type.get_canonical()
|
|
self._abi = abi
|
|
self._decl_order = 0
|
|
self._conv_cache = {}
|
|
|
|
self.size = self._type.get_size()
|
|
self.id = self._type.spelling
|
|
|
|
@property
|
|
def order(self):
|
|
return self._decl_order
|
|
|
|
def set_used(self, order=-1):
|
|
if self._decl_order <= order:
|
|
return
|
|
self._decl_order = order
|
|
|
|
def needs_conversion(self, other):
|
|
if self._type.kind == TypeKind.POINTER and self._type.get_pointee().kind == TypeKind.FUNCTIONPROTO:
|
|
return self._abi != other._abi
|
|
return False
|
|
|
|
|
|
written_converters = set()
|
|
|
|
class Struct:
|
|
def __init__(self, sdkver, abi, cursor):
|
|
self._cursor = cursor
|
|
self._sdkver = sdkver
|
|
self._abi = abi
|
|
self._fields = None
|
|
self._decl_order = 0
|
|
self._conv_cache = {}
|
|
|
|
self.name = canonical_typename(self._cursor)
|
|
self.type = self._cursor.type.get_canonical()
|
|
self.size = self.type.get_size()
|
|
self.align = self.type.get_align()
|
|
self.id = f'{abi}_{self.name}_{sdkver}'
|
|
|
|
if self.name in EXEMPT_STRUCTS:
|
|
self._fields = [Padding(0, self.size)]
|
|
|
|
@property
|
|
def order(self):
|
|
return self._decl_order
|
|
|
|
def set_used(self, order=-1):
|
|
if self._decl_order <= order:
|
|
return
|
|
self._decl_order = order
|
|
[f._type.set_used(order - 1) for f in self.fields]
|
|
|
|
@property
|
|
def callback_id(self):
|
|
is_enum = lambda c: c.kind == CursorKind.ENUM_DECL
|
|
enums = filter(is_enum, self._cursor.get_children())
|
|
is_callback = lambda c: c.displayname == "k_iCallback"
|
|
callbacks = [k for c in enums for k in filter(is_callback, c.get_children())]
|
|
if len(callbacks): return int(callbacks[0].enum_value)
|
|
return None
|
|
|
|
@property
|
|
def padded_fields(self):
|
|
if self._fields: return self._fields
|
|
|
|
size, self._fields = 0, []
|
|
for cursor in self._cursor.get_children():
|
|
if cursor.kind == CursorKind.CXX_BASE_SPECIFIER \
|
|
and len(list(cursor.type.get_fields())) > 0:
|
|
base_type = Type(cursor.type, self._sdkver, self._abi)
|
|
self._fields += base_type.padded_fields
|
|
size = cursor.type.get_size()
|
|
break
|
|
|
|
for cursor in self.type.get_fields():
|
|
assert not cursor.is_bitfield()
|
|
offset = self.type.get_offset(cursor.spelling)
|
|
assert offset % 8 == 0
|
|
offset = offset // 8
|
|
# assert offset >= size or type(self) is Union
|
|
|
|
if size < offset: self._fields.append(Padding(size, offset - size))
|
|
field_type = Type(cursor.type, self._sdkver, self._abi)
|
|
self._fields.append(Field(cursor, self, field_type, offset))
|
|
size = max(size, offset + cursor.type.get_size())
|
|
|
|
if size < self.size: self._fields.append(Padding(size, self.size - size))
|
|
return self._fields
|
|
|
|
@property
|
|
def fields(self):
|
|
return [f for f in self.padded_fields if type(f) is not Padding]
|
|
|
|
def write_definition(self, out, prefix, converters):
|
|
version = all_versions[sdkver][self.name]
|
|
kind = 'union' if type(self) is Union else 'struct'
|
|
wrapped = len(prefix) > 0
|
|
|
|
out(f'#pragma pack( push, {self.align} )\n')
|
|
out(f'{kind} {prefix}{version}\n')
|
|
out(u'{\n')
|
|
for f in self.padded_fields:
|
|
if type(f) is Field:
|
|
out(f' {declspec(f._cursor, f.name, prefix, wrapped)};\n')
|
|
else:
|
|
out(f' uint8_t __pad_{f.offset}[{f.size}];\n')
|
|
for conv in converters:
|
|
out(u'#ifdef __cplusplus\n')
|
|
out(f' operator {conv}{version}() const;\n')
|
|
out(u'#endif /* __cplusplus */\n')
|
|
out(u'};\n')
|
|
out(u'#pragma pack( pop )\n\n')
|
|
|
|
def write_checks(self, out, prefix):
|
|
version = all_versions[sdkver][self.name]
|
|
|
|
out(f'C_ASSERT( sizeof({prefix}{version}) >= {self.size} );\n')
|
|
for f in self.fields:
|
|
out(f'C_ASSERT( offsetof({prefix}{version}, {f.name}) == {f.offset} );\n')
|
|
out(f'C_ASSERT( sizeof({prefix}{version}().{f.name}) >= {f.size} );\n')
|
|
out(u'\n')
|
|
|
|
def write_converter(self, prefix, path_conv_fields):
|
|
version = all_versions[sdkver][self.name]
|
|
from_abi = self._abi[0]
|
|
func_name = f'{version}_{prefix[0]}_from_{from_abi}'
|
|
if not func_name in written_converters:
|
|
written_converters.add(func_name)
|
|
out(f'static void {func_name}(void *dst, const void *src)\n')
|
|
out(u'{\n')
|
|
out(f' {prefix[0]}_{version} *d = ({prefix[0]}_{version} *)dst;\n')
|
|
out(f' const {from_abi}_{version} *s = (const {from_abi}_{version} *)src;\n\n')
|
|
for field in self.fields:
|
|
if field.name not in path_conv_fields:
|
|
out(f' d->{field.name} = s->{field.name};\n')
|
|
else:
|
|
out(f' steamclient_unix_path_to_dos_path(1, s->{field.name}, g_tmppath, TEMP_PATH_BUFFER_LENGTH, 1);\n')
|
|
out(f' d->{field.name} = g_tmppath;\n')
|
|
out(u'}\n')
|
|
|
|
if self._abi[1:3] == '64':
|
|
out(u'#ifdef __x86_64__\n')
|
|
elif self._abi[1:3] == '32':
|
|
out(u'#ifdef __i386__\n')
|
|
else:
|
|
assert False
|
|
|
|
out(f'{self._abi}_{version}::operator {prefix}{version}() const\n')
|
|
out(u'{\n')
|
|
out(f' {prefix}{version} ret;\n')
|
|
out(f' {func_name}((void *)&ret, (const void *)this);\n')
|
|
out(u' return ret;\n')
|
|
out(u'}\n')
|
|
out(u'#endif\n\n')
|
|
|
|
def needs_conversion(self, other):
|
|
if other.id in self._conv_cache:
|
|
return self._conv_cache[other.id]
|
|
self._conv_cache[other.id] = other._conv_cache[self.id] = True
|
|
|
|
if self.name in PATH_CONV_STRUCTS and self._abi[0] != other._abi[0]:
|
|
return True
|
|
if self.name in SIZED_STRUCTS and self.size != other.size:
|
|
return True
|
|
if len(self.fields) != len(other.fields):
|
|
return True
|
|
if any([a.offset != b.offset or a.needs_conversion(b)
|
|
for a, b in zip(self.fields, other.fields)]):
|
|
return True
|
|
|
|
self._conv_cache[other.id] = other._conv_cache[self.id] = False
|
|
return False
|
|
|
|
def get_children(self):
|
|
return self._cursor.get_children()
|
|
|
|
|
|
class Union(Struct):
|
|
def __init__(self, sdkver, abi, cursor):
|
|
super().__init__(sdkver, abi, cursor)
|
|
|
|
def needs_conversion(self, other):
|
|
return True
|
|
|
|
|
|
class Method:
|
|
def __init__(self, sdkver, abi, cursor, klass, index, override):
|
|
self._sdkver = sdkver
|
|
self._abi = abi
|
|
|
|
self._cursor = cursor
|
|
self._klass = klass
|
|
self._index = index
|
|
self._override = override
|
|
|
|
self.result_type = cursor.result_type
|
|
self.spelling = cursor.spelling
|
|
|
|
@property
|
|
def name(self):
|
|
if self._override > 1: return f'{self.spelling}_{self._override}'
|
|
return self.spelling
|
|
|
|
@property
|
|
def full_name(self):
|
|
return f'{self._klass.full_name}_{self.name}'
|
|
|
|
def get_arguments(self):
|
|
return self._cursor.get_arguments()
|
|
|
|
def needs_conversion(self, other):
|
|
if len(list(self.get_arguments())) != len(list(other.get_arguments())):
|
|
return True
|
|
return False # FIXME
|
|
|
|
def returns_unix_iface(self):
|
|
return self.result_type.spelling.startswith("ISteam") or "GetISteam" in self.name
|
|
|
|
def returns_string(self):
|
|
return self.result_type.spelling == "const char *"
|
|
|
|
def write_params(self, out):
|
|
returns_record = self.result_type.get_canonical().kind == TypeKind.RECORD
|
|
|
|
if self.returns_unix_iface():
|
|
ret = 'struct u_iface _ret'
|
|
elif self.returns_string():
|
|
ret = 'struct u_buffer _ret'
|
|
else:
|
|
ret = "*_ret" if returns_record else "_ret"
|
|
ret = f'{declspec(self.result_type, ret, "w_")}'
|
|
|
|
names = [p.spelling if p.spelling != "" else f'_{chr(0x61 + i)}'
|
|
for i, p in enumerate(self.get_arguments())]
|
|
params = [declspec(p, names[i], "w_") for i, p in enumerate(self.get_arguments())]
|
|
|
|
if self.result_type.kind != TypeKind.VOID:
|
|
params = [ret] + params
|
|
names = ['_ret'] + names
|
|
|
|
if self.name in OUTSTR_PARAMS and OUTSTR_PARAMS[self.name] in names:
|
|
params = ["struct u_buffer _str"] + params
|
|
|
|
params = ['struct u_iface u_iface'] + params
|
|
names = ['u_iface'] + names
|
|
|
|
out(f'struct {self.full_name}_params\n')
|
|
out(u'{\n')
|
|
for param in params:
|
|
out(f' {param};\n')
|
|
out(u'};\n\n')
|
|
|
|
def get_children(self):
|
|
return self._cursor.get_children()
|
|
|
|
|
|
class Destructor(Method):
|
|
def __init__(self, sdkver, abi, cursor, klass, index, override):
|
|
super().__init__(sdkver, abi, cursor, klass, index, override)
|
|
|
|
@property
|
|
def name(self):
|
|
if self._override > 1: return f'destructor_{self._override}'
|
|
return 'destructor'
|
|
|
|
def write_params(self, out):
|
|
pass
|
|
|
|
|
|
class Class:
|
|
def __init__(self, sdkver, abi, cursor):
|
|
self._cursor = cursor
|
|
self._sdkver = sdkver
|
|
self._abi = abi
|
|
self._methods = None
|
|
self._decl_order = 0
|
|
|
|
self.name = cursor.spelling
|
|
self.filename = SDK_CLASSES.get(self.name, None)
|
|
versions = all_versions[sdkver]
|
|
self.version = self.name[1:].upper()
|
|
self.version = versions.get(self.version, "")
|
|
|
|
self.type = self._cursor.type.get_canonical()
|
|
self.id = f'{abi}_{self.name}_{sdkver}'
|
|
|
|
@property
|
|
def methods(self):
|
|
if self._methods:
|
|
return self._methods
|
|
|
|
overrides = {}
|
|
is_method = lambda c: c.kind == CursorKind.CXX_METHOD and c.is_virtual_method()
|
|
in_vtable = lambda c: is_method(c) or c.kind == CursorKind.DESTRUCTOR
|
|
|
|
self._methods = []
|
|
for i, method in enumerate(filter(in_vtable, self._cursor.get_children())):
|
|
index, override = overrides.get(method.spelling, (i, 1))
|
|
overrides[method.spelling] = (index, override + 1)
|
|
if method.kind == CursorKind.DESTRUCTOR:
|
|
self._methods.append(Destructor(self._sdkver, self._abi, method, self, index, override))
|
|
else:
|
|
self._methods.append(Method(self._sdkver, self._abi, method, self, index, override))
|
|
|
|
return self._methods
|
|
|
|
@property
|
|
def full_name(self):
|
|
if len(self.version) == 0:
|
|
return self.name
|
|
return f'{self.name}_{self.version}'
|
|
|
|
@property
|
|
def order(self):
|
|
return self._decl_order
|
|
|
|
def set_used(self, order=-1):
|
|
if self._decl_order <= order:
|
|
return
|
|
self._decl_order = order
|
|
|
|
def needs_conversion(self, other):
|
|
if self._abi[0] != other._abi[0]:
|
|
return True
|
|
if len(self.methods) != len(other.methods):
|
|
return True
|
|
if any(m.needs_conversion(n) for m, n in zip(self.methods, other.methods)):
|
|
return True
|
|
return False
|
|
|
|
def write_definition(self, out, prefix):
|
|
out(f'struct {prefix}{self.full_name}\n')
|
|
out(u'{\n')
|
|
out(u'#ifdef __cplusplus\n')
|
|
for method in self.methods:
|
|
types = [declspec(p, "", prefix) for p in method.get_arguments()]
|
|
|
|
# CGameID -> CGameID &
|
|
# Windows side follows the prototype in the header while Linux
|
|
# steamclient treats gameID parameter as pointer
|
|
if self.full_name == 'ISteamUser_SteamUser008' \
|
|
and method.name == 'InitiateGameConnection':
|
|
types[3] = 'CGameID *'
|
|
|
|
if type(method) is Destructor:
|
|
out(f' virtual ~{prefix}{self.full_name}( {", ".join(types)} ) = 0;\n')
|
|
else:
|
|
method_name = f'{declspec(method.result_type, "", prefix)} {method.spelling}'
|
|
out(f' virtual {method_name}( {", ".join(types)} ) = 0;\n')
|
|
out(u'#endif /* __cplusplus */\n')
|
|
out(u'};\n\n')
|
|
|
|
def get_children(self):
|
|
return self._cursor.get_children()
|
|
|
|
|
|
def Record(sdkver, abi, cursor):
|
|
if cursor.type.get_declaration().kind == CursorKind.UNION_DECL:
|
|
return Union(sdkver, abi, cursor)
|
|
|
|
method_kinds = (CursorKind.CXX_METHOD, CursorKind.DESTRUCTOR)
|
|
is_method = lambda c: c.kind in method_kinds and c.is_virtual_method()
|
|
for _ in filter(is_method, cursor.get_children()):
|
|
return Class(sdkver, abi, cursor)
|
|
|
|
return Struct(sdkver, abi, cursor)
|
|
|
|
|
|
def Type(decl, sdkver, abi):
|
|
name = canonical_typename(decl)
|
|
if name not in all_structs:
|
|
return BasicType(decl, abi)
|
|
return all_structs[name][sdkver][abi]
|
|
|
|
|
|
def canonical_typename(cursor):
|
|
if type(cursor) in (Struct, Class, Cursor):
|
|
return canonical_typename(cursor.type)
|
|
|
|
name = cursor.get_canonical().spelling
|
|
return name.removeprefix("const ")
|
|
|
|
|
|
def underlying_typename(decl):
|
|
return canonical_typename(underlying_type(decl))
|
|
|
|
|
|
def find_struct_abis(name):
|
|
if not name in all_structs:
|
|
return None
|
|
structs = all_structs[name]
|
|
if not sdkver in structs:
|
|
return None
|
|
return structs[sdkver]
|
|
|
|
|
|
def struct_needs_conversion(struct):
|
|
name = canonical_typename(struct)
|
|
if name in EXEMPT_STRUCTS:
|
|
return False
|
|
if name in MANUAL_STRUCTS:
|
|
return True
|
|
if name in PATH_CONV_STRUCTS:
|
|
return True
|
|
|
|
abis = find_struct_abis(name)
|
|
if abis is None: return False
|
|
if abis['w32'].needs_conversion(abis['u32']):
|
|
return True
|
|
if abis['w64'].needs_conversion(abis['u64']):
|
|
return True
|
|
return False
|
|
|
|
|
|
def underlying_type(decl):
|
|
if type(decl) is Cursor:
|
|
decl = decl.type
|
|
if decl.kind == TypeKind.LVALUEREFERENCE: return underlying_type(decl.get_pointee())
|
|
if decl.kind == TypeKind.CONSTANTARRAY: return underlying_type(decl.element_type)
|
|
if decl.kind == TypeKind.POINTER: return underlying_type(decl.get_pointee())
|
|
return decl
|
|
|
|
|
|
def param_needs_conversion(decl):
|
|
decl = underlying_type(decl)
|
|
return decl.kind == TypeKind.RECORD and \
|
|
struct_needs_conversion(decl)
|
|
|
|
|
|
def callconv(cursor, prefix):
|
|
if type(cursor) is not Cursor:
|
|
return ''
|
|
canon = cursor.type.get_canonical()
|
|
if canon.kind != TypeKind.POINTER:
|
|
return ''
|
|
canon = canon.get_pointee()
|
|
if canon.kind != TypeKind.FUNCTIONPROTO:
|
|
return ''
|
|
if cursor.type.kind == TypeKind.TYPEDEF:
|
|
cursor = cursor.type.get_declaration()
|
|
|
|
tokens = cursor.get_tokens()
|
|
while next(tokens).spelling != '(': pass
|
|
token = next(tokens).spelling.strip('_')
|
|
token = token.replace('*', 'cdecl')
|
|
token = token.replace('S_CALLTYPE', 'cdecl')
|
|
return f'{prefix[0].upper()}_{token.upper()} '
|
|
|
|
|
|
def declspec_func(decl, name, prefix):
|
|
ret = declspec(decl.get_result(), "", prefix, False)
|
|
params = [declspec(a, "", prefix, False) for a in decl.argument_types()]
|
|
params = ", ".join(params) if len(params) else "void"
|
|
return f'{ret} ({name})({params})'
|
|
|
|
|
|
def declspec(decl, name, prefix, wrapped=False):
|
|
call = callconv(decl, prefix)
|
|
if type(decl) is Cursor:
|
|
decl = decl.type
|
|
decl = decl.get_canonical()
|
|
|
|
const = 'const ' if decl.is_const_qualified() else ''
|
|
if decl.kind == TypeKind.FUNCTIONPROTO:
|
|
return declspec_func(decl, name, prefix)
|
|
if decl.kind in (TypeKind.POINTER, TypeKind.LVALUEREFERENCE):
|
|
decl = decl.get_pointee()
|
|
spec = declspec(decl, f"*{call}{const}{name}", prefix, False)
|
|
if wrapped:
|
|
return f'{prefix.upper()}PTR({spec}, {name})'
|
|
return spec
|
|
if decl.kind == TypeKind.CONSTANTARRAY:
|
|
decl, count = decl.element_type, decl.element_count
|
|
if wrapped:
|
|
spec = declspec(decl, const, prefix, False)
|
|
return f'{prefix.upper()}ARRAY({spec}, {count}, {name})'
|
|
return declspec(decl, f"({const}{name})[{count}]", prefix, False)
|
|
|
|
if len(name):
|
|
name = f' {name}'
|
|
|
|
if decl.kind in (TypeKind.UNEXPOSED, TypeKind.FUNCTIONPROTO):
|
|
return f'void{name}'
|
|
if decl.kind == TypeKind.ENUM:
|
|
return f'uint{decl.get_size() * 8}_t{name}'
|
|
|
|
type_name = decl.spelling.removeprefix("const ")
|
|
if decl.kind == TypeKind.RECORD \
|
|
and type_name in all_versions[sdkver] \
|
|
and type_name not in EXEMPT_STRUCTS:
|
|
if type_name in unique_structs:
|
|
return f'{const}{all_versions[sdkver][type_name]}{name}'
|
|
return f'{const}{prefix}{all_versions[sdkver][type_name]}{name}'
|
|
|
|
if type_name.startswith('ISteam'):
|
|
return f'{const}void /*{type_name}*/{name}'
|
|
if type_name in ('void', 'char', 'float', 'double'):
|
|
return f'{const}{type_name}{name}'
|
|
if type_name.startswith(('bool', 'int', 'long', 'short', 'signed')):
|
|
return f'{const}int{decl.get_size() * 8}_t{name}'
|
|
if type_name.startswith(('uint', 'unsigned')):
|
|
return f'{const}uint{decl.get_size() * 8}_t{name}'
|
|
|
|
if 'unnamed union' in decl.spelling:
|
|
return f'{const}struct {{ uint8_t _[{decl.get_size()}]; }}{name}'
|
|
return f'{decl.spelling}{name}'
|
|
|
|
|
|
def handle_method_cpp(method, classname, out):
|
|
returns_void = method.result_type.kind == TypeKind.VOID
|
|
returns_record = method.result_type.get_canonical().kind == TypeKind.RECORD
|
|
|
|
names = [p.spelling if p.spelling != "" else f'_{chr(0x61 + i)}'
|
|
for i, p in enumerate(method.get_arguments())]
|
|
outstr_param = OUTSTR_PARAMS.get(method.name, None)
|
|
|
|
need_convert = {n: p for n, p in zip(names, method.get_arguments())
|
|
if param_needs_conversion(p) and n != outstr_param}
|
|
manual_convert = {n: p for n, p in zip(names, method.get_arguments())
|
|
if underlying_type(p).spelling in MANUAL_TYPES
|
|
or p.spelling in MANUAL_PARAMS}
|
|
|
|
names = ['u_iface'] + names
|
|
|
|
out(f'NTSTATUS {method.full_name}( void *args )\n')
|
|
out(u'{\n')
|
|
out(f' struct {method.full_name}_params *params = (struct {method.full_name}_params *)args;\n')
|
|
out(f' struct u_{klass.full_name} *iface = (struct u_{klass.full_name} *)params->u_iface;\n')
|
|
if method.name in OUTSTR_PARAMS and OUTSTR_PARAMS[method.name] in names:
|
|
out(u' char *u_str;\n')
|
|
|
|
params = list(zip(names[1:], method.get_arguments()))
|
|
for i, (name, param) in enumerate(params[:-1]):
|
|
if underlying_type(param).kind != TypeKind.RECORD:
|
|
continue
|
|
next_name, next_param = params[i + 1]
|
|
if not any(w in next_name.lower() for w in ('count', 'len', 'size', 'num')):
|
|
continue
|
|
assert underlying_typename(param) in SIZED_STRUCTS | EXEMPT_STRUCTS
|
|
|
|
for i, (name, param) in enumerate(params[1:]):
|
|
if underlying_type(param).kind != TypeKind.RECORD:
|
|
continue
|
|
prev_name, prev_param = params[i - 1]
|
|
if not any(w in prev_name.lower() for w in ('count', 'len', 'size', 'num')):
|
|
continue
|
|
if underlying_typename(param) not in SIZED_STRUCTS | EXEMPT_STRUCTS:
|
|
print('Warning:', underlying_typename(param), name, 'following', prev_name)
|
|
|
|
path_conv_wtou = PATH_CONV_METHODS_WTOU.get(f'{klass.name}_{method.spelling}', {})
|
|
|
|
for name, conv in filter(lambda x: x[0] in names, path_conv_wtou.items()):
|
|
if conv['array']:
|
|
out(f' const char **u_{name} = steamclient_dos_to_unix_path_array( params->{name} );\n')
|
|
else:
|
|
out(f' char *u_{name} = steamclient_dos_to_unix_path( params->{name}, {int(conv["url"])} );\n')
|
|
|
|
need_output = {}
|
|
|
|
for name, param in sorted(need_convert.items()):
|
|
if param.type.kind != TypeKind.POINTER:
|
|
out(f' {declspec(param.type, f"u_{name}", "u_")} = params->{name};\n')
|
|
continue
|
|
|
|
pointee = param.type.get_pointee()
|
|
if pointee.kind == TypeKind.POINTER:
|
|
need_output[name] = param
|
|
out(f' {declspec(pointee, f"lin_{name}", "u_")};\n')
|
|
continue
|
|
|
|
if not pointee.is_const_qualified():
|
|
need_output[name] = param
|
|
|
|
out(f' {declspec(pointee, f"u_{name}", "u_")} = *params->{name};\n')
|
|
|
|
for name, param in sorted(manual_convert.items()):
|
|
if name in MANUAL_PARAMS:
|
|
out(f' {declspec(param, f"u_{name}", "u_")} = manual_convert_{name}( params->{name} );\n')
|
|
else:
|
|
out(f' {declspec(param, f"u_{name}", "u_")} = manual_convert_{method.name}_{name}( params->{name} );\n')
|
|
|
|
if returns_void:
|
|
out(u' ')
|
|
elif returns_record:
|
|
out(u' *params->_ret = ')
|
|
else:
|
|
out(u' params->_ret = ')
|
|
|
|
def param_call(name, param):
|
|
pfx = '&' if param.type.kind == TypeKind.POINTER else ''
|
|
if name in need_convert: return f"{pfx}u_{name}"
|
|
if name in manual_convert: return f"u_{name}"
|
|
if name in path_conv_wtou: return f"u_{name}"
|
|
if name == OUTSTR_PARAMS.get(method.name, None):
|
|
return f'params->{name} ? ({declspec(param, "", "u_")})&u_str : nullptr'
|
|
return f'params->{name}'
|
|
|
|
params = [param_call(n, p) for n, p in zip(names[1:], method.get_arguments())]
|
|
|
|
# CGameID -> CGameID &
|
|
# Windows side follows the prototype in the header while Linux
|
|
# steamclient treats gameID parameter as pointer
|
|
if klass.full_name == 'ISteamUser_SteamUser008' \
|
|
and method.name == 'InitiateGameConnection':
|
|
params[3] = f'&{params[3]}'
|
|
|
|
out(f'iface->{method.spelling}( {", ".join(params)} );\n')
|
|
|
|
for name, param in sorted(need_output.items()):
|
|
out(f' *params->{name} = u_{name};\n')
|
|
|
|
path_conv_utow = PATH_CONV_METHODS_UTOW.get(f'{klass.name}_{method.spelling}', {})
|
|
|
|
for name, conv in filter(lambda x: x[0] in names, path_conv_utow.items()):
|
|
out(u' ')
|
|
if "ret_size" in path_conv_utow:
|
|
out(u'params->_ret = ')
|
|
out(f'steamclient_unix_path_to_dos_path( params->_ret, params->{name}, params->{name}, params->{conv["len"]}, {int(conv["url"])} );\n')
|
|
|
|
for name, conv in filter(lambda x: x[0] in names, path_conv_wtou.items()):
|
|
if conv["array"]:
|
|
out(f' steamclient_free_path_array( u_{name} );\n')
|
|
else:
|
|
out(f' steamclient_free_path( u_{name} );\n')
|
|
|
|
if method.name in OUTSTR_PARAMS and OUTSTR_PARAMS[method.name] in names:
|
|
out(f' if (params->{OUTSTR_PARAMS[method.name]}) params->_str = u_str;\n');
|
|
|
|
out(u' return 0;\n')
|
|
out(u'}\n\n')
|
|
|
|
|
|
def handle_thiscall_wrapper(klass, method, out):
|
|
returns_record = method.result_type.get_canonical().kind == TypeKind.RECORD
|
|
|
|
def param_stack_size(param):
|
|
if param.type.kind == TypeKind.LVALUEREFERENCE: return 4
|
|
return ((param.type.get_size() + 3) // 4) * 4
|
|
|
|
size = 4 + sum(param_stack_size(p) for p in method.get_arguments())
|
|
if returns_record: size += 4
|
|
|
|
name = f'win{klass.full_name}_{method.name}'
|
|
out(f'DEFINE_THISCALL_WRAPPER({name}, {size})\n')
|
|
|
|
|
|
def handle_method_c(klass, method, winclassname, out):
|
|
returns_void = method.result_type.kind == TypeKind.VOID
|
|
returns_record = method.result_type.get_canonical().kind == TypeKind.RECORD
|
|
|
|
ret = "*" if returns_record else ""
|
|
ret = f'{declspec(method.result_type, ret, "w_")} '
|
|
|
|
names = [p.spelling if p.spelling != "" else f'_{chr(0x61 + i)}'
|
|
for i, p in enumerate(method.get_arguments())]
|
|
params = [declspec(p, names[i], "w_") for i, p in enumerate(method.get_arguments())]
|
|
|
|
if returns_record:
|
|
params = [f'{declspec(method.result_type, "*_ret", "w_")}'] + params
|
|
names = ['_ret'] + names
|
|
|
|
params = ['struct w_iface *_this'] + params
|
|
names = ['_this'] + names
|
|
|
|
out(f'{ret}__thiscall {winclassname}_{method.name}({", ".join(params)})\n')
|
|
out(u'{\n')
|
|
|
|
out(f' struct {method.full_name}_params params =\n')
|
|
out(u' {\n')
|
|
out(u' .u_iface = _this->u_iface,\n')
|
|
for name in names[1:]: out(f' .{name} = {name},\n')
|
|
out(u' };\n')
|
|
|
|
out(u' TRACE("%p\\n", _this);\n')
|
|
|
|
# Some games pass pointers to the data in PE modules which have no access. Access violation is handled
|
|
# by VEH (which decrypts data and changes page protection). That can only work if such access violation happens
|
|
# on the PE side, so access the data before passing to the Unix side.
|
|
for _, p in enumerate(method.get_arguments()):
|
|
pretouch = PRETOUCH_TYPES.get(p.type.spelling)
|
|
if pretouch is not None:
|
|
out(pretouch.format(p.spelling))
|
|
|
|
out(f' STEAMCLIENT_CALL( {method.full_name}, ¶ms );\n')
|
|
if method.name in OUTSTR_PARAMS and OUTSTR_PARAMS[method.name] in names:
|
|
out(f' if ({OUTSTR_PARAMS[method.name]}) *{OUTSTR_PARAMS[method.name]} = get_unix_buffer( params._str );\n')
|
|
|
|
if method.name.startswith('CreateFakeUDPPort'):
|
|
out(u' return create_winISteamNetworkingFakeUDPPort_SteamNetworkingFakeUDPPort001( params._ret );\n')
|
|
elif method.returns_unix_iface():
|
|
out(u' return create_win_interface( pchVersion, params._ret );\n')
|
|
elif method.returns_string():
|
|
out(u' return get_unix_buffer( params._ret );\n')
|
|
elif not returns_void:
|
|
out(u' return params._ret;\n')
|
|
out(u'}\n\n')
|
|
|
|
|
|
def handle_class(klass):
|
|
cppname = f"cpp{klass.full_name}"
|
|
|
|
with open(f"{cppname}.cpp", "w") as file:
|
|
out = file.write
|
|
|
|
out(u'/* This file is auto-generated, do not edit. */\n')
|
|
out(u'#include "unix_private.h"\n\n')
|
|
|
|
out(u'#if 0\n')
|
|
out(u'#pragma makedep unix\n')
|
|
out(u'#endif\n\n')
|
|
|
|
for method in klass.methods:
|
|
if type(method) is Destructor:
|
|
continue
|
|
if is_manual_method(klass, method, "u"):
|
|
continue
|
|
handle_method_cpp(method, klass.name, out)
|
|
|
|
|
|
winclassname = f"win{klass.full_name}"
|
|
with open(f"win{klass.name}.c", "a") as file:
|
|
out = file.write
|
|
|
|
for method in klass.methods:
|
|
handle_thiscall_wrapper(klass, method, out)
|
|
out('\n')
|
|
|
|
for method in klass.methods:
|
|
if is_manual_method(klass, method, "w"):
|
|
continue
|
|
if type(method) is Destructor:
|
|
out(f'void __thiscall {winclassname}_{method.name}(struct w_iface *_this)\n{{/* never called */}}\n\n')
|
|
else:
|
|
handle_method_c(klass, method, winclassname, out)
|
|
|
|
out(f'extern vtable_ptr {winclassname}_vtable;\n')
|
|
out(u'\n')
|
|
out(f'DEFINE_RTTI_DATA0({winclassname}, 0, \".?AV{klass.name}@@\")\n')
|
|
out(u'\n')
|
|
out(f'__ASM_BLOCK_BEGIN({winclassname}_vtables)\n')
|
|
out(f' __ASM_VTABLE({winclassname},\n')
|
|
for method in sorted(klass.methods, key=lambda x: (x._index, -x._override)):
|
|
out(f' VTABLE_ADD_FUNC({winclassname}_{method.name})\n')
|
|
out(u' );\n')
|
|
out(u'__ASM_BLOCK_END\n')
|
|
out(u'\n')
|
|
out(f'struct w_iface *create_{winclassname}( struct u_iface u_iface )\n')
|
|
out(u'{\n')
|
|
out(f' struct w_iface *r = alloc_mem_for_iface(sizeof(struct w_iface), "{klass.version}");\n')
|
|
out(u' TRACE("-> %p\\n", r);\n')
|
|
out(f' r->vtable = alloc_vtable(&{winclassname}_vtable, {len(klass.methods)}, "{klass.version}");\n')
|
|
out(u' r->u_iface = u_iface;\n')
|
|
out(u' return r;\n')
|
|
out(u'}\n\n')
|
|
|
|
|
|
def parse(sources, sdkver, abi):
|
|
args = [f'-m{abi[1:]}', '-I' + CLANG_PATH + '/include/', '-I/usr/include/x86_64-linux-gnu/']
|
|
if abi[0] == 'w':
|
|
args += ["-D_WIN32", "-U__linux__"]
|
|
args += ["-fms-extensions", "-mms-bitfields"]
|
|
args += ["-Wno-ignored-attributes", "-Wno-incompatible-ms-struct"]
|
|
if abi[0] == 'u':
|
|
args += ["-DGNUC"]
|
|
|
|
index = Index.create()
|
|
build = index.parse("source.cpp", args=args, unsaved_files=sources.items())
|
|
diagnostics = list(build.diagnostics)
|
|
for diag in diagnostics: print(diag)
|
|
assert len(diagnostics) == 0
|
|
|
|
return sdkver, abi, build
|
|
|
|
|
|
def load(sdkver):
|
|
sdkdir = f"steamworks_sdk_{sdkver}"
|
|
|
|
sources = {}
|
|
versions = {}
|
|
for file in os.listdir(sdkdir):
|
|
# Some files from Valve have non-UTF-8 stuff in the comments
|
|
# (typically the copyright symbol); therefore we ignore UTF-8
|
|
# encoding errors
|
|
lines = open(f"{sdkdir}/{file}", "r", errors="replace").readlines()
|
|
if """#error "This file isn't used any more"\n""" in lines:
|
|
sources[f"{sdkdir}/{file}"] = ""
|
|
|
|
results = (DEFINE_INTERFACE_VERSION.match(l) for l in lines)
|
|
for result in (r.groupdict() for r in results if r):
|
|
name, version = result['name'], result['version']
|
|
name = name.replace('_INTERFACE_VERSION', '')
|
|
name = name.replace('_VERSION', '')
|
|
versions[name] = version
|
|
|
|
source = [f'#if __has_include("{sdkdir}/{file}")\n'
|
|
f'#include "{sdkdir}/{file}"\n'
|
|
f'#endif\n'
|
|
for file in SDK_SOURCES.keys()]
|
|
sources["source.cpp"] = "\n".join(source)
|
|
|
|
return versions, sources
|
|
|
|
|
|
def classify_struct(name):
|
|
if name in EXEMPT_STRUCTS:
|
|
return None
|
|
structs = all_structs[name]
|
|
|
|
prev = []
|
|
versions = {}
|
|
unique = True
|
|
|
|
def set_class_version(abis, version):
|
|
for abi in filter(lambda x: type(x) is Class, abis):
|
|
abi.version = version
|
|
|
|
for sdkver in filter(lambda v: v in structs, reversed(SDK_VERSIONS)):
|
|
abis = [structs[sdkver][a] for a in ABIS]
|
|
|
|
if any(abis[0].needs_conversion(a) for a in abis[1:]):
|
|
unique = False
|
|
|
|
def is_always_compatible(other):
|
|
for a, b in zip(abis, other):
|
|
if a.needs_conversion(b):
|
|
return False
|
|
return True
|
|
|
|
compat = next((k for k, v in prev if is_always_compatible(v)), None)
|
|
if compat:
|
|
versions[sdkver] = versions[compat]
|
|
set_class_version(abis, compat)
|
|
else:
|
|
[abi.set_used() for abi in abis] # make sure order is computed
|
|
versions[sdkver] = f"{name}_{sdkver}"
|
|
prev += [(sdkver, abis)]
|
|
set_class_version(abis, sdkver)
|
|
|
|
if unique:
|
|
unique_structs.append(name)
|
|
|
|
if len(set(versions.values())) == 1:
|
|
versions = {sdkver: name for sdkver in versions.keys()}
|
|
for sdkver in filter(lambda v: v in structs, reversed(SDK_VERSIONS)):
|
|
abis = [structs[sdkver][a] for a in ABIS]
|
|
set_class_version(abis, "")
|
|
|
|
return versions
|
|
|
|
|
|
for i, sdkver in enumerate(SDK_VERSIONS):
|
|
print(f'loading SDKs... {i * 100 // len(SDK_VERSIONS)}%', end='\r')
|
|
all_versions[sdkver], all_sources[sdkver] = load(sdkver)
|
|
print('loading SDKs... 100%')
|
|
|
|
|
|
tmp_classes = {}
|
|
|
|
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
arg0 = [sdkver for sdkver in SDK_VERSIONS for abi in ABIS]
|
|
arg1 = [abi for sdkver in SDK_VERSIONS for abi in ABIS]
|
|
def parse_map(sdkver, abi):
|
|
return parse(all_sources[sdkver], sdkver, abi)
|
|
|
|
results = executor.map(parse_map, arg0, arg1)
|
|
for i, result in enumerate(results):
|
|
print(f'parsing SDKs... {i * 100 // len(arg0)}%', end='\r')
|
|
sdkver, abi, build = result
|
|
if sdkver not in all_records: all_records[sdkver] = {}
|
|
if sdkver not in tmp_classes: tmp_classes[sdkver] = {}
|
|
|
|
versions = all_versions[sdkver]
|
|
|
|
structs = build.cursor.get_children()
|
|
structs = filter(lambda c: c.is_definition(), structs)
|
|
structs = filter(lambda c: c.type.get_canonical().kind == TypeKind.RECORD, structs)
|
|
structs = filter(lambda c: c.kind != CursorKind.TYPEDEF_DECL, structs)
|
|
structs = filter(lambda c: c.spelling not in SDK_CLASSES, structs)
|
|
structs = [Record(sdkver, abi, c) for c in structs]
|
|
structs = {c.name: c for c in structs}
|
|
|
|
classes = build.cursor.get_children()
|
|
classes = filter(lambda c: c.is_definition(), classes)
|
|
classes = filter(lambda c: c.kind == CursorKind.CLASS_DECL, classes)
|
|
classes = filter(lambda c: c.spelling in SDK_CLASSES, classes)
|
|
classes = filter(lambda c: c.spelling[1:].upper() in versions, classes)
|
|
classes = [Class(sdkver, abi, c) for c in classes]
|
|
classes = {c.version: c for c in classes}
|
|
|
|
all_records[sdkver][abi] = structs
|
|
tmp_classes[sdkver][abi] = classes
|
|
|
|
for name, struct in structs.items():
|
|
if name not in all_structs:
|
|
all_structs[name] = {}
|
|
if sdkver not in all_structs[name]:
|
|
all_structs[name][sdkver] = {}
|
|
all_structs[name][sdkver][abi] = struct
|
|
|
|
for i, sdkver in enumerate(reversed(SDK_VERSIONS)):
|
|
all_classes.update(tmp_classes[sdkver]['u32'])
|
|
|
|
print('parsing SDKs... 100%')
|
|
|
|
|
|
tmp_structs = {}
|
|
|
|
for i, name in enumerate(all_structs.keys()):
|
|
print(f'classifying structs... {i * 100 // len(all_structs.keys())}%', end='\r')
|
|
versions = classify_struct(name)
|
|
for sdkver in SDK_VERSIONS:
|
|
if not versions or sdkver not in versions: continue
|
|
all_versions[sdkver][name] = versions[sdkver]
|
|
|
|
def struct_order(x):
|
|
name, structs = x if type(x) is tuple else (x, all_structs[x])
|
|
order = (struct.order for abis in structs.values()
|
|
for struct in abis.values())
|
|
return (min(order), name)
|
|
|
|
for name, structs in sorted(all_structs.items(), key=struct_order):
|
|
tmp_structs[name] = {}
|
|
for sdkver in filter(lambda v: v in structs, SDK_VERSIONS):
|
|
tmp_structs[name][sdkver] = {a: structs[sdkver][a] for a in ABIS}
|
|
|
|
all_structs = tmp_structs
|
|
|
|
print('classifying structs... 100%')
|
|
|
|
|
|
for klass in all_classes.values():
|
|
with open(f"win{klass.name}.c", "w") as file:
|
|
out = file.write
|
|
|
|
out(u'/* This file is auto-generated, do not edit. */\n')
|
|
out(u'#include "steamclient_private.h"\n')
|
|
out(u'\n')
|
|
out(u'WINE_DEFAULT_DEBUG_CHANNEL(steamclient);\n')
|
|
out(u'\n')
|
|
|
|
|
|
for _, klass in sorted(all_classes.items()):
|
|
sdkver = klass._sdkver
|
|
handle_class(klass)
|
|
|
|
|
|
for name in sorted(set(k.name for k in all_classes.values())):
|
|
with open(f"win{name}.c", "a") as file:
|
|
out = file.write
|
|
out(f'void init_win{name}_rtti( char *base )\n')
|
|
out(u'{\n')
|
|
out(u'#ifdef __x86_64__\n')
|
|
|
|
for _, klass in sorted(all_classes.items()):
|
|
with open(f"win{klass.name}.c", "a") as file:
|
|
out = file.write
|
|
out(f' init_win{klass.full_name}_rtti( base );\n')
|
|
|
|
for name in sorted(set(k.name for k in all_classes.values())):
|
|
with open(f"win{name}.c", "a") as file:
|
|
out = file.write
|
|
out(u'#endif /* __x86_64__ */\n')
|
|
out(u'}\n')
|
|
|
|
|
|
with open("steamclient_generated.h", "w") as file:
|
|
out = file.write
|
|
|
|
out(u'/* This file is auto-generated, do not edit. */\n\n')
|
|
|
|
for _, klass in sorted(all_classes.items()):
|
|
out(f"extern struct w_iface *create_win{klass.full_name}( struct u_iface );\n")
|
|
|
|
|
|
with open("steamclient_generated.c", "w") as file:
|
|
out = file.write
|
|
|
|
out(u'/* This file is auto-generated, do not edit. */\n\n')
|
|
out(u'#include "steamclient_private.h"\n\n')
|
|
|
|
out(u'static const struct { const char *iface_version; iface_constructor ctor; } constructors[] =\n')
|
|
out(u'{\n')
|
|
for _, klass in sorted(all_classes.items()):
|
|
out(f' {{"{klass.version}", &create_win{klass.full_name}}},\n')
|
|
for alias in VERSION_ALIASES.get(klass.version, []):
|
|
out(f' {{"{alias}", &create_win{klass.full_name}}}, /* alias */\n')
|
|
out(u'};\n')
|
|
out(u'\n')
|
|
out(u'iface_constructor find_iface_constructor( const char *iface_version )\n')
|
|
out(u'{\n')
|
|
out(u' int i;\n')
|
|
out(u' for (i = 0; i < ARRAYSIZE(constructors); ++i)\n')
|
|
out(u' if (!strcmp( iface_version, constructors[i].iface_version ))\n')
|
|
out(u' return constructors[i].ctor;\n')
|
|
out(u' return NULL;\n')
|
|
out(u'}\n\n')
|
|
|
|
for name in sorted(set(k.name for k in all_classes.values())):
|
|
out(f'extern void init_win{name}_rtti( char * );\n')
|
|
out(u'\n')
|
|
out(u'void init_rtti( char *base )\n')
|
|
out(u'{\n')
|
|
for name in sorted(set(k.name for k in all_classes.values())):
|
|
out(f' init_win{name}_rtti( base );\n')
|
|
out(u'}\n')
|
|
|
|
|
|
for name, klasses in all_classes.items():
|
|
if name not in SDK_CLASSES: continue
|
|
for sdkver, klass in klasses.items():
|
|
version = all_versions[sdkver][name[1:].upper()]
|
|
handle_class(sdkver, klass, version, SDK_CLASSES[name])
|
|
|
|
|
|
declared = {}
|
|
|
|
with open('steamclient_structs_generated.h', 'w') as file:
|
|
out = file.write
|
|
|
|
for name in sorted(unique_structs, key=struct_order):
|
|
if name in EXEMPT_STRUCTS: continue
|
|
for sdkver, abis in all_structs[name].items():
|
|
if name not in all_versions[sdkver]: continue
|
|
|
|
version = all_versions[sdkver][name]
|
|
if f'struct {version}' in declared: continue
|
|
declared[f'struct {version}'] = True
|
|
|
|
kind = 'union' if type(abis['w64']) is Union else 'struct'
|
|
|
|
out(f'typedef {kind} {version} {version};\n')
|
|
abis['w64'].write_definition(out, "", [])
|
|
|
|
for name, structs in all_structs.items():
|
|
if name in EXEMPT_STRUCTS: continue
|
|
if name in unique_structs: continue
|
|
for sdkver, abis in structs.items():
|
|
if name not in all_versions[sdkver]: continue
|
|
|
|
version = all_versions[sdkver][name]
|
|
if f'typedef {version}' in declared: continue
|
|
declared[f'typedef {version}'] = True
|
|
|
|
kind = 'union' if type(abis['w64']) is Union else 'struct'
|
|
|
|
if type(abis['w64']) is Class:
|
|
out(f'typedef {kind} u_{version} u_{version};\n')
|
|
out(f'typedef {kind} u_{version} u64_{version};\n')
|
|
out(f'typedef {kind} u_{version} u32_{version};\n')
|
|
out(f'typedef {kind} w_{version} w_{version};\n')
|
|
out(f'typedef {kind} w_{version} w64_{version};\n')
|
|
out(f'typedef {kind} w_{version} w32_{version};\n')
|
|
continue
|
|
|
|
if abis["w64"].needs_conversion(abis["u64"]):
|
|
out(f'typedef {kind} u64_{version} u64_{version};\n')
|
|
else:
|
|
out(f'typedef {kind} w64_{version} u64_{version};\n')
|
|
out(f'typedef {kind} w64_{version} w64_{version};\n')
|
|
|
|
if abis["w32"].needs_conversion(abis["u32"]):
|
|
out(f'typedef {kind} u32_{version} u32_{version};\n')
|
|
else:
|
|
out(f'typedef {kind} w32_{version} u32_{version};\n')
|
|
out(f'typedef {kind} w32_{version} w32_{version};\n')
|
|
|
|
for name, structs in all_structs.items():
|
|
if name in EXEMPT_STRUCTS: continue
|
|
if name in unique_structs: continue
|
|
for sdkver, abis in structs.items():
|
|
if name not in all_versions[sdkver]: continue
|
|
|
|
version = all_versions[sdkver][name]
|
|
if f'struct {version}' in declared: continue
|
|
declared[f'struct {version}'] = True
|
|
|
|
kind = 'union' if type(abis['w64']) is Union else 'struct'
|
|
|
|
if type(abis['w64']) is Class:
|
|
abis['w64'].write_definition(out, "w_")
|
|
abis['u64'].write_definition(out, "u_")
|
|
continue
|
|
|
|
if not abis["w64"].needs_conversion(abis["u64"]):
|
|
abis['w64'].write_definition(out, "w64_", [])
|
|
else:
|
|
abis['w64'].write_definition(out, "w64_", ["u64_"])
|
|
abis['u64'].write_definition(out, "u64_", ["w64_"])
|
|
|
|
if not abis["w32"].needs_conversion(abis["u32"]):
|
|
abis['w32'].write_definition(out, "w32_", [])
|
|
else:
|
|
abis['w32'].write_definition(out, "w32_", ["u32_"])
|
|
abis['u32'].write_definition(out, "u32_", ["w32_"])
|
|
|
|
out(u'#ifdef __i386__\n')
|
|
out(f'typedef w32_{version} w_{version};\n')
|
|
out(f'typedef u32_{version} u_{version};\n')
|
|
out(u'#endif\n')
|
|
out(u'#ifdef __x86_64__\n')
|
|
out(f'typedef w64_{version} w_{version};\n')
|
|
out(f'typedef u64_{version} u_{version};\n')
|
|
out(u'#endif\n')
|
|
out(u'\n')
|
|
|
|
|
|
all_methods = [(k, m) for _, k in sorted(all_classes.items())
|
|
for m in k.methods]
|
|
|
|
with open("unix_private_generated.h", "w") as file:
|
|
out = file.write
|
|
|
|
out(u'/* This file is auto-generated, do not edit. */\n\n')
|
|
out(u'#ifdef __cplusplus\n')
|
|
out(u'extern "C" {\n')
|
|
out(u'#endif /* __cplusplus */\n')
|
|
out(u'\n')
|
|
|
|
for klass in all_classes.values():
|
|
sdkver = klass._sdkver
|
|
klass.write_definition(out, "u_")
|
|
out(u'\n')
|
|
|
|
for klass, method in all_methods:
|
|
sdkver = klass._sdkver
|
|
if type(method) is Destructor:
|
|
continue
|
|
out(f'NTSTATUS {method.full_name}( void * );\n')
|
|
out(u'\n')
|
|
|
|
out(u'#ifdef __cplusplus\n')
|
|
out(u'} /* extern "C" */\n')
|
|
out(u'#endif /* __cplusplus */\n')
|
|
|
|
|
|
with open(u"unixlib_generated.h", "w") as file:
|
|
out = file.write
|
|
|
|
out(u'/* This file is auto-generated, do not edit. */\n')
|
|
out(u'#include <stdarg.h>\n')
|
|
out(u'#include <stddef.h>\n')
|
|
out(u'#include <stdint.h>\n')
|
|
out(u'\n')
|
|
out(u'#ifdef __cplusplus\n')
|
|
out(u'extern "C" {\n')
|
|
out(u'#endif /* __cplusplus */\n')
|
|
out(u'\n')
|
|
|
|
out(u'#include <pshpack1.h>\n\n')
|
|
for klass, method in all_methods:
|
|
sdkver = klass._sdkver
|
|
method.write_params(out)
|
|
out(u'#include <poppack.h>\n\n')
|
|
|
|
out(u'enum unix_funcs\n')
|
|
out(u'{\n')
|
|
for func in UNIX_FUNCS:
|
|
out(f' unix_{func},\n')
|
|
for klass, method in all_methods:
|
|
sdkver = klass._sdkver
|
|
if type(method) is Destructor:
|
|
continue
|
|
out(f' unix_{method.full_name},\n')
|
|
out(u'};\n')
|
|
out(u'\n')
|
|
|
|
out(u'#ifdef __cplusplus\n')
|
|
out(u'} /* extern "C" */\n')
|
|
out(u'#endif /* __cplusplus */\n')
|
|
|
|
|
|
with open('unixlib_generated.cpp', 'w') as file:
|
|
out = file.write
|
|
|
|
out(u'/* This file is auto-generated, do not edit. */\n\n')
|
|
out(u'#if 0\n')
|
|
out(u'#pragma makedep unix\n')
|
|
out(u'#endif\n')
|
|
out(u'\n')
|
|
out(u'#include "unix_private.h"\n\n')
|
|
|
|
out(u'extern "C" const unixlib_entry_t __wine_unix_call_funcs[] =\n')
|
|
out(u'{\n')
|
|
for func in UNIX_FUNCS:
|
|
out(f' {func},\n')
|
|
for klass, method in all_methods:
|
|
sdkver = klass._sdkver
|
|
if type(method) is Destructor:
|
|
continue
|
|
out(f' {method.full_name},\n')
|
|
out(u'};\n')
|
|
out(u'\n')
|
|
|
|
callbacks = []
|
|
|
|
for name in sorted(unique_structs, key=struct_order):
|
|
for sdkver, abis in all_structs[name].items():
|
|
if name not in all_versions[sdkver]: continue
|
|
|
|
version = all_versions[sdkver][name]
|
|
if f'checks {version}' in declared: continue
|
|
declared[f'checks {version}'] = True
|
|
|
|
abis['w64'].write_checks(out, "")
|
|
|
|
for name, structs in all_structs.items():
|
|
if name in EXEMPT_STRUCTS: continue
|
|
if name in unique_structs: continue
|
|
for sdkver, abis in structs.items():
|
|
if name not in all_versions[sdkver]: continue
|
|
|
|
version = all_versions[sdkver][name]
|
|
if f'checks {version}' in declared: continue
|
|
declared[f'checks {version}'] = True
|
|
|
|
if type(abis['w64']) is Class:
|
|
continue
|
|
|
|
cbid = abis["w64"].callback_id
|
|
if cbid is not None and (abis["w64"] != abis["u64"] or abis["w32"] != abis["u32"]):
|
|
callbacks += [(cbid, sdkver, abis)]
|
|
|
|
abis['w64'].write_checks(out, "w64_")
|
|
abis['u64'].write_checks(out, "u64_")
|
|
abis['w32'].write_checks(out, "w32_")
|
|
abis['u32'].write_checks(out, "u32_")
|
|
|
|
if name in MANUAL_STRUCTS: continue
|
|
|
|
for sdkver, abis in structs.items():
|
|
if name not in all_versions[sdkver]: continue
|
|
|
|
version = all_versions[sdkver][name]
|
|
if f'struct {version} convert' in declared: continue
|
|
declared[f'struct {version} convert'] = True
|
|
|
|
if type(abis['w64']) in (Class, Union):
|
|
continue
|
|
|
|
path_conv_fields = PATH_CONV_STRUCTS.get(name, {})
|
|
|
|
if abis["w64"].needs_conversion(abis["u64"]):
|
|
abis['w64'].write_converter('u64_', {})
|
|
out(u'\n')
|
|
abis['u64'].write_converter('w64_', path_conv_fields)
|
|
|
|
if abis["w32"].needs_conversion(abis["u32"]):
|
|
abis['w32'].write_converter('u32_', {})
|
|
out(u'\n')
|
|
abis['u32'].write_converter('w32_', path_conv_fields)
|
|
|
|
out(u'#ifdef __i386__\n')
|
|
out(u'const struct callback_def callback_data[] =\n{\n');
|
|
values = set()
|
|
for cbid, sdkver, abis in sorted(callbacks, key=lambda x: x[0]):
|
|
name, value = abis["u32"].name, (cbid, abis["w32"].size, abis["u32"].size)
|
|
if name in all_versions[sdkver]: name = all_versions[sdkver][name]
|
|
|
|
w_from_u = f'{name}_w_from_u'
|
|
if not w_from_u in written_converters:
|
|
w_from_u = u'nullptr'
|
|
|
|
if value not in values:
|
|
out(f' {{ {cbid}, {sdkver}, {abis["w32"].size}, {abis["u32"].size}, {w_from_u} }},\n')
|
|
else:
|
|
out(f' /*{{ {cbid}, {sdkver}, {abis["w32"].size}, {abis["u32"].size}, {w_from_u} }},*/\n')
|
|
values.add(value)
|
|
out(u'};\n');
|
|
out(u'#endif\n')
|
|
out(u'#ifdef __x86_64__\n')
|
|
out(u'const struct callback_def callback_data[] =\n{\n');
|
|
values = set()
|
|
for cbid, sdkver, abis in sorted(callbacks, key=lambda x: x[0]):
|
|
name, value = abis["u64"].name, (cbid, abis["w64"].size, abis["u64"].size)
|
|
if name in all_versions[sdkver]: name = all_versions[sdkver][name]
|
|
|
|
w_from_u = f'{name}_w_from_u'
|
|
if not w_from_u in written_converters:
|
|
w_from_u = u'nullptr'
|
|
|
|
if value not in values:
|
|
out(f' {{ {cbid}, {sdkver}, {abis["w64"].size}, {abis["u64"].size}, {w_from_u} }},\n')
|
|
else:
|
|
out(f' /*{{ {cbid}, {sdkver}, {abis["w64"].size}, {abis["u64"].size}, {w_from_u} }},*/\n')
|
|
values.add(value)
|
|
out(u'};\n');
|
|
out(u'#endif\n')
|
|
out(u'const unsigned int callback_data_size = ARRAY_SIZE(callback_data);\n');
|