diff --git a/src/common/config.cpp b/src/common/config.cpp index a5ef95bb..4fb2818e 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -4,9 +4,25 @@ #include #include #include +#include // for wstring support #include +#include "common/logging/formatter.h" #include "config.h" +namespace toml { +template +std::filesystem::path find_fs_path_or(const basic_value& v, const K& ky, + std::filesystem::path opt) { + try { + auto str = find(v, ky); + std::u8string u8str{(char8_t*)&str.front(), (char8_t*)&str.back() + 1}; + return std::filesystem::path{u8str}; + } catch (...) { + return opt; + } +} +} // namespace toml + namespace Config { static bool isNeo = false; @@ -37,7 +53,7 @@ static bool vkMarkers = false; static bool vkCrashDiagnostic = false; // Gui -std::string settings_install_dir = ""; +std::filesystem::path settings_install_dir = {}; u32 main_window_geometry_x = 400; u32 main_window_geometry_y = 400; u32 main_window_geometry_w = 1280; @@ -267,7 +283,7 @@ void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_w = w; main_window_geometry_h = h; } -void setGameInstallDir(const std::string& dir) { +void setGameInstallDir(const std::filesystem::path& dir) { settings_install_dir = dir; } void setMainWindowTheme(u32 theme) { @@ -323,7 +339,7 @@ u32 getMainWindowGeometryW() { u32 getMainWindowGeometryH() { return main_window_geometry_h; } -std::string getGameInstallDir() { +std::filesystem::path getGameInstallDir() { return settings_install_dir; } u32 getMainWindowTheme() { @@ -378,7 +394,10 @@ void load(const std::filesystem::path& path) { toml::value data; try { - data = toml::parse(path); + std::ifstream ifs; + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + ifs.open(path, std::ios_base::binary); + data = toml::parse(ifs, std::string{fmt::UTF(path.filename().u8string()).data}); } catch (std::exception& ex) { fmt::print("Got exception trying to load config file. Exception: {}\n", ex.what()); return; @@ -444,7 +463,7 @@ void load(const std::filesystem::path& path) { mw_themes = toml::find_or(gui, "theme", 0); m_window_size_W = toml::find_or(gui, "mw_width", 0); m_window_size_H = toml::find_or(gui, "mw_height", 0); - settings_install_dir = toml::find_or(gui, "installDir", ""); + settings_install_dir = toml::find_fs_path_or(gui, "installDir", {}); main_window_geometry_x = toml::find_or(gui, "geometry_x", 0); main_window_geometry_y = toml::find_or(gui, "geometry_y", 0); main_window_geometry_w = toml::find_or(gui, "geometry_w", 0); @@ -468,17 +487,19 @@ void save(const std::filesystem::path& path) { std::error_code error; if (std::filesystem::exists(path, error)) { try { - data = toml::parse(path); + std::ifstream ifs; + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + ifs.open(path, std::ios_base::binary); + data = toml::parse(ifs, std::string{fmt::UTF(path.filename().u8string()).data}); } catch (const std::exception& ex) { fmt::print("Exception trying to parse config file. Exception: {}\n", ex.what()); return; } } else { if (error) { - fmt::print("Filesystem error accessing {} (error: {})\n", path.string(), - error.message().c_str()); + fmt::print("Filesystem error: {}\n", error.message()); } - fmt::print("Saving new configuration file {}\n", path.string()); + fmt::print("Saving new configuration file {}\n", fmt::UTF(path.u8string())); } data["General"]["isPS4Pro"] = isNeo; @@ -515,7 +536,7 @@ void save(const std::filesystem::path& path) { data["GUI"]["gameTableMode"] = m_table_mode; data["GUI"]["mw_width"] = m_window_size_W; data["GUI"]["mw_height"] = m_window_size_H; - data["GUI"]["installDir"] = settings_install_dir; + data["GUI"]["installDir"] = std::string{fmt::UTF(settings_install_dir.u8string()).data}; data["GUI"]["geometry_x"] = main_window_geometry_x; data["GUI"]["geometry_y"] = main_window_geometry_y; data["GUI"]["geometry_w"] = main_window_geometry_w; diff --git a/src/common/config.h b/src/common/config.h index ef71d5f0..f2b5187f 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -72,7 +72,7 @@ bool vkCrashDiagnosticEnabled(); // Gui void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h); -void setGameInstallDir(const std::string& dir); +void setGameInstallDir(const std::filesystem::path& dir); void setMainWindowTheme(u32 theme); void setIconSize(u32 size); void setIconSizeGrid(u32 size); @@ -90,7 +90,7 @@ u32 getMainWindowGeometryX(); u32 getMainWindowGeometryY(); u32 getMainWindowGeometryW(); u32 getMainWindowGeometryH(); -std::string getGameInstallDir(); +std::filesystem::path getGameInstallDir(); u32 getMainWindowTheme(); u32 getIconSize(); u32 getIconSizeGrid(); diff --git a/src/common/logging/formatter.h b/src/common/logging/formatter.h index f80905cc..79df33e4 100644 --- a/src/common/logging/formatter.h +++ b/src/common/logging/formatter.h @@ -19,3 +19,24 @@ struct fmt::formatter, char>> } }; #endif + +namespace fmt { +template +struct UTF { + T data; + + explicit UTF(const std::u8string_view view) { + data = T{(const char*)&view.front(), (const char*)&view.back() + 1}; + } + + explicit UTF(const std::u8string& str) : UTF(std::u8string_view{str}) {} +}; +} // namespace fmt + +template <> +struct fmt::formatter, char> : formatter { + template + auto format(const UTF& wrapper, FormatContext& ctx) const { + return formatter::format(wrapper.data, ctx); + } +}; \ No newline at end of file diff --git a/src/common/memory_patcher.cpp b/src/common/memory_patcher.cpp index 85a25167..d2930cf5 100644 --- a/src/common/memory_patcher.cpp +++ b/src/common/memory_patcher.cpp @@ -119,9 +119,9 @@ std::string convertValueToHex(const std::string type, const std::string valueStr void OnGameLoaded() { if (!patchFile.empty()) { - std::string patchDir = Common::FS::GetUserPath(Common::FS::PathType::PatchesDir).string(); + std::filesystem::path patchDir = Common::FS::GetUserPath(Common::FS::PathType::PatchesDir); - std::string filePath = patchDir + "/" + patchFile; + auto filePath = (patchDir / patchFile).native(); pugi::xml_document doc; pugi::xml_parse_result result = doc.load_file(filePath.c_str()); @@ -187,8 +187,8 @@ void OnGameLoaded() { #ifdef ENABLE_QT_GUI // We use the QT headers for the xml and json parsing, this define is only true on QT builds - QString patchDir = - QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir).string()); + QString patchDir; + Common::FS::PathToQString(patchDir, Common::FS::GetUserPath(Common::FS::PathType::PatchesDir)); QString repositories[] = {"GoldHEN", "shadPS4"}; for (const QString& repository : repositories) { diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index cce12ebc..d7274fc7 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -22,6 +22,10 @@ #endif #endif +#ifdef ENABLE_QT_GUI +#include +#endif + namespace Common::FS { namespace fs = std::filesystem; @@ -165,4 +169,22 @@ void SetUserPath(PathType shad_path, const fs::path& new_path) { UserPaths.insert_or_assign(shad_path, new_path); } +#ifdef ENABLE_QT_GUI +void PathToQString(QString& result, const std::filesystem::path& path) { +#ifdef _WIN32 + result = QString::fromStdWString(path.wstring()); +#else + result = QString::fromStdString(path.string()); +#endif +} + +std::filesystem::path PathFromQString(const QString& path) { +#ifdef _WIN32 + return std::filesystem::path(path.toStdWString()); +#else + return std::filesystem::path(path.toStdString()); +#endif +} +#endif + } // namespace Common::FS \ No newline at end of file diff --git a/src/common/path_util.h b/src/common/path_util.h index 623b285e..d40f4aab 100644 --- a/src/common/path_util.h +++ b/src/common/path_util.h @@ -6,6 +6,10 @@ #include #include +#ifdef ENABLE_QT_GUI +class QString; // to avoid including in this header +#endif + namespace Common::FS { enum class PathType { @@ -96,4 +100,23 @@ constexpr auto LOG_FILE = "shad_log.txt"; */ void SetUserPath(PathType user_path, const std::filesystem::path& new_path); +#ifdef ENABLE_QT_GUI +/** + * Converts an std::filesystem::path to a QString. + * The native underlying string of a path is wstring on Windows and string on POSIX. + * + * @param result The resulting QString + * @param path The path to convert + */ +void PathToQString(QString& result, const std::filesystem::path& path); + +/** + * Converts a QString to an std::filesystem::path. + * The native underlying string of a path is wstring on Windows and string on POSIX. + * + * @param path The path to convert + */ +[[nodiscard]] std::filesystem::path PathFromQString(const QString& path); +#endif + } // namespace Common::FS diff --git a/src/core/file_format/pkg.cpp b/src/core/file_format/pkg.cpp index d86f3b28..f329e81a 100644 --- a/src/core/file_format/pkg.cpp +++ b/src/core/file_format/pkg.cpp @@ -371,8 +371,7 @@ bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem:: if (table.type == PFS_CURRENT_DIR) { current_dir = extractPaths[table.inode]; } - extractPaths[table.inode] = - current_dir.string() / std::filesystem::path(table.name); + extractPaths[table.inode] = current_dir / std::filesystem::path(table.name); if (table.type == PFS_FILE || table.type == PFS_DIR) { if (table.type == PFS_DIR) { // Create dirs. @@ -402,7 +401,7 @@ void PKG::ExtractFiles(const int index) { int bsize = iNodeBuf[inode_number].Size; Common::FS::IOFile inflated; - inflated.Open(extractPaths[inode_number].string(), Common::FS::FileAccessMode::Write); + inflated.Open(extractPaths[inode_number], Common::FS::FileAccessMode::Write); Common::FS::IOFile pkgFile; // Open the file for each iteration to avoid conflict. pkgFile.Open(pkgpath, Common::FS::FileAccessMode::Read); diff --git a/src/core/file_format/splash.cpp b/src/core/file_format/splash.cpp index f41789a8..5e06c912 100644 --- a/src/core/file_format/splash.cpp +++ b/src/core/file_format/splash.cpp @@ -12,8 +12,8 @@ #define STBI_NO_STDIO #include "externals/stb_image.h" -bool Splash::Open(const std::string& filepath) { - ASSERT_MSG(filepath.ends_with(".png"), "Unexpected file format passed"); +bool Splash::Open(const std::filesystem::path& filepath) { + ASSERT_MSG(filepath.stem().string() != "png", "Unexpected file format passed"); Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read); if (!file.IsOpen()) { diff --git a/src/core/file_format/splash.h b/src/core/file_format/splash.h index 68cc33c4..7c563f31 100644 --- a/src/core/file_format/splash.h +++ b/src/core/file_format/splash.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include "common/types.h" @@ -22,7 +23,7 @@ public: Splash() = default; ~Splash() = default; - bool Open(const std::string& filepath); + bool Open(const std::filesystem::path& filepath); [[nodiscard]] bool IsLoaded() const { return img_data.size(); } diff --git a/src/core/file_format/trp.cpp b/src/core/file_format/trp.cpp index 86865fe6..724f9078 100644 --- a/src/core/file_format/trp.cpp +++ b/src/core/file_format/trp.cpp @@ -29,7 +29,7 @@ static void removePadding(std::vector& vec) { } bool TRP::Extract(const std::filesystem::path& trophyPath) { - std::string title = trophyPath.filename().string(); + std::filesystem::path title = trophyPath.filename(); std::filesystem::path gameSysDir = trophyPath / "sce_sys/trophy/"; if (!std::filesystem::exists(gameSysDir)) { return false; diff --git a/src/core/libraries/np_trophy/np_trophy.cpp b/src/core/libraries/np_trophy/np_trophy.cpp index c28e49da..91fdeb99 100644 --- a/src/core/libraries/np_trophy/np_trophy.cpp +++ b/src/core/libraries/np_trophy/np_trophy.cpp @@ -557,10 +557,10 @@ s32 PS4_SYSV_ABI sceNpTrophyGetTrophyUnlockState(OrbisNpTrophyContext context, const auto trophyDir = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game_serial / "TrophyFiles"; + auto trophy_file = trophyDir / "trophy00" / "Xml" / "TROP.XML"; pugi::xml_document doc; - pugi::xml_parse_result result = - doc.load_file((trophyDir.string() + "/trophy00/Xml/TROP.XML").c_str()); + pugi::xml_parse_result result = doc.load_file(trophy_file.native().c_str()); int numTrophies = 0; diff --git a/src/core/libraries/save_data/save_backup.cpp b/src/core/libraries/save_data/save_backup.cpp index d8281fab..1d935aee 100644 --- a/src/core/libraries/save_data/save_backup.cpp +++ b/src/core/libraries/save_data/save_backup.cpp @@ -107,14 +107,16 @@ static void BackupThreadBody() { } g_backup_status = WorkerStatus::Running; - LOG_INFO(Lib_SaveData, "Backing up the following directory: {}", req.save_path.string()); + LOG_INFO(Lib_SaveData, "Backing up the following directory: {}", + fmt::UTF(req.save_path.u8string())); try { backup(req.save_path); } catch (const std::filesystem::filesystem_error& err) { - LOG_ERROR(Lib_SaveData, "Failed to backup {}: {}", req.save_path.string(), err.what()); + LOG_ERROR(Lib_SaveData, "Failed to backup {}: {}", fmt::UTF(req.save_path.u8string()), + err.what()); } LOG_DEBUG(Lib_SaveData, "Backing up the following directory: {} finished", - req.save_path.string()); + fmt::UTF(req.save_path.u8string())); { std::scoped_lock lk{g_backup_queue_mutex}; g_backup_queue.front().done = true; @@ -160,7 +162,7 @@ bool NewRequest(OrbisUserServiceUserId user_id, std::string_view title_id, if (g_backup_status != WorkerStatus::Waiting && g_backup_status != WorkerStatus::Running) { LOG_ERROR(Lib_SaveData, "Called backup while status is {}. Backup request to {} ignored", - magic_enum::enum_name(g_backup_status.load()), save_path.string()); + magic_enum::enum_name(g_backup_status.load()), fmt::UTF(save_path.u8string())); return false; } { @@ -184,7 +186,7 @@ bool NewRequest(OrbisUserServiceUserId user_id, std::string_view title_id, } bool Restore(const std::filesystem::path& save_path) { - LOG_INFO(Lib_SaveData, "Restoring backup for {}", save_path.string()); + LOG_INFO(Lib_SaveData, "Restoring backup for {}", fmt::UTF(save_path.u8string())); std::unique_lock lk{g_backup_running_mutex}; if (!fs::exists(save_path) || !fs::exists(save_path / backup_dir)) { return false; diff --git a/src/core/libraries/save_data/save_memory.cpp b/src/core/libraries/save_data/save_memory.cpp index d7ef5803..0a714a26 100644 --- a/src/core/libraries/save_data/save_memory.cpp +++ b/src/core/libraries/save_data/save_memory.cpp @@ -77,7 +77,7 @@ static void SaveFileSafe(void* buf, size_t count, const std::filesystem::path& p g_saving_memory = true; std::scoped_lock lk{g_saving_memory_mutex}; try { - LOG_DEBUG(Lib_SaveData, "Saving save data memory {}", g_save_path.string()); + LOG_DEBUG(Lib_SaveData, "Saving save data memory {}", fmt::UTF(g_save_path.u8string())); if (g_memory_dirty) { g_memory_dirty = false; @@ -163,7 +163,8 @@ size_t CreateSaveMemory(size_t memory_size) { bool ok = g_param_sfo.Open(g_param_sfo_path); if (!ok) { - LOG_ERROR(Lib_SaveData, "Failed to open SFO at {}", g_param_sfo_path.string()); + LOG_ERROR(Lib_SaveData, "Failed to open SFO at {}", + fmt::UTF(g_param_sfo_path.u8string())); throw std::filesystem::filesystem_error( "failed to open SFO", g_param_sfo_path, std::make_error_code(std::errc::illegal_byte_sequence)); diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp index 9599c1ff..da885d97 100644 --- a/src/core/libraries/save_data/savedata.cpp +++ b/src/core/libraries/save_data/savedata.cpp @@ -607,7 +607,7 @@ Error PS4_SYSV_ABI sceSaveDataCheckBackupData(const OrbisSaveDataCheckBackupData if (check->param != nullptr) { PSF sfo; if (!sfo.Open(backup_path / "sce_sys" / "param.sfo")) { - LOG_ERROR(Lib_SaveData, "Failed to read SFO at {}", backup_path.string()); + LOG_ERROR(Lib_SaveData, "Failed to read SFO at {}", fmt::UTF(backup_path.u8string())); return Error::INTERNAL; } check->param->FromSFO(sfo); @@ -818,7 +818,7 @@ Error PS4_SYSV_ABI sceSaveDataDirNameSearch(const OrbisSaveDataDirNameSearchCond const auto sfo_path = SaveInstance::GetParamSFOPath(dir_path); PSF sfo; if (!sfo.Open(sfo_path)) { - LOG_ERROR(Lib_SaveData, "Failed to read SFO: {}", sfo_path.string()); + LOG_ERROR(Lib_SaveData, "Failed to read SFO: {}", fmt::UTF(sfo_path.u8string())); ASSERT_MSG(false, "Failed to read SFO"); } map_dir_sfo.emplace(dir_name, std::move(sfo)); diff --git a/src/emulator.cpp b/src/emulator.cpp index 4a2e38ff..b27c7386 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -139,7 +139,7 @@ void Emulator::Run(const std::filesystem::path& file) { if (splash->IsLoaded()) { continue; } - if (!splash->Open(entry.path().string())) { + if (!splash->Open(entry.path())) { LOG_ERROR(Loader, "Game splash: unable to open file"); } } @@ -189,7 +189,7 @@ void Emulator::Run(const std::filesystem::path& file) { if (!std::filesystem::exists(mount_captures_dir)) { std::filesystem::create_directory(mount_captures_dir); } - VideoCore::SetOutputDir(mount_captures_dir.generic_string(), id); + VideoCore::SetOutputDir(mount_captures_dir, id); // Initialize kernel and library facilities. Libraries::Kernel::init_pthreads(); @@ -205,7 +205,7 @@ void Emulator::Run(const std::filesystem::path& file) { std::filesystem::path sce_module_folder = file.parent_path() / "sce_module"; if (std::filesystem::is_directory(sce_module_folder)) { for (const auto& entry : std::filesystem::directory_iterator(sce_module_folder)) { - LOG_INFO(Loader, "Loading {}", entry.path().string().c_str()); + LOG_INFO(Loader, "Loading {}", fmt::UTF(entry.path().u8string())); linker->LoadModule(entry.path()); } } diff --git a/src/imgui/renderer/imgui_core.cpp b/src/imgui/renderer/imgui_core.cpp index a3f4c507..8c2b31c9 100644 --- a/src/imgui/renderer/imgui_core.cpp +++ b/src/imgui/renderer/imgui_core.cpp @@ -51,8 +51,16 @@ void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& w io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; io.DisplaySize = ImVec2((float)window.getWidth(), (float)window.getHeight()); - io.IniFilename = SDL_strdup(config_path.string().c_str()); - io.LogFilename = SDL_strdup(log_path.string().c_str()); + + auto path = config_path.u8string(); + char* config_file_buf = new char[path.size() + 1](); + std::memcpy(config_file_buf, path.c_str(), path.size()); + io.IniFilename = config_file_buf; + + path = log_path.u8string(); + char* log_file_buf = new char[path.size() + 1](); + std::memcpy(log_file_buf, path.c_str(), path.size()); + io.LogFilename = log_file_buf; ImFontGlyphRangesBuilder rb{}; rb.AddRanges(io.Fonts->GetGlyphRangesDefault()); @@ -114,8 +122,8 @@ void Shutdown(const vk::Device& device) { Sdl::Shutdown(); DestroyContext(); - SDL_free(ini_filename); - SDL_free(log_filename); + delete[] (char*)ini_filename; + delete[] (char*)log_filename; } bool ProcessEvent(SDL_Event* event) { diff --git a/src/main.cpp b/src/main.cpp index cea92be0..de1d9232 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,7 +5,15 @@ #include "common/memory_patcher.h" #include "emulator.h" +#ifdef _WIN32 +#include +#endif + int main(int argc, char* argv[]) { +#ifdef _WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif + if (argc == 1) { fmt::print("Usage: {} \n", argv[0]); return -1; diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp index aaa6b4f0..655478d2 100644 --- a/src/qt_gui/cheats_patches.cpp +++ b/src/qt_gui/cheats_patches.cpp @@ -50,8 +50,9 @@ void CheatsPatches::setupUI() { defaultTextEdit = tr("defaultTextEdit_MSG"); defaultTextEdit.replace("\\n", "\n"); - QString CHEATS_DIR_QString = - QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::CheatsDir).string()); + QString CHEATS_DIR_QString; + Common::FS::PathToQString(CHEATS_DIR_QString, + Common::FS::GetUserPath(Common::FS::PathType::CheatsDir)); QString NameCheatJson = m_gameSerial + "_" + m_gameVersion + ".json"; m_cheatFilePath = CHEATS_DIR_QString + "/" + NameCheatJson; @@ -275,9 +276,9 @@ void CheatsPatches::onSaveButtonClicked() { int separatorIndex = selectedPatchName.indexOf(" | "); selectedPatchName = selectedPatchName.mid(separatorIndex + 3); - QString patchDir = - QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir).string()) + - "/" + selectedPatchName; + QString patchDir; + Common::FS::PathToQString(patchDir, Common::FS::GetUserPath(Common::FS::PathType::PatchesDir)); + patchDir += "/" + selectedPatchName; QString filesJsonPath = patchDir + "/files.json"; QFile jsonFile(filesJsonPath); @@ -555,10 +556,10 @@ void CheatsPatches::downloadCheats(const QString& source, const QString& gameSer if (dotIndex != -1) { baseFileName.insert(dotIndex, "_wolf2022"); } - QString filePath = - QString::fromStdString( - Common::FS::GetUserPath(Common::FS::PathType::CheatsDir).string()) + - "/" + baseFileName; + QString filePath; + Common::FS::PathToQString(filePath, + Common::FS::GetUserPath(Common::FS::PathType::CheatsDir)); + filePath += "/" + baseFileName; if (QFile::exists(filePath) && showMessageBox) { QMessageBox::StandardButton reply2; reply2 = @@ -612,8 +613,9 @@ void CheatsPatches::populateFileListPatches() { } m_patchInfos.clear(); - QString patchesDir = - QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir).string()); + QString patchesDir; + Common::FS::PathToQString(patchesDir, + Common::FS::GetUserPath(Common::FS::PathType::PatchesDir)); QDir dir(patchesDir); QStringList folders = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); @@ -906,8 +908,8 @@ void CheatsPatches::addCheatsToLayout(const QJsonArray& modsArray, const QJsonAr } void CheatsPatches::populateFileListCheats() { - QString cheatsDir = - QString::fromStdString(Common::FS::GetUserPath(Common::FS::PathType::CheatsDir).string()); + QString cheatsDir; + Common::FS::PathToQString(cheatsDir, Common::FS::GetUserPath(Common::FS::PathType::CheatsDir)); QString pattern = m_gameSerial + "_" + m_gameVersion + "*.json"; QDir dir(cheatsDir); @@ -932,8 +934,9 @@ void CheatsPatches::populateFileListCheats() { if (!selectedIndexes.isEmpty()) { QString selectedFileName = selectedIndexes.first().data().toString(); - QString cheatsDir = QString::fromStdString( - Common::FS::GetUserPath(Common::FS::PathType::CheatsDir).string()); + QString cheatsDir; + Common::FS::PathToQString( + cheatsDir, Common::FS::GetUserPath(Common::FS::PathType::CheatsDir)); QFile file(cheatsDir + "/" + selectedFileName); if (file.open(QIODevice::ReadOnly)) { diff --git a/src/qt_gui/game_grid_frame.cpp b/src/qt_gui/game_grid_frame.cpp index 96ec0e93..3113aecc 100644 --- a/src/qt_gui/game_grid_frame.cpp +++ b/src/qt_gui/game_grid_frame.cpp @@ -44,7 +44,8 @@ void GameGridFrame::PlayBackgroundMusic(QTableWidgetItem* item) { BackgroundMusicPlayer::getInstance().stopMusic(); return; } - const auto snd0path = QString::fromStdString(m_game_info->m_games[item->row()].snd0_path); + QString snd0path; + Common::FS::PathToQString(snd0path, m_game_info->m_games[item->row()].snd0_path); BackgroundMusicPlayer::getInstance().playMusic(snd0path); } @@ -122,14 +123,12 @@ void GameGridFrame::SetGridBackgroundImage(int row, int column) { int itemID = (row * this->columnCount()) + column; QWidget* item = this->cellWidget(row, column); if (item) { - QString pic1Path = QString::fromStdString((*m_games_shared)[itemID].pic_path); + QString pic1Path; + Common::FS::PathToQString(pic1Path, (*m_games_shared)[itemID].pic_path); const auto blurredPic1Path = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / (*m_games_shared)[itemID].serial / "pic1.png"; -#ifdef _WIN32 - const auto blurredPic1PathQt = QString::fromStdWString(blurredPic1Path.wstring()); -#else - const auto blurredPic1PathQt = QString::fromStdString(blurredPic1Path.string()); -#endif + QString blurredPic1PathQt; + Common::FS::PathToQString(blurredPic1PathQt, blurredPic1Path); backgroundImage = QImage(blurredPic1PathQt); if (backgroundImage.isNull()) { diff --git a/src/qt_gui/game_info.cpp b/src/qt_gui/game_info.cpp index 2821a032..6e8d8971 100644 --- a/src/qt_gui/game_info.cpp +++ b/src/qt_gui/game_info.cpp @@ -3,13 +3,15 @@ #include +#include "common/path_util.h" #include "game_info.h" GameInfoClass::GameInfoClass() = default; GameInfoClass::~GameInfoClass() = default; void GameInfoClass::GetGameInfo(QWidget* parent) { - QString installDir = QString::fromStdString(Config::getGameInstallDir()); + QString installDir; + Common::FS::PathToQString(installDir, Config::getGameInstallDir()); QStringList filePaths; QDir parentFolder(installDir); QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); @@ -19,7 +21,7 @@ void GameInfoClass::GetGameInfo(QWidget* parent) { } } m_games = QtConcurrent::mapped(filePaths, [&](const QString& path) { - return readGameInfo(path.toStdString()); + return readGameInfo(Common::FS::PathFromQString(path)); }).results(); // Progress bar, please be patient :) diff --git a/src/qt_gui/game_info.h b/src/qt_gui/game_info.h index 5ef64f07..9db25482 100644 --- a/src/qt_gui/game_info.h +++ b/src/qt_gui/game_info.h @@ -22,17 +22,19 @@ public: return a.name < b.name; } - static GameInfo readGameInfo(const std::string& filePath) { + static GameInfo readGameInfo(const std::filesystem::path& filePath) { GameInfo game; game.path = filePath; PSF psf; - if (psf.Open(std::filesystem::path(game.path) / "sce_sys" / "param.sfo")) { - game.icon_path = game.path + "/sce_sys/icon0.png"; - QString iconpath = QString::fromStdString(game.icon_path); + if (psf.Open(game.path / "sce_sys" / "param.sfo")) { + game.icon_path = game.path / "sce_sys" / "icon0.png"; + QString iconpath; + Common::FS::PathToQString(iconpath, game.icon_path); game.icon = QImage(iconpath); - game.pic_path = game.path + "/sce_sys/pic1.png"; - game.snd0_path = game.path + "/sce_sys/snd0.at9"; + game.pic_path = game.path / "sce_sys" / "pic1.png"; + game.snd0_path = game.path / "sce_sys" / "snd0.at9"; + if (const auto title = psf.GetString("TITLE"); title.has_value()) { game.name = *title; } diff --git a/src/qt_gui/game_install_dialog.cpp b/src/qt_gui/game_install_dialog.cpp index 5f031bde..d8cc7a83 100644 --- a/src/qt_gui/game_install_dialog.cpp +++ b/src/qt_gui/game_install_dialog.cpp @@ -41,7 +41,9 @@ QWidget* GameInstallDialog::SetupGamesDirectory() { // Input. m_gamesDirectory = new QLineEdit(); - m_gamesDirectory->setText(QString::fromStdString(Config::getGameInstallDir())); + QString install_dir; + Common::FS::PathToQString(install_dir, Config::getGameInstallDir()); + m_gamesDirectory->setText(install_dir); m_gamesDirectory->setMinimumWidth(400); layout->addWidget(m_gamesDirectory); @@ -76,7 +78,7 @@ void GameInstallDialog::Save() { return; } - Config::setGameInstallDir(gamesDirectory.toStdString()); + Config::setGameInstallDir(Common::FS::PathFromQString(gamesDirectory)); const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); Config::save(config_dir / "config.toml"); accept(); diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index bb3fd2e6..45e52f37 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/path_util.h" +#include "common/string_util.h" #include "game_list_frame.h" GameListFrame::GameListFrame(std::shared_ptr game_info_get, QWidget* parent) @@ -73,7 +74,8 @@ void GameListFrame::PlayBackgroundMusic(QTableWidgetItem* item) { BackgroundMusicPlayer::getInstance().stopMusic(); return; } - const auto snd0path = QString::fromStdString(m_game_info->m_games[item->row()].snd0_path); + QString snd0path; + Common::FS::PathToQString(snd0path, m_game_info->m_games[item->row()].snd0_path); BackgroundMusicPlayer::getInstance().playMusic(snd0path); } @@ -88,7 +90,9 @@ void GameListFrame::PopulateGameList() { SetTableItem(i, 4, QString::fromStdString(m_game_info->m_games[i].fw)); SetTableItem(i, 5, QString::fromStdString(m_game_info->m_games[i].size)); SetTableItem(i, 6, QString::fromStdString(m_game_info->m_games[i].version)); - SetTableItem(i, 7, QString::fromStdString(m_game_info->m_games[i].path)); + QString path; + Common::FS::PathToQString(path, m_game_info->m_games[i].path); + SetTableItem(i, 7, path); } } @@ -98,14 +102,12 @@ void GameListFrame::SetListBackgroundImage(QTableWidgetItem* item) { return; } - QString pic1Path = QString::fromStdString(m_game_info->m_games[item->row()].pic_path); + QString pic1Path; + Common::FS::PathToQString(pic1Path, m_game_info->m_games[item->row()].pic_path); const auto blurredPic1Path = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / m_game_info->m_games[item->row()].serial / "pic1.png"; -#ifdef _WIN32 - const auto blurredPic1PathQt = QString::fromStdWString(blurredPic1Path.wstring()); -#else - const auto blurredPic1PathQt = QString::fromStdString(blurredPic1Path.string()); -#endif + QString blurredPic1PathQt; + Common::FS::PathToQString(blurredPic1PathQt, blurredPic1Path); backgroundImage = QImage(blurredPic1PathQt); if (backgroundImage.isNull()) { diff --git a/src/qt_gui/game_list_utils.h b/src/qt_gui/game_list_utils.h index 93757d07..f62275ef 100644 --- a/src/qt_gui/game_list_utils.h +++ b/src/qt_gui/game_list_utils.h @@ -3,11 +3,14 @@ #pragma once +#include "common/path_util.h" + struct GameInfo { - std::string path; // root path of game directory (normally directory that contains eboot.bin) - std::string icon_path; // path of icon0.png - std::string pic_path; // path of pic1.png - std::string snd0_path; // path of snd0.at9 + std::filesystem::path path; // root path of game directory + // (normally directory that contains eboot.bin) + std::filesystem::path icon_path; // path of icon0.png + std::filesystem::path pic_path; // path of pic1.png + std::filesystem::path snd0_path; // path of snd0.at9 QImage icon; std::string size; // variables extracted from param.sfo @@ -44,7 +47,9 @@ public: } static void GetFolderSize(GameInfo& game) { - QDir dir(QString::fromStdString(game.path)); + QString dirPath; + Common::FS::PathToQString(dirPath, game.path); + QDir dir(dirPath); QDirIterator it(dir.absolutePath(), QDirIterator::Subdirectories); qint64 total = 0; while (it.hasNext()) { diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 9300ca6e..3218884d 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -75,7 +75,8 @@ public: } if (selected == &openFolder) { - QString folderPath = QString::fromStdString(m_games[itemID].path); + QString folderPath; + Common::FS::PathToQString(folderPath, m_games[itemID].path); QDesktopServices::openUrl(QUrl::fromLocalFile(folderPath)); } @@ -158,7 +159,9 @@ public: QString gameSerial = QString::fromStdString(m_games[itemID].serial); QString gameVersion = QString::fromStdString(m_games[itemID].version); QString gameSize = QString::fromStdString(m_games[itemID].size); - QPixmap gameImage(QString::fromStdString(m_games[itemID].icon_path)); + QString iconPath; + Common::FS::PathToQString(iconPath, m_games[itemID].icon_path); + QPixmap gameImage(iconPath); CheatsPatches* cheatsPatches = new CheatsPatches(gameName, gameSerial, gameVersion, gameSize, gameImage); cheatsPatches->show(); @@ -167,8 +170,9 @@ public: } if (selected == &openTrophyViewer) { - QString trophyPath = QString::fromStdString(m_games[itemID].serial); - QString gameTrpPath = QString::fromStdString(m_games[itemID].path); + QString trophyPath, gameTrpPath; + Common::FS::PathToQString(trophyPath, m_games[itemID].serial); + Common::FS::PathToQString(gameTrpPath, m_games[itemID].path); TrophyViewer* trophyViewer = new TrophyViewer(trophyPath, gameTrpPath); trophyViewer->show(); connect(widget->parent(), &QWidget::destroyed, trophyViewer, @@ -176,11 +180,13 @@ public: } if (selected == &createShortcut) { - QString targetPath = QString::fromStdString(m_games[itemID].path); + QString targetPath; + Common::FS::PathToQString(targetPath, m_games[itemID].path); QString ebootPath = targetPath + "/eboot.bin"; // Get the full path to the icon - QString iconPath = QString::fromStdString(m_games[itemID].icon_path); + QString iconPath; + Common::FS::PathToQString(iconPath, m_games[itemID].icon_path); QFileInfo iconFileInfo(iconPath); QString icoPath = iconFileInfo.absolutePath() + "/" + iconFileInfo.baseName() + ".ico"; diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index 68a674f5..8c565a19 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -8,10 +8,18 @@ #include "game_install_dialog.h" #include "main_window.h" +#ifdef _WIN32 +#include +#endif + // Custom message handler to ignore Qt logs void customMessageHandler(QtMsgType, const QMessageLogContext&, const QString&) {} int main(int argc, char* argv[]) { +#ifdef _WIN32 + SetConsoleOutputCP(CP_UTF8); +#endif + QApplication a(argc, argv); // Load configurations and initialize Qt application @@ -22,7 +30,7 @@ int main(int argc, char* argv[]) { bool has_command_line_argument = argc > 1; // Check if the game install directory is set - if (Config::getGameInstallDir() == "" && !has_command_line_argument) { + if (Config::getGameInstallDir().empty() && !has_command_line_argument) { GameInstallDialog dlg; dlg.exec(); } diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 6a1e0b42..759c6992 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -4,10 +4,12 @@ #include #include +#include #include "about_dialog.h" #include "cheats_patches.h" #include "check_update.h" #include "common/io_file.h" +#include "common/path_util.h" #include "common/string_util.h" #include "common/version.h" #include "core/file_format/pkg.h" @@ -43,7 +45,14 @@ bool MainWindow::Init() { GetPhysicalDevices(); // show ui setMinimumSize(350, minimumSizeHint().height()); - setWindowTitle(QString::fromStdString("shadPS4 v" + std::string(Common::VERSION))); + std::string window_title = ""; + if (Common::isRelease) { + window_title = fmt::format("shadPS4 v{}", Common::VERSION); + } else { + window_title = fmt::format("shadPS4 v{} {} {}", Common::VERSION, Common::g_scm_branch, + Common::g_scm_desc); + } + setWindowTitle(QString::fromStdString(window_title)); this->show(); // load game list LoadGameLists(); @@ -433,12 +442,14 @@ void MainWindow::CreateConnects() { .arg(" APP VERSION", -11) .arg(" Path"); for (const GameInfo& game : m_game_info->m_games) { + QString game_path; + Common::FS::PathToQString(game_path, game.path); out << QString("%1 %2 %3 %4 %5\n") .arg(QString::fromStdString(game.name), -50) .arg(QString::fromStdString(game.serial), -10) .arg(QString::fromStdString(game.fw), -4) .arg(QString::fromStdString(game.version), -11) - .arg(QString::fromStdString(game.path)); + .arg(game_path); } }); @@ -517,7 +528,8 @@ void MainWindow::PlayBackgroundMusic() { : m_game_grid_frame->crtRow * m_game_grid_frame->columnCnt + m_game_grid_frame->crtColumn; - const auto snd0path = QString::fromStdString(m_game_info->m_games[itemID].snd0_path); + QString snd0path; + Common::FS::PathToQString(snd0path, m_game_info->m_games[itemID].snd0_path); BackgroundMusicPlayer::getInstance().playMusic(snd0path); } @@ -529,28 +541,29 @@ void MainWindow::StartGame() { if (table_mode == 0) { if (m_game_list_frame->currentItem()) { int itemID = m_game_list_frame->currentItem()->row(); - gamePath = QString::fromStdString(m_game_info->m_games[itemID].path + "/eboot.bin"); + Common::FS::PathToQString(gamePath, m_game_info->m_games[itemID].path / "eboot.bin"); } } else if (table_mode == 1) { if (m_game_grid_frame->cellClicked) { int itemID = (m_game_grid_frame->crtRow * m_game_grid_frame->columnCnt) + m_game_grid_frame->crtColumn; - gamePath = QString::fromStdString(m_game_info->m_games[itemID].path + "/eboot.bin"); + Common::FS::PathToQString(gamePath, m_game_info->m_games[itemID].path / "eboot.bin"); } } else { if (m_elf_viewer->currentItem()) { int itemID = m_elf_viewer->currentItem()->row(); - gamePath = QString::fromStdString(m_elf_viewer->m_elf_list[itemID].toStdString()); + gamePath = m_elf_viewer->m_elf_list[itemID]; } } if (gamePath != "") { AddRecentFiles(gamePath); Core::Emulator emulator; - if (!std::filesystem::exists(gamePath.toUtf8().constData())) { + const auto path = Common::FS::PathFromQString(gamePath); + if (!std::filesystem::exists(path)) { QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Eboot.bin file not found"))); return; } - emulator.Run(gamePath.toUtf8().constData()); + emulator.Run(path); } } @@ -666,9 +679,11 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int pkg = PKG(); pkg.Open(file); std::string failreason; - auto extract_path = std::filesystem::path(Config::getGameInstallDir()) / pkg.GetTitleID(); + auto extract_path = Config::getGameInstallDir() / pkg.GetTitleID(); QString pkgType = QString::fromStdString(pkg.GetPkgFlags()); - QDir game_dir(QString::fromStdString(extract_path.string())); + QString gameDirPath; + Common::FS::PathToQString(gameDirPath, extract_path); + QDir game_dir(gameDirPath); if (game_dir.exists()) { QMessageBox msgBox; msgBox.setWindowTitle(tr("PKG Extraction")); @@ -690,7 +705,9 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int auto addon_extract_path = Common::FS::GetUserPath(Common::FS::PathType::AddonsDir) / pkg.GetTitleID() / entitlement_label; - QDir addon_dir(QString::fromStdString(addon_extract_path.string())); + QString addonDirPath; + Common::FS::PathToQString(addonDirPath, addon_extract_path); + QDir addon_dir(addonDirPath); auto category = psf.GetString("CATEGORY"); if (pkgType.contains("PATCH")) { @@ -755,8 +772,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int return; } } else { - msgBox.setText(QString(tr("DLC already installed:") + "\n" + - QString::fromStdString(addon_extract_path.string()) + + msgBox.setText(QString(tr("DLC already installed:") + "\n" + addonDirPath + "\n\n" + tr("Would you like to overwrite?"))); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); @@ -768,8 +784,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int } } } else { - msgBox.setText(QString(tr("Game already installed") + "\n" + - QString::fromStdString(extract_path.string()) + "\n" + + msgBox.setText(QString(tr("Game already installed") + "\n" + addonDirPath + "\n" + tr("Would you like to overwrite?"))); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); @@ -812,7 +827,8 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int QFutureWatcher futureWatcher; connect(&futureWatcher, &QFutureWatcher::finished, this, [=, this]() { if (pkgNum == nPkg) { - QString path = QString::fromStdString(Config::getGameInstallDir()); + QString path; + Common::FS::PathToQString(path, Config::getGameInstallDir()); QMessageBox extractMsgBox(this); extractMsgBox.setWindowTitle(tr("Extraction Finished")); extractMsgBox.setText( @@ -984,14 +1000,14 @@ void MainWindow::CreateRecentGameActions() { } connect(m_recent_files_group, &QActionGroup::triggered, this, [this](QAction* action) { - QString gamePath = action->text(); - AddRecentFiles(gamePath); // Update the list. + auto gamePath = Common::FS::PathFromQString(action->text()); + AddRecentFiles(action->text()); // Update the list. Core::Emulator emulator; - if (!std::filesystem::exists(gamePath.toUtf8().constData())) { + if (!std::filesystem::exists(gamePath)) { QMessageBox::critical(nullptr, tr("Run Game"), QString(tr("Eboot.bin file not found"))); return; } - emulator.Run(gamePath.toUtf8().constData()); + emulator.Run(gamePath); }); } @@ -1025,4 +1041,4 @@ void MainWindow::OnLanguageChanged(const std::string& locale) { Config::setEmulatorLanguage(locale); LoadTranslation(); -} \ No newline at end of file +} diff --git a/src/qt_gui/trophy_viewer.cpp b/src/qt_gui/trophy_viewer.cpp index 8b96948c..528beee0 100644 --- a/src/qt_gui/trophy_viewer.cpp +++ b/src/qt_gui/trophy_viewer.cpp @@ -21,15 +21,10 @@ TrophyViewer::TrophyViewer(QString trophyPath, QString gameTrpPath) : QMainWindo } void TrophyViewer::PopulateTrophyWidget(QString title) { -#ifdef _WIN32 const auto trophyDir = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / - title.toStdWString() / "TrophyFiles"; - const auto trophyDirQt = QString::fromStdWString(trophyDir.wstring()); -#else - const auto trophyDir = Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / - title.toStdString() / "TrophyFiles"; - const auto trophyDirQt = QString::fromStdString(trophyDir.string()); -#endif + Common::FS::PathFromQString(title) / "TrophyFiles"; + QString trophyDirQt; + Common::FS::PathToQString(trophyDirQt, trophyDir); QDir dir(trophyDirQt); if (!dir.exists()) { diff --git a/src/video_core/renderdoc.cpp b/src/video_core/renderdoc.cpp index 7f88e126..7e099499 100644 --- a/src/video_core/renderdoc.cpp +++ b/src/video_core/renderdoc.cpp @@ -110,11 +110,11 @@ void TriggerCapture() { } } -void SetOutputDir(const std::string& path, const std::string& prefix) { +void SetOutputDir(const std::filesystem::path& path, const std::string& prefix) { if (!rdoc_api) { return; } - rdoc_api->SetCaptureFilePathTemplate((path + '\\' + prefix).c_str()); + rdoc_api->SetCaptureFilePathTemplate(fmt::UTF((path / prefix).u8string()).data.data()); } } // namespace VideoCore diff --git a/src/video_core/renderdoc.h b/src/video_core/renderdoc.h index febf6fbc..91e242d0 100644 --- a/src/video_core/renderdoc.h +++ b/src/video_core/renderdoc.h @@ -20,6 +20,6 @@ void EndCapture(); void TriggerCapture(); /// Sets output directory for captures -void SetOutputDir(const std::string& path, const std::string& prefix); +void SetOutputDir(const std::filesystem::path& path, const std::string& prefix); } // namespace VideoCore