Use OneShot version of Steamshim

This commit is contained in:
Struma 2020-03-02 15:36:53 -05:00 committed by Roza
parent f859c69b02
commit 8b10b72d2a
6 changed files with 158 additions and 45 deletions

View file

@ -7,8 +7,8 @@ if [ -n "$1" ]; then
mkdir -p "${MESON_INSTALL_PREFIX}/Contents/libs"
cp "$1/libsteam_api.dylib" "${MESON_INSTALL_PREFIX}/Contents/libs"
install_name_tool -change "@loader_path/libsteam_api.dylib" "@executable_path/../libs/libsteam_api.dylib" $EXE
install_name_tool -add_rpath "@executable_path/../libs" ${EXE}_child
macpack ${EXE}_child
install_name_tool -add_rpath "@executable_path/../libs" ${EXE}_rt
macpack ${EXE}_rt
else
install_name_tool -add_rpath "@executable_path/../libs" ${EXE}
macpack ${EXE}

View file

@ -160,10 +160,10 @@ endif
exe_name = meson.project_name()
if steamworks == true
exe_name = meson.project_name() + '_child'
exe_name = meson.project_name() + '_rt'
la = ''
if build_static == true
la = '-static-libgcc -static-libstdc++'
la = '-static'
endif
shim_args = ['-DGAME_LAUNCH_NAME="' + exe_name + '"']
if get_option('steam_appid') != ''

View file

@ -1,3 +1,10 @@
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.
## Original Steamshim README:
```
What is this?
- This is a small piece of code, written in C++, that acts as a bridge between
@ -62,5 +69,4 @@ Questions? Ask me.
--ryan.
icculus@icculus.org
```

View file

@ -9,7 +9,6 @@ typedef __int32 int32;
typedef unsigned __int64 uint64;
#else
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
@ -22,14 +21,16 @@ typedef uint64_t uint64;
typedef int PipeType;
#define NULLPIPE -1
#endif
#include <stdlib.h>
#include "steamshim_child.h"
#define DEBUGPIPE 1
#if DEBUGPIPE
#ifdef STEAMSHIM_DEBUG
#define dbgpipe printf
#else
static inline void dbgpipe(const char *fmt, ...) {}
static inline void dbgpipe(const char *fmt, ...) {
(void)fmt;
}
#endif
static int writePipe(PipeType fd, const void *buf, const unsigned int _len);
@ -66,12 +67,11 @@ static void closePipe(PipeType fd)
CloseHandle(fd);
} /* closePipe */
static char *getEnvVar(const char *key, char *buf, const size_t _buflen)
static char *getEnvVar(const char *key, char *buf, const size_t buflen)
{
const DWORD buflen = (DWORD) _buflen;
const DWORD rc = GetEnvironmentVariableA(key, val, buflen);
const DWORD rc = GetEnvironmentVariableA(key, buf, buflen);
/* rc doesn't count null char, hence "<". */
return ((rc > 0) && (rc < buflen)) ? NULL : buf;
return ((rc > 0) && (rc < buflen)) ? buf : NULL;
} /* getEnvVar */
#else
@ -133,6 +133,8 @@ typedef enum ShimCmd
SHIMCMD_GETSTATI,
SHIMCMD_SETSTATF,
SHIMCMD_GETSTATF,
SHIMCMD_GETPERSONANAME,
SHIMCMD_GETCURRENTGAMELANGUAGE,
} ShimCmd;
static int write1ByteCmd(const uint8 b1)
@ -156,22 +158,15 @@ static inline int writeBye(void)
static int initPipes(void)
{
char buf[64];
unsigned long long val;
if (!getEnvVar("STEAMSHIM_READHANDLE", buf, sizeof (buf)))
return 0;
else if (sscanf(buf, "%llu", &val) != 1)
return 0;
else
GPipeRead = (PipeType) val;
GPipeRead = (PipeType) strtoull(buf, 0, 10);
if (!getEnvVar("STEAMSHIM_WRITEHANDLE", buf, sizeof (buf)))
return 0;
else if (sscanf(buf, "%llu", &val) != 1)
return 0;
else
GPipeWrite = (PipeType) val;
GPipeWrite = (PipeType) strtoull(buf, 0, 10);
return ((GPipeRead != NULLPIPE) && (GPipeWrite != NULLPIPE));
} /* initPipes */
@ -185,7 +180,9 @@ int STEAMSHIM_init(void)
return 0;
} /* if */
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
dbgpipe("Child init success!\n");
return 1;
@ -205,7 +202,9 @@ void STEAMSHIM_deinit(void)
GPipeRead = GPipeWrite = NULLPIPE;
#ifndef _WIN32
signal(SIGPIPE, SIG_DFL);
#endif
} /* STEAMSHIM_deinit */
static inline int isAlive(void)
@ -233,7 +232,7 @@ static const STEAMSHIM_Event *processEvent(const uint8 *buf, size_t buflen)
event.type = type;
event.okay = 1;
#if DEBUGPIPE
#ifdef STEAMSHIM_DEBUG
if (0) {}
#define PRINTGOTEVENT(x) else if (type == x) printf("Child got " #x ".\n")
PRINTGOTEVENT(SHIMEVENT_BYE);
@ -246,6 +245,8 @@ static const STEAMSHIM_Event *processEvent(const uint8 *buf, size_t buflen)
PRINTGOTEVENT(SHIMEVENT_GETSTATI);
PRINTGOTEVENT(SHIMEVENT_SETSTATF);
PRINTGOTEVENT(SHIMEVENT_GETSTATF);
PRINTGOTEVENT(SHIMEVENT_GETPERSONANAME);
PRINTGOTEVENT(SHIMEVENT_GETCURRENTGAMELANGUAGE);
#undef PRINTGOTEVENT
else printf("Child got unknown shimevent %d.\n", (int) type);
#endif
@ -300,6 +301,11 @@ static const STEAMSHIM_Event *processEvent(const uint8 *buf, size_t buflen)
strcpy(event.name, (const char *) buf);
break;
case SHIMEVENT_GETPERSONANAME:
case SHIMEVENT_GETCURRENTGAMELANGUAGE:
strcpy(event.name, (const char *) buf);
break;
default: /* uh oh */
return NULL;
} /* switch */
@ -440,5 +446,18 @@ void STEAMSHIM_getStatF(const char *name)
writeStatThing(SHIMCMD_GETSTATF, name, NULL, 0);
} /* STEAMSHIM_getStatF */
/* end of steamshim_child.c ... */
void STEAMSHIM_getPersonaName()
{
if (isDead()) return;
dbgpipe("Child sending SHIMCMD_GETPERSONANAME().\n");
write1ByteCmd(SHIMCMD_GETPERSONANAME);
} /* STEAMSHIM_getPersonaName */
void STEAMSHIM_getCurrentGameLanguage()
{
if (isDead()) return;
dbgpipe("Child sending SHIMCMD_GETCURRENTGAMELANGUAGE().\n");
write1ByteCmd(SHIMCMD_GETCURRENTGAMELANGUAGE);
} /* STEAMSHIM_getCurrentGameLanguage */
/* end of steamshim_child.c ... */

View file

@ -17,6 +17,8 @@ typedef enum STEAMSHIM_EventType
SHIMEVENT_GETSTATI,
SHIMEVENT_SETSTATF,
SHIMEVENT_GETSTATF,
SHIMEVENT_GETPERSONANAME,
SHIMEVENT_GETCURRENTGAMELANGUAGE,
} STEAMSHIM_EventType;
/* not all of these fields make sense in a given event. */
@ -43,6 +45,8 @@ void STEAMSHIM_setStatI(const char *name, const int _val);
void STEAMSHIM_getStatI(const char *name);
void STEAMSHIM_setStatF(const char *name, const float val);
void STEAMSHIM_getStatF(const char *name);
void STEAMSHIM_getPersonaName();
void STEAMSHIM_getCurrentGameLanguage();
#ifdef __cplusplus
}
@ -51,4 +55,3 @@ void STEAMSHIM_getStatF(const char *name);
#endif /* include-once blocker */
/* end of steamshim_child.h ... */

View file

@ -1,34 +1,38 @@
//#define GAME_LAUNCH_NAME "testapp"
#ifndef GAME_LAUNCH_NAME
#error Please define your game exe name.
#define GAME_LAUNCH_NAME "oneshot"
#endif
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN 1
#define UNICODE
#include <windows.h>
typedef PROCESS_INFORMATION ProcessType;
typedef HANDLE PipeType;
#define NULLPIPE NULL
#define LLUFMT "%I64u"
#else
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <signal.h>
#include <fcntl.h>
typedef pid_t ProcessType;
typedef int PipeType;
#define NULLPIPE -1
#define LLUFMT "%llu"
#endif
#include <stdlib.h>
#include "steam/steam_api.h"
#define DEBUGPIPE 1
#if DEBUGPIPE
#ifdef STEAMSHIM_DEBUG
#define dbgpipe printf
#else
static inline void dbgpipe(const char *fmt, ...) {}
static inline void dbgpipe(const char *fmt, ...) {
(void)fmt;
}
#endif
/* platform-specific mainline calls this. */
@ -100,11 +104,57 @@ static bool setEnvVar(const char *key, const char *val)
return (SetEnvironmentVariableA(key, val) != 0);
} // setEnvVar
static bool launchChild(ProcessType *pid);
static LPWSTR genCommandLine()
{
return (CreateProcessW(TEXT(".\\") TEXT(GAME_LAUNCH_NAME) TEXT(".exe"),
GetCommandLineW(), NULL, NULL, TRUE, 0, NULL,
NULL, NULL, pid) != 0);
// Construct a command line with the appropriate filename
LPWSTR cmdline = GetCommandLineW();
// Find the index of the first argument after 0
int iFirstArg = -1;
bool quote = false;
bool whitespace = false;
for (int i = 0; cmdline[i]; ++i)
{
if (cmdline[i] == '"' && (i == 0 || cmdline[i-1] != '\\'))
{
quote = !quote;
whitespace = false;
}
else if (!quote && (cmdline[i] == ' ' || cmdline[i] == '\t'))
{
whitespace = true;
}
else
{
if (whitespace)
{
iFirstArg = i;
break;
}
whitespace = false;
}
}
// If it doesn't exist, that must mean there are no arguments,
// so just return GAME_LAUNCH_NAME
if (iFirstArg == -1)
return _wcsdup(TEXT("\".\\" GAME_LAUNCH_NAME ".exe\""));
// Create the new string
// (`".\.exe" ` == +9
LPWSTR newcmdline = (LPWSTR)malloc(sizeof(TEXT(GAME_LAUNCH_NAME))
+ sizeof(WCHAR) * (wcslen(cmdline) - iFirstArg + 9));
wsprintf(newcmdline, TEXT("\".\\" GAME_LAUNCH_NAME ".exe\" %s"), cmdline + iFirstArg);
return newcmdline;
}
static bool launchChild(ProcessType *pid)
{
STARTUPINFOW si;
memset(&si, 0, sizeof(si));
return CreateProcessW(TEXT(".\\" GAME_LAUNCH_NAME ".exe"),
genCommandLine(), NULL, NULL, TRUE, 0, NULL,
NULL, &si, pid);
} // launchChild
static int closeProcess(ProcessType *pid)
@ -154,6 +204,8 @@ static bool createPipes(PipeType *pPipeParentRead, PipeType *pPipeParentWrite,
int fds[2];
if (pipe(fds) == -1)
return 0;
fcntl(fds[0], F_SETFL, 0);
fcntl(fds[1], F_SETFL, 0);
*pPipeParentRead = fds[0];
*pPipeChildWrite = fds[1];
@ -164,6 +216,8 @@ static bool createPipes(PipeType *pPipeParentRead, PipeType *pPipeParentWrite,
return 0;
} // if
fcntl(fds[0], F_SETFL, 0);
fcntl(fds[1], F_SETFL, 0);
*pPipeChildRead = fds[0];
*pPipeParentWrite = fds[1];
@ -192,11 +246,15 @@ static bool launchChild(ProcessType *pid)
return true; // we'll let the pipe fail if this didn't work.
// we're the child.
char buf[350] = {0};
#ifdef __APPLE__
char buf[300];
strncpy(buf, GArgv[0], sizeof(buf));
strcat(buf, "_child");
strcat(buf, "_rt");
GArgv[0] = buf;
//GArgv[0] = strdup("./" GAME_LAUNCH_NAME);
#else
GArgv[0] = strdup("./" GAME_LAUNCH_NAME);
#endif
dbgpipe("Starting %s\n", GArgv[0]);
execvp(GArgv[0], GArgv);
// still here? It failed! Terminate, closing child's ends of the pipes.
_exit(1);
@ -229,6 +287,8 @@ class SteamBridge;
static ISteamUserStats *GSteamStats = NULL;
static ISteamUtils *GSteamUtils = NULL;
static ISteamUser *GSteamUser = NULL;
static ISteamFriends *GSteamFriends = NULL;
static ISteamApps *GSteamApps = NULL;
static AppId_t GAppID = 0;
static uint64 GUserID = 0;
static SteamBridge *GSteamBridge = NULL;
@ -257,6 +317,8 @@ typedef enum ShimCmd
SHIMCMD_GETSTATI,
SHIMCMD_SETSTATF,
SHIMCMD_GETSTATF,
SHIMCMD_GETPERSONANAME,
SHIMCMD_GETCURRENTGAMELANGUAGE,
} ShimCmd;
typedef enum ShimEvent
@ -271,6 +333,8 @@ typedef enum ShimEvent
SHIMEVENT_GETSTATI,
SHIMEVENT_SETSTATF,
SHIMEVENT_GETSTATF,
SHIMEVENT_GETPERSONANAME,
SHIMEVENT_GETCURRENTGAMELANGUAGE,
} ShimEvent;
static bool write1ByteCmd(PipeType fd, const uint8 b1)
@ -291,6 +355,14 @@ static bool write3ByteCmd(PipeType fd, const uint8 b1, const uint8 b2, const uin
return writePipe(fd, buf, sizeof (buf));
} // write3ByteCmd
static bool writeString(PipeType fd, ShimEvent event, const char *str)
{
uint8 buf[256];
buf[0] = strlen(str) + 2;
buf[1] = (uint8) event;
strcpy((char *) buf + 2, str);
return writePipe(fd, buf, buf[0] + 1);
} // writeString
static inline bool writeBye(PipeType fd)
{
@ -328,7 +400,7 @@ static bool writeAchievementGet(PipeType fd, const char *name, const int status,
{
uint8 buf[256];
uint8 *ptr = buf+1;
dbgpipe("Parent sending SHIMEVENT_GETACHIEVEMENT('%s', status %d, time %llu).\n", name, status, (unsigned long long) time);
dbgpipe("Parent sending SHIMEVENT_GETACHIEVEMENT('%s', status %d, time " LLUFMT ").\n", name, status, (unsigned long long) time);
*(ptr++) = (uint8) SHIMEVENT_GETACHIEVEMENT;
*(ptr++) = (uint8) status;
memcpy(ptr, &time, sizeof (time));
@ -414,7 +486,7 @@ static bool processCommand(const uint8 *buf, unsigned int buflen, PipeType fd)
const ShimCmd cmd = (ShimCmd) *(buf++);
buflen--;
#if DEBUGPIPE
#if STEAMSHIM_DEBUG
if (false) {}
#define PRINTGOTCMD(x) else if (cmd == x) printf("Parent got " #x ".\n")
PRINTGOTCMD(SHIMCMD_BYE);
@ -428,6 +500,8 @@ static bool processCommand(const uint8 *buf, unsigned int buflen, PipeType fd)
PRINTGOTCMD(SHIMCMD_GETSTATI);
PRINTGOTCMD(SHIMCMD_SETSTATF);
PRINTGOTCMD(SHIMCMD_GETSTATF);
PRINTGOTCMD(SHIMCMD_GETPERSONANAME);
PRINTGOTCMD(SHIMCMD_GETCURRENTGAMELANGUAGE);
#undef PRINTGOTCMD
else printf("Parent got unknown shimcmd %d.\n", (int) cmd);
#endif
@ -534,6 +608,16 @@ static bool processCommand(const uint8 *buf, unsigned int buflen, PipeType fd)
writeGetStatF(fd, name, 0.0f, false);
} // if
break;
case SHIMCMD_GETPERSONANAME:
dbgpipe("Parent sending SHIMEVENT_GETPERSONANAME.\n");
writeString(fd, SHIMEVENT_GETPERSONANAME, GSteamFriends->GetPersonaName());
break;
case SHIMCMD_GETCURRENTGAMELANGUAGE:
dbgpipe("Parent sending SHIMEVENT_GETCURRENTGAMELANGUAGE.\n");
writeString(fd, SHIMEVENT_GETCURRENTGAMELANGUAGE, GSteamApps->GetCurrentGameLanguage());
break;
} // switch
return true; // keep going.
@ -580,11 +664,11 @@ static void processCommands(PipeType pipeParentRead, PipeType pipeParentWrite)
static bool setEnvironmentVars(PipeType pipeChildRead, PipeType pipeChildWrite)
{
char buf[64];
snprintf(buf, sizeof (buf), "%llu", (unsigned long long) pipeChildRead);
snprintf(buf, sizeof (buf), LLUFMT, (unsigned long long) pipeChildRead);
if (!setEnvVar("STEAMSHIM_READHANDLE", buf))
return false;
snprintf(buf, sizeof (buf), "%llu", (unsigned long long) pipeChildWrite);
snprintf(buf, sizeof (buf), LLUFMT, (unsigned long long) pipeChildWrite);
if (!setEnvVar("STEAMSHIM_WRITEHANDLE", buf))
return false;
@ -607,6 +691,8 @@ static bool initSteamworks(PipeType fd)
GSteamStats = SteamUserStats();
GSteamUtils = SteamUtils();
GSteamUser = SteamUser();
GSteamFriends = SteamFriends();
GSteamApps = SteamApps();
GAppID = GSteamUtils ? GSteamUtils->GetAppID() : 0;
GUserID = GSteamUser ? GSteamUser->GetSteamID().ConvertToUint64() : 0;
@ -675,4 +761,3 @@ static int mainline(void)
} // mainline
// end of steamshim_parent.cpp ...