From 69b8b52b0f14c1edf1cab3f3b758222c4f23b2e3 Mon Sep 17 00:00:00 2001 From: Henri Kulotie Date: Mon, 17 Aug 2026 20:17:43 +0300 Subject: [PATCH 1/5] libobs: Fix BPtr re-assignment leak --- libobs/util/util.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libobs/util/util.hpp b/libobs/util/util.hpp index f19b34026ebdf7..94e6c06a8e97e6 100644 --- a/libobs/util/util.hpp +++ b/libobs/util/util.hpp @@ -37,7 +37,7 @@ template class BPtr { public: inline BPtr(T *p = nullptr) : ptr(p) {} - inline BPtr(BPtr &&other) { *this = std::move(other); } + inline BPtr(BPtr &&other) : ptr(nullptr) { *this = std::move(other); } inline ~BPtr() { bfree(ptr); } inline T *operator=(T *p) @@ -49,6 +49,7 @@ template class BPtr { inline BPtr &operator=(BPtr &&other) { + bfree(ptr); ptr = other.ptr; other.ptr = nullptr; return *this; From 227d73d28e4d0662fa1b840350c2f6770e7c2da0 Mon Sep 17 00:00:00 2001 From: Henri Kulotie Date: Mon, 17 Aug 2026 20:23:22 +0300 Subject: [PATCH 2/5] libobs: Match config path ptr formatting Match GetAppConfigPathPtr and os_get_path_ptr_internal file path formatting with GetAppConfigPath and os_get_path_internal. Empty/null strings return base path. Remove arbitrary path length limit with dynamic allocation. --- frontend/OBSApp.cpp | 14 +++++++++++--- libobs/util/platform-nix.c | 5 +++-- libobs/util/platform-windows.c | 2 ++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/frontend/OBSApp.cpp b/frontend/OBSApp.cpp index 0d6d0e28e35fbd..6295afcbfb966d 100644 --- a/frontend/OBSApp.cpp +++ b/frontend/OBSApp.cpp @@ -1743,10 +1743,18 @@ char *GetAppConfigPathPtr(const char *name) { #if ALLOW_PORTABLE_MODE if (portable_mode) { - char path[512]; + if (!name || !*name) { + return bstrdup(CONFIG_PATH); + } + + int len = snprintf(NULL, 0, CONFIG_PATH "/%s", name); + if (len <= 0) { + return NULL; + } + char *path = (char *)bmalloc((size_t)len + 1); - if (snprintf(path, sizeof(path), CONFIG_PATH "/%s", name) > 0) { - return bstrdup(path); + if (snprintf(path, len + 1, CONFIG_PATH "/%s", name) > 0) { + return path; } else { return NULL; } diff --git a/libobs/util/platform-nix.c b/libobs/util/platform-nix.c index 5648ca61b03a2b..9b39e8890479a4 100644 --- a/libobs/util/platform-nix.c +++ b/libobs/util/platform-nix.c @@ -263,10 +263,11 @@ char *os_get_config_path_ptr(const char *name) bcrash("Could not get $HOME\n"); dstr_init_copy(&path, home_ptr); - dstr_cat(&path, "/.config/"); - dstr_cat(&path, name); + dstr_cat(&path, "/.config"); } else { dstr_init_copy(&path, xdg_ptr); + } + if (name && *name) { dstr_cat(&path, "/"); dstr_cat(&path, name); } diff --git a/libobs/util/platform-windows.c b/libobs/util/platform-windows.c index df4efb7848dd27..5af6e81632030a 100644 --- a/libobs/util/platform-windows.c +++ b/libobs/util/platform-windows.c @@ -427,8 +427,10 @@ static char *os_get_path_ptr_internal(const char *name, int folder) os_wcs_to_utf8_ptr(path_utf16, 0, &ptr); dstr_init_move_array(&path, ptr); + if (name && *name) { dstr_cat(&path, "\\"); dstr_cat(&path, name); + } return path.array; } From 689f4b6aa537cb364d372b0caaa55d478691fe5c Mon Sep 17 00:00:00 2001 From: Henri Kulotie Date: Mon, 17 Aug 2026 20:51:06 +0300 Subject: [PATCH 3/5] libobs: Ensure buffer size for utf-16 converted filepaths Use dynamically allocated paths instead of arbitrary static allocations, that may not be fit long paths on all OSs. Filepaths from Windows W apis get stored internally in UTF-8, which may increase the bytes needed. Changed some A api calls to W variants to support longer paths, and to get them in supported encoding isntead of ansi code page. Fix overflow in os_stat() when used with long filename. Fix off-by-one length check in nvvfx-load sdk path. --- frontend/OBSApp.cpp | 101 ++++++++------------- frontend/OBSApp_Themes.cpp | 12 +-- frontend/dialogs/OBSLogViewer.cpp | 12 +-- frontend/importers/classic.cpp | 7 +- frontend/importers/sl.cpp | 8 +- frontend/importers/studio.cpp | 16 ++-- frontend/importers/xsplit.cpp | 8 +- frontend/utility/platform-windows.cpp | 21 ++--- frontend/widgets/OBSBasic.cpp | 12 +-- frontend/widgets/OBSBasic_MainControls.cpp | 10 +- libobs/util/platform-nix.c | 15 ++- libobs/util/platform-windows.c | 35 ++++--- libobs/util/platform.c | 43 ++++----- plugins/nv-filters/nvafx-load.h | 31 ++++--- plugins/nv-filters/nvidia-audiofx-filter.c | 11 +-- plugins/nv-filters/nvidia-videofx-filter.c | 17 +++- plugins/nv-filters/nvvfx-load.h | 39 ++++---- plugins/text-freetype2/find-font-windows.c | 71 ++++++++------- 18 files changed, 231 insertions(+), 238 deletions(-) diff --git a/frontend/OBSApp.cpp b/frontend/OBSApp.cpp index 6295afcbfb966d..1f152f0fc608ee 100644 --- a/frontend/OBSApp.cpp +++ b/frontend/OBSApp.cpp @@ -38,6 +38,7 @@ #include #include +#include #if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER) #include #endif @@ -331,18 +332,16 @@ bool OBSApp::InitGlobalConfigDefaults() bool OBSApp::InitGlobalLocationDefaults() { - char path[512]; - - int len = GetAppConfigPath(path, sizeof(path), nullptr); - if (len <= 0) { + BPtr path = GetAppConfigPathPtr(nullptr); + if (!path || !*path.Get()) { OBSErrorBox(NULL, "Unable to get global configuration path."); return false; } - config_set_default_string(appConfig, "Locations", "Configuration", path); - config_set_default_string(appConfig, "Locations", "SceneCollections", path); - config_set_default_string(appConfig, "Locations", "Profiles", path); - config_set_default_string(appConfig, "Locations", "PluginManagerSettings", path); + config_set_default_string(appConfig, "Locations", "Configuration", path.Get()); + config_set_default_string(appConfig, "Locations", "SceneCollections", path.Get()); + config_set_default_string(appConfig, "Locations", "Profiles", path.Get()); + config_set_default_string(appConfig, "Locations", "PluginManagerSettings", path.Get()); return true; } @@ -407,49 +406,37 @@ static bool do_mkdir(const char *path) static bool MakeUserDirs() { - char path[512]; + BPtr path; - if (GetAppConfigPath(path, sizeof(path), "obs-studio/basic") <= 0) { - return false; - } - if (!do_mkdir(path)) { + path = GetAppConfigPathPtr("obs-studio/basic"); + if (!path || !*path.Get() || !do_mkdir(path)) { return false; } - if (GetAppConfigPath(path, sizeof(path), "obs-studio/logs") <= 0) { - return false; - } - if (!do_mkdir(path)) { + path = GetAppConfigPathPtr("obs-studio/logs"); + if (!path || !*path.Get() || !do_mkdir(path)) { return false; } - if (GetAppConfigPath(path, sizeof(path), "obs-studio/profiler_data") <= 0) { - return false; - } - if (!do_mkdir(path)) { + path = GetAppConfigPathPtr("obs-studio/profiler_data"); + if (!path || !*path.Get() || !do_mkdir(path)) { return false; } #ifdef _WIN32 - if (GetAppConfigPath(path, sizeof(path), "obs-studio/crashes") <= 0) { - return false; - } - if (!do_mkdir(path)) { + path = GetAppConfigPathPtr("obs-studio/crashes"); + if (!path || !*path.Get() || !do_mkdir(path)) { return false; } #endif - if (GetAppConfigPath(path, sizeof(path), "obs-studio/updates") <= 0) { - return false; - } - if (!do_mkdir(path)) { + path = GetAppConfigPathPtr("obs-studio/updates"); + if (!path || !*path.Get() || !do_mkdir(path)) { return false; } - if (GetAppConfigPath(path, sizeof(path), "obs-studio/plugin_config") <= 0) { - return false; - } - if (!do_mkdir(path)) { + path = GetAppConfigPathPtr("obs-studio/plugin_config"); + if (!path || !*path.Get() || !do_mkdir(path)) { return false; } @@ -537,10 +524,8 @@ bool OBSApp::UpdatePre22MultiviewLayout(const char *layout) bool OBSApp::InitGlobalConfig() { - char path[512]; - - int len = GetAppConfigPath(path, sizeof(path), "obs-studio/global.ini"); - if (len <= 0) { + BPtr path = GetAppConfigPathPtr("obs-studio/global.ini"); + if (!path || !*path.Get()) { return false; } @@ -676,23 +661,21 @@ static constexpr string_view OBSUserIniPath = "/obs-studio/user.ini"; bool OBSApp::MigrateGlobalSettings() { - char path[512]; - - int len = GetAppConfigPath(path, sizeof(path), nullptr); - if (len <= 0) { + BPtr path = GetAppConfigPathPtr(nullptr); + if (!path || !*path.Get()) { OBSErrorBox(nullptr, "Unable to get global configuration path."); return false; } std::string legacyConfigFileString; - legacyConfigFileString.reserve(strlen(path) + OBSGlobalIniPath.size()); - legacyConfigFileString.append(path).append(OBSGlobalIniPath); + legacyConfigFileString.reserve(strlen(path.Get()) + OBSGlobalIniPath.size()); + legacyConfigFileString.append(path.Get()).append(OBSGlobalIniPath); const std::filesystem::path legacyGlobalConfigFile = std::filesystem::u8path(legacyConfigFileString); std::string configFileString; - configFileString.reserve(strlen(path) + OBSUserIniPath.size()); - configFileString.append(path).append(OBSUserIniPath); + configFileString.reserve(strlen(path.Get()) + OBSUserIniPath.size()); + configFileString.append(path.Get()).append(OBSUserIniPath); const std::filesystem::path userConfigFile = std::filesystem::u8path(configFileString); @@ -970,13 +953,13 @@ OBSApp::~OBSApp() static void move_basic_to_profiles(void) { - char path[512]; + BPtr path = GetAppConfigPathPtr("obs-studio/basic"); - if (GetAppConfigPath(path, 512, "obs-studio/basic") <= 0) { + if (!path || !*path.Get()) { return; } - const std::filesystem::path basicPath = std::filesystem::u8path(path); + const std::filesystem::path basicPath = std::filesystem::u8path(path.Get()); if (!std::filesystem::exists(basicPath)) { return; @@ -1034,13 +1017,13 @@ static void move_basic_to_profiles(void) static void move_basic_to_scene_collections(void) { - char path[512]; + BPtr path = GetAppConfigPathPtr("obs-studio/basic"); - if (GetAppConfigPath(path, 512, "obs-studio/basic") <= 0) { + if (!path || !*path.Get()) { return; } - const std::filesystem::path basicPath = std::filesystem::u8path(path); + const std::filesystem::path basicPath = std::filesystem::u8path(path.Get()); if (!std::filesystem::exists(basicPath)) { return; @@ -1169,9 +1152,9 @@ const char *OBSApp::GetRenderModule() const static bool StartupOBS(const char *locale, profiler_name_store_t *store) { - char path[512]; + BPtr path = GetAppConfigPathPtr("obs-studio/plugin_config"); - if (GetAppConfigPath(path, sizeof(path), "obs-studio/plugin_config") <= 0) { + if (!path || !*path.Get()) { return false; } @@ -1515,16 +1498,10 @@ bool OBSApp::notify(QObject *receiver, QEvent *e) string GenerateTimeDateFilename(const char *extension, bool noSpace) { - time_t now = time(0); - char file[256] = {}; - struct tm *cur_time; - - cur_time = localtime(&now); - snprintf(file, sizeof(file), "%d-%02d-%02d%c%02d-%02d-%02d.%s", cur_time->tm_year + 1900, cur_time->tm_mon + 1, - cur_time->tm_mday, noSpace ? '_' : ' ', cur_time->tm_hour, cur_time->tm_min, cur_time->tm_sec, - extension); + QString format = noSpace ? "yyyy-MM-dd_hh-mm-ss" : "yyyy-MM-dd hh-mm-ss"; + QString filename = QDateTime::currentDateTime().toString(format) + "." + extension; - return string(file); + return filename.toStdString(); } string GenerateSpecifiedFilename(const char *extension, bool noSpace, const char *format) diff --git a/frontend/OBSApp_Themes.cpp b/frontend/OBSApp_Themes.cpp index 653c529a32ad91..e24b078b467fef 100644 --- a/frontend/OBSApp_Themes.cpp +++ b/frontend/OBSApp_Themes.cpp @@ -1000,9 +1000,9 @@ bool OBSApp::SetTheme(const QString &name) filename += ".out"; filesystem::path debugOut; - char configPath[512]; - if (GetAppConfigPath(configPath, sizeof(configPath), filename.c_str())) { - debugOut = absolute(filesystem::u8path(configPath)); + BPtr configPath = GetAppConfigPathPtr(filename.c_str()); + if (configPath && *configPath.Get()) { + debugOut = absolute(filesystem::u8path(configPath.Get())); filesystem::create_directories(debugOut.parent_path()); } @@ -1063,9 +1063,9 @@ bool OBSApp::InitTheme() QDir::addSearchPath("theme", absolute(installSearchDir)); } - char userDir[512]; - if (GetAppConfigPath(userDir, sizeof(userDir), "obs-studio/themes")) { - auto configSearchDir = filesystem::u8path(userDir); + BPtr userDir = GetAppConfigPathPtr("obs-studio/themes"); + if (userDir && *userDir.Get()) { + auto configSearchDir = filesystem::u8path(userDir.Get()); QDir::addSearchPath("theme", absolute(configSearchDir)); } diff --git a/frontend/dialogs/OBSLogViewer.cpp b/frontend/dialogs/OBSLogViewer.cpp index 74b3e583b98fe4..1542b3a7ed8bfc 100644 --- a/frontend/dialogs/OBSLogViewer.cpp +++ b/frontend/dialogs/OBSLogViewer.cpp @@ -45,11 +45,11 @@ void OBSLogViewer::on_showStartup_clicked(bool checked) void OBSLogViewer::InitLog() { - char logDir[512]; + BPtr logDir = GetAppConfigPathPtr("obs-studio/logs"); std::string path; - if (GetAppConfigPath(logDir, sizeof(logDir), "obs-studio/logs")) { - path += logDir; + if (logDir.Get()) { + path += logDir.Get(); path += "/"; path += App()->GetCurrentLog(); } @@ -114,14 +114,14 @@ void OBSLogViewer::AddLine(int type, const QString &str) void OBSLogViewer::on_openButton_clicked() { - char logDir[512]; - if (GetAppConfigPath(logDir, sizeof(logDir), "obs-studio/logs") <= 0) { + BPtr logDir = GetAppConfigPathPtr("obs-studio/logs"); + if (!logDir || !*logDir.Get()) { return; } const char *log = App()->GetCurrentLog(); - std::string path = logDir; + std::string path = logDir.Get(); path += "/"; path += log; diff --git a/frontend/importers/classic.cpp b/frontend/importers/classic.cpp index 5125e5d3b914ee..01fa7d24566ca3 100644 --- a/frontend/importers/classic.cpp +++ b/frontend/importers/classic.cpp @@ -548,9 +548,8 @@ OBSImporterFiles ClassicImporter::FindFiles() OBSImporterFiles res; #ifdef _WIN32 - char dst[512]; - int found = os_get_config_path(dst, 512, "OBS\\sceneCollection\\"); - if (found == -1) { + BPtr dst = os_get_config_path_ptr("OBS\\sceneCollection\\"); + if (!dst) { return res; } @@ -564,7 +563,7 @@ OBSImporterFiles ClassicImporter::FindFiles() string name = ent->d_name; size_t pos = name.find(".xconfig"); if (pos != -1 && pos == name.length() - 8) { - string path = dst + name; + string path = dst.Get() + name; res.push_back(path); } } diff --git a/frontend/importers/sl.cpp b/frontend/importers/sl.cpp index 7f1b2554b9b085..f8dece8d77f468 100644 --- a/frontend/importers/sl.cpp +++ b/frontend/importers/sl.cpp @@ -495,11 +495,9 @@ OBSImporterFiles SLImporter::FindFiles() { OBSImporterFiles res; #if defined(_WIN32) || defined(__APPLE__) - char dst[512]; + BPtr dst = os_get_config_path_ptr("slobs-client/SceneCollections/"); - int found = os_get_config_path(dst, 512, "slobs-client/SceneCollections/"); - - if (found == -1) { + if (!dst) { return res; } @@ -515,7 +513,7 @@ OBSImporterFiles SLImporter::FindFiles() size_t pos = name.find_last_of(".json"); size_t end_pos = name.size() - 1; if (pos != string::npos && pos == end_pos) { - string str = dst + name; + string str = dst.Get() + name; res.push_back(str); } } diff --git a/frontend/importers/studio.cpp b/frontend/importers/studio.cpp index 45c8fc102a384d..5ad1b47582c3e7 100644 --- a/frontend/importers/studio.cpp +++ b/frontend/importers/studio.cpp @@ -143,27 +143,23 @@ void TranslateOSStudio(Json &res) static string CheckPath(const string &path, const string &rootDir) { - char root[512]; - *root = 0; - size_t rootLen = os_get_abs_path(rootDir.c_str(), root, sizeof(root)); + BPtr root = os_get_abs_path_ptr(rootDir.c_str()); - char absPath[512]; - *absPath = 0; - size_t len = os_get_abs_path((rootDir + path).c_str(), absPath, sizeof(absPath)); + BPtr absPath = os_get_abs_path_ptr((rootDir + path).c_str()); - if (len == 0) { + if (!absPath || !*absPath.Get()) { return path; } - if (strstr(absPath, root) != absPath) { + if (strstr(absPath.Get(), root) != absPath.Get()) { return path; } - if (*(absPath + rootLen) != QDir::separator().toLatin1()) { + if (*(absPath.Get() + strlen(root)) != QDir::separator().toLatin1()) { return path; } - return absPath; + return string(absPath.Get()); } void TranslatePaths(Json &res, const string &rootDir) diff --git a/frontend/importers/xsplit.cpp b/frontend/importers/xsplit.cpp index f5bab6b04c8007..f88ad70b16abfc 100644 --- a/frontend/importers/xsplit.cpp +++ b/frontend/importers/xsplit.cpp @@ -503,10 +503,8 @@ OBSImporterFiles XSplitImporter::FindFiles() { OBSImporterFiles res; #ifdef _WIN32 - char dst[512]; - int found = os_get_program_data_path(dst, 512, "SplitMediaLabs\\XSplit\\Presentation2.0\\"); - - if (found == -1) { + BPtr dst = os_get_program_data_path_ptr("SplitMediaLabs\\XSplit\\Presentation2.0\\"); + if (!dst) { return res; } @@ -521,7 +519,7 @@ OBSImporterFiles XSplitImporter::FindFiles() } if (name == "Placements.bpres") { - string str = dst + name; + string str = dst.Get() + name; res.push_back(str); break; diff --git a/frontend/utility/platform-windows.cpp b/frontend/utility/platform-windows.cpp index 1cb1ef09db8350..2d2343d99ea6bf 100644 --- a/frontend/utility/platform-windows.cpp +++ b/frontend/utility/platform-windows.cpp @@ -60,11 +60,11 @@ bool GetDataFilePath(const char *data, string &output) string GetDefaultVideoSavePath() { wchar_t path_utf16[MAX_PATH]; - char path_utf8[MAX_PATH] = {}; + BPtr path_utf8; SHGetFolderPathW(NULL, CSIDL_MYVIDEO, NULL, SHGFP_TYPE_CURRENT, path_utf16); - os_wcs_to_utf8(path_utf16, wcslen(path_utf16), path_utf8, MAX_PATH); + os_wcs_to_utf8_ptr(path_utf16, wcslen(path_utf16), &path_utf8); return string(path_utf8); } @@ -91,10 +91,10 @@ static vector GetUserPreferredLocales() break; } - char conv[MAX_PATH] = {}; - os_wcs_to_utf8(&*start, separator - start, conv, MAX_PATH); + BPtr conv; + os_wcs_to_utf8_ptr(&*start, separator - start, &conv); - result.emplace_back(conv); + result.emplace_back(string(conv.Get())); start = separator + 1; } @@ -307,14 +307,11 @@ RunOnceMutex CheckIfAlreadyRunning(bool &already_running) if (!portable_mode) { name = "OBSStudioCore"; } else { - char path[500]; - char absPath[512]; - *path = 0; - *absPath = 0; - GetAppConfigPath(path, sizeof(path), ""); - os_get_abs_path(path, absPath, sizeof(absPath)); + BPtr path = GetAppConfigPathPtr(""); + BPtr absPath = os_get_abs_path_ptr(path.Get()); + name = "OBSStudioPortable"; - name += absPath; + name += absPath.Get(); } BPtr wname; diff --git a/frontend/widgets/OBSBasic.cpp b/frontend/widgets/OBSBasic.cpp index b6f5445305f301..862a7b3d6d2b7b 100644 --- a/frontend/widgets/OBSBasic.cpp +++ b/frontend/widgets/OBSBasic.cpp @@ -144,20 +144,20 @@ static void AddExtraModulePaths() return; } - char base_module_dir[512]; + BPtr base_module_dir; #if defined(_WIN32) - int ret = GetProgramDataPath(base_module_dir, sizeof(base_module_dir), "obs-studio/plugins/%module%"); + base_module_dir = GetProgramDataPathPtr("obs-studio/plugins/%module%"); #elif defined(__APPLE__) - int ret = GetAppConfigPath(base_module_dir, sizeof(base_module_dir), "obs-studio/plugins/%module%.plugin"); + base_module_dir = GetAppConfigPathPtr("obs-studio/plugins/%module%.plugin"); #else - int ret = GetAppConfigPath(base_module_dir, sizeof(base_module_dir), "obs-studio/plugins/%module%"); + base_module_dir = GetAppConfigPathPtr("obs-studio/plugins/%module%"); #endif - if (ret <= 0) { + if (!base_module_dir || !*base_module_dir.Get()) { return; } - string path = base_module_dir; + string path(base_module_dir.Get()); #if defined(__APPLE__) /* User Application Support Search Path */ obs_add_module_path((path + "/Contents/MacOS").c_str(), (path + "/Contents/Resources").c_str()); diff --git a/frontend/widgets/OBSBasic_MainControls.cpp b/frontend/widgets/OBSBasic_MainControls.cpp index 40b3aef444b418..3c62bc6ad5e2cf 100644 --- a/frontend/widgets/OBSBasic_MainControls.cpp +++ b/frontend/widgets/OBSBasic_MainControls.cpp @@ -260,12 +260,12 @@ void OBSBasic::on_actionAdvAudioProperties_triggered() static BPtr ReadLogFile(const char *subdir, const char *log) { - char logDir[512]; - if (GetAppConfigPath(logDir, sizeof(logDir), subdir) <= 0) { + BPtr logDir = GetAppConfigPathPtr(subdir); + if (!logDir || !*logDir.Get()) { return nullptr; } - string path = logDir; + string path(logDir.Get()); path += "/"; path += log; @@ -312,8 +312,8 @@ void OBSBasic::UploadLog(const char *subdir, const char *file, const LogUploadTy void OBSBasic::on_actionShowLogs_triggered() { - char logDir[512]; - if (GetAppConfigPath(logDir, sizeof(logDir), "obs-studio/logs") <= 0) { + BPtr logDir = GetAppConfigPathPtr("obs-studio/logs"); + if (!logDir || !*logDir.Get()) { return; } diff --git a/libobs/util/platform-nix.c b/libobs/util/platform-nix.c index 9b39e8890479a4..02eb9b40741745 100644 --- a/libobs/util/platform-nix.c +++ b/libobs/util/platform-nix.c @@ -404,21 +404,26 @@ size_t os_get_abs_path(const char *path, char *abspath, size_t size) char newpath[PATH_MAX]; int ret; - if (!abspath) - return 0; - if (!realpath(path, newpath)) return 0; + if (!abspath) { + return strlen(newpath); + } + ret = snprintf(abspath, min_size, "%s", newpath); return ret >= 0 ? ret : 0; } char *os_get_abs_path_ptr(const char *path) { - char *ptr = bmalloc(512); + size_t len = os_get_abs_path(path, NULL, 0); + if (!len) + return NULL; + + char *ptr = bmalloc(len + 1); - if (!os_get_abs_path(path, ptr, 512)) { + if (!os_get_abs_path(path, ptr, len + 1)) { bfree(ptr); ptr = NULL; } diff --git a/libobs/util/platform-windows.c b/libobs/util/platform-windows.c index 5af6e81632030a..a343f2fae97475 100644 --- a/libobs/util/platform-windows.c +++ b/libobs/util/platform-windows.c @@ -503,9 +503,6 @@ size_t os_get_abs_path(const char *path, char *abspath, size_t size) size_t out_len = 0; size_t len; - if (!abspath) - return 0; - len = os_utf8_to_wcs(path, 0, wpath, MAX_PATH); if (!len) return 0; @@ -517,9 +514,13 @@ size_t os_get_abs_path(const char *path, char *abspath, size_t size) char *os_get_abs_path_ptr(const char *path) { - char *ptr = bmalloc(MAX_PATH); + size_t len = os_get_abs_path(path, NULL, 0); + if (!len) + return NULL; - if (!os_get_abs_path(path, ptr, MAX_PATH)) { + char *ptr = bmalloc(len + 1); + + if (!os_get_abs_path(path, ptr, len + 1)) { bfree(ptr); ptr = NULL; } @@ -597,18 +598,22 @@ void os_closedir(os_dir_t *dir) int64_t os_get_free_space(const char *path) { ULARGE_INTEGER remainingSpace; - char abs_path[512]; - wchar_t w_abs_path[512]; + char *abs_path; + wchar_t *w_abs_path; + int64_t ret = -1; - if (os_get_abs_path(path, abs_path, 512) > 0) { - if (os_utf8_to_wcs(abs_path, 0, w_abs_path, 512) > 0) { + abs_path = os_get_abs_path_ptr(path); + if (abs_path) { + if (os_utf8_to_wcs_ptr(abs_path, 0, &w_abs_path) > 0) { BOOL success = GetDiskFreeSpaceExW(w_abs_path, (PULARGE_INTEGER)&remainingSpace, NULL, NULL); if (success) - return (int64_t)remainingSpace.QuadPart; + ret = (int64_t)remainingSpace.QuadPart; } + bfree(w_abs_path); + bfree(abs_path); } - return -1; + return ret; } static void make_globent(struct os_globent *ent, WIN32_FIND_DATA *wfd, const char *pattern) @@ -926,24 +931,26 @@ bool get_dll_ver(const wchar_t *lib, struct win_version_info *ver_info) BOOL success; LPVOID data; DWORD size; - char utf8_lib[512]; + char *utf8_lib; if (!ver_initialized && !initialize_version_functions()) return false; if (!ver_initialize_success) return false; - os_wcs_to_utf8(lib, 0, utf8_lib, sizeof(utf8_lib)); + os_wcs_to_utf8_ptr(lib, 0, &utf8_lib); size = get_file_version_info_size(lib, NULL); if (!size) { blog(LOG_ERROR, "Failed to get %s version info size", utf8_lib); + bfree(utf8_lib); return false; } data = bmalloc(size); if (!get_file_version_info(lib, 0, size, data)) { blog(LOG_ERROR, "Failed to get %s version info", utf8_lib); + bfree(utf8_lib); bfree(data); return false; } @@ -951,6 +958,7 @@ bool get_dll_ver(const wchar_t *lib, struct win_version_info *ver_info) success = ver_query_value(data, L"\\", (LPVOID *)&info, &len); if (!success || !info || !len) { blog(LOG_ERROR, "Failed to get %s version info value", utf8_lib); + bfree(utf8_lib); bfree(data); return false; } @@ -960,6 +968,7 @@ bool get_dll_ver(const wchar_t *lib, struct win_version_info *ver_info) ver_info->build = (int)HIWORD(info->dwFileVersionLS); ver_info->revis = (int)LOWORD(info->dwFileVersionLS); + bfree(utf8_lib); bfree(data); return true; } diff --git a/libobs/util/platform.c b/libobs/util/platform.c index 548afc03f73ad7..5320467f50baf0 100644 --- a/libobs/util/platform.c +++ b/libobs/util/platform.c @@ -92,31 +92,32 @@ int64_t os_fgetsize(FILE *file) #ifdef _WIN32 int os_stat(const char *file, struct stat *st) { + int ret = -1; if (file) { - wchar_t w_file[512]; - size_t size = os_utf8_to_wcs(file, 0, w_file, sizeof(w_file)); - if (size > 0) { - struct _stat st_w32; - int ret = _wstat(w_file, &st_w32); - if (ret == 0) { - st->st_dev = st_w32.st_dev; - st->st_ino = st_w32.st_ino; - st->st_mode = st_w32.st_mode; - st->st_nlink = st_w32.st_nlink; - st->st_uid = st_w32.st_uid; - st->st_gid = st_w32.st_gid; - st->st_rdev = st_w32.st_rdev; - st->st_size = st_w32.st_size; - st->st_atime = st_w32.st_atime; - st->st_mtime = st_w32.st_mtime; - st->st_ctime = st_w32.st_ctime; - } - + wchar_t *w_file; + os_utf8_to_wcs_ptr(file, 0, &w_file); + if (!w_file) { return ret; } - } - return -1; + struct _stat st_w32; + ret = _wstat(w_file, &st_w32); + if (ret == 0) { + st->st_dev = st_w32.st_dev; + st->st_ino = st_w32.st_ino; + st->st_mode = st_w32.st_mode; + st->st_nlink = st_w32.st_nlink; + st->st_uid = st_w32.st_uid; + st->st_gid = st_w32.st_gid; + st->st_rdev = st_w32.st_rdev; + st->st_size = st_w32.st_size; + st->st_atime = st_w32.st_atime; + st->st_mtime = st_w32.st_mtime; + st->st_ctime = st_w32.st_ctime; + } + bfree(w_file); + } + return ret; } #endif diff --git a/plugins/nv-filters/nvafx-load.h b/plugins/nv-filters/nvafx-load.h index fde85a002aec51..054f079d8960fd 100644 --- a/plugins/nv-filters/nvafx-load.h +++ b/plugins/nv-filters/nvafx-load.h @@ -219,22 +219,23 @@ void release_lib(void) } } -static inline bool nvafx_get_sdk_path(char *buffer, const size_t len) +static inline bool nvafx_get_sdk_path(wchar_t *buffer, const size_t len) { - DWORD ret = GetEnvironmentVariableA("NVAFX_SDK_DIR", buffer, (DWORD)len); + DWORD ret = GetEnvironmentVariableW(L"NVAFX_SDK_DIR", buffer, (DWORD)len); - if (!ret || ret >= len - 1) { - char path[MAX_PATH]; - if (!GetEnvironmentVariableA("ProgramFiles", path, MAX_PATH)) { + if (!ret || ret >= len) { + wchar_t path[MAX_PATH]; + if (!GetEnvironmentVariableW(L"ProgramFiles", path, MAX_PATH)) { buffer[0] = 0; return false; } - if (_snprintf_s(buffer, len, _TRUNCATE, "%s\\NVIDIA Corporation\\NVIDIA Audio Effects SDK", path) > 0) { - return true; + int r = _snwprintf_s(buffer, len, _TRUNCATE, L"%s\\NVIDIA Corporation\\NVIDIA Audio Effects SDK", + path); + if (r <= 0 || r >= len) { + buffer[0] = 0; + return false; } - - return false; } return true; @@ -242,19 +243,19 @@ static inline bool nvafx_get_sdk_path(char *buffer, const size_t len) static inline bool load_lib() { - char sdkPath[MAX_PATH]; - char effectsPath[MAX_PATH]; + wchar_t sdkPath[MAX_PATH]; + wchar_t effectsPath[MAX_PATH]; if (!nvafx_get_sdk_path(sdkPath, MAX_PATH)) { return false; } - if (_snprintf_s(effectsPath, _countof(effectsPath), _TRUNCATE, "%s\\NVAudioEffects.dll", sdkPath) == -1) { + if (_snwprintf_s(effectsPath, _countof(effectsPath), _TRUNCATE, L"%s\\NVAudioEffects.dll", sdkPath) == -1) { return false; } nv_audiofx = - LoadLibraryExA(effectsPath, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + LoadLibraryExW(effectsPath, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); return !!nv_audiofx; } @@ -269,14 +270,14 @@ static unsigned int get_lib_version(void) version_checked = true; - char sdkPath[MAX_PATH]; + wchar_t sdkPath[MAX_PATH]; wchar_t dllPath[MAX_PATH]; if (!nvafx_get_sdk_path(sdkPath, MAX_PATH)) { return version; } - if (_snwprintf_s(dllPath, _countof(dllPath), _TRUNCATE, L"%S\\NVAudioEffects.dll", sdkPath) == -1) { + if (_snwprintf_s(dllPath, _countof(dllPath), _TRUNCATE, L"%s\\NVAudioEffects.dll", sdkPath) == -1) { return version; } diff --git a/plugins/nv-filters/nvidia-audiofx-filter.c b/plugins/nv-filters/nvidia-audiofx-filter.c index a9a776d13a46ba..d4709c53a39be6 100644 --- a/plugins/nv-filters/nvidia-audiofx-filter.c +++ b/plugins/nv-filters/nvidia-audiofx-filter.c @@ -583,18 +583,17 @@ static void *nvidia_audio_create(obs_data_t *settings, obs_source_t *filter) ng->context = filter; - char sdk_path[MAX_PATH]; + wchar_t sdk_path[MAX_PATH]; /* find SDK */ - if (!nvafx_get_sdk_path(sdk_path, sizeof(sdk_path))) { + if (!nvafx_get_sdk_path(sdk_path, _countof(sdk_path))) { ng->nvidia_sdk_dir_found = false; do_log(LOG_ERROR, "NVAFX redist is not installed."); nvidia_audio_destroy(ng); return NULL; } else { - size_t size = sizeof(sdk_path) + 1; - ng->sdk_path = bmalloc(size); - strcpy(ng->sdk_path, sdk_path); + + os_wcs_to_utf8_ptr(sdk_path, 0, &ng->sdk_path); ng->nvidia_sdk_dir_found = true; ng->nvafx_initialized = false; ng->nvafx_loading = false; @@ -602,7 +601,7 @@ static void *nvidia_audio_create(obs_data_t *settings, obs_source_t *filter) pthread_mutex_init(&ng->nvafx_mutex, NULL); - info("NVAFX SDK redist path was found here %s", sdk_path); + info("NVAFX SDK redist path was found here %s", ng->sdk_path); // set FX const char *method = obs_data_get_string(settings, S_METHOD); set_nv_model(ng, method); diff --git a/plugins/nv-filters/nvidia-videofx-filter.c b/plugins/nv-filters/nvidia-videofx-filter.c index 15341927fe7383..cd998f18820029 100644 --- a/plugins/nv-filters/nvidia-videofx-filter.c +++ b/plugins/nv-filters/nvidia-videofx-filter.c @@ -261,13 +261,20 @@ static bool nvvfx_filter_create_internal(struct nvvfx_data *filter) log_nverror_destroy(filter, vfxErr); if (id == S_FX_AIGS || id == S_FX_BG_BLUR) { - char buffer[MAX_PATH]; - char modelDir[MAX_PATH]; + wchar_t buffer[MAX_PATH]; + wchar_t modelDir[MAX_PATH]; nvvfx_get_sdk_path(buffer, MAX_PATH); - size_t max_len = sizeof(buffer) / sizeof(char); - snprintf(modelDir, max_len, "%s\\models", buffer); - vfxErr = NvVFX_SetString(filter->handle, NVVFX_MODEL_DIRECTORY, modelDir); + int ret = _snwprintf(modelDir, _countof(buffer), L"%s\\models", buffer); + if (ret < 0 || ret >= MAX_PATH) { + return false; + } + + char *modelDirUtf8; + os_wcs_to_utf8_ptr(modelDir, wcslen(modelDir), &modelDirUtf8); + + vfxErr = NvVFX_SetString(filter->handle, NVVFX_MODEL_DIRECTORY, modelDirUtf8); vfxErr = NvVFX_SetCudaStream(filter->handle, NVVFX_CUDA_STREAM, filter->stream); + bfree(modelDirUtf8); if (NVCV_SUCCESS != vfxErr) log_nverror_destroy(filter, vfxErr); } diff --git a/plugins/nv-filters/nvvfx-load.h b/plugins/nv-filters/nvvfx-load.h index e27ad1e62afda4..e5ccc8214c303d 100644 --- a/plugins/nv-filters/nvvfx-load.h +++ b/plugins/nv-filters/nvvfx-load.h @@ -667,22 +667,22 @@ static inline void release_nv_vfx() } } -static inline bool nvvfx_get_sdk_path(char *buffer, const size_t len) +static inline bool nvvfx_get_sdk_path(wchar_t *buffer, const size_t len) { - DWORD ret = GetEnvironmentVariableA("NV_VIDEO_EFFECTS_PATH", buffer, (DWORD)len); + DWORD ret = GetEnvironmentVariableW(L"NV_VIDEO_EFFECTS_PATH", buffer, (DWORD)len); - if (!ret || ret >= len - 1) { - char path[MAX_PATH]; - if (!GetEnvironmentVariableA("ProgramFiles", path, MAX_PATH)) { + if (!ret || ret >= len) { + wchar_t path[MAX_PATH]; + if (!GetEnvironmentVariableW(L"ProgramFiles", path, MAX_PATH)) { buffer[0] = 0; return false; } - if (_snprintf_s(buffer, len, _TRUNCATE, "%s\\NVIDIA Corporation\\NVIDIA Video Effects", path) > 0) { - return true; + int r = _snwprintf_s(buffer, len, _TRUNCATE, L"%s\\NVIDIA Corporation\\NVIDIA Video Effects", path); + if (r < 0 || r >= len) { + buffer[0] = 0; + return false; } - - return false; } return true; @@ -690,27 +690,30 @@ static inline bool nvvfx_get_sdk_path(char *buffer, const size_t len) static inline bool load_nv_vfx_libs() { - char sdkPath[MAX_PATH]; - char effectsPath[MAX_PATH]; - char imagePath[MAX_PATH]; + wchar_t sdkPath[MAX_PATH]; + wchar_t effectsPath[MAX_PATH]; + wchar_t imagePath[MAX_PATH]; + int ret; if (!nvvfx_get_sdk_path(sdkPath, MAX_PATH)) { return false; } - if (_snprintf_s(effectsPath, _countof(effectsPath), _TRUNCATE, "%s\\NVVideoEffects.dll", sdkPath) == -1) { + ret = _snwprintf_s(effectsPath, _countof(effectsPath), _TRUNCATE, L"%s\\NVVideoEffects.dll", sdkPath); + if (ret < 0 || ret >= MAX_PATH) { return false; } - if (_snprintf_s(imagePath, _countof(imagePath), _TRUNCATE, "%s\\NVCVImage.dll", sdkPath) == -1) { + ret = _snwprintf_s(imagePath, _countof(imagePath), _TRUNCATE, L"%s\\NVCVImage.dll", sdkPath); + if (ret < 0 || ret >= MAX_PATH) { return false; } nv_videofx = - LoadLibraryExA(effectsPath, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + LoadLibraryExW(effectsPath, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); nv_cvimage = - LoadLibraryExA(imagePath, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + LoadLibraryExW(imagePath, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); return !!nv_videofx && !!nv_cvimage; } @@ -725,14 +728,14 @@ static unsigned int get_lib_version(void) version_checked = true; - char sdkPath[MAX_PATH]; + wchar_t sdkPath[MAX_PATH]; wchar_t dllPath[MAX_PATH]; if (!nvvfx_get_sdk_path(sdkPath, MAX_PATH)) { return version; } - if (_snwprintf_s(dllPath, _countof(dllPath), _TRUNCATE, L"%S\\NVVideoEffects.dll", sdkPath) == -1) { + if (_snwprintf_s(dllPath, _countof(dllPath), _TRUNCATE, L"%s\\NVVideoEffects.dll", sdkPath) == -1) { return version; } diff --git a/plugins/text-freetype2/find-font-windows.c b/plugins/text-freetype2/find-font-windows.c index 6c9fcd297b26ec..3f8a8c7455515a 100644 --- a/plugins/text-freetype2/find-font-windows.c +++ b/plugins/text-freetype2/find-font-windows.c @@ -170,74 +170,79 @@ char *sfnt_name_to_utf8(FT_SfntName *sfnt_name) uint32_t get_font_checksum(void) { uint32_t checksum = 0; - struct dstr path = {0}; + wchar_t path[MAX_PATH]; + wchar_t search[MAX_PATH]; HANDLE handle; - WIN32_FIND_DATAA wfd; + WIN32_FIND_DATAW wfd; - dstr_reserve(&path, MAX_PATH); - - HRESULT res = SHGetFolderPathA(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, path.array); + HRESULT res = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, path); if (res != S_OK) { blog(LOG_WARNING, "Error finding windows font folder"); return 0; } - path.len = strlen(path.array); - dstr_cat(&path, "\\*.*"); - - handle = FindFirstFileA(path.array, &wfd); - if (handle == INVALID_HANDLE_VALUE) - goto free_string; + int ret = _snwprintf(search, MAX_PATH, L"%s\\*.*", path); + if (ret < 0 || ret >= MAX_PATH) { + return checksum; + } - dstr_resize(&path, path.len - 4); + handle = FindFirstFileW(search, &wfd); + if (handle == INVALID_HANDLE_VALUE) { + return checksum; + } do { checksum = calc_crc32(checksum, &wfd.ftLastWriteTime, sizeof(FILETIME)); - checksum = calc_crc32(checksum, wfd.cFileName, strlen(wfd.cFileName)); - } while (FindNextFileA(handle, &wfd)); + checksum = calc_crc32(checksum, wfd.cFileName, wcslen(wfd.cFileName) * sizeof(wchar_t)); + } while (FindNextFileW(handle, &wfd)); FindClose(handle); - -free_string: - dstr_free(&path); return checksum; } void load_os_font_list(void) { - struct dstr path = {0}; + wchar_t path[MAX_PATH]; + wchar_t search[MAX_PATH]; HANDLE handle; - WIN32_FIND_DATAA wfd; + WIN32_FIND_DATAW wfd; + char *path_utf8; - dstr_reserve(&path, MAX_PATH); - - HRESULT res = SHGetFolderPathA(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, path.array); + HRESULT res = SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, path); if (res != S_OK) { blog(LOG_WARNING, "Error finding windows font folder"); return; } - path.len = strlen(path.array); - dstr_cat(&path, "\\*.*"); + int ret = _snwprintf(search, MAX_PATH, L"%s\\*.*", path); + if (ret < 0 || ret >= MAX_PATH) { + return; + } - handle = FindFirstFileA(path.array, &wfd); + handle = FindFirstFileW(search, &wfd); if (handle == INVALID_HANDLE_VALUE) - goto free_string; + return; - dstr_resize(&path, path.len - 4); + path_utf8 = wide_to_utf8(path, MAX_PATH); do { - struct dstr full_path = {0}; FT_Face face; FT_Long idx = 0; FT_Long max_faces = 1; + char *filename; if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; - dstr_copy_dstr(&full_path, &path); + filename = wide_to_utf8(wfd.cFileName, MAX_PATH); + if (!filename) + continue; + + struct dstr full_path = {0}; + dstr_copy(&full_path, path_utf8); dstr_cat(&full_path, "\\"); - dstr_cat(&full_path, wfd.cFileName); + dstr_cat(&full_path, filename); + bfree(filename); while (idx < max_faces) { FT_Error ret = FT_New_Face(ft2_lib, full_path.array, idx, &face); @@ -250,12 +255,10 @@ void load_os_font_list(void) } dstr_free(&full_path); - } while (FindNextFileA(handle, &wfd)); + } while (FindNextFileW(handle, &wfd)); + bfree(path_utf8); FindClose(handle); save_font_list(); - -free_string: - dstr_free(&path); } From e70bb7c8f690e6b1032ca39418a575dcc143bfa3 Mon Sep 17 00:00:00 2001 From: Henri Kulotie Date: Mon, 17 Aug 2026 20:58:03 +0300 Subject: [PATCH 4/5] libobs: Constant size for storing utf-8 paths Define FILENAME_MAX_LENGTH_UTF8 (_MAX_PATH * 3) for storing utf-16 converted paths in utf-8 on Windows for cases where dynamic allocation could may be performance issue and for simplicity. A UTF-16 code unit (wchar_t) can be up to 3 bytes/chars in UTF-8. Change os_dirent filename to be char pointer, so it is OS agnostic about the actual allocation size. It is also compiled in to lua/python wraps, so we can't use platform specific defines. --- frontend/updater/updater.cpp | 3 ++- libobs/obs-win-crash-handler.c | 9 +++++---- libobs/util/platform-nix.c | 5 +---- libobs/util/platform-windows.c | 8 +++++--- libobs/util/platform.h | 11 ++++++++++- 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/frontend/updater/updater.cpp b/frontend/updater/updater.cpp index fcb1248b2ddd6f..88f43a399c9fab 100644 --- a/frontend/updater/updater.cpp +++ b/frontend/updater/updater.cpp @@ -1568,7 +1568,8 @@ static bool Update(wchar_t *cmdLine) continue; } - char outputPath[MAX_PATH]; + // UTF-16 code unit converted to UTF-8 is 3 bytes at most + char outputPath[MAX_PATH * 3]; if (!WideToUTF8Buf(outputPath, update.outputPath.c_str())) { continue; } diff --git a/libobs/obs-win-crash-handler.c b/libobs/obs-win-crash-handler.c index 664a9c6eb12305..d5bb8ee733a419 100644 --- a/libobs/obs-win-crash-handler.c +++ b/libobs/obs-win-crash-handler.c @@ -212,8 +212,8 @@ static inline void init_cpu_info(struct exception_handler_data *data) static BOOL CALLBACK enum_all_modules(PCTSTR module_name, DWORD64 module_base, ULONG module_size, struct exception_handler_data *data) { - char name_utf8[MAX_PATH]; - os_wcs_to_utf8(module_name, 0, name_utf8, MAX_PATH); + char *name_utf8; + os_wcs_to_utf8_ptr(module_name, 0, &name_utf8); if (data->main_trace.instruction_ptr >= module_base && data->main_trace.instruction_ptr < module_base + module_size) { @@ -229,6 +229,7 @@ static BOOL CALLBACK enum_all_modules(PCTSTR module_name, DWORD64 module_base, U dstr_catf(&data->module_list, "%08" PRIX64 "-%08" PRIX64 " %s\r\n", module_base, module_base + module_size, name_utf8); #endif + bfree(name_utf8); return true; } @@ -271,14 +272,14 @@ static inline void write_header(struct exception_handler_data *data) struct module_info { DWORD64 addr; - char name_utf8[MAX_PATH]; + char name_utf8[FILENAME_MAX_LENGTH_UTF8]; }; static BOOL CALLBACK enum_module(PCTSTR module_name, DWORD64 module_base, ULONG module_size, struct module_info *info) { if (info->addr >= module_base && info->addr < module_base + module_size) { - os_wcs_to_utf8(module_name, 0, info->name_utf8, MAX_PATH); + os_wcs_to_utf8(module_name, 0, info->name_utf8, FILENAME_MAX_LENGTH_UTF8); strlwr(info->name_utf8); return false; } diff --git a/libobs/util/platform-nix.c b/libobs/util/platform-nix.c index 02eb9b40741745..deed28981bb153 100644 --- a/libobs/util/platform-nix.c +++ b/libobs/util/platform-nix.c @@ -474,10 +474,7 @@ struct os_dirent *os_readdir(os_dir_t *dir) if (!dir->cur_dirent) return NULL; - const size_t length = strlen(dir->cur_dirent->d_name); - if (sizeof(dir->out.d_name) <= length) - return NULL; - memcpy(dir->out.d_name, dir->cur_dirent->d_name, length + 1); + dir->out.d_name = dir->cur_dirent->d_name; dstr_copy(&file_path, dir->path); dstr_cat(&file_path, "/"); diff --git a/libobs/util/platform-windows.c b/libobs/util/platform-windows.c index a343f2fae97475..c809806d70e74c 100644 --- a/libobs/util/platform-windows.c +++ b/libobs/util/platform-windows.c @@ -428,8 +428,8 @@ static char *os_get_path_ptr_internal(const char *name, int folder) os_wcs_to_utf8_ptr(path_utf16, 0, &ptr); dstr_init_move_array(&path, ptr); if (name && *name) { - dstr_cat(&path, "\\"); - dstr_cat(&path, name); + dstr_cat(&path, "\\"); + dstr_cat(&path, name); } return path.array; } @@ -533,6 +533,7 @@ struct os_dir { WIN32_FIND_DATA wfd; bool first; struct os_dirent out; + char filename_utf8[FILENAME_MAX_LENGTH_UTF8]; }; os_dir_t *os_opendir(const char *path) @@ -580,8 +581,9 @@ struct os_dirent *os_readdir(os_dir_t *dir) return NULL; } - os_wcs_to_utf8(dir->wfd.cFileName, 0, dir->out.d_name, sizeof(dir->out.d_name)); + os_wcs_to_utf8(dir->wfd.cFileName, 0, dir->filename_utf8, sizeof(dir->filename_utf8)); + dir->out.d_name = dir->filename_utf8; dir->out.directory = is_dir(&dir->wfd); return &dir->out; diff --git a/libobs/util/platform.h b/libobs/util/platform.h index 592d9eca6e5c8c..c56ece333d8ecb 100644 --- a/libobs/util/platform.h +++ b/libobs/util/platform.h @@ -30,6 +30,15 @@ extern "C" { #endif +#ifdef _WIN32 +/* + * Maximum length of a UTF-8 encoded filename (not path) converted from a UTF-16 filename on Windows. + * A UTF-16 code unit (wchar_t) can be up to 3 bytes/chars in UTF-8. + * Other OSs use UTF-8 apis directly, so the there's no expansion between api and internal storage. + */ +#define FILENAME_MAX_LENGTH_UTF8 (_MAX_PATH * 3) +#endif + EXPORT FILE *os_wfopen(const wchar_t *path, const char *mode); EXPORT FILE *os_fopen(const char *path, const char *mode); EXPORT int64_t os_fgetsize(FILE *file); @@ -121,7 +130,7 @@ struct os_dir; typedef struct os_dir os_dir_t; struct os_dirent { - char d_name[256]; + char *d_name; bool directory; }; From 04856a1b1e85f4c6888e9485bfb16b1264b0dc86 Mon Sep 17 00:00:00 2001 From: Henri Kulotie Date: Mon, 3 Aug 2026 00:09:15 +0300 Subject: [PATCH 5/5] plugins: Switch to W apis for file paths in graphics-hook --- .../graphics-hook/d3d12-capture.cpp | 4 +- .../graphics-hook/d3d8-capture.cpp | 4 +- .../graphics-hook/d3d9-capture.cpp | 10 ++-- .../graphics-hook/dxgi-capture.cpp | 2 +- .../win-capture/graphics-hook/gl-capture.c | 16 +++--- .../win-capture/graphics-hook/graphics-hook.c | 13 +++-- .../win-capture/graphics-hook/graphics-hook.h | 56 ++++++++++++------- .../graphics-hook/vulkan-capture.c | 4 +- 8 files changed, 66 insertions(+), 43 deletions(-) diff --git a/plugins/win-capture/graphics-hook/d3d12-capture.cpp b/plugins/win-capture/graphics-hook/d3d12-capture.cpp index 991c4c6a29b417..a06a626182b9de 100644 --- a/plugins/win-capture/graphics-hook/d3d12-capture.cpp +++ b/plugins/win-capture/graphics-hook/d3d12-capture.cpp @@ -123,7 +123,7 @@ static bool d3d12_init_11on12(ID3D12Device *device) static bool initialized_func = false; if (!initialized_11 && !d3d11) { - d3d11 = load_system_library("d3d11.dll"); + d3d11 = load_system_library(L"d3d11.dll"); if (!d3d11) { hlog("d3d12_init_11on12: failed to load d3d11"); } @@ -403,7 +403,7 @@ static bool manually_get_d3d12_addrs(HMODULE d3d12_module, PFN_ExecuteCommandLis bool hook_d3d12(void) { - HMODULE d3d12_module = get_system_module("d3d12.dll"); + HMODULE d3d12_module = get_system_module(L"d3d12.dll"); if (!d3d12_module) { hlog_verbose("Failed to find d3d12.dll. Skipping hook attempt."); return false; diff --git a/plugins/win-capture/graphics-hook/d3d8-capture.cpp b/plugins/win-capture/graphics-hook/d3d8-capture.cpp index b9ac4916736a07..73c75ad4e951a9 100644 --- a/plugins/win-capture/graphics-hook/d3d8-capture.cpp +++ b/plugins/win-capture/graphics-hook/d3d8-capture.cpp @@ -174,7 +174,7 @@ static void d3d8_free() static void d3d8_init(IDirect3DDevice8 *device) { - data.d3d8 = get_system_module("d3d8.dll"); + data.d3d8 = get_system_module(L"d3d8.dll"); if (!d3d8_init_format_backbuffer(device)) { return; @@ -349,7 +349,7 @@ static bool manually_get_d3d8_present_addr(HMODULE d3d8_module, void **present_a bool hook_d3d8(void) { - HMODULE d3d8_module = get_system_module("d3d8.dll"); + HMODULE d3d8_module = get_system_module(L"d3d8.dll"); uint32_t d3d8_size; void *present_addr = nullptr; diff --git a/plugins/win-capture/graphics-hook/d3d9-capture.cpp b/plugins/win-capture/graphics-hook/d3d9-capture.cpp index b9b72b8bb933de..eb5f480b59cf50 100644 --- a/plugins/win-capture/graphics-hook/d3d9-capture.cpp +++ b/plugins/win-capture/graphics-hook/d3d9-capture.cpp @@ -121,13 +121,13 @@ static inline bool shex_init_d3d11() HMODULE dxgi; HRESULT hr; - d3d11 = load_system_library("d3d11.dll"); + d3d11 = load_system_library(L"d3d11.dll"); if (!d3d11) { hlog("d3d9_init: Failed to load D3D11"); return false; } - dxgi = load_system_library("dxgi.dll"); + dxgi = load_system_library(L"dxgi.dll"); if (!dxgi) { hlog("d3d9_init: Failed to load DXGI"); return false; @@ -429,7 +429,7 @@ static void d3d9_init(IDirect3DDevice9 *device) HWND window = nullptr; HRESULT hr; - data.d3d9 = get_system_module("d3d9.dll"); + data.d3d9 = get_system_module(L"d3d9.dll"); data.device = device; hr = device->QueryInterface(__uuidof(IDirect3DDevice9Ex), (void **)&d3d9ex); @@ -465,7 +465,7 @@ static inline HRESULT get_backbuffer(IDirect3DDevice9 *device, IDirect3DSurface9 static bool checked_exceptions = false; if (!checked_exceptions) { - if (_strcmpi(get_process_name(), "hotd_ng.exe") == 0) { + if (_wcsicmp(get_process_name(), L"hotd_ng.exe") == 0) { use_backbuffer = true; } checked_exceptions = true; @@ -792,7 +792,7 @@ static bool manually_get_d3d9_addrs(HMODULE d3d9_module, void **present_addr, vo bool hook_d3d9(void) { - HMODULE d3d9_module = get_system_module("d3d9.dll"); + HMODULE d3d9_module = get_system_module(L"d3d9.dll"); uint32_t d3d9_size; void *present_addr = nullptr; void *present_ex_addr = nullptr; diff --git a/plugins/win-capture/graphics-hook/dxgi-capture.cpp b/plugins/win-capture/graphics-hook/dxgi-capture.cpp index 28208eb8884545..b3f92d35bb824c 100644 --- a/plugins/win-capture/graphics-hook/dxgi-capture.cpp +++ b/plugins/win-capture/graphics-hook/dxgi-capture.cpp @@ -305,7 +305,7 @@ static HRESULT STDMETHODCALLTYPE hook_present1(IDXGISwapChain1 *swap, UINT sync_ bool hook_dxgi(void) { - HMODULE dxgi_module = get_system_module("dxgi.dll"); + HMODULE dxgi_module = get_system_module(L"dxgi.dll"); if (!dxgi_module) { hlog_verbose("Failed to find dxgi.dll. Skipping hook attempt."); return false; diff --git a/plugins/win-capture/graphics-hook/gl-capture.c b/plugins/win-capture/graphics-hook/gl-capture.c index 61d4efad9844d3..5c3a61d233f83f 100644 --- a/plugins/win-capture/graphics-hook/gl-capture.c +++ b/plugins/win-capture/graphics-hook/gl-capture.c @@ -266,13 +266,13 @@ static inline bool gl_shtex_init_d3d11(void) IDXGIAdapter *adapter; HRESULT hr; - HMODULE d3d11 = load_system_library("d3d11.dll"); + HMODULE d3d11 = load_system_library(L"d3d11.dll"); if (!d3d11) { hlog("gl_shtex_init_d3d11: failed to load D3D11.dll: %d", GetLastError()); return false; } - HMODULE dxgi = load_system_library("dxgi.dll"); + HMODULE dxgi = load_system_library(L"dxgi.dll"); if (!dxgi) { hlog("gl_shtex_init_d3d11: failed to load DXGI.dll: %d", GetLastError()); return false; @@ -563,7 +563,7 @@ static void gl_copy_backbuffer(GLuint dst) glReadBuffer(GL_BACK); /* darkest dungeon fix */ - darkest_dungeon_fix = glGetError() == GL_INVALID_OPERATION && _strcmpi(process_name, "Darkest.exe") == 0; + darkest_dungeon_fix = glGetError() == GL_INVALID_OPERATION && _wcsicmp(process_name, L"Darkest.exe") == 0; glDrawBuffer(GL_COLOR_ATTACHMENT0); if (gl_error("gl_copy_backbuffer", "failed to set draw buffer")) { @@ -820,16 +820,18 @@ bool hook_gl(void) void *wgl_slb_proc; void *wgl_sb_proc; - gl = get_system_module("opengl32.dll"); + gl = get_system_module(L"opengl32.dll"); if (!gl) { return false; } /* "life is feudal: your own" somehow uses both opengl and directx at * the same time, so blacklist it from capturing opengl */ - const char *process_name = get_process_name(); - if (_strcmpi(process_name, "yo_cm_client.exe") == 0 || _strcmpi(process_name, "cm_client.exe") == 0) { - hlog("Ignoring opengl for game: %s", process_name); + const wchar_t *process_name = get_process_name(); + if (_wcsicmp(process_name, L"yo_cm_client.exe") == 0 || _wcsicmp(process_name, L"cm_client.exe") == 0) { + char *process_name_utf8 = wide_to_utf8_ptr(process_name); + hlog("Ignoring opengl for game: %s", process_name_utf8 ? process_name_utf8 : ""); + free(process_name_utf8); return true; } diff --git a/plugins/win-capture/graphics-hook/graphics-hook.c b/plugins/win-capture/graphics-hook/graphics-hook.c index b6b3e9ec1f62dc..97dca34e977283 100644 --- a/plugins/win-capture/graphics-hook/graphics-hook.c +++ b/plugins/win-capture/graphics-hook/graphics-hook.c @@ -45,8 +45,8 @@ static HINSTANCE dll_inst = NULL; static volatile bool stop_loop = false; static HANDLE dup_hook_mutex = NULL; static HANDLE capture_thread = NULL; -char system_path[MAX_PATH] = {0}; -char process_name[MAX_PATH] = {0}; +wchar_t system_path[MAX_PATH] = {0}; +wchar_t process_name[MAX_PATH] = {0}; wchar_t keepalive_name[64] = {0}; HWND dummy_window = NULL; @@ -147,7 +147,7 @@ static inline bool init_mutexes(void) static inline bool init_system_path(void) { - UINT ret = GetSystemDirectoryA(system_path, MAX_PATH); + UINT ret = GetSystemDirectoryW(system_path, MAX_PATH); if (!ret) { hlog("Failed to get windows system path: %lu", GetLastError()); return false; @@ -158,10 +158,13 @@ static inline bool init_system_path(void) static inline void log_current_process(void) { - DWORD len = GetModuleBaseNameA(GetCurrentProcess(), NULL, process_name, MAX_PATH); + DWORD len = GetModuleBaseNameW(GetCurrentProcess(), NULL, process_name, MAX_PATH); if (len > 0) { process_name[len] = 0; - hlog("graphics-hook.dll loaded against process: %s", process_name); + char *process_name_utf8 = wide_to_utf8_ptr(process_name); + hlog("graphics-hook.dll loaded against process: %s", + process_name_utf8 ? process_name_utf8 : ""); + free(process_name_utf8); } else { hlog("graphics-hook.dll loaded"); } diff --git a/plugins/win-capture/graphics-hook/graphics-hook.h b/plugins/win-capture/graphics-hook/graphics-hook.h index 1958ae3bb6f391..b2b48bac7d05a2 100644 --- a/plugins/win-capture/graphics-hook/graphics-hook.h +++ b/plugins/win-capture/graphics-hook/graphics-hook.h @@ -32,9 +32,9 @@ extern "C" { extern void hlog(const char *format, ...); extern void hlog_hr(const char *text, HRESULT hr); -static inline const char *get_process_name(void); -static inline HMODULE get_system_module(const char *module); -static inline HMODULE load_system_library(const char *module); +static inline const wchar_t *get_process_name(void); +static inline HMODULE get_system_module(const wchar_t *module); +static inline HMODULE load_system_library(const wchar_t *module); extern uint64_t os_gettime_ns(void); #define flog(format, ...) hlog("%s: " format, __FUNCTION__, ##__VA_ARGS__) @@ -106,25 +106,43 @@ extern HANDLE signal_stop; extern HANDLE signal_ready; extern HANDLE signal_exit; extern HANDLE tex_mutexes[2]; -extern char system_path[MAX_PATH]; -extern char process_name[MAX_PATH]; +extern wchar_t system_path[MAX_PATH]; +extern wchar_t process_name[MAX_PATH]; extern wchar_t keepalive_name[64]; extern HWND dummy_window; extern volatile bool active; -static inline const char *get_process_name(void) +static inline char *wide_to_utf8_ptr(const wchar_t *wstr) +{ + if (!wstr) + return NULL; + + int wlen = (int)wcslen(wstr); + int size = WideCharToMultiByte(CP_UTF8, 0, wstr, wlen + 1, NULL, 0, NULL, NULL); + if (size <= 0) + return NULL; + + char *str = (char *)malloc((size_t)size); + if (!str) + return NULL; + + WideCharToMultiByte(CP_UTF8, 0, wstr, wlen + 1, str, size, NULL, NULL); + return str; +} + +static inline const wchar_t *get_process_name(void) { return process_name; } -static inline HMODULE get_system_module(const char *module) +static inline HMODULE get_system_module(const wchar_t *module) { - char base_path[MAX_PATH]; + wchar_t base_path[MAX_PATH]; - strcpy(base_path, system_path); - strcat(base_path, "\\"); - strcat(base_path, module); - return GetModuleHandleA(base_path); + wcscpy(base_path, system_path); + wcscat(base_path, L"\\"); + wcscat(base_path, module); + return GetModuleHandleW(base_path); } static inline uint32_t module_size(HMODULE module) @@ -134,20 +152,20 @@ static inline uint32_t module_size(HMODULE module) return success ? info.SizeOfImage : 0; } -static inline HMODULE load_system_library(const char *name) +static inline HMODULE load_system_library(const wchar_t *name) { - char base_path[MAX_PATH]; + wchar_t base_path[MAX_PATH]; HMODULE module; - strcpy(base_path, system_path); - strcat(base_path, "\\"); - strcat(base_path, name); + wcscpy(base_path, system_path); + wcscat(base_path, L"\\"); + wcscat(base_path, name); - module = GetModuleHandleA(base_path); + module = GetModuleHandleW(base_path); if (module) return module; - return LoadLibraryA(base_path); + return LoadLibraryW(base_path); } static inline bool capture_alive(void) diff --git a/plugins/win-capture/graphics-hook/vulkan-capture.c b/plugins/win-capture/graphics-hook/vulkan-capture.c index e1eded5e3e4259..53fa9b92c807f1 100644 --- a/plugins/win-capture/graphics-hook/vulkan-capture.c +++ b/plugins/win-capture/graphics-hook/vulkan-capture.c @@ -575,13 +575,13 @@ static inline bool vk_shtex_init_d3d11(struct vk_data *data) IDXGIAdapter1 *adapter; HRESULT hr; - HMODULE d3d11 = load_system_library("d3d11.dll"); + HMODULE d3d11 = load_system_library(L"d3d11.dll"); if (!d3d11) { flog("failed to load d3d11: %d", GetLastError()); return false; } - HMODULE dxgi = load_system_library("dxgi.dll"); + HMODULE dxgi = load_system_library(L"dxgi.dll"); if (!dxgi) { flog("failed to load dxgi: %d", GetLastError()); return false;