39 Commits
kvm ... 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
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
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
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
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
c15c4df29e Create CODE_OF_CONDUCT.md 2024-10-30 16:39:46 +02:00
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
8786efd144 Fix formatting and add game links 2024-06-02 17:52:52 +03:00
2024de9408 Add "Known Issues" section
* Using Vesktop may cause issues with some games #4
2024-06-02 17:45:48 +03:00
e8926edb41 Add more screenshots
* Content Warning
* Hades
* Lethal Company
* vivid/stasis
2024-06-02 17:41:16 +03:00
9f1a9de3d7 Update actions/checkout and actions/upload-artifact to v4 2024-06-01 19:57:30 +03:00
ccf09806c9 Print version on program start 2024-06-01 19:39:39 +03:00
7363ee64d5 Update bridge.manifest with correct assembly name 2024-06-01 19:35:01 +03:00
a3023e349e Update gui.png 2024-06-01 02:56:12 +03:00
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
a8904bf3f1 Update version 2024-06-01 00:57:06 +03:00
08feef776b Update version 2024-05-31 01:18:38 +03:00
8545467a85 Add .elf file to .gitignore 2024-05-30 05:33:28 +03:00
34 changed files with 654 additions and 597 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: on:
push: push:
branches: [ "master" ] branches: [ master ]
paths:
- docs/**
- mkdocs.yml
pull_request: pull_request:
branches: [ "master" ] branches: [ "master" ]
paths:
- docs/**
- mkdocs.yml
permissions: permissions:
contents: write contents: write
jobs: 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: deploy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -43,4 +35,5 @@ jobs:
restore-keys: | restore-keys: |
mkdocs-material- mkdocs-material-
- run: pip install mkdocs-material - run: pip install mkdocs-material
- run: pip install mkdocs-video
- run: mkdocs gh-deploy --force - run: mkdocs gh-deploy --force

View File

@ -5,21 +5,13 @@
"includePath": [ "includePath": [
"${workspaceFolder}/**" "${workspaceFolder}/**"
], ],
"defines": [], "defines": [
"GIT_COMMIT",
"GIT_BRANCH"
],
"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

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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal 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_SOURCES = $(shell find ./ -type f -name '*.c')
C_OBJECTS = $(C_SOURCES:.c=.o) C_OBJECTS = $(C_SOURCES:.c=.o)
CFLAGS = -std=c17 -Wno-int-conversion GIT_COMMIT = $(shell git rev-parse --short HEAD)
LFLAGS = -lgdi32 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 # DBGFLAGS = -Wl,--export-all-symbols -g -O0 -ggdb3 -Wall
@ -12,11 +17,10 @@ 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 $<)
x86_64-w64-mingw32-gcc $(CFLAGS) $(DBGFLAGS) -c $< -o $@ x86_64-w64-mingw32-gcc $(CFLAGS) $(CWARNFLAGS) $(DBGFLAGS) -c $< -o $@
clean: clean:
rm -f $(C_OBJECTS) build/bridge.exe bridge.res 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) - 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` - 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`. 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.* *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 ## Compiling from source
- Install the `wine`, `gcc-mingw-w64` and `make` packages. - Install the `wine`, `gcc-mingw-w64` and `make` packages.

488
bridge.c
View File

@ -4,8 +4,6 @@
#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
@ -22,11 +20,13 @@
#define __darwin_close 0x2000006 #define __darwin_close 0x2000006
#define __darwin_socket 0x2000061 #define __darwin_socket 0x2000061
#define __darwin_connect 0x2000062 #define __darwin_connect 0x2000062
#define __darwin_mmap 0x20000C5
#define __darwin_fcntl 0x200005C #define __darwin_fcntl 0x200005C
#define __darwin_sysctl 0x20000CA #define __darwin_sysctl 0x20000CA
#define O_RDONLY 00 #define O_RDONLY 00
/* macos & linux are the same for PROT_READ, PROT_WRITE, MAP_FIXED & MAP_PRIVATE */
#define PROT_READ 1 #define PROT_READ 1
#define PROT_WRITE 2 #define PROT_WRITE 2
#define MAP_PRIVATE 0x02 #define MAP_PRIVATE 0x02
@ -34,6 +34,8 @@
#define MAP_ANON 0x20 #define MAP_ANON 0x20
#define MAP_FAILED ((void *)-1) #define MAP_FAILED ((void *)-1)
#define __darwin_MAP_ANON 0x1000
#define SYS_SOCKET 1 #define SYS_SOCKET 1
#define SYS_CONNECT 3 #define SYS_CONNECT 3
@ -66,7 +68,7 @@ void print(char const *fmt, ...);
LPTSTR GetErrorMessage(); LPTSTR GetErrorMessage();
extern BOOL RunningAsService; extern BOOL RunningAsService;
BOOL RetryNewConnection; BOOL RetryNewConnection;
OS_INFO OSInfo = {0}; BOOL IsLinux;
HANDLE hOut = NULL; HANDLE hOut = NULL;
HANDLE hIn = NULL; HANDLE hIn = NULL;
@ -104,7 +106,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 (OSInfo.IsLinux) if (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);
@ -112,7 +114,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 (OSInfo.IsLinux) if (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);
@ -120,7 +122,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 (OSInfo.IsLinux) if (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);
@ -128,7 +130,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 (OSInfo.IsLinux) if (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);
@ -136,25 +138,34 @@ 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(OSInfo.IsLinux); if (IsLinux)
return linux_syscall(__linux_mmap2, addr, length, prot, flags, fd, offset); 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) static inline int sys_munmap(unsigned int *addr, size_t length)
{ {
assert(OSInfo.IsLinux); assert(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(OSInfo.IsLinux); assert(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 (OSInfo.IsLinux) if (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);
@ -162,7 +173,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 (OSInfo.IsLinux) if (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);
@ -176,7 +187,7 @@ char *native_getenv(const char *name)
if (ret != 0) if (ret != 0)
return lpBuffer; return lpBuffer;
if (!OSInfo.IsLinux) if (!IsLinux)
{ {
char *value = getenv(name); char *value = getenv(name);
if (value == NULL) if (value == NULL)
@ -197,11 +208,7 @@ char *native_getenv(const char *name)
/* I hope the 0x20000 is okay */ /* I hope the 0x20000 is okay */
if (environStr == NULL) if (environStr == NULL)
{ environStr = sys_mmap(0x20000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
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);
}
if ((uintptr_t)environStr > 0x7effffff) if ((uintptr_t)environStr > 0x7effffff)
print("Warning: environStr %#lx is above 2GB\n", environStr); print("Warning: environStr %#lx is above 2GB\n", environStr);
@ -211,18 +218,17 @@ char *native_getenv(const char *name)
int fd = sys_open(environStr, O_RDONLY, 0); int fd = sys_open(environStr, O_RDONLY, 0);
// sys_munmap(environStr, 4096);
if (fd < 0) if (fd < 0)
{ {
print("Failed to open /proc/self/environ: %d\n", fd); print("Failed to open /proc/self/environ: %d\n", fd);
return NULL; 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; char *result = NULL;
int bytesRead; int bytesRead;
while ((bytesRead = sys_read(fd, buffer, sizeof(buffer) - 1)) > 0) while ((bytesRead = sys_read(fd, buffer, 0x1000 - 1)) > 0)
{ {
buffer[bytesRead] = '\0'; buffer[bytesRead] = '\0';
char *env = buffer; char *env = buffer;
@ -253,9 +259,9 @@ void ConnectToSocket(int fd)
{ {
print("Connecting to socket\n"); print("Connecting to socket\n");
const char *runtime; const char *runtime;
if (OSInfo.IsLinux) if (IsLinux)
runtime = native_getenv("XDG_RUNTIME_DIR"); runtime = native_getenv("XDG_RUNTIME_DIR");
else if (OSInfo.IsDarwin) else
{ {
runtime = native_getenv("TMPDIR"); runtime = native_getenv("TMPDIR");
if (runtime == NULL) if (runtime == NULL)
@ -280,50 +286,72 @@ void ConnectToSocket(int fd)
} }
} }
} }
else
{
print("Unsupported OS\n");
ExitProcess(1);
}
print("IPC directory: %s\n", runtime); print("IPC directory: %s\n", runtime);
/* TODO: check for multiple discord instances and create a pipe for each */ /* TODO: check for multiple discord instances and create a pipe for each */
const char *discordUnixPipes[] = { const char *discordUnixSockets[] = {
"/discord-ipc-0", "%s/discord-ipc-%d",
"/snap.discord/discord-ipc-0", "%s/app/com.discordapp.Discord/discord-ipc-%d",
"/app/com.discordapp.Discord/discord-ipc-0", "%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; int sockRet = 0;
socketAddr.sun_family = AF_UNIX; for (int i = 0; i < sizeof(discordUnixSockets) / sizeof(discordUnixSockets[0]); i++)
char *pipePath = NULL;
int sockRet = -1;
for (int i = 0; i < sizeof(discordUnixPipes) / sizeof(discordUnixPipes[0]); i++)
{ {
pipePath = malloc(strlen(runtime) + strlen(discordUnixPipes[i]) + 1); size_t pipePathLen = strlen(runtime) + strlen(discordUnixSockets[i]) + 1;
strcpy(pipePath, runtime); char *pipePath = malloc(pipePathLen);
strcat(pipePath, discordUnixPipes[i]);
strcpy_s(socketAddr.sun_path, sizeof(socketAddr.sun_path), pipePath);
print("Connecting to %s\n", pipePath); for (int j = 0; j < 16; j++)
if (OSInfo.IsLinux)
{ {
unsigned long socketArgs[] = { if (IsLinux)
(unsigned long)fd, {
(unsigned long)(intptr_t)&socketAddr, struct sockaddr_un *socketAddr = sys_mmap(0x23000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
sizeof(socketAddr)}; 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) if (sockRet >= 0)
{
print("Connecting to %s\n", pipePath);
free(pipePath);
break; break;
}
free(pipePath);
} }
if (sockRet < 0) if (sockRet < 0)
@ -340,18 +368,22 @@ void ConnectToSocket(int fd)
void PipeBufferInThread(LPVOID lpParam) void PipeBufferInThread(LPVOID lpParam)
{ {
bridge_thread *bt = (bridge_thread *)lpParam; bridge_thread *bt = (bridge_thread *)lpParam;
print("In thread started using fd %d and pipe %#x\n", print("In thread started using fd %d and pipe %#x\n", bt->fd, bt->hPipe);
bt->fd, bt->hPipe);
int EOFCount = 0; 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) while (TRUE)
{ {
char buffer[BUFFER_LENGTH]; 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)) if (unlikely(read < 0))
{ {
print("Failed to read from unix pipe: %d\n", print("Failed to read from unix pipe: %d\n", read);
GetErrorMessage());
Sleep(1000); Sleep(1000);
continue; continue;
} }
@ -373,14 +405,16 @@ void PipeBufferInThread(LPVOID lpParam)
} }
EOFCount = 0; EOFCount = 0;
memcpy(buffer, l_buffer, read);
print("Reading %d bytes from unix pipe: \"", read); print("Reading %d bytes from unix pipe: \"", read);
for (int i = 0; i < read; i++) for (int i = 0; i < read; i++)
print("%c", buffer[i]); print("%c", buffer[i]);
print("\"\n"); print("\"\n");
DWORD dwWritten; DWORD dwWritten;
if (unlikely(!WriteFile(bt->hPipe, buffer, read, WINBOOL bResult = WriteFile(bt->hPipe, buffer, read, &dwWritten, NULL);
&dwWritten, NULL))) if (unlikely(bResult == FALSE))
{ {
if (GetLastError() == ERROR_BROKEN_PIPE) if (GetLastError() == ERROR_BROKEN_PIPE)
{ {
@ -389,16 +423,14 @@ void PipeBufferInThread(LPVOID lpParam)
break; break;
} }
print("Failed to read from pipe: %d\n", print("Failed to read from pipe: %s\n", GetErrorMessage());
GetErrorMessage());
Sleep(1000); Sleep(1000);
continue; continue;
} }
if (unlikely(dwWritten < 0)) if (unlikely(dwWritten < 0))
{ {
print("Failed to write to pipe: %d\n", print("Failed to write to pipe: %s\n", GetErrorMessage());
GetErrorMessage());
Sleep(1000); Sleep(1000);
continue; continue;
} }
@ -406,8 +438,8 @@ void PipeBufferInThread(LPVOID lpParam)
while (dwWritten < read) while (dwWritten < read)
{ {
int last_written = dwWritten; int last_written = dwWritten;
if (unlikely(!WriteFile(bt->hPipe, buffer + dwWritten, WINBOOL bResult = WriteFile(bt->hPipe, buffer + dwWritten, read - dwWritten, &dwWritten, NULL);
read - dwWritten, &dwWritten, NULL))) if (unlikely(bResult == FALSE))
{ {
if (GetLastError() == ERROR_BROKEN_PIPE) if (GetLastError() == ERROR_BROKEN_PIPE)
{ {
@ -416,16 +448,14 @@ void PipeBufferInThread(LPVOID lpParam)
break; break;
} }
print("Failed to read from pipe: %d\n", print("Failed to read from pipe: %s\n", GetErrorMessage());
GetErrorMessage());
Sleep(1000); Sleep(1000);
continue; continue;
} }
if (unlikely(last_written == dwWritten)) if (unlikely(last_written == dwWritten))
{ {
print("Failed to write to pipe: %d\n", print("Failed to write to pipe: %s\n", GetErrorMessage());
GetErrorMessage());
Sleep(1000); Sleep(1000);
continue; continue;
} }
@ -436,166 +466,19 @@ void PipeBufferInThread(LPVOID lpParam)
void PipeBufferOutThread(LPVOID lpParam) void PipeBufferOutThread(LPVOID lpParam)
{ {
bridge_thread *bt = (bridge_thread *)lpParam; bridge_thread *bt = (bridge_thread *)lpParam;
print("Out thread started using fd %d and pipe %#x\n", print("Out thread started using fd %d and pipe %#x\n", bt->fd, bt->hPipe);
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) while (TRUE)
{ {
char buffer[BUFFER_LENGTH]; char buffer[BUFFER_LENGTH];
DWORD dwRead; DWORD dwRead;
WINBOOL bResult = ReadFile(bt->hPipe, buffer, BUFFER_LENGTH, &dwRead, NULL);
if (unlikely(!ReadFile(bt->hPipe, buffer, sizeof(buffer), if (unlikely(bResult == FALSE))
&dwRead, NULL)))
{
if (GetLastError() == ERROR_BROKEN_PIPE)
{
RetryNewConnection = TRUE;
print("Out Broken pipe\n");
break;
}
print("Failed to read from pipe: %d\n",
GetErrorMessage());
Sleep(1000);
continue;
}
print("Writing %d bytes to unix pipe: \"", dwRead);
for (int i = 0; i < dwRead; i++)
print("%c", buffer[i]);
print("\"\n");
int written = sys_write(bt->fd, buffer, dwRead);
if (unlikely(written < 0))
{
print("Failed to write to socket: %d\n",
written);
continue;
}
while (written < dwRead)
{
int last_written = written;
written += sys_write(bt->fd, buffer + written, dwRead - written);
if (unlikely(last_written == written))
{
print("Failed to write to socket: %d\n",
GetErrorMessage());
Sleep(1000);
continue;
}
}
}
}
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) if (GetLastError() == ERROR_BROKEN_PIPE)
{ {
@ -609,38 +492,31 @@ void COMPipeBufferOutThread(LPVOID lpParam)
continue; continue;
} }
print("Writing %d bytes to COM2 port: \"", dwRead); print("Writing %d bytes to unix pipe: \"", dwRead);
for (DWORD i = 0; i < dwRead; i++) for (int i = 0; i < dwRead; i++)
print("%c", buffer[i]); print("%c", buffer[i]);
print("\"\n"); print("\"\n");
DWORD bytesWritten; memcpy(l_buffer, buffer, dwRead);
if (!WriteFile(hComPort, buffer, dwRead, &bytesWritten, NULL)) int written = sys_write(bt->fd, l_buffer, dwRead);
if (unlikely(written < 0))
{ {
print("Failed to write to COM2 port: %s\n", GetErrorMessage()); print("Failed to write to socket: %d\n", written);
continue; continue;
} }
while (bytesWritten < dwRead) while (written < dwRead)
{ {
int last_written = bytesWritten; int last_written = written;
if (!WriteFile(hComPort, buffer + bytesWritten, dwRead - bytesWritten, &bytesWritten, NULL)) written += sys_write(bt->fd, buffer + written, dwRead - written);
if (unlikely(last_written == written))
{ {
print("Failed to write to COM2 port: %s\n", GetErrorMessage()); print("Failed to write to socket: %s\n", GetErrorMessage());
Sleep(1000);
continue;
}
if (last_written == bytesWritten)
{
print("Failed to write to COM2 port: %s\n", GetErrorMessage());
Sleep(1000); Sleep(1000);
continue; continue;
} }
} }
} }
CloseHandle(hComPort);
} }
void CreateBridge() void CreateBridge()
@ -696,87 +572,60 @@ NewConnection:
print("Pipe connected\n"); print("Pipe connected\n");
int fd; int fd;
if (!OSInfo.IsWine) if (IsLinux)
{ {
print("Running on Windows\n"); // unsigned long socketArgs[] = {
/* KVM, send 0xBB1569 and wait with timeout (TODO) for response 0xB41D6E */ // (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);
print("Trying \\\\.\\COM2\n"); /* FIXME: WSAEAFNOSUPPORT: https://gitlab.winehq.org/wine/wine/-/merge_requests/2786 */
HANDLE hComPort = CreateFile("\\\\.\\COM2", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); // WSADATA wsaData;
assert(hComPort != INVALID_HANDLE_VALUE); // int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
DWORD dwWritten; // if (iResult != 0)
print("Writing to COM port\n"); // printf("WSAStartup failed: %d\n", iResult);
char buffer[4] = "\x69\x15\xBB"; // fd = socket(AF_UNIX, SOCK_STREAM, 0);
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 else
fd = sys_socket(AF_UNIX, SOCK_STREAM, 0);
if (fd == INVALID_SOCKET)
{ {
if (OSInfo.IsLinux) print("invalid socket: %d %d\n", fd, WSAGetLastError());
{ ExitProcess(1);
unsigned long socketArgs[] = {
(unsigned long)AF_UNIX,
(unsigned long)SOCK_STREAM,
0};
fd = sys_socketcall(SYS_SOCKET, socketArgs);
}
else
fd = sys_socket(AF_UNIX, SOCK_STREAM, 0);
print("Socket %d created\n", fd);
if (fd < 0)
{
print("Failed to create socket: %d\n", fd);
if (!RunningAsService)
MessageBox(NULL, "Failed to create socket",
NULL, MB_OK | MB_ICONSTOP);
ExitProcess(1);
}
ConnectToSocket(fd);
print("Connected to Discord\n");
bridge_thread bt = {fd, hPipe};
hIn = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)PipeBufferInThread,
(LPVOID)&bt,
0, NULL);
hOut = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)PipeBufferOutThread,
(LPVOID)&bt,
0, NULL);
} }
if (fd < 0)
{
print("Failed to create socket: %d\n", fd);
if (!RunningAsService)
MessageBox(NULL, "Failed to create socket",
NULL, MB_OK | MB_ICONSTOP);
ExitProcess(1);
}
print("Socket %d created\n", fd);
ConnectToSocket(fd);
print("Connected to Discord\n");
bridge_thread bt = {fd, hPipe};
hIn = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)PipeBufferInThread,
(LPVOID)&bt,
0, NULL);
print("Created in thread %#lx\n", hIn); print("Created in thread %#lx\n", hIn);
hOut = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)PipeBufferOutThread,
(LPVOID)&bt,
0, NULL);
print("Created out thread %#lx\n", hOut); print("Created out thread %#lx\n", hOut);
if (hIn == NULL || hOut == NULL) if (hIn == NULL || hOut == NULL)
@ -807,8 +656,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);
CloseHandle(hPipe); CloseHandle(hPipe);

View File

@ -1,13 +0,0 @@
#ifndef _BRIDGE_H
#define _BRIDGE_H
#include <windows.h>
typedef struct _OS_INFO
{
BOOL IsLinux;
BOOL IsDarwin;
BOOL IsWine;
} OS_INFO;
#endif // _BRIDGE_H

View File

@ -3,7 +3,7 @@
<assemblyIdentity <assemblyIdentity
version="1.0.0.0" version="1.0.0.0"
processorArchitecture="amd64" processorArchitecture="amd64"
name="EnderIce2.rpc-bridge" name="com.enderice2.rpc-bridge"
type="win32" type="win32"
/> />
<description>Simple bridge that allows you to use Discord Rich Presence with Wine games/software.</description> <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 "FileDescription", "Simple bridge that allows you to use Discord Rich Presence with Wine games/software."
VALUE "FileVersion", VER_VERSION_STR VALUE "FileVersion", VER_VERSION_STR
VALUE "InternalName", "bridge" VALUE "InternalName", "bridge"
VALUE "LegalCopyright", "Copyright (c) 2024 EnderIce2" VALUE "LegalCopyright", "Copyright (c) 2025 EnderIce2"
VALUE "OriginalFilename", "bridge.exe" VALUE "OriginalFilename", "bridge.exe"
VALUE "ProductName", "rpc-bridge" VALUE "ProductName", "rpc-bridge"
VALUE "ProductVersion", VER_VERSION_STR 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. # This script is used to run Steam Play with the bridge.
# Usage: /path/to/bridge.sh %command% # Usage: /path/to/bridge.sh %command%
@ -21,10 +21,10 @@ TEMP_PATH="$XDG_RUNTIME_DIR"
TEMP_PATH=${TEMP_PATH:-"$TMPDIR"} TEMP_PATH=${TEMP_PATH:-"$TMPDIR"}
VESSEL_PATH="$BRIDGE_PATH" 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 for discord_ipc in $IPC_PATHS; do
if [ -S "$discord_ipc"/discord-ipc-0 ]; then if [ -S "$discord_ipc"/discord-ipc-? ]; then
VESSEL_PATH="$BRIDGE_PATH:$(echo "$discord_ipc"/discord-ipc-0)" VESSEL_PATH="$BRIDGE_PATH:$(echo "$discord_ipc"/discord-ipc-?)"
break break
fi fi
done 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 SCRIPT=$LOCATION/rpc-bridge
AGENT=~/Library/LaunchAgents/com.enderice2.rpc-bridge.plist 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() { function install() {
# Directories # Directories
if [ ! -d "$SYMLINK" ]; then if [ ! -d "$SYMLINK" ]; then
@ -71,9 +81,16 @@ fi
case $1 in case $1 in
install) install)
if is_installed; then
echo "LaunchAgent is already installed."
exit 0
fi
install install
;; ;;
remove) remove)
if ! is_installed; then
echo "LaunchAgent is not installed. Continuing anyway."
fi
remove 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 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 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`. 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 ## 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 ## Credits

View File

@ -62,19 +62,6 @@ This method is recommended because it's easier to manage.
- Globally - Globally
- `flatpak override --user --filesystem=xdg-run/discord-ipc-0` - `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 ## Run without installing the service
If you prefer not to use the service, you can manually run `bridge.exe` within the Wine prefix. 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" === "Without bridge"
``` - Executable
Executable - `/mnt/games/lutris/league-of-legends/drive_c/Riot Games/League of Legends/LeagueClient.exe`
/mnt/games/lutris/league-of-legends/drive_c/Riot Games/League of Legends/LeagueClient.exe - Arguments
Arguments - `--locale=en_US --launch-product=league_of_legends --launch-patchline=live`
--locale=en_US --launch-product=league_of_legends --launch-patchline=live
```
=== "With bridge" === "With bridge"
``` - Executable
Executable - `/mnt/games/lutris/league-of-legends/drive_c/bridge.exe`
/mnt/games/lutris/league-of-legends/drive_c/bridge.exe - Arguments
Arguments - `"C:\Riot Games\League of Legends\LeagueClient.exe" --locale=en_US --launch-product=league_of_legends --launch-patchline=live`
"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`. 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). [^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. - `Install` will install the service.
- `Remove` will uninstall the service. - `Remove` will uninstall the service.
## Commands ## CLI
- `--help` Show help message - `--help` Show help message
- This will show the help message - This will show the help message

28
gui.c
View File

@ -4,7 +4,6 @@
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include "bridge.h"
#include "resource.h" #include "resource.h"
/** /**
@ -18,8 +17,7 @@ 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 OS_INFO OSInfo; extern BOOL IsLinux;
extern char *logFilePath;
HWND hwnd = NULL; HWND hwnd = NULL;
HANDLE hBridge = NULL; HANDLE hBridge = NULL;
@ -51,7 +49,7 @@ VOID HandleStartButton(BOOL Silent)
return; return;
} }
SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); 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());
@ -182,7 +180,7 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
HandleRemoveButton(); HandleRemoveButton();
break; break;
case IDM_VIEW_LOG: case IDM_VIEW_LOG:
ShellExecute(NULL, "open", "C:\\windows\\notepad.exe", logFilePath, NULL, SW_SHOW); ShellExecute(NULL, "open", "C:\\windows\\notepad.exe", "C:\\windows\\logs\\bridge.log", NULL, SW_SHOW);
break; break;
case IDM_HELP_DOCUMENTATION: case IDM_HELP_DOCUMENTATION:
ShellExecute(NULL, "open", "https://enderice2.github.io/rpc-bridge/index.html", NULL, NULL, SW_SHOWNORMAL); ShellExecute(NULL, "open", "https://enderice2.github.io/rpc-bridge/index.html", NULL, NULL, SW_SHOWNORMAL);
@ -193,11 +191,14 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
case IDM_HELP_ABOUT: case IDM_HELP_ABOUT:
{ {
char msg[256]; char msg[256];
sprintf(msg, "rpc-bridge v%s\n\n" snprintf(msg, sizeof(msg),
"Simple bridge that allows you to use Discord Rich Presence with Wine games/software.\n\n" "rpc-bridge v%s\n"
"Created by EnderIce2\n\n" " branch: %s\n"
"Licensed under the MIT License", " commit: %s\n\n"
VER_VERSION_STR); "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); MessageBox(NULL, msg, "About", MB_OK);
break; break;
} }
@ -231,6 +232,13 @@ VOID SetButtonStyles(INT *btnStartStyle, INT *btnRemoveStyle, INT *btnInstallSty
*btnRemoveStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP; *btnRemoveStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP;
*btnInstallStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP; *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);

100
kvm.c
View File

@ -1,100 +0,0 @@
#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__

125
main.c
View File

@ -3,12 +3,10 @@
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include "bridge.h"
#include "resource.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();
@ -17,7 +15,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 OS_INFO OSInfo; extern BOOL IsLinux;
LPTSTR GetErrorMessage() LPTSTR GetErrorMessage()
{ {
@ -59,8 +57,11 @@ void DetectWine()
} }
if (!GetProcAddress(hNTdll, "wine_get_version")) if (!GetProcAddress(hNTdll, "wine_get_version"))
return; {
OSInfo.IsWine = TRUE; MessageBox(NULL, "This program is only intended to run under Wine.",
"Error", 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");
@ -77,8 +78,7 @@ void DetectWine()
ExitProcess(1); ExitProcess(1);
} }
OSInfo.IsLinux = strcmp(__sysname, "Linux") == 0; IsLinux = strcmp(__sysname, "Linux") == 0;
OSInfo.IsDarwin = strcmp(__sysname, "Darwin") == 0;
} }
void print(char const *fmt, ...) void print(char const *fmt, ...)
@ -113,13 +113,14 @@ 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)
@ -220,28 +221,26 @@ void HandleArguments(int argc, char *argv[])
HeapFree(GetProcessHeap(), 0, asciiPath); HeapFree(GetProcessHeap(), 0, asciiPath);
} }
print("(Steam) Starting service...\n"); print("(Steam) Starting service and then exiting...\n");
if (StartService(schService, 0, NULL) == FALSE) fclose(g_logFile);
{
if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING)
{
print("(Steam) Service is already running\n");
CloseServiceHandle(schService);
CloseServiceHandle(hSCManager);
ExitProcess(0);
}
print("StartService: %s\n", GetErrorMessage()); if (StartService(schService, 0, NULL) != FALSE)
MessageBox(NULL, GetErrorMessage(), {
"StartService", CloseServiceHandle(schService);
MB_OK | MB_ICONSTOP); CloseServiceHandle(hSCManager);
ExitProcess(1); ExitProcess(0);
}
else if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING)
{
CloseServiceHandle(schService);
CloseServiceHandle(hSCManager);
ExitProcess(0);
} }
print("(Steam) Service started successfully, exiting...\n"); MessageBox(NULL, GetErrorMessage(),
CloseServiceHandle(schService); "StartService",
CloseServiceHandle(hSCManager); MB_OK | MB_ICONSTOP);
ExitProcess(0); ExitProcess(1);
} }
else if (strcmp(argv[1], "--install") == 0) else if (strcmp(argv[1], "--install") == 0)
{ {
@ -272,38 +271,38 @@ void HandleArguments(int argc, char *argv[])
} }
else if (strcmp(argv[1], "--version") == 0) else if (strcmp(argv[1], "--version") == 0)
{ {
printf("%s\n", VER_VERSION_STR); /* Already shows the version */
ExitProcess(0); ExitProcess(0);
} }
else if (strcmp(argv[1], "--help") == 0) else if (strcmp(argv[1], "--help") == 0)
{ {
printf("Usage:\n" print("Usage:\n"
" %s [args]\n" " %s [args]\n"
"\n" "\n"
"Arguments:\n" "Arguments:\n"
" --help Show this help\n" " --help Show this help\n"
"\n" "\n"
" --version Show version\n" " --version Show version\n"
"\n" "\n"
" --install Install service\n" " --install Install service\n"
" This will copy the binary to C:\\windows\\bridge.exe and register it as a service\n" " This will copy the binary to C:\\windows\\bridge.exe and register it as a service\n"
"\n" "\n"
" --uninstall Uninstall service\n" " --uninstall Uninstall service\n"
" This will remove the service and delete C:\\windows\\bridge.exe\n" " This will remove the service and delete C:\\windows\\bridge.exe\n"
"\n" "\n"
" --steam Reserved for Steam\n" " --steam Reserved for Steam\n"
" This will start the service and exit (used with bridge.sh)\n" " This will start the service and exit (used with bridge.sh)\n"
"\n" "\n"
" --no-service Do not run as service\n" " --no-service Do not run as service\n"
" (only for --steam)\n" " (only for --steam)\n"
"\n" "\n"
" --service Reserved for service\n" " --service Reserved for service\n"
"\n" "\n"
" --rpc <dir> Set RPC_PATH environment variable\n" " --rpc <dir> Set RPC_PATH environment variable\n"
" This is used to specify the directory where 'discord-ipc-0' is located\n" " This is used to specify the directory where 'discord-ipc-0' is located\n"
"\n" "\n"
"Note: If no arguments are provided, the GUI will be shown instead\n", "Note: If no arguments are provided, the GUI will be shown instead\n",
argv[0]); argv[0]);
ExitProcess(0); ExitProcess(0);
} }
} }
@ -311,22 +310,16 @@ void HandleArguments(int argc, char *argv[])
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
DetectWine(); DetectWine();
if (OSInfo.IsWine) char *logFilePath = "C:\\windows\\logs\\bridge.log";
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)
{ {
printf("Failed to open logs file: %ld\n", MessageBox(NULL, "Failed to open logs file", "Error", MB_OK | MB_ICONERROR);
GetLastError()); printf("Failed to open logs file: %ld\n", GetLastError());
ExitProcess(1); ExitProcess(1);
} }
print("rpc-bridge v%s %s-%s\n", VER_VERSION_STR, GIT_BRANCH, GIT_COMMIT);
if (argc > 1) if (argc > 1)
HandleArguments(argc, argv); HandleArguments(argc, argv);
else else

View File

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

View File

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

View File

@ -8,6 +8,7 @@ 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)
{ {
@ -87,6 +88,14 @@ 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)
{ {