mirror of
https://github.com/EnderIce2/rpc-bridge.git
synced 2025-07-01 10:29:14 +00:00
Compare commits
9 Commits
655caa2934
...
kvm
Author | SHA1 | Date | |
---|---|---|---|
3ac5605a52
|
|||
20728818d2
|
|||
751d9ab3b7
|
|||
b906c009f4
|
|||
38e620836e
|
|||
a640dd5af1
|
|||
8fbe00b555
|
|||
bf98b8784c
|
|||
3021f8e4ad
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
*.o
|
*.o
|
||||||
*.exe
|
*.exe
|
||||||
*.res
|
*.res
|
||||||
|
*.elf
|
||||||
|
11
.vscode/c_cpp_properties.json
vendored
11
.vscode/c_cpp_properties.json
vendored
@ -9,6 +9,17 @@
|
|||||||
"cStandard": "c17",
|
"cStandard": "c17",
|
||||||
"cppStandard": "gnu++17",
|
"cppStandard": "gnu++17",
|
||||||
"intelliSenseMode": "windows-gcc-x64"
|
"intelliSenseMode": "windows-gcc-x64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Linux",
|
||||||
|
"includePath": [
|
||||||
|
"${workspaceFolder}/**"
|
||||||
|
],
|
||||||
|
"defines": [],
|
||||||
|
"cStandard": "c17",
|
||||||
|
"cppStandard": "gnu++17",
|
||||||
|
"intelliSenseMode": "linux-gcc-x64",
|
||||||
|
"compilerPath": "/usr/bin/gcc"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"version": 4
|
"version": 4
|
||||||
|
1
Makefile
1
Makefile
@ -12,6 +12,7 @@ build: $(C_OBJECTS)
|
|||||||
$(info Linking)
|
$(info Linking)
|
||||||
x86_64-w64-mingw32-windres bridge.rc -O coff -o bridge.res
|
x86_64-w64-mingw32-windres bridge.rc -O coff -o bridge.res
|
||||||
x86_64-w64-mingw32-gcc $(C_OBJECTS) bridge.res $(LFLAGS) $(DBGFLAGS) -o build/bridge.exe
|
x86_64-w64-mingw32-gcc $(C_OBJECTS) bridge.res $(LFLAGS) $(DBGFLAGS) -o build/bridge.exe
|
||||||
|
gcc kvm.c -o build/rpc-bridge.elf
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
$(info Compiling $<)
|
$(info Compiling $<)
|
||||||
|
@ -62,7 +62,7 @@ The steps for MacOS are almost the same, but due to the way `$TMPDIR` works, you
|
|||||||
|
|
||||||
The script will add a LaunchAgent to your user, that will symlink the `$TMPDIR` directory to `/tmp/rpc-bridge/tmpdir`.
|
The script will add a LaunchAgent to your user, that will symlink the `$TMPDIR` directory to `/tmp/rpc-bridge/tmpdir`.
|
||||||
|
|
||||||
*Note: You will need to launch the `bridge.exe` file manually in Wine atleast one time for it to register and launch automatically the next times.*
|
*Note: You will need to launch the `bridge.exe` file manually in Wine at least once for it to register and launch automatically the next time.*
|
||||||
|
|
||||||
## Compiling from source
|
## Compiling from source
|
||||||
|
|
||||||
|
252
bridge.c
252
bridge.c
@ -4,6 +4,8 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "bridge.h"
|
||||||
|
|
||||||
#define __linux_read 3
|
#define __linux_read 3
|
||||||
#define __linux_write 4
|
#define __linux_write 4
|
||||||
#define __linux_open 5
|
#define __linux_open 5
|
||||||
@ -64,7 +66,7 @@ void print(char const *fmt, ...);
|
|||||||
LPTSTR GetErrorMessage();
|
LPTSTR GetErrorMessage();
|
||||||
extern BOOL RunningAsService;
|
extern BOOL RunningAsService;
|
||||||
BOOL RetryNewConnection;
|
BOOL RetryNewConnection;
|
||||||
BOOL IsLinux;
|
OS_INFO OSInfo = {0};
|
||||||
HANDLE hOut = NULL;
|
HANDLE hOut = NULL;
|
||||||
HANDLE hIn = NULL;
|
HANDLE hIn = NULL;
|
||||||
|
|
||||||
@ -102,7 +104,7 @@ static naked int darwin_syscall(int num,
|
|||||||
|
|
||||||
static inline int sys_read(int fd, void *buf, size_t count)
|
static inline int sys_read(int fd, void *buf, size_t count)
|
||||||
{
|
{
|
||||||
if (IsLinux)
|
if (OSInfo.IsLinux)
|
||||||
return linux_syscall(__linux_read, fd, buf, count, 0, 0, 0);
|
return linux_syscall(__linux_read, fd, buf, count, 0, 0, 0);
|
||||||
else
|
else
|
||||||
return darwin_syscall(__darwin_read, fd, buf, count, 0, 0, 0);
|
return darwin_syscall(__darwin_read, fd, buf, count, 0, 0, 0);
|
||||||
@ -110,7 +112,7 @@ static inline int sys_read(int fd, void *buf, size_t count)
|
|||||||
|
|
||||||
static inline int sys_write(int fd, const void *buf, size_t count)
|
static inline int sys_write(int fd, const void *buf, size_t count)
|
||||||
{
|
{
|
||||||
if (IsLinux)
|
if (OSInfo.IsLinux)
|
||||||
return linux_syscall(__linux_write, fd, buf, count, 0, 0, 0);
|
return linux_syscall(__linux_write, fd, buf, count, 0, 0, 0);
|
||||||
else
|
else
|
||||||
return darwin_syscall(__darwin_write, fd, buf, count, 0, 0, 0);
|
return darwin_syscall(__darwin_write, fd, buf, count, 0, 0, 0);
|
||||||
@ -118,7 +120,7 @@ static inline int sys_write(int fd, const void *buf, size_t count)
|
|||||||
|
|
||||||
static inline int sys_open(const char *pathname, int flags, int mode)
|
static inline int sys_open(const char *pathname, int flags, int mode)
|
||||||
{
|
{
|
||||||
if (IsLinux)
|
if (OSInfo.IsLinux)
|
||||||
return linux_syscall(__linux_open, pathname, flags, mode, 0, 0, 0);
|
return linux_syscall(__linux_open, pathname, flags, mode, 0, 0, 0);
|
||||||
else
|
else
|
||||||
return darwin_syscall(__darwin_open, pathname, flags, mode, 0, 0, 0);
|
return darwin_syscall(__darwin_open, pathname, flags, mode, 0, 0, 0);
|
||||||
@ -126,7 +128,7 @@ static inline int sys_open(const char *pathname, int flags, int mode)
|
|||||||
|
|
||||||
static inline int sys_close(int fd)
|
static inline int sys_close(int fd)
|
||||||
{
|
{
|
||||||
if (IsLinux)
|
if (OSInfo.IsLinux)
|
||||||
return linux_syscall(__linux_close, fd, 0, 0, 0, 0, 0);
|
return linux_syscall(__linux_close, fd, 0, 0, 0, 0, 0);
|
||||||
else
|
else
|
||||||
return darwin_syscall(__darwin_close, fd, 0, 0, 0, 0, 0);
|
return darwin_syscall(__darwin_close, fd, 0, 0, 0, 0, 0);
|
||||||
@ -134,25 +136,25 @@ static inline int sys_close(int fd)
|
|||||||
|
|
||||||
static inline unsigned int *sys_mmap(unsigned int *addr, size_t length, int prot, int flags, int fd, off_t offset)
|
static inline unsigned int *sys_mmap(unsigned int *addr, size_t length, int prot, int flags, int fd, off_t offset)
|
||||||
{
|
{
|
||||||
assert(IsLinux);
|
assert(OSInfo.IsLinux);
|
||||||
return linux_syscall(__linux_mmap2, addr, length, prot, flags, fd, offset);
|
return linux_syscall(__linux_mmap2, addr, length, prot, flags, fd, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int sys_munmap(unsigned int *addr, size_t length)
|
static inline int sys_munmap(unsigned int *addr, size_t length)
|
||||||
{
|
{
|
||||||
assert(IsLinux);
|
assert(OSInfo.IsLinux);
|
||||||
return linux_syscall(__linux_munmap, addr, length, 0, 0, 0, 0);
|
return linux_syscall(__linux_munmap, addr, length, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int sys_socketcall(int call, unsigned long *args)
|
static inline int sys_socketcall(int call, unsigned long *args)
|
||||||
{
|
{
|
||||||
assert(IsLinux);
|
assert(OSInfo.IsLinux);
|
||||||
return linux_syscall(__linux_socketcall, call, args, 0, 0, 0, 0);
|
return linux_syscall(__linux_socketcall, call, args, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int sys_socket(int domain, int type, int protocol)
|
static inline int sys_socket(int domain, int type, int protocol)
|
||||||
{
|
{
|
||||||
if (IsLinux)
|
if (OSInfo.IsLinux)
|
||||||
return linux_syscall(__linux_socket, domain, type, protocol, 0, 0, 0);
|
return linux_syscall(__linux_socket, domain, type, protocol, 0, 0, 0);
|
||||||
else
|
else
|
||||||
return darwin_syscall(__darwin_socket, domain, type, protocol, 0, 0, 0);
|
return darwin_syscall(__darwin_socket, domain, type, protocol, 0, 0, 0);
|
||||||
@ -160,7 +162,7 @@ static inline int sys_socket(int domain, int type, int protocol)
|
|||||||
|
|
||||||
static inline int sys_connect(int s, caddr_t name, socklen_t namelen)
|
static inline int sys_connect(int s, caddr_t name, socklen_t namelen)
|
||||||
{
|
{
|
||||||
if (IsLinux)
|
if (OSInfo.IsLinux)
|
||||||
return linux_syscall(__linux_connect, s, name, namelen, 0, 0, 0);
|
return linux_syscall(__linux_connect, s, name, namelen, 0, 0, 0);
|
||||||
else
|
else
|
||||||
return darwin_syscall(__darwin_connect, s, name, namelen, 0, 0, 0);
|
return darwin_syscall(__darwin_connect, s, name, namelen, 0, 0, 0);
|
||||||
@ -174,7 +176,7 @@ char *native_getenv(const char *name)
|
|||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return lpBuffer;
|
return lpBuffer;
|
||||||
|
|
||||||
if (!IsLinux)
|
if (!OSInfo.IsLinux)
|
||||||
{
|
{
|
||||||
char *value = getenv(name);
|
char *value = getenv(name);
|
||||||
if (value == NULL)
|
if (value == NULL)
|
||||||
@ -251,9 +253,10 @@ void ConnectToSocket(int fd)
|
|||||||
{
|
{
|
||||||
print("Connecting to socket\n");
|
print("Connecting to socket\n");
|
||||||
const char *runtime;
|
const char *runtime;
|
||||||
if (IsLinux)
|
if (OSInfo.IsLinux)
|
||||||
runtime = native_getenv("XDG_RUNTIME_DIR");
|
runtime = native_getenv("XDG_RUNTIME_DIR");
|
||||||
else
|
else if (OSInfo.IsDarwin)
|
||||||
|
{
|
||||||
runtime = native_getenv("TMPDIR");
|
runtime = native_getenv("TMPDIR");
|
||||||
if (runtime == NULL)
|
if (runtime == NULL)
|
||||||
{
|
{
|
||||||
@ -267,13 +270,21 @@ void ConnectToSocket(int fd)
|
|||||||
print("IPC directory does not exist: %s. If you're on MacOS, see the github guide on how to install the launchd service.\n", runtime);
|
print("IPC directory does not exist: %s. If you're on MacOS, see the github guide on how to install the launchd service.\n", runtime);
|
||||||
// Handle the case where the directory doesn't exist
|
// Handle the case where the directory doesn't exist
|
||||||
// For example, create the directory
|
// For example, create the directory
|
||||||
if (!RunningAsService)
|
|
||||||
MessageBox(NULL, "IPC directory does not exist",
|
int result = MessageBox(NULL, "IPC directory does not exist\nDo you want to open the installation guide?",
|
||||||
"Directory not found",
|
"Directory not found",
|
||||||
MB_OK | MB_ICONSTOP);
|
MB_YESNO | MB_ICONSTOP);
|
||||||
|
if (result == IDYES)
|
||||||
|
ShellExecute(NULL, "open", "https://enderice2.github.io/rpc-bridge/installation.html#macos", NULL, NULL, SW_SHOWNORMAL);
|
||||||
ExitProcess(1);
|
ExitProcess(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
print("Unsupported OS\n");
|
||||||
|
ExitProcess(1);
|
||||||
|
}
|
||||||
|
|
||||||
print("IPC directory: %s\n", runtime);
|
print("IPC directory: %s\n", runtime);
|
||||||
|
|
||||||
@ -298,7 +309,7 @@ void ConnectToSocket(int fd)
|
|||||||
|
|
||||||
print("Connecting to %s\n", pipePath);
|
print("Connecting to %s\n", pipePath);
|
||||||
|
|
||||||
if (IsLinux)
|
if (OSInfo.IsLinux)
|
||||||
{
|
{
|
||||||
unsigned long socketArgs[] = {
|
unsigned long socketArgs[] = {
|
||||||
(unsigned long)fd,
|
(unsigned long)fd,
|
||||||
@ -476,6 +487,162 @@ void PipeBufferOutThread(LPVOID lpParam)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void COMPipeBufferInThread(LPVOID lpParam)
|
||||||
|
{
|
||||||
|
bridge_thread *bt = (bridge_thread *)lpParam;
|
||||||
|
print("COM In thread started using COM port and pipe %#x\n", bt->hPipe);
|
||||||
|
|
||||||
|
HANDLE hComPort = CreateFile("\\\\.\\COM2", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
if (hComPort == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
print("Failed to open COM2 port: %s\n", GetErrorMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EOFCount = 0;
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
char buffer[BUFFER_LENGTH];
|
||||||
|
DWORD bytesRead;
|
||||||
|
|
||||||
|
if (!ReadFile(hComPort, buffer, sizeof(buffer), &bytesRead, NULL))
|
||||||
|
{
|
||||||
|
print("Failed to read from COM2 port: %s\n", GetErrorMessage());
|
||||||
|
Sleep(1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EOFCount > 4)
|
||||||
|
{
|
||||||
|
print("EOF count exceeded\n");
|
||||||
|
RetryNewConnection = TRUE;
|
||||||
|
TerminateThread(hOut, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytesRead == 0)
|
||||||
|
{
|
||||||
|
print("EOF\n");
|
||||||
|
Sleep(1000);
|
||||||
|
EOFCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
EOFCount = 0;
|
||||||
|
|
||||||
|
print("Reading %d bytes from COM2 port: \"", bytesRead);
|
||||||
|
for (DWORD i = 0; i < bytesRead; i++)
|
||||||
|
print("%c", buffer[i]);
|
||||||
|
print("\"\n");
|
||||||
|
|
||||||
|
DWORD dwWritten;
|
||||||
|
if (!WriteFile(bt->hPipe, buffer, bytesRead, &dwWritten, NULL))
|
||||||
|
{
|
||||||
|
if (GetLastError() == ERROR_BROKEN_PIPE)
|
||||||
|
{
|
||||||
|
RetryNewConnection = TRUE;
|
||||||
|
print("In Broken pipe\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Failed to write to pipe: %s\n", GetErrorMessage());
|
||||||
|
Sleep(1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (dwWritten < bytesRead)
|
||||||
|
{
|
||||||
|
int last_written = dwWritten;
|
||||||
|
if (!WriteFile(bt->hPipe, buffer + dwWritten, bytesRead - dwWritten, &dwWritten, NULL))
|
||||||
|
{
|
||||||
|
if (GetLastError() == ERROR_BROKEN_PIPE)
|
||||||
|
{
|
||||||
|
RetryNewConnection = TRUE;
|
||||||
|
print("In Broken pipe\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Failed to write to pipe: %s\n", GetErrorMessage());
|
||||||
|
Sleep(1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_written == dwWritten)
|
||||||
|
{
|
||||||
|
print("Failed to write to pipe: %s\n", GetErrorMessage());
|
||||||
|
Sleep(1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(hComPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
void COMPipeBufferOutThread(LPVOID lpParam)
|
||||||
|
{
|
||||||
|
bridge_thread *bt = (bridge_thread *)lpParam;
|
||||||
|
print("COM Out thread started using COM port and pipe %#x\n", bt->hPipe);
|
||||||
|
|
||||||
|
HANDLE hComPort = CreateFile("\\\\.\\COM2", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
if (hComPort == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
print("Failed to open COM2 port: %s\n", GetErrorMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
char buffer[BUFFER_LENGTH];
|
||||||
|
DWORD dwRead;
|
||||||
|
|
||||||
|
if (!ReadFile(bt->hPipe, buffer, sizeof(buffer), &dwRead, NULL))
|
||||||
|
{
|
||||||
|
if (GetLastError() == ERROR_BROKEN_PIPE)
|
||||||
|
{
|
||||||
|
RetryNewConnection = TRUE;
|
||||||
|
print("Out Broken pipe\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Failed to read from pipe: %s\n", GetErrorMessage());
|
||||||
|
Sleep(1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Writing %d bytes to COM2 port: \"", dwRead);
|
||||||
|
for (DWORD i = 0; i < dwRead; i++)
|
||||||
|
print("%c", buffer[i]);
|
||||||
|
print("\"\n");
|
||||||
|
|
||||||
|
DWORD bytesWritten;
|
||||||
|
if (!WriteFile(hComPort, buffer, dwRead, &bytesWritten, NULL))
|
||||||
|
{
|
||||||
|
print("Failed to write to COM2 port: %s\n", GetErrorMessage());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (bytesWritten < dwRead)
|
||||||
|
{
|
||||||
|
int last_written = bytesWritten;
|
||||||
|
if (!WriteFile(hComPort, buffer + bytesWritten, dwRead - bytesWritten, &bytesWritten, NULL))
|
||||||
|
{
|
||||||
|
print("Failed to write to COM2 port: %s\n", GetErrorMessage());
|
||||||
|
Sleep(1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_written == bytesWritten)
|
||||||
|
{
|
||||||
|
print("Failed to write to COM2 port: %s\n", GetErrorMessage());
|
||||||
|
Sleep(1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(hComPort);
|
||||||
|
}
|
||||||
|
|
||||||
void CreateBridge()
|
void CreateBridge()
|
||||||
{
|
{
|
||||||
LPCTSTR lpszPipename = TEXT("\\\\.\\pipe\\discord-ipc-0");
|
LPCTSTR lpszPipename = TEXT("\\\\.\\pipe\\discord-ipc-0");
|
||||||
@ -500,7 +667,7 @@ NewConnection:
|
|||||||
CreateNamedPipe("\\\\.\\pipe\\discord-ipc-0",
|
CreateNamedPipe("\\\\.\\pipe\\discord-ipc-0",
|
||||||
PIPE_ACCESS_DUPLEX,
|
PIPE_ACCESS_DUPLEX,
|
||||||
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
|
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
|
||||||
1, 1024, 1024, 0, NULL);
|
PIPE_UNLIMITED_INSTANCES, BUFFER_LENGTH, BUFFER_LENGTH, 0, NULL);
|
||||||
|
|
||||||
if (hPipe == INVALID_HANDLE_VALUE)
|
if (hPipe == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
@ -529,7 +696,49 @@ NewConnection:
|
|||||||
print("Pipe connected\n");
|
print("Pipe connected\n");
|
||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
if (IsLinux)
|
if (!OSInfo.IsWine)
|
||||||
|
{
|
||||||
|
print("Running on Windows\n");
|
||||||
|
/* KVM, send 0xBB1569 and wait with timeout (TODO) for response 0xB41D6E */
|
||||||
|
|
||||||
|
print("Trying \\\\.\\COM2\n");
|
||||||
|
HANDLE hComPort = CreateFile("\\\\.\\COM2", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
assert(hComPort != INVALID_HANDLE_VALUE);
|
||||||
|
DWORD dwWritten;
|
||||||
|
print("Writing to COM port\n");
|
||||||
|
char buffer[4] = "\x69\x15\xBB";
|
||||||
|
assert(WriteFile(hComPort, buffer, sizeof(buffer), &dwWritten, NULL));
|
||||||
|
print("Wrote %d bytes to COM port\n", dwWritten);
|
||||||
|
DWORD dwRead;
|
||||||
|
print("Reading from COM port\n");
|
||||||
|
assert(ReadFile(hComPort, buffer, sizeof(buffer), &dwRead, NULL));
|
||||||
|
print("Read %d bytes from COM port\n", dwRead);
|
||||||
|
|
||||||
|
if (dwRead != 4 || memcmp(buffer, "\x6E\x1D\xB4", 4) != 0)
|
||||||
|
{
|
||||||
|
CloseHandle(hComPort);
|
||||||
|
print("Failed to connect to COM2\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(hComPort);
|
||||||
|
print("Connected to COM2\n");
|
||||||
|
|
||||||
|
bridge_thread bt = {0, hPipe};
|
||||||
|
|
||||||
|
hIn = CreateThread(NULL, 0,
|
||||||
|
(LPTHREAD_START_ROUTINE)COMPipeBufferInThread,
|
||||||
|
(LPVOID)&bt,
|
||||||
|
0, NULL);
|
||||||
|
|
||||||
|
hOut = CreateThread(NULL, 0,
|
||||||
|
(LPTHREAD_START_ROUTINE)COMPipeBufferOutThread,
|
||||||
|
(LPVOID)&bt,
|
||||||
|
0, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (OSInfo.IsLinux)
|
||||||
{
|
{
|
||||||
unsigned long socketArgs[] = {
|
unsigned long socketArgs[] = {
|
||||||
(unsigned long)AF_UNIX,
|
(unsigned long)AF_UNIX,
|
||||||
@ -560,12 +769,14 @@ NewConnection:
|
|||||||
(LPTHREAD_START_ROUTINE)PipeBufferInThread,
|
(LPTHREAD_START_ROUTINE)PipeBufferInThread,
|
||||||
(LPVOID)&bt,
|
(LPVOID)&bt,
|
||||||
0, NULL);
|
0, NULL);
|
||||||
print("Created in thread %#lx\n", hIn);
|
|
||||||
|
|
||||||
hOut = CreateThread(NULL, 0,
|
hOut = CreateThread(NULL, 0,
|
||||||
(LPTHREAD_START_ROUTINE)PipeBufferOutThread,
|
(LPTHREAD_START_ROUTINE)PipeBufferOutThread,
|
||||||
(LPVOID)&bt,
|
(LPVOID)&bt,
|
||||||
0, NULL);
|
0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Created in thread %#lx\n", hIn);
|
||||||
print("Created out thread %#lx\n", hOut);
|
print("Created out thread %#lx\n", hOut);
|
||||||
|
|
||||||
if (hIn == NULL || hOut == NULL)
|
if (hIn == NULL || hOut == NULL)
|
||||||
@ -596,6 +807,7 @@ NewConnection:
|
|||||||
print("Failed to terminate thread: %s\n",
|
print("Failed to terminate thread: %s\n",
|
||||||
GetErrorMessage());
|
GetErrorMessage());
|
||||||
|
|
||||||
|
if (OSInfo.IsWine)
|
||||||
sys_close(fd);
|
sys_close(fd);
|
||||||
CloseHandle(hOut);
|
CloseHandle(hOut);
|
||||||
CloseHandle(hIn);
|
CloseHandle(hIn);
|
||||||
|
13
bridge.h
Normal file
13
bridge.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#ifndef _BRIDGE_H
|
||||||
|
#define _BRIDGE_H
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
typedef struct _OS_INFO
|
||||||
|
{
|
||||||
|
BOOL IsLinux;
|
||||||
|
BOOL IsDarwin;
|
||||||
|
BOOL IsWine;
|
||||||
|
} OS_INFO;
|
||||||
|
|
||||||
|
#endif // _BRIDGE_H
|
19
bridge.rc
19
bridge.rc
@ -2,8 +2,7 @@
|
|||||||
#include <winuser.h>
|
#include <winuser.h>
|
||||||
#include <winresrc.h>
|
#include <winresrc.h>
|
||||||
|
|
||||||
#define VER_VERSION 1,1,0,0
|
#include "resource.h"
|
||||||
#define VER_VERSION_STR "1.1\0"
|
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION VER_VERSION
|
FILEVERSION VER_VERSION
|
||||||
@ -33,6 +32,22 @@ BEGIN
|
|||||||
END
|
END
|
||||||
END
|
END
|
||||||
|
|
||||||
|
IDR_MAINMENU MENU
|
||||||
|
BEGIN
|
||||||
|
POPUP "&View"
|
||||||
|
BEGIN
|
||||||
|
MENUITEM "&Log", IDM_VIEW_LOG
|
||||||
|
END
|
||||||
|
POPUP "&Help"
|
||||||
|
BEGIN
|
||||||
|
MENUITEM "&Documentation", IDM_HELP_DOCUMENTATION
|
||||||
|
MENUITEM "&License", IDM_HELP_LICENSE
|
||||||
|
MENUITEM "&About", IDM_HELP_ABOUT
|
||||||
|
END
|
||||||
|
END
|
||||||
|
|
||||||
|
IDR_LICENSE_TXT RCDATA "LICENSE"
|
||||||
|
|
||||||
IDI_ICON_128 ICON "bridge.ico"
|
IDI_ICON_128 ICON "bridge.ico"
|
||||||
|
|
||||||
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST bridge.manifest
|
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST bridge.manifest
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
# This script is used to create a LaunchAgent on MacOS, to support the service functionnality.
|
# This script is used to create a LaunchAgent on MacOS, to support the service functionality.
|
||||||
# Usage: ./launchd.sh (install|remove)
|
# Usage: ./launchd.sh (install|remove)
|
||||||
|
|
||||||
SYMLINK=/tmp/rpc-bridge/tmpdir
|
SYMLINK=/tmp/rpc-bridge/tmpdir
|
||||||
@ -50,13 +50,12 @@ rm -rf "\$TARGET_DIR"
|
|||||||
</plist>" > "$AGENT"
|
</plist>" > "$AGENT"
|
||||||
launchctl load "$AGENT"
|
launchctl load "$AGENT"
|
||||||
echo "LaunchAgent has been installed."
|
echo "LaunchAgent has been installed."
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function remove() {
|
function remove() {
|
||||||
rm -rf "$SYMLINK"
|
rm -f "$SYMLINK"
|
||||||
rm -rf "$LOCATION"
|
|
||||||
rm -f "$SCRIPT"
|
rm -f "$SCRIPT"
|
||||||
|
rmdir "$LOCATION"
|
||||||
if [ -f "$AGENT" ]; then
|
if [ -f "$AGENT" ]; then
|
||||||
launchctl unload "$AGENT"
|
launchctl unload "$AGENT"
|
||||||
fi
|
fi
|
||||||
|
@ -64,14 +64,16 @@ This method is recommended because it's easier to manage.
|
|||||||
|
|
||||||
## macOS
|
## macOS
|
||||||
|
|
||||||
If using the default Wine prefix (`~/.wine`), you can follow the same steps as in Wine.
|
The steps for MacOS are almost the same, but due to the way `$TMPDIR` works, you will have to install a **LaunchAgent**.
|
||||||
|
|
||||||
Registering as a service [is not supported](https://github.com/EnderIce2/rpc-bridge/issues/1#issuecomment-2103423242 "Bridge can't get $TMPDIR unless is set with --rpc. See note below").
|
- Download the latest build from the [releases](https://github.com/EnderIce2/rpc-bridge/releases)
|
||||||
|
- Open the archive and make the `launchd.sh` script executable by doing: `chmod +x launchd.sh`
|
||||||
|
- To **install** the LaunchAgent, run `./launchd install` and to **remove** it simply run `./launchd remove`.
|
||||||
|
|
||||||
|
The script will add a LaunchAgent to your user, that will symlink the `$TMPDIR` directory to `/tmp/rpc-bridge/tmpdir`.
|
||||||
|
|
||||||
!!! info "Note"
|
!!! info "Note"
|
||||||
|
You will need to launch the `bridge.exe` file manually in Wine at least once for it to register and launch automatically the next time.
|
||||||
Since macOS doesn't have `/proc/self/environ` and `$TMPDIR` is neither always set nor the same after reboots, so you will need to [add `--rpc "$TMPDIR"` to the `bridge.exe` arguments](https://github.com/EnderIce2/rpc-bridge/issues/1#issuecomment-2104797235).
|
|
||||||
If you don't encounter any issues, you can ignore this step.
|
|
||||||
|
|
||||||
## Run without installing the service
|
## Run without installing the service
|
||||||
|
|
||||||
|
@ -13,6 +13,9 @@
|
|||||||
- `--help` Show help message
|
- `--help` Show help message
|
||||||
- This will show the help message
|
- This will show the help message
|
||||||
|
|
||||||
|
- `--version` Show version
|
||||||
|
- This will show the version of the program
|
||||||
|
|
||||||
- `--install` Install the service
|
- `--install` Install the service
|
||||||
- This will copy the binary to `C:\windows\bridge.exe` and register it as a service
|
- This will copy the binary to `C:\windows\bridge.exe` and register it as a service
|
||||||
|
|
||||||
|
112
gui.c
112
gui.c
@ -4,6 +4,9 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "bridge.h"
|
||||||
|
#include "resource.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The entire code could be better written, but at least it works.
|
* The entire code could be better written, but at least it works.
|
||||||
*
|
*
|
||||||
@ -15,7 +18,8 @@ void print(char const *fmt, ...);
|
|||||||
void InstallService(int ServiceStartType, LPCSTR Path);
|
void InstallService(int ServiceStartType, LPCSTR Path);
|
||||||
void RemoveService();
|
void RemoveService();
|
||||||
void CreateBridge();
|
void CreateBridge();
|
||||||
extern BOOL IsLinux;
|
extern OS_INFO OSInfo;
|
||||||
|
extern char *logFilePath;
|
||||||
|
|
||||||
HWND hwnd = NULL;
|
HWND hwnd = NULL;
|
||||||
HANDLE hBridge = NULL;
|
HANDLE hBridge = NULL;
|
||||||
@ -31,7 +35,7 @@ VOID HandleStartButton(BOOL Silent)
|
|||||||
SetWindowText(item, "Do you want to start, install or remove the bridge?");
|
SetWindowText(item, "Do you want to start, install or remove the bridge?");
|
||||||
RedrawWindow(item, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
|
RedrawWindow(item, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
|
||||||
item = GetDlgItem(hwnd, /* Start Button */ 1);
|
item = GetDlgItem(hwnd, /* Start Button */ 1);
|
||||||
Button_SetText(item, "Start");
|
Button_SetText(item, "&Start");
|
||||||
EnableWindow(item, FALSE);
|
EnableWindow(item, FALSE);
|
||||||
RedrawWindow(item, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
|
RedrawWindow(item, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
|
||||||
|
|
||||||
@ -47,21 +51,7 @@ VOID HandleStartButton(BOOL Silent)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsLinux)
|
SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
|
||||||
{
|
|
||||||
hBridge = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CreateBridge,
|
|
||||||
NULL, 0, NULL);
|
|
||||||
|
|
||||||
HWND item = GetDlgItem(hwnd, /* Start Button */ 1);
|
|
||||||
Button_SetText(item, "Stop");
|
|
||||||
item = GetDlgItem(hwnd, 4);
|
|
||||||
SetWindowText(item, "Bridge is running...");
|
|
||||||
IsAlreadyRunning = TRUE;
|
|
||||||
ShowWindow(hwnd, SW_MINIMIZE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
|
||||||
if (hSCManager == NULL)
|
if (hSCManager == NULL)
|
||||||
{
|
{
|
||||||
print("OpenSCManager failed: %s\n", GetErrorMessage());
|
print("OpenSCManager failed: %s\n", GetErrorMessage());
|
||||||
@ -72,7 +62,7 @@ VOID HandleStartButton(BOOL Silent)
|
|||||||
SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_START);
|
SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_START);
|
||||||
if (schService == NULL)
|
if (schService == NULL)
|
||||||
{
|
{
|
||||||
// print("OpenService failed: %s\n", GetErrorMessage());
|
print("Service doesn't exist: %s\n", GetErrorMessage());
|
||||||
|
|
||||||
/* Service doesn't exist; running without any service */
|
/* Service doesn't exist; running without any service */
|
||||||
|
|
||||||
@ -80,7 +70,7 @@ VOID HandleStartButton(BOOL Silent)
|
|||||||
NULL, 0, NULL);
|
NULL, 0, NULL);
|
||||||
|
|
||||||
HWND item = GetDlgItem(hwnd, /* Start Button */ 1);
|
HWND item = GetDlgItem(hwnd, /* Start Button */ 1);
|
||||||
Button_SetText(item, "Stop");
|
Button_SetText(item, "&Stop");
|
||||||
item = GetDlgItem(hwnd, 4);
|
item = GetDlgItem(hwnd, 4);
|
||||||
SetWindowText(item, "Bridge is running...");
|
SetWindowText(item, "Bridge is running...");
|
||||||
IsAlreadyRunning = TRUE;
|
IsAlreadyRunning = TRUE;
|
||||||
@ -115,6 +105,7 @@ VOID HandleStartButton(BOOL Silent)
|
|||||||
CloseServiceHandle(hSCManager);
|
CloseServiceHandle(hSCManager);
|
||||||
if (Silent == FALSE)
|
if (Silent == FALSE)
|
||||||
MessageBox(NULL, "Bridge service started successfully", "Info", MB_OK);
|
MessageBox(NULL, "Bridge service started successfully", "Info", MB_OK);
|
||||||
|
print("Bridge service started successfully\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID HandleInstallButton()
|
VOID HandleInstallButton()
|
||||||
@ -135,6 +126,44 @@ VOID HandleRemoveButton()
|
|||||||
ExitProcess(0);
|
ExitProcess(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShowLicenseDialog()
|
||||||
|
{
|
||||||
|
HMODULE hModule = GetModuleHandle(NULL);
|
||||||
|
HRSRC hRes = FindResource(hModule, MAKEINTRESOURCE(IDR_LICENSE_TXT), RT_RCDATA);
|
||||||
|
if (!hRes)
|
||||||
|
{
|
||||||
|
MessageBox(NULL, "Resource not found", "Error", MB_OK | MB_ICONERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HGLOBAL hResData = LoadResource(NULL, hRes);
|
||||||
|
if (!hResData)
|
||||||
|
{
|
||||||
|
MessageBox(NULL, "Resource failed to load", "Error", MB_OK | MB_ICONERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD resSize = SizeofResource(NULL, hRes);
|
||||||
|
void *pRes = LockResource(hResData);
|
||||||
|
if (!pRes)
|
||||||
|
{
|
||||||
|
MessageBox(NULL, "Resource failed to lock", "Error", MB_OK | MB_ICONERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *licenseText = (char *)malloc(resSize + 1);
|
||||||
|
if (!licenseText)
|
||||||
|
{
|
||||||
|
MessageBox(NULL, "Memory allocation failed", "Error", MB_OK | MB_ICONERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(licenseText, pRes, resSize);
|
||||||
|
licenseText[resSize] = '\0';
|
||||||
|
MessageBoxA(hwnd, licenseText, "About", MB_OK);
|
||||||
|
free(licenseText);
|
||||||
|
}
|
||||||
|
|
||||||
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
switch (msg)
|
switch (msg)
|
||||||
@ -152,6 +181,26 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|||||||
case 3:
|
case 3:
|
||||||
HandleRemoveButton();
|
HandleRemoveButton();
|
||||||
break;
|
break;
|
||||||
|
case IDM_VIEW_LOG:
|
||||||
|
ShellExecute(NULL, "open", "C:\\windows\\notepad.exe", logFilePath, NULL, SW_SHOW);
|
||||||
|
break;
|
||||||
|
case IDM_HELP_DOCUMENTATION:
|
||||||
|
ShellExecute(NULL, "open", "https://enderice2.github.io/rpc-bridge/index.html", NULL, NULL, SW_SHOWNORMAL);
|
||||||
|
break;
|
||||||
|
case IDM_HELP_LICENSE:
|
||||||
|
ShowLicenseDialog();
|
||||||
|
break;
|
||||||
|
case IDM_HELP_ABOUT:
|
||||||
|
{
|
||||||
|
char msg[256];
|
||||||
|
sprintf(msg, "rpc-bridge v%s\n\n"
|
||||||
|
"Simple bridge that allows you to use Discord Rich Presence with Wine games/software.\n\n"
|
||||||
|
"Created by EnderIce2\n\n"
|
||||||
|
"Licensed under the MIT License",
|
||||||
|
VER_VERSION_STR);
|
||||||
|
MessageBox(NULL, msg, "About", MB_OK);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -178,16 +227,9 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|||||||
|
|
||||||
VOID SetButtonStyles(INT *btnStartStyle, INT *btnRemoveStyle, INT *btnInstallStyle)
|
VOID SetButtonStyles(INT *btnStartStyle, INT *btnRemoveStyle, INT *btnInstallStyle)
|
||||||
{
|
{
|
||||||
*btnStartStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;
|
*btnStartStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP;
|
||||||
*btnRemoveStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;
|
*btnRemoveStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP;
|
||||||
*btnInstallStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;
|
*btnInstallStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP;
|
||||||
|
|
||||||
// if (!IsLinux)
|
|
||||||
// {
|
|
||||||
// *btnInstallStyle |= WS_DISABLED;
|
|
||||||
// *btnRemoveStyle |= WS_DISABLED;
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
||||||
SC_HANDLE schService = OpenService(hSCManager, "rpc-bridge", SERVICE_START | SERVICE_QUERY_STATUS);
|
SC_HANDLE schService = OpenService(hSCManager, "rpc-bridge", SERVICE_START | SERVICE_QUERY_STATUS);
|
||||||
@ -254,21 +296,24 @@ int WINAPI __WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
|||||||
0, 15, 400, 25,
|
0, 15, 400, 25,
|
||||||
hwnd, (HMENU)4, hInstance, NULL);
|
hwnd, (HMENU)4, hInstance, NULL);
|
||||||
|
|
||||||
HWND hbtn1 = CreateWindow("BUTTON", "Start",
|
HWND hbtn1 = CreateWindow("BUTTON", "&Start",
|
||||||
btnStartStyle,
|
btnStartStyle,
|
||||||
40, 60, 100, 30,
|
40, 60, 100, 30,
|
||||||
hwnd, (HMENU)1, hInstance, NULL);
|
hwnd, (HMENU)1, hInstance, NULL);
|
||||||
|
|
||||||
HWND hbtn2 = CreateWindow("BUTTON", "Install",
|
HWND hbtn2 = CreateWindow("BUTTON", "&Install",
|
||||||
btnInstallStyle,
|
btnInstallStyle,
|
||||||
150, 60, 100, 30,
|
150, 60, 100, 30,
|
||||||
hwnd, (HMENU)2, hInstance, NULL);
|
hwnd, (HMENU)2, hInstance, NULL);
|
||||||
|
|
||||||
HWND hbtn3 = CreateWindow("BUTTON", "Remove",
|
HWND hbtn3 = CreateWindow("BUTTON", "&Remove",
|
||||||
btnRemoveStyle,
|
btnRemoveStyle,
|
||||||
260, 60, 100, 30,
|
260, 60, 100, 30,
|
||||||
hwnd, (HMENU)3, hInstance, NULL);
|
hwnd, (HMENU)3, hInstance, NULL);
|
||||||
|
|
||||||
|
HMENU hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAINMENU));
|
||||||
|
SetMenu(hwnd, hMenu);
|
||||||
|
|
||||||
HDC hDC = GetDC(hwnd);
|
HDC hDC = GetDC(hwnd);
|
||||||
int nHeight = -MulDiv(11, GetDeviceCaps(hDC, LOGPIXELSY), 72);
|
int nHeight = -MulDiv(11, GetDeviceCaps(hDC, LOGPIXELSY), 72);
|
||||||
|
|
||||||
@ -288,10 +333,13 @@ int WINAPI __WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
|||||||
|
|
||||||
MSG msg;
|
MSG msg;
|
||||||
while (GetMessage(&msg, NULL, 0, 0) > 0)
|
while (GetMessage(&msg, NULL, 0, 0) > 0)
|
||||||
|
{
|
||||||
|
if (!IsDialogMessage(hwnd, &msg))
|
||||||
{
|
{
|
||||||
TranslateMessage(&msg);
|
TranslateMessage(&msg);
|
||||||
DispatchMessage(&msg);
|
DispatchMessage(&msg);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return msg.wParam;
|
return msg.wParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
100
kvm.c
Normal file
100
kvm.c
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#if defined(__linux__)
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
void transferData(int fromSock, int toSock)
|
||||||
|
{
|
||||||
|
char buffer[256];
|
||||||
|
ssize_t bytesRead;
|
||||||
|
while ((bytesRead = read(fromSock, buffer, sizeof(buffer))) > 0)
|
||||||
|
{
|
||||||
|
write(toSock, buffer, bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int connectToSocket(const char *socketPath)
|
||||||
|
{
|
||||||
|
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
assert(sock >= 0);
|
||||||
|
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
strncpy(addr.sun_path, socketPath, sizeof(addr.sun_path) - 1);
|
||||||
|
|
||||||
|
int result = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
|
||||||
|
assert(result == 0);
|
||||||
|
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kvmSock;
|
||||||
|
int discordSock;
|
||||||
|
void sigintHandler(int sig)
|
||||||
|
{
|
||||||
|
close(kvmSock);
|
||||||
|
close(discordSock);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
signal(SIGINT, sigintHandler);
|
||||||
|
|
||||||
|
const char *kvmSocketPath = "/tmp/kvm-rpc-bridge";
|
||||||
|
const char *discordSocketPath = getenv("XDG_RUNTIME_DIR");
|
||||||
|
if (discordSocketPath == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "XDG_RUNTIME_DIR environment variable not set\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
char discordSocketFullPath[256];
|
||||||
|
snprintf(discordSocketFullPath, sizeof(discordSocketFullPath), "%s/discord-ipc-0", discordSocketPath);
|
||||||
|
|
||||||
|
printf("Trying Discord socket at %s\n", discordSocketFullPath);
|
||||||
|
discordSock = connectToSocket(discordSocketFullPath);
|
||||||
|
printf("Connected to Discord socket successfully\n");
|
||||||
|
|
||||||
|
// Connect to KVM socket
|
||||||
|
printf("Trying KVM socket at %s\n", kvmSocketPath);
|
||||||
|
kvmSock = connectToSocket(kvmSocketPath);
|
||||||
|
|
||||||
|
// Read specific message from KVM and send confirmation
|
||||||
|
const char kvmExpectedMsg[] = {0x69, 0x15, 0xBB};
|
||||||
|
const char kvmResponseMsg[] = {0x6E, 0x1D, 0xB4};
|
||||||
|
|
||||||
|
char buffer[3];
|
||||||
|
ssize_t bytesRead = read(kvmSock, buffer, sizeof(buffer));
|
||||||
|
printf("Read %ld bytes from KVM (%x %x %x)\n", bytesRead, buffer[0], buffer[1], buffer[2]);
|
||||||
|
assert(bytesRead == sizeof(kvmExpectedMsg));
|
||||||
|
assert(memcmp(buffer, kvmExpectedMsg, sizeof(kvmExpectedMsg)) == 0);
|
||||||
|
|
||||||
|
ssize_t bytesSent = write(kvmSock, kvmResponseMsg, sizeof(kvmResponseMsg));
|
||||||
|
assert(bytesSent == sizeof(kvmResponseMsg));
|
||||||
|
printf("Connected to KVM socket successfully\n");
|
||||||
|
|
||||||
|
// Transfer data between KVM and Discord sockets
|
||||||
|
if (fork() == 0)
|
||||||
|
{
|
||||||
|
// Child process: transfer data from KVM to Discord
|
||||||
|
transferData(kvmSock, discordSock);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Parent process: transfer data from Discord to KVM
|
||||||
|
transferData(discordSock, kvmSock);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(kvmSock);
|
||||||
|
close(discordSock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __linux__
|
91
main.c
91
main.c
@ -3,8 +3,12 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "bridge.h"
|
||||||
|
#include "resource.h"
|
||||||
|
|
||||||
FILE *g_logFile = NULL;
|
FILE *g_logFile = NULL;
|
||||||
BOOL RunningAsService = FALSE;
|
BOOL RunningAsService = FALSE;
|
||||||
|
char *logFilePath = NULL;
|
||||||
|
|
||||||
void CreateGUI();
|
void CreateGUI();
|
||||||
void CreateBridge();
|
void CreateBridge();
|
||||||
@ -13,7 +17,7 @@ void ServiceMain(int argc, char *argv[]);
|
|||||||
void InstallService(int ServiceStartType, LPCSTR Path);
|
void InstallService(int ServiceStartType, LPCSTR Path);
|
||||||
char *native_getenv(const char *name);
|
char *native_getenv(const char *name);
|
||||||
void RemoveService();
|
void RemoveService();
|
||||||
extern BOOL IsLinux;
|
extern OS_INFO OSInfo;
|
||||||
|
|
||||||
LPTSTR GetErrorMessage()
|
LPTSTR GetErrorMessage()
|
||||||
{
|
{
|
||||||
@ -55,11 +59,8 @@ void DetectWine()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!GetProcAddress(hNTdll, "wine_get_version"))
|
if (!GetProcAddress(hNTdll, "wine_get_version"))
|
||||||
{
|
return;
|
||||||
MessageBox(NULL, "This program is only intended to run under Wine.",
|
OSInfo.IsWine = TRUE;
|
||||||
GetErrorMessage(), MB_OK | MB_ICONINFORMATION);
|
|
||||||
ExitProcess(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void(CDECL * wine_get_host_version)(const char **sysname, const char **release);
|
static void(CDECL * wine_get_host_version)(const char **sysname, const char **release);
|
||||||
wine_get_host_version = (void *)GetProcAddress(hNTdll, "wine_get_host_version");
|
wine_get_host_version = (void *)GetProcAddress(hNTdll, "wine_get_host_version");
|
||||||
@ -72,14 +73,12 @@ void DetectWine()
|
|||||||
{
|
{
|
||||||
int result = MessageBox(NULL, "This program is designed for Linux and macOS only!\nDo you want to proceed?",
|
int result = MessageBox(NULL, "This program is designed for Linux and macOS only!\nDo you want to proceed?",
|
||||||
NULL, MB_YESNO | MB_ICONQUESTION);
|
NULL, MB_YESNO | MB_ICONQUESTION);
|
||||||
if (result == IDYES)
|
if (result == IDNO)
|
||||||
return;
|
|
||||||
else if (result == IDNO)
|
|
||||||
ExitProcess(1);
|
ExitProcess(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
IsLinux = strcmp(__sysname, "Linux") == 0;
|
OSInfo.IsLinux = strcmp(__sysname, "Linux") == 0;
|
||||||
printf("Running on %s\n", __sysname);
|
OSInfo.IsDarwin = strcmp(__sysname, "Darwin") == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void print(char const *fmt, ...)
|
void print(char const *fmt, ...)
|
||||||
@ -114,14 +113,13 @@ void HandleArguments(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
else if (strcmp(argv[1], "--steam") == 0)
|
else if (strcmp(argv[1], "--steam") == 0)
|
||||||
{
|
{
|
||||||
|
assert(OSInfo.IsWine == TRUE);
|
||||||
|
|
||||||
/* All this mess just so when you close the game,
|
/* All this mess just so when you close the game,
|
||||||
it automatically closes the bridge and Steam
|
it automatically closes the bridge and Steam
|
||||||
will not say that the game is still running. */
|
will not say that the game is still running. */
|
||||||
|
|
||||||
print("Running as Steam\n");
|
print("Running as Steam\n");
|
||||||
if (IsLinux == FALSE)
|
|
||||||
CreateBridge();
|
|
||||||
|
|
||||||
if (argc > 2)
|
if (argc > 2)
|
||||||
{
|
{
|
||||||
if (strcmp(argv[2], "--no-service") == 0)
|
if (strcmp(argv[2], "--no-service") == 0)
|
||||||
@ -272,32 +270,40 @@ void HandleArguments(int argc, char *argv[])
|
|||||||
CreateBridge();
|
CreateBridge();
|
||||||
ExitProcess(0);
|
ExitProcess(0);
|
||||||
}
|
}
|
||||||
|
else if (strcmp(argv[1], "--version") == 0)
|
||||||
|
{
|
||||||
|
printf("%s\n", VER_VERSION_STR);
|
||||||
|
ExitProcess(0);
|
||||||
|
}
|
||||||
else if (strcmp(argv[1], "--help") == 0)
|
else if (strcmp(argv[1], "--help") == 0)
|
||||||
{
|
{
|
||||||
printf("Usage:\n");
|
printf("Usage:\n"
|
||||||
printf(" %s [args]\n\n", argv[0]);
|
" %s [args]\n"
|
||||||
|
"\n"
|
||||||
printf("Arguments:\n");
|
"Arguments:\n"
|
||||||
printf(" --help Show this help\n\n");
|
" --help Show this help\n"
|
||||||
|
"\n"
|
||||||
printf(" --install Install service\n");
|
" --version Show version\n"
|
||||||
printf(" This will copy the binary to C:\\windows\\bridge.exe and register it as a service\n\n");
|
"\n"
|
||||||
|
" --install Install service\n"
|
||||||
printf(" --uninstall Uninstall service\n");
|
" This will copy the binary to C:\\windows\\bridge.exe and register it as a service\n"
|
||||||
printf(" This will remove the service and delete C:\\windows\\bridge.exe\n\n");
|
"\n"
|
||||||
|
" --uninstall Uninstall service\n"
|
||||||
printf(" --steam Reserved for Steam\n");
|
" This will remove the service and delete C:\\windows\\bridge.exe\n"
|
||||||
printf(" This will start the service and exit (used with bridge.sh)\n\n");
|
"\n"
|
||||||
|
" --steam Reserved for Steam\n"
|
||||||
printf(" --no-service Do not run as service\n");
|
" This will start the service and exit (used with bridge.sh)\n"
|
||||||
printf(" (only for --steam)\n\n");
|
"\n"
|
||||||
|
" --no-service Do not run as service\n"
|
||||||
printf(" --service Reserved for service\n\n");
|
" (only for --steam)\n"
|
||||||
|
"\n"
|
||||||
printf(" --rpc <dir> Set RPC_PATH environment variable\n");
|
" --service Reserved for service\n"
|
||||||
printf(" This is used to specify the directory where 'discord-ipc-0' is located\n\n");
|
"\n"
|
||||||
|
" --rpc <dir> Set RPC_PATH environment variable\n"
|
||||||
printf("Note: If no arguments are provided, the GUI will be shown instead\n");
|
" This is used to specify the directory where 'discord-ipc-0' is located\n"
|
||||||
|
"\n"
|
||||||
|
"Note: If no arguments are provided, the GUI will be shown instead\n",
|
||||||
|
argv[0]);
|
||||||
ExitProcess(0);
|
ExitProcess(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -305,7 +311,14 @@ void HandleArguments(int argc, char *argv[])
|
|||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
DetectWine();
|
DetectWine();
|
||||||
char *logFilePath = "C:\\windows\\logs\\bridge.log";
|
if (OSInfo.IsWine)
|
||||||
|
logFilePath = "C:\\windows\\logs\\bridge.log";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logFilePath = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH);
|
||||||
|
GetTempPath(MAX_PATH, logFilePath);
|
||||||
|
strcat_s(logFilePath, MAX_PATH, "bridge.log");
|
||||||
|
}
|
||||||
g_logFile = fopen(logFilePath, "w");
|
g_logFile = fopen(logFilePath, "w");
|
||||||
if (g_logFile == NULL)
|
if (g_logFile == NULL)
|
||||||
{
|
{
|
||||||
|
9
resource.h
Normal file
9
resource.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#define IDR_MAINMENU 101
|
||||||
|
#define IDR_LICENSE_TXT 102
|
||||||
|
#define IDM_HELP_DOCUMENTATION 40001
|
||||||
|
#define IDM_HELP_LICENSE 40002
|
||||||
|
#define IDM_HELP_ABOUT 40003
|
||||||
|
#define IDM_VIEW_LOG 40004
|
||||||
|
|
||||||
|
#define VER_VERSION 1, 1, 0, 0
|
||||||
|
#define VER_VERSION_STR "1.1\0"
|
@ -8,7 +8,6 @@ SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
|
|||||||
void print(char const *fmt, ...);
|
void print(char const *fmt, ...);
|
||||||
void CreateBridge();
|
void CreateBridge();
|
||||||
LPTSTR GetErrorMessage();
|
LPTSTR GetErrorMessage();
|
||||||
extern BOOL IsLinux;
|
|
||||||
|
|
||||||
void WINAPI ServiceCtrlHandler(DWORD CtrlCode)
|
void WINAPI ServiceCtrlHandler(DWORD CtrlCode)
|
||||||
{
|
{
|
||||||
@ -88,14 +87,6 @@ void InstallService(int ServiceStartType, LPCSTR Path)
|
|||||||
{
|
{
|
||||||
print("Registering service\n");
|
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);
|
|
||||||
// }
|
|
||||||
|
|
||||||
SC_HANDLE schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
|
SC_HANDLE schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
|
||||||
if (schSCManager == NULL)
|
if (schSCManager == NULL)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user