518 lines
16 KiB
C++
518 lines
16 KiB
C++
#ifndef NSTEAM
|
|
|
|
extern "C" {
|
|
#include "qjs_steam.h"
|
|
#include "cell.h"
|
|
#include "qjs_macros.h"
|
|
#include "qjs_blob.h"
|
|
}
|
|
|
|
#include <steam/steam_api.h>
|
|
#include <steam/steam_api_flat.h>
|
|
#include <string.h>
|
|
|
|
// Steam interface pointers
|
|
static ISteamUserStats *steam_stats = NULL;
|
|
static ISteamApps *steam_apps = NULL;
|
|
static ISteamRemoteStorage *steam_remote = NULL;
|
|
static ISteamUGC *steam_ugc = NULL;
|
|
static ISteamUser *steam_user = NULL;
|
|
static ISteamFriends *steam_friends = NULL;
|
|
static ISteamUtils *steam_utils = NULL;
|
|
static bool steam_initialized = false;
|
|
|
|
// STEAM INITIALIZATION
|
|
JSC_CCALL(steam_init,
|
|
if (steam_initialized) {
|
|
return JS_NewBool(js, true);
|
|
}
|
|
|
|
SteamErrMsg err;
|
|
steam_initialized = SteamAPI_InitEx(&err);
|
|
|
|
if (!steam_initialized) {
|
|
return JS_ThrowInternalError(js, "Failed to initialize Steam: %s", err);
|
|
}
|
|
|
|
// Get interface pointers
|
|
steam_stats = SteamAPI_SteamUserStats();
|
|
steam_apps = SteamAPI_SteamApps();
|
|
steam_remote = SteamAPI_SteamRemoteStorage();
|
|
steam_ugc = SteamAPI_SteamUGC();
|
|
steam_user = SteamAPI_SteamUser();
|
|
steam_friends = SteamAPI_SteamFriends();
|
|
steam_utils = SteamAPI_SteamUtils();
|
|
|
|
return JS_NewBool(js, steam_initialized);
|
|
)
|
|
|
|
JSC_CCALL(steam_shutdown,
|
|
if (steam_initialized) {
|
|
SteamAPI_Shutdown();
|
|
steam_initialized = false;
|
|
}
|
|
return JS_UNDEFINED;
|
|
)
|
|
|
|
JSC_CCALL(steam_run_callbacks,
|
|
if (steam_initialized) {
|
|
SteamAPI_RunCallbacks();
|
|
}
|
|
return JS_UNDEFINED;
|
|
)
|
|
|
|
// USER STATS & ACHIEVEMENTS
|
|
JSC_CCALL(steam_stats_request,
|
|
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
|
|
|
SteamAPICall_t call = SteamAPI_ISteamUserStats_RequestUserStats(steam_stats, SteamAPI_ISteamUser_GetSteamID(steam_user));
|
|
bool success = (call != k_uAPICallInvalid);
|
|
return JS_NewBool(js, success);
|
|
)
|
|
|
|
JSC_CCALL(steam_stats_store,
|
|
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
|
|
|
bool success = SteamAPI_ISteamUserStats_StoreStats(steam_stats);
|
|
return JS_NewBool(js, success);
|
|
)
|
|
|
|
JSC_CCALL(steam_stats_get_int,
|
|
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
|
|
|
const char *name = JS_ToCString(js, argv[0]);
|
|
if (!name) return JS_EXCEPTION;
|
|
|
|
int32 value;
|
|
bool success = SteamAPI_ISteamUserStats_GetStatInt32(steam_stats, name, &value);
|
|
JS_FreeCString(js, name);
|
|
|
|
if (!success) return JS_UNDEFINED;
|
|
return JS_NewInt32(js, value);
|
|
)
|
|
|
|
JSC_CCALL(steam_stats_get_float,
|
|
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
|
|
|
const char *name = JS_ToCString(js, argv[0]);
|
|
if (!name) return JS_EXCEPTION;
|
|
|
|
float value;
|
|
bool success = SteamAPI_ISteamUserStats_GetStatFloat(steam_stats, name, &value);
|
|
JS_FreeCString(js, name);
|
|
|
|
if (!success) return JS_UNDEFINED;
|
|
return JS_NewFloat64(js, value);
|
|
)
|
|
|
|
JSC_CCALL(steam_stats_set_int,
|
|
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
|
|
|
const char *name = JS_ToCString(js, argv[0]);
|
|
if (!name) return JS_EXCEPTION;
|
|
|
|
int32 value;
|
|
JS_ToInt32(js, &value, argv[1]);
|
|
|
|
bool success = SteamAPI_ISteamUserStats_SetStatInt32(steam_stats, name, value);
|
|
JS_FreeCString(js, name);
|
|
|
|
return JS_NewBool(js, success);
|
|
)
|
|
|
|
JSC_CCALL(steam_stats_set_float,
|
|
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
|
|
|
const char *name = JS_ToCString(js, argv[0]);
|
|
if (!name) return JS_EXCEPTION;
|
|
|
|
double dvalue;
|
|
JS_ToFloat64(js, &dvalue, argv[1]);
|
|
float value = (float)dvalue;
|
|
|
|
bool success = SteamAPI_ISteamUserStats_SetStatFloat(steam_stats, name, value);
|
|
JS_FreeCString(js, name);
|
|
|
|
return JS_NewBool(js, success);
|
|
)
|
|
|
|
JSC_CCALL(steam_achievement_get,
|
|
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
|
|
|
const char *name = JS_ToCString(js, argv[0]);
|
|
if (!name) return JS_EXCEPTION;
|
|
|
|
bool achieved;
|
|
bool success = SteamAPI_ISteamUserStats_GetAchievement(steam_stats, name, &achieved);
|
|
JS_FreeCString(js, name);
|
|
|
|
if (!success) return JS_UNDEFINED;
|
|
return JS_NewBool(js, achieved);
|
|
)
|
|
|
|
JSC_CCALL(steam_achievement_set,
|
|
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
|
|
|
const char *name = JS_ToCString(js, argv[0]);
|
|
if (!name) return JS_EXCEPTION;
|
|
|
|
bool success = SteamAPI_ISteamUserStats_SetAchievement(steam_stats, name);
|
|
JS_FreeCString(js, name);
|
|
|
|
return JS_NewBool(js, success);
|
|
)
|
|
|
|
JSC_CCALL(steam_achievement_clear,
|
|
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
|
|
|
const char *name = JS_ToCString(js, argv[0]);
|
|
if (!name) return JS_EXCEPTION;
|
|
|
|
bool success = SteamAPI_ISteamUserStats_ClearAchievement(steam_stats, name);
|
|
JS_FreeCString(js, name);
|
|
|
|
return JS_NewBool(js, success);
|
|
)
|
|
|
|
JSC_CCALL(steam_achievement_count,
|
|
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
|
|
|
uint32 count = SteamAPI_ISteamUserStats_GetNumAchievements(steam_stats);
|
|
return JS_NewUint32(js, count);
|
|
)
|
|
|
|
JSC_CCALL(steam_achievement_name,
|
|
if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized");
|
|
|
|
uint32 index;
|
|
JS_ToUint32(js, &index, argv[0]);
|
|
|
|
const char *name = SteamAPI_ISteamUserStats_GetAchievementName(steam_stats, index);
|
|
if (!name) return JS_UNDEFINED;
|
|
|
|
return JS_NewString(js, name);
|
|
)
|
|
|
|
// APPS
|
|
JSC_CCALL(steam_app_id,
|
|
if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized");
|
|
|
|
AppId_t appid = SteamAPI_ISteamUtils_GetAppID(steam_utils);
|
|
return JS_NewUint32(js, appid);
|
|
)
|
|
|
|
JSC_CCALL(steam_app_owner,
|
|
if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized");
|
|
|
|
uint64_steamid owner = SteamAPI_ISteamApps_GetAppOwner(steam_apps);
|
|
return JS_NewBigUint64(js, owner);
|
|
)
|
|
|
|
JSC_CCALL(steam_app_installed,
|
|
if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized");
|
|
|
|
uint32 appid;
|
|
JS_ToUint32(js, &appid, argv[0]);
|
|
|
|
bool installed = SteamAPI_ISteamApps_BIsAppInstalled(steam_apps, appid);
|
|
return JS_NewBool(js, installed);
|
|
)
|
|
|
|
JSC_CCALL(steam_app_subscribed,
|
|
if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized");
|
|
|
|
bool subscribed = SteamAPI_ISteamApps_BIsSubscribed(steam_apps);
|
|
return JS_NewBool(js, subscribed);
|
|
)
|
|
|
|
JSC_CCALL(steam_app_language,
|
|
if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized");
|
|
|
|
const char *lang = SteamAPI_ISteamApps_GetCurrentGameLanguage(steam_apps);
|
|
return JS_NewString(js, lang ? lang : "english");
|
|
)
|
|
|
|
JSC_CCALL(steam_app_dlc_installed,
|
|
if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized");
|
|
|
|
AppId_t dlcid;
|
|
JS_ToUint32(js, &dlcid, argv[0]);
|
|
|
|
bool installed = SteamAPI_ISteamApps_BIsDlcInstalled(steam_apps, dlcid);
|
|
return JS_NewBool(js, installed);
|
|
)
|
|
|
|
// USER
|
|
JSC_CCALL(steam_user_logged_on,
|
|
if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized");
|
|
|
|
bool logged = SteamAPI_ISteamUser_BLoggedOn(steam_user);
|
|
return JS_NewBool(js, logged);
|
|
)
|
|
|
|
JSC_CCALL(steam_user_steam_id,
|
|
if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized");
|
|
|
|
uint64_steamid id = SteamAPI_ISteamUser_GetSteamID(steam_user);
|
|
return JS_NewBigUint64(js, id);
|
|
)
|
|
|
|
JSC_CCALL(steam_user_level,
|
|
if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized");
|
|
|
|
int level = SteamAPI_ISteamUser_GetPlayerSteamLevel(steam_user);
|
|
return JS_NewInt32(js, level);
|
|
)
|
|
|
|
// FRIENDS
|
|
JSC_CCALL(steam_friends_name,
|
|
if (!steam_friends) return JS_ThrowInternalError(js, "Steam friends not initialized");
|
|
|
|
const char *name = SteamAPI_ISteamFriends_GetPersonaName(steam_friends);
|
|
return JS_NewString(js, name ? name : "");
|
|
)
|
|
|
|
JSC_CCALL(steam_friends_state,
|
|
if (!steam_friends) return JS_ThrowInternalError(js, "Steam friends not initialized");
|
|
|
|
EPersonaState state = SteamAPI_ISteamFriends_GetPersonaState(steam_friends);
|
|
const char *state_str;
|
|
switch(state) {
|
|
case k_EPersonaStateOffline: state_str = "offline"; break;
|
|
case k_EPersonaStateOnline: state_str = "online"; break;
|
|
case k_EPersonaStateBusy: state_str = "busy"; break;
|
|
case k_EPersonaStateAway: state_str = "away"; break;
|
|
case k_EPersonaStateSnooze: state_str = "snooze"; break;
|
|
case k_EPersonaStateLookingToTrade: state_str = "trade"; break;
|
|
case k_EPersonaStateLookingToPlay: state_str = "play"; break;
|
|
default: state_str = "unknown"; break;
|
|
}
|
|
return JS_NewString(js, state_str);
|
|
)
|
|
|
|
// CLOUD STORAGE
|
|
JSC_CCALL(steam_cloud_enabled_app,
|
|
if (!steam_remote) return JS_ThrowInternalError(js, "Steam remote storage not initialized");
|
|
|
|
bool enabled = SteamAPI_ISteamRemoteStorage_IsCloudEnabledForApp(steam_remote);
|
|
return JS_NewBool(js, enabled);
|
|
)
|
|
|
|
JSC_CCALL(steam_cloud_enabled_account,
|
|
if (!steam_remote) return JS_ThrowInternalError(js, "Steam remote storage not initialized");
|
|
|
|
bool enabled = SteamAPI_ISteamRemoteStorage_IsCloudEnabledForAccount(steam_remote);
|
|
return JS_NewBool(js, enabled);
|
|
)
|
|
|
|
JSC_CCALL(steam_cloud_enable,
|
|
if (!steam_remote) return JS_ThrowInternalError(js, "Steam remote storage not initialized");
|
|
|
|
bool enable = JS_ToBool(js, argv[0]);
|
|
SteamAPI_ISteamRemoteStorage_SetCloudEnabledForApp(steam_remote, enable);
|
|
return JS_UNDEFINED;
|
|
)
|
|
|
|
JSC_CCALL(steam_cloud_quota,
|
|
if (!steam_remote) return JS_ThrowInternalError(js, "Steam remote storage not initialized");
|
|
|
|
uint64 total, available;
|
|
bool success = SteamAPI_ISteamRemoteStorage_GetQuota(steam_remote, &total, &available);
|
|
|
|
if (!success) return JS_UNDEFINED;
|
|
|
|
JSValue obj = JS_NewObject(js);
|
|
JS_SetPropertyStr(js, obj, "total", JS_NewBigUint64(js, total));
|
|
JS_SetPropertyStr(js, obj, "available", JS_NewBigUint64(js, available));
|
|
return obj;
|
|
)
|
|
|
|
JSC_CCALL(steam_cloud_write,
|
|
if (!steam_remote) return JS_ThrowInternalError(js, "Steam remote storage not initialized");
|
|
|
|
const char *filename = JS_ToCString(js, argv[0]);
|
|
if (!filename) return JS_EXCEPTION;
|
|
|
|
size_t data_len;
|
|
uint8_t *data = NULL;
|
|
|
|
if (JS_IsString(argv[1])) {
|
|
const char *str = JS_ToCStringLen(js, &data_len, argv[1]);
|
|
if (!str) {
|
|
JS_FreeCString(js, filename);
|
|
return JS_EXCEPTION;
|
|
}
|
|
data = (uint8_t*)str;
|
|
} else {
|
|
data = (uint8_t*)js_get_blob_data(js, &data_len, argv[1]);
|
|
if (!data) {
|
|
JS_FreeCString(js, filename);
|
|
return JS_ThrowTypeError(js, "Second argument must be string or ArrayBuffer");
|
|
}
|
|
}
|
|
|
|
bool success = SteamAPI_ISteamRemoteStorage_FileWrite(steam_remote, filename, data, data_len);
|
|
|
|
if (JS_IsString(argv[1])) {
|
|
JS_FreeCString(js, (const char*)data);
|
|
}
|
|
JS_FreeCString(js, filename);
|
|
|
|
return JS_NewBool(js, success);
|
|
)
|
|
|
|
JSC_CCALL(steam_cloud_read,
|
|
if (!steam_remote) return JS_ThrowInternalError(js, "Steam remote storage not initialized");
|
|
|
|
const char *filename = JS_ToCString(js, argv[0]);
|
|
if (!filename) return JS_EXCEPTION;
|
|
|
|
int32 size = SteamAPI_ISteamRemoteStorage_GetFileSize(steam_remote, filename);
|
|
if (size <= 0) {
|
|
JS_FreeCString(js, filename);
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
uint8_t *buffer = (uint8_t*)js_malloc(js, size);
|
|
if (!buffer) {
|
|
JS_FreeCString(js, filename);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
int32 read = SteamAPI_ISteamRemoteStorage_FileRead(steam_remote, filename, buffer, size);
|
|
JS_FreeCString(js, filename);
|
|
|
|
if (read != size) {
|
|
js_free(js, buffer);
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
JSValue result = js_new_blob_stoned_copy(js, buffer, size);
|
|
js_free(js, buffer);
|
|
return result;
|
|
)
|
|
|
|
JSC_CCALL(steam_cloud_delete,
|
|
if (!steam_remote) return JS_ThrowInternalError(js, "Steam remote storage not initialized");
|
|
|
|
const char *filename = JS_ToCString(js, argv[0]);
|
|
if (!filename) return JS_EXCEPTION;
|
|
|
|
bool success = SteamAPI_ISteamRemoteStorage_FileDelete(steam_remote, filename);
|
|
JS_FreeCString(js, filename);
|
|
|
|
return JS_NewBool(js, success);
|
|
)
|
|
|
|
JSC_CCALL(steam_cloud_exists,
|
|
if (!steam_remote) return JS_ThrowInternalError(js, "Steam remote storage not initialized");
|
|
|
|
const char *filename = JS_ToCString(js, argv[0]);
|
|
if (!filename) return JS_EXCEPTION;
|
|
|
|
bool exists = SteamAPI_ISteamRemoteStorage_FileExists(steam_remote, filename);
|
|
JS_FreeCString(js, filename);
|
|
|
|
return JS_NewBool(js, exists);
|
|
)
|
|
|
|
// Function lists for sub-objects
|
|
static const JSCFunctionListEntry js_steam_stats_funcs[] = {
|
|
MIST_FUNC_DEF(steam, stats_request, 0),
|
|
MIST_FUNC_DEF(steam, stats_store, 0),
|
|
MIST_FUNC_DEF(steam, stats_get_int, 1),
|
|
MIST_FUNC_DEF(steam, stats_get_float, 1),
|
|
MIST_FUNC_DEF(steam, stats_set_int, 2),
|
|
MIST_FUNC_DEF(steam, stats_set_float, 2),
|
|
};
|
|
|
|
static const JSCFunctionListEntry js_steam_achievement_funcs[] = {
|
|
MIST_FUNC_DEF(steam, achievement_get, 1),
|
|
MIST_FUNC_DEF(steam, achievement_set, 1),
|
|
MIST_FUNC_DEF(steam, achievement_clear, 1),
|
|
MIST_FUNC_DEF(steam, achievement_count, 0),
|
|
MIST_FUNC_DEF(steam, achievement_name, 1),
|
|
};
|
|
|
|
static const JSCFunctionListEntry js_steam_app_funcs[] = {
|
|
MIST_FUNC_DEF(steam, app_id, 0),
|
|
MIST_FUNC_DEF(steam, app_owner, 0),
|
|
MIST_FUNC_DEF(steam, app_installed, 1),
|
|
MIST_FUNC_DEF(steam, app_subscribed, 0),
|
|
MIST_FUNC_DEF(steam, app_language, 0),
|
|
MIST_FUNC_DEF(steam, app_dlc_installed, 1),
|
|
};
|
|
|
|
static const JSCFunctionListEntry js_steam_user_funcs[] = {
|
|
MIST_FUNC_DEF(steam, user_logged_on, 0),
|
|
MIST_FUNC_DEF(steam, user_steam_id, 0),
|
|
MIST_FUNC_DEF(steam, user_level, 0),
|
|
};
|
|
|
|
static const JSCFunctionListEntry js_steam_friends_funcs[] = {
|
|
MIST_FUNC_DEF(steam, friends_name, 0),
|
|
MIST_FUNC_DEF(steam, friends_state, 0),
|
|
};
|
|
|
|
static const JSCFunctionListEntry js_steam_cloud_funcs[] = {
|
|
MIST_FUNC_DEF(steam, cloud_enabled_app, 0),
|
|
MIST_FUNC_DEF(steam, cloud_enabled_account, 0),
|
|
MIST_FUNC_DEF(steam, cloud_enable, 1),
|
|
MIST_FUNC_DEF(steam, cloud_quota, 0),
|
|
MIST_FUNC_DEF(steam, cloud_write, 2),
|
|
MIST_FUNC_DEF(steam, cloud_read, 1),
|
|
MIST_FUNC_DEF(steam, cloud_delete, 1),
|
|
MIST_FUNC_DEF(steam, cloud_exists, 1),
|
|
};
|
|
|
|
// Main Steam API functions
|
|
static const JSCFunctionListEntry js_steam_funcs[] = {
|
|
MIST_FUNC_DEF(steam, init, 0),
|
|
MIST_FUNC_DEF(steam, shutdown, 0),
|
|
MIST_FUNC_DEF(steam, run_callbacks, 0),
|
|
};
|
|
|
|
extern "C" JSValue js_steam_use(JSContext *js) {
|
|
JSValue steam = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js, steam, js_steam_funcs, countof(js_steam_funcs));
|
|
|
|
// Stats sub-object
|
|
JSValue stats = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js, stats, js_steam_stats_funcs, countof(js_steam_stats_funcs));
|
|
JS_SetPropertyStr(js, steam, "stats", stats);
|
|
|
|
// Achievement sub-object
|
|
JSValue achievement = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js, achievement, js_steam_achievement_funcs, countof(js_steam_achievement_funcs));
|
|
JS_SetPropertyStr(js, steam, "achievement", achievement);
|
|
|
|
// App sub-object
|
|
JSValue app = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js, app, js_steam_app_funcs, countof(js_steam_app_funcs));
|
|
JS_SetPropertyStr(js, steam, "app", app);
|
|
|
|
// User sub-object
|
|
JSValue user = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js, user, js_steam_user_funcs, countof(js_steam_user_funcs));
|
|
JS_SetPropertyStr(js, steam, "user", user);
|
|
|
|
// Friends sub-object
|
|
JSValue friends = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js, friends, js_steam_friends_funcs, countof(js_steam_friends_funcs));
|
|
JS_SetPropertyStr(js, steam, "friends", friends);
|
|
|
|
// Cloud sub-object
|
|
JSValue cloud = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js, cloud, js_steam_cloud_funcs, countof(js_steam_cloud_funcs));
|
|
JS_SetPropertyStr(js, steam, "cloud", cloud);
|
|
|
|
return steam;
|
|
}
|
|
|
|
#else
|
|
// Stub when Steam is disabled
|
|
extern "C" JSValue js_steam_use(JSContext *js) {
|
|
return JS_UNDEFINED;
|
|
}
|
|
#endif
|