Proton/steam_helper/steam.cpp

281 lines
7.8 KiB
C++

/*
* 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 <windows.h>
#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;
}