diff options
Diffstat (limited to 'src/yuzu')
32 files changed, 667 insertions, 609 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index cc0790e07..634fe66a5 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -4,6 +4,12 @@ set(CMAKE_AUTOUIC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) +# Set the RPATH for Qt Libraries +# This must be done before the `yuzu` target is created +if (YUZU_USE_BUNDLED_QT AND (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")) + set(CMAKE_BUILD_RPATH "${CMAKE_BINARY_DIR}/bin/lib/") +endif() + add_executable(yuzu Info.plist about_dialog.cpp @@ -278,11 +284,14 @@ if(UNIX AND NOT APPLE) install(TARGETS yuzu RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") endif() -if (MSVC) +if (YUZU_USE_BUNDLED_QT) include(CopyYuzuQt5Deps) + copy_yuzu_Qt5_deps(yuzu) +endif() + +if (MSVC) include(CopyYuzuSDLDeps) include(CopyYuzuFFmpegDeps) - copy_yuzu_Qt5_deps(yuzu) copy_yuzu_SDL_deps(yuzu) copy_yuzu_FFmpeg_deps(yuzu) endif() diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp index 0a4c48b3d..62fd1141c 100644 --- a/src/yuzu/applets/profile_select.cpp +++ b/src/yuzu/applets/profile_select.cpp @@ -10,7 +10,7 @@ #include <QScrollArea> #include <QStandardItemModel> #include <QVBoxLayout> -#include "common/file_util.h" +#include "common/fs/path_util.h" #include "common/string_util.h" #include "core/constants.h" #include "core/hle/lock.h" @@ -26,9 +26,10 @@ QString FormatUserEntryText(const QString& username, Common::UUID uuid) { } QString GetImagePath(Common::UUID uuid) { - const auto path = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + - "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; - return QString::fromStdString(path); + const auto path = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / + fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormatSwitch()); + return QString::fromStdString(Common::FS::PathToUTF8String(path)); } QPixmap GetIcon(Common::UUID uuid) { diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp index 653486493..aa453a79f 100644 --- a/src/yuzu/applets/software_keyboard.cpp +++ b/src/yuzu/applets/software_keyboard.cpp @@ -404,12 +404,16 @@ void QtSoftwareKeyboardDialog::ShowTextCheckDialog( OverlayDialog dialog(this, system, QString{}, QString::fromStdU16String(text_check_message), tr("Cancel"), tr("OK"), Qt::AlignCenter); - if (dialog.exec() == QDialog::Accepted) { - emit SubmitNormalText(SwkbdResult::Ok, current_text); + if (dialog.exec() != QDialog::Accepted) { + StartInputThread(); break; } - StartInputThread(); + auto text = ui->topOSK->currentIndex() == 1 + ? ui->text_edit_osk->toPlainText().toStdU16String() + : ui->line_edit_osk->text().toStdU16String(); + + emit SubmitNormalText(SwkbdResult::Ok, std::move(text)); break; } } @@ -480,11 +484,7 @@ void QtSoftwareKeyboardDialog::open() { void QtSoftwareKeyboardDialog::reject() { // Pressing the ESC key in a dialog calls QDialog::reject(). // We will override this behavior to the "Cancel" action on the software keyboard. - if (is_inline) { - emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position); - } else { - emit SubmitNormalText(SwkbdResult::Cancel, current_text); - } + TranslateButtonPress(HIDButton::X); } void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) { @@ -720,21 +720,9 @@ void QtSoftwareKeyboardDialog::SetTextDrawType() { ui->line_edit_osk->setFocus(); }); - connect(ui->line_edit_osk, &QLineEdit::returnPressed, [this] { - switch (bottom_osk_index) { - case BottomOSKIndex::LowerCase: - ui->button_ok->click(); - break; - case BottomOSKIndex::UpperCase: - ui->button_ok_shift->click(); - break; - case BottomOSKIndex::NumberPad: - ui->button_ok_num->click(); - break; - default: - break; - } - }); + connect( + ui->line_edit_osk, &QLineEdit::returnPressed, this, + [this] { TranslateButtonPress(HIDButton::Plus); }, Qt::QueuedConnection); ui->line_edit_osk->setPlaceholderText( QString::fromStdU16String(initialize_parameters.guide_text)); @@ -1113,12 +1101,11 @@ void QtSoftwareKeyboardDialog::NormalKeyboardButtonClicked(QPushButton* button) } if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) { - if (ui->topOSK->currentIndex() == 1) { - emit SubmitNormalText(SwkbdResult::Ok, - ui->text_edit_osk->toPlainText().toStdU16String()); - } else { - emit SubmitNormalText(SwkbdResult::Ok, ui->line_edit_osk->text().toStdU16String()); - } + auto text = ui->topOSK->currentIndex() == 1 + ? ui->text_edit_osk->toPlainText().toStdU16String() + : ui->line_edit_osk->text().toStdU16String(); + + emit SubmitNormalText(SwkbdResult::Ok, std::move(text)); return; } @@ -1277,13 +1264,11 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) { if (is_inline) { emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position); } else { - if (ui->topOSK->currentIndex() == 1) { - emit SubmitNormalText(SwkbdResult::Cancel, - ui->text_edit_osk->toPlainText().toStdU16String()); - } else { - emit SubmitNormalText(SwkbdResult::Cancel, - ui->line_edit_osk->text().toStdU16String()); - } + auto text = ui->topOSK->currentIndex() == 1 + ? ui->text_edit_osk->toPlainText().toStdU16String() + : ui->line_edit_osk->text().toStdU16String(); + + emit SubmitNormalText(SwkbdResult::Cancel, std::move(text)); } break; case HIDButton::Y: @@ -1575,7 +1560,7 @@ void QtSoftwareKeyboard::ShowNormalKeyboard() const { void QtSoftwareKeyboard::ShowTextCheckDialog( Service::AM::Applets::SwkbdTextCheckResult text_check_result, std::u16string text_check_message) const { - emit MainWindowShowTextCheckDialog(text_check_result, text_check_message); + emit MainWindowShowTextCheckDialog(text_check_result, std::move(text_check_message)); } void QtSoftwareKeyboard::ShowInlineKeyboard( diff --git a/src/yuzu/applets/web_browser.cpp b/src/yuzu/applets/web_browser.cpp index 93e3a4f6f..34d3feb55 100644 --- a/src/yuzu/applets/web_browser.cpp +++ b/src/yuzu/applets/web_browser.cpp @@ -12,7 +12,7 @@ #include <QWebEngineUrlScheme> #endif -#include "common/file_util.h" +#include "common/fs/path_util.h" #include "core/core.h" #include "core/frontend/input_interpreter.h" #include "input_common/keyboard.h" @@ -322,21 +322,25 @@ void QtNXWebEngineView::LoadExtractedFonts() { QWebEngineScript nx_font_css; QWebEngineScript load_nx_font; - const QString fonts_dir = QString::fromStdString(Common::FS::SanitizePath( - fmt::format("{}/fonts", Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)))); + auto fonts_dir_str = Common::FS::PathToUTF8String( + Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "fonts/"); + + std::replace(fonts_dir_str.begin(), fonts_dir_str.end(), '\\', '/'); + + const auto fonts_dir = QString::fromStdString(fonts_dir_str); nx_font_css.setName(QStringLiteral("nx_font_css.js")); load_nx_font.setName(QStringLiteral("load_nx_font.js")); nx_font_css.setSourceCode( QString::fromStdString(NX_FONT_CSS) - .arg(fonts_dir + QStringLiteral("/FontStandard.ttf")) - .arg(fonts_dir + QStringLiteral("/FontChineseSimplified.ttf")) - .arg(fonts_dir + QStringLiteral("/FontExtendedChineseSimplified.ttf")) - .arg(fonts_dir + QStringLiteral("/FontChineseTraditional.ttf")) - .arg(fonts_dir + QStringLiteral("/FontKorean.ttf")) - .arg(fonts_dir + QStringLiteral("/FontNintendoExtended.ttf")) - .arg(fonts_dir + QStringLiteral("/FontNintendoExtended2.ttf"))); + .arg(fonts_dir + QStringLiteral("FontStandard.ttf")) + .arg(fonts_dir + QStringLiteral("FontChineseSimplified.ttf")) + .arg(fonts_dir + QStringLiteral("FontExtendedChineseSimplified.ttf")) + .arg(fonts_dir + QStringLiteral("FontChineseTraditional.ttf")) + .arg(fonts_dir + QStringLiteral("FontKorean.ttf")) + .arg(fonts_dir + QStringLiteral("FontNintendoExtended.ttf")) + .arg(fonts_dir + QStringLiteral("FontNintendoExtended2.ttf"))); load_nx_font.setSourceCode(QString::fromStdString(LOAD_NX_FONT)); nx_font_css.setInjectionPoint(QWebEngineScript::DocumentReady); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index e80a3df77..eb58bfa5b 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -5,8 +5,8 @@ #include <array> #include <QKeySequence> #include <QSettings> -#include "common/common_paths.h" -#include "common/file_util.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" #include "core/core.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/hid/controllers/npad.h" @@ -243,27 +243,27 @@ const std::array<UISettings::Shortcut, 17> Config::default_hotkeys{{ // clang-format on void Config::Initialize(const std::string& config_name) { + const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir); + const auto config_file = fmt::format("{}.ini", config_name); + switch (type) { case ConfigType::GlobalConfig: - qt_config_loc = fmt::format("{}" DIR_SEP "{}.ini", FS::GetUserPath(FS::UserPath::ConfigDir), - config_name); - FS::CreateFullPath(qt_config_loc); + qt_config_loc = FS::PathToUTF8String(fs_config_loc / config_file); + void(FS::CreateParentDir(qt_config_loc)); qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); Reload(); break; case ConfigType::PerGameConfig: - qt_config_loc = fmt::format("{}custom" DIR_SEP "{}.ini", - FS::GetUserPath(FS::UserPath::ConfigDir), config_name); - FS::CreateFullPath(qt_config_loc); + qt_config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / config_file); + void(FS::CreateParentDir(qt_config_loc)); qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); Reload(); break; case ConfigType::InputProfile: - qt_config_loc = fmt::format("{}input" DIR_SEP "{}.ini", - FS::GetUserPath(FS::UserPath::ConfigDir), config_name); - FS::CreateFullPath(qt_config_loc); + qt_config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file); + void(FS::CreateParentDir(qt_config_loc)); qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); break; @@ -514,6 +514,13 @@ void Config::ReadControlValues() { ReadSetting(QStringLiteral("mouse_panning_sensitivity"), 1).toFloat(); ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), true); + + // Disable docked mode if handheld is selected + const auto controller_type = Settings::values.players.GetValue()[0].controller_type; + if (controller_type == Settings::ControllerType::Handheld) { + Settings::values.use_docked_mode.SetValue(false); + } + ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"), true); ReadSettingGlobal(Settings::values.enable_accurate_vibrations, @@ -591,30 +598,34 @@ void Config::ReadDataStorageValues() { qt_config->beginGroup(QStringLiteral("Data Storage")); Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool(); - FS::GetUserPath(FS::UserPath::NANDDir, - qt_config - ->value(QStringLiteral("nand_directory"), - QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir))) - .toString() - .toStdString()); - FS::GetUserPath(FS::UserPath::SDMCDir, - qt_config - ->value(QStringLiteral("sdmc_directory"), - QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir))) - .toString() - .toStdString()); - FS::GetUserPath(FS::UserPath::LoadDir, - qt_config - ->value(QStringLiteral("load_directory"), - QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir))) - .toString() - .toStdString()); - FS::GetUserPath(FS::UserPath::DumpDir, - qt_config - ->value(QStringLiteral("dump_directory"), - QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir))) - .toString() - .toStdString()); + FS::SetYuzuPath( + FS::YuzuPath::NANDDir, + qt_config + ->value(QStringLiteral("nand_directory"), + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))) + .toString() + .toStdString()); + FS::SetYuzuPath( + FS::YuzuPath::SDMCDir, + qt_config + ->value(QStringLiteral("sdmc_directory"), + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))) + .toString() + .toStdString()); + FS::SetYuzuPath( + FS::YuzuPath::LoadDir, + qt_config + ->value(QStringLiteral("load_directory"), + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))) + .toString() + .toStdString()); + FS::SetYuzuPath( + FS::YuzuPath::DumpDir, + qt_config + ->value(QStringLiteral("dump_directory"), + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))) + .toString() + .toStdString()); Settings::values.gamecard_inserted = ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool(); Settings::values.gamecard_current_game = @@ -736,10 +747,16 @@ void Config::ReadPathValues() { void Config::ReadCpuValues() { qt_config->beginGroup(QStringLiteral("Cpu")); - if (global) { - Settings::values.cpu_accuracy = static_cast<Settings::CPUAccuracy>( - ReadSetting(QStringLiteral("cpu_accuracy"), 0).toInt()); + ReadSettingGlobal(Settings::values.cpu_accuracy, QStringLiteral("cpu_accuracy"), 0); + + ReadSettingGlobal(Settings::values.cpuopt_unsafe_unfuse_fma, + QStringLiteral("cpuopt_unsafe_unfuse_fma"), true); + ReadSettingGlobal(Settings::values.cpuopt_unsafe_reduce_fp_error, + QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true); + ReadSettingGlobal(Settings::values.cpuopt_unsafe_inaccurate_nan, + QStringLiteral("cpuopt_unsafe_inaccurate_nan"), true); + if (global) { Settings::values.cpuopt_page_tables = ReadSetting(QStringLiteral("cpuopt_page_tables"), true).toBool(); Settings::values.cpuopt_block_linking = @@ -756,13 +773,6 @@ void Config::ReadCpuValues() { ReadSetting(QStringLiteral("cpuopt_misc_ir"), true).toBool(); Settings::values.cpuopt_reduce_misalign_checks = ReadSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), true).toBool(); - - Settings::values.cpuopt_unsafe_unfuse_fma = - ReadSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), true).toBool(); - Settings::values.cpuopt_unsafe_reduce_fp_error = - ReadSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), true).toBool(); - Settings::values.cpuopt_unsafe_inaccurate_nan = - ReadSetting(QStringLiteral("cpuopt_unsafe_inaccurate_nan"), true).toBool(); } qt_config->endGroup(); @@ -811,11 +821,11 @@ void Config::ReadScreenshotValues() { UISettings::values.enable_screenshot_save_as = ReadSetting(QStringLiteral("enable_screenshot_save_as"), true).toBool(); - FS::GetUserPath( - FS::UserPath::ScreenshotsDir, + FS::SetYuzuPath( + FS::YuzuPath::ScreenshotsDir, qt_config ->value(QStringLiteral("screenshot_path"), - QString::fromStdString(FS::GetUserPath(FS::UserPath::ScreenshotsDir))) + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir))) .toString() .toStdString()); @@ -869,17 +879,14 @@ void Config::ReadSystemValues() { } } - bool custom_rtc_enabled; - ReadSettingGlobal(custom_rtc_enabled, QStringLiteral("custom_rtc_enabled"), false); - bool custom_rtc_global = - global || qt_config->value(QStringLiteral("custom_rtc/use_global"), true).toBool(); - Settings::values.custom_rtc.SetGlobal(custom_rtc_global); - if (global || !custom_rtc_global) { + if (global) { + const auto custom_rtc_enabled = + ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool(); if (custom_rtc_enabled) { - Settings::values.custom_rtc.SetValue( - std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong())); + Settings::values.custom_rtc = + std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong()); } else { - Settings::values.custom_rtc.SetValue(std::nullopt); + Settings::values.custom_rtc = std::nullopt; } } @@ -1217,17 +1224,17 @@ void Config::SaveDataStorageValues() { WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true); WriteSetting(QStringLiteral("nand_directory"), - QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir)), - QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir))); + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)), + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); WriteSetting(QStringLiteral("sdmc_directory"), - QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir)), - QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir))); + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)), + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); WriteSetting(QStringLiteral("load_directory"), - QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir)), - QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir))); + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)), + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); WriteSetting(QStringLiteral("dump_directory"), - QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)), - QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir))); + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)), + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false); WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game, false); @@ -1313,10 +1320,19 @@ void Config::SavePathValues() { void Config::SaveCpuValues() { qt_config->beginGroup(QStringLiteral("Cpu")); - if (global) { - WriteSetting(QStringLiteral("cpu_accuracy"), - static_cast<int>(Settings::values.cpu_accuracy), 0); + WriteSettingGlobal(QStringLiteral("cpu_accuracy"), + static_cast<u32>(Settings::values.cpu_accuracy.GetValue(global)), + Settings::values.cpu_accuracy.UsingGlobal(), + static_cast<u32>(Settings::CPUAccuracy::Accurate)); + + WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_unfuse_fma"), + Settings::values.cpuopt_unsafe_unfuse_fma, true); + WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), + Settings::values.cpuopt_unsafe_reduce_fp_error, true); + WriteSettingGlobal(QStringLiteral("cpuopt_unsafe_inaccurate_nan"), + Settings::values.cpuopt_unsafe_inaccurate_nan, true); + if (global) { WriteSetting(QStringLiteral("cpuopt_page_tables"), Settings::values.cpuopt_page_tables, true); WriteSetting(QStringLiteral("cpuopt_block_linking"), Settings::values.cpuopt_block_linking, @@ -1331,13 +1347,6 @@ void Config::SaveCpuValues() { WriteSetting(QStringLiteral("cpuopt_misc_ir"), Settings::values.cpuopt_misc_ir, true); WriteSetting(QStringLiteral("cpuopt_reduce_misalign_checks"), Settings::values.cpuopt_reduce_misalign_checks, true); - - WriteSetting(QStringLiteral("cpuopt_unsafe_unfuse_fma"), - Settings::values.cpuopt_unsafe_unfuse_fma, true); - WriteSetting(QStringLiteral("cpuopt_unsafe_reduce_fp_error"), - Settings::values.cpuopt_unsafe_reduce_fp_error, true); - WriteSetting(QStringLiteral("cpuopt_unsafe_inaccurate_nan"), - Settings::values.cpuopt_unsafe_inaccurate_nan, true); } qt_config->endGroup(); @@ -1392,7 +1401,7 @@ void Config::SaveScreenshotValues() { WriteSetting(QStringLiteral("enable_screenshot_save_as"), UISettings::values.enable_screenshot_save_as); WriteSetting(QStringLiteral("screenshot_path"), - QString::fromStdString(FS::GetUserPath(FS::UserPath::ScreenshotsDir))); + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir))); qt_config->endGroup(); } @@ -1432,14 +1441,14 @@ void Config::SaveSystemValues() { Settings::values.rng_seed.GetValue(global).value_or(0), Settings::values.rng_seed.UsingGlobal(), 0); - WriteSettingGlobal(QStringLiteral("custom_rtc_enabled"), - Settings::values.custom_rtc.GetValue(global).has_value(), - Settings::values.custom_rtc.UsingGlobal(), false); - WriteSettingGlobal( - QStringLiteral("custom_rtc"), - QVariant::fromValue<long long>( - Settings::values.custom_rtc.GetValue(global).value_or(std::chrono::seconds{}).count()), - Settings::values.custom_rtc.UsingGlobal(), 0); + if (global) { + WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(), + false); + WriteSetting(QStringLiteral("custom_rtc"), + QVariant::fromValue<long long>( + Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()), + 0); + } WriteSettingGlobal(QStringLiteral("sound_index"), Settings::values.sound_index, 1); diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 5a2c026b3..ce3355588 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -132,5 +132,6 @@ private: }; // These metatype declarations cannot be in common/settings.h because core is devoid of QT +Q_DECLARE_METATYPE(Settings::CPUAccuracy); Q_DECLARE_METATYPE(Settings::RendererBackend); Q_DECLARE_METATYPE(Settings::GPUAccuracy); diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp index 89be4a62d..096e42e94 100644 --- a/src/yuzu/configuration/configuration_shared.cpp +++ b/src/yuzu/configuration/configuration_shared.cpp @@ -13,32 +13,29 @@ void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox, const CheckState& tracker) { - if (tracker == CheckState::Global) { - setting->SetGlobal(true); - } else { - setting->SetGlobal(false); + if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) { setting->SetValue(checkbox->checkState()); + } else if (!Settings::IsConfiguringGlobal()) { + if (tracker == CheckState::Global) { + setting->SetGlobal(true); + } else { + setting->SetGlobal(false); + setting->SetValue(checkbox->checkState()); + } } } void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<int>* setting, const QComboBox* combobox) { - if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { - setting->SetGlobal(true); - } else { - setting->SetGlobal(false); - setting->SetValue(combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET); - } -} - -void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting, - const QComboBox* combobox) { - if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { - setting->SetGlobal(true); - } else { - setting->SetGlobal(false); - setting->SetValue(static_cast<Settings::RendererBackend>( - combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET)); + if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) { + setting->SetValue(combobox->currentIndex()); + } else if (!Settings::IsConfiguringGlobal()) { + if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { + setting->SetGlobal(true); + } else { + setting->SetGlobal(false); + setting->SetValue(combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET); + } } } @@ -51,27 +48,6 @@ void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox, } } -void ConfigurationShared::SetPerGameSetting(QComboBox* combobox, - const Settings::Setting<int>* setting) { - combobox->setCurrentIndex(setting->UsingGlobal() - ? ConfigurationShared::USE_GLOBAL_INDEX - : setting->GetValue() + ConfigurationShared::USE_GLOBAL_OFFSET); -} - -void ConfigurationShared::SetPerGameSetting( - QComboBox* combobox, const Settings::Setting<Settings::RendererBackend>* setting) { - combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX - : static_cast<int>(setting->GetValue()) + - ConfigurationShared::USE_GLOBAL_OFFSET); -} - -void ConfigurationShared::SetPerGameSetting( - QComboBox* combobox, const Settings::Setting<Settings::GPUAccuracy>* setting) { - combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX - : static_cast<int>(setting->GetValue()) + - ConfigurationShared::USE_GLOBAL_OFFSET); -} - void ConfigurationShared::SetHighlight(QWidget* widget, bool highlighted) { if (highlighted) { widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,203,255,0.5) }") diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h index 5b344cdbd..1e0ef01ca 100644 --- a/src/yuzu/configuration/configuration_shared.h +++ b/src/yuzu/configuration/configuration_shared.h @@ -15,37 +15,45 @@ constexpr int USE_GLOBAL_INDEX = 0; constexpr int USE_GLOBAL_SEPARATOR_INDEX = 1; constexpr int USE_GLOBAL_OFFSET = 2; +// CheckBoxes require a tracker for their state since we emulate a tristate CheckBox enum class CheckState { - Off, - On, - Global, - Count, + Off, // Checkbox overrides to off/false + On, // Checkbox overrides to on/true + Global, // Checkbox defers to the global state + Count, // Simply the number of states, not a valid checkbox state }; // Global-aware apply and set functions +// ApplyPerGameSetting, given a Settings::Setting and a Qt UI element, properly applies a Setting void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox, const CheckState& tracker); void ApplyPerGameSetting(Settings::Setting<int>* setting, const QComboBox* combobox); -void ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting, - const QComboBox* combobox); -void ApplyPerGameSetting(Settings::Setting<Settings::GPUAccuracy>* setting, - const QComboBox* combobox); +// Sets a Qt UI element given a Settings::Setting void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting); -void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<int>* setting); -void SetPerGameSetting(QComboBox* combobox, - const Settings::Setting<Settings::RendererBackend>* setting); -void SetPerGameSetting(QComboBox* combobox, - const Settings::Setting<Settings::GPUAccuracy>* setting); +template <typename Type> +void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<Type>* setting) { + combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX + : static_cast<int>(setting->GetValue()) + + ConfigurationShared::USE_GLOBAL_OFFSET); +} + +// (Un)highlights a Qt UI element void SetHighlight(QWidget* widget, bool highlighted); + +// Sets up a QCheckBox like a tristate one, given a Setting void SetColoredTristate(QCheckBox* checkbox, const Settings::Setting<bool>& setting, CheckState& tracker); void SetColoredTristate(QCheckBox* checkbox, bool global, bool state, bool global_state, CheckState& tracker); + +// Sets up coloring of a QWidget `target` based on the state of a QComboBox, and calls +// InsertGlobalItem void SetColoredComboBox(QComboBox* combobox, QWidget* target, int global); +// Adds the "Use Global Configuration" selection and separator to the beginning of a QComboBox void InsertGlobalItem(QComboBox* combobox, int global_index); } // namespace ConfigurationShared diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index f9507e228..fc0191432 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp @@ -99,6 +99,9 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) { } void ConfigureAudio::ApplyConfiguration() { + ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching, + ui->toggle_audio_stretching, enable_audio_stretching); + if (Settings::IsConfiguringGlobal()) { Settings::values.sink_id = ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) @@ -108,19 +111,12 @@ void ConfigureAudio::ApplyConfiguration() { .toStdString(); // Guard if during game and set to game-specific value - if (Settings::values.enable_audio_stretching.UsingGlobal()) { - Settings::values.enable_audio_stretching.SetValue( - ui->toggle_audio_stretching->isChecked()); - } if (Settings::values.volume.UsingGlobal()) { Settings::values.volume.SetValue( static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum()); } } else { - ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching, - ui->toggle_audio_stretching, - enable_audio_stretching); if (ui->volume_combo_box->currentIndex() == 0) { Settings::values.volume.SetGlobal(true); } else { diff --git a/src/yuzu/configuration/configure_cpu.cpp b/src/yuzu/configuration/configure_cpu.cpp index 4f99bc80f..525c42ff0 100644 --- a/src/yuzu/configuration/configure_cpu.cpp +++ b/src/yuzu/configuration/configure_cpu.cpp @@ -10,11 +10,14 @@ #include "common/settings.h" #include "core/core.h" #include "ui_configure_cpu.h" +#include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_cpu.h" ConfigureCpu::ConfigureCpu(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureCpu) { ui->setupUi(this); + SetupPerGameUI(); + SetConfiguration(); connect(ui->accuracy, qOverload<int>(&QComboBox::activated), this, @@ -29,19 +32,29 @@ void ConfigureCpu::SetConfiguration() { const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); ui->accuracy->setEnabled(runtime_lock); - ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy)); - UpdateGroup(static_cast<int>(Settings::values.cpu_accuracy)); - ui->cpuopt_unsafe_unfuse_fma->setEnabled(runtime_lock); - ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma); ui->cpuopt_unsafe_reduce_fp_error->setEnabled(runtime_lock); - ui->cpuopt_unsafe_reduce_fp_error->setChecked(Settings::values.cpuopt_unsafe_reduce_fp_error); ui->cpuopt_unsafe_inaccurate_nan->setEnabled(runtime_lock); - ui->cpuopt_unsafe_inaccurate_nan->setChecked(Settings::values.cpuopt_unsafe_inaccurate_nan); + + ui->cpuopt_unsafe_unfuse_fma->setChecked(Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()); + ui->cpuopt_unsafe_reduce_fp_error->setChecked( + Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue()); + ui->cpuopt_unsafe_inaccurate_nan->setChecked( + Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()); + + if (Settings::IsConfiguringGlobal()) { + ui->accuracy->setCurrentIndex(static_cast<int>(Settings::values.cpu_accuracy.GetValue())); + } else { + ConfigurationShared::SetPerGameSetting(ui->accuracy, &Settings::values.cpu_accuracy); + ConfigurationShared::SetHighlight(ui->widget_accuracy, + !Settings::values.cpu_accuracy.UsingGlobal()); + } + UpdateGroup(ui->accuracy->currentIndex()); } void ConfigureCpu::AccuracyUpdated(int index) { - if (static_cast<Settings::CPUAccuracy>(index) == Settings::CPUAccuracy::DebugMode) { + if (Settings::IsConfiguringGlobal() && + static_cast<Settings::CPUAccuracy>(index) == Settings::CPUAccuracy::DebugMode) { const auto result = QMessageBox::warning(this, tr("Setting CPU to Debug Mode"), tr("CPU Debug Mode is only intended for developer " "use. Are you sure you want to enable this?"), @@ -54,16 +67,39 @@ void ConfigureCpu::AccuracyUpdated(int index) { } void ConfigureCpu::UpdateGroup(int index) { - ui->unsafe_group->setVisible(static_cast<Settings::CPUAccuracy>(index) == - Settings::CPUAccuracy::Unsafe); + if (!Settings::IsConfiguringGlobal()) { + index -= ConfigurationShared::USE_GLOBAL_OFFSET; + } + const auto accuracy = static_cast<Settings::CPUAccuracy>(index); + ui->unsafe_group->setVisible(accuracy == Settings::CPUAccuracy::Unsafe); } void ConfigureCpu::ApplyConfiguration() { - Settings::values.cpu_accuracy = - static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex()); - Settings::values.cpuopt_unsafe_unfuse_fma = ui->cpuopt_unsafe_unfuse_fma->isChecked(); - Settings::values.cpuopt_unsafe_reduce_fp_error = ui->cpuopt_unsafe_reduce_fp_error->isChecked(); - Settings::values.cpuopt_unsafe_inaccurate_nan = ui->cpuopt_unsafe_inaccurate_nan->isChecked(); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_unfuse_fma, + ui->cpuopt_unsafe_unfuse_fma, + cpuopt_unsafe_unfuse_fma); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_reduce_fp_error, + ui->cpuopt_unsafe_reduce_fp_error, + cpuopt_unsafe_reduce_fp_error); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.cpuopt_unsafe_inaccurate_nan, + ui->cpuopt_unsafe_inaccurate_nan, + cpuopt_unsafe_inaccurate_nan); + + if (Settings::IsConfiguringGlobal()) { + // Guard if during game and set to game-specific value + if (Settings::values.cpu_accuracy.UsingGlobal()) { + Settings::values.cpu_accuracy.SetValue( + static_cast<Settings::CPUAccuracy>(ui->accuracy->currentIndex())); + } + } else { + if (ui->accuracy->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { + Settings::values.cpu_accuracy.SetGlobal(true); + } else { + Settings::values.cpu_accuracy.SetGlobal(false); + Settings::values.cpu_accuracy.SetValue(static_cast<Settings::CPUAccuracy>( + ui->accuracy->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET)); + } + } } void ConfigureCpu::changeEvent(QEvent* event) { @@ -77,3 +113,25 @@ void ConfigureCpu::changeEvent(QEvent* event) { void ConfigureCpu::RetranslateUI() { ui->retranslateUi(this); } + +void ConfigureCpu::SetupPerGameUI() { + if (Settings::IsConfiguringGlobal()) { + return; + } + + ConfigurationShared::SetColoredComboBox( + ui->accuracy, ui->widget_accuracy, + static_cast<u32>(Settings::values.cpu_accuracy.GetValue(true))); + ui->accuracy->removeItem(static_cast<u32>(Settings::CPUAccuracy::DebugMode) + + ConfigurationShared::USE_GLOBAL_OFFSET); + + ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_unfuse_fma, + Settings::values.cpuopt_unsafe_unfuse_fma, + cpuopt_unsafe_unfuse_fma); + ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_reduce_fp_error, + Settings::values.cpuopt_unsafe_reduce_fp_error, + cpuopt_unsafe_reduce_fp_error); + ConfigurationShared::SetColoredTristate(ui->cpuopt_unsafe_inaccurate_nan, + Settings::values.cpuopt_unsafe_inaccurate_nan, + cpuopt_unsafe_inaccurate_nan); +} diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h index ef77b2e7e..8e2eeb7a6 100644 --- a/src/yuzu/configuration/configure_cpu.h +++ b/src/yuzu/configuration/configure_cpu.h @@ -8,6 +8,10 @@ #include <QWidget> #include "common/settings.h" +namespace ConfigurationShared { +enum class CheckState; +} + namespace Ui { class ConfigureCpu; } @@ -30,5 +34,11 @@ private: void SetConfiguration(); + void SetupPerGameUI(); + std::unique_ptr<Ui::ConfigureCpu> ui; + + ConfigurationShared::CheckState cpuopt_unsafe_unfuse_fma; + ConfigurationShared::CheckState cpuopt_unsafe_reduce_fp_error; + ConfigurationShared::CheckState cpuopt_unsafe_inaccurate_nan; }; diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui index bcd0962e9..99b573640 100644 --- a/src/yuzu/configuration/configure_cpu.ui +++ b/src/yuzu/configuration/configure_cpu.ui @@ -23,42 +23,44 @@ </property> <layout class="QVBoxLayout"> <item> - <layout class="QHBoxLayout"> - <item> - <widget class="QLabel"> - <property name="text"> - <string>Accuracy:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="accuracy"> - <item> + <widget class="QWidget" name="widget_accuracy" native="true"> + <layout class="QHBoxLayout" name="layout_accuracy"> + <item> + <widget class="QLabel" name="label_accuracy"> <property name="text"> - <string>Accurate</string> + <string>Accuracy:</string> </property> - </item> - <item> - <property name="text"> - <string>Unsafe</string> - </property> - </item> - <item> - <property name="text"> - <string>Enable Debug Mode</string> - </property> - </item> - </widget> - </item> - </layout> + </widget> + </item> + <item> + <widget class="QComboBox" name="accuracy"> + <item> + <property name="text"> + <string>Accurate</string> + </property> + </item> + <item> + <property name="text"> + <string>Unsafe</string> + </property> + </item> + <item> + <property name="text"> + <string>Enable Debug Mode</string> + </property> + </item> + </widget> + </item> + </layout> + </widget> </item> <item> - <widget class="QLabel"> - <property name="wordWrap"> - <bool>1</bool> - </property> + <widget class="QLabel" name="label_recommended_accuracy"> <property name="text"> - <string>We recommend setting accuracy to "Accurate".</string> + <string>We recommend setting accuracy to "Accurate".</string> + </property> + <property name="wordWrap"> + <bool>false</bool> </property> </widget> </item> @@ -76,49 +78,49 @@ </property> <layout class="QVBoxLayout"> <item> - <widget class="QLabel"> - <property name="wordWrap"> - <bool>1</bool> - </property> + <widget class="QLabel" name="label_accuracy_description"> <property name="text"> <string>These settings reduce accuracy for speed.</string> </property> + <property name="wordWrap"> + <bool>false</bool> + </property> </widget> </item> <item> <widget class="QCheckBox" name="cpuopt_unsafe_unfuse_fma"> - <property name="text"> - <string>Unfuse FMA (improve performance on CPUs without FMA)</string> - </property> <property name="toolTip"> <string> <div>This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.</div> </string> </property> + <property name="text"> + <string>Unfuse FMA (improve performance on CPUs without FMA)</string> + </property> </widget> </item> <item> <widget class="QCheckBox" name="cpuopt_unsafe_reduce_fp_error"> - <property name="text"> - <string>Faster FRSQRTE and FRECPE</string> - </property> <property name="toolTip"> <string> <div>This option improves the speed of some approximate floating-point functions by using less accurate native approximations.</div> </string> </property> + <property name="text"> + <string>Faster FRSQRTE and FRECPE</string> + </property> </widget> </item> <item> <widget class="QCheckBox" name="cpuopt_unsafe_inaccurate_nan"> - <property name="text"> - <string>Inaccurate NaN handling</string> - </property> <property name="toolTip"> <string> <div>This option improves speed by removing NaN checking. Please note this also reduces accuracy of certain floating-point instructions.</div> </string> </property> + <property name="text"> + <string>Inaccurate NaN handling</string> + </property> </widget> </item> </layout> diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 6730eb356..b207e07cb 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -4,7 +4,7 @@ #include <QDesktopServices> #include <QUrl> -#include "common/file_util.h" +#include "common/fs/path_util.h" #include "common/logging/backend.h" #include "common/logging/filter.h" #include "common/settings.h" @@ -20,7 +20,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co connect(ui->open_log_button, &QPushButton::clicked, []() { const auto path = - QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::LogDir)); + QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LogDir)); QDesktopServices::openUrl(QUrl::fromLocalFile(path)); }); } diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 3ad40d2b3..6028135c5 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <QAbstractButton> +#include <QDialogButtonBox> #include <QHash> #include <QListWidgetItem> +#include <QPushButton> #include <QSignalBlocker> #include "common/settings.h" #include "core/core.h" @@ -31,6 +34,12 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, &ConfigureDialog::UpdateVisibleTabs); + if (Core::System::GetInstance().IsPoweredOn()) { + QPushButton* apply_button = ui->buttonBox->addButton(QDialogButtonBox::Apply); + connect(apply_button, &QAbstractButton::clicked, this, + &ConfigureDialog::HandleApplyButtonClicked); + } + adjustSize(); ui->selectorList->setCurrentRow(0); } @@ -80,6 +89,11 @@ void ConfigureDialog::RetranslateUI() { ui->tabWidget->setCurrentIndex(old_index); } +void ConfigureDialog::HandleApplyButtonClicked() { + UISettings::values.configuration_applied = true; + ApplyConfiguration(); +} + Q_DECLARE_METATYPE(QList<QWidget*>); void ConfigureDialog::PopulateSelectionList() { diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index 570c3b941..abe019635 100644 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h @@ -35,9 +35,10 @@ signals: private: void changeEvent(QEvent* event) override; - void RetranslateUI(); + void HandleApplyButtonClicked(); + void SetConfiguration(); void UpdateVisibleTabs(); void PopulateSelectionList(); diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp index 006eda4b0..d223c40ea 100644 --- a/src/yuzu/configuration/configure_filesystem.cpp +++ b/src/yuzu/configuration/configure_filesystem.cpp @@ -4,8 +4,8 @@ #include <QFileDialog> #include <QMessageBox> -#include "common/common_paths.h" -#include "common/file_util.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" #include "common/settings.h" #include "ui_configure_filesystem.h" #include "yuzu/configuration/configure_filesystem.h" @@ -40,14 +40,14 @@ ConfigureFilesystem::~ConfigureFilesystem() = default; void ConfigureFilesystem::setConfiguration() { ui->nand_directory_edit->setText( - QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir))); + QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir))); ui->sdmc_directory_edit->setText( - QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir))); + QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::SDMCDir))); ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path)); ui->dump_path_edit->setText( - QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::DumpDir))); + QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::DumpDir))); ui->load_path_edit->setText( - QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::LoadDir))); + QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LoadDir))); ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted); ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game); @@ -60,13 +60,13 @@ void ConfigureFilesystem::setConfiguration() { } void ConfigureFilesystem::applyConfiguration() { - Common::FS::GetUserPath(Common::FS::UserPath::NANDDir, + Common::FS::SetYuzuPath(Common::FS::YuzuPath::NANDDir, ui->nand_directory_edit->text().toStdString()); - Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir, + Common::FS::SetYuzuPath(Common::FS::YuzuPath::SDMCDir, ui->sdmc_directory_edit->text().toStdString()); - Common::FS::GetUserPath(Common::FS::UserPath::DumpDir, + Common::FS::SetYuzuPath(Common::FS::YuzuPath::DumpDir, ui->dump_path_edit->text().toStdString()); - Common::FS::GetUserPath(Common::FS::UserPath::LoadDir, + Common::FS::SetYuzuPath(Common::FS::YuzuPath::LoadDir, ui->load_path_edit->text().toStdString()); Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked(); @@ -104,25 +104,26 @@ void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit) QStringLiteral("NX Gamecard;*.xci")); } else { str = QFileDialog::getExistingDirectory(this, caption, edit->text()); - if (!str.isNull() && str.back() != QDir::separator()) { - str.append(QDir::separator()); - } } - if (str.isEmpty()) + if (str.isNull() || str.isEmpty()) { return; + } + + if (str.back() != QChar::fromLatin1('/')) { + str.append(QChar::fromLatin1('/')); + } edit->setText(str); } void ConfigureFilesystem::ResetMetadata() { - if (!Common::FS::Exists(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP + - "game_list")) { + if (!Common::FS::Exists(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / + "game_list/")) { QMessageBox::information(this, tr("Reset Metadata Cache"), tr("The metadata cache is already empty.")); - } else if (Common::FS::DeleteDirRecursively( - Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP + - "game_list")) { + } else if (Common::FS::RemoveDirRecursively( + Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "game_list")) { QMessageBox::information(this, tr("Reset Metadata Cache"), tr("The operation completed successfully.")); UISettings::values.is_game_list_reload_pending.exchange(true); diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 2fa88dcec..55a6a37bd 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -50,6 +50,9 @@ void ConfigureGeneral::SetConfiguration() { } void ConfigureGeneral::ApplyConfiguration() { + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, ui->use_multi_core, + use_multi_core); + if (Settings::IsConfiguringGlobal()) { UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); @@ -62,13 +65,7 @@ void ConfigureGeneral::ApplyConfiguration() { Qt::Checked); Settings::values.frame_limit.SetValue(ui->frame_limit->value()); } - if (Settings::values.use_multi_core.UsingGlobal()) { - Settings::values.use_multi_core.SetValue(ui->use_multi_core->isChecked()); - } } else { - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, - ui->use_multi_core, use_multi_core); - bool global_frame_limit = use_frame_limit == ConfigurationShared::CheckState::Global; Settings::values.use_frame_limit.SetGlobal(global_frame_limit); Settings::values.frame_limit.SetGlobal(global_frame_limit); @@ -94,6 +91,9 @@ void ConfigureGeneral::RetranslateUI() { void ConfigureGeneral::SetupPerGameUI() { if (Settings::IsConfiguringGlobal()) { + // Disables each setting if: + // - A game is running (thus settings in use), and + // - A non-global setting is applied. ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal()); ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal()); diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 0a7536617..fb9ec093c 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -106,6 +106,19 @@ void ConfigureGraphics::SetConfiguration() { } void ConfigureGraphics::ApplyConfiguration() { + ConfigurationShared::ApplyPerGameSetting(&Settings::values.fullscreen_mode, + ui->fullscreen_mode_combobox); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio, + ui->aspect_ratio_combobox); + + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache, + ui->use_disk_shader_cache, use_disk_shader_cache); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation, + ui->use_asynchronous_gpu_emulation, + use_asynchronous_gpu_emulation); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_nvdec_emulation, + ui->use_nvdec_emulation, use_nvdec_emulation); + if (Settings::IsConfiguringGlobal()) { // Guard if during game and set to game-specific value if (Settings::values.renderer_backend.UsingGlobal()) { @@ -114,22 +127,6 @@ void ConfigureGraphics::ApplyConfiguration() { if (Settings::values.vulkan_device.UsingGlobal()) { Settings::values.vulkan_device.SetValue(vulkan_device); } - if (Settings::values.fullscreen_mode.UsingGlobal()) { - Settings::values.fullscreen_mode.SetValue(ui->fullscreen_mode_combobox->currentIndex()); - } - if (Settings::values.aspect_ratio.UsingGlobal()) { - Settings::values.aspect_ratio.SetValue(ui->aspect_ratio_combobox->currentIndex()); - } - if (Settings::values.use_disk_shader_cache.UsingGlobal()) { - Settings::values.use_disk_shader_cache.SetValue(ui->use_disk_shader_cache->isChecked()); - } - if (Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()) { - Settings::values.use_asynchronous_gpu_emulation.SetValue( - ui->use_asynchronous_gpu_emulation->isChecked()); - } - if (Settings::values.use_nvdec_emulation.UsingGlobal()) { - Settings::values.use_nvdec_emulation.SetValue(ui->use_nvdec_emulation->isChecked()); - } if (Settings::values.bg_red.UsingGlobal()) { Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF())); Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF())); @@ -150,19 +147,6 @@ void ConfigureGraphics::ApplyConfiguration() { } } - ConfigurationShared::ApplyPerGameSetting(&Settings::values.fullscreen_mode, - ui->fullscreen_mode_combobox); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio, - ui->aspect_ratio_combobox); - - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache, - ui->use_disk_shader_cache, use_disk_shader_cache); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation, - ui->use_asynchronous_gpu_emulation, - use_asynchronous_gpu_emulation); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_nvdec_emulation, - ui->use_nvdec_emulation, use_nvdec_emulation); - if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { Settings::values.bg_red.SetGlobal(true); Settings::values.bg_green.SetGlobal(true); diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index c67609b0e..35bf9c6be 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp @@ -54,47 +54,23 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() { ui->gpu_accuracy->currentIndex() - ((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET)); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, + ui->anisotropic_filtering_combobox); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders, + ui->use_assembly_shaders, use_assembly_shaders); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders, + ui->use_asynchronous_shaders, + use_asynchronous_shaders); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, + ui->use_fast_gpu_time, use_fast_gpu_time); + if (Settings::IsConfiguringGlobal()) { // Must guard in case of a during-game configuration when set to be game-specific. if (Settings::values.gpu_accuracy.UsingGlobal()) { Settings::values.gpu_accuracy.SetValue(gpu_accuracy); } - if (Settings::values.use_vsync.UsingGlobal()) { - Settings::values.use_vsync.SetValue(ui->use_vsync->isChecked()); - } - if (Settings::values.use_assembly_shaders.UsingGlobal()) { - Settings::values.use_assembly_shaders.SetValue(ui->use_assembly_shaders->isChecked()); - } - if (Settings::values.use_asynchronous_shaders.UsingGlobal()) { - Settings::values.use_asynchronous_shaders.SetValue( - ui->use_asynchronous_shaders->isChecked()); - } - if (Settings::values.use_asynchronous_shaders.UsingGlobal()) { - Settings::values.use_asynchronous_shaders.SetValue( - ui->use_asynchronous_shaders->isChecked()); - } - if (Settings::values.use_fast_gpu_time.UsingGlobal()) { - Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked()); - } - if (Settings::values.max_anisotropy.UsingGlobal()) { - Settings::values.max_anisotropy.SetValue( - ui->anisotropic_filtering_combobox->currentIndex()); - } } else { - ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, - ui->anisotropic_filtering_combobox); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, - use_vsync); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders, - ui->use_assembly_shaders, use_assembly_shaders); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders, - ui->use_asynchronous_shaders, - use_asynchronous_shaders); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, - ui->use_fast_gpu_time, use_fast_gpu_time); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, - ui->anisotropic_filtering_combobox); - if (ui->gpu_accuracy->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { Settings::values.gpu_accuracy.SetGlobal(true); } else { diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index bd91ebc42..d89f1ad4b 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -6,16 +6,17 @@ #include <memory> #include <utility> +#include <QAbstractButton> #include <QCheckBox> +#include <QDialogButtonBox> #include <QHeaderView> #include <QMenu> +#include <QPushButton> #include <QStandardItemModel> #include <QString> #include <QTimer> #include <QTreeView> -#include "common/common_paths.h" -#include "common/file_util.h" #include "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" @@ -44,6 +45,12 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id) scene = new QGraphicsScene; ui->icon_view->setScene(scene); + if (Core::System::GetInstance().IsPoweredOn()) { + QPushButton* apply_button = ui->buttonBox->addButton(QDialogButtonBox::Apply); + connect(apply_button, &QAbstractButton::clicked, this, + &ConfigurePerGame::HandleApplyButtonClicked); + } + LoadConfiguration(); } @@ -52,6 +59,7 @@ ConfigurePerGame::~ConfigurePerGame() = default; void ConfigurePerGame::ApplyConfiguration() { ui->addonsTab->ApplyConfiguration(); ui->generalTab->ApplyConfiguration(); + ui->cpuTab->ApplyConfiguration(); ui->systemTab->ApplyConfiguration(); ui->graphicsTab->ApplyConfiguration(); ui->graphicsAdvancedTab->ApplyConfiguration(); @@ -75,6 +83,11 @@ void ConfigurePerGame::RetranslateUI() { ui->retranslateUi(this); } +void ConfigurePerGame::HandleApplyButtonClicked() { + UISettings::values.configuration_applied = true; + ApplyConfiguration(); +} + void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file) { this->file = std::move(file); LoadConfiguration(); diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h index 5f9a08cef..f6e6ab7c4 100644 --- a/src/yuzu/configuration/configure_per_game.h +++ b/src/yuzu/configuration/configure_per_game.h @@ -39,6 +39,8 @@ private: void changeEvent(QEvent* event) override; void RetranslateUI(); + void HandleApplyButtonClicked(); + void LoadConfiguration(); std::unique_ptr<Ui::ConfigurePerGame> ui; diff --git a/src/yuzu/configuration/configure_per_game.ui b/src/yuzu/configuration/configure_per_game.ui index 25975b3b9..adf6d0b39 100644 --- a/src/yuzu/configuration/configure_per_game.ui +++ b/src/yuzu/configuration/configure_per_game.ui @@ -235,6 +235,11 @@ <string>System</string> </attribute> </widget> + <widget class="ConfigureCpu" name="cpuTab"> + <attribute name="title"> + <string>CPU</string> + </attribute> + </widget> <widget class="ConfigureGraphics" name="graphicsTab"> <attribute name="title"> <string>Graphics</string> @@ -311,6 +316,12 @@ <header>configuration/configure_per_game_addons.h</header> <container>1</container> </customwidget> + <customwidget> + <class>ConfigureCpu</class> + <extends>QWidget</extends> + <header>configuration/configure_cpu.h</header> + <container>1</container> + </customwidget> </customwidgets> <resources/> <connections> diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp index cdeeec01c..9b709d405 100644 --- a/src/yuzu/configuration/configure_per_game_addons.cpp +++ b/src/yuzu/configuration/configure_per_game_addons.cpp @@ -13,8 +13,8 @@ #include <QTimer> #include <QTreeView> -#include "common/common_paths.h" -#include "common/file_util.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" #include "core/core.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/xts_archive.h" @@ -79,8 +79,8 @@ void ConfigurePerGameAddons::ApplyConfiguration() { std::sort(disabled_addons.begin(), disabled_addons.end()); std::sort(current.begin(), current.end()); if (disabled_addons != current) { - Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP + - "game_list" + DIR_SEP + fmt::format("{:016X}.pv.txt", title_id)); + void(Common::FS::RemoveFile(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / + "game_list" / fmt::format("{:016X}.pv.txt", title_id))); } Settings::values.disabled_addons[title_id] = disabled_addons; diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index d61b5e29b..f5881e58d 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp @@ -12,7 +12,7 @@ #include <QTreeView> #include <QVBoxLayout> #include "common/assert.h" -#include "common/file_util.h" +#include "common/fs/path_util.h" #include "common/settings.h" #include "common/string_util.h" #include "core/core.h" @@ -34,9 +34,10 @@ constexpr std::array<u8, 107> backup_jpeg{ }; QString GetImagePath(Common::UUID uuid) { - const auto path = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + - "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; - return QString::fromStdString(path); + const auto path = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / + fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormatSwitch()); + return QString::fromStdString(Common::FS::PathToUTF8String(path)); } QString GetAccountUsername(const Service::Account::ProfileManager& manager, Common::UUID uuid) { @@ -281,8 +282,8 @@ void ConfigureProfileManager::SetUserImage() { return; } - const auto raw_path = QString::fromStdString( - Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + "/system/save/8000000000000010"); + const auto raw_path = QString::fromStdString(Common::FS::PathToUTF8String( + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000010")); const QFileInfo raw_info{raw_path}; if (raw_info.exists() && !raw_info.isDir() && !QFile::remove(raw_path)) { QMessageBox::warning(this, tr("Error deleting file"), diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 268ed44c3..99a5df241 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -10,7 +10,6 @@ #include <QGraphicsItem> #include <QMessageBox> #include "common/assert.h" -#include "common/file_util.h" #include "common/settings.h" #include "core/core.h" #include "core/hle/service/time/time.h" @@ -65,7 +64,7 @@ void ConfigureSystem::SetConfiguration() { QStringLiteral("%1") .arg(Settings::values.rng_seed.GetValue().value_or(0), 8, 16, QLatin1Char{'0'}) .toUpper(); - const auto rtc_time = Settings::values.custom_rtc.GetValue().value_or( + const auto rtc_time = Settings::values.custom_rtc.value_or( std::chrono::seconds(QDateTime::currentSecsSinceEpoch())); ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.GetValue().has_value()); @@ -73,9 +72,8 @@ void ConfigureSystem::SetConfiguration() { Settings::values.rng_seed.UsingGlobal()); ui->rng_seed_edit->setText(rng_seed); - ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.GetValue().has_value()); - ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.GetValue().has_value() && - Settings::values.rng_seed.UsingGlobal()); + ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); + ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value()); ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count())); if (Settings::IsConfiguringGlobal()) { @@ -109,17 +107,17 @@ void ConfigureSystem::ApplyConfiguration() { // Allow setting custom RTC even if system is powered on, // to allow in-game time to be fast forwarded - if (Settings::values.custom_rtc.UsingGlobal()) { + if (Settings::IsConfiguringGlobal()) { if (ui->custom_rtc_checkbox->isChecked()) { - Settings::values.custom_rtc.SetValue( - std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch())); + Settings::values.custom_rtc = + std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()); if (system.IsPoweredOn()) { - const s64 posix_time{Settings::values.custom_rtc.GetValue()->count() + + const s64 posix_time{Settings::values.custom_rtc->count() + Service::Time::TimeManager::GetExternalTimeZoneOffset()}; system.GetTimeManager().UpdateLocalSystemClockTime(posix_time); } } else { - Settings::values.custom_rtc.SetValue(std::nullopt); + Settings::values.custom_rtc = std::nullopt; } } @@ -127,21 +125,14 @@ void ConfigureSystem::ApplyConfiguration() { return; } + ConfigurationShared::ApplyPerGameSetting(&Settings::values.language_index, ui->combo_language); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_index, ui->combo_region); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.time_zone_index, + ui->combo_time_zone); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.sound_index, ui->combo_sound); + if (Settings::IsConfiguringGlobal()) { // Guard if during game and set to game-specific value - if (Settings::values.language_index.UsingGlobal()) { - Settings::values.language_index.SetValue(ui->combo_language->currentIndex()); - } - if (Settings::values.region_index.UsingGlobal()) { - Settings::values.region_index.SetValue(ui->combo_region->currentIndex()); - } - if (Settings::values.time_zone_index.UsingGlobal()) { - Settings::values.time_zone_index.SetValue(ui->combo_time_zone->currentIndex()); - } - if (Settings::values.sound_index.UsingGlobal()) { - Settings::values.sound_index.SetValue(ui->combo_sound->currentIndex()); - } - if (Settings::values.rng_seed.UsingGlobal()) { if (ui->rng_seed_checkbox->isChecked()) { Settings::values.rng_seed.SetValue( @@ -151,13 +142,6 @@ void ConfigureSystem::ApplyConfiguration() { } } } else { - ConfigurationShared::ApplyPerGameSetting(&Settings::values.language_index, - ui->combo_language); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_index, ui->combo_region); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.time_zone_index, - ui->combo_time_zone); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.sound_index, ui->combo_sound); - switch (use_rng_seed) { case ConfigurationShared::CheckState::On: case ConfigurationShared::CheckState::Off: @@ -177,26 +161,6 @@ void ConfigureSystem::ApplyConfiguration() { case ConfigurationShared::CheckState::Count: break; } - - switch (use_custom_rtc) { - case ConfigurationShared::CheckState::On: - case ConfigurationShared::CheckState::Off: - Settings::values.custom_rtc.SetGlobal(false); - if (ui->custom_rtc_checkbox->isChecked()) { - Settings::values.custom_rtc.SetValue( - std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch())); - } else { - Settings::values.custom_rtc.SetValue(std::nullopt); - } - break; - case ConfigurationShared::CheckState::Global: - Settings::values.custom_rtc.SetGlobal(false); - Settings::values.custom_rtc.SetValue(std::nullopt); - Settings::values.custom_rtc.SetGlobal(true); - break; - case ConfigurationShared::CheckState::Count: - break; - } } system.ApplySettings(); @@ -227,8 +191,6 @@ void ConfigureSystem::SetupPerGameUI() { ui->combo_sound->setEnabled(Settings::values.sound_index.UsingGlobal()); ui->rng_seed_checkbox->setEnabled(Settings::values.rng_seed.UsingGlobal()); ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.UsingGlobal()); - ui->custom_rtc_checkbox->setEnabled(Settings::values.custom_rtc.UsingGlobal()); - ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.UsingGlobal()); return; } @@ -246,8 +208,7 @@ void ConfigureSystem::SetupPerGameUI() { ui->rng_seed_checkbox, Settings::values.rng_seed.UsingGlobal(), Settings::values.rng_seed.GetValue().has_value(), Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed); - ConfigurationShared::SetColoredTristate( - ui->custom_rtc_checkbox, Settings::values.custom_rtc.UsingGlobal(), - Settings::values.custom_rtc.GetValue().has_value(), - Settings::values.custom_rtc.GetValue(true).has_value(), use_custom_rtc); + + ui->custom_rtc_checkbox->setVisible(false); + ui->custom_rtc_edit->setVisible(false); } diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 0cdaea8a4..0a28c87c0 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp @@ -8,7 +8,7 @@ #include <QDirIterator> #include "common/common_types.h" -#include "common/file_util.h" +#include "common/fs/path_util.h" #include "common/settings.h" #include "core/core.h" #include "ui_configure_ui.h" @@ -62,13 +62,16 @@ ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::Configur // Set screenshot path to user specification. connect(ui->screenshot_path_button, &QToolButton::pressed, this, [this] { - const QString& filename = + auto dir = QFileDialog::getExistingDirectory(this, tr("Select Screenshots Path..."), - QString::fromStdString(Common::FS::GetUserPath( - Common::FS::UserPath::ScreenshotsDir))) + - QDir::separator(); - if (!filename.isEmpty()) { - ui->screenshot_path_edit->setText(filename); + QString::fromStdString(Common::FS::GetYuzuPathString( + Common::FS::YuzuPath::ScreenshotsDir))); + if (!dir.isEmpty()) { + if (dir.back() != QChar::fromLatin1('/')) { + dir.append(QChar::fromLatin1('/')); + } + + ui->screenshot_path_edit->setText(dir); } }); } @@ -84,7 +87,7 @@ void ConfigureUi::ApplyConfiguration() { UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked(); - Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir, + Common::FS::SetYuzuPath(Common::FS::YuzuPath::ScreenshotsDir, ui->screenshot_path_edit->text().toStdString()); Core::System::GetInstance().ApplySettings(); } @@ -102,8 +105,8 @@ void ConfigureUi::SetConfiguration() { ui->icon_size_combobox->findData(UISettings::values.icon_size)); ui->enable_screenshot_save_as->setChecked(UISettings::values.enable_screenshot_save_as); - ui->screenshot_path_edit->setText( - QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir))); + ui->screenshot_path_edit->setText(QString::fromStdString( + Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir))); } void ConfigureUi::changeEvent(QEvent* event) { diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp index e87aededb..333eeb84e 100644 --- a/src/yuzu/configuration/input_profiles.cpp +++ b/src/yuzu/configuration/input_profiles.cpp @@ -4,8 +4,8 @@ #include <fmt/format.h> -#include "common/common_paths.h" -#include "common/file_util.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" #include "yuzu/configuration/config.h" #include "yuzu/configuration/input_profiles.h" @@ -14,47 +14,43 @@ namespace FS = Common::FS; namespace { bool ProfileExistsInFilesystem(std::string_view profile_name) { - return FS::Exists(fmt::format("{}input" DIR_SEP "{}.ini", - FS::GetUserPath(FS::UserPath::ConfigDir), profile_name)); + return FS::Exists(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "input" / + fmt::format("{}.ini", profile_name)); } -bool IsINI(std::string_view filename) { - const std::size_t index = filename.rfind('.'); - - if (index == std::string::npos) { - return false; - } - - return filename.substr(index) == ".ini"; +bool IsINI(const std::filesystem::path& filename) { + return filename.extension() == ".ini"; } -std::string GetNameWithoutExtension(const std::string& filename) { - const std::size_t index = filename.rfind('.'); - - if (index == std::string::npos) { - return filename; - } - - return filename.substr(0, index); +std::filesystem::path GetNameWithoutExtension(std::filesystem::path filename) { + return filename.replace_extension(); } } // namespace InputProfiles::InputProfiles() { - const std::string input_profile_loc = - fmt::format("{}input", FS::GetUserPath(FS::UserPath::ConfigDir)); + const auto input_profile_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "input"; + + if (!FS::IsDir(input_profile_loc)) { + return; + } - FS::ForeachDirectoryEntry( - nullptr, input_profile_loc, - [this](u64* entries_out, const std::string& directory, const std::string& filename) { - if (IsINI(filename) && IsProfileNameValid(GetNameWithoutExtension(filename))) { + FS::IterateDirEntries( + input_profile_loc, + [this](const std::filesystem::path& full_path) { + const auto filename = full_path.filename(); + const auto name_without_ext = + Common::FS::PathToUTF8String(GetNameWithoutExtension(filename)); + + if (IsINI(filename) && IsProfileNameValid(name_without_ext)) { map_profiles.insert_or_assign( - GetNameWithoutExtension(filename), - std::make_unique<Config>(GetNameWithoutExtension(filename), - Config::ConfigType::InputProfile)); + name_without_ext, + std::make_unique<Config>(name_without_ext, Config::ConfigType::InputProfile)); } + return true; - }); + }, + FS::DirEntryFilter::File); } InputProfiles::~InputProfiles() = default; @@ -96,7 +92,7 @@ bool InputProfiles::DeleteProfile(const std::string& profile_name) { } if (!ProfileExistsInFilesystem(profile_name) || - FS::Delete(map_profiles[profile_name]->GetConfigFilePath())) { + FS::RemoveFile(map_profiles[profile_name]->GetConfigFilePath())) { map_profiles.erase(profile_name); } diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp index 7186eac76..d85408ac6 100644 --- a/src/yuzu/debugger/controller.cpp +++ b/src/yuzu/debugger/controller.cpp @@ -38,6 +38,7 @@ void ControllerDialog::refreshConfiguration() { widget->SetPlayerInputRaw(player, players[player].buttons, players[player].analogs); widget->SetConnectedStatus(players[player].connected); widget->SetControllerType(players[player].controller_type); + widget->repaint(); } QAction* ControllerDialog::toggleViewAction() { diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 23643aea2..485045334 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -12,8 +12,8 @@ #include <QFileInfo> #include <QSettings> -#include "common/common_paths.h" -#include "common/file_util.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" #include "core/core.h" #include "core/file_sys/card_image.h" #include "core/file_sys/content_archive.h" @@ -39,10 +39,11 @@ QString GetGameListCachedObject(const std::string& filename, const std::string& return generator(); } - const auto path = Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP + - "game_list" + DIR_SEP + filename + '.' + ext; + const auto path = + Common::FS::PathToUTF8String(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / + "game_list" / fmt::format("{}.{}", filename, ext)); - Common::FS::CreateFullPath(path); + void(Common::FS::CreateParentDirs(path)); if (!Common::FS::Exists(path)) { const auto str = generator(); @@ -70,12 +71,15 @@ std::pair<std::vector<u8>, std::string> GetGameListCachedObject( return generator(); } - const auto path1 = Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP + - "game_list" + DIR_SEP + filename + ".jpeg"; - const auto path2 = Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP + - "game_list" + DIR_SEP + filename + ".appname.txt"; + const auto game_list_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "game_list"; + const auto jpeg_name = fmt::format("{}.jpeg", filename); + const auto app_name = fmt::format("{}.appname.txt", filename); - Common::FS::CreateFullPath(path1); + const auto path1 = Common::FS::PathToUTF8String(game_list_dir / jpeg_name); + const auto path2 = Common::FS::PathToUTF8String(game_list_dir / app_name); + + void(Common::FS::CreateParentDirs(path1)); if (!Common::FS::Exists(path1) || !Common::FS::Exists(path2)) { const auto [icon, nacp] = generator(); @@ -281,23 +285,27 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { } } -void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, - unsigned int recursion, GameListDir* parent_dir) { +void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, bool deep_scan, + GameListDir* parent_dir) { auto& system = Core::System::GetInstance(); - const auto callback = [this, target, recursion, parent_dir, - &system](u64* num_entries_out, const std::string& directory, - const std::string& virtual_name) -> bool { + const auto callback = [this, target, parent_dir, + &system](const std::filesystem::path& path) -> bool { if (stop_processing) { // Breaks the callback loop. return false; } - const std::string physical_name = directory + DIR_SEP + virtual_name; - const bool is_dir = Common::FS::IsDirectory(physical_name); + const auto physical_name = Common::FS::PathToUTF8String(path); + const auto is_dir = Common::FS::IsDir(path); + if (!is_dir && (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read); + if (!file) { + return true; + } + auto loader = Loader::GetLoader(system, file); if (!loader) { return true; @@ -343,15 +351,19 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa compatibility_list, patch), parent_dir); } - } else if (is_dir && recursion > 0) { + } else if (is_dir) { watch_list.append(QString::fromStdString(physical_name)); - ScanFileSystem(target, physical_name, recursion - 1, parent_dir); } return true; }; - Common::FS::ForeachDirectoryEntry(nullptr, dir_path, callback); + if (deep_scan) { + Common::FS::IterateDirEntriesRecursively(dir_path, callback, + Common::FS::DirEntryFilter::All); + } else { + Common::FS::IterateDirEntries(dir_path, callback, Common::FS::DirEntryFilter::File); + } } void GameListWorker::run() { @@ -376,9 +388,9 @@ void GameListWorker::run() { auto* const game_list_dir = new GameListDir(game_dir); emit DirEntryReady(game_list_dir); ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), - game_dir.deep_scan ? 256 : 0, game_list_dir); + game_dir.deep_scan, game_list_dir); ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(), - game_dir.deep_scan ? 256 : 0, game_list_dir); + game_dir.deep_scan, game_list_dir); } } diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h index 84e4e1b42..396bb2623 100644 --- a/src/yuzu/game_list_worker.h +++ b/src/yuzu/game_list_worker.h @@ -70,7 +70,7 @@ private: PopulateGameList, }; - void ScanFileSystem(ScanTarget target, const std::string& dir_path, unsigned int recursion, + void ScanFileSystem(ScanTarget target, const std::string& dir_path, bool deep_scan, GameListDir* parent_dir); std::shared_ptr<FileSys::VfsFilesystem> vfs; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 9275cba53..0f0e228b0 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -66,9 +66,10 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include <QtConcurrent/QtConcurrent> #include <fmt/format.h> -#include "common/common_paths.h" #include "common/detached_tasks.h" -#include "common/file_util.h" +#include "common/fs/fs.h" +#include "common/fs/fs_paths.h" +#include "common/fs/path_util.h" #include "common/logging/backend.h" #include "common/logging/filter.h" #include "common/logging/log.h" @@ -178,36 +179,25 @@ static void InitializeLogging() { log_filter.ParseFilterString(Settings::values.log_filter); Log::SetGlobalFilter(log_filter); - const std::string& log_dir = FS::GetUserPath(FS::UserPath::LogDir); - FS::CreateFullPath(log_dir); - Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); + const auto log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir); + void(FS::CreateDir(log_dir)); + Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE)); #ifdef _WIN32 Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); #endif } static void RemoveCachedContents() { - const auto offline_fonts = Common::FS::SanitizePath( - fmt::format("{}/fonts", Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)), - Common::FS::DirectorySeparator::PlatformDefault); - - const auto offline_manual = Common::FS::SanitizePath( - fmt::format("{}/offline_web_applet_manual", - Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)), - Common::FS::DirectorySeparator::PlatformDefault); - const auto offline_legal_information = Common::FS::SanitizePath( - fmt::format("{}/offline_web_applet_legal_information", - Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)), - Common::FS::DirectorySeparator::PlatformDefault); - const auto offline_system_data = Common::FS::SanitizePath( - fmt::format("{}/offline_web_applet_system_data", - Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)), - Common::FS::DirectorySeparator::PlatformDefault); - - Common::FS::DeleteDirRecursively(offline_fonts); - Common::FS::DeleteDirRecursively(offline_manual); - Common::FS::DeleteDirRecursively(offline_legal_information); - Common::FS::DeleteDirRecursively(offline_system_data); + const auto cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir); + const auto offline_fonts = cache_dir / "fonts"; + const auto offline_manual = cache_dir / "offline_web_applet_manual"; + const auto offline_legal_information = cache_dir / "offline_web_applet_legal_information"; + const auto offline_system_data = cache_dir / "offline_web_applet_system_data"; + + void(Common::FS::RemoveDirRecursively(offline_fonts)); + void(Common::FS::RemoveDirRecursively(offline_manual)); + void(Common::FS::RemoveDirRecursively(offline_legal_information)); + void(Common::FS::RemoveDirRecursively(offline_system_data)); } GMainWindow::GMainWindow() @@ -773,10 +763,22 @@ void GMainWindow::InitializeWidgets() { dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); dock_status_button->setFocusPolicy(Qt::NoFocus); connect(dock_status_button, &QPushButton::clicked, [&] { - Settings::values.use_docked_mode.SetValue(!Settings::values.use_docked_mode.GetValue()); - dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); - OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(), - Settings::values.use_docked_mode.GetValue()); + const bool is_docked = Settings::values.use_docked_mode.GetValue(); + auto& controller_type = Settings::values.players.GetValue()[0].controller_type; + + if (!is_docked && controller_type == Settings::ControllerType::Handheld) { + QMessageBox::warning(this, tr("Invalid config detected"), + tr("Handheld controller can't be used on docked mode. Pro " + "controller will be selected.")); + controller_type = Settings::ControllerType::ProController; + ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get()); + configure_dialog.ApplyConfiguration(); + controller_dialog->refreshConfiguration(); + } + + Settings::values.use_docked_mode.SetValue(!is_docked); + dock_status_button->setChecked(!is_docked); + OnDockedModeChanged(is_docked, !is_docked); }); dock_status_button->setText(tr("DOCK")); dock_status_button->setCheckable(true); @@ -1406,7 +1408,8 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index) { title_name = metadata.first->GetApplicationName(); } if (res != Loader::ResultStatus::Success || title_name.empty()) { - title_name = Common::FS::GetFilename(filename.toStdString()); + title_name = Common::FS::PathToUTF8String( + std::filesystem::path{filename.toStdU16String()}.filename()); } LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); UpdateWindowTitle(title_name, title_version); @@ -1526,7 +1529,7 @@ void GMainWindow::OnGameListLoadFile(QString game_path) { void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target, const std::string& game_path) { - std::string path; + std::filesystem::path path; QString open_target; auto& system = Core::System::GetInstance(); @@ -1555,7 +1558,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target switch (target) { case GameListOpenTarget::SaveData: { open_target = tr("Save Data"); - const std::string nand_dir = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir); + const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir); if (has_user_save) { // User save data @@ -1580,34 +1583,38 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target Service::Account::ProfileManager manager; const auto user_id = manager.GetUser(static_cast<std::size_t>(index)); ASSERT(user_id); - path = nand_dir + FileSys::SaveDataFactory::GetFullPath( - system, FileSys::SaveDataSpaceId::NandUser, - FileSys::SaveDataType::SaveData, program_id, user_id->uuid, 0); + + const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( + system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, + program_id, user_id->uuid, 0); + + path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); } else { // Device save data - path = nand_dir + FileSys::SaveDataFactory::GetFullPath( - system, FileSys::SaveDataSpaceId::NandUser, - FileSys::SaveDataType::SaveData, program_id, {}, 0); + const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath( + system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, + program_id, {}, 0); + + path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path); } - if (!Common::FS::Exists(path)) { - Common::FS::CreateFullPath(path); - Common::FS::CreateDir(path); + if (!Common::FS::CreateDirs(path)) { + LOG_ERROR(Frontend, "Unable to create the directories for save data"); } break; } case GameListOpenTarget::ModData: { open_target = tr("Mod Data"); - const auto load_dir = Common::FS::GetUserPath(Common::FS::UserPath::LoadDir); - path = fmt::format("{}{:016X}", load_dir, program_id); + path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::LoadDir) / + fmt::format("{:016X}", program_id); break; } default: UNIMPLEMENTED(); } - const QString qpath = QString::fromStdString(path); + const QString qpath = QString::fromStdString(Common::FS::PathToUTF8String(path)); const QDir dir(qpath); if (!dir.exists()) { QMessageBox::warning(this, tr("Error Opening %1 Folder").arg(open_target), @@ -1620,33 +1627,35 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target } void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { - const QString shader_dir = - QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)); - const QString transferable_shader_cache_folder_path = - shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); - const QString transferable_shader_cache_file_path = - transferable_shader_cache_folder_path + QDir::separator() + - QString::fromStdString(fmt::format("{:016X}.bin", program_id)); - - if (!QFile::exists(transferable_shader_cache_file_path)) { + const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir); + const auto transferable_shader_cache_folder_path = shader_cache_dir / "opengl" / "transferable"; + const auto transferable_shader_cache_file_path = + transferable_shader_cache_folder_path / fmt::format("{:016X}.bin", program_id); + + if (!Common::FS::Exists(transferable_shader_cache_file_path)) { QMessageBox::warning(this, tr("Error Opening Transferable Shader Cache"), tr("A shader cache for this title does not exist.")); return; } + const auto qt_shader_cache_folder_path = + QString::fromStdString(Common::FS::PathToUTF8String(transferable_shader_cache_folder_path)); + const auto qt_shader_cache_file_path = + QString::fromStdString(Common::FS::PathToUTF8String(transferable_shader_cache_file_path)); + // Windows supports opening a folder with selecting a specified file in explorer. On every other // OS we just open the transferable shader cache folder without preselecting the transferable // shader cache file for the selected game. #if defined(Q_OS_WIN) const QString explorer = QStringLiteral("explorer"); QStringList param; - if (!QFileInfo(transferable_shader_cache_file_path).isDir()) { + if (!QFileInfo(qt_shader_cache_file_path).isDir()) { param << QStringLiteral("/select,"); } - param << QDir::toNativeSeparators(transferable_shader_cache_file_path); + param << QDir::toNativeSeparators(qt_shader_cache_file_path); QProcess::startDetached(explorer, param); #else - QDesktopServices::openUrl(QUrl::fromLocalFile(transferable_shader_cache_folder_path)); + QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_folder_path)); #endif } @@ -1724,8 +1733,8 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT RemoveAddOnContent(program_id, entry_type); break; } - Common::FS::DeleteDirRecursively(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + - DIR_SEP + "game_list"); + void(Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / + "game_list")); game_list->PopulateAsync(UISettings::values.game_dirs); } @@ -1814,21 +1823,17 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ } void GMainWindow::RemoveTransferableShaderCache(u64 program_id) { - const QString shader_dir = - QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)); - const QString transferable_shader_cache_folder_path = - shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); - const QString transferable_shader_cache_file_path = - transferable_shader_cache_folder_path + QDir::separator() + - QString::fromStdString(fmt::format("{:016X}.bin", program_id)); - - if (!QFile::exists(transferable_shader_cache_file_path)) { + const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir); + const auto transferable_shader_cache_file_path = + shader_cache_dir / "opengl" / "transferable" / fmt::format("{:016X}.bin", program_id); + + if (!Common::FS::Exists(transferable_shader_cache_file_path)) { QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"), tr("A shader cache for this title does not exist.")); return; } - if (QFile::remove(transferable_shader_cache_file_path)) { + if (Common::FS::RemoveFile(transferable_shader_cache_file_path)) { QMessageBox::information(this, tr("Successfully Removed"), tr("Successfully removed the transferable shader cache.")); } else { @@ -1838,19 +1843,16 @@ void GMainWindow::RemoveTransferableShaderCache(u64 program_id) { } void GMainWindow::RemoveCustomConfiguration(u64 program_id) { - const QString config_dir = - QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir)); - const QString custom_config_file_path = - config_dir + QStringLiteral("custom") + QDir::separator() + - QString::fromStdString(fmt::format("{:016X}.ini", program_id)); + const auto custom_config_file_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / + "custom" / fmt::format("{:016X}.ini", program_id); - if (!QFile::exists(custom_config_file_path)) { + if (!Common::FS::Exists(custom_config_file_path)) { QMessageBox::warning(this, tr("Error Removing Custom Configuration"), tr("A custom configuration for this title does not exist.")); return; } - if (QFile::remove(custom_config_file_path)) { + if (Common::FS::RemoveFile(custom_config_file_path)) { QMessageBox::information(this, tr("Successfully Removed"), tr("Successfully removed the custom game configuration.")); } else { @@ -1887,8 +1889,10 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa return; } - const auto path = fmt::format( - "{}{:016X}/romfs", Common::FS::GetUserPath(Common::FS::UserPath::DumpDir), *romfs_title_id); + const auto dump_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir); + const auto romfs_dir = fmt::format("{:016X}/romfs", *romfs_title_id); + + const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir); FileSys::VirtualFile romfs; @@ -1966,24 +1970,29 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, } void GMainWindow::OnGameListOpenDirectory(const QString& directory) { - QString path; + std::filesystem::path fs_path; if (directory == QStringLiteral("SDMC")) { - path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) + - "Nintendo/Contents/registered"); + fs_path = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "Nintendo/Contents/registered"; } else if (directory == QStringLiteral("UserNAND")) { - path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + - "user/Contents/registered"); + fs_path = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "user/Contents/registered"; } else if (directory == QStringLiteral("SysNAND")) { - path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + - "system/Contents/registered"); + fs_path = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/Contents/registered"; } else { - path = directory; + fs_path = directory.toStdString(); } - if (!QFileInfo::exists(path)) { - QMessageBox::critical(this, tr("Error Opening %1").arg(path), tr("Folder does not exist!")); + + const auto qt_path = QString::fromStdString(Common::FS::PathToUTF8String(fs_path)); + + if (!Common::FS::IsDir(fs_path)) { + QMessageBox::critical(this, tr("Error Opening %1").arg(qt_path), + tr("Folder does not exist!")); return; } - QDesktopServices::openUrl(QUrl::fromLocalFile(path)); + + QDesktopServices::openUrl(QUrl::fromLocalFile(qt_path)); } void GMainWindow::OnGameListAddDirectory() { @@ -2177,8 +2186,8 @@ void GMainWindow::OnMenuInstallToNAND() { : tr("%n file(s) failed to install\n", "", failed_files.size())); QMessageBox::information(this, tr("Install Results"), install_results); - Common::FS::DeleteDirRecursively(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + - DIR_SEP + "game_list"); + void(Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / + "game_list")); game_list->PopulateAsync(UISettings::values.game_dirs); ui.action_Install_File_NAND->setEnabled(true); } @@ -2578,12 +2587,12 @@ void GMainWindow::OnConfigure() { &GMainWindow::OnLanguageChanged); const auto result = configure_dialog.exec(); - if (result != QDialog::Accepted) { + if (result != QDialog::Accepted && !UISettings::values.configuration_applied) { return; + } else if (result == QDialog::Accepted) { + configure_dialog.ApplyConfiguration(); + controller_dialog->refreshConfiguration(); } - - configure_dialog.ApplyConfiguration(); - controller_dialog->refreshConfiguration(); InitializeHotkeys(); if (UISettings::values.theme != old_theme) { UpdateUITheme(); @@ -2598,6 +2607,8 @@ void GMainWindow::OnConfigure() { game_list->PopulateAsync(UISettings::values.game_dirs); } + UISettings::values.configuration_applied = false; + config->Save(); if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) { @@ -2627,23 +2638,27 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file ConfigurePerGame dialog(this, title_id); dialog.LoadFromFile(v_file); const auto result = dialog.exec(); - if (result == QDialog::Accepted) { + + if (result != QDialog::Accepted && !UISettings::values.configuration_applied) { + Settings::RestoreGlobalState(system.IsPoweredOn()); + return; + } else if (result == QDialog::Accepted) { dialog.ApplyConfiguration(); + } - const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); - if (reload) { - game_list->PopulateAsync(UISettings::values.game_dirs); - } + const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); + if (reload) { + game_list->PopulateAsync(UISettings::values.game_dirs); + } - // Do not cause the global config to write local settings into the config file - const bool is_powered_on = system.IsPoweredOn(); - Settings::RestoreGlobalState(is_powered_on); + // Do not cause the global config to write local settings into the config file + const bool is_powered_on = system.IsPoweredOn(); + Settings::RestoreGlobalState(is_powered_on); - if (!is_powered_on) { - config->Save(); - } - } else { - Settings::RestoreGlobalState(system.IsPoweredOn()); + UISettings::values.configuration_applied = false; + + if (!is_powered_on) { + config->Save(); } } @@ -2694,7 +2709,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) { void GMainWindow::OnOpenYuzuFolder() { QDesktopServices::openUrl(QUrl::fromLocalFile( - QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::UserDir)))); + QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::YuzuDir)))); } void GMainWindow::OnAbout() { @@ -2716,7 +2731,7 @@ void GMainWindow::OnCaptureScreenshot() { const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); const auto screenshot_path = - QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir)); + QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir)); const auto date = QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz")); QString filename = QStringLiteral("%1%2_%3.png") @@ -2745,23 +2760,26 @@ void GMainWindow::OnCaptureScreenshot() { // TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant void GMainWindow::MigrateConfigFiles() { - const std::string& config_dir_str = Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir); - const QDir config_dir = QDir(QString::fromStdString(config_dir_str)); + const auto config_dir_fs_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir); + const QDir config_dir = + QDir(QString::fromStdString(Common::FS::PathToUTF8String(config_dir_fs_path))); const QStringList config_dir_list = config_dir.entryList(QStringList(QStringLiteral("*.ini"))); - Common::FS::CreateFullPath(fmt::format("{}custom" DIR_SEP, config_dir_str)); - for (QStringList::const_iterator it = config_dir_list.constBegin(); - it != config_dir_list.constEnd(); ++it) { + if (!Common::FS::CreateDirs(config_dir_fs_path / "custom")) { + LOG_ERROR(Frontend, "Failed to create new config file directory"); + } + + for (auto it = config_dir_list.constBegin(); it != config_dir_list.constEnd(); ++it) { const auto filename = it->toStdString(); if (filename.find_first_not_of("0123456789abcdefACBDEF", 0) < 16) { continue; } - const auto origin = fmt::format("{}{}", config_dir_str, filename); - const auto destination = fmt::format("{}custom" DIR_SEP "{}", config_dir_str, filename); + const auto origin = config_dir_fs_path / filename; + const auto destination = config_dir_fs_path / "custom" / filename; LOG_INFO(Frontend, "Migrating config file from {} to {}", origin, destination); - if (!Common::FS::Rename(origin, destination)) { + if (!Common::FS::RenameFile(origin, destination)) { // Delete the old config file if one already exists in the new location. - Common::FS::Delete(origin); + void(Common::FS::RemoveFile(origin)); } } } @@ -2953,18 +2971,16 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { if (res == QMessageBox::Cancel) return; - Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) + - "prod.keys_autogenerated"); - Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) + - "console.keys_autogenerated"); - Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) + - "title.keys_autogenerated"); + const auto keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); + + void(Common::FS::RemoveFile(keys_dir / "prod.keys_autogenerated")); + void(Common::FS::RemoveFile(keys_dir / "console.keys_autogenerated")); + void(Common::FS::RemoveFile(keys_dir / "title.keys_autogenerated")); } Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); if (keys.BaseDeriveNecessary()) { - Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory( - Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), FileSys::Mode::Read)}; + Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory("", FileSys::Mode::Read)}; const auto function = [this, &keys, &pdm] { keys.PopulateFromPartitionData(pdm); @@ -3277,12 +3293,17 @@ int main(int argc, char* argv[]) { QCoreApplication::setOrganizationName(QStringLiteral("yuzu team")); QCoreApplication::setApplicationName(QStringLiteral("yuzu")); +#ifdef _WIN32 + // Increases the maximum open file limit to 4096 + _setmaxstdio(4096); +#endif + #ifdef __APPLE__ // If you start a bundle (binary) on OSX without the Terminal, the working directory is "/". // But since we require the working directory to be the executable path for the location of // the user folder in the Qt Frontend, we need to cd into that working directory - const std::string bin_path = Common::FS::GetBundleDirectory() + DIR_SEP + ".."; - chdir(bin_path.c_str()); + const auto bin_path = Common::FS::GetBundleDirectory() / ".."; + chdir(Common::FS::PathToUTF8String(bin_path).c_str()); #endif #ifdef __linux__ diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 5ba00b8c8..49122ec32 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -95,6 +95,8 @@ struct Values { uint8_t row_2_text_id; std::atomic_bool is_game_list_reload_pending{false}; bool cache_game_list; + + bool configuration_applied; }; extern Values values; |