aseprite/src/steam/steam.cpp
2020-06-11 17:15:21 -03:00

274 lines
7.4 KiB
C++

// Aseprite Steam Wrapper
// Copyright (c) 2020 Igara Studio S.A.
// Copyright (c) 2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "steam/steam.h"
#include "base/convert_to.h"
#include "base/dll.h"
#include "base/fs.h"
#include "base/ints.h"
#include "base/launcher.h"
#include "base/log.h"
#include "base/string.h"
namespace steam {
typedef uint32_t HSteamPipe;
typedef uint32_t HSteamUser;
typedef uint32_t ScreenshotHandle;
struct ISteamScreenshots;
struct ISteamUtils;
enum {
// Last callback received from Steam client when it's Steam is closed
kSteamServersDisconnected = 103,
kSteamUndocumentedLastCallback = 1009,
// When a screenshot is ready in the library
kScreenshotReady = 2301,
};
struct CallbackMsg_t {
HSteamUser steamUser;
int callback;
uint8_t* pubParam;
int cubParam;
};
#ifdef __GNUC__
#define __cdecl
#endif
// Steam main API
typedef bool (__cdecl *SteamAPI_Init_Func)();
typedef void (__cdecl *SteamAPI_Shutdown_Func)();
typedef HSteamPipe (__cdecl *SteamAPI_GetHSteamPipe_Func)();
// Steam callbacks
typedef void (__cdecl *SteamAPI_ManualDispatch_Init_Func)();
typedef void (__cdecl *SteamAPI_ManualDispatch_RunFrame_Func)(HSteamPipe);
typedef bool (__cdecl *SteamAPI_ManualDispatch_GetNextCallback_Func)(HSteamPipe, CallbackMsg_t*);
typedef void (__cdecl *SteamAPI_ManualDispatch_FreeLastCallback_Func)(HSteamPipe);
// ISteamScreenshots
typedef ISteamScreenshots* (__cdecl *SteamAPI_SteamScreenshots_v003_Func)();
typedef ScreenshotHandle (__cdecl *SteamAPI_ISteamScreenshots_WriteScreenshot_Func)(ISteamScreenshots*, void*, uint32_t, int, int);
// ISteamUtils
typedef ISteamUtils* (__cdecl *SteamAPI_SteamUtils_v009_Func)();
typedef uint32_t (__cdecl *SteamAPI_ISteamUtils_GetAppID_Func)(ISteamUtils*);
#ifdef _WIN32
#ifdef _WIN64
#define STEAM_API_DLL_FILENAME "steam_api64.dll"
#else
#define STEAM_API_DLL_FILENAME "steam_api.dll"
#endif
#elif defined(__APPLE__)
#define STEAM_API_DLL_FILENAME "libsteam_api.dylib"
#else
#define STEAM_API_DLL_FILENAME "libsteam_api.so"
#endif
#define GETPROC(name) base::get_dll_proc<name##_Func>(m_steamLib, #name)
class SteamAPI::Impl {
public:
Impl() {
m_steamLib = base::load_dll(
base::join_path(base::get_file_path(base::get_app_path()),
STEAM_API_DLL_FILENAME));
if (!m_steamLib) {
LOG("STEAM: Steam library not found...\n");
return;
}
auto SteamAPI_Init = GETPROC(SteamAPI_Init);
if (!SteamAPI_Init) {
LOG("STEAM: SteamAPI_Init not found...\n");
return;
}
// Call SteamAPI_Init() to connect to Steam
if (!SteamAPI_Init()) {
LOG("STEAM: Steam is not initialized...\n");
return;
}
// Get functions to dispatch callbacks manually
auto SteamAPI_ManualDispatch_Init = GETPROC(SteamAPI_ManualDispatch_Init);
SteamAPI_ManualDispatch_RunFrame = GETPROC(SteamAPI_ManualDispatch_RunFrame);
SteamAPI_ManualDispatch_GetNextCallback = GETPROC(SteamAPI_ManualDispatch_GetNextCallback);
SteamAPI_ManualDispatch_FreeLastCallback = GETPROC(SteamAPI_ManualDispatch_FreeLastCallback);
auto SteamAPI_GetHSteamPipe = GETPROC(SteamAPI_GetHSteamPipe);
if (SteamAPI_ManualDispatch_Init &&
SteamAPI_ManualDispatch_RunFrame &&
SteamAPI_ManualDispatch_GetNextCallback &&
SteamAPI_ManualDispatch_FreeLastCallback &&
SteamAPI_GetHSteamPipe) {
SteamAPI_ManualDispatch_Init();
m_pipe = SteamAPI_GetHSteamPipe();
}
LOG("STEAM: Steam initialized\n");
m_initialized = true;
}
~Impl() {
if (!m_steamLib)
return;
auto SteamAPI_Shutdown = GETPROC(SteamAPI_Shutdown);
if (SteamAPI_Shutdown) {
LOG("STEAM: Steam shutdown...\n");
SteamAPI_Shutdown();
}
unloadLib();
}
bool initialized() const {
return m_initialized;
}
void runCallbacks() {
if (!m_pipe)
return;
ASSERT(SteamAPI_ManualDispatch_RunFrame);
ASSERT(SteamAPI_ManualDispatch_GetNextCallback);
ASSERT(SteamAPI_ManualDispatch_FreeLastCallback);
SteamAPI_ManualDispatch_RunFrame(m_pipe);
CallbackMsg_t msg;
if (SteamAPI_ManualDispatch_GetNextCallback(m_pipe, &msg)) {
//TRACEARGS("SteamAPI_ManualDispatch_GetNextCallback", msg.callback);
bool disconnected = false;
switch (msg.callback) {
case kSteamServersDisconnected:
case kSteamUndocumentedLastCallback:
disconnected = true;
break;
// When a screenshot is ready, we open the Steam library of screenshots
case kScreenshotReady: {
std::string url = "steam://open/screenshots/";
auto SteamAPI_SteamUtils_v009 = GETPROC(SteamAPI_SteamUtils_v009);
auto SteamAPI_ISteamUtils_GetAppID = GETPROC(SteamAPI_ISteamUtils_GetAppID);
if (SteamAPI_SteamUtils_v009 &&
SteamAPI_ISteamUtils_GetAppID) {
ISteamUtils* utils = SteamAPI_SteamUtils_v009();
if (utils) {
int appId = SteamAPI_ISteamUtils_GetAppID(utils);
url += base::convert_to<std::string>(appId);
}
}
base::launcher::open_url(url);
break;
}
}
SteamAPI_ManualDispatch_FreeLastCallback(m_pipe);
// If the Steam client is closed, we have to unload the DLL and
// don't use the pipe or any Steam API at all, in other case we
// would crash.
if (disconnected) {
LOG("STEAM: Disconnected\n");
unloadLib();
}
}
}
bool writeScreenshot(void* rgbBuffer,
uint32_t sizeInBytes,
int width, int height) {
if (!m_initialized)
return false;
auto SteamScreenshots = GETPROC(SteamAPI_SteamScreenshots_v003);
auto WriteScreenshot = GETPROC(SteamAPI_ISteamScreenshots_WriteScreenshot);
if (!SteamScreenshots || !WriteScreenshot) {
LOG("STEAM: Error getting Steam Screenshot API functions\n");
return false;
}
auto screenshots = SteamScreenshots();
if (!screenshots) {
LOG("STEAM: Error getting Steam Screenshot API instance\n");
return false;
}
WriteScreenshot(screenshots, rgbBuffer, sizeInBytes, width, height);
return true;
}
private:
void unloadLib() {
base::unload_dll(m_steamLib);
m_steamLib = nullptr;
m_initialized = false;
m_pipe = 0;
}
bool m_initialized = false;
base::dll m_steamLib = nullptr;
// To handle callbacks manually
HSteamPipe m_pipe = 0;
SteamAPI_ManualDispatch_RunFrame_Func SteamAPI_ManualDispatch_RunFrame = nullptr;
SteamAPI_ManualDispatch_GetNextCallback_Func SteamAPI_ManualDispatch_GetNextCallback = nullptr;
SteamAPI_ManualDispatch_FreeLastCallback_Func SteamAPI_ManualDispatch_FreeLastCallback = nullptr;
};
SteamAPI* g_instance = nullptr;
// static
SteamAPI* SteamAPI::instance()
{
return g_instance;
}
SteamAPI::SteamAPI()
: m_impl(new Impl)
{
ASSERT(g_instance == nullptr);
g_instance = this;
}
SteamAPI::~SteamAPI()
{
delete m_impl;
ASSERT(g_instance == this);
g_instance = nullptr;
}
bool SteamAPI::initialized() const
{
return m_impl->initialized();
}
void SteamAPI::runCallbacks()
{
m_impl->runCallbacks();
}
bool SteamAPI::writeScreenshot(void* rgbBuffer,
uint32_t sizeInBytes,
int width, int height)
{
return m_impl->writeScreenshot(rgbBuffer, sizeInBytes, width, height);
}
} // namespace steam