@@ -108,6 +108,15 @@ namespace {
108108 constexpr const char * INI_PACK_PATH = " Pack" ;
109109 constexpr const char * INI_PACK_ENABLED = " PackEnabled" ;
110110
111+ // path::string() would narrow to the ANSI codepage and lose non-ASCII characters
112+ // (e.g. a "µ" in a pack name). Encode as UTF-8 instead, which round-trips through
113+ // the INI and renders correctly in ImGui. Pair with TextUtils::StringToWString to
114+ // decode back to a path.
115+ std::string PathToUtf8 (const std::filesystem::path& p)
116+ {
117+ return TextUtils::WStringToString (p.wstring ());
118+ }
119+
111120 // =========================================================================
112121 // gMod TextureClient helpers
113122 // =========================================================================
@@ -182,7 +191,7 @@ namespace {
182191 it->loaded = true ;
183192 continue ;
184193 }
185- packs.push_back ({p, p.stem (). string ( ), /* loaded=*/ true , /* external=*/ true });
194+ packs.push_back ({p, PathToUtf8 ( p.stem ()), /* loaded=*/ true , /* external=*/ true });
186195 }
187196 if (clear_missing) {
188197 for (auto & pack : packs) {
@@ -539,7 +548,7 @@ namespace {
539548 });
540549 if (it == packs.end ()) {
541550 if (create_if_not_found) {
542- packs.push_back ({path, path.stem (). string ( ), false , false });
551+ packs.push_back ({path, PathToUtf8 ( path.stem ()), false , false });
543552 return &packs.back ();
544553 }
545554 return nullptr ;
@@ -550,11 +559,11 @@ namespace {
550559 bool LoadTexturePack (const std::filesystem::path& path)
551560 {
552561 if (!std::filesystem::exists (path)) {
553- statusMessage = " File not found: " + path. string ( );
562+ statusMessage = " File not found: " + PathToUtf8 (path );
554563 return false ;
555564 }
556565 auto pack = FindPack (path, true );
557- const auto filename = pack->path .filename (). string ( );
566+ const auto filename = PathToUtf8 ( pack->path .filename ());
558567 pack->loaded = true ;
559568 if (!gmodReady) {
560569 // First pack configured but gMod isn't loaded yet: fetch/download it now.
@@ -738,7 +747,11 @@ namespace {
738747 void OnTexmodFileChosen (const char * path)
739748 {
740749 if (!path) return ;
741- std::filesystem::path p = path;
750+ // The file dialog hands back the path as UTF-8. Building a filesystem::path
751+ // straight from a char* would decode it in the ANSI codepage instead, mangling
752+ // non-ASCII names (e.g. a "µ" in the filename) into a path that doesn't exist.
753+ // Decode the UTF-8 to a wide string first so the full Unicode path survives.
754+ const std::filesystem::path p = TextUtils::StringToWString (path);
742755 if (!std::filesystem::exists (p)) return ;
743756 LoadTexturePack (p);
744757 }
@@ -849,7 +862,7 @@ namespace {
849862 ImGui::TextDisabled (" %s" , pack.label .c_str ());
850863
851864 ImGui::TableSetColumnIndex (2 );
852- const std::string pathStr = pack.path . string ( );
865+ const std::string pathStr = PathToUtf8 ( pack.path );
853866 ImGui::TextDisabled (" %s" , pathStr.c_str ());
854867 if (ImGui::IsItemHovered ()) ImGui::SetTooltip (" %s" , pathStr.c_str ());
855868
@@ -1205,7 +1218,7 @@ void TexmodModule::DrawSettingsInternal()
12051218 ImGui::Separator ();
12061219 DrawPackList ();
12071220 ImGui::Separator ();
1208- if (ImGui::Button (" Add texture pack" )) Resources::OpenFileDialog (OnTexmodFileChosen, " tpf,zip,dds" , Resources::GetSettingsFolderPath (). string ( ).c_str ());
1221+ if (ImGui::Button (" Add texture pack" )) Resources::OpenFileDialog (OnTexmodFileChosen, " tpf,zip,dds" , PathToUtf8 ( Resources::GetSettingsFolderPath ()).c_str ());
12091222 ImGui::TextDisabled (" Supported: .tpf, .zip (texmod format), .dds" );
12101223 ImGui::Separator ();
12111224 DrawTextureGallery ();
@@ -1220,10 +1233,12 @@ void TexmodModule::LoadSettings(ToolboxIni* ini)
12201233 const std::string key = std::string (INI_PACK_PATH) + std::to_string (i);
12211234 const char * val = ini->GetValue (INI_SECTION, key.c_str (), nullptr );
12221235 if (!val || !*val) continue ;
1223- std::filesystem::path p = val;
1236+ // Stored as UTF-8 (see SaveSettings); decode to a wide path so non-ASCII names
1237+ // survive. Falls back to the ANSI codepage for any pre-existing legacy value.
1238+ std::filesystem::path p = TextUtils::StringToWString (val);
12241239 const std::string enabled_key = std::string (INI_PACK_ENABLED) + std::to_string (i);
12251240 const bool enabled = ini->GetBoolValue (INI_SECTION, enabled_key.c_str (), false );
1226- packs.push_back ({p, p.stem (). string ( ), /* loaded=*/ enabled});
1241+ packs.push_back ({p, PathToUtf8 ( p.stem ()), /* loaded=*/ enabled});
12271242 }
12281243 // gMod is not ready yet (no device); the restored enabled flags are pushed to
12291244 // it later by RestoreLoadedPacks(). This call is a no-op until then.
@@ -1242,7 +1257,7 @@ void TexmodModule::SaveSettings(ToolboxIni* ini)
12421257 ini->SetLongValue (INI_SECTION, INI_PACK_COUNT, static_cast <long >(packs.size ()));
12431258 for (size_t i = 0 ; i < packs.size (); i++) {
12441259 const std::string key = std::string (INI_PACK_PATH) + std::to_string (i);
1245- ini->SetValue (INI_SECTION, key.c_str (), packs[i].path . string ( ).c_str ());
1260+ ini->SetValue (INI_SECTION, key.c_str (), PathToUtf8 ( packs[i].path ).c_str ());
12461261 const std::string enabled_key = std::string (INI_PACK_ENABLED) + std::to_string (i);
12471262 ini->SetBoolValue (INI_SECTION, enabled_key.c_str (), packs[i].loaded );
12481263 }
0 commit comments