mirror of
https://github.com/mkxp-z/mkxp-z.git
synced 2025-06-30 21:55:18 +02:00
Finally fix Steamshim for Windows
The C++ API blows everything up. The flat C exports don't. Go figure.
This commit is contained in:
parent
1dc86713e7
commit
e9a26fe624
4 changed files with 64 additions and 93 deletions
|
@ -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 = []
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue