Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit d94bef2
Showing
1 changed file
with
316 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,316 @@ | ||
#define GAME_LAUNCH_NAME "steelstorm" | ||
#ifndef GAME_LAUNCH_NAME | ||
#error Please define your game exe name. | ||
#endif | ||
|
||
#ifdef _WIN32 | ||
#define WIN32_LEAN_AND_MEAN 1 | ||
#include <windows.h> | ||
typedef PROCESS_INFORMATION ProcessType; | ||
typedef HANDLE PipeType; | ||
#define NULLPIPE NULL | ||
#else | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
#include <errno.h> | ||
#include <sys/wait.h> | ||
#include <signal.h> | ||
typedef pid_t ProcessType; | ||
typedef int PipeType; | ||
#define NULLPIPE -1 | ||
#endif | ||
|
||
#include "steam/steam_api.h" | ||
|
||
/* platform-specific mainline calls this. */ | ||
static int mainline(void); | ||
|
||
/* Windows and Unix implementations of this stuff below. */ | ||
static void fail(const char *err); | ||
static bool writePipe(PipeType fd, const void *buf, const unsigned int _len); | ||
static int readPipe(PipeType fd, void *buf, const unsigned int _len); | ||
static bool createPipes(PipeType *pPipeParentRead, PipeType *pPipeParentWrite, | ||
PipeType *pPipeChildRead, PipeType *pPipeChildWrite); | ||
static void closePipe(PipeType fd); | ||
static bool setEnvVar(const char *key, const char *val); | ||
static bool launchChild(ProcessType *pid); | ||
static int closeProcess(ProcessType *pid); | ||
|
||
#ifdef _WIN32 | ||
static void fail(const char *err) | ||
{ | ||
MessageBoxA(NULL, err, "ERROR", MB_ICONERROR | MB_OK); | ||
ExitProcess(1); | ||
} // fail | ||
|
||
static bool writePipe(PipeType fd, const void *buf, const unsigned int _len) | ||
{ | ||
const DWORD len = (DWORD) _len; | ||
DWORD bw = 0; | ||
return ((WriteFile(fd, buf, len, &bw, NULL) != 0) && (bw == len)); | ||
} // writePipe | ||
|
||
static int readPipe(PipeType fd, void *buf, const unsigned int _len) | ||
{ | ||
const DWORD len = (DWORD) _len; | ||
DWORD br = 0; | ||
return ReadFile(fd, buf, len, &br, NULL) ? (int) br : -1; | ||
} // readPipe | ||
|
||
static bool createPipes(PipeType *pPipeParentRead, PipeType *pPipeParentWrite, | ||
PipeType *pPipeChildRead, PipeType *pPipeChildWrite) | ||
{ | ||
SECURITY_ATTRIBUTES pipeAttr; | ||
|
||
pipeAttr.nLength = sizeof (pipeAttr); | ||
pipeAttr.lpSecurityDescriptor = NULL; | ||
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; | ||
} // if | ||
|
||
return 1; | ||
} // createPipes | ||
|
||
static void closePipe(PipeType fd) | ||
{ | ||
CloseHandle(fd); | ||
} // closePipe | ||
|
||
static bool setEnvVar(const char *key, const char *val) | ||
{ | ||
return (SetEnvironmentVariableA(key, val) != 0); | ||
} // setEnvVar | ||
|
||
static bool launchChild(ProcessType *pid); | ||
{ | ||
return (CreateProcessW(TEXT(".\\") TEXT(GAME_LAUNCH_NAME) TEXT(".exe"), | ||
GetCommandLineW(), NULL, NULL, TRUE, 0, NULL, | ||
NULL, NULL, pid) != 0); | ||
} // launchChild | ||
|
||
static int closeProcess(ProcessType *pid) | ||
{ | ||
CloseHandle(pid->hProcess); | ||
CloseHandle(pid->hThread); | ||
return 0; | ||
} // closeProcess | ||
|
||
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, | ||
LPSTR lpCmdLine, int nCmdShow) | ||
{ | ||
mainline(); | ||
ExitProcess(0); | ||
return 0; // just in case. | ||
} // WinMain | ||
|
||
|
||
#else // everyone else that isn't Windows. | ||
|
||
static void fail(const char *err) | ||
{ | ||
// !!! FIXME: zenity or something. | ||
fprintf(stderr, "%s\n", err); | ||
_exit(1); | ||
} // fail | ||
|
||
static bool writePipe(PipeType fd, const void *buf, const unsigned int _len) | ||
{ | ||
const ssize_t len = (ssize_t) _len; | ||
ssize_t bw; | ||
while (((bw = write(fd, buf, len)) == -1) && (errno == EINTR)) { /*spin*/ } | ||
return (bw == len); | ||
} // writePipe | ||
|
||
static int readPipe(PipeType fd, void *buf, const unsigned int _len) | ||
{ | ||
const ssize_t len = (ssize_t) _len; | ||
ssize_t br; | ||
while (((br = read(fd, buf, len)) == -1) && (errno == EINTR)) { /*spin*/ } | ||
return (int) br; | ||
} // readPipe | ||
|
||
static bool createPipes(PipeType *pPipeParentRead, PipeType *pPipeParentWrite, | ||
PipeType *pPipeChildRead, PipeType *pPipeChildWrite) | ||
{ | ||
int fds[2]; | ||
if (pipe(fds) == -1) | ||
return 0; | ||
*pPipeParentRead = fds[0]; | ||
*pPipeChildWrite = fds[1]; | ||
|
||
if (pipe(fds) == -1) | ||
{ | ||
close(*pPipeParentRead); | ||
close(*pPipeChildWrite); | ||
return 0; | ||
} // if | ||
|
||
*pPipeChildRead = fds[0]; | ||
*pPipeParentWrite = fds[1]; | ||
|
||
return 1; | ||
} // createPipes | ||
|
||
static void closePipe(PipeType fd) | ||
{ | ||
close(fd); | ||
} // closePipe | ||
|
||
static bool setEnvVar(const char *key, const char *val) | ||
{ | ||
return (setenv(key, val, 1) != -1); | ||
} // setEnvVar | ||
|
||
static int GArgc = 0; | ||
static char **GArgv = NULL; | ||
|
||
static bool launchChild(ProcessType *pid) | ||
{ | ||
*pid = fork(); | ||
if (*pid == -1) // failed | ||
return false; | ||
else if (*pid != 0) // we're the parent | ||
return true; // we'll let the pipe fail if this didn't work. | ||
|
||
// we're the child. | ||
GArgv[0] = strdup("./" GAME_LAUNCH_NAME); | ||
execvp(GArgv[0], GArgv); | ||
// still here? It failed! Terminate, closing child's ends of the pipes. | ||
_exit(1); | ||
} // launchChild | ||
|
||
static int closeProcess(ProcessType *pid) | ||
{ | ||
int rc; | ||
while ((waitpid(*pid, &rc, 0) == -1) && (errno == EINTR)) { /*spin*/ } | ||
if (!WIFEXITED(rc)) | ||
return 1; // oh well. | ||
return WEXITSTATUS(rc); | ||
} // closeProcess | ||
|
||
int main(int argc, char **argv) | ||
{ | ||
signal(SIGPIPE, SIG_IGN); | ||
signal(SIGCHLD, SIG_IGN); | ||
GArgc = argc; | ||
GArgv = argv; | ||
return mainline(); | ||
} // main | ||
|
||
#endif | ||
|
||
|
||
// THE ACTUAL PROGRAM. | ||
|
||
static ISteamUserStats *GStats = NULL; | ||
static bool GStatsAvailable = false; | ||
|
||
typedef enum ShimCmds | ||
{ | ||
SHIMCMD_SETACHIEVEMENT, | ||
SHIMCMD_GETACHIEVEMENT, | ||
SHIMCMD_SETSTAT, | ||
SHIMCMD_GETSTAT, | ||
} ShimCmds; | ||
|
||
static void processCommand(char *buf, const unsigned int buflen, PipeType fd) | ||
{ | ||
int cmd; | ||
|
||
if (buflen == 0) | ||
return; | ||
|
||
cmd = (int) *(buf++); | ||
switch (cmd) | ||
{ | ||
case SHIMCMD_SETACHIEVEMENT: break; | ||
case SHIMCMD_GETACHIEVEMENT: break; | ||
case SHIMCMD_SETSTAT: break; | ||
case SHIMCMD_GETSTAT: break; | ||
} // switch | ||
} // processCommand | ||
|
||
static void processCommands(PipeType pipeParentRead, PipeType pipeParentWrite) | ||
{ | ||
char buf[128]; | ||
ssize_t br; | ||
|
||
// read() blocks. | ||
while ((br = readPipe(pipeParentRead, buf, sizeof (buf))) > 0) | ||
processCommand(buf, (unsigned int) br, pipeParentWrite); | ||
} // processCommands | ||
|
||
static bool setEnvironmentVars(PipeType pipeChildRead, PipeType pipeChildWrite) | ||
{ | ||
char buf[64]; | ||
snprintf(buf, sizeof (buf), "%llu", (long long unsigned) pipeChildRead); | ||
if (!setEnvVar("STEAMSHIM_READHANDLE", buf)) | ||
return false; | ||
|
||
snprintf(buf, sizeof (buf), "%llu", (long long unsigned) pipeChildWrite); | ||
if (!setEnvVar("STEAMSHIM_WRITEHANDLE", buf)) | ||
return false; | ||
|
||
return true; | ||
} // setEnvironmentVars | ||
|
||
static bool initSteamworks(void) | ||
{ | ||
if (!SteamAPI_Init()) | ||
return 0; | ||
|
||
GStats = SteamUserStats(); | ||
if (GStats) | ||
GStats->RequestCurrentStats(); | ||
|
||
return 1; | ||
} // initSteamworks | ||
|
||
static int mainline(void) | ||
{ | ||
PipeType pipeParentRead = NULLPIPE; | ||
PipeType pipeParentWrite = NULLPIPE; | ||
PipeType pipeChildRead = NULLPIPE; | ||
PipeType pipeChildWrite = NULLPIPE; | ||
ProcessType childPid; | ||
|
||
if (!initSteamworks()) | ||
fail("Failed to initialize Steamworks"); | ||
else if (!createPipes(&pipeParentRead, &pipeParentWrite, &pipeChildRead, &pipeChildWrite)) | ||
fail("Failed to create application pipes"); | ||
else if (!setEnvironmentVars(pipeChildRead, pipeChildWrite)) | ||
fail("Failed to set environment variables"); | ||
else if (!launchChild(&childPid)) | ||
fail("Failed to launch application"); | ||
|
||
// Close the ends of the pipes that the child will use; we don't need them. | ||
closePipe(pipeChildRead); | ||
closePipe(pipeChildWrite); | ||
pipeChildRead = pipeChildWrite = NULLPIPE; | ||
|
||
// Now, we block for instructions until the pipe fails (child closed it or | ||
// terminated/crashed). | ||
processCommands(pipeParentRead, pipeParentWrite); | ||
|
||
// Close our ends of the pipes. | ||
closePipe(pipeParentRead); | ||
closePipe(pipeParentWrite); | ||
|
||
// Wait for the child to terminate, close the child process handles. | ||
return closeProcess(&childPid); | ||
} // mainline | ||
|
||
// end of steamshim.cpp ... | ||
|