Skip to content

Commit

Permalink
First pile of work.
Browse files Browse the repository at this point in the history
  • Loading branch information
icculus committed Jul 25, 2013
0 parents commit d94bef2
Showing 1 changed file with 316 additions and 0 deletions.
316 changes: 316 additions & 0 deletions steamshim.cpp
@@ -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 ...

0 comments on commit d94bef2

Please sign in to comment.