Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 29 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,39 @@
## openspy-client

openspy-client is a universal modification for old PC games to replace the now-defunct GameSpy Arcade service with an open-source alternative.
**openspy-client** is a universal modification for old PC games to replace the now-defunct GameSpy Arcade service with an open-source alternative.

This allows the multiplayer feature of those games to be used again.

The openspy client module is unobtrusive, meaning no game files need to be altered. It is loaded into the game's memory and redirects the requests from GameSpy to OpenSpy on the fly. It is very light and does not carry any performance penalty.
The **openspy-client** module is unobtrusive, meaning no game files need to be altered. It is loaded into the game's memory and redirects the requests from GameSpy to OpenSpy on the fly. It is very light and does not carry any performance penalty.

On this early stage, only a few games are listed as supported. However, due to the generic implementation, many games should be already supported.
In this early stage, only a few games are listed as supported. However, due to the generic implementation, many games should be already supported.

Help us by [reporting the games you found working](https://github.com/anzz1/openspy-client/issues/new?labels=working+game&template=report-working-game.yml&title=%5Bworking-game%5D+GAME+NAME) so we can add them to the list and [reporting the ones which don't](https://github.com/anzz1/openspy-client/issues/new?labels=game+request&template=request-for-game-support.yml&title=%5Bgame-request%5D+GAME+NAME) so we can work on adding the support.
Help us by [reporting the games you found working](https://github.com/anzz1/openspy-client/issues/new?labels=working+game&template=report-working-game.yml&title=%5Bworking-game%5D+GAME+NAME) so we can add them to the list, and [reporting the ones which don't](https://github.com/anzz1/openspy-client/issues/new?labels=game+request&template=request-for-game-support.yml&title=%5Bgame-request%5D+GAME+NAME) so we can work on adding the support.

Aim is to eventually support 100% of GameSpy Arcade titles.
The goal is to eventually support 100% of GameSpy Arcade titles.

Compatible with Windows XP / Vista / 7 / 8 / 8.1 / 10 / 11 and Server 2003 / 2003 R2 / 2008 / 2008 R2 / 2012 / 2012 R2 / 2016 / 2019 / 2022
Compatible with Windows XP / Vista / 7 / 8 / 8.1 / 10 / 11 and Server 2003 / 2003 R2 / 2008 / 2008 R2 / 2012 / 2012 R2 / 2016 / 2019 / 2022.

## How to install

1. Download the [latest release](https://github.com/anzz1/openspy-client/releases/latest/download/openspy.zip)
1. Download the [latest release](https://github.com/anzz1/openspy-client/releases/latest/download/openspy.zip).

2. Install
1. 32-bit games
2. Install:
1. For 32-bit games:
1. Extract the `openspy.x86.dll` file to the game folder, next to the game executable.
2. Rename the `openspy.x86.dll` to what is listed in the [supported games list](#supported-games-list)
2. Rename the `openspy.x86.dll` to the name listed in the [supported games list](#supported-games-list).

2. 64-bit games
2. For 64-bit games:
1. Extract the `openspy.x64.dll` file to the game folder, next to the game executable.
2. Rename the `openspy.x64.dll` to what is listed in the [supported games list](#supported-games-list)
2. Rename the `openspy.x64.dll` to the name listed in the [supported games list](#supported-games-list).

3. Play !
3. Play!

## Account creation
If the game requires an account to play, you can create one in game.

If the game does not have an option to create an account, you can [create one here](http://account.openspy.net/).
If the game requires an account to play, you can create one in-game.

Please note that the GameSpy protocol is old and does not meet modern password encryption security standards, so do not use a password which you have used elsewhere.
Please note that the GameSpy protocol is old and does not meet modern password encryption security standards, so **do not** use a password which you have used elsewhere.

## Supported games list

Expand Down Expand Up @@ -96,6 +95,8 @@ Please note that the GameSpy protocol is old and does not meet modern password e
| Serious Sam 2 | <picture><img src="./.github/img/disc.png" title="Retail" alt="(Retail)" /></picture> | `dsound.dll` | n/a | |
| Sniper Elite | <picture><img src="./.github/img/disc.png" title="Retail" alt="(Retail)" /></picture>&nbsp;<a href="https://www.gog.com/en/game/sniper_elite_berlin_1945"><img src="./.github/img/gog.png" title="GOG" alt="(GOG)" /></a>&nbsp;<a href="https://store.steampowered.com/app/3700/Sniper_Elite/"><img src="./.github/img/steam.png" title="Steam" alt="(Steam)" /></a> | `dinput8.dll` | n/a | |
| Startopia | <picture><img src="./.github/img/disc.png" title="Retail" alt="(Retail)" /></picture>&nbsp;<a href="https://www.gog.com/game/startopia"><img src="./.github/img/gog.png" title="GOG" alt="(GOG)" /></a>&nbsp;<a href="https://store.steampowered.com/app/243040/Startopia/"><img src="./.github/img/steam.png" title="Steam" alt="(Steam)" /></a> | `dsound.dll` | n/a | |
| Star Trek: Armada II | <picture><img src="./.github/img/disc.png" title="Retail" alt="(Retail)" /></picture>&nbsp;<a href="https://www.gog.com/en/game/star_trek_armada_ii"><img src="./.github/img/gog.png" title="GOG" alt="(GOG)" /></a> | `dsound.dll` | n/a | <sub><sup>[1]</sup> Latest [v1.1 update](http://armadafiles.com/files/armada-2/official-releases/patches/star-trek-armada-ii-patch-11/details) is recommended&nbsp;&nbsp;<sup>**RETAIL**</sup><br><sup>[2]</sup> Also works with free [Demo](http://armadafiles.com/files/armada-2/official-releases/demo/star-trek-armada-ii-demo/details)</sub> |
| Star Trek: Armada II Demo | <picture><img src="./.github/img/disc.png" title="Retail" alt="(Retail)" /></picture> | `dsound.dll` | n/a | <sub><sup>[1]</sup> [Free download](http://armadafiles.com/files/armada-2/official-releases/demo/star-trek-armada-ii-demo/details)</sub> |
| Star Trek: Bridge Commander | <picture><img src="./.github/img/disc.png" title="Retail" alt="(Retail)" /></picture>&nbsp;<a href="https://www.gog.com/en/game/star_trek_bridge_commander"><img src="./.github/img/gog.png" title="GOG" alt="(GOG)" /></a> | `dinput.dll` | n/a | |
| Star Trek: Legacy | <picture><img src="./.github/img/disc.png" title="Retail" alt="(Retail)" /></picture> | `dinput8.dll` | n/a | <sub><sup>[1]</sup> Latest [v1.2 update](https://taco.cab/files/games/stlegacy/STLegacyv1.2.zip) is recommended</sub> |
| Star Wars: Republic Commando | <picture><img src="./.github/img/disc.png" title="Retail" alt="(Retail)" /></picture>&nbsp;<a href="https://www.gog.com/en/game/star_wars_republic_commando"><img src="./.github/img/gog.png" title="GOG" alt="(GOG)" /></a>&nbsp;<a href="https://store.steampowered.com/app/6000/STAR_WARS_Republic_Commando/"><img src="./.github/img/steam.png" title="Steam" alt="(Steam)" /></a> | `version.dll` | n/a | |
Expand Down Expand Up @@ -141,32 +142,33 @@ Please note that the GameSpy protocol is old and does not meet modern password e
| [report unsupported game](https://github.com/anzz1/openspy-client/issues/new?labels=game+request&template=request-for-game-support.yml&title=%5Bgame-request%5D+GAME+NAME) |

## Remarks
To uninstall, simply delete the `openspy.dll` file.
To uninstall, simply delete the renamed `openspy.dll` file from the game directory.

If a game is not listed, you can try renaming it as the different variations openspy-client currently supports:
If a game is not listed, you can try renaming it as the different variations **openspy-client** currently supports:
- `dinput.dll`
- `dinput8.dll`
- `dsound.dll`
- `version.dll`
- `winmm.dll`

Please report back with your findings to help us fill the supported games list.
Please report back with your findings to help us add to the [supported games list](#supported-games-list).

Generally it does not matter how the openspy-client module is loaded into the game. This means that in addition to the variations supported, you can also use any other method to load the module into the game.
Generally, it does not matter how the **openspy-client** module is loaded into the game. This means that in addition to the DLL variations supported, you can also use any other method to load the module into the game.

For example, if you use an [ASI loader](https://github.com/ThirteenAG/Ultimate-ASI-Loader) to load mods like [Widescreen Fix](https://thirteenag.github.io/wfp) for your game, you can simply rename the OpenSpy module to `openspy.asi` and place it next to the `WideScreenFix.asi` and it will be loaded that way.


---

:earth_americas: [Web](https://beta.openspy.net/) &emsp;
<img alt="Discord" src="https://user-images.githubusercontent.com/13628128/226210682-c9044ed1-e4d9-431c-b085-1d684a9f9942.png" width="20" height="20"> [Discord](http://discord.gg/sMaWdbt)
🌎 [Web](https://openspy.net/)

This component is a part of the [OpenSpy](https://beta.openspy.net/) project
<img alt="Discord" src="https://user-images.githubusercontent.com/13628128/226210682-c9044ed1-e4d9-431c-b085-1d684a9f9942.png" height="20">&nbsp;[Discord](http://discord.gg/sMaWdbt)

- [openspy-core-v2](https://github.com/chc/openspy-core-v2)
- [openspy-web-backend](https://github.com/chc/openspy-web-backend)
- [openspy-natneg-helper](https://github.com/chc/openspy-natneg-helper)
- [openspy-discord-bot](https://github.com/chc/openspy-discord-bot)
This component is a part of the [OpenSpy](https://openspy.net/) project:

- [openspy-core](https://github.com/openspy/openspy-core)
- [openspy-web-backend](https://github.com/openspy/openspy-web-backend)
- [openspy-natneg-helper](https://github.com/openspy/natneg-helper)
- [openspy-discord-bot](https://github.com/openspy/openspy-discord-bot)
- openspy-client

3 changes: 3 additions & 0 deletions dllmain.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "include/game_thug2.h"
#include "include/game_rof.h"
#include "include/game_hd2.h"
#include "include/game_sta2.h"
#include "include/game_stbc.h"
#include "include/game_bfme2.h"
#include "include/game_blood2.h"
Expand Down Expand Up @@ -265,6 +266,8 @@ int __stdcall DllMain(HINSTANCE hInstDLL, DWORD dwReason, LPVOID lpReserved) {
patch_rof();
} else if (!__stricmp(p, "hd2.exe") || !__stricmp(p, "hd2ds.exe") || !__stricmp(p, "hd2_sabresquadron.exe") || !__stricmp(p, "hd2ds_sabresquadron.exe")) { // Hidden & Dangerous 2
patch_hd2();
} else if (!__stricmp(p, "Armada2.exe") || !__stricmp(p, "Armada2Demo.exe")) { // Star Trek: Armada II
patch_sta2();
} else if (!__stricmp(p, "stbc.exe")) { // Star Trek - Bridge Commander
patch_stbc();
} else if (!__stricmp(p, "blood2.exe") || !__stricmp(p, "blood2sv.exe") || !__stricmp(p, "b2nmsrv.exe")) { // Blood II - The Chosen
Expand Down
129 changes: 129 additions & 0 deletions include/game_sta2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// game_sta2.h

#ifndef __GAME_STA2_H
#define __GAME_STA2_H

/* According to the game's "NetHelp.txt", the following ports are required:

DirectPlay 8 Ports

2300..2400 UDP inbound and outbound
6073 TCP inbound and outbound

GameSpy Ports

80 TCP, outbound
6500 UDP, inbound
6667 TCP, outbound
13139 UDP, inbound and outbound
27900 UDP, inbound and outbound
28900 TCP, outbound
29900 TCP, outbound
29901 TCP, outbound

The documentation stating port 6073 as TCP seems to be an error. 6073/udp gets dynamically mapped by picoupnp,
and according to Microsoft documentation [MC-DPLHP], the standard assignment for directplay8 uses 6073/udp.

NAT-PMP is used to map the GameSpy ports, and UPNP is used to map the DirectPlay ports, so both must be enabled on the host's router.
Clients do not seem to need to have port forwarding in place to be able to join a game and play.

Port 27900 is stated to be required but is not automatically mapped, and does not seem to be required for multiplayer functionality.
Even with a manually created NAT rule for 27900/udp, there are no hits.

~ Ligushka 2/7/2025
*/

#include "include/global.h"
#include "iathook/iathook.h"

int __stdcall hk_bind(SOCKET s, struct sockaddr* addr, int namelen);
LPHOSTENT __stdcall hk_gethostbyname(const char* name);

LPHOSTENT __stdcall sta2_hk_gethostbyname(const char* name) {
// The game's "gamespy.cfg" specifies this server address several times, to "re-direct through activision".
// Any other GameSpy references are handled by openspy-client's globally hooked gethostbyname function.
if (name && !__strcmp(name, "STArmada2.activision.com"))
return ogethostbyname("master.openspy.net");
// Speed up the lobby connection by avoiding a DNS lookup timeout by redirecting away from defunct MOTD domain to OpenSpy's.
else if (name && !__strcmp(name, "www.armada2.com"))
return ogethostbyname("motd.openspy.net");
else
return hk_gethostbyname(name);
}

__forceinline static void sta2_hook_gs(HMODULE mod) {
HOOK_FUNC(0, gethostbyname, sta2_hk_gethostbyname, "ws2_32.dll", 52, TRUE); // the only call to gethostbyname in armada2.exe is in Transport::GetLocalIPAddresses, so this may not be needed
HOOK_FUNC(mod, gethostbyname, sta2_hk_gethostbyname, "ws2_32.dll", 52, TRUE);
HOOK_FUNC(mod, bind, hk_bind, "ws2_32.dll", 2, TRUE);
}

// DirectPlay Voice initialization crashes the game on modern operating systems, just like Star Trek: Legacy.
// Even the GOG version still needs this, since their bypasses are only for TCP/IP LAN games.
__forceinline static void sta2_disable_net_voice(HMODULE mod) {

// in Network::DirectPlay::GameCreate, nop call to Network::Lobby::GetVoiceSettings and jz -> jmp past the voice check
BYTE search[] = { 0xE8, 0xF8, 0x04, 0x01, 0x00, 0x8A, 0x48, 0x28 };
BYTE patch[] = { 0xE9, 0xA4, 0x00, 0x00 };

// nop call to Network::Lobby::GetVoiceSettings
WORD search2[] = { _ANY, _ANY, _ANY, _ANY, 0x00, 0x8A, 0x48, 0x28, 0x51 };

// jnz -> jmp in Network::Lobby::GetVoiceSettings
BYTE search3[] = { 0x85, 0xC0, 0x75, 0x34, 0x56, 0x6A, 0x44, 0xE8 };
BYTE patch3[] = { 0xEB }; // jnz -> jmp

// jnz -> jmp in Network::Lobby::GetVoiceSettingsAvailable to always return 0
BYTE search4[] = { 0x74, 0x5E, 0x56, 0x8B, 0x35, 0x14 };

BYTE* ptr = find_pattern_mem((ULONG_PTR)mod, search, search + 7, TRUE);
if (ptr)
nop_mem(ptr, 10);
write_mem(ptr + 10, patch, 4);

ptr = find_pattern_mem_wildcard((ULONG_PTR)mod, search2, search2 + 8, TRUE);
if (ptr)
nop_mem(ptr, 8);

ptr = find_pattern_mem((ULONG_PTR)mod, search3, search3 + 7, TRUE);
if (ptr)
write_mem(ptr + 2, patch3, 1);

ptr = find_pattern_mem((ULONG_PTR)mod, search4, search4 + 5, TRUE);
if (ptr)
write_mem(ptr, patch3, 1);
}

// Fix crash in Program::SystemOpen when the system path environment variable is too long.
// Crash affects versions 1.1, 1.0, and demo.
// Patched in GOG version and community "Patch Project 1.2.5".
__forceinline static void sta2_fix_path_crash() {
BYTE search[] = { 0x85, 0xC0, 0x74, 0x3F, 0x68 };
BYTE patch[] = { 0xEB };

BYTE* ptr = find_pattern_mem(0, search, search + 4, TRUE);
if (ptr)
write_mem(ptr + 2, patch, 1);
}

// GOG version patches out a portion of this condition check of the return value of Transport::GetStatus in order to prevent entering the GameSpy lobby altogether.
__forceinline static void sta2_gog_restore_state_check() {
BYTE search[] = { 0x83, 0xF8, 0x16, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x83, 0xF8, 0x3A, 0x0F, 0x8D, 0x94, 0x00 };
BYTE patch[] = { 0x0F, 0x8E, 0x9D, 0x00, 0x00, 0x00 };

BYTE* ptr = find_pattern_mem(0, search, search + 15, TRUE);
if (ptr)
write_mem(ptr + 3, patch, 6);
}

__noinline static void patch_sta2() {
sta2_fix_path_crash();

HMODULE mod = GetModuleHandleA("NetworkManager.dll");
if (mod) {
sta2_hook_gs(mod);
sta2_disable_net_voice(mod);
sta2_gog_restore_state_check();
}
}

#endif // __GAME_STA2_H
Loading