diff --git a/.vscode/settings.json b/.vscode/settings.json index eb0dcdb..2c7f1dc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,7 +2,11 @@ "C_Cpp.default.compilerPath": "/usr/bin/i686-w64-mingw32-gcc", "files.associations": { "*.su": "tsv", + "thread": "cpp", + "bitset": "cpp", + "initializer_list": "cpp", "windows.h": "c", - "namedpipeapi.h": "c" + "namedpipeapi.h": "c", + "*.rh": "c" } } \ No newline at end of file diff --git a/bridge.c b/bridge.c index 685bc3d..c5a3163 100644 --- a/bridge.c +++ b/bridge.c @@ -440,17 +440,6 @@ void PipeBufferOutThread(LPVOID lpParam) void CreateBridge() { - static void(CDECL * wine_get_host_version)(const char **sysname, const char **release); - HMODULE hntdll = GetModuleHandle("ntdll.dll"); - wine_get_host_version = (void *)GetProcAddress(hntdll, "wine_get_host_version"); - - assert(wine_get_host_version); - const char *__sysname; - const char *__release; - wine_get_host_version(&__sysname, &__release); - IsLinux = strcmp(__sysname, "Linux") == 0; - print("Running on %s\n", __sysname); - LPCTSTR lpszPipename = TEXT("\\\\.\\pipe\\discord-ipc-0"); NewConnection: diff --git a/build/bridge.sh b/build/bridge.sh new file mode 100755 index 0000000..a70bb92 --- /dev/null +++ b/build/bridge.sh @@ -0,0 +1,32 @@ +#! /bin/sh + +# This script is used to run Steam Play with the bridge. +# Usage: /path/to/bridge.sh %command% +# Original script: https://github.com/0e4ef622/wine-discord-ipc-bridge/blob/master/winediscordipcbridge-steam.sh +# As requested by https://github.com/EnderIce2/rpc-bridge/issues/2 + +# Exporting BRIDGE_PATH to provide the bridge with its location. +export BRIDGE_PATH="$(dirname "$0")/bridge.exe" + +# The "--steam" option prevents the game from +# hanging as "running" in Steam after it is closed. +# This is done by creating a dummy service with +# startup type SERVICE_DEMAND_START so this service +# is only started when we use this script. +BRIDGE_CMD="$BRIDGE_PATH --steam" + +# Linux +TEMP_PATH="$XDG_RUNTIME_DIR" +# macOS but Steam Play is not supported on macOS https://github.com/ValveSoftware/Proton/issues/1344 +TEMP_PATH=${TEMP_PATH:-"$TMPDIR"} + +VESSEL_PATH="$BRIDGE_PATH" +IPC_PATHS="$TEMP_PATH /run/user/$UID $TEMP_PATH/snap.discord $TEMP_PATH/app/com.discordapp.Discord" +for discord_ipc in $IPC_PATHS; do + if [ -S "$discord_ipc"/discord-ipc-0 ]; then + VESSEL_PATH="$BRIDGE_PATH:$(echo "$discord_ipc"/discord-ipc-0)" + break + fi +done + +PROTON_REMOTE_DEBUG_CMD="$BRIDGE_CMD" PRESSURE_VESSEL_FILESYSTEMS_RW="$VESSEL_PATH:$PRESSURE_VESSEL_FILESYSTEMS_RW" "$@" diff --git a/main.c b/main.c index 5c9723b..743d9f3 100644 --- a/main.c +++ b/main.c @@ -9,8 +9,10 @@ BOOL RunningAsService = FALSE; void CreateBridge(); void LaunchGame(int argc, char **argv); void ServiceMain(int argc, char *argv[]); -void InstallService(); +void InstallService(int ServiceStartType, LPCSTR Path); +char *native_getenv(const char *name); void RemoveService(); +extern BOOL IsLinux; LPTSTR GetErrorMessage() { @@ -45,7 +47,12 @@ void DetectWine() { HMODULE hNTdll = GetModuleHandle("ntdll.dll"); if (!hNTdll) + { + MessageBox(NULL, "Failed to load ntdll.dll", + GetErrorMessage(), MB_OK | MB_ICONERROR); ExitProcess(1); + } + if (!GetProcAddress(hNTdll, "wine_get_version")) { MessageBox(NULL, "This program is only intended to run under Wine.", @@ -69,6 +76,9 @@ void DetectWine() else if (result == IDNO) ExitProcess(1); } + + IsLinux = strcmp(__sysname, "Linux") == 0; + printf("Running on %s\n", __sysname); } void print(char const *fmt, ...) @@ -82,6 +92,190 @@ void print(char const *fmt, ...) va_end(args); } +void HandleArguments(int argc, char *argv[]) +{ + if (strcmp(argv[1], "--service") == 0) + { + RunningAsService = TRUE; + print("Running as service\n"); + + SERVICE_TABLE_ENTRY ServiceTable[] = + { + {"rpc-bridge", (LPSERVICE_MAIN_FUNCTION)ServiceMain}, + {NULL, NULL}, + }; + + if (StartServiceCtrlDispatcher(ServiceTable) == FALSE) + { + print("Service failed to start\n"); + ExitProcess(1); + } + } + else if (strcmp(argv[1], "--steam") == 0) + { + /* All this mess just so when you close the game, + it automatically closes the bridge and Steam + will not say that the game is still running. */ + + print("Running as Steam\n"); + if (IsLinux == FALSE) + CreateBridge(); + + if (argc > 2) + { + if (strcmp(argv[2], "--no-service") == 0) + CreateBridge(); + } + + SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (hSCManager == NULL) + { + print("(Steam) OpenSCManager: %s\n", GetErrorMessage()); + ExitProcess(1); + } + + SC_HANDLE schService = OpenService(hSCManager, "rpc-bridge", + SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_START); + if (schService == NULL) + { + if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST) + { + print("(Steam) OpenService: %s\n", GetErrorMessage()); + ExitProcess(1); + } + + print("(Steam) Service does not exist, registering...\n"); + + WCHAR *(CDECL * wine_get_dos_file_name)(LPCSTR str) = + (void *)GetProcAddress(GetModuleHandleA("KERNEL32"), + "wine_get_dos_file_name"); + + char *unixPath = native_getenv("BRIDGE_PATH"); + if (unixPath == NULL) + { + print("(Steam) BRIDGE_PATH not set\n"); + ExitProcess(1); + } + WCHAR *dosPath = wine_get_dos_file_name(unixPath); + LPSTR asciiPath = (LPSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH); + WideCharToMultiByte(CP_ACP, 0, dosPath, -1, asciiPath, MAX_PATH, NULL, NULL); + + strcat_s(asciiPath, MAX_PATH, " --service"); + print("(Steam) Binary path: %s\n", asciiPath); + + InstallService(SERVICE_DEMAND_START, asciiPath); + HeapFree(GetProcessHeap(), 0, asciiPath); + + /* Create handle for StartService below */ + print("(Steam) Service registered, opening handle...\n"); + /* FIXME: For some reason here it freezes??? */ + schService = OpenService(hSCManager, "rpc-bridge", SERVICE_START); + if (schService == NULL) + { + print("(Steam) Cannot open service after creation: %s\n", GetErrorMessage()); + ExitProcess(1); + } + } + else + { + DWORD dwBytesNeeded; + QueryServiceConfig(schService, NULL, 0, &dwBytesNeeded); + LPQUERY_SERVICE_CONFIG lpqsc = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LPTR, dwBytesNeeded); + if (lpqsc == NULL) + { + print("(Steam) LocalAlloc: %s\n", GetErrorMessage()); + ExitProcess(1); + } + + if (!QueryServiceConfig(schService, lpqsc, dwBytesNeeded, &dwBytesNeeded)) + { + print("(Steam) QueryServiceConfig: %s\n", GetErrorMessage()); + ExitProcess(1); + } + + WCHAR *(CDECL * wine_get_dos_file_name)(LPCSTR str) = + (void *)GetProcAddress(GetModuleHandleA("KERNEL32"), + "wine_get_dos_file_name"); + + char *unixPath = native_getenv("BRIDGE_PATH"); + if (unixPath == NULL) + { + print("(Steam) BRIDGE_PATH not set\n"); + ExitProcess(1); + } + WCHAR *dosPath = wine_get_dos_file_name(unixPath); + LPSTR asciiPath = (LPSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH); + WideCharToMultiByte(CP_ACP, 0, dosPath, -1, asciiPath, MAX_PATH, NULL, NULL); + + strcat_s(asciiPath, MAX_PATH, " --service"); + print("(Steam) Binary path: %s\n", asciiPath); + + if (strcmp(lpqsc->lpBinaryPathName, asciiPath) != 0) + { + print("(Steam) Service binary path is not correct, updating...\n"); + ChangeServiceConfig(schService, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, + asciiPath, NULL, NULL, NULL, NULL, NULL, NULL); + } + else + print("(Steam) Service binary path is correct\n"); + HeapFree(GetProcessHeap(), 0, asciiPath); + } + + print("(Steam) Starting service...\n"); + if (StartService(schService, 0, NULL) == FALSE) + { + if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING) + { + print("(Steam) Service is already running\n"); + CloseServiceHandle(schService); + CloseServiceHandle(hSCManager); + ExitProcess(0); + } + + print("StartService: %s\n", GetErrorMessage()); + MessageBox(NULL, GetErrorMessage(), + "StartService", + MB_OK | MB_ICONSTOP); + ExitProcess(1); + } + + print("(Steam) Service started successfully, exiting...\n"); + CloseServiceHandle(schService); + CloseServiceHandle(hSCManager); + ExitProcess(0); + } + else if (strcmp(argv[1], "--install") == 0) + { + char filename[MAX_PATH]; + GetModuleFileName(NULL, filename, MAX_PATH); + CopyFile(filename, "C:\\bridge.exe", FALSE); + + InstallService(SERVICE_AUTO_START, "C:\\bridge.exe --service"); + ExitProcess(0); + } + else if (strcmp(argv[1], "--uninstall") == 0) + { + RemoveService(); + ExitProcess(0); + } + else if (strcmp(argv[1], "--help") == 0) + { + printf("Usage:\n"); + printf(" %s [args]\n", argv[0]); + printf(" --help - Show this help\n"); + printf(" --install - Install service\n"); + printf(" This will copy the binary to C:\\bridge.exe and register it as a service\n"); + printf(" --uninstall - Uninstall service\n"); + printf(" This will remove the service and delete C:\\bridge.exe\n"); + printf(" --steam - Reserved for Steam\n"); + printf(" This will start the service and exit (used with bridge.sh)\n"); + printf(" --no-service - Do not run as service\n"); + printf(" (only for --steam)\n"); + printf(" --service - Reserved for service\n"); + ExitProcess(0); + } +} + int main(int argc, char *argv[]) { DetectWine(); @@ -95,39 +289,7 @@ int main(int argc, char *argv[]) } if (argc > 1) - { - if (strcmp(argv[1], "--service") == 0) - { - RunningAsService = TRUE; - print("Running as service\n"); - - SERVICE_TABLE_ENTRY ServiceTable[] = - { - {"rpc-bridge", (LPSERVICE_MAIN_FUNCTION)ServiceMain}, - {NULL, NULL}, - }; - - if (StartServiceCtrlDispatcher(ServiceTable) == FALSE) - { - print("Service failed to start\n"); - return GetLastError(); - } - return 0; - } - else if (strcmp(argv[1], "--install") == 0) - InstallService(); - else if (strcmp(argv[1], "--uninstall") == 0) - RemoveService(); - else if (strcmp(argv[1], "--help") == 0) - { - printf("Usage:\n"); - printf(" %s [args]\n", argv[0]); - printf(" --install - Install service\n"); - printf(" --uninstall - Uninstall service\n"); - printf(" --help - Show this help\n"); - ExitProcess(0); - } - } + HandleArguments(argc, argv); CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CreateBridge, NULL, 0, NULL); diff --git a/service.c b/service.c index 9084ae6..b9092da 100644 --- a/service.c +++ b/service.c @@ -8,6 +8,7 @@ SERVICE_STATUS_HANDLE g_StatusHandle = NULL; void print(char const *fmt, ...); void CreateBridge(); LPTSTR GetErrorMessage(); +extern BOOL IsLinux; void WINAPI ServiceCtrlHandler(DWORD CtrlCode) { @@ -83,68 +84,47 @@ void ServiceMain(DWORD argc, LPTSTR *argv) return; } -void DetectDarwin() +void InstallService(int ServiceStartType, LPCSTR Path) { - static void(CDECL * wine_get_host_version)(const char **sysname, const char **release); - wine_get_host_version = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), - "wine_get_host_version"); - const char *__sysname; - const char *__release; - wine_get_host_version(&__sysname, &__release); - if (strcmp(__sysname, "Darwin") == 0) + print("Registering service\n"); + + if (IsLinux == FALSE) { /* FIXME: I don't know how to get the TMPDIR without getenv */ MessageBox(NULL, "Registering as a service is not supported on macOS at the moment.", "Unsupported", MB_OK | MB_ICONINFORMATION); ExitProcess(1); } -} -void InstallService() -{ - print("Registering to run on startup\n"); - - DetectDarwin(); - - SC_HANDLE schSCManager, schService; - DWORD dwTagId; - - schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); + SC_HANDLE schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); if (schSCManager == NULL) { + print("Failed to open service manager\n"); MessageBox(NULL, GetErrorMessage(), "OpenSCManager", MB_OK | MB_ICONSTOP); ExitProcess(1); } - schService = - CreateService(schSCManager, - "rpc-bridge", "Wine RPC Bridge", - SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, - SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, - "C:\\bridge.exe --service", - NULL, &dwTagId, NULL, NULL, NULL); + DWORD dwTagId; + SC_HANDLE schService = CreateService(schSCManager, + "rpc-bridge", "Wine RPC Bridge", + SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, + ServiceStartType, SERVICE_ERROR_NORMAL, + Path, NULL, &dwTagId, NULL, NULL, NULL); if (schService == NULL) { + print("Failed to create service\n"); MessageBox(NULL, GetErrorMessage(), "CreateService", MB_OK | MB_ICONSTOP); ExitProcess(1); } - else - { - char filename[MAX_PATH]; - GetModuleFileName(NULL, filename, MAX_PATH); - CopyFile(filename, "C:\\bridge.exe", FALSE); - - print("Service installed successfully\n"); - CloseServiceHandle(schService); - } + print("Service installed successfully\n"); + CloseServiceHandle(schService); CloseServiceHandle(schSCManager); - ExitProcess(0); } void RemoveService() @@ -208,5 +188,4 @@ void RemoveService() print("Service removed successfully\n"); CloseServiceHandle(schService); CloseServiceHandle(schSCManager); - ExitProcess(0); }