/* ** binding-sandbox.cpp ** ** This file is part of mkxp. ** ** Copyright (C) 2013 - 2021 Amaryllis Kulla ** ** mkxp is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 2 of the License, or ** (at your option) any later version. ** ** mkxp is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with mkxp. If not, see . */ #include "binding-sandbox.h" #include "mkxp-polyfill.h" // std::strtol #include #include #include #include "sharedstate.h" #include "core.h" #include "encoding.h" #include "audio-binding.h" #include "bitmap-binding.h" #include "etc-binding.h" #include "font-binding.h" #include "graphics-binding.h" #include "input-binding.h" #include "plane-binding.h" #include "sprite-binding.h" #include "table-binding.h" #include "tilemap-binding.h" #include "tilemapvx-binding.h" #include "viewport-binding.h" #include "window-binding.h" #include "windowvx-binding.h" using namespace mkxp_sandbox; extern const char module_rpg1[]; extern const char module_rpg2[]; extern const char module_rpg3[]; static VALUE utf8_encoding; struct eval_script : boost::asio::coroutine { typedef decl_slots slots; void operator()(VALUE string, VALUE filename) { BOOST_ASIO_CORO_REENTER (this) { SANDBOX_AWAIT_S(0, rb_intern, "eval"); SANDBOX_AWAIT(rb_funcall, SANDBOX_NIL, SANDBOX_SLOT(0), 3, string, SANDBOX_NIL, filename); } } }; static VALUE load_data(int32_t argc, wasm_ptr_t argv, VALUE self) { struct coro : boost::asio::coroutine { typedef decl_slots slots; VALUE operator()(int32_t argc, wasm_ptr_t argv, VALUE self) { BOOST_ASIO_CORO_REENTER (this) { // TODO: require at least one argument SANDBOX_AWAIT_S(0, rb_string_value_cstr, &sb()->ref(argv, 0)); SANDBOX_AWAIT_S(1, rb_file_open, sb()->str(SANDBOX_SLOT(0)), "rb"); if (argc < 2 || !SANDBOX_VALUE_TO_BOOL(sb()->ref(argv, 1))) { SANDBOX_AWAIT_S(2, rb_marshal_load, SANDBOX_SLOT(1)); } else { SANDBOX_AWAIT_S(3, rb_intern, "read"); SANDBOX_AWAIT_S(2, rb_funcall, SANDBOX_SLOT(1), SANDBOX_SLOT(3), 0); } SANDBOX_AWAIT(rb_io_close, SANDBOX_SLOT(1)); } return SANDBOX_SLOT(2); } }; return sb()->bind()()(argc, argv, self); } static VALUE save_data(VALUE self, VALUE obj, VALUE filename) { struct coro : boost::asio::coroutine { typedef decl_slots slots; VALUE operator()(VALUE self, VALUE obj, VALUE filename) { BOOST_ASIO_CORO_REENTER (this) { SANDBOX_AWAIT_S(0, rb_string_value_cstr, &filename); SANDBOX_AWAIT_S(1, rb_file_open, sb()->str(SANDBOX_SLOT(0)), "wb"); SANDBOX_AWAIT(rb_marshal_dump, obj, SANDBOX_SLOT(1)); SANDBOX_AWAIT(rb_io_close, SANDBOX_SLOT(1)); } return SANDBOX_NIL; } }; return sb()->bind()()(self, obj, filename); } static VALUE rgss_main(VALUE self) { struct coro : boost::asio::coroutine { static VALUE func(VALUE block) { struct coro : boost::asio::coroutine { typedef decl_slots slots; VALUE operator()(VALUE block) { BOOST_ASIO_CORO_REENTER (this) { SANDBOX_AWAIT_S(0, rb_intern, "call"); SANDBOX_AWAIT(rb_funcall, block, SANDBOX_SLOT(0), 0); } return SANDBOX_UNDEF; } }; return sb()->bind()()(block); } static VALUE rescue(VALUE arg, VALUE exception) { return exception; } typedef decl_slots slots; VALUE operator()(VALUE self) { BOOST_ASIO_CORO_REENTER (this) { while (true) { SANDBOX_AWAIT_S(0, rb_block_proc); SANDBOX_AWAIT_S(1, rb_rescue2, func, SANDBOX_SLOT(0), rescue, SANDBOX_NIL, sb()->rb_eException(), 0); if (SANDBOX_SLOT(1) == SANDBOX_UNDEF) { return SANDBOX_NIL; } SANDBOX_AWAIT_S(0, rb_gv_get, "$!"); SANDBOX_AWAIT_S(0, rb_obj_class, SANDBOX_SLOT(0)); if (SANDBOX_SLOT(0) == reset_class) { SANDBOX_AWAIT(audio_reset); SANDBOX_AWAIT(graphics_reset); } else { SANDBOX_AWAIT(rb_exc_raise, SANDBOX_SLOT(1)); } } } return SANDBOX_NIL; } }; return sb()->bind()()(self); } static VALUE rgss_stop(VALUE self) { struct coro : boost::asio::coroutine { typedef decl_slots slots; VALUE operator()(VALUE self) { BOOST_ASIO_CORO_REENTER (this) { while (true) { SANDBOX_GUARD_L(SANDBOX_SLOT(0) = shState->graphics().update(sb().e)); if (SANDBOX_SLOT(0)) { SANDBOX_YIELD; } } } return SANDBOX_NIL; } }; return sb()->bind()()(self); } static VALUE delta(VALUE self) { return sb()->bind()()(shState->runTime()); } static VALUE data_directory(VALUE self) { return sb()->bind()()("/Save"); } static VALUE get_window_title(VALUE self) { return sb()->bind()()(""); } static VALUE set_window_title(VALUE self, VALUE value) { return value; } static VALUE show_settings(VALUE self) { return SANDBOX_NIL; } static VALUE desensitize(VALUE self, VALUE value) { struct coro : boost::asio::coroutine { typedef decl_slots slots; VALUE operator()(VALUE self, VALUE value) { BOOST_ASIO_CORO_REENTER (this) { SANDBOX_AWAIT_S(0, rb_string_value_cstr, &value); SANDBOX_AWAIT_S(1, rb_str_new_cstr, mkxp_retro::fs->desensitize(sb()->str(SANDBOX_SLOT(0)))); } return SANDBOX_SLOT(1); } }; return sb()->bind()()(self, value); } static VALUE platform(VALUE self) { return sb()->bind()()("libretro"); } static VALUE is_not_libretro(VALUE self) { return SANDBOX_FALSE; } static VALUE is_libretro(VALUE self) { return SANDBOX_TRUE; } static VALUE user_language(VALUE self) { const char *str; enum retro_language language; if (!mkxp_retro::environment(RETRO_ENVIRONMENT_GET_LANGUAGE, &language)) { language = RETRO_LANGUAGE_ENGLISH; } switch (language) { default: case RETRO_LANGUAGE_ENGLISH: str = "en_US"; break; case RETRO_LANGUAGE_JAPANESE: str = "ja_JP"; break; case RETRO_LANGUAGE_FRENCH: str = "fr_FR"; break; case RETRO_LANGUAGE_SPANISH: str = "es_ES"; break; case RETRO_LANGUAGE_GERMAN: str = "de_DE"; break; case RETRO_LANGUAGE_ITALIAN: str = "it_IT"; break; case RETRO_LANGUAGE_DUTCH: str = "nl_NL"; break; case RETRO_LANGUAGE_PORTUGUESE_BRAZIL: str = "pt_BR"; break; case RETRO_LANGUAGE_PORTUGUESE_PORTUGAL: str = "pt_PT"; break; case RETRO_LANGUAGE_RUSSIAN: str = "ru_RU"; break; case RETRO_LANGUAGE_KOREAN: str = "ko_KR"; break; case RETRO_LANGUAGE_CHINESE_TRADITIONAL: str = "zh_TW"; break; case RETRO_LANGUAGE_CHINESE_SIMPLIFIED: str = "zh_CN"; break; case RETRO_LANGUAGE_ESPERANTO: str = "eo"; break; case RETRO_LANGUAGE_POLISH: str = "pl_PL"; break; case RETRO_LANGUAGE_VIETNAMESE: str = "vi_VN"; break; case RETRO_LANGUAGE_ARABIC: str = "ar_SA"; break; case RETRO_LANGUAGE_GREEK: str = "el_GR"; break; case RETRO_LANGUAGE_TURKISH: str = "tr_TR"; break; case RETRO_LANGUAGE_SLOVAK: str = "sk_SK"; break; case RETRO_LANGUAGE_PERSIAN: str = "fa_IR"; break; case RETRO_LANGUAGE_HEBREW: str = "he_IL"; break; case RETRO_LANGUAGE_ASTURIAN: str = "ast_ES"; break; case RETRO_LANGUAGE_FINNISH: str = "fi_FI"; break; case RETRO_LANGUAGE_INDONESIAN: str = "id_ID"; break; case RETRO_LANGUAGE_SWEDISH: str = "sv_SE"; break; case RETRO_LANGUAGE_UKRAINIAN: str = "uk_UA"; break; case RETRO_LANGUAGE_CZECH: str = "cs_CZ"; break; case RETRO_LANGUAGE_CATALAN_VALENCIA: str = "ca_ES"; break; case RETRO_LANGUAGE_CATALAN: str = "ca_AD"; break; case RETRO_LANGUAGE_BRITISH_ENGLISH: str = "en_GB"; break; case RETRO_LANGUAGE_HUNGARIAN: str = "hu_HU"; break; case RETRO_LANGUAGE_BELARUSIAN: str = "be_BY"; break; case RETRO_LANGUAGE_GALICIAN: str = "gl_ES"; break; case RETRO_LANGUAGE_NORWEGIAN: str = "no_NO"; break; case RETRO_LANGUAGE_IRISH: str = "ga_IE"; break; } return sb()->bind()()(str); } static VALUE user_name(VALUE self) { const char *str; if (!mkxp_retro::environment(RETRO_ENVIRONMENT_GET_USERNAME, &str) || str == nullptr) { str = ""; } return sb()->bind()()(str); } static VALUE game_title(VALUE self) { return sb()->bind()()(shState->config().game.title.c_str()); } static VALUE power_state(VALUE self) { struct coro : boost::asio::coroutine { typedef decl_slots slots; VALUE operator()(VALUE self) { BOOST_ASIO_CORO_REENTER (this) { if (!mkxp_retro::environment(RETRO_ENVIRONMENT_GET_DEVICE_POWER, &sb().device_power)) { sb().device_power.state = RETRO_POWERSTATE_UNKNOWN; sb().device_power.seconds = RETRO_POWERSTATE_NO_ESTIMATE; sb().device_power.percent = RETRO_POWERSTATE_NO_ESTIMATE; } SANDBOX_AWAIT_S(0, rb_hash_new); SANDBOX_AWAIT_S(3, rb_intern, "seconds"); SANDBOX_AWAIT_S(1, rb_id2sym, SANDBOX_SLOT(3)); if (sb().device_power.seconds != RETRO_POWERSTATE_NO_ESTIMATE) { SANDBOX_AWAIT_S(2, rb_ll2inum, sb().device_power.seconds); } else { SANDBOX_SLOT(2) = SANDBOX_NIL; } SANDBOX_AWAIT(rb_hash_aset, SANDBOX_SLOT(0), SANDBOX_SLOT(1), SANDBOX_SLOT(2)); SANDBOX_AWAIT_S(3, rb_intern, "percent"); SANDBOX_AWAIT_S(1, rb_id2sym, SANDBOX_SLOT(3)); if (sb().device_power.percent != RETRO_POWERSTATE_NO_ESTIMATE) { SANDBOX_AWAIT_S(2, rb_ll2inum, sb().device_power.percent); } else { SANDBOX_SLOT(2) = SANDBOX_NIL; } SANDBOX_AWAIT(rb_hash_aset, SANDBOX_SLOT(0), SANDBOX_SLOT(1), SANDBOX_SLOT(2)); SANDBOX_AWAIT_S(3, rb_intern, "discharging"); SANDBOX_AWAIT_S(1, rb_id2sym, SANDBOX_SLOT(3)); if (sb().device_power.state != RETRO_POWERSTATE_UNKNOWN) { SANDBOX_SLOT(2) = SANDBOX_BOOL_TO_VALUE(sb().device_power.state == RETRO_POWERSTATE_DISCHARGING); } else { SANDBOX_SLOT(2) = SANDBOX_NIL; } SANDBOX_AWAIT(rb_hash_aset, SANDBOX_SLOT(0), SANDBOX_SLOT(1), SANDBOX_SLOT(2)); } return SANDBOX_SLOT(0); } }; return sb()->bind()()(self); } static VALUE nproc(VALUE self) { return sb()->bind()()(1); } static VALUE memory(VALUE self) { return sb()->bind()()(0); } static VALUE reload_cache(VALUE self) { mkxp_retro::fs->reloadPathCache(); return SANDBOX_NIL; } static VALUE mount(int32_t argc, wasm_ptr_t argv, VALUE self) { sb()->bind()()(Exception(Exception::PHYSFSError, "Failed to mount (operation not supported in libretro builds)")); return SANDBOX_NIL; } static VALUE unmount(int32_t argc, wasm_ptr_t argv, VALUE self) { sb()->bind()()(Exception(Exception::PHYSFSError, "Failed to unmount (operation not supported in libretro builds)")); return SANDBOX_NIL; } static VALUE file_exists(VALUE self, VALUE value) { struct coro : boost::asio::coroutine { typedef decl_slots slots; VALUE operator()(VALUE self, VALUE value) { BOOST_ASIO_CORO_REENTER (this) { SANDBOX_AWAIT_S(0, rb_string_value_cstr, &value); return SANDBOX_BOOL_TO_VALUE(mkxp_retro::fs->exists(sb()->str(SANDBOX_SLOT(0)))); } return SANDBOX_NIL; } }; return sb()->bind()()(self, value); } static VALUE launch(VALUE self, VALUE cmdname, VALUE args) { sb()->bind()()(Exception(Exception::MKXPError, "Failed to launch (operation not supported in libretro builds)")); return SANDBOX_NIL; } static VALUE default_font_family(VALUE self, VALUE value) { struct coro : boost::asio::coroutine { typedef decl_slots slots; VALUE operator()(VALUE self, VALUE value) { BOOST_ASIO_CORO_REENTER (this) { SANDBOX_AWAIT_S(0, rb_string_value_cstr, &value); shState->fontState().setDefaultFontFamily(std::string(sb()->str(SANDBOX_SLOT(0)))); } return SANDBOX_NIL; } }; return sb()->bind()()(self, value); } static VALUE to_utf8(VALUE self) { struct coro : boost::asio::coroutine { typedef decl_slots slots; VALUE operator()(VALUE self) { BOOST_ASIO_CORO_REENTER (this) { SANDBOX_AWAIT_S(0, rb_string_value_cstr, &self); SANDBOX_AWAIT_S(1, rb_str_new_cstr, Encoding::convertString((const char *)sb()->str(SANDBOX_SLOT(0))).c_str()); } return SANDBOX_SLOT(1); } }; return sb()->bind()()(self); } static VALUE to_utf8_bang(VALUE self) { struct coro : boost::asio::coroutine { typedef decl_slots slots; VALUE operator()(VALUE self) { BOOST_ASIO_CORO_REENTER (this) { SANDBOX_AWAIT_S(0, rb_string_value_cstr, &self); sb().convert_string_buffer = Encoding::convertString((const char *)sb()->str(SANDBOX_SLOT(0))); SANDBOX_AWAIT(rb_str_resize, self, sb().convert_string_buffer.length()); SANDBOX_AWAIT_S(0, rb_string_value_cstr, &self); sb()->strcpy(SANDBOX_SLOT(0), sb().convert_string_buffer.c_str()); SANDBOX_AWAIT_S(1, rb_intern, "force_encoding"); SANDBOX_AWAIT(rb_funcall, self, SANDBOX_SLOT(1), 1, utf8_encoding); } return self; } void end() noexcept { sb().convert_string_buffer.clear(); } }; return sb()->bind()()(self); } void sandbox_binding_init::operator()() { static VALUE system_module; static VALUE cfg_module; struct register_ruby_revision : boost::asio::coroutine { typedef decl_slots slots; void operator()() { BOOST_ASIO_CORO_REENTER (this) { SANDBOX_AWAIT_S(2, rb_intern, "RUBY_REVISION"); SANDBOX_AWAIT_S(1, rb_const_get, sb()->rb_cObject(), SANDBOX_SLOT(2)); SANDBOX_AWAIT_S(0, rb_string_value_cstr, &SANDBOX_SLOT(1)); struct sandbox_str_guard str = sb()->str(SANDBOX_SLOT(0)); if (std::strlen(str) != 2 * sizeof mkxp_retro::ruby_revision) { std::abort(); } for (size_t i = 0; i < sizeof mkxp_retro::ruby_revision; ++i) { mkxp_retro::ruby_revision[i] = std::strtol(std::string(str + 2 * i, 2).c_str(), nullptr, 16); } } } }; struct register_utf8_encoding : boost::asio::coroutine { typedef decl_slots slots; void operator()() { BOOST_ASIO_CORO_REENTER (this) { SANDBOX_AWAIT_S(0, rb_intern, "UTF_8"); SANDBOX_AWAIT_R(utf8_encoding, rb_const_get, sb()->rb_cEncoding(), SANDBOX_SLOT(0)); } } }; BOOST_ASIO_CORO_REENTER (this) { SANDBOX_AWAIT(register_ruby_revision); SANDBOX_AWAIT(register_utf8_encoding); SANDBOX_AWAIT(exception_binding_init); SANDBOX_AWAIT(table_binding_init); SANDBOX_AWAIT(etc_binding_init); SANDBOX_AWAIT(font_binding_init); SANDBOX_AWAIT(bitmap_binding_init); SANDBOX_AWAIT(sprite_binding_init); SANDBOX_AWAIT(viewport_binding_init); SANDBOX_AWAIT(plane_binding_init); if (rgssVer == 1) { SANDBOX_AWAIT(window_binding_init); SANDBOX_AWAIT(tilemap_binding_init); } else { SANDBOX_AWAIT(windowvx_binding_init); SANDBOX_AWAIT(tilemapvx_binding_init); } SANDBOX_AWAIT(input_binding_init); SANDBOX_AWAIT(audio_binding_init); SANDBOX_AWAIT(graphics_binding_init); SANDBOX_AWAIT(rb_define_module_function, sb()->rb_mKernel(), "load_data", (VALUE (*)(ANYARGS))load_data, -1); SANDBOX_AWAIT(rb_define_module_function, sb()->rb_mKernel(), "save_data", (VALUE (*)(ANYARGS))save_data, 2); if (rgssVer >= 3) { SANDBOX_AWAIT(rb_define_module_function, sb()->rb_mKernel(), "rgss_main", (VALUE (*)(ANYARGS))rgss_main, 0); SANDBOX_AWAIT(rb_define_module_function, sb()->rb_mKernel(), "rgss_stop", (VALUE (*)(ANYARGS))rgss_stop, 0); } if (rgssVer == 1) { SANDBOX_AWAIT(rb_eval_string, module_rpg1); } else if (rgssVer == 2) { SANDBOX_AWAIT(rb_eval_string, module_rpg2); } else if (rgssVer == 3) { SANDBOX_AWAIT(rb_eval_string, module_rpg3); } else { std::abort(); } SANDBOX_AWAIT_R(system_module, rb_define_module, "System"); SANDBOX_AWAIT(rb_define_module_function, system_module, "delta", (VALUE (*)(ANYARGS))delta, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "uptime", (VALUE (*)(ANYARGS))delta, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "data_directory", (VALUE (*)(ANYARGS))data_directory, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "set_window_title", (VALUE (*)(ANYARGS))set_window_title, 1); SANDBOX_AWAIT(rb_define_module_function, system_module, "window_title", (VALUE (*)(ANYARGS))get_window_title, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "window_title=", (VALUE (*)(ANYARGS))set_window_title, 1); SANDBOX_AWAIT(rb_define_module_function, system_module, "show_settings", (VALUE (*)(ANYARGS))show_settings, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "desensitize", (VALUE (*)(ANYARGS))desensitize, 1); SANDBOX_AWAIT(rb_define_module_function, system_module, "platform", (VALUE (*)(ANYARGS))platform, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "is_mac?", (VALUE (*)(ANYARGS))is_not_libretro, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "is_rosetta?", (VALUE (*)(ANYARGS))is_not_libretro, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "is_linux?", (VALUE (*)(ANYARGS))is_not_libretro, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "is_windows?", (VALUE (*)(ANYARGS))is_not_libretro, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "is_wine?", (VALUE (*)(ANYARGS))is_not_libretro, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "is_really_mac?", (VALUE (*)(ANYARGS))is_not_libretro, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "is_really_linux?", (VALUE (*)(ANYARGS))is_not_libretro, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "is_really_windows?", (VALUE (*)(ANYARGS))is_not_libretro, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "is_libretro?", (VALUE (*)(ANYARGS))is_libretro, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "user_language", (VALUE (*)(ANYARGS))user_language, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "user_name", (VALUE (*)(ANYARGS))user_name, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "game_title", (VALUE (*)(ANYARGS))game_title, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "power_state", (VALUE (*)(ANYARGS))power_state, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "nproc", (VALUE (*)(ANYARGS))nproc, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "memory", (VALUE (*)(ANYARGS))memory, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "reload_cache", (VALUE (*)(ANYARGS))reload_cache, 0); SANDBOX_AWAIT(rb_define_module_function, system_module, "mount", (VALUE (*)(ANYARGS))mount, -1); SANDBOX_AWAIT(rb_define_module_function, system_module, "unmount", (VALUE (*)(ANYARGS))unmount, -1); SANDBOX_AWAIT(rb_define_module_function, system_module, "file_exists?", (VALUE (*)(ANYARGS))file_exists, 1); SANDBOX_AWAIT(rb_define_module_function, system_module, "launch", (VALUE (*)(ANYARGS))launch, 2); SANDBOX_AWAIT(rb_define_module_function, system_module, "default_font_family=", (VALUE (*)(ANYARGS))default_font_family, 1); SANDBOX_AWAIT(rb_define_method, sb()->rb_cString(), "to_utf8", (VALUE (*)(ANYARGS))to_utf8, 0); SANDBOX_AWAIT(rb_define_method, sb()->rb_cString(), "to_utf8!", (VALUE (*)(ANYARGS))to_utf8_bang, 0); SANDBOX_AWAIT_R(cfg_module, rb_define_module, "CFG"); // TODO //SANDBOX_AWAIT(rb_define_module_function, cfg_module, "[]", (VALUE (*)(ANYARGS))get_json_setting, 1); //SANDBOX_AWAIT(rb_define_module_function, cfg_module, "[]=", (VALUE (*)(ANYARGS))set_json_setting, 2); //SANDBOX_AWAIT(rb_define_module_function, cfg_module, "to_hash", (VALUE (*)(ANYARGS))get_all_json_settings, 0); } } void sandbox_run_rmxp_scripts::operator()() { BOOST_ASIO_CORO_REENTER (this) { // Unmarshal the script array into slot 1 if (rgssVer == 1) { SANDBOX_AWAIT_S(0, rb_file_open, "Data/Scripts.rxdata", "rb"); } else if (rgssVer == 2) { SANDBOX_AWAIT_S(0, rb_file_open, "Data/Scripts.rvdata", "rb"); } else if (rgssVer == 3) { SANDBOX_AWAIT_S(0, rb_file_open, "Data/Scripts.rvdata2", "rb"); } else { std::abort(); } SANDBOX_AWAIT_S(1, rb_marshal_load, SANDBOX_SLOT(0)); SANDBOX_AWAIT(rb_io_close, SANDBOX_SLOT(0)); // Assign it to the "$RGSS_SCRIPTS" global variable SANDBOX_AWAIT(rb_gv_set, "$RGSS_SCRIPTS", SANDBOX_SLOT(1)); // Get the number of scripts into slot 2 SANDBOX_AWAIT_S(2, get_length, SANDBOX_SLOT(1)); sb().script_decode_buffer.resize(0x1000); for (SANDBOX_SLOT(3) = 0; SANDBOX_SLOT(3) < SANDBOX_SLOT(2); ++SANDBOX_SLOT(3)) { // Get the script into slot 4 SANDBOX_AWAIT_S(4, rb_ary_entry, SANDBOX_SLOT(1), SANDBOX_SLOT(3)); // Skip this script object if it's not an array SANDBOX_AWAIT_S(0, rb_obj_is_kind_of, SANDBOX_SLOT(4), sb()->rb_cArray()); if (!SANDBOX_VALUE_TO_BOOL(SANDBOX_SLOT(0))) { continue; } // Get the name of the script, the zlib-compressed script contents of the script and the length of said contents into slots 5, 6 and 7, respectively SANDBOX_AWAIT_S(0, rb_ary_entry, SANDBOX_SLOT(4), 1); SANDBOX_AWAIT_S(5, rb_string_value_cstr, &SANDBOX_SLOT(0)); SANDBOX_AWAIT_S(0, rb_ary_entry, SANDBOX_SLOT(4), 2); SANDBOX_AWAIT_S(6, rb_string_value_ptr, &SANDBOX_SLOT(0)); SANDBOX_AWAIT_S(7, get_bytesize, SANDBOX_SLOT(0)); // Decompress the script contents { int zlib_result = Z_OK; unsigned long buffer_len = sb().script_decode_buffer.size() - 1; while (true) { zlib_result = uncompress( sb().script_decode_buffer.data(), &buffer_len, (const unsigned char *)(const char *)sb()->str(SANDBOX_SLOT(6)), SANDBOX_SLOT(7) ); sb().script_decode_buffer[buffer_len] = 0; if (zlib_result != Z_BUF_ERROR) { break; } if (2UL * (unsigned long)sb().script_decode_buffer.size() <= (unsigned long)sb().script_decode_buffer.size()) { MKXPZ_THROW(std::bad_alloc()); } sb().script_decode_buffer.resize(2 * sb().script_decode_buffer.size()); buffer_len = sb().script_decode_buffer.size() - 1; } if (zlib_result != Z_OK) { mkxp_retro::log_printf(RETRO_LOG_ERROR, "Error decoding script %zu: '%s'\n", SANDBOX_SLOT(3), (const char *)sb()->str(SANDBOX_SLOT(5))); break; } } SANDBOX_AWAIT_S(0, rb_utf8_str_new_cstr, (const char *)sb().script_decode_buffer.data()); SANDBOX_AWAIT(rb_ary_store, SANDBOX_SLOT(4), 3, SANDBOX_SLOT(0)); } sb().script_decode_buffer.clear(); // TODO: preload scripts while (true) { for (SANDBOX_SLOT(3) = 0; SANDBOX_SLOT(3) < SANDBOX_SLOT(2); ++SANDBOX_SLOT(3)) { // Get the script into slot 4 SANDBOX_AWAIT_S(4, rb_ary_entry, SANDBOX_SLOT(1), SANDBOX_SLOT(3)); // Skip this script object if it's not an array SANDBOX_AWAIT_S(0, rb_obj_is_kind_of, SANDBOX_SLOT(4), sb()->rb_cArray()); if (!SANDBOX_VALUE_TO_BOOL(SANDBOX_SLOT(0))) { continue; } SANDBOX_AWAIT_S(9, rb_ary_entry, SANDBOX_SLOT(4), 3); SANDBOX_AWAIT_S(10, rb_ary_entry, SANDBOX_SLOT(4), 1); SANDBOX_AWAIT(eval_script, SANDBOX_SLOT(9), SANDBOX_SLOT(10)); } // Stop unless a reset was requested SANDBOX_AWAIT_S(0, rb_gv_get, "$!"); SANDBOX_AWAIT_S(0, rb_obj_class, SANDBOX_SLOT(0)); if (SANDBOX_SLOT(0) != reset_class) { break; } SANDBOX_AWAIT(audio_reset); SANDBOX_AWAIT(graphics_reset); } } } void sandbox_run_rmxp_scripts::end() noexcept { sb().script_decode_buffer.clear(); }