From 99b6e6b47ca58958eb4196e1a06fd43c242f6374 Mon Sep 17 00:00:00 2001 From: bubio Date: Tue, 16 Jun 2026 18:42:58 +0900 Subject: [PATCH] =?UTF-8?q?macOS=E3=81=A7=E3=81=AE=E3=83=91=E3=82=B9?= =?UTF-8?q?=E6=96=87=E5=AD=97=E5=88=97=E8=A7=A3=E6=B1=BA=E3=81=ABFoundatio?= =?UTF-8?q?n=E3=82=92=E4=BD=BF=E7=94=A8=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Builder/External/SDL2/CMakeLists.txt | 8 +--- CMakeLists.txt | 13 ++++++- Source/UI/converter.cpp | 41 +++++++------------- Source/UI/converter_mac.h | 8 ++++ Source/UI/converter_mac.mm | 38 +++++++++++++++++++ Tests/converter_mac_test.mm | 57 ++++++++++++++++++++++++++++ 6 files changed, 130 insertions(+), 35 deletions(-) create mode 100644 Source/UI/converter_mac.h create mode 100644 Source/UI/converter_mac.mm create mode 100644 Tests/converter_mac_test.mm diff --git a/Builder/External/SDL2/CMakeLists.txt b/Builder/External/SDL2/CMakeLists.txt index 6371519..03b4b63 100644 --- a/Builder/External/SDL2/CMakeLists.txt +++ b/Builder/External/SDL2/CMakeLists.txt @@ -12,16 +12,10 @@ else() endif() set(SDL_TEST_ENABLED_BY_DEFAULT OFF) -# Apple環境の場合、システムのiconvを使用する設定 -if(APPLE) - set(SDL_SYSTEM_ICONV ON CACHE BOOL "Use iconv() from system-installed libraries") - set(SDL_LIBICONV OFF CACHE BOOL "Prefer iconv() from libiconv, if available, over libc version") -endif() - include(functions/FetchContent_ExcludeFromAll_backport) include(FetchContent) FetchContent_Declare(SDL2 URL https://github.com/libsdl-org/SDL/releases/download/release-2.32.8/SDL2-2.32.8.tar.gz URL_HASH SHA256=0ca83e9c9b31e18288c7ec811108e58bac1f1bb5ec6577ad386830eac51c787e ) -FetchContent_MakeAvailable_ExcludeFromAll(SDL2) \ No newline at end of file +FetchContent_MakeAvailable_ExcludeFromAll(SDL2) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5db5e31..5fe4ff4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,7 +149,10 @@ set(SRCS ) if(APPLE) - list(APPEND SRCS Source/UI/pathresolver_mac.mm) + list(APPEND SRCS + Source/UI/converter_mac.mm + Source/UI/pathresolver_mac.mm + ) endif() set(BIN_TARGET "xm8") @@ -232,6 +235,14 @@ if(BUILD_TESTING) add_test(NAME pathresolver_test COMMAND pathresolver_test) if(APPLE) + add_executable(converter_mac_test + Tests/converter_mac_test.mm + Source/UI/converter_mac.mm + ) + target_include_directories(converter_mac_test PRIVATE Source/UI) + target_link_libraries(converter_mac_test PRIVATE "-framework Foundation") + add_test(NAME converter_mac_test COMMAND converter_mac_test) + add_executable(pathresolver_mac_test Tests/pathresolver_mac_test.mm Source/UI/pathresolver.cpp diff --git a/Source/UI/converter.cpp b/Source/UI/converter.cpp index 587ae0e..94a1a5e 100644 --- a/Source/UI/converter.cpp +++ b/Source/UI/converter.cpp @@ -14,6 +14,9 @@ #include "common.h" #include "converter.h" +#if defined(__APPLE__) +#include "converter_mac.h" +#endif #if defined(__ANDROID__) #include "xm8jni.h" #endif @@ -100,29 +103,7 @@ void Converter::Deinit() // int Converter::Utf8macToUtf8(const char *src, char *dst, size_t len) { - SDL_iconv_t conv; // conversion descriptor - char buf[_MAX_PATH]; - const char *src_buf = buf; - char *dst_buf = dst; - size_t src_len = strlen(src); - size_t dst_len = len - 1; - int ret = 0; - strncpy(buf, src, sizeof(buf)); - if ((conv = SDL_iconv_open("UTF-8", "UTF-8-MAC")) == (SDL_iconv_t) - 1) { - fprintf(stderr, "error: %s: %s\n", __FUNCTION__, SDL_GetError()); - return -1; - } - if (SDL_iconv(conv, &src_buf, &src_len, &dst_buf, &dst_len) == (size_t) - 1) { - fprintf(stderr, "error: %s: %s\n", __FUNCTION__, SDL_GetError()); - ret = -1; - } - *dst_buf = '\0'; - if (SDL_iconv_close(conv) == -1) { - fprintf(stderr, "error: %s: %s\n", __FUNCTION__, SDL_GetError()); - ret = -1; - } - - return ret; + return NormalizeUtf8ToNfcMac(src, dst, len); } #endif @@ -244,10 +225,16 @@ void Converter::SjisToUtf(const char *sjis, char *utf) // void Converter::UtfToSjis(const char *utf, char *sjis) { -#if defined(__APPLE__) - char* utf8_nfc = (char*)SDL_malloc(strlen(utf) * 3 + 1); - Utf8macToUtf8(utf, utf8_nfc, strlen(utf) + 1); - UtfNfcToSjis(utf8_nfc, sjis); +#if defined(__APPLE__) + size_t utf8_nfc_len = strlen(utf) * 3 + 1; + char* utf8_nfc = (char*)SDL_malloc(utf8_nfc_len); + if (utf8_nfc != NULL && Utf8macToUtf8(utf, utf8_nfc, + utf8_nfc_len) == 0) { + UtfNfcToSjis(utf8_nfc, sjis); + } + else { + UtfNfcToSjis(utf, sjis); + } SDL_free(utf8_nfc); #elif defined(__ANDROID__) char* utf8_nfc = (char*)SDL_malloc(strlen(utf) * 3 + 1); diff --git a/Source/UI/converter_mac.h b/Source/UI/converter_mac.h new file mode 100644 index 0000000..4bd5844 --- /dev/null +++ b/Source/UI/converter_mac.h @@ -0,0 +1,8 @@ +#ifndef CONVERTER_MAC_H +#define CONVERTER_MAC_H + +#include + +int NormalizeUtf8ToNfcMac(const char *src, char *dst, size_t capacity); + +#endif // CONVERTER_MAC_H diff --git a/Source/UI/converter_mac.mm b/Source/UI/converter_mac.mm new file mode 100644 index 0000000..1940d35 --- /dev/null +++ b/Source/UI/converter_mac.mm @@ -0,0 +1,38 @@ +#import + +#include + +#include "converter_mac.h" + +int NormalizeUtf8ToNfcMac(const char *src, char *dst, size_t capacity) +{ + if (src == nullptr || dst == nullptr || capacity == 0) { + return -1; + } + + @autoreleasepool { + NSString *string = [NSString stringWithUTF8String:src]; + if (string == nil) { + dst[0] = '\0'; + return -1; + } + + NSString *normalized = + [string precomposedStringWithCanonicalMapping]; + const char *utf8 = [normalized UTF8String]; + if (utf8 == nullptr) { + dst[0] = '\0'; + return -1; + } + + const size_t length = std::strlen(utf8); + if (length >= capacity) { + dst[0] = '\0'; + return -1; + } + + std::memcpy(dst, utf8, length + 1); + } + + return 0; +} diff --git a/Tests/converter_mac_test.mm b/Tests/converter_mac_test.mm new file mode 100644 index 0000000..0efb83f --- /dev/null +++ b/Tests/converter_mac_test.mm @@ -0,0 +1,57 @@ +#import + +#include +#include + +#include "converter_mac.h" + +namespace { + +int failures = 0; + +void Check(bool condition, const char *message) +{ + if (!condition) { + std::cerr << "FAIL: " << message << '\n'; + failures++; + } +} + +} + +int main() +{ + @autoreleasepool { + char normalized[32]; + Check(NormalizeUtf8ToNfcMac("ka", normalized, + sizeof(normalized)) == 0, "normalize ascii succeeds"); + Check(std::strcmp(normalized, "ka") == 0, + "ascii remains unchanged"); + + const char *nfd = "\xe3\x81\x8b\xe3\x82\x99"; + const char *nfc = "\xe3\x81\x8c"; + Check(NormalizeUtf8ToNfcMac(nfd, normalized, + sizeof(normalized)) == 0, "normalize nfd succeeds"); + Check(std::strcmp(normalized, nfc) == 0, + "nfd kana becomes nfc kana"); + + char too_small[3] = {'x', 'y', 'z'}; + Check(NormalizeUtf8ToNfcMac(nfd, too_small, + sizeof(too_small)) == -1, "small buffer fails"); + Check(too_small[0] == '\0', + "small buffer is cleared after failure"); + + Check(NormalizeUtf8ToNfcMac(nullptr, normalized, + sizeof(normalized)) == -1, "null source fails"); + Check(NormalizeUtf8ToNfcMac("ka", nullptr, + sizeof(normalized)) == -1, "null destination fails"); + Check(NormalizeUtf8ToNfcMac("ka", normalized, 0) == -1, + "zero capacity fails"); + + const char invalid[] = {static_cast(0xff), '\0'}; + Check(NormalizeUtf8ToNfcMac(invalid, normalized, + sizeof(normalized)) == -1, "invalid utf-8 fails"); + } + + return failures == 0 ? 0 : 1; +}