From efdd7b86c6b0307a33318891693bbb8b8c9d0b45 Mon Sep 17 00:00:00 2001 From: Henri Kulotie Date: Mon, 29 Sep 2025 23:38:27 +0300 Subject: [PATCH 1/3] frontend,libobs,plugins: Ensure ctype functions use unsigned chars Cast ctype function char parameters to unsigned char to ensure they are in correct range (0 to 255 vs -128 to 127) when used with utf-8 encoding (or extended ascii). Fixes dstr astrcmp* functions when used with utf-8 (or extended ascii) characters, so now they are treated greater than the base ascii and thus sorted after them, not before. --- frontend/OBSApp_Themes.cpp | 6 +++--- libobs/util/cf-lexer.c | 2 +- libobs/util/dstr.c | 12 ++++++------ libobs/util/lexer.c | 20 ++++++++++++-------- plugins/text-freetype2/find-font.c | 4 ++-- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/frontend/OBSApp_Themes.cpp b/frontend/OBSApp_Themes.cpp index dc28bd89bf9ec9..fcd96f0478605c 100644 --- a/frontend/OBSApp_Themes.cpp +++ b/frontend/OBSApp_Themes.cpp @@ -339,7 +339,7 @@ static vector ParseThemeVariables(const char *themeData) /* Look for a suffix and mark variable as size if it exists */ while (ch < end) { - if (!isdigit(*ch) && !isspace(*ch) && *ch != '.') { + if (!isdigit((unsigned char)*ch) && !isspace((unsigned char)*ch) && *ch != '.') { var.suffix = QString::fromUtf8(ch, end - ch); var.type = OBSThemeVariable::Size; break; @@ -526,14 +526,14 @@ static OBSThemeVariable ParseMathVariable(const QHash const QByteArray utf8 = value.toUtf8(); const char *data = utf8.constData(); - if (isdigit(*data)) { + if (isdigit((unsigned char)*data)) { double f = os_strtod(data); var.type = OBSThemeVariable::Number; var.value = f; const char *dataEnd = data + utf8.size(); while (data < dataEnd) { - if (*data && !isdigit(*data) && *data != '.') { + if (*data && !isdigit((unsigned char)*data) && *data != '.') { var.suffix = QString::fromUtf8(data, dataEnd - data); var.type = OBSThemeVariable::Size; break; diff --git a/libobs/util/cf-lexer.c b/libobs/util/cf-lexer.c index 0212cda3978c11..afa75d2a493f36 100644 --- a/libobs/util/cf-lexer.c +++ b/libobs/util/cf-lexer.c @@ -71,7 +71,7 @@ static inline void cf_convert_from_escape_literal(char **p_dst, const char **p_s /* oct */ default: - if (isdigit(*src)) { + if (isdigit((unsigned char)*src)) { *(dst++) = (char)strtoul(src, NULL, 8); src += 3; } diff --git a/libobs/util/dstr.c b/libobs/util/dstr.c index 4dc415cb356841..c07b12dbe5b182 100644 --- a/libobs/util/dstr.c +++ b/libobs/util/dstr.c @@ -43,8 +43,8 @@ int astrcmpi(const char *str1, const char *str2) str2 = astrblank; do { - char ch1 = (char)toupper(*str1); - char ch2 = (char)toupper(*str2); + unsigned char ch1 = (unsigned char)toupper((unsigned char)*str1); + unsigned char ch2 = (unsigned char)toupper((unsigned char)*str2); if (ch1 < ch2) return -1; @@ -85,8 +85,8 @@ int astrcmp_n(const char *str1, const char *str2, size_t n) str2 = astrblank; do { - char ch1 = *str1; - char ch2 = *str2; + unsigned char ch1 = (unsigned char)*str1; + unsigned char ch2 = (unsigned char)*str2; if (ch1 < ch2) return -1; @@ -129,8 +129,8 @@ int astrcmpi_n(const char *str1, const char *str2, size_t n) str2 = astrblank; do { - char ch1 = (char)toupper(*str1); - char ch2 = (char)toupper(*str2); + unsigned char ch1 = (unsigned char)toupper((unsigned char)*str1); + unsigned char ch2 = (unsigned char)toupper((unsigned char)*str2); if (ch1 < ch2) return -1; diff --git a/libobs/util/lexer.c b/libobs/util/lexer.c index a69634c154fd0a..cada3ba758959d 100644 --- a/libobs/util/lexer.c +++ b/libobs/util/lexer.c @@ -29,7 +29,8 @@ int strref_cmp(const struct strref *str1, const char *str2) str2 = astrblank; do { - char ch1, ch2; + unsigned char ch1; + unsigned char ch2; ch1 = (i < str1->len) ? str1->array[i] : 0; ch2 = *str2; @@ -53,10 +54,11 @@ int strref_cmpi(const struct strref *str1, const char *str2) str2 = astrblank; do { - char ch1, ch2; + unsigned char ch1; + unsigned char ch2; - ch1 = (i < str1->len) ? (char)toupper(str1->array[i]) : 0; - ch2 = (char)toupper(*str2); + ch1 = (i < str1->len) ? (unsigned char)toupper((unsigned char)str1->array[i]) : 0; + ch2 = (unsigned char)toupper((unsigned char)*str2); if (ch1 < ch2) return -1; @@ -77,7 +79,8 @@ int strref_cmp_strref(const struct strref *str1, const struct strref *str2) return -1; do { - char ch1, ch2; + unsigned char ch1; + unsigned char ch2; ch1 = (i < str1->len) ? str1->array[i] : 0; ch2 = (i < str2->len) ? str2->array[i] : 0; @@ -103,10 +106,11 @@ int strref_cmpi_strref(const struct strref *str1, const struct strref *str2) return -1; do { - char ch1, ch2; + unsigned char ch1; + unsigned char ch2; - ch1 = (i < str1->len) ? (char)toupper(str1->array[i]) : 0; - ch2 = (i < str2->len) ? (char)toupper(str2->array[i]) : 0; + ch1 = (i < str1->len) ? (unsigned char)toupper((unsigned char)str1->array[i]) : 0; + ch2 = (i < str2->len) ? (unsigned char)toupper((unsigned char)str2->array[i]) : 0; if (ch1 < ch2) return -1; diff --git a/plugins/text-freetype2/find-font.c b/plugins/text-freetype2/find-font.c index 79312528ad1d9a..217e2f0165abd8 100644 --- a/plugins/text-freetype2/find-font.c +++ b/plugins/text-freetype2/find-font.c @@ -333,8 +333,8 @@ static inline size_t get_rating(struct font_path_info *info, struct dstr *cmp) size_t num = 0; do { - char ch1 = (char)toupper(*src); - char ch2 = (char)toupper(*dst); + unsigned char ch1 = (unsigned char)toupper((unsigned char)*src); + unsigned char ch2 = (unsigned char)toupper((unsigned char)*dst); if (ch1 != ch2) break; From fad011f976eec44f34cc70c2a8deeff41264ff98 Mon Sep 17 00:00:00 2001 From: Henri Kulotie Date: Mon, 29 Sep 2025 23:39:04 +0300 Subject: [PATCH 2/3] logging: Use ISO 8601 time format for logging Switch locale-aware timestamping for logging / crash handling to %H:%M:%S Update frontend/OBSApp.cpp Co-authored-by: Patrick Heyer Update libobs/obs-win-crash-handler.c Co-authored-by: Patrick Heyer --- frontend/OBSApp.cpp | 2 +- libobs/obs-win-crash-handler.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/OBSApp.cpp b/frontend/OBSApp.cpp index 602769d29ee5ac..93b4ea84b23e78 100644 --- a/frontend/OBSApp.cpp +++ b/frontend/OBSApp.cpp @@ -285,7 +285,7 @@ string CurrentDateTimeString() struct tm tstruct; char buf[80]; tstruct = *localtime(&now); - strftime(buf, sizeof(buf), "%Y-%m-%d, %X", &tstruct); + strftime(buf, sizeof(buf), "%Y-%m-%d, %H:%M:%S", &tstruct); return buf; } diff --git a/libobs/obs-win-crash-handler.c b/libobs/obs-win-crash-handler.c index 664a9c6eb12305..befc3843a8f391 100644 --- a/libobs/obs-win-crash-handler.c +++ b/libobs/obs-win-crash-handler.c @@ -245,7 +245,7 @@ static inline void write_header(struct exception_handler_data *data) time_t now = time(0); struct tm ts; ts = *localtime(&now); - strftime(date_time, sizeof(date_time), "%Y-%m-%d, %X", &ts); + strftime(date_time, sizeof(date_time), "%Y-%m-%d, %H:%M:%S", &ts); const char *obs_bitness; if (sizeof(void *) == 8) From 017c69a1448b8df2ca61347cc18a00028135b52f Mon Sep 17 00:00:00 2001 From: Henri Kulotie Date: Sun, 8 Feb 2026 19:22:31 +0200 Subject: [PATCH 3/3] frontend: Add utf-8 active code page manifest Declaring Utf-8 as active code page in manifest makes Win32 API use utf-8 instead of ANSI codepages when using the "A" versions of functions. Manifest declaration also encodes command line arguments as utf8. This allows for example --profile to load profiles with special characters. --- frontend/cmake/windows/obs.manifest | 5 +++++ libobs/obs.h | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/frontend/cmake/windows/obs.manifest b/frontend/cmake/windows/obs.manifest index c5d4297d945860..4e309f7b7778ea 100644 --- a/frontend/cmake/windows/obs.manifest +++ b/frontend/cmake/windows/obs.manifest @@ -11,6 +11,11 @@ + + + UTF-8 + + diff --git a/libobs/obs.h b/libobs/obs.h index 57844b05694cec..d979e203729794 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -382,8 +382,11 @@ EXPORT const char *obs_get_version_string(void); * and safely copies argv/argc from main(). Subsequent calls do nothing. * * @param argc The count of command line arguments, from main() - * @param argv An array of command line arguments, copied from main() and ends + * @param argv An array of command line arguments in UTF-8, copied from main() and ends * with NULL. + * + * On Windows this requires that active code page is UTF-8. + * For example as a declaration in obs.manifest. */ EXPORT void obs_set_cmdline_args(int argc, const char *const *argv);