/* * Copyright (c) 2019, Valve Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* This is a stub steam.exe implementation for use inside Proton. It provides * a small subset of the actual Steam functionality for games that expect * Windows version of Steam running. */ #include #pragma push_macro("_WIN32") #pragma push_macro("__cdecl") #undef _WIN32 #undef __cdecl #include "steam_api.h" #pragma pop_macro("_WIN32") #pragma pop_macro("__cdecl") #include "wine/unicode.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(steam); EXTERN_C HANDLE CDECL __wine_make_process_system(void); static void set_active_process_pid(void) { DWORD pid = GetCurrentProcessId(); RegSetKeyValueA(HKEY_CURRENT_USER, "Software\\Valve\\Steam\\ActiveProcess", "pid", REG_DWORD, &pid, sizeof(pid)); } static DWORD WINAPI create_steam_window(void *arg) { static WNDCLASSEXW wndclass = { sizeof(WNDCLASSEXW) }; static const WCHAR class_nameW[] = {'v','g','u','i','P','o','p','u','p','W','i','n','d','o','w',0}; static const WCHAR steamW[] = {'S','t','e','a','m',0}; MSG msg; wndclass.lpfnWndProc = DefWindowProcW; wndclass.lpszClassName = class_nameW; RegisterClassExW(&wndclass); CreateWindowW(class_nameW, steamW, WS_POPUP, 40, 40, 400, 300, NULL, NULL, NULL, NULL); while (GetMessageW(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } static void setup_steam_registry(void) { const char *ui_lang; uint32 appid; char buf[256]; HKEY key; LSTATUS status; if (!SteamAPI_Init()) { WINE_ERR("SteamAPI_Init failed\n"); return; } ui_lang = SteamUtils()->GetSteamUILanguage(); WINE_TRACE("UI language: %s\n", wine_dbgstr_a(ui_lang)); RegSetKeyValueA(HKEY_CURRENT_USER, "Software\\Valve\\Steam", "language", REG_SZ, ui_lang, strlen(ui_lang) + 1); appid = SteamUtils()->GetAppID(); WINE_TRACE("appid: %u\n", appid); sprintf(buf, "Software\\Valve\\Steam\\Apps\\%u", appid); status = RegCreateKeyA(HKEY_CURRENT_USER, buf, &key); if (!status) { DWORD value; value = 1; RegSetKeyValueA(key, NULL, "Installed", REG_DWORD, &value, sizeof(value)); RegSetKeyValueA(key, NULL, "Running", REG_DWORD, &value, sizeof(value)); value = 0; RegSetKeyValueA(key, NULL, "Updating", REG_DWORD, &value, sizeof(value)); RegCloseKey(key); } else WINE_ERR("Could not create key: %u\n", status); SteamAPI_Shutdown(); } static WCHAR *find_quote(WCHAR *str) { WCHAR *end = strchrW(str, '"'), *ch; int odd; while (end) { odd = 0; ch = end - 1; while (ch >= str && *ch == '\\') { odd = !odd; --ch; } if (!odd) return end; end = strchrW(end + 1, '"'); } return NULL; } static HANDLE run_process(void) { WCHAR *cmdline = GetCommandLineW(); STARTUPINFOW si = { sizeof(si) }; PROCESS_INFORMATION pi; /* skip argv[0] */ if (*cmdline == '"') { cmdline = find_quote(cmdline + 1); if (cmdline) cmdline++; } else { cmdline = strchrW(cmdline, ' '); } if (!cmdline) { WINE_ERR("Invalid command\n"); return INVALID_HANDLE_VALUE; } while (*cmdline == ' ') cmdline++; /* convert absolute unix path to dos */ if (cmdline[0] == '/' || (cmdline[0] == '"' && cmdline[1] == '/')) { WCHAR *scratchW; char *scratchA; WCHAR *start, *end, *dos, *remainder, *new_cmdline; size_t argv0_len; int r; static const WCHAR dquoteW[] = {'"',0}; WINE_TRACE("Converting unix command: %s\n", wine_dbgstr_w(cmdline)); if (cmdline[0] == '"') { start = cmdline + 1; end = find_quote(start); if (!end) { WINE_ERR("Unmatched quote? %s\n", wine_dbgstr_w(cmdline)); goto run; } remainder = end + 1; } else { start = cmdline; end = strchrW(start, ' '); if (!end) end = strchrW(start, '\0'); remainder = end; } argv0_len = end - start; scratchW = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, (argv0_len + 1) * sizeof(WCHAR)); memcpy(scratchW, start, argv0_len * sizeof(WCHAR)); scratchW[argv0_len] = '\0'; r = WideCharToMultiByte(CP_UNIXCP, 0, scratchW, -1, NULL, 0, NULL, NULL); if (!r) { WINE_ERR("Char conversion size failed?\n"); goto run; } scratchA = (char *)HeapAlloc(GetProcessHeap(), 0, r); r = WideCharToMultiByte(CP_UNIXCP, 0, scratchW, -1, scratchA, r, NULL, NULL); if (!r) { WINE_ERR("Char conversion failed?\n"); goto run; } dos = wine_get_dos_file_name(scratchA); new_cmdline = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, (strlenW(dos) + 3 + strlenW(remainder) + 1) * sizeof(WCHAR)); strcpyW(new_cmdline, dquoteW); strcatW(new_cmdline, dos); strcatW(new_cmdline, dquoteW); strcatW(new_cmdline, remainder); cmdline = new_cmdline; } run: WINE_TRACE("Running command %s\n", wine_dbgstr_w(cmdline)); if (!CreateProcessW(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { WINE_ERR("Failed to create process %s: %u\n", wine_dbgstr_w(cmdline), GetLastError()); return INVALID_HANDLE_VALUE; } CloseHandle(pi.hThread); return pi.hProcess; } int main(int argc, char *argv[]) { HANDLE wait_handle = INVALID_HANDLE_VALUE; WINE_TRACE("\n"); if (getenv("SteamGameId")) { /* do setup only for game process */ CreateThread(NULL, 0, create_steam_window, NULL, 0, NULL); set_active_process_pid(); setup_steam_registry(); wait_handle = __wine_make_process_system(); } if (argc > 1) { HANDLE child; child = run_process(); if (child == INVALID_HANDLE_VALUE) return 1; if (wait_handle == INVALID_HANDLE_VALUE) wait_handle = child; else CloseHandle(child); } WaitForSingleObject(wait_handle, INFINITE); return 0; }