#include "SDL_internal.h"
#include "../SDL_sysstorage.h"
#if defined(_WIN64)
#define SDL_DRIVER_STEAMAPI_DYNAMIC "steam_api64.dll"
#elif defined(_WIN32)
#define SDL_DRIVER_STEAMAPI_DYNAMIC "steam_api.dll"
#elif defined(__APPLE__)
#define SDL_DRIVER_STEAMAPI_DYNAMIC "libsteam_api.dylib"
#else
#define SDL_DRIVER_STEAMAPI_DYNAMIC "libsteam_api.so"
#endif
SDL_ELF_NOTE_DLOPEN(
"storage-steam",
"Support for Steam user storage",
SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
SDL_DRIVER_STEAMAPI_DYNAMIC
)
#define STEAM_PROC(ret, func, parms) \
typedef ret (*steamfntype_##func) parms;
#include "SDL_steamstorage_proc.h"
typedef struct STEAM_RemoteStorage
{
SDL_SharedObject *libsteam_api;
#define STEAM_PROC(ret, func, parms) \
steamfntype_##func func;
#include "SDL_steamstorage_proc.h"
} STEAM_RemoteStorage;
static SDL_AtomicInt SDL_steam_storage_refcount;
static bool STEAM_CloseStorage(void *userdata)
{
bool result = true;
STEAM_RemoteStorage *steam = (STEAM_RemoteStorage *)userdata;
void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
bool end_batch = SDL_AtomicDecRef(&SDL_steam_storage_refcount);
if (steamremotestorage == NULL) {
result = SDL_SetError("SteamRemoteStorage unavailable");
} else if (end_batch) {
if (!steam->SteamAPI_ISteamRemoteStorage_EndFileWriteBatch(steamremotestorage)) {
result = SDL_SetError("SteamRemoteStorage()->EndFileWriteBatch() failed");
}
}
SDL_UnloadObject(steam->libsteam_api);
SDL_free(steam);
return result;
}
static bool STEAM_StorageReady(void *userdata)
{
return true;
}
static char *GetNormalizedStoragePath(const char *path, bool add_separator)
{
if (SDL_strcmp(path, ".") == 0) {
path = "";
} else {
while (*path == '/') {
++path;
}
}
size_t pathlen = SDL_strlen(path);
while (pathlen > 0 && path[pathlen - 1] == '/') {
--pathlen;
}
char *normalized = (char *)SDL_malloc(pathlen + add_separator + 1);
if (normalized) {
SDL_memcpy(normalized, path, pathlen);
if (add_separator) {
normalized[pathlen++] = '/';
}
normalized[pathlen] = '\0';
}
return normalized;
}
static bool STEAM_EnumerateStorageDirectory(void *userdata, const char *path, SDL_EnumerateDirectoryCallback callback, void *callback_userdata)
{
bool result = true;
STEAM_RemoteStorage *steam = (STEAM_RemoteStorage *)userdata;
void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
if (steamremotestorage == NULL) {
return SDL_SetError("SteamRemoteStorage unavailable");
}
char *dirname = GetNormalizedStoragePath(path, true);
if (!dirname) {
return false;
}
size_t dirlen = SDL_strlen(dirname);
bool done = false;
Sint32 count = steam->SteamAPI_ISteamRemoteStorage_GetFileCount(steamremotestorage);
for (Sint32 i = count; i-- && !done; ) {
const char *file = steam->SteamAPI_ISteamRemoteStorage_GetFileNameAndSize(steamremotestorage, i, NULL);
if (!file) {
continue;
}
const char *fname;
if (dirlen > 1) {
if (SDL_strncmp(dirname, file, dirlen) != 0) {
continue;
}
fname = file + dirlen;
} else {
fname = file;
}
if (SDL_strchr(fname, '/') != NULL) {
continue;
}
switch (callback(callback_userdata, dirname, fname)) {
case SDL_ENUM_SUCCESS:
done = true;
break;
case SDL_ENUM_FAILURE:
result = false;
done = true;
break;
default:
break;
}
}
SDL_free(dirname);
return result;
}
static bool STEAM_GetStoragePathInfo(void *userdata, const char *path, SDL_PathInfo *info)
{
STEAM_RemoteStorage *steam = (STEAM_RemoteStorage *)userdata;
void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
if (steamremotestorage == NULL) {
return SDL_SetError("SteamRemoteStorage unavailable");
}
if (!steam->SteamAPI_ISteamRemoteStorage_FileExists(steamremotestorage, path)) {
return SDL_SetError("Can't stat");
}
if (info) {
SDL_zerop(info);
info->type = SDL_PATHTYPE_FILE;
info->size = steam->SteamAPI_ISteamRemoteStorage_GetFileSize(steamremotestorage, path);
Sint64 mtime = steam->SteamAPI_ISteamRemoteStorage_GetFileTimestamp(steamremotestorage, path);
info->modify_time = (SDL_Time)SDL_SECONDS_TO_NS(mtime);
}
return true;
}
static bool STEAM_ReadStorageFile(void *userdata, const char *path, void *destination, Uint64 length)
{
bool result = false;
STEAM_RemoteStorage *steam = (STEAM_RemoteStorage *)userdata;
void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
if (steamremotestorage == NULL) {
return SDL_SetError("SteamRemoteStorage unavailable");
}
if (length > SDL_MAX_SINT32) {
return SDL_SetError("SteamRemoteStorage only supports INT32_MAX read size");
}
if (steam->SteamAPI_ISteamRemoteStorage_FileRead(steamremotestorage, path, destination, (Sint32) length) == length) {
result = true;
} else {
SDL_SetError("SteamRemoteStorage()->FileRead() failed");
}
return result;
}
static bool STEAM_WriteStorageFile(void *userdata, const char *path, const void *source, Uint64 length)
{
bool result = false;
STEAM_RemoteStorage *steam = (STEAM_RemoteStorage *)userdata;
void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
if (steamremotestorage == NULL) {
return SDL_SetError("SteamRemoteStorage unavailable");
}
if (length > SDL_MAX_SINT32) {
return SDL_SetError("SteamRemoteStorage only supports INT32_MAX write size");
}
if (steam->SteamAPI_ISteamRemoteStorage_FileWrite(steamremotestorage, path, source, (Sint32)length)) {
result = true;
} else {
SDL_SetError("SteamRemoteStorage()->FileWrite() failed");
}
return result;
}
static bool STEAM_RemoveStoragePath(void *userdata, const char *path)
{
STEAM_RemoteStorage *steam = (STEAM_RemoteStorage *)userdata;
void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
if (steamremotestorage == NULL) {
return SDL_SetError("SteamRemoteStorage unavailable");
}
if (!steam->SteamAPI_ISteamRemoteStorage_FileDelete(steamremotestorage, path)) {
return SDL_SetError("SteamRemoteStorage()->FileDelete() failed");
}
return true;
}
static Uint64 STEAM_GetStorageSpaceRemaining(void *userdata)
{
Uint64 total, remaining;
STEAM_RemoteStorage *steam = (STEAM_RemoteStorage *)userdata;
void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
if (steamremotestorage == NULL) {
SDL_SetError("SteamRemoteStorage unavailable");
return 0;
}
if (!steam->SteamAPI_ISteamRemoteStorage_GetQuota(steamremotestorage, &total, &remaining)) {
SDL_SetError("SteamRemoteStorage()->GetQuota() failed");
return 0;
}
return remaining;
}
static const SDL_StorageInterface STEAM_user_iface = {
sizeof(SDL_StorageInterface),
STEAM_CloseStorage,
STEAM_StorageReady,
STEAM_EnumerateStorageDirectory,
STEAM_GetStoragePathInfo,
STEAM_ReadStorageFile,
STEAM_WriteStorageFile,
NULL, STEAM_RemoveStoragePath,
NULL, NULL, STEAM_GetStorageSpaceRemaining
};
static SDL_Storage *STEAM_User_Create(const char *org, const char *app, SDL_PropertiesID props)
{
SDL_Storage *result;
STEAM_RemoteStorage *steam;
void *steamremotestorage;
steam = (STEAM_RemoteStorage *)SDL_malloc(sizeof(STEAM_RemoteStorage));
if (steam == NULL) {
return NULL;
}
steam->libsteam_api = SDL_LoadObject(SDL_DRIVER_STEAMAPI_DYNAMIC);
if (steam->libsteam_api == NULL) {
SDL_free(steam);
return NULL;
}
#define STEAM_PROC(ret, func, parms) \
steam->func = (steamfntype_##func) SDL_LoadFunction(steam->libsteam_api, #func); \
if (steam->func == NULL) { \
SDL_SetError("Could not load function " #func); \
goto steamfail; \
}
#include "SDL_steamstorage_proc.h"
steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
if (steamremotestorage == NULL) {
SDL_SetError("SteamRemoteStorage unavailable");
goto steamfail;
}
if (!steam->SteamAPI_ISteamRemoteStorage_IsCloudEnabledForAccount(steamremotestorage)) {
SDL_SetError("Steam cloud is disabled for this user");
goto steamfail;
}
if (!steam->SteamAPI_ISteamRemoteStorage_IsCloudEnabledForApp(steamremotestorage)) {
SDL_SetError("Steam cloud is disabled for this application");
goto steamfail;
}
result = SDL_OpenStorage(&STEAM_user_iface, steam);
if (!result) {
goto steamfail;
}
if (SDL_AtomicIncRef(&SDL_steam_storage_refcount) == 0) {
if (!steam->SteamAPI_ISteamRemoteStorage_BeginFileWriteBatch(steamremotestorage)) {
}
}
return result;
steamfail:
SDL_UnloadObject(steam->libsteam_api);
SDL_free(steam);
return NULL;
}
UserStorageBootStrap STEAM_userbootstrap = {
"steam",
"SDL Steam user storage driver",
STEAM_User_Create
};