Compare commits

...

37 Commits
v1.2 ... master

Author SHA1 Message Date
c1078b5b8b
fix: 🐛 mp4 is detected as audio
Some checks failed
Build Project / build (push) Has been cancelled
Deploy Documentation / deploy (push) Has been cancelled
Changed video to webm, maybe this will work.
2025-04-25 06:45:30 +03:00
1de80cdbb9
fix: 🐛 update mkdocs-video plugin configuration to force mp4 type 2025-04-25 06:35:52 +03:00
975ace70f9
fix: 🎨 correct shebang formatting in bridge.sh 2025-04-25 04:31:47 +03:00
ad076dfac7
docs: 📝 improve documentation to be more detailed
Included video tutorial link for installing the LaunchAgent and bridge inside CrossOver.
2025-04-25 04:29:35 +03:00
8c54d5bb2f
Fix theme toggle names and icons in light and dark modes 2025-04-25 03:43:35 +03:00
a0615229ef
Add installation checks to launchd.sh for improved user feedback 2025-04-25 03:42:34 +03:00
18c422101b
Remove obsolete installation and removal scripts 2025-04-25 03:41:09 +03:00
77a2a748ad
Fix macos version
Some checks failed
Build Project / build (push) Has been cancelled
2025-04-09 10:03:54 +03:00
0d4f46e536
Fix build error #14 2025-04-09 06:38:25 +03:00
d065019137
Fix formatting in documentation
Some checks failed
Build Project / build (push) Has been cancelled
Deploy Documentation / deploy (push) Has been cancelled
2025-04-08 12:11:52 +03:00
e03dce1a7c
Improve formatting of installation instructions in documentation 2025-04-08 12:09:38 +03:00
7c806779d2
Add link to latest pre-release in documentation 2025-04-08 12:05:58 +03:00
885cf775f9
Remove link to latest pre-release in documentation 2025-04-08 12:05:04 +03:00
7c07952df1
Add paths-ignore to GitHub Actions for build workflow 2025-04-08 12:04:16 +03:00
ea566cfdeb
Update documentation links in index.md 2025-04-08 12:00:56 +03:00
d56de21e74
Refactor GitHub Actions workflows 2025-04-08 11:58:50 +03:00
26a016cb26
Bump version 2025-04-08 11:51:50 +03:00
edfcce2ae9
Update copyright year in bridge.rc to 2025
[no ci]
2025-04-08 11:51:02 +03:00
9bec33c358
Update bug_report.md 2025-04-08 11:36:51 +03:00
3e6b5dbb43
Merge branch 'master' of https://github.com/EnderIce2/rpc-bridge 2025-04-08 11:14:41 +03:00
4c432a0e50
Fix #12, socket -14 (EFAULT) 2025-04-08 11:14:32 +03:00
65b5625a57
Update error message for Wine compatibility check 2025-04-08 08:21:03 +03:00
EnderIce2
9d2440f616
Update LICENSE
Some checks failed
Build and Deploy docs / deploy (push) Failing after 12m12s
Build and Deploy docs / build (push) Failing after 6m5s
2025-01-04 06:55:43 +02:00
EnderIce2
51cbb666f9
Update build-deploy.yml
Some checks failed
Build and Deploy docs / build (push) Failing after 4m46s
Build and Deploy docs / deploy (push) Failing after 48s
2024-12-30 00:27:21 +02:00
EnderIce2
1deccc5a63
Update CODE_OF_CONDUCT.md
Some checks failed
Build and Deploy docs / build (push) Failing after 28s
Build and Deploy docs / deploy (push) Failing after 34s
2024-12-11 05:54:45 +02:00
EnderIce2
3412c3c011
Update issue templates
Some checks failed
Build and Deploy docs / build (push) Has been cancelled
Build and Deploy docs / deploy (push) Has been cancelled
2024-10-30 16:52:46 +02:00
EnderIce2
c15c4df29e
Create CODE_OF_CONDUCT.md 2024-10-30 16:39:46 +02:00
EnderIce2
3a0b9ddb2a
Ensure log file is properly closed
Some checks are pending
Build and Deploy docs / build (push) Waiting to run
Build and Deploy docs / deploy (push) Waiting to run
* Closing the log before service starts prevents log file to be corrupted (like in #7)
2024-10-30 01:08:12 +02:00
EnderIce2
8786efd144
Fix formatting and add game links 2024-06-02 17:52:52 +03:00
EnderIce2
2024de9408
Add "Known Issues" section
* Using Vesktop may cause issues with some games #4
2024-06-02 17:45:48 +03:00
EnderIce2
e8926edb41
Add more screenshots
* Content Warning
* Hades
* Lethal Company
* vivid/stasis
2024-06-02 17:41:16 +03:00
EnderIce2
9f1a9de3d7
Update actions/checkout and actions/upload-artifact to v4 2024-06-01 19:57:30 +03:00
EnderIce2
ccf09806c9
Print version on program start 2024-06-01 19:39:39 +03:00
EnderIce2
7363ee64d5
Update bridge.manifest with correct assembly name 2024-06-01 19:35:01 +03:00
EnderIce2
a3023e349e
Update gui.png 2024-06-01 02:56:12 +03:00
EnderIce2
39966d7149
Update Discord socket search
* Test multiple instances
* Add support for Discord Canary (Snap)
* Add support for Vesktop (Flatpak)
2024-06-01 01:37:40 +03:00
EnderIce2
a8904bf3f1
Update version 2024-06-01 00:57:06 +03:00
31 changed files with 582 additions and 200 deletions

31
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,31 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: EnderIce2
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Log File**
Located in `/path/to/prefix/drive_c/windows/logs/bridge.log` or a screenshot with the terminal should suffice.
**System Info (please complete the following information):**
- OS: [e.g. SteamOS 3.0, Ubuntu 22.04, macOS 15, or output from command `uname -srm`]
- Wine: [e.g. 10.5]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: EnderIce2
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

35
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,35 @@
name: Build Project
on:
push:
branches: [ "master" ]
paths-ignore:
- 'ISSUE_TEMPLATE/**'
- 'workflows/**'
- '.vsocde/**'
- 'docs/**'
pull_request:
branches: [ "master" ]
paths-ignore:
- 'ISSUE_TEMPLATE/**'
- 'workflows/**'
- '.vsocde/**'
- 'docs/**'
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: dependencies
run: sudo apt update && sudo apt -y install gcc-mingw-w64 make
- name: make
run: make
- name: artifact
uses: actions/upload-artifact@v4
with:
name: bridge
path: build

View File

@ -1,29 +1,21 @@
name: Build and Deploy docs
name: Deploy Documentation
on:
push:
branches: [ "master" ]
branches: [ master ]
paths:
- docs/**
- mkdocs.yml
pull_request:
branches: [ "master" ]
paths:
- docs/**
- mkdocs.yml
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: dependencies
run: sudo apt-get update && sudo apt-get install gcc-mingw-w64 make
- name: make
run: make
- name: artifact
uses: actions/upload-artifact@v3
with:
name: bridge
path: build
deploy:
runs-on: ubuntu-latest
steps:
@ -43,4 +35,5 @@ jobs:
restore-keys: |
mkdocs-material-
- run: pip install mkdocs-material
- run: pip install mkdocs-video
- run: mkdocs gh-deploy --force

View File

@ -5,7 +5,10 @@
"includePath": [
"${workspaceFolder}/**"
],
"defines": [],
"defines": [
"GIT_COMMIT",
"GIT_BRANCH"
],
"cStandard": "c17",
"cppStandard": "gnu++17",
"intelliSenseMode": "windows-gcc-x64"

128
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
enderice2@protonmail.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@ -1,4 +1,4 @@
Copyright (c) 2024 EnderIce2
Copyright (c) 2025 EnderIce2
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,8 +1,13 @@
C_SOURCES = $(shell find ./ -type f -name '*.c')
C_OBJECTS = $(C_SOURCES:.c=.o)
CFLAGS = -std=c17 -Wno-int-conversion
LFLAGS = -lgdi32
GIT_COMMIT = $(shell git rev-parse --short HEAD)
GIT_BRANCH = $(shell git rev-parse --abbrev-ref HEAD)
CWARNFLAGS = -Wno-int-conversion -Wno-incompatible-pointer-types
CFLAGS = -std=c17 -DGIT_COMMIT='"$(GIT_COMMIT)"' -DGIT_BRANCH='"$(GIT_BRANCH)"'
LFLAGS = -lgdi32 -lws2_32
# DBGFLAGS = -Wl,--export-all-symbols -g -O0 -ggdb3 -Wall
@ -15,7 +20,7 @@ build: $(C_OBJECTS)
%.o: %.c
$(info Compiling $<)
x86_64-w64-mingw32-gcc $(CFLAGS) $(DBGFLAGS) -c $< -o $@
x86_64-w64-mingw32-gcc $(CFLAGS) $(CWARNFLAGS) $(DBGFLAGS) -c $< -o $@
clean:
rm -f $(C_OBJECTS) build/bridge.exe bridge.res

View File

@ -58,12 +58,14 @@ The steps for MacOS are almost the same, but due to the way `$TMPDIR` works, you
- 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`.
- To **install** the LaunchAgent, run `./launchd.sh install` and to **remove** it simply run `./launchd.sh remove`.
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 at least once for it to register and launch automatically the next time.*
More details on how to install the LaunchAgent can be found in the [documentation](https://enderice2.github.io/rpc-bridge/).
## Compiling from source
- Install the `wine`, `gcc-mingw-w64` and `make` packages.

194
bridge.c
View File

@ -20,11 +20,13 @@
#define __darwin_close 0x2000006
#define __darwin_socket 0x2000061
#define __darwin_connect 0x2000062
#define __darwin_mmap 0x20000C5
#define __darwin_fcntl 0x200005C
#define __darwin_sysctl 0x20000CA
#define O_RDONLY 00
/* macos & linux are the same for PROT_READ, PROT_WRITE, MAP_FIXED & MAP_PRIVATE */
#define PROT_READ 1
#define PROT_WRITE 2
#define MAP_PRIVATE 0x02
@ -32,6 +34,8 @@
#define MAP_ANON 0x20
#define MAP_FAILED ((void *)-1)
#define __darwin_MAP_ANON 0x1000
#define SYS_SOCKET 1
#define SYS_CONNECT 3
@ -134,8 +138,17 @@ 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)
{
assert(IsLinux);
return linux_syscall(__linux_mmap2, addr, length, prot, flags, fd, offset);
if (IsLinux)
return linux_syscall(__linux_mmap2, addr, length, prot, flags, fd, offset);
else
{
if (flags & MAP_ANON)
{
flags &= ~MAP_ANON;
flags |= __darwin_MAP_ANON;
}
return darwin_syscall(__darwin_mmap, addr, length, prot, flags, fd, offset);
}
}
static inline int sys_munmap(unsigned int *addr, size_t length)
@ -195,11 +208,7 @@ char *native_getenv(const char *name)
/* I hope the 0x20000 is okay */
if (environStr == NULL)
{
environStr = sys_mmap(0x20000, 4096, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
print("Allocated 4096 bytes at %#lx\n", environStr);
}
environStr = sys_mmap(0x20000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
if ((uintptr_t)environStr > 0x7effffff)
print("Warning: environStr %#lx is above 2GB\n", environStr);
@ -209,18 +218,17 @@ char *native_getenv(const char *name)
int fd = sys_open(environStr, O_RDONLY, 0);
// sys_munmap(environStr, 4096);
if (fd < 0)
{
print("Failed to open /proc/self/environ: %d\n", fd);
return NULL;
}
char buffer[4096];
char *buffer = sys_mmap(0x22000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
char *result = NULL;
int bytesRead;
while ((bytesRead = sys_read(fd, buffer, sizeof(buffer) - 1)) > 0)
while ((bytesRead = sys_read(fd, buffer, 0x1000 - 1)) > 0)
{
buffer[bytesRead] = '\0';
char *env = buffer;
@ -282,41 +290,68 @@ void ConnectToSocket(int fd)
print("IPC directory: %s\n", runtime);
/* TODO: check for multiple discord instances and create a pipe for each */
const char *discordUnixPipes[] = {
"/discord-ipc-0",
"/snap.discord/discord-ipc-0",
"/app/com.discordapp.Discord/discord-ipc-0",
const char *discordUnixSockets[] = {
"%s/discord-ipc-%d",
"%s/app/com.discordapp.Discord/discord-ipc-%d",
"%s/.flatpak/dev.vencord.Vesktop/xdg-run/discord-ipc-%d",
"%s/snap.discord/discord-ipc-%d",
"%s/snap.discord-canary/discord-ipc-%d",
};
struct sockaddr_un socketAddr;
socketAddr.sun_family = AF_UNIX;
char *pipePath = NULL;
int sockRet = -1;
for (int i = 0; i < sizeof(discordUnixPipes) / sizeof(discordUnixPipes[0]); i++)
int sockRet = 0;
for (int i = 0; i < sizeof(discordUnixSockets) / sizeof(discordUnixSockets[0]); i++)
{
pipePath = malloc(strlen(runtime) + strlen(discordUnixPipes[i]) + 1);
strcpy(pipePath, runtime);
strcat(pipePath, discordUnixPipes[i]);
strcpy_s(socketAddr.sun_path, sizeof(socketAddr.sun_path), pipePath);
size_t pipePathLen = strlen(runtime) + strlen(discordUnixSockets[i]) + 1;
char *pipePath = malloc(pipePathLen);
print("Connecting to %s\n", pipePath);
if (IsLinux)
for (int j = 0; j < 16; j++)
{
unsigned long socketArgs[] = {
(unsigned long)fd,
(unsigned long)(intptr_t)&socketAddr,
sizeof(socketAddr)};
if (IsLinux)
{
struct sockaddr_un *socketAddr = sys_mmap(0x23000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
print("Socket address allocated at %#lx\n", socketAddr);
socketAddr->sun_family = AF_UNIX;
sockRet = sys_socketcall(SYS_CONNECT, socketArgs);
snprintf(pipePath, pipePathLen, discordUnixSockets[i], runtime, j);
strcpy_s(socketAddr->sun_path, sizeof(socketAddr->sun_path), pipePath);
print("Probing %s\n", pipePath);
// unsigned long socketArgs[] = {
// (unsigned long)fd,
// (unsigned long)(intptr_t)&socketAddr,
// sizeof(socketAddr)};
unsigned long *socketArgs = sys_mmap(0x24000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
socketArgs[0] = (unsigned long)fd;
socketArgs[1] = (unsigned long)(intptr_t)socketAddr;
socketArgs[2] = sizeof(struct sockaddr_un);
socketArgs[3] = 0;
sockRet = sys_socketcall(SYS_CONNECT, socketArgs);
}
else
{
struct sockaddr_un socketAddr;
socketAddr.sun_family = AF_UNIX;
snprintf(pipePath, pipePathLen, discordUnixSockets[i], runtime, j);
strcpy_s(socketAddr.sun_path, sizeof(socketAddr.sun_path), pipePath);
print("Probing %s\n", pipePath);
sockRet = sys_connect(fd, (caddr_t)&socketAddr, sizeof(socketAddr));
}
print(" error: %d\n", sockRet);
if (sockRet >= 0)
break;
}
else
sockRet = sys_connect(fd, (caddr_t)&socketAddr, sizeof(socketAddr));
free(pipePath);
if (sockRet >= 0)
{
print("Connecting to %s\n", pipePath);
free(pipePath);
break;
}
free(pipePath);
}
if (sockRet < 0)
@ -333,18 +368,22 @@ void ConnectToSocket(int fd)
void PipeBufferInThread(LPVOID lpParam)
{
bridge_thread *bt = (bridge_thread *)lpParam;
print("In thread started using fd %d and pipe %#x\n",
bt->fd, bt->hPipe);
print("In thread started using fd %d and pipe %#x\n", bt->fd, bt->hPipe);
int EOFCount = 0;
char *l_buffer;
if (IsLinux)
l_buffer = sys_mmap(0x25000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
else
l_buffer = malloc(BUFFER_LENGTH);
print("Buffer in thread allocated at %#lx\n", l_buffer);
while (TRUE)
{
char buffer[BUFFER_LENGTH];
int read = sys_read(bt->fd, buffer, sizeof(buffer));
int read = sys_read(bt->fd, l_buffer, BUFFER_LENGTH);
if (unlikely(read < 0))
{
print("Failed to read from unix pipe: %d\n",
GetErrorMessage());
print("Failed to read from unix pipe: %d\n", read);
Sleep(1000);
continue;
}
@ -366,14 +405,16 @@ void PipeBufferInThread(LPVOID lpParam)
}
EOFCount = 0;
memcpy(buffer, l_buffer, read);
print("Reading %d bytes from unix pipe: \"", read);
for (int i = 0; i < read; i++)
print("%c", buffer[i]);
print("\"\n");
DWORD dwWritten;
if (unlikely(!WriteFile(bt->hPipe, buffer, read,
&dwWritten, NULL)))
WINBOOL bResult = WriteFile(bt->hPipe, buffer, read, &dwWritten, NULL);
if (unlikely(bResult == FALSE))
{
if (GetLastError() == ERROR_BROKEN_PIPE)
{
@ -382,16 +423,14 @@ void PipeBufferInThread(LPVOID lpParam)
break;
}
print("Failed to read from pipe: %d\n",
GetErrorMessage());
print("Failed to read from pipe: %s\n", GetErrorMessage());
Sleep(1000);
continue;
}
if (unlikely(dwWritten < 0))
{
print("Failed to write to pipe: %d\n",
GetErrorMessage());
print("Failed to write to pipe: %s\n", GetErrorMessage());
Sleep(1000);
continue;
}
@ -399,8 +438,8 @@ void PipeBufferInThread(LPVOID lpParam)
while (dwWritten < read)
{
int last_written = dwWritten;
if (unlikely(!WriteFile(bt->hPipe, buffer + dwWritten,
read - dwWritten, &dwWritten, NULL)))
WINBOOL bResult = WriteFile(bt->hPipe, buffer + dwWritten, read - dwWritten, &dwWritten, NULL);
if (unlikely(bResult == FALSE))
{
if (GetLastError() == ERROR_BROKEN_PIPE)
{
@ -409,16 +448,14 @@ void PipeBufferInThread(LPVOID lpParam)
break;
}
print("Failed to read from pipe: %d\n",
GetErrorMessage());
print("Failed to read from pipe: %s\n", GetErrorMessage());
Sleep(1000);
continue;
}
if (unlikely(last_written == dwWritten))
{
print("Failed to write to pipe: %d\n",
GetErrorMessage());
print("Failed to write to pipe: %s\n", GetErrorMessage());
Sleep(1000);
continue;
}
@ -429,15 +466,19 @@ void PipeBufferInThread(LPVOID lpParam)
void PipeBufferOutThread(LPVOID lpParam)
{
bridge_thread *bt = (bridge_thread *)lpParam;
print("Out thread started using fd %d and pipe %#x\n",
bt->fd, bt->hPipe);
print("Out thread started using fd %d and pipe %#x\n", bt->fd, bt->hPipe);
char *l_buffer;
if (IsLinux)
l_buffer = sys_mmap(0x26000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
else
l_buffer = malloc(BUFFER_LENGTH);
print("Buffer out thread allocated at %#lx\n", l_buffer);
while (TRUE)
{
char buffer[BUFFER_LENGTH];
DWORD dwRead;
if (unlikely(!ReadFile(bt->hPipe, buffer, sizeof(buffer),
&dwRead, NULL)))
WINBOOL bResult = ReadFile(bt->hPipe, buffer, BUFFER_LENGTH, &dwRead, NULL);
if (unlikely(bResult == FALSE))
{
if (GetLastError() == ERROR_BROKEN_PIPE)
{
@ -446,8 +487,7 @@ void PipeBufferOutThread(LPVOID lpParam)
break;
}
print("Failed to read from pipe: %d\n",
GetErrorMessage());
print("Failed to read from pipe: %s\n", GetErrorMessage());
Sleep(1000);
continue;
}
@ -457,11 +497,11 @@ void PipeBufferOutThread(LPVOID lpParam)
print("%c", buffer[i]);
print("\"\n");
int written = sys_write(bt->fd, buffer, dwRead);
memcpy(l_buffer, buffer, dwRead);
int written = sys_write(bt->fd, l_buffer, dwRead);
if (unlikely(written < 0))
{
print("Failed to write to socket: %d\n",
written);
print("Failed to write to socket: %d\n", written);
continue;
}
@ -471,8 +511,7 @@ void PipeBufferOutThread(LPVOID lpParam)
written += sys_write(bt->fd, buffer + written, dwRead - written);
if (unlikely(last_written == written))
{
print("Failed to write to socket: %d\n",
GetErrorMessage());
print("Failed to write to socket: %s\n", GetErrorMessage());
Sleep(1000);
continue;
}
@ -535,16 +574,31 @@ NewConnection:
int fd;
if (IsLinux)
{
unsigned long socketArgs[] = {
(unsigned long)AF_UNIX,
(unsigned long)SOCK_STREAM,
0};
// unsigned long socketArgs[] = {
// (unsigned long)AF_UNIX,
// (unsigned long)SOCK_STREAM,
// 0};
unsigned long *socketArgs = sys_mmap(0x21000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
socketArgs[0] = (unsigned long)AF_UNIX;
socketArgs[1] = (unsigned long)SOCK_STREAM;
socketArgs[2] = 0;
fd = sys_socketcall(SYS_SOCKET, socketArgs);
/* FIXME: WSAEAFNOSUPPORT: https://gitlab.winehq.org/wine/wine/-/merge_requests/2786 */
// WSADATA wsaData;
// int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
// if (iResult != 0)
// printf("WSAStartup failed: %d\n", iResult);
// fd = socket(AF_UNIX, SOCK_STREAM, 0);
}
else
fd = sys_socket(AF_UNIX, SOCK_STREAM, 0);
print("Socket %d created\n", fd);
if (fd == INVALID_SOCKET)
{
print("invalid socket: %d %d\n", fd, WSAGetLastError());
ExitProcess(1);
}
if (fd < 0)
{
@ -555,6 +609,8 @@ NewConnection:
ExitProcess(1);
}
print("Socket %d created\n", fd);
ConnectToSocket(fd);
print("Connected to Discord\n");

View File

@ -3,7 +3,7 @@
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="amd64"
name="EnderIce2.rpc-bridge"
name="com.enderice2.rpc-bridge"
type="win32"
/>
<description>Simple bridge that allows you to use Discord Rich Presence with Wine games/software.</description>

View File

@ -19,7 +19,7 @@ BEGIN
VALUE "FileDescription", "Simple bridge that allows you to use Discord Rich Presence with Wine games/software."
VALUE "FileVersion", VER_VERSION_STR
VALUE "InternalName", "bridge"
VALUE "LegalCopyright", "Copyright (c) 2024 EnderIce2"
VALUE "LegalCopyright", "Copyright (c) 2025 EnderIce2"
VALUE "OriginalFilename", "bridge.exe"
VALUE "ProductName", "rpc-bridge"
VALUE "ProductVersion", VER_VERSION_STR

View File

@ -1,4 +1,4 @@
#! /bin/sh
#!/bin/sh
# This script is used to run Steam Play with the bridge.
# Usage: /path/to/bridge.sh %command%
@ -21,10 +21,10 @@ TEMP_PATH="$XDG_RUNTIME_DIR"
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"
IPC_PATHS="$TEMP_PATH /run/user/$UID $TEMP_PATH/app/com.discordapp.Discord $TEMP_PATH/.flatpak/dev.vencord.Vesktop/xdg-run $TEMP_PATH/snap.discord $TEMP_PATH/snap.discord-canary"
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)"
if [ -S "$discord_ipc"/discord-ipc-? ]; then
VESSEL_PATH="$BRIDGE_PATH:$(echo "$discord_ipc"/discord-ipc-?)"
break
fi
done

View File

@ -1,3 +0,0 @@
@echo off
START /WAIT bridge.exe --install

View File

@ -8,6 +8,16 @@ LOCATION=~/Library/Application\ Support/rpc-bridge
SCRIPT=$LOCATION/rpc-bridge
AGENT=~/Library/LaunchAgents/com.enderice2.rpc-bridge.plist
function is_installed() {
if [ -f "$AGENT" ]; then
launchctl list | grep -q "com.enderice2.rpc-bridge"
if [ $? -eq 0 ]; then
return 0
fi
fi
return 1
}
function install() {
# Directories
if [ ! -d "$SYMLINK" ]; then
@ -71,9 +81,16 @@ fi
case $1 in
install)
if is_installed; then
echo "LaunchAgent is already installed."
exit 0
fi
install
;;
remove)
if ! is_installed; then
echo "LaunchAgent is not installed. Continuing anyway."
fi
remove
;;
*)

View File

@ -1,3 +0,0 @@
@echo off
START /WAIT bridge.exe --uninstall

35
docs/README.md Normal file
View File

@ -0,0 +1,35 @@
# Documentation
> **Note:** This documentation is built and deployed using [MkDocs Material](https://squidfunk.github.io/mkdocs-material/) (with mkdocs-video) via GitHub Actions. For the best experience, view it on the [published site](https://enderice2.github.io/rpc-bridge/) or with MkDocs locally. Some features (such as tabs, videos, or special formatting) may not display correctly in plain Markdown viewers.
This repository contains the documentation for the project. It is written in Markdown and rendered using MkDocs.
## Getting Started
To view the documentation locally, install the required Python packages:
```bash
pip install mkdocs mkdocs-material mkdocs-video
```
Once installed, you can serve the documentation locally by running:
```bash
mkdocs serve
```
This will start a local web server and you can view the documentation in your browser at `http://127.0.0.1:8000`.
## Contributing
If you want to contribute to the documentation, please follow these steps:
1. Fork the repository.
2. Make your changes in a branch.
3. Submit a pull request.
Please ensure that your changes are consistent with the existing style and structure of the documentation.
## License
This documentation is licensed under the MIT License. See the LICENSE file for more information.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

BIN
docs/assets/hades.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 KiB

Binary file not shown.

BIN
docs/assets/vividstasis.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 945 KiB

View File

@ -9,7 +9,7 @@ Simple bridge that allows you to use Discord Rich Presence with Wine games/softw
[Download latest release](https://github.com/EnderIce2/rpc-bridge/releases/latest/download/bridge.zip "Recommended"){ .md-button .md-button--primary }
[Download latest pre-release](https://github.com/EnderIce2/rpc-bridge/releases "Unstable builds with experimental features"){ .md-button }
<!-- [Download latest build](https://github.com/EnderIce2/rpc-bridge/actions "Builds from the latest commits, here be dragons!"){ .md-button } -->
[Download latest build](https://github.com/EnderIce2/rpc-bridge/actions/workflows/build.yml "Builds from the latest commits, here be dragons!"){ .md-button }
Works by running a small program in the background that creates a [named pipe](https://learn.microsoft.com/en-us/windows/win32/ipc/named-pipes) `\\.\pipe\discord-ipc-0` inside the prefix and forwards all data to the pipe `/run/user/1000/discord-ipc-0`.
@ -20,11 +20,61 @@ For v1.0, see [the original README](https://github.com/EnderIce2/rpc-bridge/blob
---
## Known Issues
- If you use **Vesktop**
Some games may not show up in Discord. This is because Vesktop uses arRPC, which it doesn't work with some games [#4](https://github.com/EnderIce2/rpc-bridge/issues/4#issuecomment-2143549407). This is not an issue with the bridge.
---
## My game is not showing up in Discord
If your game is not showing up in Discord, please check the following:
- The game you are playing has [Rich Presence](https://discord.com/developers/docs/rich-presence/overview) support!
- Some games may not have this feature. It's up to developers of the game to implement it.
This is not an issue related to the bridge.
- You followed the installation steps correctly.
- You are using the latest version of the bridge. Currently is ![GitHub Release](https://img.shields.io/github/v/release/EnderIce2/rpc-bridge?style=flat-square&label=%20).
### I still want to see the game in Discord!
This is outside the scope of this project, but here are some workarounds:
- You can manually add the game to Discord by going to `User Settings >` under `Activity Settings` in `Registered Games` tab. [Official Article](https://support.discord.com/hc/en-us/articles/7931156448919-Activity-Status-Recent-Activity#h_01HTJA8QV5ABSA6FY6GEPMA946)
- Tip: You can rename the game to whatever you want.
---
## Compiling from source
- Install the `wine`, `gcc-mingw-w64` and `make` packages.
- Open a terminal in the directory that contains this file and run `make`.
- The compiled executable will be located in `build/bridge.exe`.
---
## Examples
![lol](assets/lutris_lol.png "League Of Legends running under Wine using Lutris"){ width="600" }
[**League Of Legends**](https://www.leagueoflegends.com/en-us/) running under Wine using Lutris
![image](assets/lutris_lol.png){ width="600" }
![among us](assets/steam_amongus.png "Among Us on Steam"){ width="600" }
[**Among Us**](https://store.steampowered.com/app/945360/Among_Us/) on Steam
![image](assets/steam_amongus.png){ width="600" }
[**Content Warning**](https://store.steampowered.com/app/2881650/Content_Warning/) on Steam
![image](assets/contentwarning.png){ width="600" }
[**Hades**](https://store.steampowered.com/app/1145360/Hades/) on Steam
![image](assets/hades.png){ width="600" }
[**Lethal Company**](https://store.steampowered.com/app/1966720/Lethal_Company/) ([modded](https://thunderstore.io/c/lethal-company/p/mrov/LethalRichPresence/)) on Steam
![image](assets/lethalcompany.png){ width="600" }
[**vivid/stasis**](https://store.steampowered.com/app/2093940/vividstasis/) on Steam
![image](assets/vividstasis.png){ width="600" }
## Credits

View File

@ -62,19 +62,6 @@ This method is recommended because it's easier to manage.
- Globally
- `flatpak override --user --filesystem=xdg-run/discord-ipc-0`
## macOS
The steps for MacOS are almost the same, but due to the way `$TMPDIR` works, you will have to install a **LaunchAgent**.
- 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"
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.
## Run without installing the service
If you prefer not to use the service, you can manually run `bridge.exe` within the Wine prefix.
@ -84,28 +71,18 @@ In Lutris, you can achieve this by adding the path to `bridge.exe` in the `Execu
=== "Without bridge"
```
Executable
/mnt/games/lutris/league-of-legends/drive_c/Riot Games/League of Legends/LeagueClient.exe
Arguments
--locale=en_US --launch-product=league_of_legends --launch-patchline=live
```
- Executable
- `/mnt/games/lutris/league-of-legends/drive_c/Riot Games/League of Legends/LeagueClient.exe`
- Arguments
- `--locale=en_US --launch-product=league_of_legends --launch-patchline=live`
=== "With bridge"
```
Executable
/mnt/games/lutris/league-of-legends/drive_c/bridge.exe
Arguments
"C:\Riot Games\League of Legends\LeagueClient.exe" --locale=en_US --launch-product=league_of_legends --launch-patchline=live
```
- Executable
- `/mnt/games/lutris/league-of-legends/drive_c/bridge.exe`
- Arguments
- `"C:\Riot Games\League of Legends\LeagueClient.exe" --locale=en_US --launch-product=league_of_legends --launch-patchline=live`
In Wine, all you need to do is run `bridge.exe` and select `Start`.
## Compiling from source
- Install the `wine`, `gcc-mingw-w64` and `make` packages.
- Open a terminal in the directory that contains this file and run `make`.
- The compiled executable will be located in `build/bridge.exe`.
[^1]: As requested [here](https://github.com/EnderIce2/rpc-bridge/issues/2).

28
docs/macos.md Normal file
View File

@ -0,0 +1,28 @@
# Installation
Installation will copy itself to `C:\windows\bridge.exe` and create a Windows service.
Logs are stored in `C:\windows\logs\bridge.log`.
## Preparing macOS for Installation
Before proceeding with the installation, you need to set up a **LaunchAgent** due to the way `$TMPDIR` works on macOS.
- 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.sh install` and to **remove** it simply run `./launchd.sh remove`.
The script will add a LaunchAgent to your user, that will symlink the `$TMPDIR` directory to `/tmp/rpc-bridge/tmpdir`.
## Video Tutorial on how to install the LaunchAgent + bridge inside CrossOver
![type:video](assets/macos-crossover.webm){: style='width: 66%; height: 20vw;'}
## Wine (~/.wine)
- Double click `bridge.exe` and click `Install`.
- ![gui](assets/gui.png "rpc-bridge GUI")
- To remove, the same process can be followed, but click `Remove` instead.
## Run without installing the service
If you prefer not to use the service, you can manually run `bridge.exe` within the prefix, and click on `Start` in the GUI.

View File

@ -8,7 +8,7 @@
- `Install` will install the service.
- `Remove` will uninstall the service.
## Commands
## CLI
- `--help` Show help message
- This will show the help message

13
gui.c
View File

@ -191,11 +191,14 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
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);
snprintf(msg, sizeof(msg),
"rpc-bridge v%s\n"
" branch: %s\n"
" commit: %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, GIT_BRANCH, GIT_COMMIT);
MessageBox(NULL, msg, "About", MB_OK);
break;
}

99
main.c
View File

@ -59,7 +59,7 @@ void DetectWine()
if (!GetProcAddress(hNTdll, "wine_get_version"))
{
MessageBox(NULL, "This program is only intended to run under Wine.",
GetErrorMessage(), MB_OK | MB_ICONINFORMATION);
"Error", MB_OK | MB_ICONINFORMATION);
ExitProcess(1);
}
@ -221,28 +221,26 @@ void HandleArguments(int argc, char *argv[])
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("(Steam) Starting service and then exiting...\n");
fclose(g_logFile);
print("StartService: %s\n", GetErrorMessage());
MessageBox(NULL, GetErrorMessage(),
"StartService",
MB_OK | MB_ICONSTOP);
ExitProcess(1);
if (StartService(schService, 0, NULL) != FALSE)
{
CloseServiceHandle(schService);
CloseServiceHandle(hSCManager);
ExitProcess(0);
}
else if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING)
{
CloseServiceHandle(schService);
CloseServiceHandle(hSCManager);
ExitProcess(0);
}
print("(Steam) Service started successfully, exiting...\n");
CloseServiceHandle(schService);
CloseServiceHandle(hSCManager);
ExitProcess(0);
MessageBox(NULL, GetErrorMessage(),
"StartService",
MB_OK | MB_ICONSTOP);
ExitProcess(1);
}
else if (strcmp(argv[1], "--install") == 0)
{
@ -273,38 +271,38 @@ void HandleArguments(int argc, char *argv[])
}
else if (strcmp(argv[1], "--version") == 0)
{
printf("%s\n", VER_VERSION_STR);
/* Already shows the version */
ExitProcess(0);
}
else if (strcmp(argv[1], "--help") == 0)
{
printf("Usage:\n"
" %s [args]\n"
"\n"
"Arguments:\n"
" --help Show this help\n"
"\n"
" --version Show version\n"
"\n"
" --install Install service\n"
" This will copy the binary to C:\\windows\\bridge.exe and register it as a service\n"
"\n"
" --uninstall Uninstall service\n"
" This will remove the service and delete C:\\windows\\bridge.exe\n"
"\n"
" --steam Reserved for Steam\n"
" This will start the service and exit (used with bridge.sh)\n"
"\n"
" --no-service Do not run as service\n"
" (only for --steam)\n"
"\n"
" --service Reserved for service\n"
"\n"
" --rpc <dir> Set RPC_PATH environment variable\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]);
print("Usage:\n"
" %s [args]\n"
"\n"
"Arguments:\n"
" --help Show this help\n"
"\n"
" --version Show version\n"
"\n"
" --install Install service\n"
" This will copy the binary to C:\\windows\\bridge.exe and register it as a service\n"
"\n"
" --uninstall Uninstall service\n"
" This will remove the service and delete C:\\windows\\bridge.exe\n"
"\n"
" --steam Reserved for Steam\n"
" This will start the service and exit (used with bridge.sh)\n"
"\n"
" --no-service Do not run as service\n"
" (only for --steam)\n"
"\n"
" --service Reserved for service\n"
"\n"
" --rpc <dir> Set RPC_PATH environment variable\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);
}
}
@ -316,11 +314,12 @@ int main(int argc, char *argv[])
g_logFile = fopen(logFilePath, "w");
if (g_logFile == NULL)
{
printf("Failed to open logs file: %ld\n",
GetLastError());
MessageBox(NULL, "Failed to open logs file", "Error", MB_OK | MB_ICONERROR);
printf("Failed to open logs file: %ld\n", GetLastError());
ExitProcess(1);
}
print("rpc-bridge v%s %s-%s\n", VER_VERSION_STR, GIT_BRANCH, GIT_COMMIT);
if (argc > 1)
HandleArguments(argc, argv);
else

View File

@ -14,22 +14,22 @@ theme:
palette:
- media: "(prefers-color-scheme)"
toggle:
icon: material/weather-sunny
name: Switch to light mode
icon: material/brightness-auto
name: Switch to dark mode
- media: "(prefers-color-scheme: dark)"
scheme: slate
primary: black
accent: indigo
toggle:
icon: material/brightness-4
name: Switch to system preference
name: Switch to light mode
- media: "(prefers-color-scheme: light)"
scheme: default
primary: indigo
accent: indigo
toggle:
icon: material/brightness-7
name: Switch to dark mode
name: Switch to system preference
font:
text: Roboto
code: Roboto Mono
@ -70,7 +70,13 @@ markdown_extensions:
alternate_style: true
plugins:
- offline
- mkdocs-video:
is_video: true
video_controls: true
video_loop: false
video_muted: false
nav:
- Home: index.md
- Installation: installation.md
- Linux: linux.md
- macOS: macos.md
- Usage: usage.md

View File

@ -5,5 +5,5 @@
#define IDM_HELP_ABOUT 40003
#define IDM_VIEW_LOG 40004
#define VER_VERSION 1, 2, 0, 0
#define VER_VERSION_STR "1.2\0"
#define VER_VERSION 1, 4, 0, 0
#define VER_VERSION_STR "1.4.0.0\0"