#include "ntstatus.h" #define WIN32_NO_STATUS #include "unix_private.h" #include #include #include #include #include #include #if 0 #pragma makedep unix #endif WINE_DEFAULT_DEBUG_CHANNEL(steamclient); char *g_tmppath; struct callback_entry { struct list entry; struct callback callback; }; static struct list callbacks = LIST_INIT( callbacks ); static pthread_mutex_t callbacks_lock = PTHREAD_MUTEX_INITIALIZER; static const struct callback_def *find_first_callback_def_by_id( int id, bool wow64 ); static int callback_len_utow( int cb_id, int u_len, bool wow64 ) { const struct callback_def *c, *end; if (!(c = find_first_callback_def_by_id( cb_id, wow64 ))) return u_len; if (wow64) end = wow64_callback_data + wow64_callback_data_size; else end = callback_data + callback_data_size; while (c != end && c->id == cb_id) { if (c->u_callback_len == u_len) return c->w_callback_len; ++c; } ERR( "Unix len %d not found for callback %d.\n", u_len, cb_id ); return find_first_callback_def_by_id( cb_id, wow64 )->w_callback_len; } void queue_vtable_callback( struct w_iface *w_iface, enum callback_type type, uint64_t arg0, uint64_t arg1 ) { struct callback_entry *entry; uint32_t size = 0; size += sizeof(struct callback_entry); if (!(entry = (struct callback_entry *)calloc( 1, size ))) return; entry->callback.type = type; size -= offsetof( struct callback_entry, callback ); entry->callback.size = size; entry->callback.call_iface_vtable.iface = w_iface; entry->callback.call_iface_vtable.arg0 = arg0; entry->callback.call_iface_vtable.arg1 = arg1; pthread_mutex_lock( &callbacks_lock ); list_add_tail( &callbacks, &entry->entry ); pthread_mutex_unlock( &callbacks_lock ); } void queue_vtable_callback_0_server_responded( struct w_iface *w_iface, gameserveritem_t_105 *server ) { uint32_t size = sizeof(*server); struct callback_entry *entry; size += sizeof(struct callback_entry); if (!(entry = (struct callback_entry *)calloc( 1, size ))) return; entry->callback.type = CALL_IFACE_VTABLE_0_SERVER_RESPONDED; size -= offsetof( struct callback_entry, callback ); entry->callback.size = size; entry->callback.server_responded.iface = w_iface; entry->callback.server_responded.server[0] = *server; pthread_mutex_lock( &callbacks_lock ); list_add_tail( &callbacks, &entry->entry ); pthread_mutex_unlock( &callbacks_lock ); } void queue_vtable_callback_0_add_player_to_list( struct w_iface *w_iface, const char *pchName, int nScore, float flTimePlayed ) { uint32_t name_size = strlen( pchName ) + 1, size = name_size; struct callback_entry *entry; size += sizeof(struct callback_entry); if (!(entry = (struct callback_entry *)calloc( 1, size ))) return; entry->callback.type = CALL_IFACE_VTABLE_0_ADD_PLAYER_TO_LIST; size -= offsetof( struct callback_entry, callback ); entry->callback.size = size; entry->callback.add_player_to_list.iface = w_iface; entry->callback.add_player_to_list.score = nScore; entry->callback.add_player_to_list.time_played = flTimePlayed; memcpy( (char *)entry->callback.add_player_to_list.name, pchName, name_size ); pthread_mutex_lock( &callbacks_lock ); list_add_tail( &callbacks, &entry->entry ); pthread_mutex_unlock( &callbacks_lock ); } void queue_vtable_callback_0_rules_responded( struct w_iface *w_iface, const char *pchRule, const char *pchValue ) { uint32_t rule_size = strlen( pchRule ) + 1, value_size = strlen( pchValue ) + 1, size = rule_size + value_size; struct callback_entry *entry; size += sizeof(struct callback_entry); if (!(entry = (struct callback_entry *)calloc( 1, size ))) return; entry->callback.type = CALL_IFACE_VTABLE_0_RULES_RESPONDED; size -= offsetof( struct callback_entry, callback ); entry->callback.size = size; entry->callback.rules_responded.iface = w_iface; memcpy( (char *)entry->callback.rules_responded.rule_and_value, pchRule, rule_size ); memcpy( (char *)entry->callback.rules_responded.rule_and_value + rule_size, pchValue, value_size ); pthread_mutex_lock( &callbacks_lock ); list_add_tail( &callbacks, &entry->entry ); pthread_mutex_unlock( &callbacks_lock ); } static w_FSteamNetworkingSocketsDebugOutput w_steam_networking_socket_debug_output; static void u_steam_networking_socket_debug_output( uint32_t nType, const char *pszMsg ) { uint32_t msg_size = strlen( pszMsg ) + 1, size = msg_size; struct callback_entry *entry; size += sizeof(struct callback_entry); if (!(entry = (struct callback_entry *)calloc( 1, size ))) return; entry->callback.type = SOCKETS_DEBUG_OUTPUT; size -= offsetof( struct callback_entry, callback ); entry->callback.size = size; entry->callback.sockets_debug_output.pfnFunc = w_steam_networking_socket_debug_output; entry->callback.sockets_debug_output.type = nType; memcpy( (char *)entry->callback.sockets_debug_output.msg, pszMsg, msg_size ); pthread_mutex_lock( &callbacks_lock ); list_add_tail( &callbacks, &entry->entry ); pthread_mutex_unlock( &callbacks_lock ); } void queue_cdecl_func_callback( w_cdecl_func func, void *data, uint32_t data_size ) { uint32_t size = data_size; struct callback_entry *entry; size += sizeof(struct callback_entry); if (!(entry = (struct callback_entry *)calloc( 1, size ))) return; entry->callback.type = CALL_CDECL_FUNC_DATA; size -= offsetof( struct callback_entry, callback ); entry->callback.size = size; entry->callback.call_cdecl_func_data.pFunc = func; memcpy( (char *)entry->callback.call_cdecl_func_data.data, data, data_size ); pthread_mutex_lock( &callbacks_lock ); list_add_tail( &callbacks, &entry->entry ); pthread_mutex_unlock( &callbacks_lock ); } u_FSteamNetworkingSocketsDebugOutput manual_convert_SetDebugOutputFunction_pfnFunc( w_FSteamNetworkingSocketsDebugOutput w_func ) { w_steam_networking_socket_debug_output = w_func; return &u_steam_networking_socket_debug_output; } static w_SteamAPIWarningMessageHook_t w_steam_api_warning_message_hook; static void u_steam_api_warning_message_hook( int severity, const char *msg ) { uint32_t msg_size = strlen( msg ) + 1, size = msg_size; struct callback_entry *entry; size += sizeof(struct callback_entry); if (!(entry = (struct callback_entry *)calloc( 1, size ))) return; entry->callback.type = WARNING_MESSAGE_HOOK; size -= offsetof( struct callback_entry, callback ); entry->callback.size = size; entry->callback.warning_message_hook.pFunction = w_steam_api_warning_message_hook; entry->callback.warning_message_hook.severity = severity; memcpy( (char *)entry->callback.warning_message_hook.msg, msg, msg_size ); pthread_mutex_lock( &callbacks_lock ); list_add_tail( &callbacks, &entry->entry ); pthread_mutex_unlock( &callbacks_lock ); } u_SteamAPIWarningMessageHook_t manual_convert_SetWarningMessageHook_pFunction( w_SteamAPIWarningMessageHook_t w_func ) { w_steam_api_warning_message_hook = w_func; return &u_steam_api_warning_message_hook; } static uint32_t U_CDECL u_steam_api_check_callback_registered( int32_t v ) { return 1; } u_SteamAPI_CheckCallbackRegistered_t manual_convert_Set_SteamAPI_CCheckCallbackRegisteredInProcess_func( w_SteamAPI_CheckCallbackRegistered_t w_func ) { FIXME("not implemented!\n"); return &u_steam_api_check_callback_registered; } static uint32_t U_CDECL u_steam_api_check_callback_registered_156( int32_t v ) { return 1; } u_SteamAPI_CheckCallbackRegistered_t_156 manual_convert_Set_SteamAPI_CCheckCallbackRegisteredInProcess_func_156( w_SteamAPI_CheckCallbackRegistered_t_156 w_func ) { FIXME("not implemented!\n"); return &u_steam_api_check_callback_registered_156; } static void U_CDECL u_steam_api_post_api_result_in_process( uint64_t a, void *b, uint32_t c, int d ) { } u_SteamAPI_PostAPIResultInProcess_t manual_convert_Set_SteamAPI_CPostAPIResultInProcess_func( w_SteamAPI_PostAPIResultInProcess_t w_func ) { FIXME("not implemented!\n"); return &u_steam_api_post_api_result_in_process; } u_SteamAPI_PostAPIResultInProcess_t manual_convert_Remove_SteamAPI_CPostAPIResultInProcess_func( w_SteamAPI_PostAPIResultInProcess_t w_func ) { FIXME("not implemented!\n"); return &u_steam_api_post_api_result_in_process; } static void U_CDECL u_void_steam_api_post_api_result_in_process(void) { } u_void_SteamAPI_PostAPIResultInProcess_t manual_convert_DEPRECATED_Set_SteamAPI_CPostAPIResultInProcess__a( w_void_SteamAPI_PostAPIResultInProcess_t w_func ) { FIXME("not implemented!\n"); return &u_void_steam_api_post_api_result_in_process; } u_void_SteamAPI_PostAPIResultInProcess_t manual_convert_DEPRECATED_Remove_SteamAPI_CPostAPIResultInProcess__a( w_void_SteamAPI_PostAPIResultInProcess_t w_func ) { FIXME("not implemented!\n"); return &u_void_steam_api_post_api_result_in_process; } template< typename Iface, typename Params > static NTSTATUS ISteamClient_Set_SteamAPI_CCheckCallbackRegisteredInProcess( Iface *iface, Params *params, bool wow64 ) { uint32_t (*U_CDECL lin_func)(int32_t) = manual_convert_Set_SteamAPI_CCheckCallbackRegisteredInProcess_func_156( params->func ); iface->Set_SteamAPI_CCheckCallbackRegisteredInProcess( lin_func ); return 0; } LSTEAMCLIENT_UNIX_IMPL( ISteamClient, SteamClient020, Set_SteamAPI_CCheckCallbackRegisteredInProcess ); LSTEAMCLIENT_UNIX_IMPL( ISteamClient, SteamClient021, Set_SteamAPI_CCheckCallbackRegisteredInProcess ); template< typename Params > static NTSTATUS steamclient_next_callback( Params *params, bool wow64 ) { uint32_t capacity = params->size; struct list *ptr; pthread_mutex_lock( &callbacks_lock ); if ((ptr = list_head( &callbacks ))) { struct callback_entry *entry = LIST_ENTRY( ptr, struct callback_entry, entry ); params->size = entry->callback.size; if (params->size <= capacity) { memcpy( params->callback, &entry->callback, params->size ); list_remove( &entry->entry ); free( entry ); } } pthread_mutex_unlock( &callbacks_lock ); params->_ret = !!ptr; return 0; } static void *(*p_CreateInterface)( const char *name, int *return_code ); static int8_t (*p_Steam_BGetCallback)( int32_t a, u_CallbackMsg_t *b, int32_t *c ); static int8_t (*p_Steam_GetAPICallResult)( int32_t, uint64_t, void *, int, int, int8_t * ); static int8_t (*p_Steam_FreeLastCallback)( int32_t ); static void (*p_Steam_ReleaseThreadLocalMemory)( int ); static bool (*p_Steam_IsKnownInterface)( const char * ); static void (*p_Steam_NotifyMissingInterface)( int32_t, const char * ); template< typename Params > static NTSTATUS steamclient_Steam_BGetCallback( Params *params, bool wow64 ) { u_CallbackMsg_t *u_msg, u_msg_tmp; auto *w_msg = &*params->w_msg; if (!p_Steam_BGetCallback( params->pipe, &u_msg_tmp, params->ignored )) params->_ret = false; else { u_msg = new u_CallbackMsg_t(u_msg_tmp); TRACE( "id %d, u_size %d.\n", u_msg->m_iCallback, u_msg->m_cubParam ); w_msg->m_hSteamUser = u_msg->m_hSteamUser; w_msg->m_iCallback = u_msg->m_iCallback; w_msg->m_cubParam = callback_len_utow( u_msg->m_iCallback, u_msg->m_cubParam, false ); params->cookie = (UINT_PTR)u_msg; params->_ret = true; } return 0; } template< typename Params > static NTSTATUS steamclient_callback_message_receive( Params *params, bool wow64 ) { u_CallbackMsg_t *u_msg = (u_CallbackMsg_t *)(UINT_PTR)params->cookie; auto *w_msg = &*params->w_msg; if (!u_msg) return 0; convert_callback_utow( u_msg->m_iCallback, (void *)u_msg->m_pubParam, u_msg->m_cubParam, (void *)w_msg->m_pubParam, w_msg->m_cubParam, false ); if (w_msg->m_iCallback == 703 /* SteamAPICallCompleted_t::k_iCallback */) { SteamAPICallCompleted_t_137 *c = (SteamAPICallCompleted_t_137 *)u_msg->m_pubParam; if (sizeof(SteamAPICallCompleted_t_137) == w_msg->m_cubParam) { int len; len = callback_len_utow( c->m_iCallback, c->m_cubParam, false ); TRACE( "SteamAPICallCompleted_t id %d, size %d -> %d.\n", c->m_iCallback, c->m_cubParam, len ); c->m_cubParam = len; } else { WARN( "Unexpected SteamAPICallCompleted_t callback size %d, not doing API callback size conversion.", w_msg->m_cubParam ); } } delete u_msg; return 0; } template< typename Params > static NTSTATUS steamclient_Steam_FreeLastCallback( Params *params, bool wow64 ) { params->_ret = p_Steam_FreeLastCallback( params->pipe ); return 0; } template< typename Params > static NTSTATUS steamclient_Steam_GetAPICallResult( Params *params, bool wow64 ) { int u_callback_len = params->w_callback_len; void *u_callback; if (!(u_callback = alloc_callback_wtou( params->id, params->w_callback, &u_callback_len, false ))) return false; params->_ret = p_Steam_GetAPICallResult( params->pipe, params->call, u_callback, u_callback_len, params->id, params->failed ); if (params->_ret && u_callback != params->w_callback) { convert_callback_utow( params->id, u_callback, u_callback_len, params->w_callback, params->w_callback_len, false ); free( u_callback ); } return 0; } static UINT asciiz_to_unicode( WCHAR *dst, const char *src ) { WCHAR *p = dst; while ((*p++ = *src++)); return (p - dst) * sizeof(WCHAR); } static BOOL set_reg_value( HANDLE hkey, const WCHAR *name, UINT type, const void *value, DWORD count ) { unsigned short name_size = name ? lstrlenW( name ) * sizeof(WCHAR) : 0; UNICODE_STRING nameW = { name_size, name_size, (WCHAR *)name }; return !NtSetValueKey( hkey, &nameW, 0, type, value, count ); } static void set_reg_ascii_dword( HANDLE hkey, const char *name, DWORD value ) { WCHAR nameW[64], valueW[128]; asciiz_to_unicode( nameW, name ); set_reg_value( hkey, nameW, REG_DWORD, &value, sizeof(value) ); } static void set_reg_ascii_str( HANDLE hkey, const char *name, const char *value ) { size_t len = strlen( value ) + 1; WCHAR nameW[64], *valueW; if (!(valueW = (WCHAR *)malloc( len * sizeof(*valueW) ))) return; asciiz_to_unicode( valueW, value ); asciiz_to_unicode( nameW, name ); set_reg_value( hkey, nameW, REG_SZ, valueW, len * sizeof(*valueW) ); free( valueW ); } static void set_reg_ascii_wstr( HANDLE hkey, const char *name, const WCHAR *valueW ) { size_t len = lstrlenW( valueW ) + 1; WCHAR nameW[64]; asciiz_to_unicode( nameW, name ); set_reg_value( hkey, nameW, REG_SZ, valueW, len * sizeof(*valueW) ); } static inline void init_unicode_string( UNICODE_STRING *str, const WCHAR *data ) { str->Length = wcslen( data ) * sizeof(WCHAR); str->MaximumLength = str->Length + sizeof(WCHAR); str->Buffer = (WCHAR *)data; } static NTSTATUS open_hkcu_key( const char *path, HANDLE *key ) { NTSTATUS status; char buffer[256]; WCHAR bufferW[256]; DWORD_PTR sid_data[(sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE) / sizeof(DWORD_PTR)]; DWORD i, len = sizeof(sid_data); SID *sid; UNICODE_STRING name; OBJECT_ATTRIBUTES attr; status = NtQueryInformationToken( GetCurrentThreadEffectiveToken(), TokenUser, sid_data, len, &len ); if (status) return status; sid = (SID *)((TOKEN_USER *)sid_data)->User.Sid; len = snprintf( buffer, sizeof(buffer), "\\Registry\\User\\S-%u-%u", sid->Revision, (int)MAKELONG( MAKEWORD( sid->IdentifierAuthority.Value[5], sid->IdentifierAuthority.Value[4] ), MAKEWORD( sid->IdentifierAuthority.Value[3], sid->IdentifierAuthority.Value[2] ))); for (i = 0; i < sid->SubAuthorityCount; i++) len += snprintf( buffer + len, sizeof(buffer) - len, "-%u", (int)sid->SubAuthority[i] ); len += snprintf( buffer + len, sizeof(buffer) - len, "\\%s", path ); asciiz_to_unicode( bufferW, buffer ); init_unicode_string( &name, bufferW ); InitializeObjectAttributes( &attr, &name, OBJ_CASE_INSENSITIVE, 0, NULL ); return NtCreateKey( key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ); } /* requires steam API to be initialized */ static void setup_steam_registry( u_ISteamClient_SteamClient017 *client, int pipe, int user ) { static const int system_locale_appids[] = { 1284210 /* Guild Wars 2 */ }; const char *ui_lang, *language, *languages, *locale = NULL; u_ISteamApps_STEAMAPPS_INTERFACE_VERSION008 *apps; u_ISteamUtils_SteamUtils009 *utils; unsigned int appid, status; char buf[256]; HANDLE key; int i; utils = (u_ISteamUtils_SteamUtils009 *)client->GetISteamUtils( pipe, "SteamUtils009" ); ui_lang = utils->GetSteamUILanguage(); TRACE( "UI language: %s\n", debugstr_a(ui_lang) ); if ((status = open_hkcu_key( "Software\\Valve\\Steam", &key ))) ERR( "Could not create key, status %#x\n", status ); else { set_reg_ascii_str( key, "language", ui_lang ); NtClose( key ); } appid = utils->GetAppID(); TRACE( "appid: %u\n", appid ); if ((status = open_hkcu_key( "Software\\Valve\\Steam\\Apps", &key ))) ERR( "Could not create key, status %#x\n", status ); else NtClose( key ); sprintf( buf, "Software\\Valve\\Steam\\Apps\\%u", appid ); if ((status = open_hkcu_key( buf, &key ))) ERR( "Could not create key, status %#x\n", status ); else { DWORD value; value = 1; set_reg_ascii_dword( key, "Installed", value ); set_reg_ascii_dword( key, "Running", value ); value = 0; set_reg_ascii_dword( key, "Updating", value ); NtClose( key ); } apps = (u_ISteamApps_STEAMAPPS_INTERFACE_VERSION008 *)client->GetISteamApps( user, pipe, "STEAMAPPS_INTERFACE_VERSION008" ); language = apps->GetCurrentGameLanguage(); languages = apps->GetAvailableGameLanguages(); TRACE( "Game language %s, available %s\n", debugstr_a(language), debugstr_a(languages) ); if (!strchr( languages, ',' )) /* If there is a list of languages then respect that */ { for (i = 0; language && i < ARRAY_SIZE(system_locale_appids); i++) { if (system_locale_appids[i] == appid) { TRACE( "Not changing system locale for application %i\n", appid ); language = NULL; } } } if (!language) locale = NULL; else if (!strcmp( language, "arabic" )) locale = "ar_001.UTF-8"; else if (!strcmp( language, "bulgarian" )) locale = "bg_BG.UTF-8"; else if (!strcmp( language, "schinese" )) locale = "zh_CN.UTF-8"; else if (!strcmp( language, "tchinese" )) locale = "zh_TW.UTF-8"; else if (!strcmp( language, "czech" )) locale = "cs_CZ.UTF-8"; else if (!strcmp( language, "danish" )) locale = "da_DK.UTF-8"; else if (!strcmp( language, "dutch" )) locale = "nl_NL.UTF-8"; else if (!strcmp( language, "english" )) locale = "en_US.UTF-8"; else if (!strcmp( language, "finnish" )) locale = "fi_FI.UTF-8"; else if (!strcmp( language, "french" )) locale = "fr_FR.UTF-8"; else if (!strcmp( language, "german" )) locale = "de_DE.UTF-8"; else if (!strcmp( language, "greek" )) locale = "el_GR.UTF-8"; else if (!strcmp( language, "hungarian" )) locale = "hu_HU.UTF-8"; else if (!strcmp( language, "italian" )) locale = "it_IT.UTF-8"; else if (!strcmp( language, "japanese" )) locale = "ja_JP.UTF-8"; else if (!strcmp( language, "koreana" )) locale = "ko_KR.UTF-8"; else if (!strcmp( language, "norwegian" )) locale = "nb_NO.UTF-8"; else if (!strcmp( language, "polish" )) locale = "pl_PL.UTF-8"; else if (!strcmp( language, "portuguese" )) locale = "pt_PT.UTF-8"; else if (!strcmp( language, "brazilian" )) locale = "pt_BR.UTF-8"; else if (!strcmp( language, "romanian" )) locale = "ro_RO.UTF-8"; else if (!strcmp( language, "russian" )) locale = "ru_RU.UTF-8"; else if (!strcmp( language, "spanish" )) locale = "es_ES.UTF-8"; else if (!strcmp( language, "latam" )) locale = "es_419.UTF-8"; else if (!strcmp( language, "swedish" )) locale = "sv_SE.UTF-8"; else if (!strcmp( language, "thai" )) locale = "th_TH.UTF-8"; else if (!strcmp( language, "turkish" )) locale = "tr_TR.UTF-8"; else if (!strcmp( language, "ukrainian" )) locale = "uk_UA.UTF-8"; else if (!strcmp( language, "vietnamese" )) locale = "vi_VN.UTF-8"; else FIXME( "Unsupported game language %s\n", debugstr_a(language) ); /* HACK: Bug 23597 Granado Espada Japan (1219160) launcher needs Japanese locale to display correctly */ if (appid == 1219160) locale = "ja_JP.UTF-8"; if (locale) { FIXME( "Game language %s, defaulting LC_CTYPE / LC_MESSAGES to %s.\n", debugstr_a(language), locale ); setenv( "LC_CTYPE", locale, FALSE ); setenv( "LC_MESSAGES", locale, FALSE ); } } /* requires steam API to be initialized */ static void setup_battleye_bridge( u_ISteamClient_SteamClient017 *client, int pipe, int user ) { u_ISteamApps_STEAMAPPS_INTERFACE_VERSION008 *apps; const unsigned int be_runtime_appid = 1161040; char path[2048]; int error; apps = (u_ISteamApps_STEAMAPPS_INTERFACE_VERSION008 *)client->GetISteamApps( user, pipe, "STEAMAPPS_INTERFACE_VERSION008" ); if (!apps->BIsAppInstalled( be_runtime_appid )) return; if (!apps->GetAppInstallDir( be_runtime_appid, path, sizeof(path) )) return; TRACE( "Found battleye runtime at %s\n", path ); setenv( "PROTON_BATTLEYE_RUNTIME", path, 1 ); } static void setup_eac_bridge( u_ISteamClient_SteamClient017 *client, int pipe, int user ) { u_ISteamApps_STEAMAPPS_INTERFACE_VERSION008 *apps; const unsigned int eac_runtime_appid = 1826330; char path[2048]; int error; apps = (u_ISteamApps_STEAMAPPS_INTERFACE_VERSION008 *)client->GetISteamApps( user, pipe, "STEAMAPPS_INTERFACE_VERSION008" ); if (!apps->BIsAppInstalled( eac_runtime_appid )) return; if (!apps->GetAppInstallDir( eac_runtime_appid, path, sizeof(path) )) return; TRACE( "Found easyanticheat runtime at %s\n", path ); setenv( "PROTON_EAC_RUNTIME", path, 1 ); } static void setup_proton_voice_files( u_ISteamClient_SteamClient017 *client, int pipe, int user ) { u_ISteamApps_STEAMAPPS_INTERFACE_VERSION008 *apps; const unsigned int eac_runtime_appid = 3086180; char path[2048]; int error; apps = (u_ISteamApps_STEAMAPPS_INTERFACE_VERSION008 *)client->GetISteamApps( user, pipe, "STEAMAPPS_INTERFACE_VERSION008" ); if (!apps->BIsAppInstalled( eac_runtime_appid )) return; if (!apps->GetAppInstallDir( eac_runtime_appid, path, sizeof(path) )) return; TRACE( "Found proton voice files at %s\n", path ); setenv( "PROTON_VOICE_FILES", path, 1 ); } static void setup_proton_soundfonts( u_ISteamClient_SteamClient017 *client, int pipe, int user ) { static const WCHAR PROTON_SOUNDFILES_FILES_W[] = u"PROTON_SOUNDFONT_FILES"; static const WCHAR SF2_NAME[] = u"FluidR3_GM.sf2"; u_ISteamApps_STEAMAPPS_INTERFACE_VERSION008 *apps; const unsigned int soundfonts_appid = 3368180; std::vector dos_path{}; OBJECT_ATTRIBUTES attr; ULONG nt_path_len = 0; char unix_path[2048]; UNICODE_STRING name; NTSTATUS status; char *path_end; HANDLE gm_key; apps = (u_ISteamApps_STEAMAPPS_INTERFACE_VERSION008 *)client->GetISteamApps( user, pipe, "STEAMAPPS_INTERFACE_VERSION008" ); if (!apps->BIsAppInstalled( soundfonts_appid )) return; if (!apps->GetAppInstallDir( soundfonts_appid, unix_path, sizeof(unix_path) )) return; WINE_TRACE( "Found Proton Soundfont at %s\n", unix_path ); if ((status = wine_unix_to_nt_file_name( unix_path, NULL, &nt_path_len )) != STATUS_BUFFER_TOO_SMALL) { WINE_ERR( "Failed to convert unix path to NT: %lx\n", status ); return; } nt_path_len += ARRAYSIZE(SF2_NAME); dos_path = std::vector(nt_path_len); if ((status = wine_unix_to_nt_file_name( unix_path, dos_path.data(), &nt_path_len ))) { WINE_ERR( "Failed to convert unix path to NT: %x\n", status ); return; } dos_path[nt_path_len - 1] = u'\\'; wcscpy( dos_path.data() + nt_path_len, SF2_NAME ); WINE_TRACE( "GM file path %s\n", wine_dbgstr_w(dos_path.data()) ); init_unicode_string( &name, u"\\Registry\\Machine\\Software\\Microsoft\\DirectMusic" ); InitializeObjectAttributes( &attr, &name, OBJ_CASE_INSENSITIVE, 0, NULL ); if ((status = NtCreateKey( &gm_key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ))) { WINE_ERR( "Failed to open DirectMusic key: %x\n", status ); return; } set_reg_ascii_wstr( gm_key, "GMFilePath", dos_path.data() ); NtClose( gm_key ); init_unicode_string( &name, u"\\Registry\\Machine\\Software\\Wow6432Node\\Microsoft\\DirectMusic" ); if ((status = NtCreateKey( &gm_key, KEY_ALL_ACCESS | KEY_WOW64_32KEY, &attr, 0, NULL, 0, NULL))) { WINE_ERR( "Failed to open DirectMusic key (32 bit): %x\n", status); return; } set_reg_ascii_wstr( gm_key, "GMFilePath", dos_path.data() ); NtClose( gm_key ); } template< typename Params > static NTSTATUS steamclient_init_registry( Params *params, bool wow64 ) { u_ISteamClient_SteamClient017 *client; int pipe, user, error; client = (u_ISteamClient_SteamClient017 *)p_CreateInterface( "SteamClient017", &error ); if (!(pipe = client->CreateSteamPipe()) || !(user = client->ConnectToGlobalUser( pipe ))) { ERR( "Failed to connect to Steam\n" ); if (pipe) client->BReleaseSteamPipe( pipe ); return -1; } setup_steam_registry( client, pipe, user ); setup_battleye_bridge( client, pipe, user ); setup_eac_bridge( client, pipe, user ); setup_proton_voice_files( client, pipe, user ); setup_proton_soundfonts( client, pipe, user ); client->BReleaseSteamPipe( pipe ); return 0; } template< typename Params > static NTSTATUS steamclient_init( Params *params, bool wow64 ) { char path[PATH_MAX], resolved_path[PATH_MAX]; static void *steamclient; g_tmppath = params->g_tmppath; if (params->steam_app_id_unset) unsetenv( "SteamAppId" ); else if (params->steam_app_id) setenv( "SteamAppId", params->steam_app_id, TRUE ); if (params->ignore_child_processes_unset) unsetenv( "IgnoreChildProcesses" ); else if (params->ignore_child_processes) setenv( "IgnoreChildProcesses", params->ignore_child_processes, TRUE ); if (steamclient) return 0; #ifdef __APPLE__ if (getenv( "STEAM_COMPAT_CLIENT_INSTALL_PATH" )) snprintf( path, PATH_MAX, "%s/steamclient.dylib", getenv( "STEAM_COMPAT_CLIENT_INSTALL_PATH" ) ); else { WARN( "Old Steam client, falling back to DYLD environment to locate native steamclient " "library\n" ); strcpy( path, "steamclient.dylib" ); } #else /* __APPLE__ */ #if defined(__x86_64__) #define STEAM_ARCH "64" #elif defined(__i386__) #define STEAM_ARCH "32" #elif defined(__aarch64__) #define STEAM_ARCH "arm64" #else #error Unknown target architecture #endif snprintf( path, PATH_MAX, "%s/.steam/sdk" STEAM_ARCH "/steamclient.so", getenv( "HOME" ) ); #undef STEAM_ARCH if (realpath( path, resolved_path )) { strcpy( path, resolved_path ); path[PATH_MAX - 1] = 0; } #endif /* __APPLE__ */ if (!(steamclient = dlopen( path, RTLD_NOW ))) { ERR( "unable to load native steamclient library\n" ); return -1; } #define LOAD_FUNC( x ) \ if (!(p_##x = (decltype(p_##x))dlsym( steamclient, #x ))) \ { \ ERR( "unable to load " #x "\n" ); \ return -1; \ } LOAD_FUNC( CreateInterface ); LOAD_FUNC( Steam_BGetCallback ); LOAD_FUNC( Steam_GetAPICallResult ); LOAD_FUNC( Steam_FreeLastCallback ); LOAD_FUNC( Steam_ReleaseThreadLocalMemory ); LOAD_FUNC( Steam_IsKnownInterface ); LOAD_FUNC( Steam_NotifyMissingInterface ); TRACE( "Loaded host steamclient from %s\n", debugstr_a(path) ); return 0; } namespace std { template<> struct hash< struct u_buffer > { using argument_type = struct u_buffer; using result_type = std::size_t; result_type operator()( argument_type const& buf ) const { return buf.ptr; } }; } static pthread_mutex_t buffer_cache_lock = PTHREAD_MUTEX_INITIALIZER; static std::unordered_map< struct u_buffer, void * > buffer_cache; template< typename Params > static NTSTATUS steamclient_get_unix_buffer( Params *params, bool wow64 ) { struct cache_entry *entry; struct rb_entry *ptr; pthread_mutex_lock( &buffer_cache_lock ); auto iter = buffer_cache.find( params->buf ); if (iter != buffer_cache.end()) params->ptr = iter->second; else { memcpy( params->ptr, (char *)params->buf, params->buf.len ); buffer_cache[params->buf] = params->ptr; } pthread_mutex_unlock( &buffer_cache_lock ); return 0; } template< typename Params > static NTSTATUS steamclient_CreateInterface( Params *params, bool wow64 ) { params->_ret = p_CreateInterface( params->name, params->return_code ); return 0; } template< typename Params > static NTSTATUS steamclient_Steam_ReleaseThreadLocalMemory( Params *params, bool wow64 ) { p_Steam_ReleaseThreadLocalMemory( params->thread_exit ); return 0; } template< typename Params > static NTSTATUS steamclient_Steam_IsKnownInterface( Params *params, bool wow64 ) { params->_ret = p_Steam_IsKnownInterface( params->version ); return 0; } template< typename Params > static NTSTATUS steamclient_Steam_NotifyMissingInterface( Params *params, bool wow64 ) { p_Steam_NotifyMissingInterface( params->pipe, params->version ); return 0; } #define IS_ABSOLUTE( x ) (*x == '/' || *x == '\\' || (*x && *(x + 1) == ':')) static void collapse_path( WCHAR *path, UINT mark ) { WCHAR *p, *next; /* convert every / into a \ */ for (p = path; *p; p++) if (*p == '/') *p = '\\'; /* collapse duplicate backslashes */ next = path + std::max( 1u, mark ); for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p; *next = 0; p = path + mark; while (*p) { if (*p == '.') { switch(p[1]) { case '\\': /* .\ component */ next = p + 2; memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) ); continue; case 0: /* final . */ if (p > path + mark) p--; *p = 0; continue; case '.': if (p[2] == '\\') /* ..\ component */ { next = p + 3; if (p > path + mark) { p--; while (p > path + mark && p[-1] != '\\') p--; } memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) ); continue; } else if (!p[2]) /* final .. */ { if (p > path + mark) { p--; while (p > path + mark && p[-1] != '\\') p--; if (p > path + mark) p--; } *p = 0; continue; } break; } } /* skip to the next component */ while (*p && *p != '\\') p++; if (*p == '\\') { /* remove last dot in previous dir name */ if (p > path + mark && p[-1] == '.') memmove( p-1, p, (wcslen(p) + 1) * sizeof(WCHAR) ); else p++; } } /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */ while (p > path + mark && (p[-1] == ' ' || p[-1] == '.')) p--; *p = 0; } static char *get_unix_file_name( const WCHAR *path ) { UNICODE_STRING nt_name; OBJECT_ATTRIBUTES attr; NTSTATUS status; ULONG size = 256; char *buffer; nt_name.Buffer = (WCHAR *)path; nt_name.MaximumLength = nt_name.Length = lstrlenW( path ) * sizeof(WCHAR); InitializeObjectAttributes( &attr, &nt_name, 0, 0, NULL ); for (;;) { if (!(buffer = (char *)malloc( size ))) return NULL; status = wine_nt_to_unix_file_name( &attr, buffer, &size, FILE_OPEN_IF ); if (status != STATUS_BUFFER_TOO_SMALL) break; free( buffer ); } if (status && status != STATUS_NO_SUCH_FILE) { free( buffer ); return NULL; } return buffer; } char *steamclient_dos_to_unix_path( const char *src, int is_url ) { static const char file_prot[] = "file://"; char buffer[4096], *dst = buffer; uint32_t len; TRACE( "src %s, is_url %u\n", debugstr_a(src), is_url ); if (!src) return NULL; *dst = 0; if (!*src) goto done; if (is_url) { if (strncmp( src, file_prot, 7 ) != 0) { strcpy( dst, src ); goto done; } src += 7; memcpy( dst, file_prot, sizeof(file_prot) ); dst += 7; } if (IS_ABSOLUTE( src )) { /* absolute path, use wine conversion */ WCHAR srcW[PATH_MAX] = {'\\', '?', '?', '\\', 0}; char *unix_path; uint32_t r; if (is_url) while (*src == '/') ++src; r = ntdll_umbstowcs( src, strlen( src ) + 1, srcW + 4, PATH_MAX - 4 ); if (r == 0) unix_path = NULL; else { collapse_path( srcW, 4 ); unix_path = get_unix_file_name( srcW ); } if (!unix_path) { WARN( "Unable to convert DOS filename to unix: %s\n", src ); goto done; } lstrcpynA( dst, unix_path, PATH_MAX ); free( unix_path ); } else { /* relative path, just fix up backslashes */ const char *s; char *d; for (s = src, d = dst; *s; ++s, ++d) { if (*s == '\\') *d = '/'; else *d = *s; } *d = 0; } done: len = strlen( buffer ) + 1; if (!(dst = (char *)malloc( len ))) return NULL; memcpy( dst, buffer, len ); TRACE( "-> %s\n", debugstr_a(dst) ); return dst; } void steamclient_free_path( char *path ) { free( path ); } const char **steamclient_dos_to_unix_path_array( const char **src ) { size_t len; const char **s; char **out, **o; WCHAR scratch[PATH_MAX] = {'\\', '?', '?', '\\', 0}; TRACE( "src %p\n", src ); if (!src) return NULL; len = sizeof(char *); /* NUL */ for (s = src; *s; ++s) len += sizeof(char *); out = (char **)malloc( len ); for (s = src, o = out; *s; ++s, ++o) { TRACE( " src[%zu] %s\n", s - src, debugstr_a(*s) ); if (IS_ABSOLUTE( *s )) { ntdll_umbstowcs( *s, strlen( *s ) + 1, scratch + 4, PATH_MAX - 4 ); collapse_path( scratch, 4 ); *o = get_unix_file_name( scratch ); } else { const char *r; char *l; *o = (char *)malloc( strlen( *s ) + 1 ); for (r = *s, l = *o; *r; ++l, ++r) { if (*r == '\\') *l = '/'; else *l = *r; } *l = 0; } TRACE( " -> %s\n", debugstr_a(*o) ); } *o = NULL; TRACE( " -> %p\n", out ); return (const char **)out; } const char **wow64_steamclient_dos_to_unix_path_array( ptr32< const char ** > src_array ) { ptr32< const char * > *ptr = src_array, *end = src_array; const char **array, **ret; while (*end) end++; array = new const char *[end - ptr + 1]; for (end = ptr; *end; end++) array[end - ptr] = *end; ret = steamclient_dos_to_unix_path_array( array ); delete array; return ret; } void steamclient_free_path_array( const char **path_array ) { const char **path; if (!path_array) return; for (path = path_array; *path; path++) free( *(char **)path ); free( path_array ); } /* Returns: * - if successful, the number of bytes written to dst, including the NULL terminator; * - 0 if failed; * - PATH_MAX if insufficient output buffer (TODO: should be actual required length including NULL terminator). */ unsigned int steamclient_unix_path_to_dos_path( bool api_result, const char *src, char *dst, uint32_t dst_bytes, int is_url ) { static const char file_prot[] = "file://"; NTSTATUS status; ULONG size = 0; uint32_t r = 0; WCHAR *dosW; TRACE( "api_result %u, src %s, dst %p, dst_bytes %u is_url %u\n", api_result, debugstr_a(src), dst, dst_bytes, is_url ); if (!src || !*src || !api_result || !dst || !dst_bytes) { if (dst && dst_bytes) *dst = 0; return api_result ? PATH_MAX : 0; } if (is_url) { /* convert only file: URLs */ if (strncmp( src, file_prot, 7 )) { r = strlen( src ) + 1; if (r > dst_bytes) *dst = 0; else memmove( dst, src, r ); return r; } if (dst_bytes < sizeof(file_prot)) { *dst = 0; return PATH_MAX; } memmove( dst, src, 7 ); src += 7; dst += 7; dst_bytes -= 7; } status = wine_unix_to_nt_file_name( src, NULL, &size ); if (status != STATUS_BUFFER_TOO_SMALL) { WARN( "Unable to convert unix filename to DOS: %s.\n", debugstr_a(src) ); *dst = 0; return 0; } dosW = (WCHAR *)malloc( size * sizeof(WCHAR) ); status = wine_unix_to_nt_file_name( src, dosW, &size ); if (!status) r = ntdll_wcstoumbs( dosW, size, dst, dst_bytes, FALSE ); else *dst = 0; free( dosW ); if (!strncmp( dst, "\\??\\", 4 )) { memmove( dst, dst + 4, r - 4 ); r -= 4; } TRACE( "-> dst %s, r %u\n", debugstr_a(dst), r ); return r; } static const struct callback_def *find_first_callback_def_by_id( int id, bool wow64 ) { const struct callback_def *data; unsigned int l, r, m; if (wow64) r = wow64_callback_data_size; else r = callback_data_size; if (wow64) data = wow64_callback_data; else data = callback_data; l = 0; while (l < r) { m = (l + r) /2; if (data[m].id == id) { while (m && data[m - 1].id == id) --m; return &data[m]; } if (id < data[m].id) r = m; else l = m + 1; } return NULL; } void *alloc_callback_wtou( int id, void *callback, int *callback_len, bool wow64 ) { const struct callback_def *c, *end, *best; if (!(c = find_first_callback_def_by_id( id, wow64 ))) return callback; if (wow64) end = wow64_callback_data + wow64_callback_data_size; else end = callback_data + callback_data_size; best = NULL; while (c != end && c->id == id) { if (c->w_callback_len == *callback_len) { best = c; break; } if (!best && *callback_len >= c->w_callback_len) best = c; ++c; } if (!best) { ERR( "len %d is too small for callback %d, using default.\n", *callback_len, id ); best = find_first_callback_def_by_id( id, wow64 ); } if (best->w_callback_len != *callback_len) WARN( "Found len %d for id %d, len %d.\n", best->w_callback_len, id, *callback_len ); *callback_len = best->u_callback_len; return malloc( *callback_len ); } void convert_callback_utow( int id, void *u_callback, int u_callback_len, void *w_callback, int w_callback_len, bool wow64 ) { const struct callback_def *c, *end, *best; if (!(c = find_first_callback_def_by_id( id, wow64 ))) { memcpy( w_callback, u_callback, u_callback_len ); return; } if (wow64) end = wow64_callback_data + wow64_callback_data_size; else end = callback_data + callback_data_size; best = NULL; while (c != end && c->id == id) { if (c->w_callback_len == w_callback_len && c->u_callback_len == u_callback_len) { best = c; break; } if ((!best || best->w_callback_len > w_callback_len) && c->u_callback_len == u_callback_len && c->w_callback_len <= w_callback_len) best = c; if (!best && c->u_callback_len == u_callback_len) best = c; ++c; } if (!best) { ERR( "Could not find id %d, u_callback_len %d, w_callback_len %d.\n", id, u_callback_len, w_callback_len ); best = find_first_callback_def_by_id( id, wow64 ); } if (best->w_callback_len != w_callback_len || best->u_callback_len != u_callback_len) WARN( "Found len %d, %d for id %d, len %d, %d.\n", best->w_callback_len, best->u_callback_len, id, w_callback_len, u_callback_len ); if (best->conv_w_from_u) best->conv_w_from_u( w_callback, u_callback ); else memcpy( w_callback, u_callback, u_callback_len ); } #if defined(__x86_64__) || defined(__aarch64__) #define STEAMCLIENT_UNIX_WOW64_FUNC( name ) \ NTSTATUS wow64_ ## name( void *args ) { return name( (struct wow64_ ## name ## _params *)args, true ); } #else #define STEAMCLIENT_UNIX_WOW64_FUNC( name ) #endif #define STEAMCLIENT_UNIX_FUNC( name ) \ NTSTATUS name( void *args ) { return name( (struct name ## _params *)args, false ); } \ STEAMCLIENT_UNIX_WOW64_FUNC( name ) STEAMCLIENT_UNIX_FUNC( steamclient_init ) STEAMCLIENT_UNIX_FUNC( steamclient_init_registry ) STEAMCLIENT_UNIX_FUNC( steamclient_next_callback ) STEAMCLIENT_UNIX_FUNC( steamclient_get_unix_buffer ) STEAMCLIENT_UNIX_FUNC( steamclient_CreateInterface ) STEAMCLIENT_UNIX_FUNC( steamclient_Steam_GetAPICallResult ) STEAMCLIENT_UNIX_FUNC( steamclient_Steam_BGetCallback ) STEAMCLIENT_UNIX_FUNC( steamclient_callback_message_receive ) STEAMCLIENT_UNIX_FUNC( steamclient_Steam_FreeLastCallback ) STEAMCLIENT_UNIX_FUNC( steamclient_Steam_ReleaseThreadLocalMemory ) STEAMCLIENT_UNIX_FUNC( steamclient_Steam_IsKnownInterface ) STEAMCLIENT_UNIX_FUNC( steamclient_Steam_NotifyMissingInterface ) #if defined(__x86_64__) || defined(__aarch64__) struct buf32 { uint32_t pos32; char *pos; buf32() : pos(g_tmppath), pos32( (uint32_t)(UINT_PTR)g_tmppath ) {} template< typename T > void append_str( ptr32< T* >& dst, const char *src ) { size_t len = strlen( src ) + 1; if (g_tmppath + TEMP_PATH_BUFFER_LENGTH - pos < len) return; memcpy( pos, src, len ); pos += len; dst.value = pos32; pos32 += len; } void append_path( ptr32< const char * >& dst, const char *src ) { size_t len; steamclient_unix_path_to_dos_path( 1, src, pos, g_tmppath + TEMP_PATH_BUFFER_LENGTH - pos, 1 ); len = strlen( pos ) + 1; pos += len; dst.value = pos32; pos32 += len; } }; u64_CallbackMsg_t::operator w32_CallbackMsg_t() const { w32_CallbackMsg_t ret; ret.m_hSteamUser = this->m_hSteamUser; ret.m_iCallback = this->m_iCallback; /*ret.m_pubParam = this->m_pubParam;*/ ret.m_cubParam = this->m_cubParam; return ret; } u64_HTML_ChangedTitle_t::operator w32_HTML_ChangedTitle_t() const { w32_HTML_ChangedTitle_t ret; struct buf32 buf; ret.unBrowserHandle = this->unBrowserHandle; buf.append_str( ret.pchTitle, this->pchTitle ); return ret; } u64_HTML_ComboNeedsPaint_t::operator w32_HTML_ComboNeedsPaint_t() const { w32_HTML_ComboNeedsPaint_t ret; struct buf32 buf; ret.unBrowserHandle = this->unBrowserHandle; buf.append_str( ret.pBGRA, this->pBGRA ); ret.unWide = this->unWide; ret.unTall = this->unTall; return ret; } u64_HTML_FileOpenDialog_t::operator w32_HTML_FileOpenDialog_t() const { w32_HTML_FileOpenDialog_t ret; struct buf32 buf; ret.unBrowserHandle = this->unBrowserHandle; buf.append_str( ret.pchTitle, this->pchTitle ); buf.append_path( ret.pchInitialFile, this->pchInitialFile ); return ret; } u64_HTML_FinishedRequest_t::operator w32_HTML_FinishedRequest_t() const { w32_HTML_FinishedRequest_t ret; struct buf32 buf; ret.unBrowserHandle = this->unBrowserHandle; buf.append_path( ret.pchURL, this->pchURL ); buf.append_str( ret.pchPageTitle, this->pchPageTitle ); return ret; } u64_HTML_JSAlert_t::operator w32_HTML_JSAlert_t() const { w32_HTML_JSAlert_t ret; struct buf32 buf; ret.unBrowserHandle = this->unBrowserHandle; buf.append_str( ret.pchMessage, this->pchMessage ); return ret; } u64_HTML_JSConfirm_t::operator w32_HTML_JSConfirm_t() const { w32_HTML_JSConfirm_t ret; struct buf32 buf; ret.unBrowserHandle = this->unBrowserHandle; buf.append_str( ret.pchMessage, this->pchMessage ); return ret; } u64_HTML_LinkAtPosition_t::operator w32_HTML_LinkAtPosition_t() const { w32_HTML_LinkAtPosition_t ret; struct buf32 buf; ret.unBrowserHandle = this->unBrowserHandle; ret.x = this->x; ret.y = this->y; buf.append_path( ret.pchURL, this->pchURL ); ret.bInput = this->bInput; ret.bLiveLink = this->bLiveLink; return ret; } u64_HTML_NeedsPaint_t::operator w32_HTML_NeedsPaint_t() const { w32_HTML_NeedsPaint_t ret; struct buf32 buf; ret.unBrowserHandle = this->unBrowserHandle; buf.append_str( ret.pBGRA, this->pBGRA ); ret.unWide = this->unWide; ret.unTall = this->unTall; ret.unUpdateX = this->unUpdateX; ret.unUpdateY = this->unUpdateY; ret.unUpdateWide = this->unUpdateWide; ret.unUpdateTall = this->unUpdateTall; ret.unScrollX = this->unScrollX; ret.unScrollY = this->unScrollY; ret.flPageScale = this->flPageScale; ret.unPageSerial = this->unPageSerial; return ret; } u64_HTML_NewWindow_t_132x::operator w32_HTML_NewWindow_t_132x() const { w32_HTML_NewWindow_t_132x ret; struct buf32 buf; ret.unBrowserHandle = this->unBrowserHandle; buf.append_path( ret.pchURL, this->pchURL ); ret.unX = this->unX; ret.unY = this->unY; ret.unWide = this->unWide; ret.unTall = this->unTall; ret.unNewWindow_BrowserHandle_IGNORE = this->unNewWindow_BrowserHandle_IGNORE; return ret; } u64_HTML_NewWindow_t_130x::operator w32_HTML_NewWindow_t_130x() const { w32_HTML_NewWindow_t_130x ret; struct buf32 buf; ret.unBrowserHandle = this->unBrowserHandle; buf.append_path( ret.pchURL, this->pchURL ); ret.unX = this->unX; ret.unY = this->unY; ret.unWide = this->unWide; ret.unTall = this->unTall; return ret; } u64_HTML_OpenLinkInNewTab_t::operator w32_HTML_OpenLinkInNewTab_t() const { w32_HTML_OpenLinkInNewTab_t ret; struct buf32 buf; ret.unBrowserHandle = this->unBrowserHandle; buf.append_path( ret.pchURL, this->pchURL ); return ret; } u64_HTML_ShowToolTip_t::operator w32_HTML_ShowToolTip_t() const { w32_HTML_ShowToolTip_t ret; struct buf32 buf; ret.unBrowserHandle = this->unBrowserHandle; buf.append_str( ret.pchMsg, this->pchMsg ); return ret; } u64_HTML_StartRequest_t::operator w32_HTML_StartRequest_t() const { w32_HTML_StartRequest_t ret; struct buf32 buf; ret.unBrowserHandle = this->unBrowserHandle; buf.append_path( ret.pchURL, this->pchURL ); buf.append_str( ret.pchTarget, this->pchTarget ); buf.append_str( ret.pchPostData, this->pchPostData ); ret.bIsRedirect = this->bIsRedirect; return ret; } u64_HTML_StatusText_t::operator w32_HTML_StatusText_t() const { w32_HTML_StatusText_t ret; struct buf32 buf; ret.unBrowserHandle = this->unBrowserHandle; buf.append_str( ret.pchMsg, this->pchMsg ); return ret; } u64_HTML_URLChanged_t::operator w32_HTML_URLChanged_t() const { w32_HTML_URLChanged_t ret; struct buf32 buf; ret.unBrowserHandle = this->unBrowserHandle; buf.append_path( ret.pchURL, this->pchURL ); buf.append_str( ret.pchPostData, this->pchPostData ); ret.bIsRedirect = this->bIsRedirect; buf.append_str( ret.pchPageTitle, this->pchPageTitle ); ret.bNewNavigation = this->bNewNavigation; return ret; } u64_HTML_UpdateToolTip_t::operator w32_HTML_UpdateToolTip_t() const { w32_HTML_UpdateToolTip_t ret; struct buf32 buf; ret.unBrowserHandle = this->unBrowserHandle; buf.append_str( ret.pchMsg, this->pchMsg ); return ret; } u64_RemoteStorageDownloadUGCResult_t_123::operator w32_RemoteStorageDownloadUGCResult_t_123() const { w32_RemoteStorageDownloadUGCResult_t_123 ret; ret.m_eResult = this->m_eResult; ret.m_hFile = this->m_hFile; ret.m_nAppID = this->m_nAppID; ret.m_nSizeInBytes = this->m_nSizeInBytes; ret.m_pchFileName = this->m_pchFileName; ret.m_ulSteamIDOwner = this->m_ulSteamIDOwner; return ret; } u64_RemoteStorageDownloadUGCResult_t_116x::operator w32_RemoteStorageDownloadUGCResult_t_116x() const { w32_RemoteStorageDownloadUGCResult_t_116x ret; ret.m_eResult = this->m_eResult; ret.m_hFile = this->m_hFile; ret.m_nAppID = this->m_nAppID; ret.m_nSizeInBytes = this->m_nSizeInBytes; ret.m_pchFileName = this->m_pchFileName; ret.m_ulSteamIDOwner = this->m_ulSteamIDOwner; return ret; } u64_RemoteStorageDownloadUGCResult_t_111x::operator w32_RemoteStorageDownloadUGCResult_t_111x() const { w32_RemoteStorageDownloadUGCResult_t_111x ret; struct buf32 buf; ret.m_eResult = this->m_eResult; ret.m_hFile = this->m_hFile; ret.m_nAppID = this->m_nAppID; ret.m_nSizeInBytes = this->m_nSizeInBytes; buf.append_str( ret.m_pchFileName, this->m_pchFileName ); ret.m_ulSteamIDOwner = this->m_ulSteamIDOwner; return ret; } w64_SteamParamStringArray_t::w64_SteamParamStringArray_t( w32_SteamParamStringArray_t const& w32 ) : m_nNumStrings(w32.m_nNumStrings) { uint32_t count = m_nNumStrings; m_ppStrings = new const char *[count]; while (count--) m_ppStrings[count] = w32.m_ppStrings[count]; } w64_SteamParamStringArray_t::~w64_SteamParamStringArray_t() { delete[] m_ppStrings; } #endif