- initial work to integrate theam SDK with megaglest
This commit is contained in:
parent
aebd07f3bb
commit
36bc6a4497
|
@ -186,6 +186,7 @@ IF(BUILD_MEGAGLEST)
|
||||||
menu
|
menu
|
||||||
network
|
network
|
||||||
sound
|
sound
|
||||||
|
steam
|
||||||
steamshim
|
steamshim
|
||||||
type_instances
|
type_instances
|
||||||
types
|
types
|
||||||
|
|
|
@ -25,6 +25,9 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
|
|
||||||
|
#include "steamshim_child.h"
|
||||||
|
#include "steam.h"
|
||||||
#include "game.h"
|
#include "game.h"
|
||||||
#include "main_menu.h"
|
#include "main_menu.h"
|
||||||
#include "program.h"
|
#include "program.h"
|
||||||
|
@ -142,6 +145,34 @@ class NavtiveLanguageNameListCacheGenerator : public SimpleTaskCallbackInterface
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
static void printEvent(const STEAMSHIM_Event *e)
|
||||||
|
{
|
||||||
|
if (!e) return;
|
||||||
|
|
||||||
|
printf("CHILD EVENT: ");
|
||||||
|
switch (e->type)
|
||||||
|
{
|
||||||
|
#define PRINTGOTEVENT(x) case SHIMEVENT_##x: printf("%s(", #x); break
|
||||||
|
PRINTGOTEVENT(BYE);
|
||||||
|
PRINTGOTEVENT(STATSRECEIVED);
|
||||||
|
PRINTGOTEVENT(STATSSTORED);
|
||||||
|
PRINTGOTEVENT(SETACHIEVEMENT);
|
||||||
|
PRINTGOTEVENT(GETACHIEVEMENT);
|
||||||
|
PRINTGOTEVENT(RESETSTATS);
|
||||||
|
PRINTGOTEVENT(SETSTATI);
|
||||||
|
PRINTGOTEVENT(GETSTATI);
|
||||||
|
PRINTGOTEVENT(SETSTATF);
|
||||||
|
PRINTGOTEVENT(GETSTATF);
|
||||||
|
#undef PRINTGOTEVENT
|
||||||
|
default: printf("UNKNOWN("); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%sokay, ival=%d, fval=%f, time=%llu, name='%s').\n",
|
||||||
|
e->okay ? "" : "!", e->ivalue, e->fvalue, e->epochsecs, e->name);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// =====================================================
|
// =====================================================
|
||||||
// class ExceptionHandler
|
// class ExceptionHandler
|
||||||
// =====================================================
|
// =====================================================
|
||||||
|
@ -3295,21 +3326,29 @@ void ShowINISettings(int argc, char **argv,Config &config,Config &configKeys) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupSteamSettings(){
|
void setupSteamSettings(bool steamEnabled) {
|
||||||
bool needToSaveConfig=false;
|
bool needToSaveConfig=false;
|
||||||
Config &config = Config::getInstance();
|
Config &config = Config::getInstance();
|
||||||
string steamPlayerName = safeCharPtrCopy(getenv("SteamAppUser"),100);
|
config.setBool("SteamEnabled",steamEnabled,true);
|
||||||
if( steamPlayerName=="") return;// not a steam launch
|
if(steamEnabled) {
|
||||||
|
printf("*NOTE: Steam Integration Enabled.\n");
|
||||||
|
//string steamPlayerName = safeCharPtrCopy(getenv("SteamAppUser"),100);
|
||||||
|
//if( steamPlayerName=="") return;// not a steam launch
|
||||||
|
Steam steam;
|
||||||
|
string steamPlayerName = steam.userName();
|
||||||
|
string steamLang = steam.lang();
|
||||||
|
printf("Steam Integration Enabled!\nSteam User Name is [%s] Language is [%s]\n", steamPlayerName.c_str(), steamLang.c_str());
|
||||||
|
|
||||||
string currentPLayerName = config.getString("NetPlayerName","");
|
string currentPLayerName = config.getString("NetPlayerName","");
|
||||||
if( currentPLayerName == "newbie" || currentPLayerName == "" ) {
|
if( currentPLayerName == "newbie" || currentPLayerName == "" ) {
|
||||||
config.setString("NetPlayerName",steamPlayerName);
|
config.setString("NetPlayerName",steamPlayerName);
|
||||||
needToSaveConfig=true;
|
needToSaveConfig=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( needToSaveConfig == true ) {
|
if( needToSaveConfig == true ) {
|
||||||
config.save();
|
config.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CheckForDuplicateData() {
|
void CheckForDuplicateData() {
|
||||||
Config &config = Config::getInstance();
|
Config &config = Config::getInstance();
|
||||||
|
@ -4201,6 +4240,18 @@ int glestMain(int argc, char** argv) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if(hasCommandArgument(argc, argv,GAME_ARGS[GAME_ARG_STEAM]) == true) {
|
||||||
|
// STEAMSHIM_requestStats();
|
||||||
|
// while (STEAMSHIM_alive()) {
|
||||||
|
// const STEAMSHIM_Event *e = STEAMSHIM_pump();
|
||||||
|
// printEvent(e);
|
||||||
|
// if (e && e->type == SHIMEVENT_STATSRECEIVED) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// usleep(100 * 1000);
|
||||||
|
// } // while
|
||||||
|
// }
|
||||||
|
|
||||||
if( hasCommandArgument(argc, argv,string(GAME_ARGS[GAME_ARG_MASTERSERVER_MODE])) == true) {
|
if( hasCommandArgument(argc, argv,string(GAME_ARGS[GAME_ARG_MASTERSERVER_MODE])) == true) {
|
||||||
//isMasterServerModeEnabled = true;
|
//isMasterServerModeEnabled = true;
|
||||||
//Window::setMasterserverMode(isMasterServerModeEnabled);
|
//Window::setMasterserverMode(isMasterServerModeEnabled);
|
||||||
|
@ -4539,7 +4590,7 @@ int glestMain(int argc, char** argv) {
|
||||||
Config &config = Config::getInstance();
|
Config &config = Config::getInstance();
|
||||||
setupGameItemPaths(argc, argv, &config);
|
setupGameItemPaths(argc, argv, &config);
|
||||||
|
|
||||||
setupSteamSettings();
|
setupSteamSettings(hasCommandArgument(argc, argv,GAME_ARGS[GAME_ARG_STEAM]));
|
||||||
|
|
||||||
if(config.getString("PlayerId","") == "") {
|
if(config.getString("PlayerId","") == "") {
|
||||||
char uuid_str[38];
|
char uuid_str[38];
|
||||||
|
@ -4985,6 +5036,12 @@ int glestMain(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
|
if(hasCommandArgument(argc, argv,GAME_ARGS[GAME_ARG_STEAM]) == true) {
|
||||||
|
Steam steam;
|
||||||
|
string steamUser = steam.userName();
|
||||||
|
string steamLang = steam.lang();
|
||||||
|
printf("Steam Integration Enabled!\nSteam User Name is [%s] Language is [%s]\n", steamUser.c_str(), steamLang.c_str());
|
||||||
|
}
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
int localeBufferSize = GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_SISO639LANGNAME, NULL, 0);
|
int localeBufferSize = GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_SISO639LANGNAME, NULL, 0);
|
||||||
wchar_t *sysLocale = new wchar_t[localeBufferSize];
|
wchar_t *sysLocale = new wchar_t[localeBufferSize];
|
||||||
|
@ -5986,6 +6043,14 @@ __try {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
IRCThread::setGlobalCacheContainerName(GameConstants::ircClientCacheLookupKey);
|
IRCThread::setGlobalCacheContainerName(GameConstants::ircClientCacheLookupKey);
|
||||||
|
|
||||||
|
if(hasCommandArgument(argc, argv,GAME_ARGS[GAME_ARG_STEAM]) == true) {
|
||||||
|
if (!STEAMSHIM_init()) {
|
||||||
|
printf("Child init failed, terminating.\n");
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result = glestMain(argc, argv);
|
result = glestMain(argc, argv);
|
||||||
|
|
||||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
||||||
|
@ -6003,10 +6068,17 @@ __try {
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(hasCommandArgument(argc, argv,GAME_ARGS[GAME_ARG_STEAM]) == true) {
|
||||||
|
STEAMSHIM_deinit();
|
||||||
|
}
|
||||||
|
|
||||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
||||||
return result;
|
return result;
|
||||||
#ifdef WIN32_STACK_TRACE
|
#ifdef WIN32_STACK_TRACE
|
||||||
} __except(stackdumper(0, GetExceptionInformation(),true), EXCEPTION_CONTINUE_SEARCH) { return 0; }
|
}
|
||||||
|
__except(stackdumper(0, GetExceptionInformation(),true), EXCEPTION_CONTINUE_SEARCH) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "network_message.h"
|
#include "network_message.h"
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
#include "auto_test.h"
|
#include "auto_test.h"
|
||||||
|
#include "steam.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "leak_dumper.h"
|
#include "leak_dumper.h"
|
||||||
|
@ -46,6 +47,7 @@ MenuStateRoot::MenuStateRoot(Program *program, MainMenu *mainMenu) :
|
||||||
buttonAbout("MainMenu","buttonAbout"),
|
buttonAbout("MainMenu","buttonAbout"),
|
||||||
buttonExit("MainMenu","buttonExit"),
|
buttonExit("MainMenu","buttonExit"),
|
||||||
labelVersion("MainMenu","labelVersion"),
|
labelVersion("MainMenu","labelVersion"),
|
||||||
|
labelGreeting("MainMenu","labelGreeting"),
|
||||||
|
|
||||||
mainMessageBox("MainMenu","mainMessageBox"),
|
mainMessageBox("MainMenu","mainMessageBox"),
|
||||||
errorMessageBox("MainMenu","errorMessageBox"),
|
errorMessageBox("MainMenu","errorMessageBox"),
|
||||||
|
@ -76,6 +78,15 @@ MenuStateRoot::MenuStateRoot(Program *program, MainMenu *mainMenu) :
|
||||||
labelVersion.setText(glestVersionString + " [" + getGITRevisionString() + "]");
|
labelVersion.setText(glestVersionString + " [" + getGITRevisionString() + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
labelGreeting.init(labelVersion.getX(), labelVersion.getY()-16);
|
||||||
|
labelGreeting.setText("");
|
||||||
|
Config &config = Config::getInstance();
|
||||||
|
if(config.getBool("SteamEnabled")) {
|
||||||
|
Steam steam;
|
||||||
|
string steamPlayerName = steam.userName();
|
||||||
|
labelGreeting.setText("Welcome Steam Player: " + steamPlayerName);
|
||||||
|
}
|
||||||
|
|
||||||
yPos-=55;
|
yPos-=55;
|
||||||
//buttonNewGame.registerGraphicComponent(containerName,"buttonNewGame");
|
//buttonNewGame.registerGraphicComponent(containerName,"buttonNewGame");
|
||||||
buttonNewGame.init(buttonXPosition, yPos, buttonWidth);
|
buttonNewGame.init(buttonXPosition, yPos, buttonWidth);
|
||||||
|
@ -581,6 +592,7 @@ void MenuStateRoot::render() {
|
||||||
renderer.renderButton(&buttonAbout);
|
renderer.renderButton(&buttonAbout);
|
||||||
renderer.renderButton(&buttonExit);
|
renderer.renderButton(&buttonExit);
|
||||||
renderer.renderLabel(&labelVersion);
|
renderer.renderLabel(&labelVersion);
|
||||||
|
renderer.renderLabel(&labelGreeting);
|
||||||
|
|
||||||
renderer.renderConsole(&console);
|
renderer.renderConsole(&console);
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ private:
|
||||||
GraphicButton buttonAbout;
|
GraphicButton buttonAbout;
|
||||||
GraphicButton buttonExit;
|
GraphicButton buttonExit;
|
||||||
GraphicLabel labelVersion;
|
GraphicLabel labelVersion;
|
||||||
|
GraphicLabel labelGreeting;
|
||||||
|
|
||||||
GraphicMessageBox mainMessageBox;
|
GraphicMessageBox mainMessageBox;
|
||||||
GraphicMessageBox errorMessageBox;
|
GraphicMessageBox errorMessageBox;
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
#include "steam.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include "steamshim_child.h"
|
||||||
|
|
||||||
|
/* Achievements */
|
||||||
|
static const char *const achievementNames[] = {
|
||||||
|
// "X",
|
||||||
|
};
|
||||||
|
//#define NUM_ACHIEVEMENTS (sizeof(achievementNames) / sizeof(achievementNames[0]))
|
||||||
|
|
||||||
|
/* Language map */
|
||||||
|
static inline std::map<std::string, std::string> gen_langToCode()
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> map;
|
||||||
|
map["brazilian"] = "pt_BR";
|
||||||
|
map["bulgarian"] = "bg";
|
||||||
|
map["czech"] = "cz";
|
||||||
|
map["danish"] = "da";
|
||||||
|
map["dutch"] = "nl";
|
||||||
|
map["english"] = "en";
|
||||||
|
map["finnish"] = "fi";
|
||||||
|
map["french"] = "fr";
|
||||||
|
map["german"] = "de";
|
||||||
|
map["greek"] = "el";
|
||||||
|
map["hungarian"] = "hu";
|
||||||
|
map["italian"] = "it";
|
||||||
|
map["japanese"] = "ja";
|
||||||
|
map["koreana"] = "ko";
|
||||||
|
map["korean"] = "ko";
|
||||||
|
map["norwegian"] = "no";
|
||||||
|
map["polish"] = "pl";
|
||||||
|
map["portuguese"] = "pt";
|
||||||
|
map["romanian"] = "ro";
|
||||||
|
map["russian"] = "ru";
|
||||||
|
map["schinese"] = "zh_CN";
|
||||||
|
map["spanish"] = "es";
|
||||||
|
map["swedish"] = "sv";
|
||||||
|
map["tchinese"] = "zh_TW";
|
||||||
|
map["thai"] = "th";
|
||||||
|
map["turkish"] = "tr";
|
||||||
|
map["ukrainian"] = "uk";
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
static const std::map<std::string, std::string> langToCode = gen_langToCode();
|
||||||
|
|
||||||
|
static std::string steamToIsoLang(const char *steamLang)
|
||||||
|
{
|
||||||
|
//printf("Steam language [%s]\n",steamLang);
|
||||||
|
std::map<std::string, std::string>::const_iterator it = langToCode.find(steamLang);
|
||||||
|
if (it != langToCode.end())
|
||||||
|
return it->second;
|
||||||
|
return "en";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SteamPrivate */
|
||||||
|
struct SteamPrivate
|
||||||
|
{
|
||||||
|
//std::map<std::string, bool> achievements;
|
||||||
|
|
||||||
|
std::string userName;
|
||||||
|
std::string lang;
|
||||||
|
|
||||||
|
SteamPrivate()
|
||||||
|
{
|
||||||
|
STEAMSHIM_getPersonaName();
|
||||||
|
STEAMSHIM_getCurrentGameLanguage();
|
||||||
|
// for (size_t i = 0; i < NUM_ACHIEVEMENTS; ++i)
|
||||||
|
// STEAMSHIM_getAchievement(achievementNames[i]);
|
||||||
|
while (!initialized())
|
||||||
|
{
|
||||||
|
SDL_Delay(100);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// void setAchievement(const char *name, bool set)
|
||||||
|
// {
|
||||||
|
// achievements[name] = set;
|
||||||
|
// STEAMSHIM_setAchievement(name, set);
|
||||||
|
// STEAMSHIM_storeStats();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// void updateAchievement(const char *name, bool isSet)
|
||||||
|
// {
|
||||||
|
// achievements[name] = isSet;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// bool isAchievementSet(const char *name)
|
||||||
|
// {
|
||||||
|
// return achievements[name];
|
||||||
|
// }
|
||||||
|
|
||||||
|
void update()
|
||||||
|
{
|
||||||
|
const STEAMSHIM_Event *e;
|
||||||
|
while ((e = STEAMSHIM_pump()) != 0)
|
||||||
|
{
|
||||||
|
/* Handle events */
|
||||||
|
switch (e->type)
|
||||||
|
{
|
||||||
|
// case SHIMEVENT_GETACHIEVEMENT:
|
||||||
|
// updateAchievement(e->name, e->ivalue);
|
||||||
|
// break;
|
||||||
|
case SHIMEVENT_GETPERSONANAME:
|
||||||
|
userName = e->name;
|
||||||
|
break;
|
||||||
|
case SHIMEVENT_GETCURRENTGAMELANGUAGE:
|
||||||
|
lang = steamToIsoLang(e->name);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool initialized()
|
||||||
|
{
|
||||||
|
return !userName.empty()
|
||||||
|
&& !lang.empty();
|
||||||
|
//&& achievements.size() == NUM_ACHIEVEMENTS;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Steam */
|
||||||
|
Steam::Steam()
|
||||||
|
: p(new SteamPrivate())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Steam::~Steam()
|
||||||
|
{
|
||||||
|
delete p;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &Steam::userName() const
|
||||||
|
{
|
||||||
|
return p->userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &Steam::lang() const
|
||||||
|
{
|
||||||
|
return p->lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
//void Steam::unlock(const char *name)
|
||||||
|
//{
|
||||||
|
// p->setAchievement(name, true);
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//void Steam::lock(const char *name)
|
||||||
|
//{
|
||||||
|
// p->setAchievement(name, false);
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//bool Steam::isUnlocked(const char *name)
|
||||||
|
//{
|
||||||
|
// return p->isAchievementSet(name);
|
||||||
|
//}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef STEAM_H
|
||||||
|
#define STEAM_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct SteamPrivate;
|
||||||
|
|
||||||
|
class Steam
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// void unlock(const char *name);
|
||||||
|
// void lock(const char *name);
|
||||||
|
// bool isUnlocked(const char *name);
|
||||||
|
|
||||||
|
const std::string &userName() const;
|
||||||
|
const std::string &lang() const;
|
||||||
|
|
||||||
|
Steam();
|
||||||
|
~Steam();
|
||||||
|
|
||||||
|
private:
|
||||||
|
//friend struct SharedStatePrivate;
|
||||||
|
|
||||||
|
SteamPrivate *p;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // STEAM_H
|
|
@ -0,0 +1,463 @@
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define WIN32_LEAN_AND_MEAN 1
|
||||||
|
#include <windows.h>
|
||||||
|
typedef HANDLE PipeType;
|
||||||
|
#define NULLPIPE NULL
|
||||||
|
typedef unsigned __int8 uint8;
|
||||||
|
typedef __int32 int32;
|
||||||
|
typedef unsigned __int64 uint64;
|
||||||
|
#else
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <signal.h>
|
||||||
|
typedef uint8_t uint8;
|
||||||
|
typedef int32_t int32;
|
||||||
|
typedef uint64_t uint64;
|
||||||
|
typedef int PipeType;
|
||||||
|
#define NULLPIPE -1
|
||||||
|
#endif
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "steamshim_child.h"
|
||||||
|
|
||||||
|
#ifdef STEAMSHIM_DEBUG
|
||||||
|
#define dbgpipe printf
|
||||||
|
#else
|
||||||
|
static inline void dbgpipe(const char *fmt, ...) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int writePipe(PipeType fd, const void *buf, const unsigned int _len);
|
||||||
|
static int readPipe(PipeType fd, void *buf, const unsigned int _len);
|
||||||
|
static void closePipe(PipeType fd);
|
||||||
|
static char *getEnvVar(const char *key, char *buf, const size_t buflen);
|
||||||
|
static int pipeReady(PipeType fd);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
static int pipeReady(PipeType fd)
|
||||||
|
{
|
||||||
|
DWORD avail = 0;
|
||||||
|
return (PeekNamedPipe(fd, NULL, 0, NULL, &avail, NULL) && (avail > 0));
|
||||||
|
} /* pipeReady */
|
||||||
|
|
||||||
|
static int 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 void closePipe(PipeType fd)
|
||||||
|
{
|
||||||
|
CloseHandle(fd);
|
||||||
|
} /* closePipe */
|
||||||
|
|
||||||
|
static char *getEnvVar(const char *key, char *buf, const size_t buflen)
|
||||||
|
{
|
||||||
|
const DWORD rc = GetEnvironmentVariableA(key, buf, buflen);
|
||||||
|
/* rc doesn't count null char, hence "<". */
|
||||||
|
return ((rc > 0) && (rc < buflen)) ? buf : NULL;
|
||||||
|
} /* getEnvVar */
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static int pipeReady(PipeType fd)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
struct pollfd pfd = { fd, POLLIN | POLLERR | POLLHUP, 0 };
|
||||||
|
while (((rc = poll(&pfd, 1, 0)) == -1) && (errno == EINTR)) { /*spin*/ }
|
||||||
|
return (rc == 1);
|
||||||
|
} /* pipeReady */
|
||||||
|
|
||||||
|
static int 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 void closePipe(PipeType fd)
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
} /* closePipe */
|
||||||
|
|
||||||
|
static char *getEnvVar(const char *key, char *buf, const size_t buflen)
|
||||||
|
{
|
||||||
|
const char *envr = getenv(key);
|
||||||
|
if (!envr || (strlen(envr) >= buflen))
|
||||||
|
return NULL;
|
||||||
|
strcpy(buf, envr);
|
||||||
|
return buf;
|
||||||
|
} /* getEnvVar */
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static PipeType GPipeRead = NULLPIPE;
|
||||||
|
static PipeType GPipeWrite = NULLPIPE;
|
||||||
|
|
||||||
|
typedef enum ShimCmd
|
||||||
|
{
|
||||||
|
SHIMCMD_BYE,
|
||||||
|
SHIMCMD_PUMP,
|
||||||
|
SHIMCMD_REQUESTSTATS,
|
||||||
|
SHIMCMD_STORESTATS,
|
||||||
|
SHIMCMD_SETACHIEVEMENT,
|
||||||
|
SHIMCMD_GETACHIEVEMENT,
|
||||||
|
SHIMCMD_RESETSTATS,
|
||||||
|
SHIMCMD_SETSTATI,
|
||||||
|
SHIMCMD_GETSTATI,
|
||||||
|
SHIMCMD_SETSTATF,
|
||||||
|
SHIMCMD_GETSTATF,
|
||||||
|
SHIMCMD_GETPERSONANAME,
|
||||||
|
SHIMCMD_GETCURRENTGAMELANGUAGE,
|
||||||
|
} ShimCmd;
|
||||||
|
|
||||||
|
static int write1ByteCmd(const uint8 b1)
|
||||||
|
{
|
||||||
|
const uint8 buf[] = { 1, b1 };
|
||||||
|
return writePipe(GPipeWrite, buf, sizeof (buf));
|
||||||
|
} /* write1ByteCmd */
|
||||||
|
|
||||||
|
static int write2ByteCmd(const uint8 b1, const uint8 b2)
|
||||||
|
{
|
||||||
|
const uint8 buf[] = { 2, b1, b2 };
|
||||||
|
return writePipe(GPipeWrite, buf, sizeof (buf));
|
||||||
|
} /* write2ByteCmd */
|
||||||
|
|
||||||
|
static inline int writeBye(void)
|
||||||
|
{
|
||||||
|
dbgpipe("Child sending SHIMCMD_BYE().\n");
|
||||||
|
return write1ByteCmd(SHIMCMD_BYE);
|
||||||
|
} // writeBye
|
||||||
|
|
||||||
|
static int initPipes(void)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
|
||||||
|
if (!getEnvVar("STEAMSHIM_READHANDLE", buf, sizeof (buf)))
|
||||||
|
return 0;
|
||||||
|
GPipeRead = (PipeType) strtoull(buf, 0, 10);
|
||||||
|
|
||||||
|
if (!getEnvVar("STEAMSHIM_WRITEHANDLE", buf, sizeof (buf)))
|
||||||
|
return 0;
|
||||||
|
GPipeWrite = (PipeType) strtoull(buf, 0, 10);
|
||||||
|
|
||||||
|
return ((GPipeRead != NULLPIPE) && (GPipeWrite != NULLPIPE));
|
||||||
|
} /* initPipes */
|
||||||
|
|
||||||
|
|
||||||
|
int STEAMSHIM_init(void)
|
||||||
|
{
|
||||||
|
printf("Initializing Steam Shim...\n");
|
||||||
|
dbgpipe("Child init start.\n");
|
||||||
|
if (!initPipes())
|
||||||
|
{
|
||||||
|
dbgpipe("Child init failed.\n");
|
||||||
|
return 0;
|
||||||
|
} /* if */
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
printf("Init of Steam Shim successful!\n");
|
||||||
|
dbgpipe("Child init success!\n");
|
||||||
|
return 1;
|
||||||
|
} /* STEAMSHIM_init */
|
||||||
|
|
||||||
|
void STEAMSHIM_deinit(void)
|
||||||
|
{
|
||||||
|
dbgpipe("Child deinit.\n");
|
||||||
|
if (GPipeWrite != NULLPIPE)
|
||||||
|
{
|
||||||
|
writeBye();
|
||||||
|
closePipe(GPipeWrite);
|
||||||
|
} /* if */
|
||||||
|
|
||||||
|
if (GPipeRead != NULLPIPE)
|
||||||
|
closePipe(GPipeRead);
|
||||||
|
|
||||||
|
GPipeRead = GPipeWrite = NULLPIPE;
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
signal(SIGPIPE, SIG_DFL);
|
||||||
|
#endif
|
||||||
|
} /* STEAMSHIM_deinit */
|
||||||
|
|
||||||
|
static inline int isAlive(void)
|
||||||
|
{
|
||||||
|
return ((GPipeRead != NULLPIPE) && (GPipeWrite != NULLPIPE));
|
||||||
|
} /* isAlive */
|
||||||
|
|
||||||
|
static inline int isDead(void)
|
||||||
|
{
|
||||||
|
return !isAlive();
|
||||||
|
} /* isDead */
|
||||||
|
|
||||||
|
int STEAMSHIM_alive(void)
|
||||||
|
{
|
||||||
|
return isAlive();
|
||||||
|
} /* STEAMSHIM_alive */
|
||||||
|
|
||||||
|
static const STEAMSHIM_Event *processEvent(const uint8 *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
static STEAMSHIM_Event event;
|
||||||
|
const STEAMSHIM_EventType type = (STEAMSHIM_EventType) *(buf++);
|
||||||
|
buflen--;
|
||||||
|
|
||||||
|
memset(&event, '\0', sizeof (event));
|
||||||
|
event.type = type;
|
||||||
|
event.okay = 1;
|
||||||
|
|
||||||
|
#ifdef STEAMSHIM_DEBUG
|
||||||
|
if (0) {}
|
||||||
|
#define PRINTGOTEVENT(x) else if (type == x) printf("Child got " #x ".\n")
|
||||||
|
PRINTGOTEVENT(SHIMEVENT_BYE);
|
||||||
|
PRINTGOTEVENT(SHIMEVENT_STATSRECEIVED);
|
||||||
|
PRINTGOTEVENT(SHIMEVENT_STATSSTORED);
|
||||||
|
PRINTGOTEVENT(SHIMEVENT_SETACHIEVEMENT);
|
||||||
|
PRINTGOTEVENT(SHIMEVENT_GETACHIEVEMENT);
|
||||||
|
PRINTGOTEVENT(SHIMEVENT_RESETSTATS);
|
||||||
|
PRINTGOTEVENT(SHIMEVENT_SETSTATI);
|
||||||
|
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
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case SHIMEVENT_BYE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHIMEVENT_STATSRECEIVED:
|
||||||
|
case SHIMEVENT_STATSSTORED:
|
||||||
|
if (!buflen) return NULL;
|
||||||
|
event.okay = *(buf++) ? 1 : 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHIMEVENT_SETACHIEVEMENT:
|
||||||
|
if (buflen < 3) return NULL;
|
||||||
|
event.ivalue = *(buf++) ? 1 : 0;
|
||||||
|
event.okay = *(buf++) ? 1 : 0;
|
||||||
|
strcpy(event.name, (const char *) buf);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHIMEVENT_GETACHIEVEMENT:
|
||||||
|
if (buflen < 10) return NULL;
|
||||||
|
event.ivalue = (int) *(buf++);
|
||||||
|
if (event.ivalue == 2)
|
||||||
|
event.ivalue = event.okay = 0;
|
||||||
|
event.epochsecs = (long long unsigned) *((uint64 *) buf);
|
||||||
|
buf += sizeof (uint64);
|
||||||
|
strcpy(event.name, (const char *) buf);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHIMEVENT_RESETSTATS:
|
||||||
|
if (buflen != 2) return NULL;
|
||||||
|
event.ivalue = *(buf++) ? 1 : 0;
|
||||||
|
event.okay = *(buf++) ? 1 : 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHIMEVENT_SETSTATI:
|
||||||
|
case SHIMEVENT_GETSTATI:
|
||||||
|
event.okay = *(buf++) ? 1 : 0;
|
||||||
|
event.ivalue = (int) *((int32 *) buf);
|
||||||
|
buf += sizeof (int32);
|
||||||
|
strcpy(event.name, (const char *) buf);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHIMEVENT_SETSTATF:
|
||||||
|
case SHIMEVENT_GETSTATF:
|
||||||
|
event.okay = *(buf++) ? 1 : 0;
|
||||||
|
event.fvalue = (int) *((float *) buf);
|
||||||
|
buf += sizeof (float);
|
||||||
|
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 */
|
||||||
|
|
||||||
|
return &event;
|
||||||
|
} /* processEvent */
|
||||||
|
|
||||||
|
const STEAMSHIM_Event *STEAMSHIM_pump(void)
|
||||||
|
{
|
||||||
|
static uint8 buf[256];
|
||||||
|
static int br = 0;
|
||||||
|
int evlen = (br > 0) ? ((int) buf[0]) : 0;
|
||||||
|
|
||||||
|
if (isDead())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (br <= evlen) /* we have an incomplete commmand. Try to read more. */
|
||||||
|
{
|
||||||
|
if (pipeReady(GPipeRead))
|
||||||
|
{
|
||||||
|
const int morebr = readPipe(GPipeRead, buf + br, sizeof (buf) - br);
|
||||||
|
if (morebr > 0)
|
||||||
|
br += morebr;
|
||||||
|
else /* uh oh */
|
||||||
|
{
|
||||||
|
dbgpipe("Child readPipe failed! Shutting down.\n");
|
||||||
|
STEAMSHIM_deinit(); /* kill it all. */
|
||||||
|
} /* else */
|
||||||
|
} /* if */
|
||||||
|
} /* if */
|
||||||
|
|
||||||
|
if (evlen && (br > evlen))
|
||||||
|
{
|
||||||
|
const STEAMSHIM_Event *retval = processEvent(buf+1, evlen);
|
||||||
|
br -= evlen + 1;
|
||||||
|
if (br > 0)
|
||||||
|
memmove(buf, buf+evlen+1, br);
|
||||||
|
return retval;
|
||||||
|
} /* if */
|
||||||
|
|
||||||
|
/* Run Steam event loop. */
|
||||||
|
if (br == 0)
|
||||||
|
{
|
||||||
|
dbgpipe("Child sending SHIMCMD_PUMP().\n");
|
||||||
|
write1ByteCmd(SHIMCMD_PUMP);
|
||||||
|
} /* if */
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
} /* STEAMSHIM_pump */
|
||||||
|
|
||||||
|
void STEAMSHIM_requestStats(void)
|
||||||
|
{
|
||||||
|
if (isDead()) return;
|
||||||
|
dbgpipe("Child sending SHIMCMD_REQUESTSTATS().\n");
|
||||||
|
write1ByteCmd(SHIMCMD_REQUESTSTATS);
|
||||||
|
} /* STEAMSHIM_requestStats */
|
||||||
|
|
||||||
|
void STEAMSHIM_storeStats(void)
|
||||||
|
{
|
||||||
|
if (isDead()) return;
|
||||||
|
dbgpipe("Child sending SHIMCMD_STORESTATS().\n");
|
||||||
|
write1ByteCmd(SHIMCMD_STORESTATS);
|
||||||
|
} /* STEAMSHIM_storeStats */
|
||||||
|
|
||||||
|
void STEAMSHIM_setAchievement(const char *name, const int enable)
|
||||||
|
{
|
||||||
|
uint8 buf[256];
|
||||||
|
uint8 *ptr = buf+1;
|
||||||
|
if (isDead()) return;
|
||||||
|
dbgpipe("Child sending SHIMCMD_SETACHIEVEMENT('%s', %senable).\n", name, enable ? "" : "!");
|
||||||
|
*(ptr++) = (uint8) SHIMCMD_SETACHIEVEMENT;
|
||||||
|
*(ptr++) = enable ? 1 : 0;
|
||||||
|
strcpy((char *) ptr, name);
|
||||||
|
ptr += strlen(name) + 1;
|
||||||
|
buf[0] = (uint8) ((ptr-1) - buf);
|
||||||
|
writePipe(GPipeWrite, buf, buf[0] + 1);
|
||||||
|
} /* STEAMSHIM_setAchievement */
|
||||||
|
|
||||||
|
void STEAMSHIM_getAchievement(const char *name)
|
||||||
|
{
|
||||||
|
uint8 buf[256];
|
||||||
|
uint8 *ptr = buf+1;
|
||||||
|
if (isDead()) return;
|
||||||
|
dbgpipe("Child sending SHIMCMD_GETACHIEVEMENT('%s').\n", name);
|
||||||
|
*(ptr++) = (uint8) SHIMCMD_GETACHIEVEMENT;
|
||||||
|
strcpy((char *) ptr, name);
|
||||||
|
ptr += strlen(name) + 1;
|
||||||
|
buf[0] = (uint8) ((ptr-1) - buf);
|
||||||
|
writePipe(GPipeWrite, buf, buf[0] + 1);
|
||||||
|
} /* STEAMSHIM_getAchievement */
|
||||||
|
|
||||||
|
void STEAMSHIM_resetStats(const int bAlsoAchievements)
|
||||||
|
{
|
||||||
|
if (isDead()) return;
|
||||||
|
dbgpipe("Child sending SHIMCMD_RESETSTATS(%salsoAchievements).\n", bAlsoAchievements ? "" : "!");
|
||||||
|
write2ByteCmd(SHIMCMD_RESETSTATS, bAlsoAchievements ? 1 : 0);
|
||||||
|
} /* STEAMSHIM_resetStats */
|
||||||
|
|
||||||
|
static void writeStatThing(const ShimCmd cmd, const char *name, const void *val, const size_t vallen)
|
||||||
|
{
|
||||||
|
uint8 buf[256];
|
||||||
|
uint8 *ptr = buf+1;
|
||||||
|
if (isDead()) return;
|
||||||
|
*(ptr++) = (uint8) cmd;
|
||||||
|
if (vallen)
|
||||||
|
{
|
||||||
|
memcpy(ptr, val, vallen);
|
||||||
|
ptr += vallen;
|
||||||
|
} /* if */
|
||||||
|
strcpy((char *) ptr, name);
|
||||||
|
ptr += strlen(name) + 1;
|
||||||
|
buf[0] = (uint8) ((ptr-1) - buf);
|
||||||
|
writePipe(GPipeWrite, buf, buf[0] + 1);
|
||||||
|
} /* writeStatThing */
|
||||||
|
|
||||||
|
void STEAMSHIM_setStatI(const char *name, const int _val)
|
||||||
|
{
|
||||||
|
const int32 val = (int32) _val;
|
||||||
|
dbgpipe("Child sending SHIMCMD_SETSTATI('%s', val %d).\n", name, val);
|
||||||
|
writeStatThing(SHIMCMD_SETSTATI, name, &val, sizeof (val));
|
||||||
|
} /* STEAMSHIM_setStatI */
|
||||||
|
|
||||||
|
void STEAMSHIM_getStatI(const char *name)
|
||||||
|
{
|
||||||
|
dbgpipe("Child sending SHIMCMD_GETSTATI('%s').\n", name);
|
||||||
|
writeStatThing(SHIMCMD_GETSTATI, name, NULL, 0);
|
||||||
|
} /* STEAMSHIM_getStatI */
|
||||||
|
|
||||||
|
void STEAMSHIM_setStatF(const char *name, const float val)
|
||||||
|
{
|
||||||
|
dbgpipe("Child sending SHIMCMD_SETSTATF('%s', val %f).\n", name, val);
|
||||||
|
writeStatThing(SHIMCMD_SETSTATF, name, &val, sizeof (val));
|
||||||
|
} /* STEAMSHIM_setStatF */
|
||||||
|
|
||||||
|
void STEAMSHIM_getStatF(const char *name)
|
||||||
|
{
|
||||||
|
dbgpipe("Child sending SHIMCMD_GETSTATF('%s').\n", name);
|
||||||
|
writeStatThing(SHIMCMD_GETSTATF, name, NULL, 0);
|
||||||
|
} /* STEAMSHIM_getStatF */
|
||||||
|
|
||||||
|
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 ... */
|
|
@ -0,0 +1,57 @@
|
||||||
|
#ifndef _INCL_STEAMSHIM_CHILD_H_
|
||||||
|
#define _INCL_STEAMSHIM_CHILD_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum STEAMSHIM_EventType
|
||||||
|
{
|
||||||
|
SHIMEVENT_BYE,
|
||||||
|
SHIMEVENT_STATSRECEIVED,
|
||||||
|
SHIMEVENT_STATSSTORED,
|
||||||
|
SHIMEVENT_SETACHIEVEMENT,
|
||||||
|
SHIMEVENT_GETACHIEVEMENT,
|
||||||
|
SHIMEVENT_RESETSTATS,
|
||||||
|
SHIMEVENT_SETSTATI,
|
||||||
|
SHIMEVENT_GETSTATI,
|
||||||
|
SHIMEVENT_SETSTATF,
|
||||||
|
SHIMEVENT_GETSTATF,
|
||||||
|
SHIMEVENT_GETPERSONANAME,
|
||||||
|
SHIMEVENT_GETCURRENTGAMELANGUAGE,
|
||||||
|
} STEAMSHIM_EventType;
|
||||||
|
|
||||||
|
/* not all of these fields make sense in a given event. */
|
||||||
|
typedef struct STEAMSHIM_Event
|
||||||
|
{
|
||||||
|
STEAMSHIM_EventType type;
|
||||||
|
int okay;
|
||||||
|
int ivalue;
|
||||||
|
float fvalue;
|
||||||
|
unsigned long long epochsecs;
|
||||||
|
char name[256];
|
||||||
|
} STEAMSHIM_Event;
|
||||||
|
|
||||||
|
int STEAMSHIM_init(void); /* non-zero on success, zero on failure. */
|
||||||
|
void STEAMSHIM_deinit(void);
|
||||||
|
int STEAMSHIM_alive(void);
|
||||||
|
const STEAMSHIM_Event *STEAMSHIM_pump(void);
|
||||||
|
void STEAMSHIM_requestStats(void);
|
||||||
|
void STEAMSHIM_storeStats(void);
|
||||||
|
void STEAMSHIM_setAchievement(const char *name, const int enable);
|
||||||
|
void STEAMSHIM_getAchievement(const char *name);
|
||||||
|
void STEAMSHIM_resetStats(const int bAlsoAchievements);
|
||||||
|
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
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* include-once blocker */
|
||||||
|
|
||||||
|
/* end of steamshim_child.h ... */
|
|
@ -98,6 +98,7 @@ const char *GAME_ARGS[] = {
|
||||||
"--enable-new-protocol",
|
"--enable-new-protocol",
|
||||||
|
|
||||||
"--create-data-archives",
|
"--create-data-archives",
|
||||||
|
"--steam",
|
||||||
|
|
||||||
"--verbose"
|
"--verbose"
|
||||||
|
|
||||||
|
@ -182,6 +183,7 @@ enum GAME_ARG_TYPE {
|
||||||
GAME_ARG_ENABLE_NEW_PROTOCOL,
|
GAME_ARG_ENABLE_NEW_PROTOCOL,
|
||||||
|
|
||||||
GAME_ARG_CREATE_DATA_ARCHIVES,
|
GAME_ARG_CREATE_DATA_ARCHIVES,
|
||||||
|
GAME_ARG_STEAM,
|
||||||
|
|
||||||
GAME_ARG_VERBOSE_MODE,
|
GAME_ARG_VERBOSE_MODE,
|
||||||
|
|
||||||
|
@ -462,6 +464,9 @@ void printParameterHelp(const char *argv0, bool foundInvalidArgs) {
|
||||||
printf("\n\n \tWhere y = include_main to include main (non mod) data.");
|
printf("\n\n \tWhere y = include_main to include main (non mod) data.");
|
||||||
printf("\n\n \texample: %s %s=all",extractFileFromDirectoryPath(argv0).c_str(),GAME_ARGS[GAME_ARG_CREATE_DATA_ARCHIVES]);
|
printf("\n\n \texample: %s %s=all",extractFileFromDirectoryPath(argv0).c_str(),GAME_ARGS[GAME_ARG_CREATE_DATA_ARCHIVES]);
|
||||||
|
|
||||||
|
printf("\n\n%s=x=y ",GAME_ARGS[GAME_ARG_STEAM]);
|
||||||
|
printf("\n\n \tRun with Steam Client Integration.");
|
||||||
|
|
||||||
printf("\n\n%s \t\tDisplays verbose information in the console.",GAME_ARGS[GAME_ARG_VERBOSE_MODE]);
|
printf("\n\n%s \t\tDisplays verbose information in the console.",GAME_ARGS[GAME_ARG_VERBOSE_MODE]);
|
||||||
printf("\n\n");
|
printf("\n\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
TARGET := megaglest_shim
|
||||||
|
GAME_LAUNCH_NAME ?= megaglest
|
||||||
|
|
||||||
|
ifndef STEAMWORKS
|
||||||
|
# STEAMWORKS ?= /home/softcoder/Code/steamworks_sdk/sdk
|
||||||
|
$(error STEAMWORKS is not set)
|
||||||
|
endif
|
||||||
|
|
||||||
|
CXX ?= clang++
|
||||||
|
WINDRES ?= windres
|
||||||
|
HOST ?= linux64
|
||||||
|
FLAGS := -I$(STEAMWORKS)/public -DGAME_LAUNCH_NAME=\"$(GAME_LAUNCH_NAME)\" -Wall
|
||||||
|
|
||||||
|
ifeq ($(HOST),w32)
|
||||||
|
FLAGS += -L$(STEAMWORKS)/redistributable_bin
|
||||||
|
else ifeq ($(HOST),linux32)
|
||||||
|
FLAGS += -L$(STEAMWORKS)/redistributable_bin/linux32 -m32
|
||||||
|
else ifeq ($(HOST),linux64)
|
||||||
|
FLAGS += -L$(STEAMWORKS)/redistributable_bin/linux64 -m64
|
||||||
|
else ifeq ($(HOST),osx)
|
||||||
|
FLAGS += -L$(STEAMWORKS)/redistributable_bin/osx32
|
||||||
|
endif
|
||||||
|
|
||||||
|
FLAGS += -lsteam_api
|
||||||
|
|
||||||
|
ifeq ($(DEBUG),1)
|
||||||
|
FLAGS += -DSTEAMSHIM_DEBUG
|
||||||
|
else ifeq ($(HOST),w32)
|
||||||
|
FLAGS += -mwindows
|
||||||
|
endif
|
||||||
|
|
||||||
|
SRC := steamshim_parent.cpp
|
||||||
|
RES := resources.rc
|
||||||
|
RESOBJ := resources.o
|
||||||
|
|
||||||
|
ifeq ($(HOST),w32)
|
||||||
|
$(TARGET).exe: $(SRC) $(RESOBJ)
|
||||||
|
$(CXX) $^ -o $@ $(FLAGS)
|
||||||
|
$(RESOBJ): $(RES)
|
||||||
|
$(WINDRES) $< $@
|
||||||
|
else
|
||||||
|
$(TARGET): $(SRC)
|
||||||
|
$(CXX) $^ -o $@ $(FLAGS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(TARGET) $(TARGET).exe $(RESOBJ)
|
||||||
|
|
||||||
|
.PHONY: clean
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Use this script to build MegaGlest using cmake
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Written by Mark Vejvoda <mark_vejvoda@hotmail.com>
|
||||||
|
# Copyright (c) 2011-2013 Mark Vejvoda under GNU GPL v3.0+
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
rm steamshim
|
||||||
|
#make STEAMWORKS?=/home/softcoder/Code/steamworks_sdk/sdk
|
||||||
|
make $@
|
|
@ -0,0 +1 @@
|
||||||
|
578870
|
|
@ -0,0 +1,787 @@
|
||||||
|
#ifndef GAME_LAUNCH_NAME
|
||||||
|
#define GAME_LAUNCH_NAME "megaglest_shim"
|
||||||
|
#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 <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"
|
||||||
|
|
||||||
|
#ifdef STEAMSHIM_DEBUG
|
||||||
|
#define dbgpipe printf
|
||||||
|
#else
|
||||||
|
static inline void dbgpipe(const char *fmt, ...) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 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 LPWSTR genCommandLine()
|
||||||
|
{
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
fcntl(fds[0], F_SETFL, 0);
|
||||||
|
fcntl(fds[1], F_SETFL, 0);
|
||||||
|
*pPipeParentRead = fds[0];
|
||||||
|
*pPipeChildWrite = fds[1];
|
||||||
|
|
||||||
|
if (pipe(fds) == -1)
|
||||||
|
{
|
||||||
|
close(*pPipeParentRead);
|
||||||
|
close(*pPipeChildWrite);
|
||||||
|
return 0;
|
||||||
|
} // if
|
||||||
|
|
||||||
|
fcntl(fds[0], F_SETFL, 0);
|
||||||
|
fcntl(fds[1], F_SETFL, 0);
|
||||||
|
*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)
|
||||||
|
{
|
||||||
|
dbgpipe("***IN launchChild START.\n");
|
||||||
|
|
||||||
|
*pid = fork();
|
||||||
|
|
||||||
|
dbgpipe("***IN launchChild pid = %d.\n",*pid);
|
||||||
|
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);
|
||||||
|
|
||||||
|
dbgpipe("***IN launchChild [%s].\n",GArgv[0]);
|
||||||
|
|
||||||
|
int ret = execvp(GArgv[0], GArgv);
|
||||||
|
|
||||||
|
dbgpipe("***IN launchChild [%s] got ret = %d errorno = %d.\n",GArgv[0],ret,errno);
|
||||||
|
// still here? It failed! Terminate, closing child's ends of the pipes.
|
||||||
|
_exit(1);
|
||||||
|
} // launchChild
|
||||||
|
|
||||||
|
static int closeProcess(ProcessType *pid)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
printf("#1 =============> Megaglest Parent starting mainline.\n");
|
||||||
|
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
GArgc = argc;
|
||||||
|
GArgv = argv;
|
||||||
|
return mainline();
|
||||||
|
} // main
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// THE ACTUAL PROGRAM.
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
class SteamBridge
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SteamBridge(PipeType _fd);
|
||||||
|
STEAM_CALLBACK(SteamBridge, OnUserStatsReceived, UserStatsReceived_t, m_CallbackUserStatsReceived);
|
||||||
|
STEAM_CALLBACK(SteamBridge, OnUserStatsStored, UserStatsStored_t, m_CallbackUserStatsStored);
|
||||||
|
|
||||||
|
private:
|
||||||
|
PipeType fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum ShimCmd
|
||||||
|
{
|
||||||
|
SHIMCMD_BYE,
|
||||||
|
SHIMCMD_PUMP,
|
||||||
|
SHIMCMD_REQUESTSTATS,
|
||||||
|
SHIMCMD_STORESTATS,
|
||||||
|
SHIMCMD_SETACHIEVEMENT,
|
||||||
|
SHIMCMD_GETACHIEVEMENT,
|
||||||
|
SHIMCMD_RESETSTATS,
|
||||||
|
SHIMCMD_SETSTATI,
|
||||||
|
SHIMCMD_GETSTATI,
|
||||||
|
SHIMCMD_SETSTATF,
|
||||||
|
SHIMCMD_GETSTATF,
|
||||||
|
SHIMCMD_GETPERSONANAME,
|
||||||
|
SHIMCMD_GETCURRENTGAMELANGUAGE,
|
||||||
|
} ShimCmd;
|
||||||
|
|
||||||
|
typedef enum ShimEvent
|
||||||
|
{
|
||||||
|
SHIMEVENT_BYE,
|
||||||
|
SHIMEVENT_STATSRECEIVED,
|
||||||
|
SHIMEVENT_STATSSTORED,
|
||||||
|
SHIMEVENT_SETACHIEVEMENT,
|
||||||
|
SHIMEVENT_GETACHIEVEMENT,
|
||||||
|
SHIMEVENT_RESETSTATS,
|
||||||
|
SHIMEVENT_SETSTATI,
|
||||||
|
SHIMEVENT_GETSTATI,
|
||||||
|
SHIMEVENT_SETSTATF,
|
||||||
|
SHIMEVENT_GETSTATF,
|
||||||
|
SHIMEVENT_GETPERSONANAME,
|
||||||
|
SHIMEVENT_GETCURRENTGAMELANGUAGE,
|
||||||
|
} ShimEvent;
|
||||||
|
|
||||||
|
static bool write1ByteCmd(PipeType fd, const uint8 b1)
|
||||||
|
{
|
||||||
|
const uint8 buf[] = { 1, b1 };
|
||||||
|
return writePipe(fd, buf, sizeof (buf));
|
||||||
|
} // write1ByteCmd
|
||||||
|
|
||||||
|
static bool write2ByteCmd(PipeType fd, const uint8 b1, const uint8 b2)
|
||||||
|
{
|
||||||
|
const uint8 buf[] = { 2, b1, b2 };
|
||||||
|
return writePipe(fd, buf, sizeof (buf));
|
||||||
|
} // write2ByteCmd
|
||||||
|
|
||||||
|
static bool write3ByteCmd(PipeType fd, const uint8 b1, const uint8 b2, const uint8 b3)
|
||||||
|
{
|
||||||
|
const uint8 buf[] = { 3, b1, b2, b3 };
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
dbgpipe("Parent sending SHIMEVENT_BYE().\n");
|
||||||
|
return write1ByteCmd(fd, SHIMEVENT_BYE);
|
||||||
|
} // writeBye
|
||||||
|
|
||||||
|
static inline bool writeStatsReceived(PipeType fd, const bool okay)
|
||||||
|
{
|
||||||
|
dbgpipe("Parent sending SHIMEVENT_STATSRECEIVED(%sokay).\n", okay ? "" : "!");
|
||||||
|
return write2ByteCmd(fd, SHIMEVENT_STATSRECEIVED, okay ? 1 : 0);
|
||||||
|
} // writeStatsReceived
|
||||||
|
|
||||||
|
static inline bool writeStatsStored(PipeType fd, const bool okay)
|
||||||
|
{
|
||||||
|
dbgpipe("Parent sending SHIMEVENT_STATSSTORED(%sokay).\n", okay ? "" : "!");
|
||||||
|
return write2ByteCmd(fd, SHIMEVENT_STATSSTORED, okay ? 1 : 0);
|
||||||
|
} // writeStatsStored
|
||||||
|
|
||||||
|
static bool writeAchievementSet(PipeType fd, const char *name, const bool enable, const bool okay)
|
||||||
|
{
|
||||||
|
uint8 buf[256];
|
||||||
|
uint8 *ptr = buf+1;
|
||||||
|
dbgpipe("Parent sending SHIMEVENT_SETACHIEVEMENT('%s', %senable, %sokay).\n", name, enable ? "" : "!", okay ? "" : "!");
|
||||||
|
*(ptr++) = (uint8) SHIMEVENT_SETACHIEVEMENT;
|
||||||
|
*(ptr++) = enable ? 1 : 0;
|
||||||
|
*(ptr++) = okay ? 1 : 0;
|
||||||
|
strcpy((char *) ptr, name);
|
||||||
|
ptr += strlen(name) + 1;
|
||||||
|
buf[0] = (uint8) ((ptr-1) - buf);
|
||||||
|
return writePipe(fd, buf, buf[0] + 1);
|
||||||
|
} // writeAchievementSet
|
||||||
|
|
||||||
|
static bool writeAchievementGet(PipeType fd, const char *name, const int status, const uint64 time)
|
||||||
|
{
|
||||||
|
uint8 buf[256];
|
||||||
|
uint8 *ptr = buf+1;
|
||||||
|
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));
|
||||||
|
ptr += sizeof (time);
|
||||||
|
strcpy((char *) ptr, name);
|
||||||
|
ptr += strlen(name) + 1;
|
||||||
|
buf[0] = (uint8) ((ptr-1) - buf);
|
||||||
|
return writePipe(fd, buf, buf[0] + 1);
|
||||||
|
} // writeAchievementGet
|
||||||
|
|
||||||
|
static inline bool writeResetStats(PipeType fd, const bool alsoAch, const bool okay)
|
||||||
|
{
|
||||||
|
dbgpipe("Parent sending SHIMEVENT_RESETSTATS(%salsoAchievements, %sokay).\n", alsoAch ? "" : "!", okay ? "" : "!");
|
||||||
|
return write3ByteCmd(fd, SHIMEVENT_RESETSTATS, alsoAch ? 1 : 0, okay ? 1 : 0);
|
||||||
|
} // writeResetStats
|
||||||
|
|
||||||
|
static bool writeStatThing(PipeType fd, const ShimEvent ev, const char *name, const void *val, const size_t vallen, const bool okay)
|
||||||
|
{
|
||||||
|
uint8 buf[256];
|
||||||
|
uint8 *ptr = buf+1;
|
||||||
|
*(ptr++) = (uint8) ev;
|
||||||
|
*(ptr++) = okay ? 1 : 0;
|
||||||
|
memcpy(ptr, val, vallen);
|
||||||
|
ptr += vallen;
|
||||||
|
strcpy((char *) ptr, name);
|
||||||
|
ptr += strlen(name) + 1;
|
||||||
|
buf[0] = (uint8) ((ptr-1) - buf);
|
||||||
|
return writePipe(fd, buf, buf[0] + 1);
|
||||||
|
} // writeStatThing
|
||||||
|
|
||||||
|
static inline bool writeSetStatI(PipeType fd, const char *name, const int32 val, const bool okay)
|
||||||
|
{
|
||||||
|
dbgpipe("Parent sending SHIMEVENT_SETSTATI('%s', val %d, %sokay).\n", name, (int) val, okay ? "" : "!");
|
||||||
|
return writeStatThing(fd, SHIMEVENT_SETSTATI, name, &val, sizeof (val), okay);
|
||||||
|
} // writeSetStatI
|
||||||
|
|
||||||
|
static inline bool writeSetStatF(PipeType fd, const char *name, const float val, const bool okay)
|
||||||
|
{
|
||||||
|
dbgpipe("Parent sending SHIMEVENT_SETSTATF('%s', val %f, %sokay).\n", name, val, okay ? "" : "!");
|
||||||
|
return writeStatThing(fd, SHIMEVENT_SETSTATF, name, &val, sizeof (val), okay);
|
||||||
|
} // writeSetStatF
|
||||||
|
|
||||||
|
static inline bool writeGetStatI(PipeType fd, const char *name, const int32 val, const bool okay)
|
||||||
|
{
|
||||||
|
dbgpipe("Parent sending SHIMEVENT_GETSTATI('%s', val %d, %sokay).\n", name, (int) val, okay ? "" : "!");
|
||||||
|
return writeStatThing(fd, SHIMEVENT_GETSTATI, name, &val, sizeof (val), okay);
|
||||||
|
} // writeGetStatI
|
||||||
|
|
||||||
|
static inline bool writeGetStatF(PipeType fd, const char *name, const float val, const bool okay)
|
||||||
|
{
|
||||||
|
dbgpipe("Parent sending SHIMEVENT_GETSTATF('%s', val %f, %sokay).\n", name, val, okay ? "" : "!");
|
||||||
|
return writeStatThing(fd, SHIMEVENT_GETSTATF, name, &val, sizeof (val), okay);
|
||||||
|
} // writeGetStatF
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SteamBridge::SteamBridge(PipeType _fd)
|
||||||
|
: m_CallbackUserStatsReceived( this, &SteamBridge::OnUserStatsReceived )
|
||||||
|
, m_CallbackUserStatsStored( this, &SteamBridge::OnUserStatsStored )
|
||||||
|
, fd(_fd)
|
||||||
|
{
|
||||||
|
} // SteamBridge::SteamBridge
|
||||||
|
|
||||||
|
void SteamBridge::OnUserStatsReceived(UserStatsReceived_t *pCallback)
|
||||||
|
{
|
||||||
|
dbgpipe("***IN OnUserStatsReceived GAppID = %d, GUserID = %lld\n",GAppID,GUserID);
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
dbgpipe("***IN OnUserStatsStored GAppID = %d, GUserID = %lld\n",GAppID,GUserID);
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
dbgpipe("***IN processCommand GAppID = %d, GUserID = %lld\n",GAppID,GUserID);
|
||||||
|
|
||||||
|
if (buflen == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const ShimCmd cmd = (ShimCmd) *(buf++);
|
||||||
|
buflen--;
|
||||||
|
|
||||||
|
#if STEAMSHIM_DEBUG
|
||||||
|
if (false) {}
|
||||||
|
#define PRINTGOTCMD(x) else if (cmd == x) printf("Parent got " #x ".\n")
|
||||||
|
PRINTGOTCMD(SHIMCMD_BYE);
|
||||||
|
PRINTGOTCMD(SHIMCMD_PUMP);
|
||||||
|
PRINTGOTCMD(SHIMCMD_REQUESTSTATS);
|
||||||
|
PRINTGOTCMD(SHIMCMD_STORESTATS);
|
||||||
|
PRINTGOTCMD(SHIMCMD_SETACHIEVEMENT);
|
||||||
|
PRINTGOTCMD(SHIMCMD_GETACHIEVEMENT);
|
||||||
|
PRINTGOTCMD(SHIMCMD_RESETSTATS);
|
||||||
|
PRINTGOTCMD(SHIMCMD_SETSTATI);
|
||||||
|
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
|
||||||
|
|
||||||
|
switch (cmd)
|
||||||
|
{
|
||||||
|
case SHIMCMD_PUMP:
|
||||||
|
SteamAPI_RunCallbacks();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHIMCMD_BYE:
|
||||||
|
writeBye(fd);
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case SHIMCMD_REQUESTSTATS:
|
||||||
|
if ((!GSteamStats) || (!GSteamStats->RequestCurrentStats()))
|
||||||
|
writeStatsReceived(fd, false);
|
||||||
|
// callback later.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHIMCMD_STORESTATS:
|
||||||
|
if ((!GSteamStats) || (!GSteamStats->StoreStats()))
|
||||||
|
writeStatsStored(fd, false);
|
||||||
|
// callback later.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHIMCMD_SETACHIEVEMENT:
|
||||||
|
if (buflen >= 2)
|
||||||
|
{
|
||||||
|
const bool enable = (*(buf++) != 0);
|
||||||
|
const char *name = (const char *) buf; // !!! FIXME: buffer overflow possible.
|
||||||
|
if (!GSteamStats)
|
||||||
|
writeAchievementSet(fd, name, enable, false);
|
||||||
|
else if (enable && !GSteamStats->SetAchievement(name))
|
||||||
|
writeAchievementSet(fd, name, enable, false);
|
||||||
|
else if (!enable && !GSteamStats->ClearAchievement(name))
|
||||||
|
writeAchievementSet(fd, name, enable, false);
|
||||||
|
else
|
||||||
|
writeAchievementSet(fd, name, enable, true);
|
||||||
|
} // if
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHIMCMD_GETACHIEVEMENT:
|
||||||
|
if (buflen)
|
||||||
|
{
|
||||||
|
const char *name = (const char *) buf; // !!! FIXME: buffer overflow possible.
|
||||||
|
bool ach = false;
|
||||||
|
uint32 t = 0;
|
||||||
|
if ((GSteamStats) && (GSteamStats->GetAchievementAndUnlockTime(name, &ach, &t)))
|
||||||
|
writeAchievementGet(fd, name, ach ? 1 : 0, t);
|
||||||
|
else
|
||||||
|
writeAchievementGet(fd, name, 2, 0);
|
||||||
|
} // if
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHIMCMD_RESETSTATS:
|
||||||
|
if (buflen)
|
||||||
|
{
|
||||||
|
const bool alsoAch = (*(buf++) != 0);
|
||||||
|
writeResetStats(fd, alsoAch, (GSteamStats) && (GSteamStats->ResetAllStats(alsoAch)));
|
||||||
|
} // if
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHIMCMD_SETSTATI:
|
||||||
|
if (buflen >= 5)
|
||||||
|
{
|
||||||
|
const int32 val = *((int32 *) buf);
|
||||||
|
buf += sizeof (int32);
|
||||||
|
const char *name = (const char *) buf; // !!! FIXME: buffer overflow possible.
|
||||||
|
writeSetStatI(fd, name, val, (GSteamStats) && (GSteamStats->SetStat(name, val)));
|
||||||
|
} // if
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHIMCMD_GETSTATI:
|
||||||
|
if (buflen)
|
||||||
|
{
|
||||||
|
const char *name = (const char *) buf; // !!! FIXME: buffer overflow possible.
|
||||||
|
int32 val = 0;
|
||||||
|
if ((GSteamStats) && (GSteamStats->GetStat(name, &val)))
|
||||||
|
writeGetStatI(fd, name, val, true);
|
||||||
|
else
|
||||||
|
writeGetStatI(fd, name, 0, false);
|
||||||
|
} // if
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHIMCMD_SETSTATF:
|
||||||
|
if (buflen >= 5)
|
||||||
|
{
|
||||||
|
const float val = *((float *) buf);
|
||||||
|
buf += sizeof (float);
|
||||||
|
const char *name = (const char *) buf; // !!! FIXME: buffer overflow possible.
|
||||||
|
writeSetStatF(fd, name, val, (GSteamStats) && (GSteamStats->SetStat(name, val)));
|
||||||
|
} // if
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHIMCMD_GETSTATF:
|
||||||
|
if (buflen)
|
||||||
|
{
|
||||||
|
const char *name = (const char *) buf; // !!! FIXME: buffer overflow possible.
|
||||||
|
float val = 0;
|
||||||
|
if ((GSteamStats) && (GSteamStats->GetStat(name, &val)))
|
||||||
|
writeGetStatF(fd, name, val, true);
|
||||||
|
else
|
||||||
|
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.
|
||||||
|
} // processCommand
|
||||||
|
|
||||||
|
static void processCommands(PipeType pipeParentRead, PipeType pipeParentWrite)
|
||||||
|
{
|
||||||
|
bool quit = false;
|
||||||
|
uint8 buf[256];
|
||||||
|
int br;
|
||||||
|
|
||||||
|
dbgpipe("***START processCommands.\n");
|
||||||
|
|
||||||
|
// this read blocks.
|
||||||
|
while (!quit && ((br = readPipe(pipeParentRead, buf, sizeof (buf))) > 0))
|
||||||
|
{
|
||||||
|
dbgpipe("***IN processCommands readPipe found something.\n");
|
||||||
|
while (br > 0)
|
||||||
|
{
|
||||||
|
const int cmdlen = (int) buf[0];
|
||||||
|
if ((br-1) >= cmdlen)
|
||||||
|
{
|
||||||
|
dbgpipe("***IN processCommands processCommand.\n");
|
||||||
|
if (!processCommand(buf+1, cmdlen, pipeParentWrite))
|
||||||
|
{
|
||||||
|
quit = true;
|
||||||
|
break;
|
||||||
|
} // if
|
||||||
|
|
||||||
|
br -= cmdlen + 1;
|
||||||
|
if (br > 0)
|
||||||
|
memmove(buf, buf+cmdlen+1, br);
|
||||||
|
} // if
|
||||||
|
else // get more data.
|
||||||
|
{
|
||||||
|
dbgpipe("***IN processCommands readPipe#2....\n");
|
||||||
|
const int morebr = readPipe(pipeParentRead, buf+br, sizeof (buf) - br);
|
||||||
|
if (morebr <= 0)
|
||||||
|
{
|
||||||
|
quit = true; // uhoh.
|
||||||
|
break;
|
||||||
|
} // if
|
||||||
|
br += morebr;
|
||||||
|
} // else
|
||||||
|
} // while
|
||||||
|
} // while
|
||||||
|
dbgpipe("***END processCommands.\n");
|
||||||
|
} // processCommands
|
||||||
|
|
||||||
|
static bool setEnvironmentVars(PipeType pipeChildRead, PipeType pipeChildWrite)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
snprintf(buf, sizeof (buf), LLUFMT, (unsigned long long) pipeChildRead);
|
||||||
|
if (!setEnvVar("STEAMSHIM_READHANDLE", buf))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
snprintf(buf, sizeof (buf), LLUFMT, (unsigned long long) pipeChildWrite);
|
||||||
|
if (!setEnvVar("STEAMSHIM_WRITEHANDLE", buf))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} // setEnvironmentVars
|
||||||
|
|
||||||
|
static bool initSteamworks(PipeType fd)
|
||||||
|
{
|
||||||
|
dbgpipe("***IN initSteamworks Start\n");
|
||||||
|
|
||||||
|
// this can fail for many reasons:
|
||||||
|
// - you forgot a steam_appid.txt in the current working directory.
|
||||||
|
// - you don't have Steam running
|
||||||
|
// - you don't own the game listed in steam_appid.txt
|
||||||
|
if (!SteamAPI_Init())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
GSteamStats = SteamUserStats();
|
||||||
|
GSteamUtils = SteamUtils();
|
||||||
|
GSteamUser = SteamUser();
|
||||||
|
GSteamFriends = SteamFriends();
|
||||||
|
GSteamApps = SteamApps();
|
||||||
|
|
||||||
|
GAppID = GSteamUtils ? GSteamUtils->GetAppID() : 0;
|
||||||
|
GUserID = GSteamUser ? GSteamUser->GetSteamID().ConvertToUint64() : 0;
|
||||||
|
|
||||||
|
dbgpipe("***IN initSteamworks GAppID = %d, GUserID = %lld\n",GAppID,GUserID);
|
||||||
|
|
||||||
|
GSteamBridge = new SteamBridge(fd);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
} // initSteamworks
|
||||||
|
|
||||||
|
static void deinitSteamworks(void)
|
||||||
|
{
|
||||||
|
dbgpipe("***IN deinitSteamworks GAppID = %d, GUserID = %lld\n",GAppID,GUserID);
|
||||||
|
|
||||||
|
SteamAPI_Shutdown();
|
||||||
|
delete GSteamBridge;
|
||||||
|
GSteamBridge = NULL;
|
||||||
|
GSteamStats = NULL;
|
||||||
|
GSteamUtils= NULL;
|
||||||
|
GSteamUser = NULL;
|
||||||
|
} // deinitSteamworks
|
||||||
|
|
||||||
|
static int mainline(void)
|
||||||
|
{
|
||||||
|
PipeType pipeParentRead = NULLPIPE;
|
||||||
|
PipeType pipeParentWrite = NULLPIPE;
|
||||||
|
PipeType pipeChildRead = NULLPIPE;
|
||||||
|
PipeType pipeChildWrite = NULLPIPE;
|
||||||
|
ProcessType childPid;
|
||||||
|
|
||||||
|
printf("#2 =============> Megaglest Parent starting mainline.\n");
|
||||||
|
|
||||||
|
#ifdef STEAMSHIM_DEBUG
|
||||||
|
printf("=============> Megaglest Parent starting mainline.\n");
|
||||||
|
|
||||||
|
#endif
|
||||||
|
dbgpipe("Parent starting mainline.\n");
|
||||||
|
|
||||||
|
if (!createPipes(&pipeParentRead, &pipeParentWrite, &pipeChildRead, &pipeChildWrite))
|
||||||
|
fail("Failed to create application pipes");
|
||||||
|
else if (!initSteamworks(pipeParentWrite))
|
||||||
|
fail("Failed to initialize Steamworks");
|
||||||
|
else if (!setEnvironmentVars(pipeChildRead, pipeChildWrite))
|
||||||
|
fail("Failed to set environment variables");
|
||||||
|
else if (!launchChild(&childPid))
|
||||||
|
fail("Failed to launch application");
|
||||||
|
|
||||||
|
dbgpipe("Parent ending mainline\n");
|
||||||
|
|
||||||
|
// Close the ends of the pipes that the child will use; we don't need them.
|
||||||
|
closePipe(pipeChildRead);
|
||||||
|
closePipe(pipeChildWrite);
|
||||||
|
pipeChildRead = pipeChildWrite = NULLPIPE;
|
||||||
|
|
||||||
|
dbgpipe("Parent in command processing loop.\n");
|
||||||
|
|
||||||
|
// Now, we block for instructions until the pipe fails (child closed it or
|
||||||
|
// terminated/crashed).
|
||||||
|
processCommands(pipeParentRead, pipeParentWrite);
|
||||||
|
|
||||||
|
dbgpipe("Parent shutting down.\n");
|
||||||
|
|
||||||
|
// Close our ends of the pipes.
|
||||||
|
writeBye(pipeParentWrite);
|
||||||
|
closePipe(pipeParentRead);
|
||||||
|
closePipe(pipeParentWrite);
|
||||||
|
|
||||||
|
deinitSteamworks();
|
||||||
|
|
||||||
|
dbgpipe("Parent waiting on child process.\n");
|
||||||
|
|
||||||
|
// Wait for the child to terminate, close the child process handles.
|
||||||
|
const int retval = closeProcess(&childPid);
|
||||||
|
|
||||||
|
dbgpipe("Parent exiting mainline (child exit code %d).\n", retval);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
} // mainline
|
||||||
|
|
||||||
|
// end of steamshim_parent.cpp ...
|
Loading…
Reference in New Issue