summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Lutz <michi@icosahedron.de>2018-04-29 00:34:01 +0200
committerMichael Lutz <michi@icosahedron.de>2018-06-06 21:37:09 +0200
commitf4394debdc092487d603f95716a026a5c8834f8c (patch)
treedb4865150cfd88422f279d6fa5213ab11e27beab
parent2b662b448cd020886c00ff7ec800d7bd7cb008fa (diff)
downloadopenttd-f4394debdc092487d603f95716a026a5c8834f8c.tar.xz
Add: [Win32] Native natural sort implementation.
-rw-r--r--src/os/windows/win32.cpp65
-rw-r--r--src/os/windows/win32.h3
-rw-r--r--src/string.cpp11
-rw-r--r--src/strings.cpp5
4 files changed, 83 insertions, 1 deletions
diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp
index 8a90db4f7..2d853037f 100644
--- a/src/os/windows/win32.cpp
+++ b/src/os/windows/win32.cpp
@@ -28,6 +28,7 @@
#include "../../crashlog.h"
#include <errno.h>
#include <sys/stat.h>
+#include "../../language.h"
/* Due to TCHAR, strncat and strncpy have to remain (for a while). */
#include "../../safeguards.h"
@@ -739,6 +740,70 @@ uint GetCPUCoreCount()
return info.dwNumberOfProcessors;
}
+
+static WCHAR _cur_iso_locale[16] = L"";
+
+void Win32SetCurrentLocaleName(const char *iso_code)
+{
+ /* Convert the iso code into the format that windows expects. */
+ char iso[16];
+ if (strcmp(iso_code, "zh_TW") == 0) {
+ strecpy(iso, "zh-Hant", lastof(iso));
+ } else if (strcmp(iso_code, "zh_CN") == 0) {
+ strecpy(iso, "zh-Hans", lastof(iso));
+ } else {
+ /* Windows expects a '-' between language and country code, but we use a '_'. */
+ strecpy(iso, iso_code, lastof(iso));
+ for (char *c = iso; *c != '\0'; c++) {
+ if (*c == '_') *c = '-';
+ }
+ }
+
+ MultiByteToWideChar(CP_UTF8, 0, iso, -1, _cur_iso_locale, lengthof(_cur_iso_locale));
+}
+
+int OTTDStringCompare(const char *s1, const char *s2)
+{
+ typedef int (WINAPI *PFNCOMPARESTRINGEX)(LPCWSTR, DWORD, LPCWCH, int, LPCWCH, int, LPVOID, LPVOID, LPARAM);
+ static PFNCOMPARESTRINGEX _CompareStringEx = NULL;
+ static bool first_time = true;
+
+#ifndef SORT_DIGITSASNUMBERS
+# define SORT_DIGITSASNUMBERS 0x00000008 // use digits as numbers sort method
+#endif
+#ifndef LINGUISTIC_IGNORECASE
+# define LINGUISTIC_IGNORECASE 0x00000010 // linguistically appropriate 'ignore case'
+#endif
+
+ if (first_time) {
+ _CompareStringEx = (PFNCOMPARESTRINGEX)GetProcAddress(GetModuleHandle(_T("Kernel32")), "CompareStringEx");
+ first_time = false;
+ }
+
+ if (_CompareStringEx != NULL) {
+ /* CompareStringEx takes UTF-16 strings, even in ANSI-builds. */
+ int len_s1 = MultiByteToWideChar(CP_UTF8, 0, s1, -1, NULL, 0);
+ int len_s2 = MultiByteToWideChar(CP_UTF8, 0, s2, -1, NULL, 0);
+
+ if (len_s1 != 0 && len_s2 != 0) {
+ LPWSTR str_s1 = AllocaM(WCHAR, len_s1);
+ LPWSTR str_s2 = AllocaM(WCHAR, len_s2);
+
+ MultiByteToWideChar(CP_UTF8, 0, s1, -1, str_s1, len_s1);
+ MultiByteToWideChar(CP_UTF8, 0, s2, -1, str_s2, len_s2);
+
+ int result = _CompareStringEx(_cur_iso_locale, LINGUISTIC_IGNORECASE | SORT_DIGITSASNUMBERS, str_s1, -1, str_s2, -1, NULL, NULL, 0);
+ if (result != 0) return result;
+ }
+ }
+
+ TCHAR s1_buf[512], s2_buf[512];
+ convert_to_fs(s1, s1_buf, lengthof(s1_buf));
+ convert_to_fs(s2, s2_buf, lengthof(s2_buf));
+
+ return CompareString(MAKELCID(_current_language->winlangid, SORT_DEFAULT), NORM_IGNORECASE, s1_buf, -1, s2_buf, -1);
+}
+
#ifdef _MSC_VER
/* Code from MSDN: https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
const DWORD MS_VC_EXCEPTION = 0x406D1388;
diff --git a/src/os/windows/win32.h b/src/os/windows/win32.h
index 16632f6e9..4f813c4a6 100644
--- a/src/os/windows/win32.h
+++ b/src/os/windows/win32.h
@@ -45,4 +45,7 @@ void SetWin32ThreadName(DWORD dwThreadID, const char* threadName);
static inline void SetWin32ThreadName(DWORD dwThreadID, const char* threadName) {}
#endif
+void Win32SetCurrentLocaleName(const char *iso_code);
+int OTTDStringCompare(const char *s1, const char *s2);
+
#endif /* WIN32_H */
diff --git a/src/string.cpp b/src/string.cpp
index 6306e6f75..170334792 100644
--- a/src/string.cpp
+++ b/src/string.cpp
@@ -25,6 +25,10 @@
#include <errno.h> // required by vsnprintf implementation for MSVC
#endif
+#ifdef WIN32
+#include "os/windows/win32.h"
+#endif
+
#ifdef WITH_ICU_SORT
/* Required by strnatcmp. */
#include <unicode/ustring.h>
@@ -572,15 +576,20 @@ int strnatcmp(const char *s1, const char *s2, bool ignore_garbage_at_front)
s1 = SkipGarbage(s1);
s2 = SkipGarbage(s2);
}
+
#ifdef WITH_ICU_SORT
if (_current_collator != NULL) {
UErrorCode status = U_ZERO_ERROR;
int result = _current_collator->compareUTF8(s1, s2, status);
if (U_SUCCESS(status)) return result;
}
-
#endif /* WITH_ICU_SORT */
+#if defined(WIN32) && !defined(STRGEN) && !defined(SETTINGSGEN)
+ int res = OTTDStringCompare(s1, s2);
+ if (res != 0) return res - 2; // Convert to normal C return values.
+#endif
+
/* Do a normal comparison if ICU is missing or if we cannot create a collator. */
return strcasecmp(s1, s2);
}
diff --git a/src/strings.cpp b/src/strings.cpp
index fd45e6a0b..4ababcc5a 100644
--- a/src/strings.cpp
+++ b/src/strings.cpp
@@ -1788,6 +1788,11 @@ bool ReadLanguagePack(const LanguageMetadata *lang)
strecpy(_config_language_file, c_file, lastof(_config_language_file));
SetCurrentGrfLangID(_current_language->newgrflangid);
+#ifdef WIN32
+ extern void Win32SetCurrentLocaleName(const char *iso_code);
+ Win32SetCurrentLocaleName(_current_language->isocode);
+#endif
+
#ifdef WITH_ICU_SORT
/* Delete previous collator. */
if (_current_collator != NULL) {