From 42b1cdcc2179e84dda3e587485f7a73b05610492 Mon Sep 17 00:00:00 2001 From: Roza Date: Tue, 22 Jun 2021 18:32:39 -0400 Subject: [PATCH] Make json config accessible from `System::CONFIG` --- binding/binding-mri.cpp | 6 +- binding/filesystem-binding.cpp | 3 +- binding/http-binding.cpp | 4 +- binding/input-binding.cpp | 11 +- macos/mkxp-z.xcodeproj/project.pbxproj | 2 + src/config.cpp | 150 ++++--------------------- src/config.h | 5 + src/eventthread.h | 10 +- src/meson.build | 13 +-- src/util/encoding.h | 68 +++++++++++ 10 files changed, 118 insertions(+), 154 deletions(-) create mode 100644 src/util/encoding.h diff --git a/binding/binding-mri.cpp b/binding/binding-mri.cpp index cf4196d..ceb0513 100644 --- a/binding/binding-mri.cpp +++ b/binding/binding-mri.cpp @@ -30,12 +30,12 @@ #include "util/boost-hash.h" #include "util/exception.h" +#include "config.h" #include "binding-util.h" #include "binding.h" #include "sharedstate.h" -#include "config.h" #include "eventthread.h" #include @@ -144,6 +144,8 @@ RB_METHOD(mriRgssMain); RB_METHOD(mriRgssStop); RB_METHOD(_kernelCaller); +VALUE json2rb(json5pp::value const &v); + static void mriBindingInit() { tableBindingInit(); etcBindingInit(); @@ -255,6 +257,8 @@ static void mriBindingInit() { rb_gv_set("TEST", debug); rb_gv_set("BTEST", rb_bool_new(shState->config().editor.battleTest)); + + rb_define_const(mod, "CONFIG", json2rb(shState->config().raw)); // Set $stdout and its ilk accordingly on Windows #ifdef __WIN32__ diff --git a/binding/filesystem-binding.cpp b/binding/filesystem-binding.cpp index 34bf579..74c1d3b 100644 --- a/binding/filesystem-binding.cpp +++ b/binding/filesystem-binding.cpp @@ -19,11 +19,12 @@ ** along with mkxp. If not, see . */ +#include "src/config.h" + #include "binding-util.h" #include "filesystem.h" #include "sharedstate.h" -#include "src/config.h" #include "src/util/util.h" #if RAPI_FULL > 187 diff --git a/binding/http-binding.cpp b/binding/http-binding.cpp index fad14fe..f499935 100644 --- a/binding/http-binding.cpp +++ b/binding/http-binding.cpp @@ -293,8 +293,8 @@ RB_METHOD(httpJsonParse) { try { v = json5pp::parse5(RSTRING_PTR(jsonv)); } - catch (...) { - raiseRbExc(Exception(Exception::MKXPError, "Failed to parse JSON")); + catch (const std::exception &e) { + raiseRbExc(Exception(Exception::MKXPError, "Failed to parse JSON: %s", e.what())); } return json2rb(v); diff --git a/binding/input-binding.cpp b/binding/input-binding.cpp index 3fff6c5..a40c2cc 100644 --- a/binding/input-binding.cpp +++ b/binding/input-binding.cpp @@ -19,17 +19,18 @@ ** along with mkxp. If not, see . */ +#include +#include + + +#include "eventthread.h" + #include "binding-util.h" #include "util/exception.h" #include "input/input.h" #include "sharedstate.h" #include "src/util/util.h" -#include "eventthread.h" - -#include -#include - RB_METHOD(inputDelta) { RB_UNUSED_PARAM; diff --git a/macos/mkxp-z.xcodeproj/project.pbxproj b/macos/mkxp-z.xcodeproj/project.pbxproj index be34542..b6d2a5f 100644 --- a/macos/mkxp-z.xcodeproj/project.pbxproj +++ b/macos/mkxp-z.xcodeproj/project.pbxproj @@ -977,6 +977,7 @@ 3B5A8463256A46B200BAF2E5 /* systemImplApple.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = systemImplApple.mm; sourceTree = ""; }; 3B5E1F0925A881FB0086FFDC /* libEGL.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libEGL.dylib; path = Dependencies/Frameworks/ANGLE/libEGL.dylib; sourceTree = ""; }; 3B5E1F0A25A881FB0086FFDC /* libGLESv2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libGLESv2.dylib; path = Dependencies/Frameworks/ANGLE/libGLESv2.dylib; sourceTree = ""; }; + 3B609374268274CE0038E9D6 /* encoding.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = encoding.h; sourceTree = ""; }; 3BA08EA4256641ED00449CFF /* Assets.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Assets.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 3BA08EA6256641EE00449CFF /* Assets.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Assets.plist; sourceTree = ""; }; 3BA6944E263DAB53004194EB /* libnsgif.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = libnsgif.c; sourceTree = ""; }; @@ -1342,6 +1343,7 @@ 3B10EE1F2569348E00372D13 /* json5pp.hpp */, 3B1BC0DF266F7C0C00794D22 /* iniconfig.h */, 3B1BC0E0266F7C0C00794D22 /* iniconfig.cpp */, + 3B609374268274CE0038E9D6 /* encoding.h */, ); path = util; sourceTree = ""; diff --git a/src/config.cpp b/src/config.cpp index 9540937..29de5ed 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -19,95 +19,13 @@ #include "util/util.h" #include "util/json5pp.hpp" -#include "util/iniconfig.h" -#if defined(MKXPZ_BUILD_XCODE) || defined(MKXPZ_INI_ENCODING) -#include -#include -#include -#endif +#include "util/iniconfig.h" +#include "util/encoding.h" + namespace json = json5pp; -/* http://stackoverflow.com/a/1031773 */ -static bool validUtf8(const char *string) -{ - const uint8_t *bytes = (uint8_t*) string; - - while(*bytes) - { - if( (/* ASCII - * use bytes[0] <= 0x7F to allow ASCII control characters */ - bytes[0] == 0x09 || - bytes[0] == 0x0A || - bytes[0] == 0x0D || - (0x20 <= bytes[0] && bytes[0] <= 0x7E) - ) - ) { - bytes += 1; - continue; - } - - if( (/* non-overlong 2-byte */ - (0xC2 <= bytes[0] && bytes[0] <= 0xDF) && - (0x80 <= bytes[1] && bytes[1] <= 0xBF) - ) - ) { - bytes += 2; - continue; - } - - if( (/* excluding overlongs */ - bytes[0] == 0xE0 && - (0xA0 <= bytes[1] && bytes[1] <= 0xBF) && - (0x80 <= bytes[2] && bytes[2] <= 0xBF) - ) || - (/* straight 3-byte */ - ((0xE1 <= bytes[0] && bytes[0] <= 0xEC) || - bytes[0] == 0xEE || - bytes[0] == 0xEF) && - (0x80 <= bytes[1] && bytes[1] <= 0xBF) && - (0x80 <= bytes[2] && bytes[2] <= 0xBF) - ) || - (/* excluding surrogates */ - bytes[0] == 0xED && - (0x80 <= bytes[1] && bytes[1] <= 0x9F) && - (0x80 <= bytes[2] && bytes[2] <= 0xBF) - ) - ) { - bytes += 3; - continue; - } - - if( (/* planes 1-3 */ - bytes[0] == 0xF0 && - (0x90 <= bytes[1] && bytes[1] <= 0xBF) && - (0x80 <= bytes[2] && bytes[2] <= 0xBF) && - (0x80 <= bytes[3] && bytes[3] <= 0xBF) - ) || - (/* planes 4-15 */ - (0xF1 <= bytes[0] && bytes[0] <= 0xF3) && - (0x80 <= bytes[1] && bytes[1] <= 0xBF) && - (0x80 <= bytes[2] && bytes[2] <= 0xBF) && - (0x80 <= bytes[3] && bytes[3] <= 0xBF) - ) || - (/* plane 16 */ - bytes[0] == 0xF4 && - (0x80 <= bytes[1] && bytes[1] <= 0x8F) && - (0x80 <= bytes[2] && bytes[2] <= 0xBF) && - (0x80 <= bytes[3] && bytes[3] <= 0xBF) - ) - ) { - bytes += 4; - continue; - } - - return false; - } - - return true; -} - std::string prefPath(const char *org, const char *app) { char *path = SDL_GetPrefPath(org, app); if (!path) @@ -238,7 +156,6 @@ try { exp } catch (...) {} editor.battleTest = true; for (int i = 1; i < argc; i++) { - const char *arg = argv[i]; launchArgs.push_back(argv[i]); } } @@ -247,13 +164,20 @@ try { exp } catch (...) {} json::value confData = json::value(0); try { - confData = json::parse5(mkxp_fs::contentsOfFileAsString(CONF_FILE)); - } catch (...) { - Debug() << "Failed to parse JSON configuration"; + std::string cfg = mkxp_fs::contentsOfFileAsString(CONF_FILE); + confData = json::parse5(Encoding::convertString(cfg)); + } + catch (const std::exception &e) { + Debug() << "Failed to parse JSON configuration:" << e.what(); + } + catch (const Exception &e) { + Debug() << "Failed to parse JSON configuration: Unknown encoding"; } if (!confData.is_object()) confData = json::object({}); + + raw = confData; copyObject(optsJ, confData); copyObject(opts["bindingNames"], confData.as_object()["bindingNames"], "bindingNames ."); @@ -347,6 +271,7 @@ void Config::readGameINI() { std::string iniFileName(execName + ".ini"); SDLRWStream iniFile(iniFileName.c_str(), "r"); + bool convSuccess = false; if (iniFile) { INIConfiguration ic; @@ -368,48 +293,13 @@ void Config::readGameINI() { else Debug() << "Could not read" << iniFileName; - bool convSuccess = false; - - // Attempt to convert from other encodings to UTF-8 - if (!validUtf8(game.title.c_str())) - { -#if defined(MKXPZ_BUILD_XCODE) || defined(MKXPZ_INI_ENCODING) - uchardet_t ud = uchardet_new(); - uchardet_handle_data(ud, game.title.c_str(), game.title.length()); - uchardet_data_end(ud); - const char *charset = uchardet_get_charset(ud); - - Debug() << iniFileName << ": Assuming encoding is" << charset << "..."; - iconv_t cd = iconv_open("UTF-8", charset); - - uchardet_delete(ud); - - size_t inLen = game.title.size(); - size_t outLen = inLen * 4; - std::string buf(outLen, '\0'); - char *inPtr = const_cast(game.title.c_str()); - char *outPtr = const_cast(buf.c_str()); - - errno = 0; - size_t result = iconv(cd, &inPtr, &inLen, &outPtr, &outLen); - - iconv_close(cd); - - if (result != (size_t)-1 && errno == 0) - { - buf.resize(buf.size()-outLen); - game.title = buf; - convSuccess = true; - } - else { - Debug() << iniFileName << ": failed to convert game title to UTF-8"; - } -#else - Debug() << iniFileName << ": Game title isn't valid UTF-8"; -#endif - } - else + try { + game.title = Encoding::convertString(game.title); convSuccess = true; + } + catch (const Exception &e) { + Debug() << iniFileName + ": Could not determine encoding of Game.Title"; + } if (game.title.empty() || !convSuccess) game.title = "mkxp-z"; diff --git a/src/config.h b/src/config.h index 8d7b697..b67fdb7 100644 --- a/src/config.h +++ b/src/config.h @@ -22,11 +22,16 @@ #ifndef CONFIG_H #define CONFIG_H +#include "util/json5pp.hpp" + #include #include #include struct Config { + // Used for sending the JSON data to Ruby as System::CONFIG + json5pp::value raw; + int rgssVersion; bool debugMode; diff --git a/src/eventthread.h b/src/eventthread.h index 475c035..551a833 100644 --- a/src/eventthread.h +++ b/src/eventthread.h @@ -22,11 +22,6 @@ #ifndef EVENTTHREAD_H #define EVENTTHREAD_H -#include "config.h" -#include "etc-internal.h" -#include "sdl-util.h" -#include "keybindings.h" - #include #include #include @@ -36,6 +31,11 @@ #include +#include "config.h" +#include "etc-internal.h" +#include "sdl-util.h" +#include "keybindings.h" + struct RGSSThreadData; typedef struct MKXPZ_ALCDEVICE ALCdevice; struct SDL_Window; diff --git a/src/meson.build b/src/meson.build index 73e60d3..e75c7aa 100755 --- a/src/meson.build +++ b/src/meson.build @@ -12,6 +12,8 @@ pixman = dependency('pixman-1', static: build_static) png = dependency('libpng', static: build_static) jpeg = dependency('libjpeg', static: build_static) zlib = dependency('zlib', static: build_static) +iconv = compilers['cpp'].find_library('iconv', static: build_static) +uchardet = dependency('uchardet', static: build_static) if host_system == 'windows' bz2 = dependency('bzip2', static: build_static) @@ -33,15 +35,6 @@ if get_option('enable-https') == true endif endif -# If iconv is present, mkxp will try to identify and convert the encoding -# of the game's title -iconv = compilers['cpp'].find_library('iconv', static: build_static, required: false) -if iconv.found() == true - global_dependencies += iconv - global_dependencies += dependency('uchardet', static: build_static) - global_args += '-DMKXPZ_INI_ENCODING' -endif - # Windows needs to be treated like a special needs child here explicit_libs = '' if host_system == 'windows' @@ -85,7 +78,7 @@ global_include_dirs += include_directories('.', 'util', 'util/sigslot', 'util/sigslot/adapter' ) -global_dependencies += [openal, zlib, bz2, sdl2, sdl_sound, pixman, physfs, vorbisfile, vorbis, ogg, sdl2_ttf, freetype, sdl2_image, png, jpeg] +global_dependencies += [openal, zlib, bz2, sdl2, sdl_sound, pixman, physfs, vorbisfile, vorbis, ogg, sdl2_ttf, freetype, sdl2_image, png, jpeg, iconv, uchardet] if host_system == 'windows' global_dependencies += compilers['cpp'].find_library('wsock32') endif diff --git a/src/util/encoding.h b/src/util/encoding.h new file mode 100644 index 0000000..4bddcaf --- /dev/null +++ b/src/util/encoding.h @@ -0,0 +1,68 @@ +// +// encoding.h +// mkxp-z +// +// Created by ゾロアーク on 6/22/21. +// + +#ifndef encoding_h +#define encoding_h + +#include +#include + +#include "util/encoding.h" +#include +#include +#include + +namespace Encoding { + +static std::string getCharset(std::string &str) { + uchardet_t ud = uchardet_new(); + uchardet_handle_data(ud, str.c_str(), str.length()); + uchardet_data_end(ud); + + std::string ret(uchardet_get_charset(ud)); + uchardet_delete(ud); + + if (ret.empty()) + throw Exception(Exception::MKXPError, "Could not detect encoding of \"%s\"", str.c_str()); + return ret; +} + +static std::string convertString(std::string &str) { + + std::string charset = getCharset(str); + + // Conversion doesn't need to happen if it's already UTF-8 + if (!strcmp(charset.c_str(), "UTF-8") || !strcmp(charset.c_str(), "ASCII")) { + return std::string(str); + } + + iconv_t cd = iconv_open("UTF-8", getCharset(str).c_str()); + + size_t inLen = str.size(); + size_t outLen = inLen * 4; + std::string buf(outLen, '\0'); + char *inPtr = const_cast(str.c_str()); + char *outPtr = const_cast(buf.c_str()); + + errno = 0; + size_t result = iconv(cd, &inPtr, &inLen, &outPtr, &outLen); + + iconv_close(cd); + + if (result != (size_t)-1 && errno == 0) + { + buf.resize(buf.size()-outLen); + } + else { + throw Exception(Exception::MKXPError, "Failed to convert \"%s\" to UTF-8", str.c_str()); + } + + return buf; +} +} + +#endif /* encoding_h */