Finally fix Steamshim for Windows

The C++ API blows everything up. The flat C exports don't. Go figure.
This commit is contained in:
Struma 2020-03-04 05:33:22 -05:00 committed by Roza
parent 1dc86713e7
commit e9a26fe624
4 changed files with 64 additions and 93 deletions

View file

@ -9,7 +9,7 @@ host_system = host_machine.system()
compilers = {'cpp': meson.get_compiler('cpp'), 'objc': meson.get_compiler('objc'), 'objcpp': meson.get_compiler('objcpp')} compilers = {'cpp': meson.get_compiler('cpp'), 'objc': meson.get_compiler('objc'), 'objcpp': meson.get_compiler('objcpp')}
if compilers['objc'].get_id() != 'clang' or compilers['objcpp'].get_id() != 'clang' if compilers['objc'].get_id() != 'clang' or compilers['objcpp'].get_id() != 'clang'
error('This program must be built with Clang! ( try: OBJC=clang OBJCXX=clang++ meson build )') error('This program must be built with Clang! ( export CC=clang OBJC=clang CXX=clang++ OBJCXX=clang++ )')
endif endif
global_sources = [] global_sources = []

View file

@ -1,10 +1,5 @@
This is a modified version of Steamshim used in [OneShot](https://github.com/elizagamedev/mkxp-oneshot) This is a modified version of Steamshim used in [OneShot](https://github.com/elizagamedev/mkxp-oneshot)
which should be compatible with Windows, further modified (not by much, at the moment) for mkxp-z. further modified for mkxp-z.
The key word, at the moment, is *"should"*. While it works fine on Unix, I found the Win32 code to be
basically completely nonfunctional. After some changes it will form the pipes and spawn the process
just fine, but steam_api will raise a memory access violation once it calls the shim's callbacks.
## Original Steamshim README: ## Original Steamshim README:

View file

@ -47,6 +47,7 @@ static int pipeReady(PipeType fd);
static int pipeReady(PipeType fd) static int pipeReady(PipeType fd)
{ {
return 1;
DWORD avail = 0; DWORD avail = 0;
return (PeekNamedPipe(fd, NULL, 0, NULL, &avail, NULL) && (avail > 0)); return (PeekNamedPipe(fd, NULL, 0, NULL, &avail, NULL) && (avail > 0));
} /* pipeReady */ } /* pipeReady */
@ -63,7 +64,7 @@ static int readPipe(PipeType fd, void *buf, const unsigned int _len)
{ {
const ssize_t len = (ssize_t) _len; const ssize_t len = (ssize_t) _len;
ssize_t br; ssize_t br;
while (((br = _read(fd, buf, len)) == -1) && (errno == EINTR)) { /*spin*/ } while (((br = _read(fd, buf, len)) == 0)) { /*spin*/ }
return (int) br; return (int) br;
} /* readPipe */ } /* readPipe */

View file

@ -1,16 +1,11 @@
#ifdef _WIN32 #ifdef _WIN32
#define WIN32_LEAN_AND_MEAN 1 #define WIN32_LEAN_AND_MEAN 1
#define UNICODE #define UNICODE
#include <cstdio>
#include <errno.h>
#include <fcntl.h>
#include <io.h>
#include <process.h>
#include <windows.h> #include <windows.h>
#include <shellapi.h> #include <stdio.h>
typedef int ProcessType; typedef PROCESS_INFORMATION ProcessType;
typedef int PipeType; typedef HANDLE PipeType;
#define NULLPIPE NULL #define NULLPIPE NULL
#define LLUFMT "%I64u" #define LLUFMT "%I64u"
#else #else
@ -28,7 +23,7 @@ typedef int PipeType;
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include "steam/steam_api.h" #include "steam/steam_api_flat.h"
#ifdef STEAMSHIM_DEBUG #ifdef STEAMSHIM_DEBUG
#define dbgpipe printf #define dbgpipe printf
@ -57,42 +52,40 @@ static void fail(const char *err) {
} // fail } // fail
static bool writePipe(PipeType fd, const void *buf, const unsigned int _len) { static bool writePipe(PipeType fd, const void *buf, const unsigned int _len) {
const ssize_t len = (ssize_t)_len; const DWORD len = (DWORD)_len;
ssize_t bw; DWORD bw = 0;
while (((bw = _write(fd, buf, len)) == -1) && (errno == EINTR)) { /*spin*/ return ((WriteFile(fd, buf, len, &bw, NULL) != 0) && (bw == len));
}
return (bw == len);
} // writePipe } // writePipe
static int readPipe(PipeType fd, void *buf, const unsigned int _len) { static int readPipe(PipeType fd, void *buf, const unsigned int _len) {
const ssize_t len = (ssize_t)_len; const DWORD len = (DWORD)_len;
ssize_t br; DWORD br = 0;
while (((br = _read(fd, buf, len)) == -1) && (errno == EINTR)) { /*spin*/ return ReadFile(fd, buf, len, &br, NULL) ? (int)br : -1;
}
return (int)br;
} // readPipe } // readPipe
static bool createPipes(PipeType *pPipeParentRead, PipeType *pPipeParentWrite, static bool createPipes(PipeType *pPipeParentRead, PipeType *pPipeParentWrite,
PipeType *pPipeChildRead, PipeType *pPipeChildWrite) { PipeType *pPipeChildRead, PipeType *pPipeChildWrite) {
int fds[2]; SECURITY_ATTRIBUTES pipeAttr;
if (_pipe(&fds[0], 1000, _O_BINARY) == -1)
return 0;
*pPipeParentRead = fds[0];
*pPipeChildWrite = fds[1];
if (_pipe(&fds[0], 1000, _O_BINARY) == -1) { pipeAttr.nLength = sizeof(pipeAttr);
_close(*pPipeParentRead); pipeAttr.lpSecurityDescriptor = NULL;
_close(*pPipeChildWrite); pipeAttr.bInheritHandle = TRUE;
if (!CreatePipe(pPipeParentRead, pPipeChildWrite, &pipeAttr, 0))
return 0;
pipeAttr.nLength = sizeof(pipeAttr);
pipeAttr.lpSecurityDescriptor = NULL;
pipeAttr.bInheritHandle = TRUE;
if (!CreatePipe(pPipeChildRead, pPipeParentWrite, &pipeAttr, 0)) {
CloseHandle(*pPipeParentRead);
CloseHandle(*pPipeChildWrite);
return 0; return 0;
} // if } // if
*pPipeChildRead = fds[0];
*pPipeParentWrite = fds[1];
return 1; return 1;
} // createPipes } // createPipes
static void closePipe(PipeType fd) { _close(fd); } // closePipe static void closePipe(PipeType fd) { CloseHandle(fd); } // closePipe
static bool setEnvVar(const char *key, const char *val) { static bool setEnvVar(const char *key, const char *val) {
return (SetEnvironmentVariableA(key, val) != 0); return (SetEnvironmentVariableA(key, val) != 0);
@ -137,19 +130,15 @@ static LPWSTR genCommandLine() {
} }
static bool launchChild(ProcessType *pid) { static bool launchChild(ProcessType *pid) {
int nargs; STARTUPINFOW si;
LPWSTR *argv = CommandLineToArgvW(genCommandLine(), &nargs); memset(&si, 0, sizeof(si));
ProcessType ret = return CreateProcessW(TEXT(".\\" GAME_LAUNCH_NAME ".exe"), genCommandLine(),
(ProcessType)_wspawnv(1, TEXT(".\\" GAME_LAUNCH_NAME ".exe"), argv); NULL, NULL, TRUE, 0, NULL, NULL, &si, pid);
if (ret == (ProcessType)-1)
return false;
*pid = ret;
return true;
} // launchChild } // launchChild
static int closeProcess(ProcessType *pid) { static int closeProcess(ProcessType *pid) {
_cwait(0, *pid, 0); CloseHandle(pid->hProcess);
CloseHandle(pid->hThread);
return 0; return 0;
} // closeProcess } // closeProcess
@ -273,10 +262,6 @@ static SteamBridge *GSteamBridge = NULL;
class SteamBridge { class SteamBridge {
public: public:
SteamBridge(PipeType _fd); SteamBridge(PipeType _fd);
STEAM_CALLBACK(SteamBridge, OnUserStatsReceived, UserStatsReceived_t,
m_CallbackUserStatsReceived);
STEAM_CALLBACK(SteamBridge, OnUserStatsStored, UserStatsStored_t,
m_CallbackUserStatsStored);
private: private:
PipeType fd; PipeType fd;
@ -435,25 +420,8 @@ static inline bool writeGetStatF(PipeType fd, const char *name, const float val,
return writeStatThing(fd, SHIMEVENT_GETSTATF, name, &val, sizeof(val), okay); return writeStatThing(fd, SHIMEVENT_GETSTATF, name, &val, sizeof(val), okay);
} // writeGetStatF } // writeGetStatF
SteamBridge::SteamBridge(PipeType _fd) SteamBridge::SteamBridge(PipeType _fd) : fd(_fd) {} // SteamBridge::SteamBridge
: m_CallbackUserStatsReceived(this, &SteamBridge::OnUserStatsReceived),
m_CallbackUserStatsStored(this, &SteamBridge::OnUserStatsStored),
fd(_fd) {} // SteamBridge::SteamBridge
void SteamBridge::OnUserStatsReceived(UserStatsReceived_t *pCallback) {
if (GAppID != pCallback->m_nGameID) {
return;
}
if (GUserID != pCallback->m_steamIDUser.ConvertToUint64()) {
return;
}
writeStatsReceived(fd, pCallback->m_eResult == k_EResultOK);
} // SteamBridge::OnUserStatsReceived
void SteamBridge::OnUserStatsStored(UserStatsStored_t *pCallback) {
if (GAppID != pCallback->m_nGameID)
return;
writeStatsStored(fd, pCallback->m_eResult == k_EResultOK);
} // SteamBridge::OnUserStatsStored
static bool processCommand(const uint8 *buf, unsigned int buflen, PipeType fd) { static bool processCommand(const uint8 *buf, unsigned int buflen, PipeType fd) {
if (buflen == 0) if (buflen == 0)
return true; return true;
@ -492,14 +460,12 @@ static bool processCommand(const uint8 *buf, unsigned int buflen, PipeType fd) {
return false; return false;
case SHIMCMD_REQUESTSTATS: case SHIMCMD_REQUESTSTATS:
if ((!GSteamStats) || (!GSteamStats->RequestCurrentStats())) writeStatsReceived(
writeStatsReceived(fd, false); fd, SteamAPI_ISteamUserStats_RequestCurrentStats(GSteamStats));
// callback later.
break; break;
case SHIMCMD_STORESTATS: case SHIMCMD_STORESTATS:
if ((!GSteamStats) || (!GSteamStats->StoreStats())) writeStatsStored(fd, SteamAPI_ISteamUserStats_StoreStats(GSteamStats));
writeStatsStored(fd, false);
// callback later. // callback later.
break; break;
@ -510,9 +476,11 @@ static bool processCommand(const uint8 *buf, unsigned int buflen, PipeType fd) {
(const char *)buf; // !!! FIXME: buffer overflow possible. (const char *)buf; // !!! FIXME: buffer overflow possible.
if (!GSteamStats) if (!GSteamStats)
writeAchievementSet(fd, name, enable, false); writeAchievementSet(fd, name, enable, false);
else if (enable && !GSteamStats->SetAchievement(name)) else if (enable &&
!SteamAPI_ISteamUserStats_SetAchievement(GSteamStats, name))
writeAchievementSet(fd, name, enable, false); writeAchievementSet(fd, name, enable, false);
else if (!enable && !GSteamStats->ClearAchievement(name)) else if (!enable &&
!SteamAPI_ISteamUserStats_ClearAchievement(GSteamStats, name))
writeAchievementSet(fd, name, enable, false); writeAchievementSet(fd, name, enable, false);
else else
writeAchievementSet(fd, name, enable, true); writeAchievementSet(fd, name, enable, true);
@ -526,7 +494,8 @@ static bool processCommand(const uint8 *buf, unsigned int buflen, PipeType fd) {
bool ach = false; bool ach = false;
uint32 t = 0; uint32 t = 0;
if ((GSteamStats) && if ((GSteamStats) &&
(GSteamStats->GetAchievementAndUnlockTime(name, &ach, &t))) (SteamAPI_ISteamUserStats_GetAchievementAndUnlockTime(
GSteamStats, name, &ach, &t)))
writeAchievementGet(fd, name, ach ? 1 : 0, t); writeAchievementGet(fd, name, ach ? 1 : 0, t);
else else
writeAchievementGet(fd, name, 2, 0); writeAchievementGet(fd, name, 2, 0);
@ -537,7 +506,8 @@ static bool processCommand(const uint8 *buf, unsigned int buflen, PipeType fd) {
if (buflen) { if (buflen) {
const bool alsoAch = (*(buf++) != 0); const bool alsoAch = (*(buf++) != 0);
writeResetStats(fd, alsoAch, writeResetStats(fd, alsoAch,
(GSteamStats) && (GSteamStats->ResetAllStats(alsoAch))); (GSteamStats) && (SteamAPI_ISteamUserStats_ResetAllStats(
GSteamStats, alsoAch)));
} // if } // if
break; break;
@ -548,7 +518,8 @@ static bool processCommand(const uint8 *buf, unsigned int buflen, PipeType fd) {
const char *name = const char *name =
(const char *)buf; // !!! FIXME: buffer overflow possible. (const char *)buf; // !!! FIXME: buffer overflow possible.
writeSetStatI(fd, name, val, writeSetStatI(fd, name, val,
(GSteamStats) && (GSteamStats->SetStat(name, val))); (GSteamStats) && (SteamAPI_ISteamUserStats_SetStatInt32(
GSteamStats, name, val)));
} // if } // if
break; break;
@ -557,7 +528,8 @@ static bool processCommand(const uint8 *buf, unsigned int buflen, PipeType fd) {
const char *name = const char *name =
(const char *)buf; // !!! FIXME: buffer overflow possible. (const char *)buf; // !!! FIXME: buffer overflow possible.
int32 val = 0; int32 val = 0;
if ((GSteamStats) && (GSteamStats->GetStat(name, &val))) if ((GSteamStats) &&
(SteamAPI_ISteamUserStats_GetStatInt32(GSteamStats, name, &val)))
writeGetStatI(fd, name, val, true); writeGetStatI(fd, name, val, true);
else else
writeGetStatI(fd, name, 0, false); writeGetStatI(fd, name, 0, false);
@ -571,7 +543,8 @@ static bool processCommand(const uint8 *buf, unsigned int buflen, PipeType fd) {
const char *name = const char *name =
(const char *)buf; // !!! FIXME: buffer overflow possible. (const char *)buf; // !!! FIXME: buffer overflow possible.
writeSetStatF(fd, name, val, writeSetStatF(fd, name, val,
(GSteamStats) && (GSteamStats->SetStat(name, val))); (GSteamStats) && (SteamAPI_ISteamUserStats_SetStatFloat(
GSteamStats, name, val)));
} // if } // if
break; break;
@ -580,7 +553,8 @@ static bool processCommand(const uint8 *buf, unsigned int buflen, PipeType fd) {
const char *name = const char *name =
(const char *)buf; // !!! FIXME: buffer overflow possible. (const char *)buf; // !!! FIXME: buffer overflow possible.
float val = 0; float val = 0;
if ((GSteamStats) && (GSteamStats->GetStat(name, &val))) if ((GSteamStats) &&
(SteamAPI_ISteamUserStats_GetStatFloat(GSteamStats, name, &val)))
writeGetStatF(fd, name, val, true); writeGetStatF(fd, name, val, true);
else else
writeGetStatF(fd, name, 0.0f, false); writeGetStatF(fd, name, 0.0f, false);
@ -589,13 +563,14 @@ static bool processCommand(const uint8 *buf, unsigned int buflen, PipeType fd) {
case SHIMCMD_GETPERSONANAME: case SHIMCMD_GETPERSONANAME:
dbgpipe("Parent sending SHIMEVENT_GETPERSONANAME.\n"); dbgpipe("Parent sending SHIMEVENT_GETPERSONANAME.\n");
writeString(fd, SHIMEVENT_GETPERSONANAME, GSteamFriends->GetPersonaName()); writeString(fd, SHIMEVENT_GETPERSONANAME,
SteamAPI_ISteamFriends_GetPersonaName(GSteamFriends));
break; break;
case SHIMCMD_GETCURRENTGAMELANGUAGE: case SHIMCMD_GETCURRENTGAMELANGUAGE:
dbgpipe("Parent sending SHIMEVENT_GETCURRENTGAMELANGUAGE.\n"); dbgpipe("Parent sending SHIMEVENT_GETCURRENTGAMELANGUAGE.\n");
writeString(fd, SHIMEVENT_GETCURRENTGAMELANGUAGE, writeString(fd, SHIMEVENT_GETCURRENTGAMELANGUAGE,
GSteamApps->GetCurrentGameLanguage()); SteamAPI_ISteamApps_GetCurrentGameLanguage(GSteamApps));
break; break;
} // switch } // switch
@ -656,14 +631,14 @@ static int initSteamworks(PipeType fd) {
if (!SteamAPI_Init()) if (!SteamAPI_Init())
return 0; return 0;
GSteamStats = SteamUserStats(); SteamInternal_Init_SteamUserStats(&GSteamStats);
GSteamUtils = SteamUtils(); SteamInternal_Init_SteamUtils(&GSteamUtils);
GSteamUser = SteamUser(); SteamInternal_Init_SteamUser(&GSteamUser);
GSteamFriends = SteamFriends(); SteamInternal_Init_SteamFriends(&GSteamFriends);
GSteamApps = SteamApps(); SteamInternal_Init_SteamApps(&GSteamApps);
GAppID = GSteamUtils ? SteamUtils()->GetAppID() : 0; GAppID = GSteamUtils ? SteamAPI_ISteamUtils_GetAppID(GSteamUtils) : 0;
GUserID = GSteamUser ? SteamUser()->GetSteamID().ConvertToUint64() : 0; GUserID = GSteamUser ? SteamAPI_ISteamUser_GetSteamID(GSteamUser) : 0;
GSteamBridge = new SteamBridge(fd); GSteamBridge = new SteamBridge(fd);
return 1; return 1;