diff --git a/mkxp.conf.sample b/mkxp.conf.sample index 86c33a9..fcb1894 100644 --- a/mkxp.conf.sample +++ b/mkxp.conf.sample @@ -163,3 +163,6 @@ # Activate "reverb" effect for midi playback # # midi.reverb=false + +# Specify character encoding of Game.ini +# iniCodec=UTF-8 diff --git a/mkxp.pro b/mkxp.pro index 1e42f8e..02a1f31 100644 --- a/mkxp.pro +++ b/mkxp.pro @@ -8,12 +8,16 @@ INCLUDEPATH += . src CONFIG(release, debug|release): DEFINES += NDEBUG -CONFIG += MIDI +CONFIG += MIDI INI_CODEC DISABLE_MIDI { CONFIG -= MIDI } +DISABLE_INI_CODEC { + CONFIG -= INI_CODEC +} + isEmpty(BINDING) { BINDING = MRI } @@ -77,8 +81,9 @@ contains(RGSS_VER, 3) { unix { CONFIG += link_pkgconfig - PKGCONFIG += sigc++-2.0 pixman-1 zlib physfs \ - sdl2 SDL2_image SDL2_ttf SDL_sound openal + PKGCONFIG += sigc++-2.0 pixman-1 physfs \ + sdl2 SDL2_image SDL2_ttf SDL_sound + LIBS += -lz RGSS2 { PKGCONFIG += vorbisfile @@ -112,6 +117,17 @@ unix { LIBS += -lboost_program_options$$BOOST_LIB_SUFFIX } +unix:!macx { + PKGCONFIG += openal +} + +macx { + CONFIG -= app_bundle + QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.6 + INCLUDEPATH += /System/Library/Frameworks/OpenAL.framework/Headers + LIBS += -liconv -framework OpenAL +} + # Input HEADERS += \ src/quadarray.h \ @@ -248,6 +264,13 @@ MIDI { DEFINES += MIDI } +INI_CODEC { + DEFINES += INI_CODEC + win32 { + LIBS += -liconv + } +} + defineReplace(xxdOutput) { return($$basename(1).xxd) } diff --git a/src/config.cpp b/src/config.cpp index 8eb29cd..7b03195 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -27,6 +27,10 @@ #include +#ifdef INI_CODEC +#include +#endif + #include "debugwriter.h" #include "util.h" @@ -78,7 +82,8 @@ void Config::read(int argc, char *argv[]) PO_DESC(midi.reverb, bool) \ PO_DESC(customScript, std::string) \ PO_DESC(pathCache, bool) \ - PO_DESC(useScriptNames, bool) + PO_DESC(useScriptNames, bool) \ + PO_DESC(iniCodec, std::string) // Not gonna take your shit boost #define GUARD_ALL( exp ) try { exp } catch(...) {} @@ -141,6 +146,44 @@ static std::string baseName(const std::string &path) return path.substr(pos + 1); } +static std::string iconvString(iconv_t cd, const std::string &srcStr) +{ + size_t srcSize = srcStr.length(); + /* iconv unnecessarily requires a char** instead of const char** */ + char *src = const_cast(srcStr.c_str()); + + size_t dstBuffSize = srcSize; + char *dstBuff = static_cast(malloc(dstBuffSize)); + + /* These will be changed by iconv */ + size_t dstSize = dstBuffSize; + char *dst = dstBuff; + + /* Continue to convert/realloc until we've converted everything */ + while (iconv(cd, &src, &srcSize, &dst, &dstSize) == (size_t) -1) + { + if (errno == E2BIG) + { + /* Grow buffer and retry */ + size_t total = dstBuffSize - dstSize; + dstSize += dstBuffSize; + dstBuffSize *= 2; + dstBuff = static_cast(realloc(dstBuff, dstBuffSize)); + dst = dstBuff + total; + } + else + { + free(dstBuff); + return std::string(); + } + } + + /* Create std::string and return */ + std::string dstStr(dstBuff, dstBuff + (dstBuffSize - dstSize)); + free(dstBuff); + return dstStr; +} + void Config::readGameINI() { if (!customScript.empty()) @@ -171,6 +214,16 @@ void Config::readGameINI() strReplace(game.scripts, '\\', '/'); +#ifdef INI_CODEC + if (!iniCodec.empty()) + { + iconv_t cd = iconv_open("utf-8", iniCodec.c_str()); + game.title = iconvString(cd, game.title); + game.scripts = iconvString(cd, game.scripts); + iconv_close(cd); + } +#endif + if (game.title.empty()) game.title = baseName(gameFolder); } diff --git a/src/config.h b/src/config.h index 8427abb..f799692 100644 --- a/src/config.h +++ b/src/config.h @@ -66,6 +66,8 @@ struct Config } midi; bool useScriptNames; + + std::string iniCodec; std::string customScript; std::vector rtps; diff --git a/src/filesystem.cpp b/src/filesystem.cpp index 74ac971..dfde81b 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -37,6 +37,10 @@ #include #include +#ifdef __APPLE__ +#include +#endif + static inline PHYSFS_File *sdlPHYS(SDL_RWops *ops) { return static_cast(ops->hidden.unknown.data1); @@ -137,6 +141,35 @@ struct FileSystemPrivate std::vector extensions[FileSystem::Undefined+1]; +#ifdef __APPLE__ + /* Convert NFD UTF-8 filenames to NFC UTF-8 filenames */ + iconv_t nfd2nfc; + + FileSystemPrivate() + { + nfd2nfc = iconv_open("utf-8", "utf-8-mac"); + } + + ~FileSystemPrivate() + { + iconv_close(nfd2nfc); + } + + void nfcFromNfd(char *dst, const char *src, size_t dstSize) + { + size_t srcSize = strlen(src); + /* Reserve room for null terminator */ + --dstSize; + /* iconv takes a char** instead of a const char**, even though + * the string data isn't written to. */ + iconv(nfd2nfc, + const_cast(&src), &srcSize, + &dst, &dstSize); + /* Null-terminate */ + *dst = 0; + } +#endif + /* Attempt to locate an extension string in a filename. * Either a pointer into the input string pointing at the * extension, or null is returned */ @@ -376,7 +409,14 @@ static void cacheEnumCB(void *d, const char *origdir, else snprintf(buf, sizeof(buf), "%s/%s", origdir, fname); - char *ptr = buf; +#ifdef __APPLE__ + char bufNfc[sizeof(buf)]; + p->nfcFromNfd(bufNfc, buf, sizeof(bufNfc)); +#else + char *const bufNfc = buf; +#endif + + char *ptr = bufNfc; /* Trim leading slash */ if (*ptr == '/') @@ -384,7 +424,7 @@ static void cacheEnumCB(void *d, const char *origdir, std::string mixedCase(ptr); - for (char *p = buf; *p; ++p) + for (char *p = bufNfc; *p; ++p) *p = tolower(*p); std::string lowerCase(ptr); diff --git a/src/gl-fun.h b/src/gl-fun.h index e555c99..b87fa16 100644 --- a/src/gl-fun.h +++ b/src/gl-fun.h @@ -59,7 +59,7 @@ typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); typedef void (APIENTRY * _GLDEBUGPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void *userParam); typedef void (APIENTRYP _PFNGLDEBUGMESSAGECALLBACKPROC) (_GLDEBUGPROC callback, const void *userParam); -#ifdef GLES2_HEADER +#if defined GLES2_HEADER || defined __APPLE__ #define GL_NUM_EXTENSIONS 0x821D /* Buffer object */