diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 2be45f28d..125cdfdb6 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -161,6 +161,8 @@ void Config::ReadValues() { static_cast(sdl2_config->GetInteger("Layout", "layout_option", 0)); Settings::values.swap_screen = sdl2_config->GetBoolean("Layout", "swap_screen", false); Settings::values.upright_screen = sdl2_config->GetBoolean("Layout", "upright_screen", false); + Settings::values.large_screen_proportion = + sdl2_config->GetReal("Layout", "large_screen_proportion", 4.0); Settings::values.custom_layout = sdl2_config->GetBoolean("Layout", "custom_layout", false); Settings::values.custom_top_left = static_cast(sdl2_config->GetInteger("Layout", "custom_top_left", 0)); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 9cc72ba09..19f57afb8 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -216,6 +216,10 @@ swap_screen = # 0 (default): Off, 1: On upright_screen = +# The proportion between the large and small screens when playing in Large Screen Small Screen layout. +# Must be a real value between 1.0 and 16.0. Default is 4 +large_screen_proportion = + # Dumps textures as PNG to dump/textures/[Title ID]/. # 0 (default): Off, 1: On dump_textures = diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 340a0b7ca..dc130ff36 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -487,6 +487,7 @@ void Config::ReadLayoutValues() { ReadGlobalSetting(Settings::values.layout_option); ReadGlobalSetting(Settings::values.swap_screen); ReadGlobalSetting(Settings::values.upright_screen); + ReadGlobalSetting(Settings::values.large_screen_proportion); if (global) { ReadBasicSetting(Settings::values.mono_render_option); @@ -1004,6 +1005,7 @@ void Config::SaveLayoutValues() { WriteGlobalSetting(Settings::values.layout_option); WriteGlobalSetting(Settings::values.swap_screen); WriteGlobalSetting(Settings::values.upright_screen); + WriteGlobalSetting(Settings::values.large_screen_proportion); if (global) { WriteBasicSetting(Settings::values.mono_render_option); diff --git a/src/citra_qt/configuration/configure_debug.cpp b/src/citra_qt/configuration/configure_debug.cpp index db5b33a63..3a8d12392 100644 --- a/src/citra_qt/configuration/configure_debug.cpp +++ b/src/citra_qt/configuration/configure_debug.cpp @@ -5,6 +5,7 @@ #include #include #include +#include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configure_debug.h" #include "citra_qt/debugger/console.h" #include "citra_qt/uisettings.h" @@ -16,6 +17,17 @@ #include "ui_configure_debug.h" #include "video_core/renderer_vulkan/vk_instance.h" +// The QSlider doesn't have an easy way to set a custom step amount, +// so we can just convert from the sliders range (0 - 79) to the expected +// settings range (5 - 400) with simple math. +static constexpr int SliderToSettings(int value) { + return 5 * value + 5; +} + +static constexpr int SettingsToSlider(int value) { + return (value - 5) / 5; +} + ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(std::make_unique()) { ui->setupUi(this); @@ -60,6 +72,19 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) ui->toggle_cpu_jit->setEnabled(!is_powered_on); ui->toggle_renderer_debug->setEnabled(!is_powered_on); ui->toggle_dump_command_buffers->setEnabled(!is_powered_on); + + // Set a minimum width for the label to prevent the slider from changing size. + // This scales across DPIs. (This value should be enough for "xxx%") + ui->clock_display_label->setMinimumWidth(40); + + connect(ui->slider_clock_speed, &QSlider::valueChanged, this, [&](int value) { + ui->clock_display_label->setText(QStringLiteral("%1%").arg(SliderToSettings(value))); + }); + + ui->clock_speed_label->setVisible(Settings::IsConfiguringGlobal()); + ui->clock_speed_combo->setVisible(!Settings::IsConfiguringGlobal()); + + SetupPerGameUI(); } ConfigureDebug::~ConfigureDebug() = default; @@ -74,6 +99,23 @@ void ConfigureDebug::SetConfiguration() { ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue()); ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug.GetValue()); ui->toggle_dump_command_buffers->setChecked(Settings::values.dump_command_buffers.GetValue()); + + if (!Settings::IsConfiguringGlobal()) { + if (Settings::values.cpu_clock_percentage.UsingGlobal()) { + ui->clock_speed_combo->setCurrentIndex(0); + ui->slider_clock_speed->setEnabled(false); + } else { + ui->clock_speed_combo->setCurrentIndex(1); + ui->slider_clock_speed->setEnabled(true); + } + ConfigurationShared::SetHighlight(ui->clock_speed_widget, + !Settings::values.cpu_clock_percentage.UsingGlobal()); + } + + ui->slider_clock_speed->setValue( + SettingsToSlider(Settings::values.cpu_clock_percentage.GetValue())); + ui->clock_display_label->setText( + QStringLiteral("%1%").arg(Settings::values.cpu_clock_percentage.GetValue())); } void ConfigureDebug::ApplyConfiguration() { @@ -88,6 +130,27 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked(); Settings::values.dump_command_buffers = ui->toggle_dump_command_buffers->isChecked(); + + ConfigurationShared::ApplyPerGameSetting( + &Settings::values.cpu_clock_percentage, ui->clock_speed_combo, + [this](s32) { return SliderToSettings(ui->slider_clock_speed->value()); }); +} + +void ConfigureDebug::SetupPerGameUI() { + // Block the global settings if a game is currently running that overrides them + if (Settings::IsConfiguringGlobal()) { + ui->slider_clock_speed->setEnabled(Settings::values.cpu_clock_percentage.UsingGlobal()); + return; + } + + connect(ui->clock_speed_combo, qOverload(&QComboBox::activated), this, [this](int index) { + ui->slider_clock_speed->setEnabled(index == 1); + ConfigurationShared::SetHighlight(ui->clock_speed_widget, index == 1); + }); + + ui->groupBox->setVisible(false); + ui->groupBox_2->setVisible(false); + ui->toggle_cpu_jit->setVisible(false); } void ConfigureDebug::RetranslateUI() { diff --git a/src/citra_qt/configuration/configure_debug.h b/src/citra_qt/configuration/configure_debug.h index 898f81544..02101016f 100644 --- a/src/citra_qt/configuration/configure_debug.h +++ b/src/citra_qt/configuration/configure_debug.h @@ -21,6 +21,7 @@ public: void ApplyConfiguration(); void RetranslateUI(); void SetConfiguration(); + void SetupPerGameUI(); private: std::unique_ptr ui; diff --git a/src/citra_qt/configuration/configure_debug.ui b/src/citra_qt/configuration/configure_debug.ui index fbac8fecc..a551248a9 100644 --- a/src/citra_qt/configuration/configure_debug.ui +++ b/src/citra_qt/configuration/configure_debug.ui @@ -6,8 +6,8 @@ 0 0 - 454 - 356 + 443 + 358 @@ -107,12 +107,80 @@ - + - Miscellaneous + CPU - - + + + + + + 7 + + + + + + Use global clock speed + + + + + Set clock speed: + + + + + + + + CPU Clock Speed + + + + + + + <html><body>Changes the emulated CPU clock frequency.<br>Underclocking can increase performance but may cause the game to freeze.<br>Overclocking may reduce in game lag but also might cause freezes</body></html> + + + 0 + + + 79 + + + 5 + + + 15 + + + 25 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + <html><head/><body><p>Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes</p></body></html> @@ -142,6 +210,16 @@ + + + + <html><head/><body><p>CPU Clock Speed Information<br/>Underclocking can increase performance but may cause the game to freeze.<br/>Overclocking may reduce in game lag but also might cause freezes</p></body></html> + + + Qt::RichText + + + @@ -163,7 +241,6 @@ log_filter_edit toggle_console open_log_button - toggle_cpu_jit diff --git a/src/citra_qt/configuration/configure_enhancements.cpp b/src/citra_qt/configuration/configure_enhancements.cpp index f48612d34..1c2d6b993 100644 --- a/src/citra_qt/configuration/configure_enhancements.cpp +++ b/src/citra_qt/configuration/configure_enhancements.cpp @@ -69,6 +69,7 @@ void ConfigureEnhancements::SetConfiguration() { static_cast(Settings::values.layout_option.GetValue())); ui->swap_screen->setChecked(Settings::values.swap_screen.GetValue()); ui->upright_screen->setChecked(Settings::values.upright_screen.GetValue()); + ui->large_screen_proportion->setValue(Settings::values.large_screen_proportion.GetValue()); ui->toggle_dump_textures->setChecked(Settings::values.dump_textures.GetValue()); ui->toggle_custom_textures->setChecked(Settings::values.custom_textures.GetValue()); ui->toggle_preload_textures->setChecked(Settings::values.preload_textures.GetValue()); @@ -122,6 +123,7 @@ void ConfigureEnhancements::ApplyConfiguration() { static_cast(ui->layout_combobox->currentIndex()); Settings::values.swap_screen = ui->swap_screen->isChecked(); Settings::values.upright_screen = ui->upright_screen->isChecked(); + Settings::values.large_screen_proportion = ui->large_screen_proportion->value(); Settings::values.dump_textures = ui->toggle_dump_textures->isChecked(); Settings::values.custom_textures = ui->toggle_custom_textures->isChecked(); Settings::values.preload_textures = ui->toggle_preload_textures->isChecked(); diff --git a/src/citra_qt/configuration/configure_enhancements.ui b/src/citra_qt/configuration/configure_enhancements.ui index 0d4ed0161..d8b31af86 100644 --- a/src/citra_qt/configuration/configure_enhancements.ui +++ b/src/citra_qt/configuration/configure_enhancements.ui @@ -295,6 +295,30 @@ + + + + + + Large Screen Proportion: + + + + + + + 1 + + + 16 + + + 4 + + + + + @@ -384,6 +408,7 @@ layout_combobox swap_screen upright_screen + large_screen_proportion bg_button toggle_custom_textures toggle_dump_textures diff --git a/src/citra_qt/configuration/configure_per_game.cpp b/src/citra_qt/configuration/configure_per_game.cpp index fc74e9e4c..506b597ec 100644 --- a/src/citra_qt/configuration/configure_per_game.cpp +++ b/src/citra_qt/configuration/configure_per_game.cpp @@ -9,6 +9,7 @@ #include #include "citra_qt/configuration/config.h" #include "citra_qt/configuration/configure_audio.h" +#include "citra_qt/configuration/configure_debug.h" #include "citra_qt/configuration/configure_general.h" #include "citra_qt/configuration/configure_graphics.h" #include "citra_qt/configuration/configure_per_game.h" @@ -31,6 +32,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString general_tab = std::make_unique(this); graphics_tab = std::make_unique(this); system_tab = std::make_unique(this); + debug_tab = std::make_unique(this); ui->setupUi(this); @@ -38,6 +40,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString ui->tabWidget->addTab(system_tab.get(), tr("System")); ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics")); ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); + ui->tabWidget->addTab(debug_tab.get(), tr("Debug")); setFocusPolicy(Qt::ClickFocus); setWindowTitle(tr("Properties")); @@ -80,6 +83,7 @@ void ConfigurePerGame::ApplyConfiguration() { system_tab->ApplyConfiguration(); graphics_tab->ApplyConfiguration(); audio_tab->ApplyConfiguration(); + debug_tab->ApplyConfiguration(); Settings::LogSettings(); diff --git a/src/citra_qt/configuration/configure_per_game.h b/src/citra_qt/configuration/configure_per_game.h index 3bc2564b4..87addd6e7 100644 --- a/src/citra_qt/configuration/configure_per_game.h +++ b/src/citra_qt/configuration/configure_per_game.h @@ -17,6 +17,7 @@ class ConfigureAudio; class ConfigureGeneral; class ConfigureGraphics; class ConfigureSystem; +class ConfigureDebug; class QGraphicsScene; class QStandardItem; @@ -66,4 +67,5 @@ private: std::unique_ptr general_tab; std::unique_ptr graphics_tab; std::unique_ptr system_tab; + std::unique_ptr debug_tab; }; diff --git a/src/citra_qt/configuration/configure_system.cpp b/src/citra_qt/configuration/configure_system.cpp index 8c4fbe5c5..73afb482a 100644 --- a/src/citra_qt/configuration/configure_system.cpp +++ b/src/citra_qt/configuration/configure_system.cpp @@ -225,17 +225,6 @@ static const std::array country_names = { QT_TRANSLATE_NOOP("ConfigureSystem", "Bermuda"), // 180-186 }; -// The QSlider doesn't have an easy way to set a custom step amount, -// so we can just convert from the sliders range (0 - 79) to the expected -// settings range (5 - 400) with simple math. -static constexpr int SliderToSettings(int value) { - return 5 * value + 5; -} - -static constexpr int SettingsToSlider(int value) { - return (value - 5) / 5; -} - ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(std::make_unique()) { ui->setupUi(this); @@ -255,17 +244,6 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) } } - // Set a minimum width for the label to prevent the slider from changing size. - // This scales across DPIs. (This value should be enough for "xxx%") - ui->clock_display_label->setMinimumWidth(40); - - connect(ui->slider_clock_speed, &QSlider::valueChanged, this, [&](int value) { - ui->clock_display_label->setText(QStringLiteral("%1%").arg(SliderToSettings(value))); - }); - - ui->clock_speed_label->setVisible(Settings::IsConfiguringGlobal()); - ui->clock_speed_combo->setVisible(!Settings::IsConfiguringGlobal()); - SetupPerGameUI(); ui->combo_download_mode->setCurrentIndex(1); // set to Recommended @@ -325,22 +303,6 @@ void ConfigureSystem::SetConfiguration() { ui->label_disable_info->hide(); } - if (!Settings::IsConfiguringGlobal()) { - if (Settings::values.cpu_clock_percentage.UsingGlobal()) { - ui->clock_speed_combo->setCurrentIndex(0); - ui->slider_clock_speed->setEnabled(false); - } else { - ui->clock_speed_combo->setCurrentIndex(1); - ui->slider_clock_speed->setEnabled(true); - } - ConfigurationShared::SetHighlight(ui->clock_speed_widget, - !Settings::values.cpu_clock_percentage.UsingGlobal()); - } - - ui->slider_clock_speed->setValue( - SettingsToSlider(Settings::values.cpu_clock_percentage.GetValue())); - ui->clock_display_label->setText( - QStringLiteral("%1%").arg(Settings::values.cpu_clock_percentage.GetValue())); ui->toggle_new_3ds->setChecked(Settings::values.is_new_3ds.GetValue()); ui->plugin_loader->setChecked(Settings::values.plugin_loader_enabled.GetValue()); ui->allow_plugin_loader->setChecked(Settings::values.allow_plugin_loader.GetValue()); @@ -452,10 +414,6 @@ void ConfigureSystem::ApplyConfiguration() { Settings::values.plugin_loader_enabled.SetValue(ui->plugin_loader->isChecked()); Settings::values.allow_plugin_loader.SetValue(ui->allow_plugin_loader->isChecked()); } - - ConfigurationShared::ApplyPerGameSetting( - &Settings::values.cpu_clock_percentage, ui->clock_speed_combo, - [this](s32) { return SliderToSettings(ui->slider_clock_speed->value()); }); } void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) { @@ -534,7 +492,6 @@ void ConfigureSystem::SetupPerGameUI() { // Block the global settings if a game is currently running that overrides them if (Settings::IsConfiguringGlobal()) { ui->toggle_new_3ds->setEnabled(Settings::values.is_new_3ds.UsingGlobal()); - ui->slider_clock_speed->setEnabled(Settings::values.cpu_clock_percentage.UsingGlobal()); return; } @@ -568,11 +525,6 @@ void ConfigureSystem::SetupPerGameUI() { ui->plugin_loader->setVisible(false); ui->allow_plugin_loader->setVisible(false); - connect(ui->clock_speed_combo, qOverload(&QComboBox::activated), this, [this](int index) { - ui->slider_clock_speed->setEnabled(index == 1); - ConfigurationShared::SetHighlight(ui->clock_speed_widget, index == 1); - }); - ConfigurationShared::SetColoredTristate(ui->toggle_new_3ds, Settings::values.is_new_3ds, is_new_3ds); } diff --git a/src/citra_qt/configuration/configure_system.ui b/src/citra_qt/configuration/configure_system.ui index 02acf35c5..064ce3835 100644 --- a/src/citra_qt/configuration/configure_system.ui +++ b/src/citra_qt/configuration/configure_system.ui @@ -6,7 +6,7 @@ 0 0 - 525 + 535 619 @@ -274,7 +274,7 @@ - + days @@ -287,7 +287,7 @@ - + HH:mm:ss @@ -341,25 +341,25 @@ - - - 3GX Plugin Loader: - - + + + 3GX Plugin Loader: + + - - - Enable 3GX plugin loader - - + + + Enable 3GX plugin loader + + - - - Allow games to change plugin loader state - - + + + Allow games to change plugin loader state + + @@ -410,86 +410,6 @@ - - - - Advanced - - - - - - - 7 - - - - - - Use global clock speed - - - - - Set clock speed: - - - - - - - - CPU Clock Speed - - - - - - - <html><body>Changes the emulated CPU clock frequency.<br>Underclocking can increase performance but may cause the game to freeze.<br>Overclocking may reduce in game lag but also might cause freezes</body></html> - - - 0 - - - 79 - - - 5 - - - 15 - - - 25 - - - Qt::Horizontal - - - QSlider::TicksBelow - - - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - @@ -500,16 +420,6 @@ - - - - <html><head/><body><p>CPU Clock Speed Information<br/>Underclocking can increase performance but may cause the game to freeze.<br/>Overclocking may reduce in game lag but also might cause freezes</p></body></html> - - - Qt::RichText - - - diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui index b3e36a978..4c88252eb 100644 --- a/src/citra_qt/main.ui +++ b/src/citra_qt/main.ui @@ -100,8 +100,6 @@ - - @@ -189,6 +187,8 @@ + + diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 9aa4c4813..11248bd7a 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -127,6 +127,7 @@ void LogSettings() { log_setting("Layout_LayoutOption", values.layout_option.GetValue()); log_setting("Layout_SwapScreen", values.swap_screen.GetValue()); log_setting("Layout_UprightScreen", values.upright_screen.GetValue()); + log_setting("Layout_LargeScreenProportion", values.large_screen_proportion.GetValue()); log_setting("Utility_DumpTextures", values.dump_textures.GetValue()); log_setting("Utility_CustomTextures", values.custom_textures.GetValue()); log_setting("Utility_UseDiskShaderCache", values.use_disk_shader_cache.GetValue()); @@ -204,6 +205,7 @@ void RestoreGlobalState(bool is_powered_on) { values.layout_option.SetGlobal(true); values.swap_screen.SetGlobal(true); values.upright_screen.SetGlobal(true); + values.large_screen_proportion.SetGlobal(true); values.bg_red.SetGlobal(true); values.bg_green.SetGlobal(true); values.bg_blue.SetGlobal(true); diff --git a/src/common/settings.h b/src/common/settings.h index 50f30a7d7..6279a4878 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -466,6 +466,8 @@ struct Values { SwitchableSetting layout_option{LayoutOption::Default, "layout_option"}; SwitchableSetting swap_screen{false, "swap_screen"}; SwitchableSetting upright_screen{false, "upright_screen"}; + SwitchableSetting large_screen_proportion{4.f, 1.f, 16.f, + "large_screen_proportion"}; Setting custom_layout{false, "custom_layout"}; Setting custom_top_left{0, "custom_top_left"}; Setting custom_top_top{0, "custom_top_top"}; diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index acb81fdaa..2b37a2494 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -197,7 +197,8 @@ void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height, case Settings::LayoutOption::LargeScreen: layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), - Settings::values.upright_screen.GetValue()); + Settings::values.upright_screen.GetValue(), + Settings::values.large_screen_proportion.GetValue()); break; case Settings::LayoutOption::SideScreen: layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen.GetValue(), diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index 75f330f18..092812053 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -231,7 +231,8 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool up return res; } -FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upright) { +FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upright, + float scale_factor) { ASSERT(width > 0); ASSERT(height > 0); @@ -244,25 +245,29 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr float small_screen_aspect_ratio; if (upright) { if (swapped) { - emulation_aspect_ratio = (Core::kScreenBottomWidth * 4.0f + Core::kScreenTopWidth) / - (Core::kScreenBottomHeight * 4); + emulation_aspect_ratio = + (Core::kScreenBottomWidth * scale_factor + Core::kScreenTopWidth) / + (Core::kScreenBottomHeight * scale_factor); large_screen_aspect_ratio = BOT_SCREEN_UPRIGHT_ASPECT_RATIO; small_screen_aspect_ratio = TOP_SCREEN_UPRIGHT_ASPECT_RATIO; } else { - emulation_aspect_ratio = (Core::kScreenTopWidth * 4.0f + Core::kScreenBottomWidth) / - (Core::kScreenTopHeight * 4); + emulation_aspect_ratio = + (Core::kScreenTopWidth * scale_factor + Core::kScreenBottomWidth) / + (Core::kScreenTopHeight * scale_factor); large_screen_aspect_ratio = TOP_SCREEN_UPRIGHT_ASPECT_RATIO; small_screen_aspect_ratio = BOT_SCREEN_UPRIGHT_ASPECT_RATIO; } } else { if (swapped) { - emulation_aspect_ratio = Core::kScreenBottomHeight * 4 / - (Core::kScreenBottomWidth * 4.0f + Core::kScreenTopWidth); + emulation_aspect_ratio = + Core::kScreenBottomHeight * scale_factor / + (Core::kScreenBottomWidth * scale_factor + Core::kScreenTopWidth); large_screen_aspect_ratio = BOT_SCREEN_ASPECT_RATIO; small_screen_aspect_ratio = TOP_SCREEN_ASPECT_RATIO; } else { - emulation_aspect_ratio = Core::kScreenTopHeight * 4 / - (Core::kScreenTopWidth * 4.0f + Core::kScreenBottomWidth); + emulation_aspect_ratio = + Core::kScreenTopHeight * scale_factor / + (Core::kScreenTopWidth * scale_factor + Core::kScreenBottomWidth); large_screen_aspect_ratio = TOP_SCREEN_ASPECT_RATIO; small_screen_aspect_ratio = BOT_SCREEN_ASPECT_RATIO; } @@ -271,7 +276,7 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr Common::Rectangle screen_window_area{0, 0, width, height}; Common::Rectangle total_rect = maxRectangle(screen_window_area, emulation_aspect_ratio); Common::Rectangle large_screen = maxRectangle(total_rect, large_screen_aspect_ratio); - Common::Rectangle fourth_size_rect = total_rect.Scale(.25f); + Common::Rectangle fourth_size_rect = total_rect.Scale(1.f / scale_factor); Common::Rectangle small_screen = maxRectangle(fourth_size_rect, small_screen_aspect_ratio); if (window_aspect_ratio < emulation_aspect_ratio) { @@ -416,22 +421,35 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar if (Settings::values.upright_screen.GetValue()) { if (Settings::values.swap_screen.GetValue()) { width = Core::kScreenBottomHeight * res_scale; - height = (Core::kScreenBottomWidth + Core::kScreenTopWidth / 4) * res_scale; + height = (Core::kScreenBottomWidth + + Core::kScreenTopWidth / + Settings::values.large_screen_proportion.GetValue()) * + res_scale; } else { width = Core::kScreenTopHeight * res_scale; - height = (Core::kScreenTopWidth + Core::kScreenBottomWidth / 4) * res_scale; + height = (Core::kScreenTopWidth + + Core::kScreenBottomWidth / + Settings::values.large_screen_proportion.GetValue()) * + res_scale; } } else { if (Settings::values.swap_screen.GetValue()) { - width = (Core::kScreenBottomWidth + Core::kScreenTopWidth / 4) * res_scale; + width = (Core::kScreenBottomWidth + + Core::kScreenTopWidth / + Settings::values.large_screen_proportion.GetValue()) * + res_scale; height = Core::kScreenBottomHeight * res_scale; } else { - width = (Core::kScreenTopWidth + Core::kScreenBottomWidth / 4) * res_scale; + width = (Core::kScreenTopWidth + + Core::kScreenBottomWidth / + Settings::values.large_screen_proportion.GetValue()) * + res_scale; height = Core::kScreenTopHeight * res_scale; } } layout = LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), - Settings::values.upright_screen.GetValue()); + Settings::values.upright_screen.GetValue(), + Settings::values.large_screen_proportion.GetValue()); break; case Settings::LayoutOption::SideScreen: if (Settings::values.upright_screen.GetValue()) { @@ -576,9 +594,12 @@ std::pair GetMinimumSizeFromLayout(Settings::LayoutOption la min_height = Core::kScreenBottomHeight; break; case Settings::LayoutOption::LargeScreen: - min_width = Settings::values.swap_screen - ? Core::kScreenTopWidth / 4 + Core::kScreenBottomWidth - : Core::kScreenTopWidth + Core::kScreenBottomWidth / 4; + min_width = + Settings::values.swap_screen + ? Core::kScreenTopWidth / Settings::values.large_screen_proportion.GetValue() + + Core::kScreenBottomWidth + : Core::kScreenTopWidth + Core::kScreenBottomWidth / + Settings::values.large_screen_proportion.GetValue(); min_height = Core::kScreenBottomHeight; break; case Settings::LayoutOption::SideScreen: diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index 763262663..36a983ee3 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h @@ -83,9 +83,11 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool is_swapped, bool * @param width Window framebuffer width in pixels * @param height Window framebuffer height in pixels * @param is_swapped if true, the bottom screen will be the large display + * @param scale_factor The ratio between the large screen with respect to the smaller screen * @return Newly created FramebufferLayout object with default screen regions initialized */ -FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool is_swapped, bool upright); +FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool is_swapped, bool upright, + float scale_factor); /** * Factory method for constructing a Frame with the Top screen and bottom diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 4578ffc23..af4532b75 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1087,7 +1087,11 @@ void Module::Interface::DeleteTicket(Kernel::HLERequestContext& ctx) { void Module::Interface::GetNumTickets(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x0008, 0, 0); // 0x00080000 + u32 ticket_count = 0; + for (const auto& title_list : am->am_title_list) { + ticket_count += static_cast(title_list.size()); + } IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); @@ -1101,9 +1105,18 @@ void Module::Interface::GetTicketList(Kernel::HLERequestContext& ctx) { u32 ticket_index = rp.Pop(); auto& ticket_tids_out = rp.PopMappedBuffer(); + u32 tickets_written = 0; + for (const auto& title_list : am->am_title_list) { + const auto tickets_to_write = + std::min(static_cast(title_list.size()), ticket_list_count - tickets_written); + ticket_tids_out.Write(title_list.data(), tickets_written * sizeof(u64), + tickets_to_write * sizeof(u64)); + tickets_written += tickets_to_write; + } + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); - rb.Push(ticket_list_count); + rb.Push(tickets_written); rb.PushMappedBuffer(ticket_tids_out); LOG_WARNING(Service_AM, "(STUBBED) ticket_list_count=0x{:08x}, ticket_index=0x{:08x}", ticket_list_count, ticket_index); diff --git a/src/core/hle/service/nim/nim_u.cpp b/src/core/hle/service/nim/nim_u.cpp index b44ef3539..7a677aa85 100644 --- a/src/core/hle/service/nim/nim_u.cpp +++ b/src/core/hle/service/nim/nim_u.cpp @@ -3,9 +3,9 @@ // Refer to the license.txt file included. #include "common/archives.h" +#include "common/string_util.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" #include "core/hle/service/nim/nim_u.h" SERVICE_CONSTRUCT_IMPL(Service::NIM::NIM_U) @@ -13,37 +13,736 @@ SERIALIZE_EXPORT_IMPL(Service::NIM::NIM_U) namespace Service::NIM { +enum class SystemUpdateState : u32 { + NotInitialized, + StartingSystemUpdate, + FetchingHashAndAccountStatus, + InstallingNewTickets, + InstallingTitles, + UpdateComplete, + SystemUpdatesDisabled, + Unknown7, + Unknown8, +}; + +struct SystemUpdateProgress { + SystemUpdateState state; + u32 last_operation_result; + u64 current_title_downloaded_bytes; + u64 current_title_total_bytes; + u64 titles_downloaded; + u64 titles_total; +}; + +static_assert(sizeof(SystemUpdateProgress) == 0x28, "SystemUpdateProgress structure size is wrong"); + +enum class TitleDownloadState : u32 { + NotInitialized, + StartingTitleDownload, + InstallingTmd, + CommittingTmd, + InstallingContents, + ContentsInstalled, + CommittingTitles, + Finished, + Unknown8, + Unknown9, + BackgroundDownloadFailed, +}; + +struct TitleDownloadProgress { + TitleDownloadState state; + u32 last_operation_result; + u64 downloaded_bytes; + u64 total_bytes; +}; + +static_assert(sizeof(TitleDownloadProgress) == 0x18, + "TitleDownloadProgress structure size is wrong"); + +struct TitleDownloadConfig { + u64 title_id; + u32 title_version; + u32 unknown_1; + u8 age_rating; + u8 media_type; + INSERT_PADDING_BYTES(2); + u32 unknown_2; +}; + +static_assert(sizeof(TitleDownloadConfig) == 0x18, "TitleDownloadConfig structure size is wrong"); + +#pragma pack(1) +struct BackgroundTitleDownloadConfig { + TitleDownloadConfig base_config; + u8 unknown_1; + u8 unknown_2; + INSERT_PADDING_BYTES(6); + u64 requester_title_id; + std::array title_name; + u16 title_name_terminator; + std::array developer_name; + u16 developer_name_terminator; +}; + +static_assert(sizeof(BackgroundTitleDownloadConfig) == 0x104, + "BackgroundTitleDownloadConfig structure size is wrong"); + +struct BackgroundTitleDownloadTaskInfo { + BackgroundTitleDownloadConfig config; + INSERT_PADDING_BYTES(4); + TitleDownloadProgress progress; +}; + +static_assert(sizeof(BackgroundTitleDownloadTaskInfo) == 0x120, + "BackgroundTitleDownloadTaskInfo structure size is wrong"); + +struct AutoTitleDownloadTaskInfo { + u64 task_id; + u64 title_id; + u32 title_version; + u8 unknown_4[0x14]; + u64 required_size; + u32 last_operation_result_code; + u32 last_operation_customer_support_code; + std::array title_name; + u16 title_name_terminator; + std::array developer_name; + u16 developer_name_terminator; + u8 unknown_5[0x24]; +}; + +static_assert(sizeof(AutoTitleDownloadTaskInfo) == 0x138, + "AutoTitleDownloadTaskInfo structure size is wrong"); + +struct AutoDbgDat { + u8 unknown_1[0x4]; + u32 num_auto_download_tasks; + u8 unknown_2[0x100]; +}; + +static_assert(sizeof(AutoDbgDat) == 0x108, "AutoDbgDat structure size is wrong"); + NIM_U::NIM_U(Core::System& system) : ServiceFramework("nim:u", 2) { const FunctionInfo functions[] = { - {0x00010000, nullptr, "StartSysUpdate"}, - {0x00020000, nullptr, "GetUpdateDownloadProgress"}, - {0x00040000, nullptr, "FinishTitlesInstall"}, - {0x00050000, &NIM_U::CheckForSysUpdateEvent, "CheckForSysUpdateEvent"}, - {0x00090000, &NIM_U::CheckSysUpdateAvailable, "CheckSysUpdateAvailable"}, - {0x000A0000, nullptr, "GetState"}, - {0x000B0000, nullptr, "GetSystemTitleHash"}, + {0x00010000, &NIM_U::StartNetworkUpdate, "StartNetworkUpdate"}, + {0x00020000, &NIM_U::GetProgress, "GetProgress"}, + {0x00030000, &NIM_U::Cancel, "Cancel"}, + {0x00040000, &NIM_U::CommitSystemTitles, "CommitSystemTitles"}, + {0x00050000, &NIM_U::GetBackgroundEventForMenu, "GetBackgroundEventForMenu"}, + {0x00060000, &NIM_U::GetBackgroundEventForNews, "GetBackgroundEventForNews"}, + {0x00070000, &NIM_U::FormatSaveData, "FormatSaveData"}, + {0x00080000, &NIM_U::GetCustomerSupportCode, "GetCustomerSupportCode"}, + {0x00090000, &NIM_U::IsCommittableAllSystemTitles, "IsCommittableAllSystemTitles"}, + {0x000A0000, &NIM_U::GetBackgroundProgress, "GetBackgroundProgress"}, + {0x000B0000, &NIM_U::GetSavedHash, "GetSavedHash"}, + {0x000C0082, &NIM_U::UnregisterTask, "UnregisterTask"}, + {0x000D0080, &NIM_U::IsRegistered, "IsRegistered"}, + {0x000E0080, &NIM_U::FindTaskInfo, "FindTaskInfo"}, + {0x000F0042, &NIM_U::GetTaskInfos, "GetTaskInfos"}, + {0x00100000, &NIM_U::DeleteUnmanagedContexts, "DeleteUnmanagedContexts"}, + {0x00110000, &NIM_U::UpdateAutoTitleDownloadTasksAsync, + "UpdateAutoTitleDownloadTasksAsync"}, + {0x00120000, &NIM_U::StartPendingAutoTitleDownloadTasksAsync, + "StartPendingAutoTitleDownloadTasksAsync"}, + {0x00130000, &NIM_U::GetAsyncResult, "GetAsyncResult"}, + {0x00140000, &NIM_U::CancelAsyncCall, "CancelAsyncCall"}, + {0x00150000, &NIM_U::IsPendingAutoTitleDownloadTasks, "IsPendingAutoTitleDownloadTasks"}, + {0x00160000, &NIM_U::GetNumAutoTitleDownloadTasks, "GetNumAutoTitleDownloadTasks"}, + {0x00170042, &NIM_U::GetAutoTitleDownloadTaskInfos, "GetAutoTitleDownloadTaskInfos"}, + {0x00180080, &NIM_U::CancelAutoTitleDownloadTask, "CancelAutoTitleDownloadTask"}, + {0x00190002, &NIM_U::SetAutoDbgDat, "SetAutoDbgDat"}, + {0x001A0002, &NIM_U::GetAutoDbgDat, "GetAutoDbgDat"}, + {0x001B0042, &NIM_U::SetDbgTasks, "SetDbgTasks"}, + {0x001C0042, &NIM_U::GetDbgTasks, "GetDbgTasks"}, + {0x001D0000, &NIM_U::DeleteDbgData, "DeleteDbgData"}, + {0x001E0042, &NIM_U::SetTslXml, "SetTslXml"}, + {0x001F0000, &NIM_U::GetTslXmlSize, "GetTslXmlSize"}, + {0x00200042, &NIM_U::GetTslXml, "GetTslXml"}, + {0x00210000, &NIM_U::DeleteTslXml, "DeleteTslXml"}, + {0x00220042, &NIM_U::SetDtlXml, "SetDtlXml"}, + {0x00230000, &NIM_U::GetDtlXmlSize, "GetDtlXmlSize"}, + {0x00240042, &NIM_U::GetDtlXml, "GetDtlXml"}, + {0x00250000, &NIM_U::UpdateAccountStatus, "UpdateAccountStatus"}, + {0x00260180, &NIM_U::StartTitleDownload, "StartTitleDownload"}, + {0x00270000, &NIM_U::StopTitleDownload, "StopTitleDownload"}, + {0x00280000, &NIM_U::GetTitleDownloadProgress, "GetTitleDownloadProgress"}, + {0x00290246, &NIM_U::RegisterTask, "RegisterTask"}, + {0x002A0000, &NIM_U::IsSystemUpdateAvailable, "IsSystemUpdateAvailable"}, + {0x002B0000, &NIM_U::Unknown2B, "Unknown2B"}, + {0x002C0000, &NIM_U::UpdateTickets, "UpdateTickets"}, + {0x002D00C0, &NIM_U::DownloadTitleSeedAsync, "DownloadTitleSeedAsync"}, + {0x002E0000, &NIM_U::DownloadMissingTitleSeedsAsync, "DownloadMissingTitleSeedsAsync"}, }; RegisterHandlers(functions); - nim_system_update_event = - system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "NIM System Update Event"); + nim_system_update_event_for_menu = + system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "NIM System Update Event (Menu)"); + nim_system_update_event_for_news = + system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "NIM System Update Event (News)"); + nim_async_completion_event = + system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "NIM Async Completion Event"); } NIM_U::~NIM_U() = default; -void NIM_U::CheckForSysUpdateEvent(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx, 0x5, 0, 0); // 0x50000 - IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); +void NIM_U::StartNetworkUpdate(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x1, 0, 0); // 0x10000 + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(nim_system_update_event); - LOG_TRACE(Service_NIM, "called"); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); } -void NIM_U::CheckSysUpdateAvailable(Kernel::HLERequestContext& ctx) { +void NIM_U::GetProgress(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2, 0, 0); // 0x20000 + + SystemUpdateProgress progress{}; + std::memset(&progress, 0, sizeof(progress)); + + IPC::RequestBuilder rb = rp.MakeBuilder(13, 0); + rb.Push(RESULT_SUCCESS); + rb.PushRaw(progress); + rb.Push(0); + rb.Push(0); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::Cancel(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x3, 0, 0); // 0x30000 + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::CommitSystemTitles(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x4, 0, 0); // 0x40000 + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::GetBackgroundEventForMenu(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x5, 0, 0); // 0x50000 + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(nim_system_update_event_for_menu); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::GetBackgroundEventForNews(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x6, 0, 0); // 0x60000 + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(nim_system_update_event_for_news); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::FormatSaveData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x7, 0, 0); // 0x70000 + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::GetCustomerSupportCode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x8, 0, 0); // 0x80000 + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(0); // Customer support code + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::IsCommittableAllSystemTitles(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x9, 0, 0); // 0x90000 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); - rb.Push(false); // No update available + rb.Push(false); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::GetBackgroundProgress(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0xA, 0, 0); // 0xA0000 + + SystemUpdateProgress progress{}; + std::memset(&progress, 0, sizeof(progress)); + + IPC::RequestBuilder rb = rp.MakeBuilder(13, 0); + rb.Push(RESULT_SUCCESS); + rb.PushRaw(progress); + rb.Push(0); + rb.Push(0); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::GetSavedHash(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0xB, 0, 0); // 0xB0000 + + std::array hash{}; + std::memset(&hash, 0, sizeof(hash)); + + IPC::RequestBuilder rb = rp.MakeBuilder(10, 0); + rb.Push(RESULT_SUCCESS); + rb.PushRaw(hash); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::UnregisterTask(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0xC, 2, 2); // 0xC0082 + + const u64 title_id = rp.Pop(); + const u32 process_id = rp.PopPID(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::NIM, ErrorSummary::NotFound, + ErrorLevel::Status)); + + LOG_WARNING(Service_NIM, "(STUBBED) called title_id={:016X}, process_id={:08X}", title_id, + process_id); +} + +void NIM_U::IsRegistered(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0xD, 2, 0); // 0xD0080 + + const u64 title_id = rp.Pop(); + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(false); + + LOG_WARNING(Service_NIM, "(STUBBED) called title_id={:016X}", title_id); +} + +void NIM_U::FindTaskInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0xE, 2, 0); // 0xE0080 + + const u64 title_id = rp.Pop(); + + std::vector buffer(0x120, 0); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::NIM, ErrorSummary::NotFound, + ErrorLevel::Status)); + rb.PushStaticBuffer(std::move(buffer), 0); + + LOG_WARNING(Service_NIM, "(STUBBED) called title_id={:016X}", title_id); +} + +void NIM_U::GetTaskInfos(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0xF, 1, 2); // 0xF0042 + + const u64 max_task_infos = rp.Pop(); + auto& task_infos_buffer = rp.PopMappedBuffer(); + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); + rb.Push(RESULT_SUCCESS); + rb.Push(0); + rb.PushMappedBuffer(task_infos_buffer); + + LOG_WARNING(Service_NIM, "(STUBBED) called max_task_infos={:08X}, task_infos_buffer=0x{:08X}", + max_task_infos, task_infos_buffer.GetId()); +} + +void NIM_U::DeleteUnmanagedContexts(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x10, 0, 0); // 0x100000 + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::UpdateAutoTitleDownloadTasksAsync(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x11, 0, 0); // 0x110000 + + // Since this is a stub, signal the completion event so the caller won't get stuck waiting. + nim_async_completion_event->Signal(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(nim_async_completion_event); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::StartPendingAutoTitleDownloadTasksAsync(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x11, 0, 0); // 0x120000 + + // Since this is a stub, signal the completion event so the caller won't get stuck waiting. + nim_async_completion_event->Signal(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(nim_async_completion_event); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::GetAsyncResult(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x13, 0, 0); // 0x130000 + + IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(RESULT_SUCCESS); + rb.Push(0); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::CancelAsyncCall(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x14, 0, 0); // 0x140000 + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::IsPendingAutoTitleDownloadTasks(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x15, 0, 0); // 0x150000 + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(false); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::GetNumAutoTitleDownloadTasks(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x16, 0, 0); // 0x160000 + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(0); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::GetAutoTitleDownloadTaskInfos(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x17, 1, 2); // 0x170042 + + const u64 max_task_infos = rp.Pop(); + auto& task_infos_buffer = rp.PopMappedBuffer(); + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); + rb.Push(RESULT_SUCCESS); + rb.Push(0); + rb.PushMappedBuffer(task_infos_buffer); + + LOG_WARNING(Service_NIM, "(STUBBED) called max_task_infos={:08X}, task_infos_buffer=0x{:08X}", + max_task_infos, task_infos_buffer.GetId()); +} + +void NIM_U::CancelAutoTitleDownloadTask(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x18, 2, 0); // 0x180080 + + const u64 task_id = rp.Pop(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_NIM, "(STUBBED) called task_id={:016X}", task_id); +} + +void NIM_U::SetAutoDbgDat(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x19, 0, 2); // 0x190002 + + auto& auto_dbg_dat_buffer = rp.PopMappedBuffer(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushMappedBuffer(auto_dbg_dat_buffer); + + LOG_WARNING(Service_NIM, "(STUBBED) called auto_dbg_dat_buffer=0x{:08X}", + auto_dbg_dat_buffer.GetId()); +} + +void NIM_U::GetAutoDbgDat(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x1A, 0, 2); // 0x1A0002 + + auto& auto_dbg_dat_buffer = rp.PopMappedBuffer(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushMappedBuffer(auto_dbg_dat_buffer); + + LOG_WARNING(Service_NIM, "(STUBBED) called auto_dbg_dat_buffer=0x{:08X}", + auto_dbg_dat_buffer.GetId()); +} + +void NIM_U::SetDbgTasks(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x1B, 1, 2); // 0x1B0042 + + const u64 max_task_infos = rp.Pop(); + auto& task_infos_buffer = rp.PopMappedBuffer(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushMappedBuffer(task_infos_buffer); + + LOG_WARNING(Service_NIM, "(STUBBED) called max_task_infos={:08X}, task_infos_buffer=0x{:08X}", + max_task_infos, task_infos_buffer.GetId()); +} + +void NIM_U::GetDbgTasks(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x1C, 1, 2); // 0x1C0042 + + const u64 max_task_infos = rp.Pop(); + auto& task_infos_buffer = rp.PopMappedBuffer(); + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); + rb.Push(RESULT_SUCCESS); + rb.Push(0); + rb.PushMappedBuffer(task_infos_buffer); + + LOG_WARNING(Service_NIM, "(STUBBED) called max_task_infos={:08X}, task_infos_buffer=0x{:08X}", + max_task_infos, task_infos_buffer.GetId()); +} + +void NIM_U::DeleteDbgData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x1D, 0, 0); // 0x1D0000 + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::SetTslXml(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x1E, 1, 2); // 0x1E0042 + + const u32 buffer_size = rp.Pop(); + auto& xml_buffer = rp.PopMappedBuffer(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushMappedBuffer(xml_buffer); + + LOG_WARNING(Service_NIM, "(STUBBED) called buffer_size={:08X}, xml_buffer=0x{:08X}", + buffer_size, xml_buffer.GetId()); +} + +void NIM_U::GetTslXmlSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x1F, 0, 0); // 0x1F0000 + + IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(0); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::GetTslXml(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x20, 1, 2); // 0x200042 + + const u32 buffer_capacity = rp.Pop(); + auto& xml_buffer = rp.PopMappedBuffer(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushMappedBuffer(xml_buffer); + + LOG_WARNING(Service_NIM, "(STUBBED) called buffer_capacity={:08X}, xml_buffer=0x{:08X}", + buffer_capacity, xml_buffer.GetId()); +} + +void NIM_U::DeleteTslXml(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x21, 0, 0); // 0x210000 + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::SetDtlXml(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x22, 1, 2); // 0x220042 + + const u32 buffer_size = rp.Pop(); + auto& xml_buffer = rp.PopMappedBuffer(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushMappedBuffer(xml_buffer); + + LOG_WARNING(Service_NIM, "(STUBBED) called buffer_size={:08X}, xml_buffer=0x{:08X}", + buffer_size, xml_buffer.GetId()); +} + +void NIM_U::GetDtlXmlSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x23, 0, 0); // 0x230000 + + IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(0); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::GetDtlXml(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x24, 1, 2); // 0x240042 + + const u32 buffer_capacity = rp.Pop(); + auto& xml_buffer = rp.PopMappedBuffer(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushMappedBuffer(xml_buffer); + + LOG_WARNING(Service_NIM, "(STUBBED) called buffer_capacity={:08X}, xml_buffer=0x{:08X}", + buffer_capacity, xml_buffer.GetId()); +} + +void NIM_U::UpdateAccountStatus(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x25, 0, 0); // 0x250000 + + IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(RESULT_SUCCESS); + rb.Push(0); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::StartTitleDownload(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x26, 6, 0); // 0x260180 + + const auto& download_config = rp.PopRaw(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_NIM, "(STUBBED) called title_id={:016X}", download_config.title_id); +} + +void NIM_U::StopTitleDownload(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x27, 0, 0); // 0x270000 + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::GetTitleDownloadProgress(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x28, 0, 0); // 0x280000 + + TitleDownloadProgress progress{}; + std::memset(&progress, 0, sizeof(progress)); + + IPC::RequestBuilder rb = rp.MakeBuilder(9, 0); + rb.Push(RESULT_SUCCESS); + rb.PushRaw(progress); + rb.Push(0); + rb.Push(0); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::RegisterTask(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x29, 9, 6); // 0x290246 + + const auto& download_config = rp.PopRaw(); + const u32 unknown_1 = rp.Pop(); + const u32 unknown_2 = rp.Pop(); + const u32 unknown_3 = rp.Pop(); + const u32 pid = rp.PopPID(); + const auto& title_name = rp.PopStaticBuffer(); + const auto& developer_name = rp.PopStaticBuffer(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + const auto title_name_end = std::find(title_name.begin(), title_name.end(), u'\0'); + const auto title_name_utf8 = + Common::UTF16ToUTF8(std::u16string{title_name.begin(), title_name_end}); + + const auto developer_name_end = std::find(developer_name.begin(), developer_name.end(), u'\0'); + const auto developer_name_utf8 = + Common::UTF16ToUTF8(std::u16string{developer_name.begin(), developer_name_end}); + + LOG_WARNING(Service_NIM, + "(STUBBED) called title_id={:016X}, unknown_1={:08X}, unknown_2={:08X}, " + "unknown_3={:08X}, pid={:08X}, title_name='{}', developer_name='{}'", + download_config.title_id, unknown_1, unknown_2, unknown_3, pid, title_name_utf8, + developer_name_utf8); +} + +void NIM_U::IsSystemUpdateAvailable(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2A, 0, 0); // 0x2A0000 + + IPC::RequestBuilder rb = rp.MakeBuilder(4, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(RESULT_SUCCESS); + rb.Push(0); + rb.Push(false); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::Unknown2B(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2B, 0, 0); // 0x2B0000 + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::UpdateTickets(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2C, 0, 0); // 0x2C0000 + + IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(RESULT_SUCCESS); + rb.Push(0); + + LOG_WARNING(Service_NIM, "(STUBBED) called"); +} + +void NIM_U::DownloadTitleSeedAsync(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2D, 3, 0); // 0x2D00C0 + + const u64 title_id = rp.Pop(); + const u16 country_code = rp.Pop(); + + // Since this is a stub, signal the completion event so the caller won't get stuck waiting. + nim_async_completion_event->Signal(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(nim_async_completion_event); + + LOG_WARNING(Service_NIM, "(STUBBED) called title_id={:016X}, country_code={:04X}", title_id, + country_code); +} + +void NIM_U::DownloadMissingTitleSeedsAsync(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2E, 0, 0); // 0x2E0000 + + // Since this is a stub, signal the completion event so the caller won't get stuck waiting. + nim_async_completion_event->Signal(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(nim_async_completion_event); LOG_WARNING(Service_NIM, "(STUBBED) called"); } diff --git a/src/core/hle/service/nim/nim_u.h b/src/core/hle/service/nim/nim_u.h index 98fec69b2..c3042e985 100644 --- a/src/core/hle/service/nim/nim_u.h +++ b/src/core/hle/service/nim/nim_u.h @@ -20,32 +20,522 @@ public: private: /** - * NIM::CheckForSysUpdateEvent service function + * NIM::StartNetworkUpdate service function * Inputs: * 1 : None * Outputs: * 1 : Result of function, 0 on success, otherwise error code - * 2 : Copy handle descriptor - * 3 : System Update event handle */ - void CheckForSysUpdateEvent(Kernel::HLERequestContext& ctx); + void StartNetworkUpdate(Kernel::HLERequestContext& ctx); /** - * NIM::CheckSysUpdateAvailable service function + * NIM::GetProgress service function * Inputs: * 1 : None * Outputs: * 1 : Result of function, 0 on success, otherwise error code - * 2 : u8 flag, 0 = no system update available, 1 = system update available. + * 2-11 : SystemUpdateProgress structure for the foreground system update + * 12: ? + * 13: ? */ - void CheckSysUpdateAvailable(Kernel::HLERequestContext& ctx); + void GetProgress(Kernel::HLERequestContext& ctx); - std::shared_ptr nim_system_update_event; + /** + * NIM::Cancel service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void Cancel(Kernel::HLERequestContext& ctx); + + /** + * NIM::CommitSystemTitles service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void CommitSystemTitles(Kernel::HLERequestContext& ctx); + + /** + * NIM::GetBackgroundEventForMenu service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Copy handle IPC header + * 3 : System update ready event handle for home menu + */ + void GetBackgroundEventForMenu(Kernel::HLERequestContext& ctx); + + /** + * NIM::GetBackgroundEventForNews service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Copy handle IPC header + * 3 : System update ready event handle for news module + */ + void GetBackgroundEventForNews(Kernel::HLERequestContext& ctx); + + /** + * NIM::FormatSaveData service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void FormatSaveData(Kernel::HLERequestContext& ctx); + + /** + * NIM::GetCustomerSupportCode service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Customer support code for the last system update error + */ + void GetCustomerSupportCode(Kernel::HLERequestContext& ctx); + + /** + * NIM::IsCommittableAllSystemTitles service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Boolean indicating whether system titles are ready to commit + */ + void IsCommittableAllSystemTitles(Kernel::HLERequestContext& ctx); + + /** + * NIM::GetBackgroundProgress service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2-11 : SystemUpdateProgress structure for the background system update + * 12: ? + * 13: ? + */ + void GetBackgroundProgress(Kernel::HLERequestContext& ctx); + + /** + * NIM::GetSavedHash service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2-10 : NUL-terminated saved system update hash + */ + void GetSavedHash(Kernel::HLERequestContext& ctx); + + /** + * NIM::UnregisterTask service function + * Inputs: + * 1-2 : Title ID + * 3 : Process ID IPC Header + * 4 : Process ID (Auto-filled by kernel) + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void UnregisterTask(Kernel::HLERequestContext& ctx); + + /** + * NIM::IsRegistered service function + * Inputs: + * 1-2 : Title ID + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Boolean indicating whether a download task is registered for the title ID + */ + void IsRegistered(Kernel::HLERequestContext& ctx); + + /** + * NIM::FindTaskInfo service function + * Inputs: + * 1-2 : Title ID + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Static Buffer IPC Header (ID = 0, Size = 0x120) + * 3 : BackgroundTitleDownloadTaskInfo structure pointer + */ + void FindTaskInfo(Kernel::HLERequestContext& ctx); + + /** + * NIM::GetTaskInfos service function + * Inputs: + * 1 : Maximum Number of BackgroundTitleDownloadTaskInfos + * 2 : Mapped Output Buffer IPC Header + * 3 : BackgroundTitleDownloadTaskInfos Output Buffer Pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Number of BackgroundTitleDownloadTaskInfos Read + * 3 : Mapped Output Buffer IPC Header + * 4 : BackgroundTitleDownloadTaskInfos Output Buffer Pointer + */ + void GetTaskInfos(Kernel::HLERequestContext& ctx); + + /** + * NIM::DeleteUnmanagedContexts service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void DeleteUnmanagedContexts(Kernel::HLERequestContext& ctx); + + /** + * NIM::UpdateAutoTitleDownloadTasksAsync service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Copy Handle IPC Header + * 3 : Event handle signaled when the operation completes + */ + void UpdateAutoTitleDownloadTasksAsync(Kernel::HLERequestContext& ctx); + + /** + * NIM::StartPendingAutoTitleDownloadTasksAsync service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Copy Handle IPC Header + * 3 : Event handle signaled when the operation completes + */ + void StartPendingAutoTitleDownloadTasksAsync(Kernel::HLERequestContext& ctx); + + /** + * NIM::GetAsyncResult service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Async operation result code + * 3 : Async operation customer support code + */ + void GetAsyncResult(Kernel::HLERequestContext& ctx); + + /** + * NIM::CancelAsyncCall service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void CancelAsyncCall(Kernel::HLERequestContext& ctx); + + /** + * NIM::IsPendingAutoTitleDownloadTasks service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Boolean indicating whether there are auto title downloads ready to start + */ + void IsPendingAutoTitleDownloadTasks(Kernel::HLERequestContext& ctx); + + /** + * NIM::GetNumAutoTitleDownloadTasks service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Number of auto title download tasks + */ + void GetNumAutoTitleDownloadTasks(Kernel::HLERequestContext& ctx); + + /** + * NIM::GetAutoTitleDownloadTasks service function + * Inputs: + * 1 : Maximum number of AutoTitleDownloadTaskInfos + * 2 : Mapped Output Buffer IPC Header + * 3 : AutoTitleDownloadTaskInfos Output Buffer Pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Number of AutoTitleDownloadTaskInfos Read + * 3 : Mapped Output Buffer IPC Header + * 4 : AutoTitleDownloadTaskInfos Output Buffer Pointer + */ + void GetAutoTitleDownloadTaskInfos(Kernel::HLERequestContext& ctx); + + /** + * NIM::CancelAutoTitleDownloadTask service function + * Inputs: + * 1-2 : Auto Title Download Task ID + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void CancelAutoTitleDownloadTask(Kernel::HLERequestContext& ctx); + + /** + * NIM::SetAutoDbgDat service function + * Inputs: + * 1 : Mapped Input Buffer IPC Header + * 2 : AutoDbgDat Input Buffer Pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Mapped Input Buffer IPC Header + * 3 : AutoDbgDat Input Buffer Pointer + */ + void SetAutoDbgDat(Kernel::HLERequestContext& ctx); + + /** + * NIM::GetAutoDbgDat service function + * Inputs: + * 1 : Mapped Output Buffer IPC Header + * 2 : AutoDbgDat Output Buffer Pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Mapped Output Buffer IPC Header + * 3 : AutoDbgDat Output Buffer Pointer + */ + void GetAutoDbgDat(Kernel::HLERequestContext& ctx); + + /** + * NIM::SetDbgTasks service function + * Inputs: + * 1 : Number of AutoTitleDownloadTaskInfos + * 2 : Mapped Input Buffer IPC Header + * 3 : AutoTitleDownloadTaskInfos Input Buffer Pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 3 : Mapped Input Buffer IPC Header + * 4 : AutoTitleDownloadTaskInfos Input Buffer Pointer + */ + void SetDbgTasks(Kernel::HLERequestContext& ctx); + + /** + * NIM::GetDbgTasks service function + * Inputs: + * 1 : Maximum number of AutoTitleDownloadTaskInfos + * 2 : Mapped Output Buffer IPC Header + * 3 : AutoTitleDownloadTaskInfos Output Buffer Pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Number of AutoTitleDownloadTaskInfos Read + * 3 : Mapped Output Buffer IPC Header + * 4 : AutoTitleDownloadTaskInfos Output Buffer Pointer + */ + void GetDbgTasks(Kernel::HLERequestContext& ctx); + + /** + * NIM::DeleteDbgData service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void DeleteDbgData(Kernel::HLERequestContext& ctx); + + /** + * NIM::SetTslXml service function + * Inputs: + * 1 : Buffer Size + * 2 : Mapped Input Buffer IPC Header + * 3 : XML Input Buffer Pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Mapped Input Buffer IPC Header + * 3 : XML Input Buffer Pointer + */ + void SetTslXml(Kernel::HLERequestContext& ctx); + + /** + * NIM::GetTslXmlSize service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2-3 : XML Size + */ + void GetTslXmlSize(Kernel::HLERequestContext& ctx); + + /** + * NIM::GetTslXml service function + * Inputs: + * 1 : Buffer Capacity + * 2 : Mapped Output Buffer IPC Header + * 3 : XML Output Buffer Pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Mapped Output Buffer IPC Header + * 3 : XML Output Buffer Pointer + */ + void GetTslXml(Kernel::HLERequestContext& ctx); + + /** + * NIM::DeleteTslXml service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void DeleteTslXml(Kernel::HLERequestContext& ctx); + + /** + * NIM::SetDtlXml service function + * Inputs: + * 1 : Buffer Size + * 2 : Mapped Input Buffer IPC Header + * 3 : XML Input Buffer Pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Mapped Input Buffer IPC Header + * 3 : XML Input Buffer Pointer + */ + void SetDtlXml(Kernel::HLERequestContext& ctx); + + /** + * NIM::GetDtlXmlSize service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2-3 : XML Size + */ + void GetDtlXmlSize(Kernel::HLERequestContext& ctx); + + /** + * NIM::GetDtlXml service function + * Inputs: + * 1 : Buffer Capacity + * 2 : Mapped Output Buffer IPC Header + * 3 : XML Output Buffer Pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Mapped Output Buffer IPC Header + * 3 : XML Output Buffer Pointer + */ + void GetDtlXml(Kernel::HLERequestContext& ctx); + + /** + * NIM::UpdateAccountStatus service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Result of the actual operation + * 3 : Customer support code of the actual operation + */ + void UpdateAccountStatus(Kernel::HLERequestContext& ctx); + + /** + * NIM::StartTitleDownload service function + * Inputs: + * 1-6 : TitleDownloadConfig structure + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void StartTitleDownload(Kernel::HLERequestContext& ctx); + + /** + * NIM::StopDownload service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void StopTitleDownload(Kernel::HLERequestContext& ctx); + + /** + * NIM::GetTitleDownloadProgress service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2-7 : TitleDownloadProgress structure + * 8: ? + * 9: ? + */ + void GetTitleDownloadProgress(Kernel::HLERequestContext& ctx); + + /** + * NIM::RegisterTask service function + * Inputs: + * 1-6 : TitleDownloadConfig structure + * 7: ? + * 8: ? + * 9: ? + * 10: Process ID IPC Header + * 11: Process ID (Auto-filled by Kernel) + * 12: Static Buffer IPC Header (ID = 0, Size = 0x90) + * 13: Title Name UTF-16 String Pointer + * 14: Static Buffer IPC Header (ID = 1, Size = 0x48) + * 15: Developer Name UTF-16 String Pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void RegisterTask(Kernel::HLERequestContext& ctx); + + /** + * NIM::IsSystemUpdateAvailable service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Result code from the actual operation + * 3 : Customer support code from the actual operation + * 4 : Boolean indicating whether a system update is available + */ + void IsSystemUpdateAvailable(Kernel::HLERequestContext& ctx); + + /** + * NIM::Unknown2B service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void Unknown2B(Kernel::HLERequestContext& ctx); + + /** + * NIM::UpdateTickets service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Result code from the actual operation + * 3 : Customer support code from the actual operation + */ + void UpdateTickets(Kernel::HLERequestContext& ctx); + + /** + * NIM::DownloadTitleSeedAsync service function + * Inputs: + * 1-2 : Title ID + * 3: u16 Country Code + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Copy Handle IPC Header + * 3 : Event handle signaled when the operation completes + */ + void DownloadTitleSeedAsync(Kernel::HLERequestContext& ctx); + + /** + * NIM::DownloadMissingTitleSeedsAsync service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Copy Handle IPC Header + * 3 : Event handle signaled when the operation completes + */ + void DownloadMissingTitleSeedsAsync(Kernel::HLERequestContext& ctx); + + std::shared_ptr nim_system_update_event_for_menu; + std::shared_ptr nim_system_update_event_for_news; + std::shared_ptr nim_async_completion_event; template void serialize(Archive& ar, const unsigned int) { ar& boost::serialization::base_object(*this); - ar& nim_system_update_event; + ar& nim_system_update_event_for_menu; + ar& nim_system_update_event_for_news; + ar& nim_async_completion_event; } friend class boost::serialization::access; }; diff --git a/src/core/hw/aes/key.cpp b/src/core/hw/aes/key.cpp index 8fdb7f514..0cf2a217d 100644 --- a/src/core/hw/aes/key.cpp +++ b/src/core/hw/aes/key.cpp @@ -233,7 +233,7 @@ void LoadNativeFirmKeysOld3DS() { firm->Read(0, firm_buffer.size(), firm_buffer.data()); firm->Close(); - constexpr std::size_t SLOT_0x25_KEY_X_SECRET_OFFSET = 933480; + constexpr std::size_t SLOT_0x25_KEY_X_SECRET_OFFSET = 934444; constexpr std::size_t SLOT_0x25_KEY_X_SECRET_SIZE = 64; std::vector secret_data(SLOT_0x25_KEY_X_SECRET_SIZE); std::memcpy(secret_data.data(), firm_buffer.data() + SLOT_0x25_KEY_X_SECRET_OFFSET, diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index c5255481b..cb7b1b9cb 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -876,7 +876,7 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f } glUniform1i(uniform_layer, 0); - if (!Settings::values.swap_screen) { + if (!Settings::values.swap_screen.GetValue()) { DrawTopScreen(layout, top_screen, stereo_single_screen); glUniform1i(uniform_layer, 0); ApplySecondLayerOpacity(); @@ -887,13 +887,12 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f ApplySecondLayerOpacity(); DrawTopScreen(layout, top_screen, stereo_single_screen); } - state.blend.enabled = false; + ResetSecondLayerOpacity(); } void RendererOpenGL::ApplySecondLayerOpacity() { if (Settings::values.custom_layout && Settings::values.custom_second_layer_opacity.GetValue() < 100) { - state.blend.enabled = true; state.blend.src_rgb_func = GL_CONSTANT_ALPHA; state.blend.src_a_func = GL_CONSTANT_ALPHA; state.blend.dst_a_func = GL_ONE_MINUS_CONSTANT_ALPHA; @@ -902,6 +901,17 @@ void RendererOpenGL::ApplySecondLayerOpacity() { } } +void RendererOpenGL::ResetSecondLayerOpacity() { + if (Settings::values.custom_layout && + Settings::values.custom_second_layer_opacity.GetValue() < 100) { + state.blend.src_rgb_func = GL_ONE; + state.blend.dst_rgb_func = GL_ZERO; + state.blend.src_a_func = GL_ONE; + state.blend.dst_a_func = GL_ZERO; + state.blend.color.alpha = 0.0f; + } +} + void RendererOpenGL::DrawTopScreen(const Layout::FramebufferLayout& layout, const Common::Rectangle& top_screen, const bool stereo_single_screen) { @@ -911,8 +921,10 @@ void RendererOpenGL::DrawTopScreen(const Layout::FramebufferLayout& layout, if (layout.is_rotated) { if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) { - DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left, (float)top_screen.top, - (float)top_screen.GetWidth(), (float)top_screen.GetHeight()); + int eye = static_cast(Settings::values.mono_render_option.GetValue()); + DrawSingleScreenRotated(screen_infos[eye], (float)top_screen.left, + (float)top_screen.top, (float)top_screen.GetWidth(), + (float)top_screen.GetHeight()); } else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) { DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left / 2, @@ -938,7 +950,8 @@ void RendererOpenGL::DrawTopScreen(const Layout::FramebufferLayout& layout, } } else { if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) { - DrawSingleScreen(screen_infos[0], (float)top_screen.left, (float)top_screen.top, + int eye = static_cast(Settings::values.mono_render_option.GetValue()); + DrawSingleScreen(screen_infos[eye], (float)top_screen.left, (float)top_screen.top, (float)top_screen.GetWidth(), (float)top_screen.GetHeight()); } else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) { diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index c52e4f422..1f6633bb0 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -93,6 +93,7 @@ private: */ void DrawScreens(const Layout::FramebufferLayout& layout, bool flipped); void ApplySecondLayerOpacity(); + void ResetSecondLayerOpacity(); void DrawBottomScreen(const Layout::FramebufferLayout& layout, const Common::Rectangle& bottom_screen, const bool stereo_single_screen);