From 44c46f2bb4fe117a2a0b3fc9a0e287af3818aca5 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Sat, 22 Nov 2025 22:25:59 -0600 Subject: [PATCH] use curl --- meson.build | 72 +- source/jsffi.c | 7 - source/picohttpparser.c | 685 -------- source/picohttpparser.h | 90 -- source/qjs_http.c | 285 +--- source/qjs_steam.cpp | 3265 -------------------------------------- source/qjs_steam.h | 16 - subprojects/mbedtls.wrap | 4 - tests/http.ce | 12 +- 9 files changed, 29 insertions(+), 4407 deletions(-) delete mode 100644 source/picohttpparser.c delete mode 100644 source/picohttpparser.h delete mode 100644 source/qjs_steam.cpp delete mode 100644 source/qjs_steam.h delete mode 100644 subprojects/mbedtls.wrap diff --git a/meson.build b/meson.build index c94fad48..81d3bd45 100644 --- a/meson.build +++ b/meson.build @@ -67,32 +67,6 @@ endif cmake = import('cmake') -# Try to find system-installed mbedtls first -mbedtls_dep = dependency('mbedtls', static: true, required: false) -mbedx509_dep = dependency('mbedx509', static: true, required: false) -mbedcrypto_dep = dependency('mbedcrypto', static: true, required: false) - -if not mbedtls_dep.found() or not mbedx509_dep.found() or not mbedcrypto_dep.found() - message('⚙ System mbedtls not found, building subproject...') - mbedtls_opts = cmake.subproject_options() - mbedtls_opts.add_cmake_defines({ - 'ENABLE_PROGRAMS': 'OFF', # Disable Mbed TLS programs - 'ENABLE_TESTING': 'OFF', # Disable Mbed TLS tests - 'CMAKE_BUILD_TYPE': 'Release', # Optimize for release - 'MBEDTLS_FATAL_WARNINGS': 'ON', # Treat warnings as errors - 'USE_STATIC_MBEDTLS_LIBRARY': 'ON',# Build static libraries - 'USE_SHARED_MBEDTLS_LIBRARY': 'OFF'# Disable shared libraries - }) - mbedtls_proj = cmake.subproject('mbedtls', options: mbedtls_opts) - deps += [ - mbedtls_proj.dependency('mbedtls'), - mbedtls_proj.dependency('mbedx509'), - mbedtls_proj.dependency('mbedcrypto') - ] -else - deps += [mbedtls_dep, mbedx509_dep, mbedcrypto_dep] -endif - sdl3_opts = cmake.subproject_options() sdl3_opts.add_cmake_defines({ 'SDL_STATIC': 'ON', @@ -222,6 +196,15 @@ else deps += chipmunk_dep endif +# Try to find system-installed curl first +curl_dep = dependency('libcurl', static: true, required: false) +if not curl_dep.found() + message('⚙ System curl not found, building subproject...') + deps += dependency('libcurl', static:true) +else + deps += curl_dep +endif + if host_machine.system() != 'emscripten' # Try to find system-installed enet first enet_dep = dependency('enet', static: true, required: false) @@ -288,48 +271,13 @@ else message('⚙ QR code support disabled') endif -# Always build for Steam unless it's Emscripten -if host_machine.system() == 'emscripten' - storefront = 'none' -else - storefront = 'steam' -endif -if storefront == 'steam' - steam_sdk_path = meson.current_source_dir() / 'sdk' - - if host_machine.system() == 'darwin' - steam_lib_path = steam_sdk_path / 'redistributable_bin' / 'osx' / 'libsteam_api.dylib' - elif host_machine.system() == 'linux' - steam_lib_path = steam_sdk_path / 'redistributable_bin' / 'linux64' / 'libsteam_api.so' - elif host_machine.system() == 'windows' - steam_lib_path = steam_sdk_path / 'redistributable_bin' / 'win64' / 'steam_api64.lib' - else - steam_lib_path = '' - endif - - if fs.exists(steam_lib_path) - steam_dep = declare_dependency( - include_directories: include_directories('sdk/public'), - link_args: [steam_lib_path] - ) - deps += steam_dep - src += 'qjs_steam.cpp' - message('Steam SDK enabled') - else - error('Steam SDK required but not found at: ' + steam_lib_path) - endif -else - add_project_arguments('-DNSTEAM', language: ['c', 'cpp']) - message('Storefront: ' + storefront) -endif - link_args = link sources = [] src += [ 'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c', 'render.c','simplex.c','spline.c', 'transform.c','cell.c', 'wildmatch.c', 'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_input.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_sdl_gpu.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_fd.c', 'qjs_os.c', 'qjs_actor.c', - 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c', 'qjs_staef.c', 'qjs_layout.c', 'cell_qoi.c' + 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'qjs_miniz.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c', 'qjs_staef.c', 'qjs_layout.c', 'cell_qoi.c' ] # quirc src src += [ diff --git a/source/jsffi.c b/source/jsffi.c index ee763240..ce495cff 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -55,9 +55,6 @@ #include "qjs_staef.h" #include "qjs_io.h" #include "qjs_fd.h" -#ifndef NSTEAM -#include "qjs_steam.h" -#endif // External transform function declarations extern JSClassID js_transform_id; @@ -1177,10 +1174,6 @@ void ffi_load(JSContext *js) arrput(rt->module_registry, MISTLINE(imgui)); -#ifndef NSTEAM - arrput(rt->module_registry, MISTLINE(steam)); -#endif - JSValue globalThis = JS_GetGlobalObject(js); JSValue prosp = JS_NewObject(js); diff --git a/source/picohttpparser.c b/source/picohttpparser.c deleted file mode 100644 index 680039b4..00000000 --- a/source/picohttpparser.c +++ /dev/null @@ -1,685 +0,0 @@ -/* - * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase, - * Shigeo Mitsunari - * - * The software is licensed under either the MIT License (below) or the Perl - * license. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include -#include -#include -#ifdef __SSE4_2__ -#ifdef _MSC_VER -#include -#else -#include -#endif -#endif -#include "picohttpparser.h" - -#if __GNUC__ >= 3 -#define likely(x) __builtin_expect(!!(x), 1) -#define unlikely(x) __builtin_expect(!!(x), 0) -#else -#define likely(x) (x) -#define unlikely(x) (x) -#endif - -#ifdef _MSC_VER -#define ALIGNED(n) _declspec(align(n)) -#else -#define ALIGNED(n) __attribute__((aligned(n))) -#endif - -#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u) - -#define CHECK_EOF() \ - if (buf == buf_end) { \ - *ret = -2; \ - return NULL; \ - } - -#define EXPECT_CHAR_NO_CHECK(ch) \ - if (*buf++ != ch) { \ - *ret = -1; \ - return NULL; \ - } - -#define EXPECT_CHAR(ch) \ - CHECK_EOF(); \ - EXPECT_CHAR_NO_CHECK(ch); - -#define ADVANCE_TOKEN(tok, toklen) \ - do { \ - const char *tok_start = buf; \ - static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \ - int found2; \ - buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \ - if (!found2) { \ - CHECK_EOF(); \ - } \ - while (1) { \ - if (*buf == ' ') { \ - break; \ - } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \ - if ((unsigned char)*buf < '\040' || *buf == '\177') { \ - *ret = -1; \ - return NULL; \ - } \ - } \ - ++buf; \ - CHECK_EOF(); \ - } \ - tok = tok_start; \ - toklen = buf - tok_start; \ - } while (0) - -static const char *token_char_map = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0" - "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1" - "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - -static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found) -{ - *found = 0; -#if __SSE4_2__ - if (likely(buf_end - buf >= 16)) { - __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges); - - size_t left = (buf_end - buf) & ~15; - do { - __m128i b16 = _mm_loadu_si128((const __m128i *)buf); - int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS); - if (unlikely(r != 16)) { - buf += r; - *found = 1; - break; - } - buf += 16; - left -= 16; - } while (likely(left != 0)); - } -#else - /* suppress unused parameter warning */ - (void)buf_end; - (void)ranges; - (void)ranges_size; -#endif - return buf; -} - -static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret) -{ - const char *token_start = buf; - -#ifdef __SSE4_2__ - static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */ - "\012\037" /* allow SP and up to but not including DEL */ - "\177\177"; /* allow chars w. MSB set */ - int found; - buf = findchar_fast(buf, buf_end, ranges1, 6, &found); - if (found) - goto FOUND_CTL; -#else - /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */ - while (likely(buf_end - buf >= 8)) { -#define DOIT() \ - do { \ - if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \ - goto NonPrintable; \ - ++buf; \ - } while (0) - DOIT(); - DOIT(); - DOIT(); - DOIT(); - DOIT(); - DOIT(); - DOIT(); - DOIT(); -#undef DOIT - continue; - NonPrintable: - if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { - goto FOUND_CTL; - } - ++buf; - } -#endif - for (;; ++buf) { - CHECK_EOF(); - if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { - if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { - goto FOUND_CTL; - } - } - } -FOUND_CTL: - if (likely(*buf == '\015')) { - ++buf; - EXPECT_CHAR('\012'); - *token_len = buf - 2 - token_start; - } else if (*buf == '\012') { - *token_len = buf - token_start; - ++buf; - } else { - *ret = -1; - return NULL; - } - *token = token_start; - - return buf; -} - -static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret) -{ - int ret_cnt = 0; - buf = last_len < 3 ? buf : buf + last_len - 3; - - while (1) { - CHECK_EOF(); - if (*buf == '\015') { - ++buf; - CHECK_EOF(); - EXPECT_CHAR('\012'); - ++ret_cnt; - } else if (*buf == '\012') { - ++buf; - ++ret_cnt; - } else { - ++buf; - ret_cnt = 0; - } - if (ret_cnt == 2) { - return buf; - } - } - - *ret = -2; - return NULL; -} - -#define PARSE_INT(valp_, mul_) \ - if (*buf < '0' || '9' < *buf) { \ - buf++; \ - *ret = -1; \ - return NULL; \ - } \ - *(valp_) = (mul_) * (*buf++ - '0'); - -#define PARSE_INT_3(valp_) \ - do { \ - int res_ = 0; \ - PARSE_INT(&res_, 100) \ - *valp_ = res_; \ - PARSE_INT(&res_, 10) \ - *valp_ += res_; \ - PARSE_INT(&res_, 1) \ - *valp_ += res_; \ - } while (0) - -/* returned pointer is always within [buf, buf_end), or null */ -static const char *parse_token(const char *buf, const char *buf_end, const char **token, size_t *token_len, char next_char, - int *ret) -{ - /* We use pcmpestri to detect non-token characters. This instruction can take no more than eight character ranges (8*2*8=128 - * bits that is the size of a SSE register). Due to this restriction, characters `|` and `~` are handled in the slow loop. */ - static const char ALIGNED(16) ranges[] = "\x00 " /* control chars and up to SP */ - "\"\"" /* 0x22 */ - "()" /* 0x28,0x29 */ - ",," /* 0x2c */ - "//" /* 0x2f */ - ":@" /* 0x3a-0x40 */ - "[]" /* 0x5b-0x5d */ - "{\xff"; /* 0x7b-0xff */ - const char *buf_start = buf; - int found; - buf = findchar_fast(buf, buf_end, ranges, sizeof(ranges) - 1, &found); - if (!found) { - CHECK_EOF(); - } - while (1) { - if (*buf == next_char) { - break; - } else if (!token_char_map[(unsigned char)*buf]) { - *ret = -1; - return NULL; - } - ++buf; - CHECK_EOF(); - } - *token = buf_start; - *token_len = buf - buf_start; - return buf; -} - -/* returned pointer is always within [buf, buf_end), or null */ -static const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret) -{ - /* we want at least [HTTP/1.] to try to parse */ - if (buf_end - buf < 9) { - *ret = -2; - return NULL; - } - EXPECT_CHAR_NO_CHECK('H'); - EXPECT_CHAR_NO_CHECK('T'); - EXPECT_CHAR_NO_CHECK('T'); - EXPECT_CHAR_NO_CHECK('P'); - EXPECT_CHAR_NO_CHECK('/'); - EXPECT_CHAR_NO_CHECK('1'); - EXPECT_CHAR_NO_CHECK('.'); - PARSE_INT(minor_version, 1); - return buf; -} - -static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers, - size_t max_headers, int *ret) -{ - for (;; ++*num_headers) { - CHECK_EOF(); - if (*buf == '\015') { - ++buf; - EXPECT_CHAR('\012'); - break; - } else if (*buf == '\012') { - ++buf; - break; - } - if (*num_headers == max_headers) { - *ret = -1; - return NULL; - } - if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) { - /* parsing name, but do not discard SP before colon, see - * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */ - if ((buf = parse_token(buf, buf_end, &headers[*num_headers].name, &headers[*num_headers].name_len, ':', ret)) == NULL) { - return NULL; - } - if (headers[*num_headers].name_len == 0) { - *ret = -1; - return NULL; - } - ++buf; - for (;; ++buf) { - CHECK_EOF(); - if (!(*buf == ' ' || *buf == '\t')) { - break; - } - } - } else { - headers[*num_headers].name = NULL; - headers[*num_headers].name_len = 0; - } - const char *value; - size_t value_len; - if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) { - return NULL; - } - /* remove trailing SPs and HTABs */ - const char *value_end = value + value_len; - for (; value_end != value; --value_end) { - const char c = *(value_end - 1); - if (!(c == ' ' || c == '\t')) { - break; - } - } - headers[*num_headers].value = value; - headers[*num_headers].value_len = value_end - value; - } - return buf; -} - -static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path, - size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, - size_t max_headers, int *ret) -{ - /* skip first empty line (some clients add CRLF after POST content) */ - CHECK_EOF(); - if (*buf == '\015') { - ++buf; - EXPECT_CHAR('\012'); - } else if (*buf == '\012') { - ++buf; - } - - /* parse request line */ - if ((buf = parse_token(buf, buf_end, method, method_len, ' ', ret)) == NULL) { - return NULL; - } - do { - ++buf; - CHECK_EOF(); - } while (*buf == ' '); - ADVANCE_TOKEN(*path, *path_len); - do { - ++buf; - CHECK_EOF(); - } while (*buf == ' '); - if (*method_len == 0 || *path_len == 0) { - *ret = -1; - return NULL; - } - if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) { - return NULL; - } - if (*buf == '\015') { - ++buf; - EXPECT_CHAR('\012'); - } else if (*buf == '\012') { - ++buf; - } else { - *ret = -1; - return NULL; - } - - return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); -} - -int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path, - size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len) -{ - const char *buf = buf_start, *buf_end = buf_start + len; - size_t max_headers = *num_headers; - int r; - - *method = NULL; - *method_len = 0; - *path = NULL; - *path_len = 0; - *minor_version = -1; - *num_headers = 0; - - /* if last_len != 0, check if the request is complete (a fast countermeasure - againt slowloris */ - if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { - return r; - } - - if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, minor_version, headers, num_headers, max_headers, - &r)) == NULL) { - return r; - } - - return (int)(buf - buf_start); -} - -static const char *parse_response(const char *buf, const char *buf_end, int *minor_version, int *status, const char **msg, - size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret) -{ - /* parse "HTTP/1.x" */ - if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) { - return NULL; - } - /* skip space */ - if (*buf != ' ') { - *ret = -1; - return NULL; - } - do { - ++buf; - CHECK_EOF(); - } while (*buf == ' '); - /* parse status code, we want at least [:digit:][:digit:][:digit:] to try to parse */ - if (buf_end - buf < 4) { - *ret = -2; - return NULL; - } - PARSE_INT_3(status); - - /* get message including preceding space */ - if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) { - return NULL; - } - if (*msg_len == 0) { - /* ok */ - } else if (**msg == ' ') { - /* Remove preceding space. Successful return from `get_token_to_eol` guarantees that we would hit something other than SP - * before running past the end of the given buffer. */ - do { - ++*msg; - --*msg_len; - } while (**msg == ' '); - } else { - /* garbage found after status code */ - *ret = -1; - return NULL; - } - - return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); -} - -int phr_parse_response(const char *buf_start, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len, - struct phr_header *headers, size_t *num_headers, size_t last_len) -{ - const char *buf = buf_start, *buf_end = buf + len; - size_t max_headers = *num_headers; - int r; - - *minor_version = -1; - *status = 0; - *msg = NULL; - *msg_len = 0; - *num_headers = 0; - - /* if last_len != 0, check if the response is complete (a fast countermeasure - against slowloris */ - if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { - return r; - } - - if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) { - return r; - } - - return (int)(buf - buf_start); -} - -int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len) -{ - const char *buf = buf_start, *buf_end = buf + len; - size_t max_headers = *num_headers; - int r; - - *num_headers = 0; - - /* if last_len != 0, check if the response is complete (a fast countermeasure - against slowloris */ - if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { - return r; - } - - if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) { - return r; - } - - return (int)(buf - buf_start); -} - -enum { - CHUNKED_IN_CHUNK_SIZE, - CHUNKED_IN_CHUNK_EXT, - CHUNKED_IN_CHUNK_DATA, - CHUNKED_IN_CHUNK_CRLF, - CHUNKED_IN_TRAILERS_LINE_HEAD, - CHUNKED_IN_TRAILERS_LINE_MIDDLE -}; - -static int decode_hex(int ch) -{ - if ('0' <= ch && ch <= '9') { - return ch - '0'; - } else if ('A' <= ch && ch <= 'F') { - return ch - 'A' + 0xa; - } else if ('a' <= ch && ch <= 'f') { - return ch - 'a' + 0xa; - } else { - return -1; - } -} - -ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz) -{ - size_t dst = 0, src = 0, bufsz = *_bufsz; - ssize_t ret = -2; /* incomplete */ - - decoder->_total_read += bufsz; - - while (1) { - switch (decoder->_state) { - case CHUNKED_IN_CHUNK_SIZE: - for (;; ++src) { - int v; - if (src == bufsz) - goto Exit; - if ((v = decode_hex(buf[src])) == -1) { - if (decoder->_hex_count == 0) { - ret = -1; - goto Exit; - } - /* the only characters that may appear after the chunk size are BWS, semicolon, or CRLF */ - switch (buf[src]) { - case ' ': - case '\011': - case ';': - case '\012': - case '\015': - break; - default: - ret = -1; - goto Exit; - } - break; - } - if (decoder->_hex_count == sizeof(size_t) * 2) { - ret = -1; - goto Exit; - } - decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v; - ++decoder->_hex_count; - } - decoder->_hex_count = 0; - decoder->_state = CHUNKED_IN_CHUNK_EXT; - /* fallthru */ - case CHUNKED_IN_CHUNK_EXT: - /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */ - for (;; ++src) { - if (src == bufsz) - goto Exit; - if (buf[src] == '\012') - break; - } - ++src; - if (decoder->bytes_left_in_chunk == 0) { - if (decoder->consume_trailer) { - decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; - break; - } else { - goto Complete; - } - } - decoder->_state = CHUNKED_IN_CHUNK_DATA; - /* fallthru */ - case CHUNKED_IN_CHUNK_DATA: { - size_t avail = bufsz - src; - if (avail < decoder->bytes_left_in_chunk) { - if (dst != src) - memmove(buf + dst, buf + src, avail); - src += avail; - dst += avail; - decoder->bytes_left_in_chunk -= avail; - goto Exit; - } - if (dst != src) - memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk); - src += decoder->bytes_left_in_chunk; - dst += decoder->bytes_left_in_chunk; - decoder->bytes_left_in_chunk = 0; - decoder->_state = CHUNKED_IN_CHUNK_CRLF; - } - /* fallthru */ - case CHUNKED_IN_CHUNK_CRLF: - for (;; ++src) { - if (src == bufsz) - goto Exit; - if (buf[src] != '\015') - break; - } - if (buf[src] != '\012') { - ret = -1; - goto Exit; - } - ++src; - decoder->_state = CHUNKED_IN_CHUNK_SIZE; - break; - case CHUNKED_IN_TRAILERS_LINE_HEAD: - for (;; ++src) { - if (src == bufsz) - goto Exit; - if (buf[src] != '\015') - break; - } - if (buf[src++] == '\012') - goto Complete; - decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE; - /* fallthru */ - case CHUNKED_IN_TRAILERS_LINE_MIDDLE: - for (;; ++src) { - if (src == bufsz) - goto Exit; - if (buf[src] == '\012') - break; - } - ++src; - decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; - break; - default: - assert(!"decoder is corrupt"); - } - } - -Complete: - ret = bufsz - src; -Exit: - if (dst != src) - memmove(buf + dst, buf + src, bufsz - src); - *_bufsz = dst; - /* if incomplete but the overhead of the chunked encoding is >=100KB and >80%, signal an error */ - if (ret == -2) { - decoder->_total_overhead += bufsz - dst; - if (decoder->_total_overhead >= 100 * 1024 && decoder->_total_read - decoder->_total_overhead < decoder->_total_read / 4) - ret = -1; - } - return ret; -} - -int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) -{ - return decoder->_state == CHUNKED_IN_CHUNK_DATA; -} - -#undef CHECK_EOF -#undef EXPECT_CHAR -#undef ADVANCE_TOKEN diff --git a/source/picohttpparser.h b/source/picohttpparser.h deleted file mode 100644 index 13bc855b..00000000 --- a/source/picohttpparser.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase, - * Shigeo Mitsunari - * - * The software is licensed under either the MIT License (below) or the Perl - * license. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef picohttpparser_h -#define picohttpparser_h - -#include -#include - -#ifdef _MSC_VER -#define ssize_t intptr_t -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* contains name and value of a header (name == NULL if is a continuing line - * of a multiline header */ -struct phr_header { - const char *name; - size_t name_len; - const char *value; - size_t value_len; -}; - -/* returns number of bytes consumed if successful, -2 if request is partial, - * -1 if failed */ -int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, - int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len); - -/* ditto */ -int phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len, - struct phr_header *headers, size_t *num_headers, size_t last_len); - -/* ditto */ -int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len); - -/* should be zero-filled before start */ -struct phr_chunked_decoder { - size_t bytes_left_in_chunk; /* number of bytes left in current chunk */ - char consume_trailer; /* if trailing headers should be consumed */ - char _hex_count; - char _state; - uint64_t _total_read; - uint64_t _total_overhead; -}; - -/* the function rewrites the buffer given as (buf, bufsz) removing the chunked- - * encoding headers. When the function returns without an error, bufsz is - * updated to the length of the decoded data available. Applications should - * repeatedly call the function while it returns -2 (incomplete) every time - * supplying newly arrived data. If the end of the chunked-encoded data is - * found, the function returns a non-negative number indicating the number of - * octets left undecoded, that starts from the offset returned by `*bufsz`. - * Returns -1 on error. - */ -ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz); - -/* returns if the chunked decoder is in middle of chunked data */ -int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/source/qjs_http.c b/source/qjs_http.c index 6608e394..6d5d890b 100644 --- a/source/qjs_http.c +++ b/source/qjs_http.c @@ -1,134 +1,10 @@ #include "qjs_blob.h" -#include "picohttpparser.h" -#include -#include -#include -#include -#include +#define PAR_EASYCURL_IMPLEMENTATION +#include "thirdparty/par/par_easycurl.h" #include #include #include -#if defined(__MINGW32__) || defined(__MINGW64__) -static void *memmem(const void *hay, size_t haylen, - const void *ndl, size_t ndllen) { - const unsigned char *h = hay, *n = ndl; - if (!ndllen) return (void*)h; - haylen -= ndllen - 1; - for (size_t i = 0; i < haylen; i++) - if (memcmp(h + i, n, ndllen) == 0) - return (void*)(h + i); - return NULL; -} - -static char *strndup(const char *s, size_t n) { - size_t len = strnlen(s, n); - char *d = malloc(len + 1); - if (!d) return NULL; - memcpy(d, s, len); - d[len] = '\0'; - return d; -} -#endif - -// Simple dynamic buffer for reading the response -typedef struct { - char *data; - size_t size; - size_t capacity; -} buffer_t; - -static void buffer_init(buffer_t *b) { - b->data = NULL; - b->size = 0; - b->capacity = 0; -} -static int buffer_append(buffer_t *b, const void *p, size_t len) { - if (b->size + len > b->capacity) { - size_t new_cap = b->capacity ? b->capacity * 2 : 4096; - while (new_cap < b->size + len) new_cap *= 2; - char *nx = realloc(b->data, new_cap); - if (!nx) return -1; - b->data = nx; - b->capacity = new_cap; - } - memcpy(b->data + b->size, p, len); - b->size += len; - return 0; -} -static void buffer_free(buffer_t *b) { - free(b->data); - b->data = NULL; - b->size = 0; - b->capacity = 0; -} - -// Parses "http://host:port/path" or "https://host:port/path" -static int parse_url(const char *url, int *use_ssl, - char **host, char **port, char **path) { - *host = *port = *path = NULL; - *use_ssl = 0; - const char *p = url; - if (strncmp(p, "https://", 8) == 0) { - *use_ssl = 1; - p += 8; - } else if (strncmp(p, "http://", 7) == 0) { - *use_ssl = 0; - p += 7; - } else { - return -1; - } - // find path - const char *path_start = strchr(p, '/'); - size_t hostport_len = path_start ? (size_t)(path_start - p) : strlen(p); - *path = path_start ? strdup(path_start) : strdup("/"); - if (!*path) return -1; - - // check for explicit port - const char *colon = memmem(p, hostport_len, ":", 1); - if (colon) { - size_t hlen = (size_t)(colon - p); - *host = strndup(p, hlen); - size_t plen = hostport_len - hlen - 1; - *port = strndup(colon + 1, plen); - } else { - *host = strndup(p, hostport_len); - *port = strdup(*use_ssl ? "443" : "80"); - } - if (!*host || !*port) { - free(*host); free(*port); free(*path); - return -1; - } - return 0; -} - -// Builds a GET request into buf -static int build_get_request(buffer_t *buf, - const char *host, const char *port, const char *path) { - // If port is “80” for HTTP or “443” for HTTPS, omit the “:port” in Host: - char host_header[512]; - if ((strcmp(port, "80") == 0) || (strcmp(port, "443") == 0)) { - // default port: just “Host: hostname” - snprintf(host_header, sizeof(host_header), "Host: %s\r\n", host); - } else { - // non-default port: “Host: hostname:port” - snprintf(host_header, sizeof(host_header), "Host: %s:%s\r\n", host, port); - } - - // Now build the rest of the request, including a User-Agent: - char tmp[1024]; - int n = snprintf(tmp, sizeof(tmp), - "GET %s HTTP/1.1\r\n" - "%s" // the host_header line - "User-Agent: ProsperonFetch/1.0\r\n" - "Accept: */*\r\n" - "Connection: close\r\n" - "\r\n", - path, host_header); - if (n < 0) return -1; - return buffer_append(buf, tmp, (size_t)n); -} - // Performs a blocking HTTP GET and returns a QuickJS Blob of the body static JSValue js_fetch_picoparser(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -138,158 +14,18 @@ static JSValue js_fetch_picoparser(JSContext *ctx, JSValueConst this_val, const char *url = JS_ToCString(ctx, argv[0]); if (!url) return JS_ThrowTypeError(ctx, "fetch: invalid URL"); - int use_ssl = 0; - char *host = NULL, *port = NULL, *path = NULL; - if (parse_url(url, &use_ssl, &host, &port, &path) < 0) { + par_byte *data = NULL; + int nbytes = 0; + if (!par_easycurl_to_memory(url, &data, &nbytes)) { JS_FreeCString(ctx, url); - return JS_ThrowTypeError(ctx, "fetch: URL parse error"); + return JS_ThrowTypeError(ctx, "fetch: failed to fetch URL"); } - // Prepare mbedTLS structures - mbedtls_net_context net_ctx; - mbedtls_ssl_context ssl; - mbedtls_ssl_config conf; - mbedtls_ctr_drbg_context drbg; - mbedtls_entropy_context entropy; - mbedtls_net_init(&net_ctx); - mbedtls_ssl_init(&ssl); - mbedtls_ssl_config_init(&conf); - mbedtls_ctr_drbg_init(&drbg); - mbedtls_entropy_init(&entropy); - - int ret; - const char *pers = "js_fetch"; - if (use_ssl) { - if ((ret = mbedtls_ctr_drbg_seed(&drbg, mbedtls_entropy_func, &entropy, - (const unsigned char *)pers, - strlen(pers))) != 0) - goto cleanup_tls; - if ((ret = mbedtls_ssl_config_defaults(&conf, - MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT)) != 0) - goto cleanup_tls; - mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL); - mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &drbg); - if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0) - goto cleanup_tls; - if ((ret = mbedtls_ssl_set_hostname(&ssl, host)) != 0) - goto cleanup_tls; - } - - // Connect to host:port - if ((ret = mbedtls_net_connect(&net_ctx, host, port, - MBEDTLS_NET_PROTO_TCP)) != 0) - goto cleanup_tls; - - if (use_ssl) { - mbedtls_ssl_set_bio(&ssl, &net_ctx, - mbedtls_net_send, mbedtls_net_recv, NULL); - while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) { - if (ret != MBEDTLS_ERR_SSL_WANT_READ && - ret != MBEDTLS_ERR_SSL_WANT_WRITE) - goto cleanup_all; - } - } - - // Build and send GET request - buffer_t req = {0}, resp = {0}; - buffer_init(&req); - buffer_init(&resp); - if (build_get_request(&req, host, port, path) < 0) - goto cleanup_all; - - // Write request - size_t off = 0; - while (off < req.size) { - if (use_ssl) { - ret = mbedtls_ssl_write(&ssl, - (const unsigned char *)req.data + off, - req.size - off); - if (ret == MBEDTLS_ERR_SSL_WANT_READ || - ret == MBEDTLS_ERR_SSL_WANT_WRITE) - continue; - } else { - ret = mbedtls_net_send(&net_ctx, - (unsigned char *)req.data + off, - req.size - off); - } - if (ret <= 0) goto cleanup_all; - off += (size_t)ret; - } - buffer_free(&req); - - // Read entire response into resp.data - unsigned char buf[4096]; - do { - if (use_ssl) { - ret = mbedtls_ssl_read(&ssl, buf, sizeof(buf)); - if (ret == MBEDTLS_ERR_SSL_WANT_READ || - ret == MBEDTLS_ERR_SSL_WANT_WRITE) - continue; - } else { - ret = mbedtls_net_recv(&net_ctx, buf, sizeof(buf)); - } - if (ret > 0) { - if (buffer_append(&resp, buf, (size_t)ret) < 0) - goto cleanup_all; - } - } while (ret > 0); - - // Parse status-line + headers with picohttpparser - struct phr_header hdrv[32]; - size_t hdr_count = sizeof(hdrv)/sizeof(hdrv[0]); - int minor, status; - const char *reason = NULL; - size_t reason_len = 0; - int pret = phr_parse_response( - resp.data, resp.size, - &minor, &status, - &reason, &reason_len, /* provide pointers, even if unused */ - hdrv, &hdr_count, - 0); - if (pret < 0) goto cleanup_all; - size_t header_len = (size_t)pret; - char *body_ptr = resp.data + header_len; - size_t body_len = resp.size - header_len; - - // Check for chunked encoding - int is_chunked = 0; - for (size_t i = 0; i < hdr_count; i++) { - if ((hdrv[i].name_len == 17 && - !strncasecmp(hdrv[i].name, "Transfer-Encoding", 17) && - memmem(hdrv[i].value, hdrv[i].value_len, "chunked", 7))) { - is_chunked = 1; - break; - } - } - - // If chunked, decode in-place - if (is_chunked) { - struct phr_chunked_decoder cd = {0}; - size_t blen = body_len; - ssize_t rest = phr_decode_chunked(&cd, body_ptr, &blen); - if (rest < 0) goto cleanup_all; - // blen = decoded length; rest = trailer start offset (ignored) - body_len = blen; - } - - // Return a Blob wrapping the body buffer (no extra copy) - JSValue blob = js_new_blob_stoned_copy(ctx, - (uint8_t *)body_ptr, body_len); - -cleanup_all: - buffer_free(&resp); - mbedtls_ssl_close_notify(&ssl); - -cleanup_tls: - mbedtls_net_free(&net_ctx); - mbedtls_ssl_free(&ssl); - mbedtls_ssl_config_free(&conf); - mbedtls_ctr_drbg_free(&drbg); - mbedtls_entropy_free(&entropy); - JS_FreeCString(ctx, url); - free(host); free(port); free(path); + + // Return a Blob wrapping the data + JSValue blob = js_new_blob_stoned_copy(ctx, data, (size_t)nbytes); + free(data); // par_easycurl allocates with malloc, so we free it return blob; } @@ -298,6 +34,7 @@ static const JSCFunctionListEntry js_http_funcs[] = { }; JSValue js_http_use(JSContext *js) { + par_easycurl_init(0); // Initialize curl JSValue obj = JS_NewObject(js); JS_SetPropertyFunctionList(js, obj, js_http_funcs, sizeof(js_http_funcs)/sizeof(js_http_funcs[0])); diff --git a/source/qjs_steam.cpp b/source/qjs_steam.cpp deleted file mode 100644 index 5d358a37..00000000 --- a/source/qjs_steam.cpp +++ /dev/null @@ -1,3265 +0,0 @@ -#ifndef NSTEAM - -// Include C headers first to get types -#include "qjs_steam.h" -#include "jsffi.h" -#include "qjs_blob.h" - -// C++ headers -#include -#include -#include - -// Steam ID wrapper class -static JSClassID js_steamid_id; - -typedef struct { - uint64_t id; -} steamid_t; - -static void js_steamid_finalizer(JSRuntime *rt, JSValue val) { - steamid_t *sid = (steamid_t*)JS_GetOpaque(val, js_steamid_id); - if (sid) js_free_rt(rt, sid); -} - -static JSClassDef js_steamid_class = { - .class_name = "SteamID", - .finalizer = js_steamid_finalizer, -}; - -static steamid_t *js2steamid(JSContext *js, JSValue val) { - if (JS_GetClassID(val) != js_steamid_id) return NULL; - return (steamid_t*)JS_GetOpaque(val, js_steamid_id); -} - -static JSValue steamid2js(JSContext *js, uint64_t id) { - steamid_t *sid = (steamid_t*)js_mallocz(js, sizeof(steamid_t)); - if (!sid) return JS_ThrowOutOfMemory(js); - - sid->id = id; - - JSValue obj = JS_NewObjectClass(js, js_steamid_id); - JS_SetOpaque(obj, sid); - return obj; -} - -static JSValue js_steamid_toString(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { - steamid_t *sid = js2steamid(js, self); - if (!sid) return JS_ThrowTypeError(js, "Expected SteamID"); - - char buf[32]; - snprintf(buf, sizeof(buf), "%llu", (unsigned long long)sid->id); - return JS_NewString(js, buf); -} - -static JSValue js_steamid_toNumber(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { - steamid_t *sid = js2steamid(js, self); - if (!sid) return JS_ThrowTypeError(js, "Expected SteamID"); - - // Return as a safe JS number (may lose precision for very large IDs) - return JS_NewFloat64(js, (double)sid->id); -} - -static const JSCFunctionListEntry js_steamid_proto_funcs[] = { - JS_CFUNC_DEF("toString", 0, js_steamid_toString), - JS_CFUNC_DEF("toNumber", 0, js_steamid_toNumber), -}; - -// 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 ISteamMatchmaking *steam_matchmaking = NULL; -static ISteamNetworking *steam_networking = NULL; -static ISteamScreenshots *steam_screenshots = NULL; -static ISteamHTTP *steam_http = NULL; -static ISteamInput *steam_input = NULL; -static ISteamInventory *steam_inventory = NULL; -static ISteamMusic *steam_music = NULL; -static bool steam_initialized = false; - -extern "C" { - -// STEAM INITIALIZATION -JSC_CCALL(steam_init, - if (steam_initialized) { - return JS_NewBool(js, true); - } - - ESteamAPIInitResult init_result = SteamAPI_InitFlat(nullptr); - - switch (init_result) { - case k_ESteamAPIInitResult_OK: - steam_initialized = true; - break; - - case k_ESteamAPIInitResult_NoSteamClient: - return JS_ThrowInternalError(js, "Failed to initialize Steam: Steam client is not running or not logged in"); - - case k_ESteamAPIInitResult_VersionMismatch: - return JS_ThrowInternalError(js, "Failed to initialize Steam: Steam client version is out of date"); - - case k_ESteamAPIInitResult_FailedGeneric: - default: - return JS_ThrowInternalError(js, "Failed to initialize Steam: Generic failure (check steam_appid.txt file exists and contains valid AppID)"); - } - - // Get interface pointers - must be called after SteamAPI_InitFlat succeeds - steam_user = SteamAPI_SteamUser(); - steam_utils = SteamAPI_SteamUtils(); - steam_friends = SteamAPI_SteamFriends(); - steam_apps = SteamAPI_SteamApps(); - steam_stats = SteamAPI_SteamUserStats(); - steam_remote = SteamAPI_SteamRemoteStorage(); - steam_ugc = SteamAPI_SteamUGC(); - steam_matchmaking = SteamAPI_SteamMatchmaking(); - steam_networking = SteamAPI_SteamNetworking(); - steam_screenshots = SteamAPI_SteamScreenshots(); - steam_http = SteamAPI_SteamHTTP(); - steam_input = SteamAPI_SteamInput(); - steam_inventory = SteamAPI_SteamInventory(); - steam_music = SteamAPI_SteamMusic(); - - - // Verify critical interfaces were obtained - if (!steam_user) { - steam_initialized = false; - SteamAPI_Shutdown(); - return JS_ThrowInternalError(js, "Failed to get Steam user interface"); - } - - if (!steam_utils) { - steam_initialized = false; - SteamAPI_Shutdown(); - return JS_ThrowInternalError(js, "Failed to get Steam utils interface"); - } - - if (!steam_friends) { - steam_initialized = false; - SteamAPI_Shutdown(); - return JS_ThrowInternalError(js, "Failed to get Steam friends interface"); - } - - // Verify user is logged in - if (!SteamAPI_ISteamUser_BLoggedOn(steam_user)) { - steam_initialized = false; - SteamAPI_Shutdown(); - return JS_ThrowInternalError(js, "Steam user is not logged in"); - } - - return JS_NewBool(js, steam_initialized); -) - -JSC_CCALL(steam_shutdown, - if (steam_initialized) { - SteamAPI_Shutdown(); - steam_initialized = false; - } - return JS_NULL; -) - -JSC_CCALL(steam_run_callbacks, - if (steam_initialized) { - SteamAPI_RunCallbacks(); - } - return JS_NULL; -) - -// USER STATS & ACHIEVEMENTS -JSC_CCALL(stats_request, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - bool success = SteamAPI_ISteamUserStats_RequestUserStats(steam_stats, SteamAPI_ISteamUser_GetSteamID(steam_user)); - return JS_NewBool(js, success); -) - -JSC_CCALL(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(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_NULL; - return JS_NewInt32(js, value); -) - -JSC_CCALL(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_NULL; - return JS_NewFloat64(js, value); -) - -JSC_CCALL(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(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(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_NULL; - return JS_NewBool(js, achieved); -) - -JSC_CCALL(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(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(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(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_NULL; - - return JS_NewString(js, name); -) - -JSC_CCALL(achievement_get_and_unlock_time, - 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; - uint32 unlock_time; - bool success = SteamAPI_ISteamUserStats_GetAchievementAndUnlockTime(steam_stats, name, &achieved, &unlock_time); - JS_FreeCString(js, name); - - if (!success) return JS_NULL; - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "achieved", JS_NewBool(js, achieved)); - JS_SetPropertyStr(js, obj, "unlock_time", JS_NewUint32(js, unlock_time)); - return obj; -) - -JSC_CCALL(achievement_get_icon, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - const char *name = JS_ToCString(js, argv[0]); - if (!name) return JS_EXCEPTION; - - int icon = SteamAPI_ISteamUserStats_GetAchievementIcon(steam_stats, name); - JS_FreeCString(js, name); - - return JS_NewInt32(js, icon); -) - -JSC_CCALL(achievement_get_display_attribute, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - const char *name = JS_ToCString(js, argv[0]); - if (!name) return JS_EXCEPTION; - - const char *key = JS_ToCString(js, argv[1]); - if (!key) { - JS_FreeCString(js, name); - return JS_EXCEPTION; - } - - const char *value = SteamAPI_ISteamUserStats_GetAchievementDisplayAttribute(steam_stats, name, key); - JS_FreeCString(js, name); - JS_FreeCString(js, key); - - return JS_NewString(js, value ? value : ""); -) - -JSC_CCALL(achievement_indicate_progress, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - const char *name = JS_ToCString(js, argv[0]); - if (!name) return JS_EXCEPTION; - - uint32 cur_progress, max_progress; - JS_ToUint32(js, &cur_progress, argv[1]); - JS_ToUint32(js, &max_progress, argv[2]); - - bool success = SteamAPI_ISteamUserStats_IndicateAchievementProgress(steam_stats, name, cur_progress, max_progress); - JS_FreeCString(js, name); - - return JS_NewBool(js, success); -) - -JSC_CCALL(achievement_get_user_achievement, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a SteamID"); - - const char *name = JS_ToCString(js, argv[1]); - if (!name) return JS_EXCEPTION; - - bool achieved; - bool success = SteamAPI_ISteamUserStats_GetUserAchievement(steam_stats, sid->id, name, &achieved); - JS_FreeCString(js, name); - - if (!success) return JS_NULL; - return JS_NewBool(js, achieved); -) - -JSC_CCALL(achievement_get_user_achievement_and_unlock_time, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a SteamID"); - - const char *name = JS_ToCString(js, argv[1]); - if (!name) return JS_EXCEPTION; - - bool achieved; - uint32 unlock_time; - bool success = SteamAPI_ISteamUserStats_GetUserAchievementAndUnlockTime(steam_stats, sid->id, name, &achieved, &unlock_time); - JS_FreeCString(js, name); - - if (!success) return JS_NULL; - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "achieved", JS_NewBool(js, achieved)); - JS_SetPropertyStr(js, obj, "unlock_time", JS_NewUint32(js, unlock_time)); - return obj; -) - -JSC_CCALL(achievement_get_achieved_percent, - 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 percent; - bool success = SteamAPI_ISteamUserStats_GetAchievementAchievedPercent(steam_stats, name, &percent); - JS_FreeCString(js, name); - - if (!success) return JS_NULL; - return JS_NewFloat64(js, percent); -) - -JSC_CCALL(achievement_get_progress_limits_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 min_progress, max_progress; - bool success = SteamAPI_ISteamUserStats_GetAchievementProgressLimitsInt32(steam_stats, name, &min_progress, &max_progress); - JS_FreeCString(js, name); - - if (!success) return JS_NULL; - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "min_progress", JS_NewInt32(js, min_progress)); - JS_SetPropertyStr(js, obj, "max_progress", JS_NewInt32(js, max_progress)); - return obj; -) - -JSC_CCALL(achievement_get_progress_limits_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 min_progress, max_progress; - bool success = SteamAPI_ISteamUserStats_GetAchievementProgressLimitsFloat(steam_stats, name, &min_progress, &max_progress); - JS_FreeCString(js, name); - - if (!success) return JS_NULL; - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "min_progress", JS_NewFloat64(js, min_progress)); - JS_SetPropertyStr(js, obj, "max_progress", JS_NewFloat64(js, max_progress)); - return obj; -) - -JSC_CCALL(stats_update_avg_rate, - 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 count_this_session, session_length; - JS_ToFloat64(js, &count_this_session, argv[1]); - JS_ToFloat64(js, &session_length, argv[2]); - - bool success = SteamAPI_ISteamUserStats_UpdateAvgRateStat(steam_stats, name, (float)count_this_session, session_length); - JS_FreeCString(js, name); - - return JS_NewBool(js, success); -) - -JSC_CCALL(stats_get_user_stat_int, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a SteamID"); - - const char *name = JS_ToCString(js, argv[1]); - if (!name) return JS_EXCEPTION; - - int32 value; - bool success = SteamAPI_ISteamUserStats_GetUserStatInt32(steam_stats, sid->id, name, &value); - JS_FreeCString(js, name); - - if (!success) return JS_NULL; - return JS_NewInt32(js, value); -) - -JSC_CCALL(stats_get_user_stat_float, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a SteamID"); - - const char *name = JS_ToCString(js, argv[1]); - if (!name) return JS_EXCEPTION; - - float value; - bool success = SteamAPI_ISteamUserStats_GetUserStatFloat(steam_stats, sid->id, name, &value); - JS_FreeCString(js, name); - - if (!success) return JS_NULL; - return JS_NewFloat64(js, value); -) - -JSC_CCALL(stats_reset_all, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - bool achievements_too = JS_ToBool(js, argv[0]); - - bool success = SteamAPI_ISteamUserStats_ResetAllStats(steam_stats, achievements_too); - return JS_NewBool(js, success); -) - -JSC_CCALL(leaderboard_find_or_create, - 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 sort_method, display_type; - JS_ToInt32(js, &sort_method, argv[1]); - JS_ToInt32(js, &display_type, argv[2]); - - SteamAPICall_t call = SteamAPI_ISteamUserStats_FindOrCreateLeaderboard(steam_stats, name, (ELeaderboardSortMethod)sort_method, (ELeaderboardDisplayType)display_type); - JS_FreeCString(js, name); - - return steamid2js(js, call); -) - -JSC_CCALL(leaderboard_find, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - const char *name = JS_ToCString(js, argv[0]); - if (!name) return JS_EXCEPTION; - - SteamAPICall_t call = SteamAPI_ISteamUserStats_FindLeaderboard(steam_stats, name); - JS_FreeCString(js, name); - - return steamid2js(js, call); -) - -JSC_CCALL(leaderboard_get_name, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - uint64 leaderboard; - JS_ToIndex(js, &leaderboard, argv[0]); - - const char *name = SteamAPI_ISteamUserStats_GetLeaderboardName(steam_stats, (SteamLeaderboard_t)leaderboard); - return JS_NewString(js, name ? name : ""); -) - -JSC_CCALL(leaderboard_get_entry_count, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - uint64 leaderboard; - JS_ToIndex(js, &leaderboard, argv[0]); - - int count = SteamAPI_ISteamUserStats_GetLeaderboardEntryCount(steam_stats, (SteamLeaderboard_t)leaderboard); - return JS_NewInt32(js, count); -) - -JSC_CCALL(leaderboard_get_sort_method, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - uint64 leaderboard; - JS_ToIndex(js, &leaderboard, argv[0]); - - ELeaderboardSortMethod method = SteamAPI_ISteamUserStats_GetLeaderboardSortMethod(steam_stats, (SteamLeaderboard_t)leaderboard); - return JS_NewInt32(js, method); -) - -JSC_CCALL(leaderboard_get_display_type, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - uint64 leaderboard; - JS_ToIndex(js, &leaderboard, argv[0]); - - ELeaderboardDisplayType type = SteamAPI_ISteamUserStats_GetLeaderboardDisplayType(steam_stats, (SteamLeaderboard_t)leaderboard); - return JS_NewInt32(js, type); -) - -JSC_CCALL(leaderboard_download_entries, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - uint64 leaderboard; - JS_ToIndex(js, &leaderboard, argv[0]); - - int32 request_type, range_start, range_end; - JS_ToInt32(js, &request_type, argv[1]); - JS_ToInt32(js, &range_start, argv[2]); - JS_ToInt32(js, &range_end, argv[3]); - - SteamAPICall_t call = SteamAPI_ISteamUserStats_DownloadLeaderboardEntries(steam_stats, (SteamLeaderboard_t)leaderboard, (ELeaderboardDataRequest)request_type, range_start, range_end); - return steamid2js(js, call); -) - -JSC_CCALL(leaderboard_download_entries_for_users, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - uint64 leaderboard; - JS_ToIndex(js, &leaderboard, argv[0]); - - // For simplicity, assume single user for now - proper implementation would handle array - steamid_t *sid = js2steamid(js, argv[1]); - if (!sid) return JS_ThrowTypeError(js, "Second argument must be a SteamID"); - - CSteamID user_id = CSteamID(sid->id); - SteamAPICall_t call = SteamAPI_ISteamUserStats_DownloadLeaderboardEntriesForUsers(steam_stats, (SteamLeaderboard_t)leaderboard, &user_id, 1); - - return steamid2js(js, call); -) - -JSC_CCALL(leaderboard_get_downloaded_entry, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - uint64 entries; - JS_ToIndex(js, &entries, argv[0]); - - int32 index; - JS_ToInt32(js, &index, argv[1]); - - LeaderboardEntry_t entry; - int32 details[32]; // Max details buffer - bool success = SteamAPI_ISteamUserStats_GetDownloadedLeaderboardEntry(steam_stats, (SteamLeaderboardEntries_t)entries, index, &entry, details, 32); - - if (!success) return JS_NULL; - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "steam_id", steamid2js(js, entry.m_steamIDUser.ConvertToUint64())); - JS_SetPropertyStr(js, obj, "global_rank", JS_NewInt32(js, entry.m_nGlobalRank)); - JS_SetPropertyStr(js, obj, "score", JS_NewInt32(js, entry.m_nScore)); - JS_SetPropertyStr(js, obj, "details_count", JS_NewInt32(js, entry.m_cDetails)); - return obj; -) - -JSC_CCALL(leaderboard_upload_score, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - uint64 leaderboard; - JS_ToIndex(js, &leaderboard, argv[0]); - - int32 upload_method, score; - JS_ToInt32(js, &upload_method, argv[1]); - JS_ToInt32(js, &score, argv[2]); - - SteamAPICall_t call = SteamAPI_ISteamUserStats_UploadLeaderboardScore(steam_stats, (SteamLeaderboard_t)leaderboard, (ELeaderboardUploadScoreMethod)upload_method, score, NULL, 0); - return steamid2js(js, call); -) - -JSC_CCALL(leaderboard_attach_ugc, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - uint64 leaderboard, ugc_handle; - JS_ToIndex(js, &leaderboard, argv[0]); - JS_ToIndex(js, &ugc_handle, argv[1]); - - SteamAPICall_t call = SteamAPI_ISteamUserStats_AttachLeaderboardUGC(steam_stats, (SteamLeaderboard_t)leaderboard, (UGCHandle_t)ugc_handle); - return steamid2js(js, call); -) - -JSC_CCALL(stats_get_number_of_current_players, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - SteamAPICall_t call = SteamAPI_ISteamUserStats_GetNumberOfCurrentPlayers(steam_stats); - return steamid2js(js, call); -) - -JSC_CCALL(stats_request_global_achievement_percentages, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - SteamAPICall_t call = SteamAPI_ISteamUserStats_RequestGlobalAchievementPercentages(steam_stats); - return steamid2js(js, call); -) - -JSC_CCALL(achievement_get_most_achieved_info, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - char name[256]; - float percent; - bool achieved; - - int result = SteamAPI_ISteamUserStats_GetMostAchievedAchievementInfo(steam_stats, name, 256, &percent, &achieved); - - if (result <= 0) return JS_NULL; - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "name", JS_NewString(js, name)); - JS_SetPropertyStr(js, obj, "percent", JS_NewFloat64(js, percent)); - JS_SetPropertyStr(js, obj, "achieved", JS_NewBool(js, achieved)); - return obj; -) - -JSC_CCALL(achievement_get_next_most_achieved_info, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - int32 iterator_previous; - JS_ToInt32(js, &iterator_previous, argv[0]); - - char name[256]; - float percent; - bool achieved; - - int result = SteamAPI_ISteamUserStats_GetNextMostAchievedAchievementInfo(steam_stats, iterator_previous, name, 256, &percent, &achieved); - - if (result <= 0) return JS_NULL; - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "name", JS_NewString(js, name)); - JS_SetPropertyStr(js, obj, "percent", JS_NewFloat64(js, percent)); - JS_SetPropertyStr(js, obj, "achieved", JS_NewBool(js, achieved)); - JS_SetPropertyStr(js, obj, "iterator", JS_NewInt32(js, result)); - return obj; -) - -JSC_CCALL(stats_request_global, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - int32 history_days; - JS_ToInt32(js, &history_days, argv[0]); - - SteamAPICall_t call = SteamAPI_ISteamUserStats_RequestGlobalStats(steam_stats, history_days); - return steamid2js(js, call); -) - -JSC_CCALL(stats_get_global_stat_int64, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - const char *name = JS_ToCString(js, argv[0]); - if (!name) return JS_EXCEPTION; - - int64 value; - bool success = SteamAPI_ISteamUserStats_GetGlobalStatInt64(steam_stats, name, &value); - JS_FreeCString(js, name); - - if (!success) return JS_NULL; - return steamid2js(js, value); -) - -JSC_CCALL(stats_get_global_stat_double, - 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 value; - bool success = SteamAPI_ISteamUserStats_GetGlobalStatDouble(steam_stats, name, &value); - JS_FreeCString(js, name); - - if (!success) return JS_NULL; - return JS_NewFloat64(js, value); -) - -JSC_CCALL(stats_get_global_stat_history_int64, - if (!steam_stats) return JS_ThrowInternalError(js, "Steam stats not initialized"); - - const char *name = JS_ToCString(js, argv[0]); - if (!name) return JS_EXCEPTION; - - int64 data[100]; // Buffer for history data - int32 result = SteamAPI_ISteamUserStats_GetGlobalStatHistoryInt64(steam_stats, name, data, sizeof(data)); - JS_FreeCString(js, name); - - if (result <= 0) return JS_NULL; - - JSValue arr = JS_NewArray(js); - for (int i = 0; i < result; i++) { - JS_SetPropertyUint32(js, arr, i, steamid2js(js, data[i])); - } - return arr; -) - -JSC_CCALL(stats_get_global_stat_history_double, - 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 data[100]; // Buffer for history data - int32 result = SteamAPI_ISteamUserStats_GetGlobalStatHistoryDouble(steam_stats, name, data, sizeof(data)); - JS_FreeCString(js, name); - - if (result <= 0) return JS_NULL; - - JSValue arr = JS_NewArray(js); - for (int i = 0; i < result; i++) { - JS_SetPropertyUint32(js, arr, i, JS_NewFloat64(js, data[i])); - } - return arr; -) - -// APPS -JSC_CCALL(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(app_owner, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - uint64_steamid owner = SteamAPI_ISteamApps_GetAppOwner(steam_apps); - return steamid2js(js, owner); -) - -JSC_CCALL(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(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(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(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); -) - -JSC_CCALL(app_is_low_violence, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - bool low_violence = SteamAPI_ISteamApps_BIsLowViolence(steam_apps); - return JS_NewBool(js, low_violence); -) - -JSC_CCALL(app_is_cybercafe, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - bool cybercafe = SteamAPI_ISteamApps_BIsCybercafe(steam_apps); - return JS_NewBool(js, cybercafe); -) - -JSC_CCALL(app_is_vac_banned, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - bool vac_banned = SteamAPI_ISteamApps_BIsVACBanned(steam_apps); - return JS_NewBool(js, vac_banned); -) - -JSC_CCALL(app_available_languages, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - const char *languages = SteamAPI_ISteamApps_GetAvailableGameLanguages(steam_apps); - return JS_NewString(js, languages ? languages : ""); -) - -JSC_CCALL(app_is_subscribed_app, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - AppId_t appid; - JS_ToUint32(js, &appid, argv[0]); - - bool subscribed = SteamAPI_ISteamApps_BIsSubscribedApp(steam_apps, appid); - return JS_NewBool(js, subscribed); -) - -JSC_CCALL(app_earliest_purchase_time, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - AppId_t appid; - JS_ToUint32(js, &appid, argv[0]); - - uint32 time = SteamAPI_ISteamApps_GetEarliestPurchaseUnixTime(steam_apps, appid); - return JS_NewUint32(js, time); -) - -JSC_CCALL(app_is_subscribed_from_free_weekend, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - bool free_weekend = SteamAPI_ISteamApps_BIsSubscribedFromFreeWeekend(steam_apps); - return JS_NewBool(js, free_weekend); -) - -JSC_CCALL(app_dlc_count, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - int count = SteamAPI_ISteamApps_GetDLCCount(steam_apps); - return JS_NewInt32(js, count); -) - -JSC_CCALL(app_get_dlc_data_by_index, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - int index; - JS_ToInt32(js, &index, argv[0]); - - AppId_t appid; - bool available; - char name[256]; - - bool success = SteamAPI_ISteamApps_BGetDLCDataByIndex(steam_apps, index, &appid, &available, name, sizeof(name)); - - if (!success) return JS_NULL; - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "appid", JS_NewUint32(js, appid)); - JS_SetPropertyStr(js, obj, "available", JS_NewBool(js, available)); - JS_SetPropertyStr(js, obj, "name", JS_NewString(js, name)); - return obj; -) - -JSC_CCALL(app_install_dlc, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - AppId_t appid; - JS_ToUint32(js, &appid, argv[0]); - - SteamAPI_ISteamApps_InstallDLC(steam_apps, appid); - return JS_NULL; -) - -JSC_CCALL(app_uninstall_dlc, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - AppId_t appid; - JS_ToUint32(js, &appid, argv[0]); - - SteamAPI_ISteamApps_UninstallDLC(steam_apps, appid); - return JS_NULL; -) - -JSC_CCALL(app_request_proof_of_purchase_key, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - AppId_t appid; - JS_ToUint32(js, &appid, argv[0]); - - SteamAPI_ISteamApps_RequestAppProofOfPurchaseKey(steam_apps, appid); - return JS_NULL; -) - -JSC_CCALL(app_get_current_beta_name, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - char beta_name[256]; - bool has_beta = SteamAPI_ISteamApps_GetCurrentBetaName(steam_apps, beta_name, sizeof(beta_name)); - - if (!has_beta) return JS_NULL; - return JS_NewString(js, beta_name); -) - -JSC_CCALL(app_mark_content_corrupt, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - bool missing_files_only = false; - if (argc > 0) { - missing_files_only = JS_ToBool(js, argv[0]); - } - - bool success = SteamAPI_ISteamApps_MarkContentCorrupt(steam_apps, missing_files_only); - return JS_NewBool(js, success); -) - -JSC_CCALL(app_get_installed_depots, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - AppId_t appid; - JS_ToUint32(js, &appid, argv[0]); - - const uint32 max_depots = 32; - DepotId_t depots[max_depots]; - - uint32 count = SteamAPI_ISteamApps_GetInstalledDepots(steam_apps, appid, depots, max_depots); - - JSValue arr = JS_NewArray(js); - for (uint32 i = 0; i < count; i++) { - JS_SetPropertyUint32(js, arr, i, JS_NewUint32(js, depots[i])); - } - return arr; -) - -JSC_CCALL(app_get_install_dir, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - AppId_t appid; - JS_ToUint32(js, &appid, argv[0]); - - char folder[512]; - uint32 size = SteamAPI_ISteamApps_GetAppInstallDir(steam_apps, appid, folder, sizeof(folder)); - - if (size == 0) return JS_NULL; - return JS_NewString(js, folder); -) - -JSC_CCALL(app_get_launch_query_param, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - const char *key = JS_ToCString(js, argv[0]); - if (!key) return JS_ThrowTypeError(js, "Expected string parameter"); - - const char *value = SteamAPI_ISteamApps_GetLaunchQueryParam(steam_apps, key); - JS_FreeCString(js, key); - - if (!value) return JS_NULL; - return JS_NewString(js, value); -) - -JSC_CCALL(app_get_dlc_download_progress, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - AppId_t appid; - JS_ToUint32(js, &appid, argv[0]); - - uint64 bytes_downloaded, bytes_total; - bool success = SteamAPI_ISteamApps_GetDlcDownloadProgress(steam_apps, appid, &bytes_downloaded, &bytes_total); - - if (!success) return JS_NULL; - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "downloaded", steamid2js(js, bytes_downloaded)); - JS_SetPropertyStr(js, obj, "total", steamid2js(js, bytes_total)); - return obj; -) - -JSC_CCALL(app_get_build_id, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - int build_id = SteamAPI_ISteamApps_GetAppBuildId(steam_apps); - return JS_NewInt32(js, build_id); -) - -JSC_CCALL(app_request_all_proof_of_purchase_keys, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - SteamAPI_ISteamApps_RequestAllProofOfPurchaseKeys(steam_apps); - return JS_NULL; -) - -JSC_CCALL(app_get_file_details, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - const char *filename = JS_ToCString(js, argv[0]); - if (!filename) return JS_ThrowTypeError(js, "Expected string parameter"); - - SteamAPICall_t call = SteamAPI_ISteamApps_GetFileDetails(steam_apps, filename); - JS_FreeCString(js, filename); - - return steamid2js(js, call); -) - -JSC_CCALL(app_get_launch_command_line, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - char command_line[1024]; - int size = SteamAPI_ISteamApps_GetLaunchCommandLine(steam_apps, command_line, sizeof(command_line)); - - if (size <= 0) return JS_NULL; - return JS_NewString(js, command_line); -) - -JSC_CCALL(app_is_subscribed_from_family_sharing, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - bool family_sharing = SteamAPI_ISteamApps_BIsSubscribedFromFamilySharing(steam_apps); - return JS_NewBool(js, family_sharing); -) - -JSC_CCALL(app_is_timed_trial, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - uint32 seconds_allowed, seconds_played; - bool is_trial = SteamAPI_ISteamApps_BIsTimedTrial(steam_apps, &seconds_allowed, &seconds_played); - - if (!is_trial) return JS_NewBool(js, false); - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "is_trial", JS_NewBool(js, true)); - JS_SetPropertyStr(js, obj, "seconds_allowed", JS_NewUint32(js, seconds_allowed)); - JS_SetPropertyStr(js, obj, "seconds_played", JS_NewUint32(js, seconds_played)); - return obj; -) - -JSC_CCALL(app_set_dlc_context, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - AppId_t appid; - JS_ToUint32(js, &appid, argv[0]); - - bool success = SteamAPI_ISteamApps_SetDlcContext(steam_apps, appid); - return JS_NewBool(js, success); -) - -JSC_CCALL(app_get_num_betas, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - int available, private_betas; - int total = SteamAPI_ISteamApps_GetNumBetas(steam_apps, &available, &private_betas); - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "total", JS_NewInt32(js, total)); - JS_SetPropertyStr(js, obj, "available", JS_NewInt32(js, available)); - JS_SetPropertyStr(js, obj, "private", JS_NewInt32(js, private_betas)); - return obj; -) - -JSC_CCALL(app_get_beta_info, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - int beta_index; - JS_ToInt32(js, &beta_index, argv[0]); - - uint32 flags, build_id; - char beta_name[128], description[512]; - - bool success = SteamAPI_ISteamApps_GetBetaInfo(steam_apps, beta_index, &flags, &build_id, - beta_name, sizeof(beta_name), - description, sizeof(description)); - - if (!success) return JS_NULL; - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "flags", JS_NewUint32(js, flags)); - JS_SetPropertyStr(js, obj, "build_id", JS_NewUint32(js, build_id)); - JS_SetPropertyStr(js, obj, "name", JS_NewString(js, beta_name)); - JS_SetPropertyStr(js, obj, "description", JS_NewString(js, description)); - return obj; -) - -JSC_CCALL(app_set_active_beta, - if (!steam_apps) return JS_ThrowInternalError(js, "Steam apps not initialized"); - - const char *beta_name = JS_ToCString(js, argv[0]); - if (!beta_name) return JS_ThrowTypeError(js, "Expected string parameter"); - - bool success = SteamAPI_ISteamApps_SetActiveBeta(steam_apps, beta_name); - JS_FreeCString(js, beta_name); - - return JS_NewBool(js, success); -) - -// USER -JSC_CCALL(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(user_steam_id, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - uint64_steamid id = SteamAPI_ISteamUser_GetSteamID(steam_user); - return steamid2js(js, id); -) - -JSC_CCALL(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); -) - -JSC_CCALL(user_hsteam_user, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - HSteamUser user = SteamAPI_ISteamUser_GetHSteamUser(steam_user); - return JS_NewUint32(js, user); -) - -JSC_CCALL(user_get_user_data_folder, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - char buffer[1024]; - bool success = SteamAPI_ISteamUser_GetUserDataFolder(steam_user, buffer, sizeof(buffer)); - - if (!success) return JS_NULL; - return JS_NewString(js, buffer); -) - -JSC_CCALL(user_start_voice_recording, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - SteamAPI_ISteamUser_StartVoiceRecording(steam_user); - return JS_NULL; -) - -JSC_CCALL(user_stop_voice_recording, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - SteamAPI_ISteamUser_StopVoiceRecording(steam_user); - return JS_NULL; -) - -JSC_CCALL(user_get_available_voice, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - uint32 compressed, uncompressed_deprecated; - uint32 sample_rate = 11025; - if (argc > 0) JS_ToUint32(js, &sample_rate, argv[0]); - - EVoiceResult result = SteamAPI_ISteamUser_GetAvailableVoice(steam_user, &compressed, &uncompressed_deprecated, sample_rate); - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "result", JS_NewInt32(js, result)); - JS_SetPropertyStr(js, obj, "compressed_bytes", JS_NewUint32(js, compressed)); - return obj; -) - -JSC_CCALL(user_get_voice, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - uint32 buffer_size = 8192; - if (argc > 0) JS_ToUint32(js, &buffer_size, argv[0]); - - void *buffer = js_malloc(js, buffer_size); - if (!buffer) return JS_ThrowOutOfMemory(js); - - uint32 bytes_written; - EVoiceResult result = SteamAPI_ISteamUser_GetVoice(steam_user, true, buffer, buffer_size, &bytes_written, false, NULL, 0, NULL, 0); - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "result", JS_NewInt32(js, result)); - - if (result == k_EVoiceResultOK && bytes_written > 0) { - JSValue blob = js_new_blob_stoned_copy(js, buffer, bytes_written); - JS_SetPropertyStr(js, obj, "data", blob); - JS_SetPropertyStr(js, obj, "bytes_written", JS_NewUint32(js, bytes_written)); - } - - js_free(js, buffer); - return obj; -) - -JSC_CCALL(user_decompress_voice, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - size_t compressed_size; - void *compressed_data = js_get_blob_data(js, &compressed_size, argv[0]); - if (!compressed_data) return JS_ThrowTypeError(js, "First argument must be a blob"); - - uint32 dest_buffer_size = 22050 * 2; - uint32 sample_rate = 22050; - if (argc > 1) JS_ToUint32(js, &dest_buffer_size, argv[1]); - if (argc > 2) JS_ToUint32(js, &sample_rate, argv[2]); - - void *dest_buffer = js_malloc(js, dest_buffer_size); - if (!dest_buffer) return JS_ThrowOutOfMemory(js); - - uint32 bytes_written; - EVoiceResult result = SteamAPI_ISteamUser_DecompressVoice(steam_user, compressed_data, compressed_size, dest_buffer, dest_buffer_size, &bytes_written, sample_rate); - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "result", JS_NewInt32(js, result)); - - if (result == k_EVoiceResultOK && bytes_written > 0) { - JSValue blob = js_new_blob_stoned_copy(js, dest_buffer, bytes_written); - JS_SetPropertyStr(js, obj, "data", blob); - JS_SetPropertyStr(js, obj, "bytes_written", JS_NewUint32(js, bytes_written)); - } - - js_free(js, dest_buffer); - return obj; -) - -JSC_CCALL(user_get_voice_optimal_sample_rate, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - uint32 rate = SteamAPI_ISteamUser_GetVoiceOptimalSampleRate(steam_user); - return JS_NewUint32(js, rate); -) - -JSC_CCALL(user_get_auth_session_ticket, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - uint8_t ticket[1024]; - uint32 ticket_size; - - HAuthTicket auth_ticket = SteamAPI_ISteamUser_GetAuthSessionTicket(steam_user, ticket, sizeof(ticket), &ticket_size, NULL); - - if (auth_ticket == k_HAuthTicketInvalid) return JS_NULL; - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "auth_ticket", JS_NewUint32(js, auth_ticket)); - - if (ticket_size > 0) { - JSValue blob = js_new_blob_stoned_copy(js, ticket, ticket_size); - JS_SetPropertyStr(js, obj, "ticket_data", blob); - JS_SetPropertyStr(js, obj, "ticket_size", JS_NewUint32(js, ticket_size)); - } - - return obj; -) - -JSC_CCALL(user_get_auth_ticket_for_web_api, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - const char *identity = JS_ToCString(js, argv[0]); - if (!identity) return JS_EXCEPTION; - - HAuthTicket auth_ticket = SteamAPI_ISteamUser_GetAuthTicketForWebApi(steam_user, identity); - JS_FreeCString(js, identity); - - if (auth_ticket == k_HAuthTicketInvalid) return JS_NULL; - return JS_NewUint32(js, auth_ticket); -) - -JSC_CCALL(user_begin_auth_session, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - size_t ticket_size; - void *ticket_data = js_get_blob_data(js, &ticket_size, argv[0]); - if (!ticket_data) return JS_ThrowTypeError(js, "First argument must be a blob"); - - steamid_t *sid = js2steamid(js, argv[1]); - if (!sid) return JS_ThrowTypeError(js, "Second argument must be a SteamID"); - - EBeginAuthSessionResult result = SteamAPI_ISteamUser_BeginAuthSession(steam_user, ticket_data, ticket_size, sid->id); - return JS_NewInt32(js, result); -) - -JSC_CCALL(user_end_auth_session, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a SteamID"); - - SteamAPI_ISteamUser_EndAuthSession(steam_user, sid->id); - return JS_NULL; -) - -JSC_CCALL(user_cancel_auth_ticket, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - uint32 auth_ticket; - JS_ToUint32(js, &auth_ticket, argv[0]); - - SteamAPI_ISteamUser_CancelAuthTicket(steam_user, auth_ticket); - return JS_NULL; -) - -JSC_CCALL(user_has_license_for_app, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a SteamID"); - - uint32 app_id; - JS_ToUint32(js, &app_id, argv[1]); - - EUserHasLicenseForAppResult result = SteamAPI_ISteamUser_UserHasLicenseForApp(steam_user, sid->id, app_id); - return JS_NewInt32(js, result); -) - -JSC_CCALL(user_is_behind_nat, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - bool behind_nat = SteamAPI_ISteamUser_BIsBehindNAT(steam_user); - return JS_NewBool(js, behind_nat); -) - -JSC_CCALL(user_advertise_game, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a SteamID"); - - uint32 ip_server, port_server; - JS_ToUint32(js, &ip_server, argv[1]); - JS_ToUint32(js, &port_server, argv[2]); - - SteamAPI_ISteamUser_AdvertiseGame(steam_user, sid->id, ip_server, (uint16)port_server); - return JS_NULL; -) - -JSC_CCALL(user_request_encrypted_app_ticket, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - size_t data_size = 0; - void *data = NULL; - - if (argc > 0 && !JS_IsNull(argv[0])) { - data = js_get_blob_data(js, &data_size, argv[0]); - if (!data) return JS_ThrowTypeError(js, "First argument must be a blob or null"); - } - - SteamAPICall_t call = SteamAPI_ISteamUser_RequestEncryptedAppTicket(steam_user, data, data_size); - return steamid2js(js, call); -) - -JSC_CCALL(user_get_encrypted_app_ticket, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - uint8_t ticket[1024]; - uint32 ticket_size; - - bool success = SteamAPI_ISteamUser_GetEncryptedAppTicket(steam_user, ticket, sizeof(ticket), &ticket_size); - - if (!success) return JS_NULL; - - JSValue obj = JS_NewObject(js); - if (ticket_size > 0) { - JSValue blob = js_new_blob_stoned_copy(js, ticket, ticket_size); - JS_SetPropertyStr(js, obj, "ticket_data", blob); - JS_SetPropertyStr(js, obj, "ticket_size", JS_NewUint32(js, ticket_size)); - } - - return obj; -) - -JSC_CCALL(user_get_game_badge_level, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - int32 series; - JS_ToInt32(js, &series, argv[0]); - - bool foil = argc > 1 ? JS_ToBool(js, argv[1]) : false; - - int level = SteamAPI_ISteamUser_GetGameBadgeLevel(steam_user, series, foil); - return JS_NewInt32(js, level); -) - -JSC_CCALL(user_request_store_auth_url, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - const char *redirect_url = JS_ToCString(js, argv[0]); - if (!redirect_url) return JS_EXCEPTION; - - SteamAPICall_t call = SteamAPI_ISteamUser_RequestStoreAuthURL(steam_user, redirect_url); - JS_FreeCString(js, redirect_url); - - return steamid2js(js, call); -) - -JSC_CCALL(user_is_phone_verified, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - bool verified = SteamAPI_ISteamUser_BIsPhoneVerified(steam_user); - return JS_NewBool(js, verified); -) - -JSC_CCALL(user_is_two_factor_enabled, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - bool enabled = SteamAPI_ISteamUser_BIsTwoFactorEnabled(steam_user); - return JS_NewBool(js, enabled); -) - -JSC_CCALL(user_is_phone_identifying, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - bool identifying = SteamAPI_ISteamUser_BIsPhoneIdentifying(steam_user); - return JS_NewBool(js, identifying); -) - -JSC_CCALL(user_is_phone_requiring_verification, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - bool requiring = SteamAPI_ISteamUser_BIsPhoneRequiringVerification(steam_user); - return JS_NewBool(js, requiring); -) - -JSC_CCALL(user_get_market_eligibility, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - SteamAPICall_t call = SteamAPI_ISteamUser_GetMarketEligibility(steam_user); - return steamid2js(js, call); -) - -JSC_CCALL(user_get_duration_control, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - SteamAPICall_t call = SteamAPI_ISteamUser_GetDurationControl(steam_user); - return steamid2js(js, call); -) - -JSC_CCALL(user_set_duration_control_online_state, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - int32 state; - JS_ToInt32(js, &state, argv[0]); - - bool success = SteamAPI_ISteamUser_BSetDurationControlOnlineState(steam_user, (EDurationControlOnlineState)state); - return JS_NewBool(js, success); -) - -JSC_CCALL(user_track_app_usage_event, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - uint64 game_id; - JS_ToIndex(js, &game_id, argv[0]); - - int32 app_usage_event; - JS_ToInt32(js, &app_usage_event, argv[1]); - - const char *extra_info = argc > 2 ? JS_ToCString(js, argv[2]) : ""; - - SteamAPI_ISteamUser_TrackAppUsageEvent(steam_user, game_id, app_usage_event, extra_info); - - if (argc > 2) JS_FreeCString(js, extra_info); - return JS_NULL; -) - -JSC_CCALL(user_initiate_game_connection_deprecated, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - steamid_t *server_sid = js2steamid(js, argv[0]); - if (!server_sid) return JS_ThrowTypeError(js, "First argument must be a SteamID"); - - uint32 ip_server, port_server; - JS_ToUint32(js, &ip_server, argv[1]); - JS_ToUint32(js, &port_server, argv[2]); - - bool secure = argc > 3 ? JS_ToBool(js, argv[3]) : false; - - uint8_t auth_blob[1024]; - int result = SteamAPI_ISteamUser_InitiateGameConnection_DEPRECATED(steam_user, auth_blob, sizeof(auth_blob), server_sid->id, ip_server, (uint16)port_server, secure); - - if (result <= 0) return JS_NULL; - - JSValue obj = JS_NewObject(js); - JSValue blob = js_new_blob_stoned_copy(js, auth_blob, result); - JS_SetPropertyStr(js, obj, "auth_blob", blob); - JS_SetPropertyStr(js, obj, "size", JS_NewInt32(js, result)); - - return obj; -) - -JSC_CCALL(user_terminate_game_connection_deprecated, - if (!steam_user) return JS_ThrowInternalError(js, "Steam user not initialized"); - - uint32 ip_server, port_server; - JS_ToUint32(js, &ip_server, argv[0]); - JS_ToUint32(js, &port_server, argv[1]); - - SteamAPI_ISteamUser_TerminateGameConnection_DEPRECATED(steam_user, ip_server, (uint16)port_server); - return JS_NULL; -) - -// FRIENDS -JSC_CCALL(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(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); -) - -JSC_CCALL(friends_count, - if (!steam_friends) return JS_ThrowInternalError(js, "Steam friends not initialized"); - - int flags = k_EFriendFlagImmediate; - if (argc > 0) { - JS_ToInt32(js, &flags, argv[0]); - } - - int count = SteamAPI_ISteamFriends_GetFriendCount(steam_friends, flags); - return JS_NewInt32(js, count); -) - -JSC_CCALL(friends_get_friend, - if (!steam_friends) return JS_ThrowInternalError(js, "Steam friends not initialized"); - - int index; - JS_ToInt32(js, &index, argv[0]); - - int flags = k_EFriendFlagImmediate; - if (argc > 1) { - JS_ToInt32(js, &flags, argv[1]); - } - - uint64_steamid friendID = SteamAPI_ISteamFriends_GetFriendByIndex(steam_friends, index, flags); - if (friendID == 0) return JS_NULL; - - return steamid2js(js, friendID); -) - -JSC_CCALL(friends_get_friend_name, - if (!steam_friends) return JS_ThrowInternalError(js, "Steam friends not initialized"); - - uint64_steamid friendID; - if (argc > 0 && JS_IsObject(argv[0])) { - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "Expected SteamID"); - friendID = sid->id; - } else { - double val; - JS_ToFloat64(js, &val, argv[0]); - friendID = (uint64_steamid)val; - } - - const char *name = SteamAPI_ISteamFriends_GetFriendPersonaName(steam_friends, friendID); - return JS_NewString(js, name ? name : ""); -) - -JSC_CCALL(friends_activate_overlay, - if (!steam_friends) return JS_ThrowInternalError(js, "Steam friends not initialized"); - - const char *dialog = JS_ToCString(js, argv[0]); - if (!dialog) return JS_EXCEPTION; - - SteamAPI_ISteamFriends_ActivateGameOverlay(steam_friends, dialog); - JS_FreeCString(js, dialog); - - return JS_NULL; -) - -JSC_CCALL(friends_activate_overlay_to_user, - if (!steam_friends) return JS_ThrowInternalError(js, "Steam friends not initialized"); - - const char *dialog = JS_ToCString(js, argv[0]); - if (!dialog) return JS_EXCEPTION; - - uint64_steamid userID; - if (argc > 1 && JS_IsObject(argv[1])) { - steamid_t *sid = js2steamid(js, argv[1]); - if (!sid) { - JS_FreeCString(js, dialog); - return JS_ThrowTypeError(js, "Expected SteamID"); - } - userID = sid->id; - } else { - double val; - JS_ToFloat64(js, &val, argv[1]); - userID = (uint64_steamid)val; - } - - SteamAPI_ISteamFriends_ActivateGameOverlayToUser(steam_friends, dialog, userID); - JS_FreeCString(js, dialog); - - return JS_NULL; -) - -JSC_CCALL(friends_activate_overlay_to_web, - if (!steam_friends) return JS_ThrowInternalError(js, "Steam friends not initialized"); - - const char *url = JS_ToCString(js, argv[0]); - if (!url) return JS_EXCEPTION; - - SteamAPI_ISteamFriends_ActivateGameOverlayToWebPage(steam_friends, url, k_EActivateGameOverlayToWebPageMode_Default); - JS_FreeCString(js, url); - - return JS_NULL; -) - -JSC_CCALL(friends_set_rich_presence, - if (!steam_friends) return JS_ThrowInternalError(js, "Steam friends not initialized"); - - const char *key = JS_ToCString(js, argv[0]); - if (!key) return JS_EXCEPTION; - - const char *value = JS_ToCString(js, argv[1]); - if (!value) { - JS_FreeCString(js, key); - return JS_EXCEPTION; - } - - bool success = SteamAPI_ISteamFriends_SetRichPresence(steam_friends, key, value); - - JS_FreeCString(js, key); - JS_FreeCString(js, value); - - return JS_NewBool(js, success); -) - -JSC_CCALL(friends_clear_rich_presence, - if (!steam_friends) return JS_ThrowInternalError(js, "Steam friends not initialized"); - - SteamAPI_ISteamFriends_ClearRichPresence(steam_friends); - return JS_NULL; -) - -// CLOUD STORAGE -JSC_CCALL(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(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(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_NULL; -) - -JSC_CCALL(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_NULL; - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "total", JS_NewFloat64(js, (double)total)); - JS_SetPropertyStr(js, obj, "available", JS_NewFloat64(js, (double)available)); - return obj; -) - -JSC_CCALL(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(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_NULL; - } - - 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_NULL; - } - - JSValue result = js_new_blob_stoned_copy(js, buffer, size); - js_free(js, buffer); - return result; -) - -JSC_CCALL(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(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); -) - -// EXPANDED STEAM UTILS -JSC_CCALL(utils_overlay_enabled, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - bool enabled = SteamAPI_ISteamUtils_IsOverlayEnabled(steam_utils); - return JS_NewBool(js, enabled); -) - -JSC_CCALL(utils_big_picture_mode, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - bool bigPicture = SteamAPI_ISteamUtils_IsSteamInBigPictureMode(steam_utils); - return JS_NewBool(js, bigPicture); -) - -JSC_CCALL(utils_vr_mode, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - bool vrMode = SteamAPI_ISteamUtils_IsSteamRunningInVR(steam_utils); - return JS_NewBool(js, vrMode); -) - -JSC_CCALL(utils_steam_deck, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - bool steamDeck = SteamAPI_ISteamUtils_IsSteamRunningOnSteamDeck(steam_utils); - return JS_NewBool(js, steamDeck); -) - -JSC_CCALL(utils_battery_power, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - uint8 battery = SteamAPI_ISteamUtils_GetCurrentBatteryPower(steam_utils); - return JS_NewUint32(js, battery); -) - -JSC_CCALL(utils_seconds_since_app_active, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - uint32 seconds = SteamAPI_ISteamUtils_GetSecondsSinceAppActive(steam_utils); - return JS_NewUint32(js, seconds); -) - -JSC_CCALL(utils_ip_country, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - const char *country = SteamAPI_ISteamUtils_GetIPCountry(steam_utils); - return JS_NewString(js, country ? country : ""); -) - -JSC_CCALL(utils_ui_language, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - const char *language = SteamAPI_ISteamUtils_GetSteamUILanguage(steam_utils); - return JS_NewString(js, language ? language : "english"); -) - -JSC_CCALL(utils_get_seconds_since_computer_active, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - uint32 seconds = SteamAPI_ISteamUtils_GetSecondsSinceComputerActive(steam_utils); - return JS_NewUint32(js, seconds); -) - -JSC_CCALL(utils_get_connected_universe, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - EUniverse universe = SteamAPI_ISteamUtils_GetConnectedUniverse(steam_utils); - return JS_NewUint32(js, universe); -) - -JSC_CCALL(utils_get_server_real_time, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - uint32 server_time = SteamAPI_ISteamUtils_GetServerRealTime(steam_utils); - return JS_NewUint32(js, server_time); -) - -JSC_CCALL(utils_get_image_size, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - int image_handle; - JS_ToInt32(js, &image_handle, argv[0]); - - uint32 width, height; - bool success = SteamAPI_ISteamUtils_GetImageSize(steam_utils, image_handle, &width, &height); - - if (!success) return JS_NULL; - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "width", JS_NewUint32(js, width)); - JS_SetPropertyStr(js, obj, "height", JS_NewUint32(js, height)); - return obj; -) - -JSC_CCALL(utils_get_image_rgba, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - int image_handle; - JS_ToInt32(js, &image_handle, argv[0]); - - // First get the image size to allocate buffer - uint32 width, height; - if (!SteamAPI_ISteamUtils_GetImageSize(steam_utils, image_handle, &width, &height)) { - return JS_NULL; - } - - uint32 buffer_size = width * height * 4; // RGBA = 4 bytes per pixel - uint8_t *buffer = (uint8_t*)js_malloc(js, buffer_size); - if (!buffer) return JS_EXCEPTION; - - bool success = SteamAPI_ISteamUtils_GetImageRGBA(steam_utils, image_handle, buffer, buffer_size); - - if (!success) { - js_free(js, buffer); - return JS_NULL; - } - - JSValue result = js_new_blob_stoned_copy(js, buffer, buffer_size); - js_free(js, buffer); - return result; -) - -JSC_CCALL(utils_set_overlay_notification_position, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - uint32 position; - JS_ToUint32(js, &position, argv[0]); - - SteamAPI_ISteamUtils_SetOverlayNotificationPosition(steam_utils, (ENotificationPosition)position); - return JS_NULL; -) - -JSC_CCALL(utils_is_api_call_completed, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - double call_double; - JS_ToFloat64(js, &call_double, argv[0]); - SteamAPICall_t call = (SteamAPICall_t)call_double; - - bool failed; - bool completed = SteamAPI_ISteamUtils_IsAPICallCompleted(steam_utils, call, &failed); - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "completed", JS_NewBool(js, completed)); - JS_SetPropertyStr(js, obj, "failed", JS_NewBool(js, failed)); - return obj; -) - -JSC_CCALL(utils_get_api_call_failure_reason, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - double call_double; - JS_ToFloat64(js, &call_double, argv[0]); - SteamAPICall_t call = (SteamAPICall_t)call_double; - - ESteamAPICallFailure reason = SteamAPI_ISteamUtils_GetAPICallFailureReason(steam_utils, call); - return JS_NewUint32(js, reason); -) - -JSC_CCALL(utils_get_ipc_call_count, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - uint32 count = SteamAPI_ISteamUtils_GetIPCCallCount(steam_utils); - return JS_NewUint32(js, count); -) - -JSC_CCALL(utils_overlay_needs_present, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - bool needs_present = SteamAPI_ISteamUtils_BOverlayNeedsPresent(steam_utils); - return JS_NewBool(js, needs_present); -) - -JSC_CCALL(utils_check_file_signature, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - const char *filename = JS_ToCString(js, argv[0]); - if (!filename) return JS_EXCEPTION; - - SteamAPICall_t call = SteamAPI_ISteamUtils_CheckFileSignature(steam_utils, filename); - JS_FreeCString(js, filename); - - return JS_NewFloat64(js, (double)call); -) - -JSC_CCALL(utils_show_gamepad_text_input, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - uint32 input_mode, line_mode, char_max; - JS_ToUint32(js, &input_mode, argv[0]); - JS_ToUint32(js, &line_mode, argv[1]); - - const char *description = JS_ToCString(js, argv[2]); - if (!description) return JS_EXCEPTION; - - JS_ToUint32(js, &char_max, argv[3]); - - const char *existing_text = argc > 4 ? JS_ToCString(js, argv[4]) : ""; - - bool success = SteamAPI_ISteamUtils_ShowGamepadTextInput(steam_utils, (EGamepadTextInputMode)input_mode, (EGamepadTextInputLineMode)line_mode, description, char_max, existing_text); - - JS_FreeCString(js, description); - if (argc > 4) JS_FreeCString(js, existing_text); - - return JS_NewBool(js, success); -) - -JSC_CCALL(utils_get_entered_gamepad_text_length, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - uint32 length = SteamAPI_ISteamUtils_GetEnteredGamepadTextLength(steam_utils); - return JS_NewUint32(js, length); -) - -JSC_CCALL(utils_get_entered_gamepad_text_input, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - uint32 length = SteamAPI_ISteamUtils_GetEnteredGamepadTextLength(steam_utils); - if (length == 0) return JS_NewString(js, ""); - - char *buffer = (char*)js_malloc(js, length + 1); - if (!buffer) return JS_EXCEPTION; - - bool success = SteamAPI_ISteamUtils_GetEnteredGamepadTextInput(steam_utils, buffer, length + 1); - - if (!success) { - js_free(js, buffer); - return JS_NewString(js, ""); - } - - JSValue result = JS_NewString(js, buffer); - js_free(js, buffer); - return result; -) - -JSC_CCALL(utils_set_overlay_notification_inset, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - int horizontal_inset, vertical_inset; - JS_ToInt32(js, &horizontal_inset, argv[0]); - JS_ToInt32(js, &vertical_inset, argv[1]); - - SteamAPI_ISteamUtils_SetOverlayNotificationInset(steam_utils, horizontal_inset, vertical_inset); - return JS_NULL; -) - -JSC_CCALL(utils_start_vr_dashboard, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - SteamAPI_ISteamUtils_StartVRDashboard(steam_utils); - return JS_NULL; -) - -JSC_CCALL(utils_is_vr_headset_streaming_enabled, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - bool enabled = SteamAPI_ISteamUtils_IsVRHeadsetStreamingEnabled(steam_utils); - return JS_NewBool(js, enabled); -) - -JSC_CCALL(utils_set_vr_headset_streaming_enabled, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - bool enabled = JS_ToBool(js, argv[0]); - SteamAPI_ISteamUtils_SetVRHeadsetStreamingEnabled(steam_utils, enabled); - return JS_NULL; -) - -JSC_CCALL(utils_is_steam_china_launcher, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - bool is_china = SteamAPI_ISteamUtils_IsSteamChinaLauncher(steam_utils); - return JS_NewBool(js, is_china); -) - -JSC_CCALL(utils_init_filter_text, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - uint32 filter_options; - JS_ToUint32(js, &filter_options, argv[0]); - - bool success = SteamAPI_ISteamUtils_InitFilterText(steam_utils, filter_options); - return JS_NewBool(js, success); -) - -JSC_CCALL(utils_filter_text, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - uint32 context; - JS_ToUint32(js, &context, argv[0]); - - steamid_t *sid = js2steamid(js, argv[1]); - if (!sid) return JS_ThrowTypeError(js, "Second argument must be a SteamID"); - - const char *input_message = JS_ToCString(js, argv[2]); - if (!input_message) return JS_EXCEPTION; - - // Allocate buffer for filtered text (same size as input should be sufficient) - size_t input_len = strlen(input_message); - uint32 buffer_size = input_len + 1; - char *filtered_buffer = (char*)js_malloc(js, buffer_size); - if (!filtered_buffer) { - JS_FreeCString(js, input_message); - return JS_EXCEPTION; - } - - int result = SteamAPI_ISteamUtils_FilterText(steam_utils, (ETextFilteringContext)context, sid->id, input_message, filtered_buffer, buffer_size); - - JS_FreeCString(js, input_message); - - if (result < 0) { - js_free(js, filtered_buffer); - return JS_NULL; - } - - JSValue js_result = JS_NewString(js, filtered_buffer); - js_free(js, filtered_buffer); - return js_result; -) - -JSC_CCALL(utils_get_ipv6_connectivity_state, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - uint32 protocol; - JS_ToUint32(js, &protocol, argv[0]); - - ESteamIPv6ConnectivityState state = SteamAPI_ISteamUtils_GetIPv6ConnectivityState(steam_utils, (ESteamIPv6ConnectivityProtocol)protocol); - return JS_NewUint32(js, state); -) - -JSC_CCALL(utils_show_floating_gamepad_text_input, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - uint32 keyboard_mode; - int x_pos, y_pos, width, height; - JS_ToUint32(js, &keyboard_mode, argv[0]); - JS_ToInt32(js, &x_pos, argv[1]); - JS_ToInt32(js, &y_pos, argv[2]); - JS_ToInt32(js, &width, argv[3]); - JS_ToInt32(js, &height, argv[4]); - - bool success = SteamAPI_ISteamUtils_ShowFloatingGamepadTextInput(steam_utils, (EFloatingGamepadTextInputMode)keyboard_mode, x_pos, y_pos, width, height); - return JS_NewBool(js, success); -) - -JSC_CCALL(utils_set_game_launcher_mode, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - bool launcher_mode = JS_ToBool(js, argv[0]); - SteamAPI_ISteamUtils_SetGameLauncherMode(steam_utils, launcher_mode); - return JS_NULL; -) - -JSC_CCALL(utils_dismiss_floating_gamepad_text_input, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - bool success = SteamAPI_ISteamUtils_DismissFloatingGamepadTextInput(steam_utils); - return JS_NewBool(js, success); -) - -JSC_CCALL(utils_dismiss_gamepad_text_input, - if (!steam_utils) return JS_ThrowInternalError(js, "Steam utils not initialized"); - - bool success = SteamAPI_ISteamUtils_DismissGamepadTextInput(steam_utils); - return JS_NewBool(js, success); -) - -// SCREENSHOTS API -JSC_CCALL(screenshots_write, - if (!steam_screenshots) return JS_ThrowInternalError(js, "Steam screenshots not initialized"); - - const char *filename = JS_ToCString(js, argv[0]); - if (!filename) return JS_EXCEPTION; - - uint32 width, height; - JS_ToUint32(js, &width, argv[1]); - JS_ToUint32(js, &height, argv[2]); - - // Get image data from ArrayBuffer - size_t data_len; - uint8_t *data = (uint8_t*)js_get_blob_data(js, &data_len, argv[3]); - if (!data) { - JS_FreeCString(js, filename); - return JS_ThrowTypeError(js, "Fourth argument must be ArrayBuffer containing RGB data"); - } - - ScreenshotHandle handle = SteamAPI_ISteamScreenshots_WriteScreenshot(steam_screenshots, data, data_len, width, height); - JS_FreeCString(js, filename); - - return JS_NewUint32(js, handle); -) - -JSC_CCALL(screenshots_trigger, - if (!steam_screenshots) return JS_ThrowInternalError(js, "Steam screenshots not initialized"); - - SteamAPI_ISteamScreenshots_TriggerScreenshot(steam_screenshots); - return JS_NULL; -) - -// NETWORKING API -JSC_CCALL(networking_send_p2p, - if (!steam_networking) return JS_ThrowInternalError(js, "Steam networking not initialized"); - - // Get SteamID - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a SteamID"); - - // Get data - size_t data_len; - uint8_t *data = (uint8_t*)js_get_blob_data(js, &data_len, argv[1]); - if (!data) return JS_ThrowTypeError(js, "Second argument must be ArrayBuffer"); - - // Get send type (optional, defaults to reliable) - EP2PSend send_type = k_EP2PSendReliable; - if (argc > 2) { - uint32_t type; - JS_ToUint32(js, &type, argv[2]); - send_type = (EP2PSend)type; - } - - // Get channel (optional, defaults to 0) - int channel = 0; - if (argc > 3) { - JS_ToInt32(js, &channel, argv[3]); - } - - bool success = SteamAPI_ISteamNetworking_SendP2PPacket(steam_networking, sid->id, data, data_len, send_type, channel); - return JS_NewBool(js, success); -) - -JSC_CCALL(networking_read_p2p, - if (!steam_networking) return JS_ThrowInternalError(js, "Steam networking not initialized"); - - // Get channel (optional, defaults to 0) - int channel = 0; - if (argc > 0) { - JS_ToInt32(js, &channel, argv[0]); - } - - uint32 msg_size; - if (!SteamAPI_ISteamNetworking_IsP2PPacketAvailable(steam_networking, &msg_size, channel)) { - return JS_NULL; // No packet available - } - - uint8_t *buffer = (uint8_t*)js_malloc(js, msg_size); - if (!buffer) return JS_EXCEPTION; - - CSteamID remote_steamid; - uint32 bytes_read; - bool success = SteamAPI_ISteamNetworking_ReadP2PPacket(steam_networking, buffer, msg_size, &bytes_read, &remote_steamid, channel); - - if (!success) { - js_free(js, buffer); - return JS_NULL; - } - - JSValue result = JS_NewObject(js); - JSValue data_buffer = js_new_blob_stoned_copy(js, buffer, bytes_read); - JSValue steamid_obj = steamid2js(js, remote_steamid.ConvertToUint64()); - - JS_SetPropertyStr(js, result, "data", data_buffer); - JS_SetPropertyStr(js, result, "steamid", steamid_obj); - JS_SetPropertyStr(js, result, "size", JS_NewUint32(js, bytes_read)); - - js_free(js, buffer); - return result; -) - -JSC_CCALL(networking_accept_p2p, - if (!steam_networking) return JS_ThrowInternalError(js, "Steam networking not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a SteamID"); - - bool success = SteamAPI_ISteamNetworking_AcceptP2PSessionWithUser(steam_networking, sid->id); - return JS_NewBool(js, success); -) - -JSC_CCALL(networking_close_p2p, - if (!steam_networking) return JS_ThrowInternalError(js, "Steam networking not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a SteamID"); - - // Get channel (optional, defaults to 0) - int channel = 0; - if (argc > 1) { - JS_ToInt32(js, &channel, argv[1]); - } - - bool success = SteamAPI_ISteamNetworking_CloseP2PSessionWithUser(steam_networking, sid->id); - return JS_NewBool(js, success); -) - -JSC_CCALL(networking_close_p2p_channel, - if (!steam_networking) return JS_ThrowInternalError(js, "Steam networking not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a SteamID"); - - int channel; - JS_ToInt32(js, &channel, argv[1]); - - bool success = SteamAPI_ISteamNetworking_CloseP2PChannelWithUser(steam_networking, sid->id, channel); - return JS_NewBool(js, success); -) - -JSC_CCALL(networking_p2p_session_state, - if (!steam_networking) return JS_ThrowInternalError(js, "Steam networking not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a SteamID"); - - P2PSessionState_t state; - if (!SteamAPI_ISteamNetworking_GetP2PSessionState(steam_networking, sid->id, &state)) { - return JS_NULL; - } - - JSValue result = JS_NewObject(js); - JS_SetPropertyStr(js, result, "connection_active", JS_NewBool(js, state.m_bConnectionActive)); - JS_SetPropertyStr(js, result, "connecting", JS_NewBool(js, state.m_bConnecting)); - JS_SetPropertyStr(js, result, "session_error", JS_NewUint32(js, state.m_eP2PSessionError)); - JS_SetPropertyStr(js, result, "using_relay", JS_NewBool(js, state.m_bUsingRelay)); - JS_SetPropertyStr(js, result, "bytes_queued_for_send", JS_NewInt32(js, state.m_nBytesQueuedForSend)); - JS_SetPropertyStr(js, result, "packets_queued_for_send", JS_NewInt32(js, state.m_nPacketsQueuedForSend)); - JS_SetPropertyStr(js, result, "remote_ip", JS_NewUint32(js, state.m_nRemoteIP)); - JS_SetPropertyStr(js, result, "remote_port", JS_NewUint32(js, state.m_nRemotePort)); - - return result; -) - -// MATCHMAKING API -JSC_CCALL(matchmaking_create_lobby, - if (!steam_matchmaking) return JS_ThrowInternalError(js, "Steam matchmaking not initialized"); - - // Get lobby type (defaults to public) - ELobbyType lobby_type = k_ELobbyTypePublic; - if (argc > 0) { - uint32_t type; - JS_ToUint32(js, &type, argv[0]); - lobby_type = (ELobbyType)type; - } - - // Get max members (defaults to 4) - int max_members = 4; - if (argc > 1) { - JS_ToInt32(js, &max_members, argv[1]); - } - - SteamAPICall_t call = SteamAPI_ISteamMatchmaking_CreateLobby(steam_matchmaking, lobby_type, max_members); - return JS_NewFloat64(js, (double)call); -) - -JSC_CCALL(matchmaking_join_lobby, - if (!steam_matchmaking) return JS_ThrowInternalError(js, "Steam matchmaking not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a SteamID"); - - SteamAPICall_t call = SteamAPI_ISteamMatchmaking_JoinLobby(steam_matchmaking, sid->id); - return JS_NewFloat64(js, (double)call); -) - -JSC_CCALL(matchmaking_leave_lobby, - if (!steam_matchmaking) return JS_ThrowInternalError(js, "Steam matchmaking not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a SteamID"); - - SteamAPI_ISteamMatchmaking_LeaveLobby(steam_matchmaking, sid->id); - return JS_NULL; -) - -JSC_CCALL(matchmaking_invite_user, - if (!steam_matchmaking) return JS_ThrowInternalError(js, "Steam matchmaking not initialized"); - - steamid_t *lobby_sid = js2steamid(js, argv[0]); - if (!lobby_sid) return JS_ThrowTypeError(js, "First argument must be a lobby SteamID"); - - steamid_t *user_sid = js2steamid(js, argv[1]); - if (!user_sid) return JS_ThrowTypeError(js, "Second argument must be a user SteamID"); - - bool success = SteamAPI_ISteamMatchmaking_InviteUserToLobby(steam_matchmaking, lobby_sid->id, user_sid->id); - return JS_NewBool(js, success); -) - -JSC_CCALL(matchmaking_lobby_member_count, - if (!steam_matchmaking) return JS_ThrowInternalError(js, "Steam matchmaking not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a lobby SteamID"); - - int count = SteamAPI_ISteamMatchmaking_GetNumLobbyMembers(steam_matchmaking, sid->id); - return JS_NewInt32(js, count); -) - -JSC_CCALL(matchmaking_lobby_member_by_index, - if (!steam_matchmaking) return JS_ThrowInternalError(js, "Steam matchmaking not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a lobby SteamID"); - - int index; - JS_ToInt32(js, &index, argv[1]); - - uint64_steamid member = SteamAPI_ISteamMatchmaking_GetLobbyMemberByIndex(steam_matchmaking, sid->id, index); - return steamid2js(js, member); -) - -JSC_CCALL(matchmaking_lobby_owner, - if (!steam_matchmaking) return JS_ThrowInternalError(js, "Steam matchmaking not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a lobby SteamID"); - - uint64_steamid owner = SteamAPI_ISteamMatchmaking_GetLobbyOwner(steam_matchmaking, sid->id); - return steamid2js(js, owner); -) - -JSC_CCALL(matchmaking_set_lobby_data, - if (!steam_matchmaking) return JS_ThrowInternalError(js, "Steam matchmaking not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a lobby SteamID"); - - const char *key = JS_ToCString(js, argv[1]); - if (!key) return JS_EXCEPTION; - - const char *value = JS_ToCString(js, argv[2]); - if (!value) { - JS_FreeCString(js, key); - return JS_EXCEPTION; - } - - bool success = SteamAPI_ISteamMatchmaking_SetLobbyData(steam_matchmaking, sid->id, key, value); - - JS_FreeCString(js, key); - JS_FreeCString(js, value); - - return JS_NewBool(js, success); -) - -JSC_CCALL(matchmaking_get_lobby_data, - if (!steam_matchmaking) return JS_ThrowInternalError(js, "Steam matchmaking not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a lobby SteamID"); - - const char *key = JS_ToCString(js, argv[1]); - if (!key) return JS_EXCEPTION; - - const char *value = SteamAPI_ISteamMatchmaking_GetLobbyData(steam_matchmaking, sid->id, key); - JS_FreeCString(js, key); - - return JS_NewString(js, value ? value : ""); -) - -JSC_CCALL(matchmaking_set_lobby_member_data, - if (!steam_matchmaking) return JS_ThrowInternalError(js, "Steam matchmaking not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a lobby SteamID"); - - const char *key = JS_ToCString(js, argv[1]); - if (!key) return JS_EXCEPTION; - - const char *value = JS_ToCString(js, argv[2]); - if (!value) { - JS_FreeCString(js, key); - return JS_EXCEPTION; - } - - SteamAPI_ISteamMatchmaking_SetLobbyMemberData(steam_matchmaking, sid->id, key, value); - - JS_FreeCString(js, key); - JS_FreeCString(js, value); - - return JS_NULL; -) - -JSC_CCALL(matchmaking_get_lobby_member_data, - if (!steam_matchmaking) return JS_ThrowInternalError(js, "Steam matchmaking not initialized"); - - steamid_t *lobby_sid = js2steamid(js, argv[0]); - if (!lobby_sid) return JS_ThrowTypeError(js, "First argument must be a lobby SteamID"); - - steamid_t *user_sid = js2steamid(js, argv[1]); - if (!user_sid) return JS_ThrowTypeError(js, "Second argument must be a user SteamID"); - - const char *key = JS_ToCString(js, argv[2]); - if (!key) return JS_EXCEPTION; - - const char *value = SteamAPI_ISteamMatchmaking_GetLobbyMemberData(steam_matchmaking, lobby_sid->id, user_sid->id, key); - JS_FreeCString(js, key); - - return JS_NewString(js, value ? value : ""); -) - -JSC_CCALL(matchmaking_send_lobby_chat_msg, - if (!steam_matchmaking) return JS_ThrowInternalError(js, "Steam matchmaking not initialized"); - - steamid_t *sid = js2steamid(js, argv[0]); - if (!sid) return JS_ThrowTypeError(js, "First argument must be a lobby SteamID"); - - size_t msg_len; - const char *message = JS_ToCStringLen(js, &msg_len, argv[1]); - if (!message) return JS_EXCEPTION; - - bool success = SteamAPI_ISteamMatchmaking_SendLobbyChatMsg(steam_matchmaking, sid->id, message, msg_len); - JS_FreeCString(js, message); - - return JS_NewBool(js, success); -) - -// UGC (User Generated Content) API -JSC_CCALL(ugc_create_query_user_ugc_request, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - uint32 account_id, list_type, matching_type, sort_order, creator_app_id, consumer_app_id, page; - JS_ToUint32(js, &account_id, argv[0]); - JS_ToUint32(js, &list_type, argv[1]); - JS_ToUint32(js, &matching_type, argv[2]); - JS_ToUint32(js, &sort_order, argv[3]); - JS_ToUint32(js, &creator_app_id, argv[4]); - JS_ToUint32(js, &consumer_app_id, argv[5]); - JS_ToUint32(js, &page, argv[6]); - - UGCQueryHandle_t handle = SteamAPI_ISteamUGC_CreateQueryUserUGCRequest(steam_ugc, account_id, (EUserUGCList)list_type, (EUGCMatchingUGCType)matching_type, (EUserUGCListSortOrder)sort_order, creator_app_id, consumer_app_id, page); - return JS_NewFloat64(js, (double)handle); -) - -JSC_CCALL(ugc_create_query_all_ugc_request_page, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - uint32 query_type, matching_type, creator_app_id, consumer_app_id, page; - JS_ToUint32(js, &query_type, argv[0]); - JS_ToUint32(js, &matching_type, argv[1]); - JS_ToUint32(js, &creator_app_id, argv[2]); - JS_ToUint32(js, &consumer_app_id, argv[3]); - JS_ToUint32(js, &page, argv[4]); - - UGCQueryHandle_t handle = SteamAPI_ISteamUGC_CreateQueryAllUGCRequestPage(steam_ugc, (EUGCQuery)query_type, (EUGCMatchingUGCType)matching_type, creator_app_id, consumer_app_id, page); - return JS_NewFloat64(js, (double)handle); -) - -JSC_CCALL(ugc_create_query_ugc_details_request, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - // Get array of published file IDs - if (!JS_IsArray(js, argv[0])) return JS_ThrowTypeError(js, "First argument must be an array of published file IDs"); - - JSValue length_val = JS_GetPropertyStr(js, argv[0], "length"); - uint32 num_ids; - JS_ToUint32(js, &num_ids, length_val); - JS_FreeValue(js, length_val); - - if (num_ids == 0) return JS_ThrowRangeError(js, "Array must contain at least one published file ID"); - - PublishedFileId_t *file_ids = (PublishedFileId_t*)js_malloc(js, sizeof(PublishedFileId_t) * num_ids); - if (!file_ids) return JS_EXCEPTION; - - for (uint32 i = 0; i < num_ids; i++) { - JSValue id_val = JS_GetPropertyUint32(js, argv[0], i); - double id_double; - JS_ToFloat64(js, &id_double, id_val); - file_ids[i] = (PublishedFileId_t)id_double; - JS_FreeValue(js, id_val); - } - - UGCQueryHandle_t handle = SteamAPI_ISteamUGC_CreateQueryUGCDetailsRequest(steam_ugc, file_ids, num_ids); - js_free(js, file_ids); - - return JS_NewFloat64(js, (double)handle); -) - -JSC_CCALL(ugc_send_query_ugc_request, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double handle_double; - JS_ToFloat64(js, &handle_double, argv[0]); - UGCQueryHandle_t handle = (UGCQueryHandle_t)handle_double; - - SteamAPICall_t call = SteamAPI_ISteamUGC_SendQueryUGCRequest(steam_ugc, handle); - return JS_NewFloat64(js, (double)call); -) - -JSC_CCALL(ugc_release_query_ugc_request, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double handle_double; - JS_ToFloat64(js, &handle_double, argv[0]); - UGCQueryHandle_t handle = (UGCQueryHandle_t)handle_double; - - bool success = SteamAPI_ISteamUGC_ReleaseQueryUGCRequest(steam_ugc, handle); - return JS_NewBool(js, success); -) - -JSC_CCALL(ugc_add_required_tag, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double handle_double; - JS_ToFloat64(js, &handle_double, argv[0]); - UGCQueryHandle_t handle = (UGCQueryHandle_t)handle_double; - - const char *tag = JS_ToCString(js, argv[1]); - if (!tag) return JS_EXCEPTION; - - bool success = SteamAPI_ISteamUGC_AddRequiredTag(steam_ugc, handle, tag); - JS_FreeCString(js, tag); - - return JS_NewBool(js, success); -) - -JSC_CCALL(ugc_add_excluded_tag, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double handle_double; - JS_ToFloat64(js, &handle_double, argv[0]); - UGCQueryHandle_t handle = (UGCQueryHandle_t)handle_double; - - const char *tag = JS_ToCString(js, argv[1]); - if (!tag) return JS_EXCEPTION; - - bool success = SteamAPI_ISteamUGC_AddExcludedTag(steam_ugc, handle, tag); - JS_FreeCString(js, tag); - - return JS_NewBool(js, success); -) - -JSC_CCALL(ugc_set_search_text, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double handle_double; - JS_ToFloat64(js, &handle_double, argv[0]); - UGCQueryHandle_t handle = (UGCQueryHandle_t)handle_double; - - const char *search_text = JS_ToCString(js, argv[1]); - if (!search_text) return JS_EXCEPTION; - - bool success = SteamAPI_ISteamUGC_SetSearchText(steam_ugc, handle, search_text); - JS_FreeCString(js, search_text); - - return JS_NewBool(js, success); -) - -JSC_CCALL(ugc_set_language, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double handle_double; - JS_ToFloat64(js, &handle_double, argv[0]); - UGCQueryHandle_t handle = (UGCQueryHandle_t)handle_double; - - const char *language = JS_ToCString(js, argv[1]); - if (!language) return JS_EXCEPTION; - - bool success = SteamAPI_ISteamUGC_SetLanguage(steam_ugc, handle, language); - JS_FreeCString(js, language); - - return JS_NewBool(js, success); -) - -JSC_CCALL(ugc_subscribe_item, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double file_id; - JS_ToFloat64(js, &file_id, argv[0]); - - SteamAPICall_t call = SteamAPI_ISteamUGC_SubscribeItem(steam_ugc, (PublishedFileId_t)file_id); - return JS_NewFloat64(js, (double)call); -) - -JSC_CCALL(ugc_unsubscribe_item, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double file_id; - JS_ToFloat64(js, &file_id, argv[0]); - - SteamAPICall_t call = SteamAPI_ISteamUGC_UnsubscribeItem(steam_ugc, (PublishedFileId_t)file_id); - return JS_NewFloat64(js, (double)call); -) - -JSC_CCALL(ugc_get_num_subscribed_items, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - bool include_locally_disabled = argc > 0 ? JS_ToBool(js, argv[0]) : false; - - uint32 count = SteamAPI_ISteamUGC_GetNumSubscribedItems(steam_ugc, include_locally_disabled); - return JS_NewUint32(js, count); -) - -JSC_CCALL(ugc_get_subscribed_items, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - uint32 max_entries; - JS_ToUint32(js, &max_entries, argv[0]); - - bool include_locally_disabled = argc > 1 ? JS_ToBool(js, argv[1]) : false; - - if (max_entries == 0) return JS_NewArray(js); - - PublishedFileId_t *file_ids = (PublishedFileId_t*)js_malloc(js, sizeof(PublishedFileId_t) * max_entries); - if (!file_ids) return JS_EXCEPTION; - - uint32 actual_count = SteamAPI_ISteamUGC_GetSubscribedItems(steam_ugc, file_ids, max_entries, include_locally_disabled); - - JSValue result = JS_NewArray(js); - for (uint32 i = 0; i < actual_count; i++) { - JS_SetPropertyUint32(js, result, i, JS_NewFloat64(js, (double)file_ids[i])); - } - - js_free(js, file_ids); - return result; -) - -JSC_CCALL(ugc_get_item_state, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double file_id; - JS_ToFloat64(js, &file_id, argv[0]); - - uint32 state = SteamAPI_ISteamUGC_GetItemState(steam_ugc, (PublishedFileId_t)file_id); - return JS_NewUint32(js, state); -) - -JSC_CCALL(ugc_get_item_install_info, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double file_id; - JS_ToFloat64(js, &file_id, argv[0]); - - uint64 size_on_disk; - char folder[1024]; - uint32 timestamp; - - bool success = SteamAPI_ISteamUGC_GetItemInstallInfo(steam_ugc, (PublishedFileId_t)file_id, &size_on_disk, folder, sizeof(folder), ×tamp); - - if (!success) return JS_NULL; - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "size_on_disk", JS_NewFloat64(js, (double)size_on_disk)); - JS_SetPropertyStr(js, obj, "folder", JS_NewString(js, folder)); - JS_SetPropertyStr(js, obj, "timestamp", JS_NewUint32(js, timestamp)); - return obj; -) - -JSC_CCALL(ugc_get_item_download_info, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double file_id; - JS_ToFloat64(js, &file_id, argv[0]); - - uint64 bytes_downloaded, bytes_total; - - bool success = SteamAPI_ISteamUGC_GetItemDownloadInfo(steam_ugc, (PublishedFileId_t)file_id, &bytes_downloaded, &bytes_total); - - if (!success) return JS_NULL; - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "bytes_downloaded", JS_NewFloat64(js, (double)bytes_downloaded)); - JS_SetPropertyStr(js, obj, "bytes_total", JS_NewFloat64(js, (double)bytes_total)); - return obj; -) - -JSC_CCALL(ugc_download_item, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double file_id; - JS_ToFloat64(js, &file_id, argv[0]); - - bool high_priority = argc > 1 ? JS_ToBool(js, argv[1]) : false; - - bool success = SteamAPI_ISteamUGC_DownloadItem(steam_ugc, (PublishedFileId_t)file_id, high_priority); - return JS_NewBool(js, success); -) - -JSC_CCALL(ugc_create_item, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - uint32 consumer_app_id, file_type; - JS_ToUint32(js, &consumer_app_id, argv[0]); - JS_ToUint32(js, &file_type, argv[1]); - - SteamAPICall_t call = SteamAPI_ISteamUGC_CreateItem(steam_ugc, consumer_app_id, (EWorkshopFileType)file_type); - return JS_NewFloat64(js, (double)call); -) - -JSC_CCALL(ugc_start_item_update, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - uint32 consumer_app_id; - double file_id; - JS_ToUint32(js, &consumer_app_id, argv[0]); - JS_ToFloat64(js, &file_id, argv[1]); - - UGCUpdateHandle_t handle = SteamAPI_ISteamUGC_StartItemUpdate(steam_ugc, consumer_app_id, (PublishedFileId_t)file_id); - return JS_NewFloat64(js, (double)handle); -) - -JSC_CCALL(ugc_set_item_title, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double handle_double; - JS_ToFloat64(js, &handle_double, argv[0]); - UGCUpdateHandle_t handle = (UGCUpdateHandle_t)handle_double; - - const char *title = JS_ToCString(js, argv[1]); - if (!title) return JS_EXCEPTION; - - bool success = SteamAPI_ISteamUGC_SetItemTitle(steam_ugc, handle, title); - JS_FreeCString(js, title); - - return JS_NewBool(js, success); -) - -JSC_CCALL(ugc_set_item_description, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double handle_double; - JS_ToFloat64(js, &handle_double, argv[0]); - UGCUpdateHandle_t handle = (UGCUpdateHandle_t)handle_double; - - const char *description = JS_ToCString(js, argv[1]); - if (!description) return JS_EXCEPTION; - - bool success = SteamAPI_ISteamUGC_SetItemDescription(steam_ugc, handle, description); - JS_FreeCString(js, description); - - return JS_NewBool(js, success); -) - -JSC_CCALL(ugc_set_item_content, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double handle_double; - JS_ToFloat64(js, &handle_double, argv[0]); - UGCUpdateHandle_t handle = (UGCUpdateHandle_t)handle_double; - - const char *content_folder = JS_ToCString(js, argv[1]); - if (!content_folder) return JS_EXCEPTION; - - bool success = SteamAPI_ISteamUGC_SetItemContent(steam_ugc, handle, content_folder); - JS_FreeCString(js, content_folder); - - return JS_NewBool(js, success); -) - -JSC_CCALL(ugc_set_item_preview, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double handle_double; - JS_ToFloat64(js, &handle_double, argv[0]); - UGCUpdateHandle_t handle = (UGCUpdateHandle_t)handle_double; - - const char *preview_file = JS_ToCString(js, argv[1]); - if (!preview_file) return JS_EXCEPTION; - - bool success = SteamAPI_ISteamUGC_SetItemPreview(steam_ugc, handle, preview_file); - JS_FreeCString(js, preview_file); - - return JS_NewBool(js, success); -) - -JSC_CCALL(ugc_submit_item_update, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double handle_double; - JS_ToFloat64(js, &handle_double, argv[0]); - UGCUpdateHandle_t handle = (UGCUpdateHandle_t)handle_double; - - const char *change_note = argc > 1 ? JS_ToCString(js, argv[1]) : NULL; - - SteamAPICall_t call = SteamAPI_ISteamUGC_SubmitItemUpdate(steam_ugc, handle, change_note); - - if (change_note) JS_FreeCString(js, change_note); - - return JS_NewFloat64(js, (double)call); -) - -JSC_CCALL(ugc_get_item_update_progress, - if (!steam_ugc) return JS_ThrowInternalError(js, "Steam UGC not initialized"); - - double handle_double; - JS_ToFloat64(js, &handle_double, argv[0]); - UGCUpdateHandle_t handle = (UGCUpdateHandle_t)handle_double; - - uint64 bytes_processed, bytes_total; - - EItemUpdateStatus status = SteamAPI_ISteamUGC_GetItemUpdateProgress(steam_ugc, handle, &bytes_processed, &bytes_total); - - JSValue obj = JS_NewObject(js); - JS_SetPropertyStr(js, obj, "status", JS_NewUint32(js, status)); - JS_SetPropertyStr(js, obj, "bytes_processed", JS_NewFloat64(js, (double)bytes_processed)); - JS_SetPropertyStr(js, obj, "bytes_total", JS_NewFloat64(js, (double)bytes_total)); - return obj; -) - -// Function lists for sub-objects -static const JSCFunctionListEntry js_steam_stats_funcs[] = { - MIST_FUNC_DEF(stats, request, 0), - MIST_FUNC_DEF(stats, store, 0), - MIST_FUNC_DEF(stats, get_int, 1), - MIST_FUNC_DEF(stats, get_float, 1), - MIST_FUNC_DEF(stats, set_int, 2), - MIST_FUNC_DEF(stats, set_float, 2), - MIST_FUNC_DEF(stats, update_avg_rate, 3), - MIST_FUNC_DEF(stats, get_user_stat_int, 2), - MIST_FUNC_DEF(stats, get_user_stat_float, 2), - MIST_FUNC_DEF(stats, reset_all, 1), - MIST_FUNC_DEF(stats, get_number_of_current_players, 0), - MIST_FUNC_DEF(stats, request_global_achievement_percentages, 0), - MIST_FUNC_DEF(stats, request_global, 1), - MIST_FUNC_DEF(stats, get_global_stat_int64, 1), - MIST_FUNC_DEF(stats, get_global_stat_double, 1), - MIST_FUNC_DEF(stats, get_global_stat_history_int64, 1), - MIST_FUNC_DEF(stats, get_global_stat_history_double, 1), -}; - -static const JSCFunctionListEntry js_steam_achievement_funcs[] = { - MIST_FUNC_DEF(achievement, get, 1), - MIST_FUNC_DEF(achievement, set, 1), - MIST_FUNC_DEF(achievement, clear, 1), - MIST_FUNC_DEF(achievement, count, 0), - MIST_FUNC_DEF(achievement, name, 1), - MIST_FUNC_DEF(achievement, get_and_unlock_time, 1), - MIST_FUNC_DEF(achievement, get_icon, 1), - MIST_FUNC_DEF(achievement, get_display_attribute, 2), - MIST_FUNC_DEF(achievement, indicate_progress, 3), - MIST_FUNC_DEF(achievement, get_user_achievement, 2), - MIST_FUNC_DEF(achievement, get_user_achievement_and_unlock_time, 2), - MIST_FUNC_DEF(achievement, get_achieved_percent, 1), - MIST_FUNC_DEF(achievement, get_progress_limits_int, 1), - MIST_FUNC_DEF(achievement, get_progress_limits_float, 1), - MIST_FUNC_DEF(achievement, get_most_achieved_info, 0), - MIST_FUNC_DEF(achievement, get_next_most_achieved_info, 1), -}; - -static const JSCFunctionListEntry js_steam_leaderboard_funcs[] = { - MIST_FUNC_DEF(leaderboard, find_or_create, 3), - MIST_FUNC_DEF(leaderboard, find, 1), - MIST_FUNC_DEF(leaderboard, get_name, 1), - MIST_FUNC_DEF(leaderboard, get_entry_count, 1), - MIST_FUNC_DEF(leaderboard, get_sort_method, 1), - MIST_FUNC_DEF(leaderboard, get_display_type, 1), - MIST_FUNC_DEF(leaderboard, download_entries, 4), - MIST_FUNC_DEF(leaderboard, download_entries_for_users, 2), - MIST_FUNC_DEF(leaderboard, get_downloaded_entry, 2), - MIST_FUNC_DEF(leaderboard, upload_score, 3), - MIST_FUNC_DEF(leaderboard, attach_ugc, 2), -}; - -static const JSCFunctionListEntry js_steam_app_funcs[] = { - MIST_FUNC_DEF(app, id, 0), - MIST_FUNC_DEF(app, owner, 0), - MIST_FUNC_DEF(app, installed, 1), - MIST_FUNC_DEF(app, subscribed, 0), - MIST_FUNC_DEF(app, language, 0), - MIST_FUNC_DEF(app, dlc_installed, 1), - MIST_FUNC_DEF(app, is_low_violence, 0), - MIST_FUNC_DEF(app, is_cybercafe, 0), - MIST_FUNC_DEF(app, is_vac_banned, 0), - MIST_FUNC_DEF(app, available_languages, 0), - MIST_FUNC_DEF(app, is_subscribed_app, 1), - MIST_FUNC_DEF(app, earliest_purchase_time, 1), - MIST_FUNC_DEF(app, is_subscribed_from_free_weekend, 0), - MIST_FUNC_DEF(app, dlc_count, 0), - MIST_FUNC_DEF(app, get_dlc_data_by_index, 1), - MIST_FUNC_DEF(app, install_dlc, 1), - MIST_FUNC_DEF(app, uninstall_dlc, 1), - MIST_FUNC_DEF(app, request_proof_of_purchase_key, 1), - MIST_FUNC_DEF(app, get_current_beta_name, 0), - MIST_FUNC_DEF(app, mark_content_corrupt, 1), - MIST_FUNC_DEF(app, get_installed_depots, 1), - MIST_FUNC_DEF(app, get_install_dir, 1), - MIST_FUNC_DEF(app, get_launch_query_param, 1), - MIST_FUNC_DEF(app, get_dlc_download_progress, 1), - MIST_FUNC_DEF(app, get_build_id, 0), - MIST_FUNC_DEF(app, request_all_proof_of_purchase_keys, 0), - MIST_FUNC_DEF(app, get_file_details, 1), - MIST_FUNC_DEF(app, get_launch_command_line, 0), - MIST_FUNC_DEF(app, is_subscribed_from_family_sharing, 0), - MIST_FUNC_DEF(app, is_timed_trial, 0), - MIST_FUNC_DEF(app, set_dlc_context, 1), - MIST_FUNC_DEF(app, get_num_betas, 0), - MIST_FUNC_DEF(app, get_beta_info, 1), - MIST_FUNC_DEF(app, set_active_beta, 1), -}; - -static const JSCFunctionListEntry js_steam_user_funcs[] = { - MIST_FUNC_DEF(user, logged_on, 0), - MIST_FUNC_DEF(user, steam_id, 0), - MIST_FUNC_DEF(user, level, 0), - MIST_FUNC_DEF(user, hsteam_user, 0), - MIST_FUNC_DEF(user, get_user_data_folder, 0), - MIST_FUNC_DEF(user, start_voice_recording, 0), - MIST_FUNC_DEF(user, stop_voice_recording, 0), - MIST_FUNC_DEF(user, get_available_voice, 1), - MIST_FUNC_DEF(user, get_voice, 1), - MIST_FUNC_DEF(user, decompress_voice, 3), - MIST_FUNC_DEF(user, get_voice_optimal_sample_rate, 0), - MIST_FUNC_DEF(user, get_auth_session_ticket, 0), - MIST_FUNC_DEF(user, get_auth_ticket_for_web_api, 1), - MIST_FUNC_DEF(user, begin_auth_session, 2), - MIST_FUNC_DEF(user, end_auth_session, 1), - MIST_FUNC_DEF(user, cancel_auth_ticket, 1), - MIST_FUNC_DEF(user, has_license_for_app, 2), - MIST_FUNC_DEF(user, is_behind_nat, 0), - MIST_FUNC_DEF(user, advertise_game, 3), - MIST_FUNC_DEF(user, request_encrypted_app_ticket, 1), - MIST_FUNC_DEF(user, get_encrypted_app_ticket, 0), - MIST_FUNC_DEF(user, get_game_badge_level, 2), - MIST_FUNC_DEF(user, request_store_auth_url, 1), - MIST_FUNC_DEF(user, is_phone_verified, 0), - MIST_FUNC_DEF(user, is_two_factor_enabled, 0), - MIST_FUNC_DEF(user, is_phone_identifying, 0), - MIST_FUNC_DEF(user, is_phone_requiring_verification, 0), - MIST_FUNC_DEF(user, get_market_eligibility, 0), - MIST_FUNC_DEF(user, get_duration_control, 0), - MIST_FUNC_DEF(user, set_duration_control_online_state, 1), - MIST_FUNC_DEF(user, track_app_usage_event, 3), - MIST_FUNC_DEF(user, initiate_game_connection_deprecated, 4), - MIST_FUNC_DEF(user, terminate_game_connection_deprecated, 2), -}; - -static const JSCFunctionListEntry js_steam_friends_funcs[] = { - MIST_FUNC_DEF(friends, name, 0), - MIST_FUNC_DEF(friends, state, 0), - MIST_FUNC_DEF(friends, count, 1), - MIST_FUNC_DEF(friends, get_friend, 2), - MIST_FUNC_DEF(friends, get_friend_name, 1), - MIST_FUNC_DEF(friends, activate_overlay, 1), - MIST_FUNC_DEF(friends, activate_overlay_to_user, 2), - MIST_FUNC_DEF(friends, activate_overlay_to_web, 1), - MIST_FUNC_DEF(friends, set_rich_presence, 2), - MIST_FUNC_DEF(friends, clear_rich_presence, 0), -}; - -static const JSCFunctionListEntry js_steam_cloud_funcs[] = { - MIST_FUNC_DEF(cloud, enabled_app, 0), - MIST_FUNC_DEF(cloud, enabled_account, 0), - MIST_FUNC_DEF(cloud, enable, 1), - MIST_FUNC_DEF(cloud, quota, 0), - MIST_FUNC_DEF(cloud, write, 2), - MIST_FUNC_DEF(cloud, read, 1), - MIST_FUNC_DEF(cloud, delete, 1), - MIST_FUNC_DEF(cloud, exists, 1), -}; - -static const JSCFunctionListEntry js_steam_utils_funcs[] = { - MIST_FUNC_DEF(utils, overlay_enabled, 0), - MIST_FUNC_DEF(utils, big_picture_mode, 0), - MIST_FUNC_DEF(utils, vr_mode, 0), - MIST_FUNC_DEF(utils, steam_deck, 0), - MIST_FUNC_DEF(utils, battery_power, 0), - MIST_FUNC_DEF(utils, seconds_since_app_active, 0), - MIST_FUNC_DEF(utils, ip_country, 0), - MIST_FUNC_DEF(utils, ui_language, 0), - MIST_FUNC_DEF(utils, get_seconds_since_computer_active, 0), - MIST_FUNC_DEF(utils, get_connected_universe, 0), - MIST_FUNC_DEF(utils, get_server_real_time, 0), - MIST_FUNC_DEF(utils, get_image_size, 1), - MIST_FUNC_DEF(utils, get_image_rgba, 1), - MIST_FUNC_DEF(utils, set_overlay_notification_position, 1), - MIST_FUNC_DEF(utils, is_api_call_completed, 1), - MIST_FUNC_DEF(utils, get_api_call_failure_reason, 1), - MIST_FUNC_DEF(utils, get_ipc_call_count, 0), - MIST_FUNC_DEF(utils, overlay_needs_present, 0), - MIST_FUNC_DEF(utils, check_file_signature, 1), - MIST_FUNC_DEF(utils, show_gamepad_text_input, 5), - MIST_FUNC_DEF(utils, get_entered_gamepad_text_length, 0), - MIST_FUNC_DEF(utils, get_entered_gamepad_text_input, 0), - MIST_FUNC_DEF(utils, set_overlay_notification_inset, 2), - MIST_FUNC_DEF(utils, start_vr_dashboard, 0), - MIST_FUNC_DEF(utils, is_vr_headset_streaming_enabled, 0), - MIST_FUNC_DEF(utils, set_vr_headset_streaming_enabled, 1), - MIST_FUNC_DEF(utils, is_steam_china_launcher, 0), - MIST_FUNC_DEF(utils, init_filter_text, 1), - MIST_FUNC_DEF(utils, filter_text, 3), - MIST_FUNC_DEF(utils, get_ipv6_connectivity_state, 1), - MIST_FUNC_DEF(utils, show_floating_gamepad_text_input, 5), - MIST_FUNC_DEF(utils, set_game_launcher_mode, 1), - MIST_FUNC_DEF(utils, dismiss_floating_gamepad_text_input, 0), - MIST_FUNC_DEF(utils, dismiss_gamepad_text_input, 0), -}; - -static const JSCFunctionListEntry js_steam_screenshots_funcs[] = { - MIST_FUNC_DEF(screenshots, write, 4), - MIST_FUNC_DEF(screenshots, trigger, 0), -}; - -static const JSCFunctionListEntry js_steam_networking_funcs[] = { - MIST_FUNC_DEF(networking, send_p2p, 4), - MIST_FUNC_DEF(networking, read_p2p, 1), - MIST_FUNC_DEF(networking, accept_p2p, 1), - MIST_FUNC_DEF(networking, close_p2p, 2), - MIST_FUNC_DEF(networking, close_p2p_channel, 2), - MIST_FUNC_DEF(networking, p2p_session_state, 1), -}; - -static const JSCFunctionListEntry js_steam_matchmaking_funcs[] = { - MIST_FUNC_DEF(matchmaking, create_lobby, 2), - MIST_FUNC_DEF(matchmaking, join_lobby, 1), - MIST_FUNC_DEF(matchmaking, leave_lobby, 1), - MIST_FUNC_DEF(matchmaking, invite_user, 2), - MIST_FUNC_DEF(matchmaking, lobby_member_count, 1), - MIST_FUNC_DEF(matchmaking, lobby_member_by_index, 2), - MIST_FUNC_DEF(matchmaking, lobby_owner, 1), - MIST_FUNC_DEF(matchmaking, set_lobby_data, 3), - MIST_FUNC_DEF(matchmaking, get_lobby_data, 2), - MIST_FUNC_DEF(matchmaking, set_lobby_member_data, 3), - MIST_FUNC_DEF(matchmaking, get_lobby_member_data, 3), - MIST_FUNC_DEF(matchmaking, send_lobby_chat_msg, 2), -}; - -static const JSCFunctionListEntry js_steam_ugc_funcs[] = { - MIST_FUNC_DEF(ugc, create_query_user_ugc_request, 7), - MIST_FUNC_DEF(ugc, create_query_all_ugc_request_page, 5), - MIST_FUNC_DEF(ugc, create_query_ugc_details_request, 1), - MIST_FUNC_DEF(ugc, send_query_ugc_request, 1), - MIST_FUNC_DEF(ugc, release_query_ugc_request, 1), - MIST_FUNC_DEF(ugc, add_required_tag, 2), - MIST_FUNC_DEF(ugc, add_excluded_tag, 2), - MIST_FUNC_DEF(ugc, set_search_text, 2), - MIST_FUNC_DEF(ugc, set_language, 2), - MIST_FUNC_DEF(ugc, subscribe_item, 1), - MIST_FUNC_DEF(ugc, unsubscribe_item, 1), - MIST_FUNC_DEF(ugc, get_num_subscribed_items, 1), - MIST_FUNC_DEF(ugc, get_subscribed_items, 2), - MIST_FUNC_DEF(ugc, get_item_state, 1), - MIST_FUNC_DEF(ugc, get_item_install_info, 1), - MIST_FUNC_DEF(ugc, get_item_download_info, 1), - MIST_FUNC_DEF(ugc, download_item, 2), - MIST_FUNC_DEF(ugc, create_item, 2), - MIST_FUNC_DEF(ugc, start_item_update, 2), - MIST_FUNC_DEF(ugc, set_item_title, 2), - MIST_FUNC_DEF(ugc, set_item_description, 2), - MIST_FUNC_DEF(ugc, set_item_content, 2), - MIST_FUNC_DEF(ugc, set_item_preview, 2), - MIST_FUNC_DEF(ugc, submit_item_update, 2), - MIST_FUNC_DEF(ugc, get_item_update_progress, 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), -}; - -JSValue js_steam_use(JSContext *js) { - // Register SteamID class if not already done - static int steamid_class_initialized = 0; - if (!steamid_class_initialized) { - JS_NewClassID(&js_steamid_id); - JS_NewClass(JS_GetRuntime(js), js_steamid_id, &js_steamid_class); - - JSValue steamid_proto = JS_NewObject(js); - JS_SetPropertyFunctionList(js, steamid_proto, js_steamid_proto_funcs, countof(js_steamid_proto_funcs)); - JS_SetClassProto(js, js_steamid_id, steamid_proto); - - steamid_class_initialized = 1; - } - - 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); - - // Leaderboard sub-object - JSValue leaderboard = JS_NewObject(js); - JS_SetPropertyFunctionList(js, leaderboard, js_steam_leaderboard_funcs, countof(js_steam_leaderboard_funcs)); - JS_SetPropertyStr(js, steam, "leaderboard", leaderboard); - - // 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); - - // Utils sub-object - JSValue utils = JS_NewObject(js); - JS_SetPropertyFunctionList(js, utils, js_steam_utils_funcs, countof(js_steam_utils_funcs)); - JS_SetPropertyStr(js, steam, "utils", utils); - - // Screenshots sub-object - JSValue screenshots = JS_NewObject(js); - JS_SetPropertyFunctionList(js, screenshots, js_steam_screenshots_funcs, countof(js_steam_screenshots_funcs)); - JS_SetPropertyStr(js, steam, "screenshots", screenshots); - - // Networking sub-object - JSValue networking = JS_NewObject(js); - JS_SetPropertyFunctionList(js, networking, js_steam_networking_funcs, countof(js_steam_networking_funcs)); - JS_SetPropertyStr(js, steam, "networking", networking); - - // Matchmaking sub-object - JSValue matchmaking = JS_NewObject(js); - JS_SetPropertyFunctionList(js, matchmaking, js_steam_matchmaking_funcs, countof(js_steam_matchmaking_funcs)); - JS_SetPropertyStr(js, steam, "matchmaking", matchmaking); - - // UGC sub-object - JSValue ugc = JS_NewObject(js); - JS_SetPropertyFunctionList(js, ugc, js_steam_ugc_funcs, countof(js_steam_ugc_funcs)); - JS_SetPropertyStr(js, steam, "ugc", ugc); - - return steam; -} - -} // extern "C" - -#else -// Stub when Steam is disabled -extern "C" JSValue js_steam_use(JSContext *js) { - return JS_NULL; -} -#endif diff --git a/source/qjs_steam.h b/source/qjs_steam.h deleted file mode 100644 index 94e4e7db..00000000 --- a/source/qjs_steam.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef QJS_STEAM_H -#define QJS_STEAM_H - -#include "cell.h" - -#ifdef __cplusplus -extern "C" { -#endif - -JSValue js_steam_use(JSContext *js); - -#ifdef __cplusplus -} -#endif - -#endif // QJS_STEAM_H \ No newline at end of file diff --git a/subprojects/mbedtls.wrap b/subprojects/mbedtls.wrap deleted file mode 100644 index 1352a20a..00000000 --- a/subprojects/mbedtls.wrap +++ /dev/null @@ -1,4 +0,0 @@ -[wrap-git] -url = https://github.com/Mbed-TLS/mbedtls.git -revision = v3.6.3.1 -depth = 1 diff --git a/tests/http.ce b/tests/http.ce index 93521b09..a8fe6d37 100644 --- a/tests/http.ce +++ b/tests/http.ce @@ -1,15 +1,19 @@ var http = use('http') var text = use('text') var time = use('time') +var io = use('io') try { var st = time.number() var b2 = http.fetch(arg[0]) log.console(`time took ${time.number()-st}`) log.console(b2.length) - var text2 = text(b2) - log.console(text(b2)) - $_.stop() + io.writepath('.') + io.slurpwrite("download.zip", b2) } catch (e) { - log.console("dictionary error:", e) + log.console("error:", e) + log.console("message:", e.message) + log.console("stack:", e.stack) +} finally { + $_.stop() } \ No newline at end of file