mirror of
https://github.com/mkxp-z/mkxp-z.git
synced 2025-04-21 21:52:04 +02:00

rb_raise calls longjmp, which bypasses C++ destructors, and also keeps the error for catch blocks from being unallocated if passed by reference, which we do for exceptions. Some of the calls I left can still jump out of try blocks, which you're not supposed to do, but there shouldn't be any objects with destructors initialized at those points so it's probably fine.
1252 lines
34 KiB
C++
1252 lines
34 KiB
C++
/*
|
|
** binding-mri.cpp
|
|
**
|
|
** This file is part of mkxp.
|
|
**
|
|
** Copyright (C) 2013 - 2021 Amaryllis Kulla <ancurio@mapleshrine.eu>
|
|
**
|
|
** 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "audio/audio.h"
|
|
#include "filesystem/filesystem.h"
|
|
#include "display/graphics.h"
|
|
#include "display/font.h"
|
|
#include "system/system.h"
|
|
|
|
#include "util/util.h"
|
|
#include "util/sdl-util.h"
|
|
#include "util/debugwriter.h"
|
|
#include "util/boost-hash.h"
|
|
#include "util/exception.h"
|
|
#include "util/encoding.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include "binding-util.h"
|
|
#include "binding.h"
|
|
|
|
#include "sharedstate.h"
|
|
#include "eventthread.h"
|
|
|
|
#include <vector>
|
|
|
|
extern "C" {
|
|
#include <ruby.h>
|
|
|
|
#if RAPI_FULL >= 190
|
|
#include <ruby/encoding.h>
|
|
#endif
|
|
}
|
|
|
|
#ifdef __WIN32__
|
|
#include "binding-mri-win32.h"
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <string>
|
|
#include <zlib.h>
|
|
|
|
#include <SDL_cpuinfo.h>
|
|
#include <SDL_filesystem.h>
|
|
#include <SDL_loadso.h>
|
|
#include <SDL_power.h>
|
|
|
|
extern const char module_rpg1[];
|
|
extern const char module_rpg2[];
|
|
extern const char module_rpg3[];
|
|
|
|
static void mriBindingExecute();
|
|
static void mriBindingTerminate();
|
|
static void mriBindingReset();
|
|
|
|
ScriptBinding scriptBindingImpl = {mriBindingExecute, mriBindingTerminate,
|
|
mriBindingReset};
|
|
|
|
ScriptBinding *scriptBinding = &scriptBindingImpl;
|
|
|
|
void tableBindingInit();
|
|
void etcBindingInit();
|
|
void fontBindingInit();
|
|
void bitmapBindingInit();
|
|
void spriteBindingInit();
|
|
void viewportBindingInit();
|
|
void planeBindingInit();
|
|
void windowBindingInit();
|
|
void tilemapBindingInit();
|
|
void windowVXBindingInit();
|
|
void tilemapVXBindingInit();
|
|
|
|
void inputBindingInit();
|
|
void audioBindingInit();
|
|
void graphicsBindingInit();
|
|
|
|
void fileIntBindingInit();
|
|
|
|
#ifdef MKXPZ_MINIFFI
|
|
void MiniFFIBindingInit();
|
|
#endif
|
|
|
|
#ifdef MKXPZ_STEAM
|
|
void CUSLBindingInit();
|
|
#endif
|
|
|
|
void httpBindingInit();
|
|
|
|
RB_METHOD(mkxpDelta);
|
|
RB_METHOD(mriPrint);
|
|
RB_METHOD(mriP);
|
|
RB_METHOD(mkxpDataDirectory);
|
|
RB_METHOD(mkxpSetTitle);
|
|
RB_METHOD(mkxpGetTitle);
|
|
RB_METHOD(mkxpDesensitize);
|
|
RB_METHOD(mkxpPuts);
|
|
|
|
RB_METHOD(mkxpPlatform);
|
|
RB_METHOD(mkxpIsMacHost);
|
|
RB_METHOD(mkxpIsWindowsHost);
|
|
RB_METHOD(mkxpIsLinuxHost);
|
|
RB_METHOD(mkxpIsUsingRosetta);
|
|
RB_METHOD(mkxpIsUsingWine);
|
|
RB_METHOD(mkxpIsReallyMacHost);
|
|
RB_METHOD(mkxpIsReallyLinuxHost);
|
|
RB_METHOD(mkxpIsReallyWindowsHost);
|
|
|
|
RB_METHOD(mkxpUserLanguage);
|
|
RB_METHOD(mkxpUserName);
|
|
RB_METHOD(mkxpGameTitle);
|
|
RB_METHOD(mkxpPowerState);
|
|
RB_METHOD(mkxpSettingsMenu);
|
|
RB_METHOD(mkxpCpuCount);
|
|
RB_METHOD(mkxpSystemMemory);
|
|
RB_METHOD(mkxpReloadPathCache);
|
|
RB_METHOD(mkxpAddPath);
|
|
RB_METHOD(mkxpRemovePath);
|
|
RB_METHOD(mkxpFileExists);
|
|
RB_METHOD(mkxpLaunch);
|
|
|
|
RB_METHOD(mkxpGetJSONSetting);
|
|
RB_METHOD(mkxpSetJSONSetting);
|
|
RB_METHOD(mkxpGetAllJSONSettings);
|
|
|
|
RB_METHOD(mkxpSetDefaultFontFamily);
|
|
|
|
RB_METHOD(mriRgssMain);
|
|
RB_METHOD(mriRgssStop);
|
|
RB_METHOD(_kernelCaller);
|
|
|
|
RB_METHOD(mkxpStringToUTF8);
|
|
RB_METHOD(mkxpStringToUTF8Bang);
|
|
|
|
VALUE json2rb(json5pp::value const &v);
|
|
json5pp::value rb2json(VALUE v);
|
|
|
|
static void mriBindingInit() {
|
|
tableBindingInit();
|
|
etcBindingInit();
|
|
fontBindingInit();
|
|
bitmapBindingInit();
|
|
spriteBindingInit();
|
|
viewportBindingInit();
|
|
planeBindingInit();
|
|
|
|
if (rgssVer == 1) {
|
|
windowBindingInit();
|
|
tilemapBindingInit();
|
|
} else {
|
|
windowVXBindingInit();
|
|
tilemapVXBindingInit();
|
|
}
|
|
|
|
inputBindingInit();
|
|
audioBindingInit();
|
|
graphicsBindingInit();
|
|
|
|
fileIntBindingInit();
|
|
|
|
#ifdef MKXPZ_MINIFFI
|
|
MiniFFIBindingInit();
|
|
#endif
|
|
|
|
#ifdef MKXPZ_STEAM
|
|
CUSLBindingInit();
|
|
#endif
|
|
|
|
httpBindingInit();
|
|
|
|
if (rgssVer >= 3) {
|
|
_rb_define_module_function(rb_mKernel, "rgss_main", mriRgssMain);
|
|
_rb_define_module_function(rb_mKernel, "rgss_stop", mriRgssStop);
|
|
|
|
_rb_define_module_function(rb_mKernel, "msgbox", mriPrint);
|
|
_rb_define_module_function(rb_mKernel, "msgbox_p", mriP);
|
|
|
|
rb_define_global_const("RGSS_VERSION", rb_utf8_str_new_cstr("3.0.1"));
|
|
} else {
|
|
_rb_define_module_function(rb_mKernel, "print", mriPrint);
|
|
_rb_define_module_function(rb_mKernel, "p", mriP);
|
|
|
|
rb_define_alias(rb_singleton_class(rb_mKernel), "_mkxp_kernel_caller_alias",
|
|
"caller");
|
|
_rb_define_module_function(rb_mKernel, "caller", _kernelCaller);
|
|
}
|
|
|
|
if (rgssVer == 1)
|
|
rb_eval_string(module_rpg1);
|
|
else if (rgssVer == 2)
|
|
rb_eval_string(module_rpg2);
|
|
else if (rgssVer == 3)
|
|
rb_eval_string(module_rpg3);
|
|
else
|
|
assert(!"unreachable");
|
|
|
|
VALUE mod = rb_define_module("System");
|
|
_rb_define_module_function(mod, "delta", mkxpDelta);
|
|
_rb_define_module_function(mod, "uptime", mkxpDelta);
|
|
_rb_define_module_function(mod, "data_directory", mkxpDataDirectory);
|
|
_rb_define_module_function(mod, "set_window_title", mkxpSetTitle);
|
|
_rb_define_module_function(mod, "window_title", mkxpGetTitle);
|
|
_rb_define_module_function(mod, "window_title=", mkxpSetTitle);
|
|
_rb_define_module_function(mod, "show_settings", mkxpSettingsMenu);
|
|
_rb_define_module_function(mod, "puts", mkxpPuts);
|
|
_rb_define_module_function(mod, "desensitize", mkxpDesensitize);
|
|
_rb_define_module_function(mod, "platform", mkxpPlatform);
|
|
|
|
_rb_define_module_function(mod, "is_mac?", mkxpIsMacHost);
|
|
_rb_define_module_function(mod, "is_rosetta?", mkxpIsUsingRosetta);
|
|
|
|
_rb_define_module_function(mod, "is_linux?", mkxpIsLinuxHost);
|
|
|
|
_rb_define_module_function(mod, "is_windows?", mkxpIsWindowsHost);
|
|
_rb_define_module_function(mod, "is_wine?", mkxpIsUsingWine);
|
|
_rb_define_module_function(mod, "is_really_mac?", mkxpIsReallyMacHost);
|
|
_rb_define_module_function(mod, "is_really_linux?", mkxpIsReallyLinuxHost);
|
|
_rb_define_module_function(mod, "is_really_windows?", mkxpIsReallyWindowsHost);
|
|
|
|
|
|
_rb_define_module_function(mod, "user_language", mkxpUserLanguage);
|
|
_rb_define_module_function(mod, "user_name", mkxpUserName);
|
|
_rb_define_module_function(mod, "game_title", mkxpGameTitle);
|
|
_rb_define_module_function(mod, "power_state", mkxpPowerState);
|
|
_rb_define_module_function(mod, "nproc", mkxpCpuCount);
|
|
_rb_define_module_function(mod, "memory", mkxpSystemMemory);
|
|
_rb_define_module_function(mod, "reload_cache", mkxpReloadPathCache);
|
|
_rb_define_module_function(mod, "mount", mkxpAddPath);
|
|
_rb_define_module_function(mod, "unmount", mkxpRemovePath);
|
|
_rb_define_module_function(mod, "file_exist?", mkxpFileExists);
|
|
_rb_define_module_function(mod, "launch", mkxpLaunch);
|
|
|
|
_rb_define_module_function(mod, "default_font_family=", mkxpSetDefaultFontFamily);
|
|
|
|
_rb_define_method(rb_cString, "to_utf8", mkxpStringToUTF8);
|
|
_rb_define_method(rb_cString, "to_utf8!", mkxpStringToUTF8Bang);
|
|
|
|
VALUE cmod = rb_define_module("CFG");
|
|
_rb_define_module_function(cmod, "[]", mkxpGetJSONSetting);
|
|
_rb_define_module_function(cmod, "[]=", mkxpSetJSONSetting);
|
|
_rb_define_module_function(cmod, "to_hash", mkxpGetAllJSONSettings);
|
|
|
|
/* Load global constants */
|
|
rb_gv_set("MKXP", Qtrue);
|
|
|
|
VALUE debug = rb_bool_new(shState->config().editor.debug);
|
|
if (rgssVer == 1)
|
|
rb_gv_set("DEBUG", debug);
|
|
else if (rgssVer >= 2)
|
|
rb_gv_set("TEST", debug);
|
|
|
|
rb_gv_set("BTEST", rb_bool_new(shState->config().editor.battleTest));
|
|
|
|
#ifdef MKXPZ_BUILD_XCODE
|
|
std::string version = std::string(MKXPZ_VERSION "/") + getPlistValue("GIT_COMMIT_HASH");
|
|
VALUE vers = rb_utf8_str_new_cstr(version.c_str());
|
|
#else
|
|
VALUE vers = rb_utf8_str_new_cstr(MKXPZ_VERSION "/" MKXPZ_GIT_HASH);
|
|
#endif
|
|
rb_str_freeze(vers);
|
|
rb_define_const(mod, "VERSION", vers);
|
|
|
|
// Automatically load zlib if it's present -- the correct way this time
|
|
int state;
|
|
rb_eval_string_protect("require('zlib') if !Kernel.const_defined?(:Zlib)", &state);
|
|
if (state) {
|
|
Debug() << "Could not load Zlib. If this is important, make sure Ruby was built with static extensions, or that"
|
|
<< ((MKXPZ_PLATFORM == MKXPZ_PLATFORM_MACOS) ? "zlib.bundle" : "zlib.so")
|
|
<< "is present and reachable by Ruby's loadpath.";
|
|
}
|
|
|
|
// Set $stdout and its ilk accordingly on Windows
|
|
// I regret teaching you that word
|
|
#ifdef __WIN32__
|
|
if (shState->config().winConsole)
|
|
configureWindowsStreams();
|
|
#endif
|
|
}
|
|
|
|
static void showMsg(const std::string &msg) {
|
|
shState->eThread().showMessageBox(msg.c_str());
|
|
}
|
|
|
|
static void printP(int argc, VALUE *argv, const char *convMethod,
|
|
const char *sep) {
|
|
VALUE dispString = rb_str_buf_new(128);
|
|
ID conv = rb_intern(convMethod);
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
|
VALUE str = rb_funcall2(argv[i], conv, 0, NULL);
|
|
rb_str_buf_append(dispString, str);
|
|
|
|
if (i < argc)
|
|
rb_str_buf_cat2(dispString, sep);
|
|
}
|
|
|
|
showMsg(RSTRING_PTR(dispString));
|
|
}
|
|
|
|
|
|
RB_METHOD(mriPrint) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
printP(argc, argv, "to_s", "");
|
|
|
|
return Qnil;
|
|
}
|
|
|
|
RB_METHOD(mriP) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
printP(argc, argv, "inspect", "\n");
|
|
|
|
return Qnil;
|
|
}
|
|
|
|
RB_METHOD(mkxpDelta) {
|
|
RB_UNUSED_PARAM;
|
|
return rb_float_new(shState->runTime());
|
|
}
|
|
|
|
RB_METHOD(mkxpDataDirectory) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
const std::string &path = shState->config().customDataPath;
|
|
const char *s = path.empty() ? "." : path.c_str();
|
|
|
|
std::string s_nml = shState->fileSystem().normalize(s, 1, 1);
|
|
VALUE ret = rb_utf8_str_new_cstr(s_nml.c_str());
|
|
|
|
return ret;
|
|
}
|
|
|
|
RB_METHOD(mkxpSetTitle) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
VALUE s;
|
|
rb_scan_args(argc, argv, "1", &s);
|
|
SafeStringValue(s);
|
|
|
|
shState->eThread().requestWindowRename(RSTRING_PTR(s));
|
|
return s;
|
|
}
|
|
|
|
RB_METHOD(mkxpGetTitle) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
rb_check_argc(argc, 0);
|
|
|
|
return rb_utf8_str_new_cstr(SDL_GetWindowTitle(shState->sdlWindow()));
|
|
}
|
|
|
|
RB_METHOD(mkxpDesensitize) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
VALUE filename;
|
|
rb_scan_args(argc, argv, "1", &filename);
|
|
SafeStringValue(filename);
|
|
|
|
return rb_utf8_str_new_cstr(
|
|
shState->fileSystem().desensitize(RSTRING_PTR(filename)));
|
|
}
|
|
|
|
RB_METHOD(mkxpPuts) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
const char *str;
|
|
rb_get_args(argc, argv, "z", &str RB_ARG_END);
|
|
|
|
Debug() << str;
|
|
|
|
return Qnil;
|
|
}
|
|
|
|
RB_METHOD(mkxpPlatform) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
#if MKXPZ_PLATFORM == MKXPZ_PLATFORM_MACOS
|
|
std::string platform("macOS");
|
|
|
|
if (mkxp_sys::isRosetta())
|
|
platform += " (Rosetta)";
|
|
|
|
#elif MKXPZ_PLATFORM == MKXPZ_PLATFORM_WINDOWS
|
|
std::string platform("Windows");
|
|
|
|
if (mkxp_sys::isWine()) {
|
|
platform += " (Wine - ";
|
|
switch (mkxp_sys::getRealHostType()) {
|
|
case mkxp_sys::WineHostType::Mac:
|
|
platform += "macOS)";
|
|
break;
|
|
default:
|
|
platform += "Linux)";
|
|
break;
|
|
}
|
|
}
|
|
#else
|
|
std::string platform("Linux");
|
|
#endif
|
|
|
|
return rb_utf8_str_new_cstr(platform.c_str());
|
|
}
|
|
|
|
RB_METHOD(mkxpIsMacHost) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
return rb_bool_new(MKXPZ_PLATFORM == MKXPZ_PLATFORM_MACOS);
|
|
}
|
|
|
|
RB_METHOD(mkxpIsUsingRosetta) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
return rb_bool_new(mkxp_sys::isRosetta());
|
|
}
|
|
|
|
RB_METHOD(mkxpIsLinuxHost) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
return rb_bool_new(MKXPZ_PLATFORM == MKXPZ_PLATFORM_LINUX);
|
|
}
|
|
|
|
RB_METHOD(mkxpIsWindowsHost) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
return rb_bool_new(MKXPZ_PLATFORM == MKXPZ_PLATFORM_WINDOWS);
|
|
}
|
|
|
|
RB_METHOD(mkxpIsUsingWine) {
|
|
RB_UNUSED_PARAM;
|
|
return rb_bool_new(mkxp_sys::isWine());
|
|
}
|
|
|
|
RB_METHOD(mkxpIsReallyMacHost) {
|
|
RB_UNUSED_PARAM;
|
|
return rb_bool_new(mkxp_sys::getRealHostType() == mkxp_sys::WineHostType::Mac);
|
|
}
|
|
|
|
RB_METHOD(mkxpIsReallyLinuxHost) {
|
|
RB_UNUSED_PARAM;
|
|
return rb_bool_new(mkxp_sys::getRealHostType() == mkxp_sys::WineHostType::Linux);
|
|
}
|
|
|
|
RB_METHOD(mkxpIsReallyWindowsHost) {
|
|
RB_UNUSED_PARAM;
|
|
return rb_bool_new(mkxp_sys::getRealHostType() == mkxp_sys::WineHostType::Windows);
|
|
}
|
|
|
|
RB_METHOD(mkxpUserLanguage) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
return rb_utf8_str_new_cstr(mkxp_sys::getSystemLanguage().c_str());
|
|
}
|
|
|
|
RB_METHOD(mkxpUserName) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
// Using the Windows API isn't working with usernames that involve Unicode
|
|
// characters for some dumb reason
|
|
#ifdef __WIN32__
|
|
VALUE env = rb_const_get(rb_mKernel, rb_intern("ENV"));
|
|
return rb_funcall(env, rb_intern("[]"), 1, rb_str_new_cstr("USERNAME"));
|
|
#else
|
|
return rb_utf8_str_new_cstr(mkxp_sys::getUserName().c_str());
|
|
#endif
|
|
}
|
|
|
|
RB_METHOD(mkxpGameTitle) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
return rb_utf8_str_new_cstr(shState->config().game.title.c_str());
|
|
}
|
|
|
|
RB_METHOD(mkxpPowerState) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
int secs, pct;
|
|
SDL_PowerState ps = SDL_GetPowerInfo(&secs, &pct);
|
|
|
|
VALUE hash = rb_hash_new();
|
|
|
|
rb_hash_aset(hash, ID2SYM(rb_intern("seconds")),
|
|
(secs > -1) ? INT2NUM(secs) : RUBY_Qnil);
|
|
|
|
rb_hash_aset(hash, ID2SYM(rb_intern("percent")),
|
|
(pct > -1) ? INT2NUM(pct) : RUBY_Qnil);
|
|
|
|
rb_hash_aset(hash, ID2SYM(rb_intern("discharging")),
|
|
rb_bool_new(ps == SDL_POWERSTATE_ON_BATTERY));
|
|
|
|
return hash;
|
|
}
|
|
|
|
RB_METHOD(mkxpSettingsMenu) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
shState->eThread().requestSettingsMenu();
|
|
|
|
return Qnil;
|
|
}
|
|
|
|
RB_METHOD(mkxpCpuCount) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
return INT2NUM(SDL_GetCPUCount());
|
|
}
|
|
|
|
RB_METHOD(mkxpSystemMemory) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
return INT2NUM(SDL_GetSystemRAM());
|
|
}
|
|
|
|
RB_METHOD_GUARD(mkxpReloadPathCache) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
shState->fileSystem().reloadPathCache();
|
|
return Qnil;
|
|
}
|
|
RB_METHOD_GUARD_END
|
|
|
|
RB_METHOD_GUARD(mkxpAddPath) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
VALUE path, mountpoint, reload;
|
|
rb_scan_args(argc, argv, "12", &path, &mountpoint, &reload);
|
|
SafeStringValue(path);
|
|
if (mountpoint != Qnil) SafeStringValue(mountpoint);
|
|
|
|
const char *mp = (mountpoint == Qnil) ? 0 : RSTRING_PTR(mountpoint);
|
|
|
|
bool rl = true;
|
|
if (reload != Qnil)
|
|
rb_bool_arg(reload, &rl);
|
|
|
|
shState->fileSystem().addPath(RSTRING_PTR(path), mp, rl);
|
|
|
|
return path;
|
|
}
|
|
RB_METHOD_GUARD_END
|
|
|
|
RB_METHOD_GUARD(mkxpRemovePath) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
VALUE path, reload;
|
|
rb_scan_args(argc, argv, "11", &path, &reload);
|
|
SafeStringValue(path);
|
|
|
|
bool rl = true;
|
|
if (reload != Qnil)
|
|
rb_bool_arg(reload, &rl);
|
|
|
|
shState->fileSystem().removePath(RSTRING_PTR(path), rl);
|
|
|
|
return path;
|
|
}
|
|
RB_METHOD_GUARD_END
|
|
|
|
RB_METHOD(mkxpFileExists) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
VALUE path;
|
|
rb_scan_args(argc, argv, "1", &path);
|
|
SafeStringValue(path);
|
|
|
|
if (shState->fileSystem().exists(RSTRING_PTR(path)))
|
|
return Qtrue;
|
|
return Qfalse;
|
|
}
|
|
|
|
RB_METHOD(mkxpSetDefaultFontFamily) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
VALUE familyV;
|
|
rb_scan_args(argc, argv, "1", &familyV);
|
|
SafeStringValue(familyV);
|
|
|
|
std::string family(RSTRING_PTR(familyV));
|
|
shState->fontState().setDefaultFontFamily(family);
|
|
|
|
return Qnil;
|
|
}
|
|
|
|
RB_METHOD_GUARD(mkxpStringToUTF8) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
rb_check_argc(argc, 0);
|
|
|
|
std::string ret(RSTRING_PTR(self), RSTRING_LEN(self));
|
|
ret = Encoding::convertString(ret);
|
|
|
|
return rb_utf8_str_new(ret.c_str(), ret.length());
|
|
}
|
|
RB_METHOD_GUARD_END
|
|
|
|
RB_METHOD_GUARD(mkxpStringToUTF8Bang) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
rb_check_argc(argc, 0);
|
|
|
|
std::string ret(RSTRING_PTR(self), RSTRING_LEN(self));
|
|
ret = Encoding::convertString(ret);
|
|
|
|
rb_str_resize(self, ret.length());
|
|
memcpy(RSTRING_PTR(self), ret.c_str(), RSTRING_LEN(self));
|
|
|
|
#if RAPI_FULL >= 190
|
|
rb_funcall(self, rb_intern("force_encoding"), 1, rb_enc_from_encoding(rb_utf8_encoding()));
|
|
#endif
|
|
|
|
return self;
|
|
}
|
|
RB_METHOD_GUARD_END
|
|
|
|
#ifdef __APPLE__
|
|
#define OPENCMD "open "
|
|
#define OPENARGS "--args"
|
|
#elif defined(__linux__)
|
|
#define OPENCMD "xdg-open "
|
|
#define OPENARGS ""
|
|
#else
|
|
#define OPENCMD "start /b \"launch\" "
|
|
#define OPENARGS ""
|
|
#endif
|
|
|
|
RB_METHOD_GUARD(mkxpLaunch) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
VALUE cmdname, args;
|
|
|
|
rb_scan_args(argc, argv, "11", &cmdname, &args);
|
|
SafeStringValue(cmdname);
|
|
|
|
std::string command(OPENCMD);
|
|
command += "\""; command += RSTRING_PTR(cmdname); command += "\"";
|
|
|
|
if (args != RUBY_Qnil) {
|
|
#ifndef __linux__
|
|
command += " ";
|
|
command += OPENARGS;
|
|
Check_Type(args, T_ARRAY);
|
|
|
|
for (int i = 0; i < RARRAY_LEN(args); i++) {
|
|
VALUE arg = rb_ary_entry(args, i);
|
|
SafeStringValue(arg);
|
|
|
|
if (RSTRING_LEN(arg) <= 0)
|
|
continue;
|
|
|
|
command += " ";
|
|
command += RSTRING_PTR(arg);
|
|
}
|
|
#else
|
|
Debug() << command << ":" << "Arguments are not supported with xdg-open. Ignoring.";
|
|
#endif
|
|
}
|
|
|
|
if (std::system(command.c_str()) != 0) {
|
|
throw Exception(Exception::MKXPError, "Failed to launch \"%s\"", RSTRING_PTR(cmdname));
|
|
}
|
|
|
|
return RUBY_Qnil;
|
|
}
|
|
RB_METHOD_GUARD_END
|
|
|
|
json5pp::value loadUserSettings() {
|
|
json5pp::value ret;
|
|
VALUE cpath = rb_utf8_str_new_cstr(shState->config().userConfPath.c_str());
|
|
|
|
if (rb_funcall(rb_cFile, rb_intern("exists?"), 1, cpath) == Qtrue) {
|
|
VALUE f = rb_funcall(rb_cFile, rb_intern("open"), 2, cpath, rb_str_new("r", 1));
|
|
VALUE data = rb_funcall(f, rb_intern("read"), 0);
|
|
rb_funcall(f, rb_intern("close"), 0);
|
|
ret = json5pp::parse5(RSTRING_PTR(data));
|
|
}
|
|
|
|
if (!ret.is_object())
|
|
ret = json5pp::object({});
|
|
|
|
return ret;
|
|
}
|
|
|
|
void saveUserSettings(json5pp::value &settings) {
|
|
VALUE cpath = rb_utf8_str_new_cstr(shState->config().userConfPath.c_str());
|
|
VALUE f = rb_funcall(rb_cFile, rb_intern("open"), 2, cpath, rb_str_new("w", 1));
|
|
rb_funcall(f, rb_intern("write"), 1, rb_utf8_str_new_cstr(settings.stringify5(json5pp::rule::space_indent<>()).c_str()));
|
|
rb_funcall(f, rb_intern("close"), 0);
|
|
}
|
|
|
|
RB_METHOD(mkxpGetJSONSetting) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
VALUE sname;
|
|
rb_scan_args(argc, argv, "1", &sname);
|
|
SafeStringValue(sname);
|
|
|
|
auto settings = loadUserSettings();
|
|
auto &s = settings.as_object();
|
|
|
|
if (s[RSTRING_PTR(sname)].is_null()) {
|
|
return json2rb(shState->config().raw.as_object()[RSTRING_PTR(sname)]);
|
|
}
|
|
|
|
return json2rb(s[RSTRING_PTR(sname)]);
|
|
|
|
}
|
|
|
|
RB_METHOD_GUARD(mkxpSetJSONSetting) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
VALUE sname, svalue;
|
|
rb_scan_args(argc, argv, "2", &sname, &svalue);
|
|
SafeStringValue(sname);
|
|
|
|
auto settings = loadUserSettings();
|
|
auto &s = settings.as_object();
|
|
s[RSTRING_PTR(sname)] = rb2json(svalue);
|
|
saveUserSettings(settings);
|
|
|
|
return Qnil;
|
|
}
|
|
RB_METHOD_GUARD_END
|
|
|
|
RB_METHOD(mkxpGetAllJSONSettings) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
return json2rb(shState->config().raw);
|
|
}
|
|
|
|
static VALUE rgssMainCb(VALUE block) {
|
|
rb_funcall2(block, rb_intern("call"), 0, 0);
|
|
return Qnil;
|
|
}
|
|
|
|
static VALUE rgssMainRescue(VALUE arg, VALUE exc) {
|
|
VALUE *excRet = (VALUE *)arg;
|
|
|
|
*excRet = exc;
|
|
|
|
return Qnil;
|
|
}
|
|
|
|
static void processReset() {
|
|
shState->graphics().reset();
|
|
shState->audio().reset();
|
|
|
|
shState->rtData().rqReset.clear();
|
|
shState->graphics().repaintWait(shState->rtData().rqResetFinish, false);
|
|
}
|
|
|
|
RB_METHOD(mriRgssMain) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
while (true) {
|
|
VALUE exc = Qnil;
|
|
#if RAPI_FULL < 270
|
|
rb_rescue2((VALUE(*)(ANYARGS))rgssMainCb, rb_block_proc(),
|
|
(VALUE(*)(ANYARGS))rgssMainRescue, (VALUE)&exc, rb_eException,
|
|
(VALUE)0);
|
|
#else
|
|
rb_rescue2(rgssMainCb, rb_block_proc(), rgssMainRescue, (VALUE)&exc,
|
|
rb_eException, (VALUE)0);
|
|
#endif
|
|
|
|
if (NIL_P(exc))
|
|
break;
|
|
|
|
if (rb_obj_class(exc) == getRbData()->exc[Reset])
|
|
processReset();
|
|
else
|
|
rb_exc_raise(exc);
|
|
}
|
|
|
|
return Qnil;
|
|
}
|
|
|
|
RB_METHOD(mriRgssStop) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
while (true)
|
|
shState->graphics().update();
|
|
|
|
return Qnil;
|
|
}
|
|
|
|
RB_METHOD(_kernelCaller) {
|
|
RB_UNUSED_PARAM;
|
|
|
|
VALUE trace =
|
|
rb_funcall2(rb_mKernel, rb_intern("_mkxp_kernel_caller_alias"), 0, 0);
|
|
|
|
if (!RB_TYPE_P(trace, RUBY_T_ARRAY))
|
|
return trace;
|
|
|
|
long len = RARRAY_LEN(trace);
|
|
|
|
if (len < 2)
|
|
return trace;
|
|
|
|
/* Remove useless "ruby:1:in 'eval'" */
|
|
rb_ary_pop(trace);
|
|
|
|
/* Also remove trace of this helper function */
|
|
rb_ary_shift(trace);
|
|
|
|
len -= 2;
|
|
|
|
if (len == 0)
|
|
return trace;
|
|
|
|
/* RMXP does this, not sure if specific or 1.8 related */
|
|
VALUE args[] = {rb_utf8_str_new_cstr(":in `<main>'"), rb_utf8_str_new_cstr("")};
|
|
rb_funcall2(rb_ary_entry(trace, len - 1), rb_intern("gsub!"), 2, args);
|
|
|
|
return trace;
|
|
}
|
|
|
|
#if RAPI_FULL > 187
|
|
static VALUE newStringUTF8(const char *string, long length) {
|
|
return rb_enc_str_new(string, length, rb_utf8_encoding());
|
|
}
|
|
#else
|
|
#define newStringUTF8 rb_str_new
|
|
#endif
|
|
|
|
struct evalArg {
|
|
VALUE string;
|
|
VALUE filename;
|
|
};
|
|
|
|
static VALUE evalHelper(evalArg *arg) {
|
|
VALUE argv[] = {arg->string, Qnil, arg->filename};
|
|
return rb_funcall2(Qnil, rb_intern("eval"), ARRAY_SIZE(argv), argv);
|
|
}
|
|
|
|
static VALUE evalString(VALUE string, VALUE filename, int *state) {
|
|
evalArg arg = {string, filename};
|
|
return rb_protect((VALUE(*)(VALUE))evalHelper, (VALUE)&arg, state);
|
|
}
|
|
|
|
static void runCustomScript(const std::string &filename) {
|
|
std::string scriptData;
|
|
|
|
if (!readFileSDL(filename.c_str(), scriptData)) {
|
|
showMsg(std::string("Unable to open '") + filename + "'");
|
|
return;
|
|
}
|
|
|
|
evalString(newStringUTF8(scriptData.c_str(), scriptData.size()),
|
|
newStringUTF8(filename.c_str(), filename.size()), NULL);
|
|
}
|
|
|
|
VALUE kernelLoadDataInt(const char *filename, bool rubyExc, bool raw);
|
|
|
|
struct BacktraceData {
|
|
/* Maps: Ruby visible filename, To: Actual script name */
|
|
BoostHash<std::string, std::string> scriptNames;
|
|
};
|
|
|
|
bool evalScript(VALUE string, const char *filename)
|
|
{
|
|
int state;
|
|
evalString(string, rb_utf8_str_new_cstr(filename), &state);
|
|
if (state) return false;
|
|
return true;
|
|
}
|
|
|
|
|
|
#define SCRIPT_SECTION_FMT (rgssVer >= 3 ? "{%04ld}" : "Section%03ld")
|
|
|
|
static void runRMXPScripts(BacktraceData &btData) {
|
|
const Config &conf = shState->rtData().config;
|
|
const std::string &scriptPack = conf.game.scripts;
|
|
|
|
if (scriptPack.empty()) {
|
|
showMsg("No script file has been specified. Check the game's INI and try again.");
|
|
return;
|
|
}
|
|
|
|
if (!shState->fileSystem().exists(scriptPack.c_str())) {
|
|
showMsg("Unable to load scripts from '" + scriptPack + "'");
|
|
return;
|
|
}
|
|
|
|
VALUE scriptArray;
|
|
|
|
/* We checked if Scripts.rxdata exists, but something might
|
|
* still go wrong */
|
|
try {
|
|
scriptArray = kernelLoadDataInt(scriptPack.c_str(), false, false);
|
|
} catch (const Exception &e) {
|
|
showMsg(std::string("Failed to read script data: ") + e.msg);
|
|
return;
|
|
}
|
|
|
|
if (!RB_TYPE_P(scriptArray, RUBY_T_ARRAY)) {
|
|
showMsg("Failed to read script data");
|
|
return;
|
|
}
|
|
|
|
rb_gv_set("$RGSS_SCRIPTS", scriptArray);
|
|
|
|
long scriptCount = RARRAY_LEN(scriptArray);
|
|
|
|
std::string decodeBuffer;
|
|
decodeBuffer.resize(0x1000);
|
|
|
|
for (long i = 0; i < scriptCount; ++i) {
|
|
VALUE script = rb_ary_entry(scriptArray, i);
|
|
|
|
if (!RB_TYPE_P(script, RUBY_T_ARRAY))
|
|
continue;
|
|
|
|
VALUE scriptName = rb_ary_entry(script, 1);
|
|
VALUE scriptString = rb_ary_entry(script, 2);
|
|
|
|
int result = Z_OK;
|
|
unsigned long bufferLen;
|
|
|
|
while (true) {
|
|
unsigned char *bufferPtr = reinterpret_cast<unsigned char *>(
|
|
const_cast<char *>(decodeBuffer.c_str()));
|
|
const unsigned char *sourcePtr =
|
|
reinterpret_cast<const unsigned char *>(RSTRING_PTR(scriptString));
|
|
|
|
bufferLen = decodeBuffer.length();
|
|
|
|
result = uncompress(bufferPtr, &bufferLen, sourcePtr,
|
|
RSTRING_LEN(scriptString));
|
|
|
|
bufferPtr[bufferLen] = '\0';
|
|
|
|
if (result != Z_BUF_ERROR)
|
|
break;
|
|
|
|
decodeBuffer.resize(decodeBuffer.size() * 2);
|
|
}
|
|
|
|
if (result != Z_OK) {
|
|
static char buffer[256];
|
|
snprintf(buffer, sizeof(buffer), "Error decoding script %ld: '%s'", i,
|
|
RSTRING_PTR(scriptName));
|
|
|
|
showMsg(buffer);
|
|
|
|
break;
|
|
}
|
|
|
|
rb_ary_store(script, 3, rb_utf8_str_new_cstr(decodeBuffer.c_str()));
|
|
}
|
|
|
|
/* Execute preloaded scripts */
|
|
for (std::vector<std::string>::const_iterator i = conf.preloadScripts.begin();
|
|
i != conf.preloadScripts.end(); ++i)
|
|
{
|
|
if (shState->rtData().rqTerm)
|
|
break;
|
|
runCustomScript(*i);
|
|
}
|
|
|
|
VALUE exc = rb_gv_get("$!");
|
|
if (exc != Qnil)
|
|
return;
|
|
|
|
while (true) {
|
|
for (long i = 0; i < scriptCount; ++i) {
|
|
if (shState->rtData().rqTerm)
|
|
break;
|
|
|
|
VALUE script = rb_ary_entry(scriptArray, i);
|
|
VALUE scriptDecoded = rb_ary_entry(script, 3);
|
|
VALUE string =
|
|
newStringUTF8(RSTRING_PTR(scriptDecoded), RSTRING_LEN(scriptDecoded));
|
|
|
|
VALUE fname;
|
|
const char *scriptName = RSTRING_PTR(rb_ary_entry(script, 1));
|
|
char buf[512];
|
|
int len;
|
|
|
|
if (conf.useScriptNames)
|
|
len = snprintf(buf, sizeof(buf), "%03ld:%s", i, scriptName);
|
|
else
|
|
len = snprintf(buf, sizeof(buf), SCRIPT_SECTION_FMT, i);
|
|
|
|
fname = newStringUTF8(buf, len);
|
|
btData.scriptNames.insert(buf, scriptName);
|
|
|
|
|
|
// if the script name starts with |s|, only execute
|
|
// it if "s" is the same first letter as the platform
|
|
// we're running on
|
|
|
|
// |W| - Windows, |M| - Mac OS X, |L| - Linux
|
|
|
|
// Adding a 'not' symbol means it WON'T run on that
|
|
// platform (i.e. |!W| won't run on Windows)
|
|
/*
|
|
if (scriptName[0] == '|') {
|
|
int len = strlen(scriptName);
|
|
if (len > 2) {
|
|
if (scriptName[1] == '!' && len > 3 &&
|
|
scriptName[3] == scriptName[0]) {
|
|
if (toupper(scriptName[2]) == platform[0])
|
|
continue;
|
|
}
|
|
if (scriptName[2] == scriptName[0] &&
|
|
toupper(scriptName[1]) != platform[0])
|
|
continue;
|
|
}
|
|
}
|
|
*/
|
|
|
|
int state;
|
|
|
|
evalString(string, fname, &state);
|
|
if (state)
|
|
break;
|
|
}
|
|
|
|
VALUE exc = rb_gv_get("$!");
|
|
if (rb_obj_class(exc) != getRbData()->exc[Reset])
|
|
break;
|
|
|
|
processReset();
|
|
}
|
|
}
|
|
|
|
static void showExc(VALUE exc, const BacktraceData &btData) {
|
|
VALUE bt = rb_funcall2(exc, rb_intern("backtrace"), 0, NULL);
|
|
VALUE msg = rb_funcall2(exc, rb_intern("message"), 0, NULL);
|
|
VALUE bt0 = rb_ary_entry(bt, 0);
|
|
VALUE name = rb_class_path(rb_obj_class(exc));
|
|
|
|
VALUE ds = rb_sprintf("%" PRIsVALUE ": %" PRIsVALUE " (%" PRIsVALUE ")",
|
|
#if RAPI_MAJOR >= 2
|
|
bt0, exc, name);
|
|
#else
|
|
// Ruby 1.9's version of this function needs char*
|
|
RSTRING_PTR(bt0), RSTRING_PTR(exc), RSTRING_PTR(name));
|
|
#endif
|
|
/* omit "useless" last entry (from ruby:1:in `eval') */
|
|
for (long i = 1, btlen = RARRAY_LEN(bt) - 1; i < btlen; ++i)
|
|
rb_str_catf(ds, "\n\tfrom %" PRIsVALUE,
|
|
#if RAPI_MAJOR >= 2
|
|
rb_ary_entry(bt, i));
|
|
#else
|
|
RSTRING_PTR(rb_ary_entry(bt, i)));
|
|
#endif
|
|
Debug() << StringValueCStr(ds);
|
|
|
|
char *s = RSTRING_PTR(bt0);
|
|
|
|
char line[16];
|
|
std::string file(512, '\0');
|
|
|
|
char *p = s + strlen(s);
|
|
char *e;
|
|
|
|
while (p != s)
|
|
if (*--p == ':')
|
|
break;
|
|
|
|
e = p;
|
|
|
|
while (p != s)
|
|
if (*--p == ':')
|
|
break;
|
|
|
|
/* s p e
|
|
* SectionXXX:YY: in 'blabla' */
|
|
|
|
*e = '\0';
|
|
strncpy(line, *p ? p + 1 : p, sizeof(line));
|
|
line[sizeof(line) - 1] = '\0';
|
|
*e = ':';
|
|
e = p;
|
|
|
|
/* s e
|
|
* SectionXXX:YY: in 'blabla' */
|
|
|
|
*e = '\0';
|
|
strncpy(&file[0], s, file.size());
|
|
*e = ':';
|
|
|
|
/* Shrink to fit */
|
|
file.resize(strlen(file.c_str()));
|
|
file = btData.scriptNames.value(file, file);
|
|
|
|
std::string ms(640, '\0');
|
|
snprintf(&ms[0], ms.size(), "Script '%s' line %s: %s occured.\n\n%s",
|
|
file.c_str(), line, RSTRING_PTR(name), RSTRING_PTR(msg));
|
|
|
|
showMsg(ms);
|
|
}
|
|
|
|
static void mriBindingExecute() {
|
|
Config &conf = shState->rtData().config;
|
|
|
|
#if RAPI_MAJOR >= 2
|
|
/* Normally only a ruby executable would do a sysinit,
|
|
* but not doing it will lead to crashes due to closed
|
|
* stdio streams on some platforms (eg. Windows) */
|
|
int argc = 0;
|
|
char **argv = 0;
|
|
ruby_sysinit(&argc, &argv);
|
|
|
|
RUBY_INIT_STACK;
|
|
ruby_init();
|
|
|
|
std::vector<const char*> rubyArgsC{"mkxp-z"};
|
|
rubyArgsC.push_back("-e ");
|
|
void *node;
|
|
if (conf.jit.enabled) {
|
|
#if RAPI_FULL >= 310
|
|
// Ruby v3.1.0 renamed the --jit options to --mjit.
|
|
std::string verboseLevel("--mjit-verbose=");
|
|
std::string maxCache("--mjit-max-cache=");
|
|
std::string minCalls("--mjit-min-calls=");
|
|
rubyArgsC.push_back("--mjit");
|
|
#else
|
|
std::string verboseLevel("--jit-verbose=");
|
|
std::string maxCache("--jit-max-cache=");
|
|
std::string minCalls("--jit-min-calls=");
|
|
rubyArgsC.push_back("--jit");
|
|
#endif
|
|
verboseLevel += std::to_string(conf.jit.verboseLevel);
|
|
maxCache += std::to_string(conf.jit.maxCache);
|
|
minCalls += std::to_string(conf.jit.minCalls);
|
|
|
|
rubyArgsC.push_back(verboseLevel.c_str());
|
|
rubyArgsC.push_back(maxCache.c_str());
|
|
rubyArgsC.push_back(minCalls.c_str());
|
|
node = ruby_options(rubyArgsC.size(), const_cast<char**>(rubyArgsC.data()));
|
|
} else if (conf.yjit.enabled) {
|
|
rubyArgsC.push_back("--yjit");
|
|
// TODO: Maybe support --yjit-exec-mem-size, --yjit-call-threshold
|
|
node = ruby_options(rubyArgsC.size(), const_cast<char**>(rubyArgsC.data()));
|
|
} else {
|
|
node = ruby_options(rubyArgsC.size(), const_cast<char**>(rubyArgsC.data()));
|
|
}
|
|
|
|
int state;
|
|
bool valid = ruby_executable_node(node, &state);
|
|
if (valid)
|
|
state = ruby_exec_node(node);
|
|
if (state || !valid) {
|
|
// The message is formatted for and automatically spits
|
|
// out to the terminal, so let's leave it that way for now
|
|
/*
|
|
VALUE exc = rb_errinfo();
|
|
#if RAPI_FULL >= 250
|
|
VALUE msg = rb_funcall(exc, rb_intern("full_message"), 0);
|
|
#else
|
|
VALUE msg = rb_funcall(exc, rb_intern("message"), 0);
|
|
#endif
|
|
*/
|
|
showMsg("An error occurred while initializing Ruby. (Invalid JIT settings?)");
|
|
ruby_cleanup(state);
|
|
shState->rtData().rqTermAck.set();
|
|
return;
|
|
}
|
|
rb_enc_set_default_internal(rb_enc_from_encoding(rb_utf8_encoding()));
|
|
rb_enc_set_default_external(rb_enc_from_encoding(rb_utf8_encoding()));
|
|
#else
|
|
ruby_init();
|
|
rb_eval_string("$KCODE='U'");
|
|
#ifdef __WIN32__
|
|
if (!conf.winConsole) {
|
|
VALUE iostr = rb_str_new2("NUL");
|
|
// Sysinit isn't a thing yet, so send io to /dev/null instead
|
|
rb_funcall(rb_gv_get("$stderr"), rb_intern("reopen"), 1, iostr);
|
|
rb_funcall(rb_gv_get("$stdout"), rb_intern("reopen"), 1, iostr);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
VALUE rbArgv = rb_get_argv();
|
|
for (const auto &str : conf.launchArgs)
|
|
rb_ary_push(rbArgv, rb_utf8_str_new_cstr(str.c_str()));
|
|
|
|
// Duplicates get pushed for some reason
|
|
rb_funcall(rbArgv, rb_intern("uniq!"), 0);
|
|
|
|
VALUE lpaths = rb_gv_get(":");
|
|
rb_ary_clear(lpaths);
|
|
|
|
#if defined(MKXPZ_BUILD_XCODE) && RAPI_MAJOR >= 2
|
|
std::string resPath = mkxp_fs::getResourcePath();
|
|
resPath += "/Ruby/" + std::to_string(RAPI_MAJOR) + "." + std::to_string(RAPI_MINOR) + ".0";
|
|
rb_ary_push(lpaths, rb_str_new(resPath.c_str(), resPath.size()));
|
|
#endif
|
|
|
|
if (!conf.rubyLoadpaths.empty()) {
|
|
/* Setup custom load paths */
|
|
for (size_t i = 0; i < conf.rubyLoadpaths.size(); ++i) {
|
|
std::string &path = conf.rubyLoadpaths[i];
|
|
|
|
VALUE pathv = rb_str_new(path.c_str(), path.size());
|
|
rb_ary_push(lpaths, pathv);
|
|
}
|
|
}
|
|
#ifndef WORKDIR_CURRENT
|
|
else {
|
|
rb_ary_push(lpaths, rb_utf8_str_new_cstr(mkxp_fs::getCurrentDirectory().c_str()));
|
|
}
|
|
#endif
|
|
|
|
RbData rbData;
|
|
shState->setBindingData(&rbData);
|
|
BacktraceData btData;
|
|
|
|
mriBindingInit();
|
|
|
|
std::string &customScript = conf.customScript;
|
|
if (!customScript.empty())
|
|
runCustomScript(customScript);
|
|
else
|
|
runRMXPScripts(btData);
|
|
|
|
#if RAPI_FULL > 187
|
|
VALUE exc = rb_errinfo();
|
|
#else
|
|
VALUE exc = rb_gv_get("$!");
|
|
#endif
|
|
if (!NIL_P(exc) && !rb_obj_is_kind_of(exc, rb_eSystemExit))
|
|
showExc(exc, btData);
|
|
|
|
ruby_cleanup(0);
|
|
|
|
shState->rtData().rqTermAck.set();
|
|
}
|
|
|
|
static void mriBindingTerminate() { rb_raise(rb_eSystemExit, " "); }
|
|
|
|
static void mriBindingReset() { rb_raise(getRbData()->exc[Reset], " "); }
|