Make json config accessible from System::CONFIG

This commit is contained in:
Roza 2021-06-22 18:32:39 -04:00
parent 7a9d60cdbc
commit 42b1cdcc21
10 changed files with 118 additions and 154 deletions

View file

@ -30,12 +30,12 @@
#include "util/boost-hash.h" #include "util/boost-hash.h"
#include "util/exception.h" #include "util/exception.h"
#include "config.h"
#include "binding-util.h" #include "binding-util.h"
#include "binding.h" #include "binding.h"
#include "sharedstate.h" #include "sharedstate.h"
#include "config.h"
#include "eventthread.h" #include "eventthread.h"
#include <vector> #include <vector>
@ -144,6 +144,8 @@ RB_METHOD(mriRgssMain);
RB_METHOD(mriRgssStop); RB_METHOD(mriRgssStop);
RB_METHOD(_kernelCaller); RB_METHOD(_kernelCaller);
VALUE json2rb(json5pp::value const &v);
static void mriBindingInit() { static void mriBindingInit() {
tableBindingInit(); tableBindingInit();
etcBindingInit(); etcBindingInit();
@ -255,6 +257,8 @@ static void mriBindingInit() {
rb_gv_set("TEST", debug); rb_gv_set("TEST", debug);
rb_gv_set("BTEST", rb_bool_new(shState->config().editor.battleTest)); 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 // Set $stdout and its ilk accordingly on Windows
#ifdef __WIN32__ #ifdef __WIN32__

View file

@ -19,11 +19,12 @@
** along with mkxp. If not, see <http://www.gnu.org/licenses/>. ** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "src/config.h"
#include "binding-util.h" #include "binding-util.h"
#include "filesystem.h" #include "filesystem.h"
#include "sharedstate.h" #include "sharedstate.h"
#include "src/config.h"
#include "src/util/util.h" #include "src/util/util.h"
#if RAPI_FULL > 187 #if RAPI_FULL > 187

View file

@ -293,8 +293,8 @@ RB_METHOD(httpJsonParse) {
try { try {
v = json5pp::parse5(RSTRING_PTR(jsonv)); v = json5pp::parse5(RSTRING_PTR(jsonv));
} }
catch (...) { catch (const std::exception &e) {
raiseRbExc(Exception(Exception::MKXPError, "Failed to parse JSON")); raiseRbExc(Exception(Exception::MKXPError, "Failed to parse JSON: %s", e.what()));
} }
return json2rb(v); return json2rb(v);

View file

@ -19,17 +19,18 @@
** along with mkxp. If not, see <http://www.gnu.org/licenses/>. ** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <SDL_joystick.h>
#include <string>
#include "eventthread.h"
#include "binding-util.h" #include "binding-util.h"
#include "util/exception.h" #include "util/exception.h"
#include "input/input.h" #include "input/input.h"
#include "sharedstate.h" #include "sharedstate.h"
#include "src/util/util.h" #include "src/util/util.h"
#include "eventthread.h"
#include <SDL_joystick.h>
#include <string>
RB_METHOD(inputDelta) { RB_METHOD(inputDelta) {
RB_UNUSED_PARAM; RB_UNUSED_PARAM;

View file

@ -977,6 +977,7 @@
3B5A8463256A46B200BAF2E5 /* systemImplApple.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = systemImplApple.mm; sourceTree = "<group>"; }; 3B5A8463256A46B200BAF2E5 /* systemImplApple.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = systemImplApple.mm; sourceTree = "<group>"; };
3B5E1F0925A881FB0086FFDC /* libEGL.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libEGL.dylib; path = Dependencies/Frameworks/ANGLE/libEGL.dylib; sourceTree = "<group>"; }; 3B5E1F0925A881FB0086FFDC /* libEGL.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libEGL.dylib; path = Dependencies/Frameworks/ANGLE/libEGL.dylib; sourceTree = "<group>"; };
3B5E1F0A25A881FB0086FFDC /* libGLESv2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libGLESv2.dylib; path = Dependencies/Frameworks/ANGLE/libGLESv2.dylib; sourceTree = "<group>"; }; 3B5E1F0A25A881FB0086FFDC /* libGLESv2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libGLESv2.dylib; path = Dependencies/Frameworks/ANGLE/libGLESv2.dylib; sourceTree = "<group>"; };
3B609374268274CE0038E9D6 /* encoding.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = encoding.h; sourceTree = "<group>"; };
3BA08EA4256641ED00449CFF /* Assets.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Assets.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = "<group>"; }; 3BA08EA6256641EE00449CFF /* Assets.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Assets.plist; sourceTree = "<group>"; };
3BA6944E263DAB53004194EB /* libnsgif.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = libnsgif.c; sourceTree = "<group>"; }; 3BA6944E263DAB53004194EB /* libnsgif.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = libnsgif.c; sourceTree = "<group>"; };
@ -1342,6 +1343,7 @@
3B10EE1F2569348E00372D13 /* json5pp.hpp */, 3B10EE1F2569348E00372D13 /* json5pp.hpp */,
3B1BC0DF266F7C0C00794D22 /* iniconfig.h */, 3B1BC0DF266F7C0C00794D22 /* iniconfig.h */,
3B1BC0E0266F7C0C00794D22 /* iniconfig.cpp */, 3B1BC0E0266F7C0C00794D22 /* iniconfig.cpp */,
3B609374268274CE0038E9D6 /* encoding.h */,
); );
path = util; path = util;
sourceTree = "<group>"; sourceTree = "<group>";

View file

@ -19,95 +19,13 @@
#include "util/util.h" #include "util/util.h"
#include "util/json5pp.hpp" #include "util/json5pp.hpp"
#include "util/iniconfig.h"
#if defined(MKXPZ_BUILD_XCODE) || defined(MKXPZ_INI_ENCODING) #include "util/iniconfig.h"
#include <iconv.h> #include "util/encoding.h"
#include <uchardet.h>
#include <errno.h>
#endif
namespace json = json5pp; 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) { std::string prefPath(const char *org, const char *app) {
char *path = SDL_GetPrefPath(org, app); char *path = SDL_GetPrefPath(org, app);
if (!path) if (!path)
@ -238,7 +156,6 @@ try { exp } catch (...) {}
editor.battleTest = true; editor.battleTest = true;
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
const char *arg = argv[i];
launchArgs.push_back(argv[i]); launchArgs.push_back(argv[i]);
} }
} }
@ -247,13 +164,20 @@ try { exp } catch (...) {}
json::value confData = json::value(0); json::value confData = json::value(0);
try { try {
confData = json::parse5(mkxp_fs::contentsOfFileAsString(CONF_FILE)); std::string cfg = mkxp_fs::contentsOfFileAsString(CONF_FILE);
} catch (...) { confData = json::parse5(Encoding::convertString(cfg));
Debug() << "Failed to parse JSON configuration"; }
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()) if (!confData.is_object())
confData = json::object({}); confData = json::object({});
raw = confData;
copyObject(optsJ, confData); copyObject(optsJ, confData);
copyObject(opts["bindingNames"], confData.as_object()["bindingNames"], "bindingNames ."); copyObject(opts["bindingNames"], confData.as_object()["bindingNames"], "bindingNames .");
@ -347,6 +271,7 @@ void Config::readGameINI() {
std::string iniFileName(execName + ".ini"); std::string iniFileName(execName + ".ini");
SDLRWStream iniFile(iniFileName.c_str(), "r"); SDLRWStream iniFile(iniFileName.c_str(), "r");
bool convSuccess = false;
if (iniFile) if (iniFile)
{ {
INIConfiguration ic; INIConfiguration ic;
@ -368,48 +293,13 @@ void Config::readGameINI() {
else else
Debug() << "Could not read" << iniFileName; Debug() << "Could not read" << iniFileName;
bool convSuccess = false; try {
game.title = Encoding::convertString(game.title);
// 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<char*>(game.title.c_str());
char *outPtr = const_cast<char*>(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
convSuccess = true; convSuccess = true;
}
catch (const Exception &e) {
Debug() << iniFileName + ": Could not determine encoding of Game.Title";
}
if (game.title.empty() || !convSuccess) if (game.title.empty() || !convSuccess)
game.title = "mkxp-z"; game.title = "mkxp-z";

View file

@ -22,11 +22,16 @@
#ifndef CONFIG_H #ifndef CONFIG_H
#define CONFIG_H #define CONFIG_H
#include "util/json5pp.hpp"
#include <set> #include <set>
#include <string> #include <string>
#include <vector> #include <vector>
struct Config { struct Config {
// Used for sending the JSON data to Ruby as System::CONFIG
json5pp::value raw;
int rgssVersion; int rgssVersion;
bool debugMode; bool debugMode;

View file

@ -22,11 +22,6 @@
#ifndef EVENTTHREAD_H #ifndef EVENTTHREAD_H
#define EVENTTHREAD_H #define EVENTTHREAD_H
#include "config.h"
#include "etc-internal.h"
#include "sdl-util.h"
#include "keybindings.h"
#include <SDL_scancode.h> #include <SDL_scancode.h>
#include <SDL_joystick.h> #include <SDL_joystick.h>
#include <SDL_mouse.h> #include <SDL_mouse.h>
@ -36,6 +31,11 @@
#include <stdint.h> #include <stdint.h>
#include "config.h"
#include "etc-internal.h"
#include "sdl-util.h"
#include "keybindings.h"
struct RGSSThreadData; struct RGSSThreadData;
typedef struct MKXPZ_ALCDEVICE ALCdevice; typedef struct MKXPZ_ALCDEVICE ALCdevice;
struct SDL_Window; struct SDL_Window;

View file

@ -12,6 +12,8 @@ pixman = dependency('pixman-1', static: build_static)
png = dependency('libpng', static: build_static) png = dependency('libpng', static: build_static)
jpeg = dependency('libjpeg', static: build_static) jpeg = dependency('libjpeg', static: build_static)
zlib = dependency('zlib', 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' if host_system == 'windows'
bz2 = dependency('bzip2', static: build_static) bz2 = dependency('bzip2', static: build_static)
@ -33,15 +35,6 @@ if get_option('enable-https') == true
endif endif
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 # Windows needs to be treated like a special needs child here
explicit_libs = '' explicit_libs = ''
if host_system == 'windows' if host_system == 'windows'
@ -85,7 +78,7 @@ global_include_dirs += include_directories('.',
'util', 'util/sigslot', 'util/sigslot/adapter' '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' if host_system == 'windows'
global_dependencies += compilers['cpp'].find_library('wsock32') global_dependencies += compilers['cpp'].find_library('wsock32')
endif endif

68
src/util/encoding.h Normal file
View file

@ -0,0 +1,68 @@
//
// encoding.h
// mkxp-z
//
// Created by ゾロアーク on 6/22/21.
//
#ifndef encoding_h
#define encoding_h
#include <string>
#include <string.h>
#include "util/encoding.h"
#include <iconv.h>
#include <uchardet.h>
#include <errno.h>
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<char*>(str.c_str());
char *outPtr = const_cast<char*>(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 */