Merge branch 'master' of https://github.com/GPUCode/citra into vulkan-2

This commit is contained in:
GPUCode
2023-02-19 16:05:11 +02:00
24 changed files with 1507 additions and 219 deletions

View File

@ -161,6 +161,8 @@ void Config::ReadValues() {
static_cast<Settings::LayoutOption>(sdl2_config->GetInteger("Layout", "layout_option", 0)); static_cast<Settings::LayoutOption>(sdl2_config->GetInteger("Layout", "layout_option", 0));
Settings::values.swap_screen = sdl2_config->GetBoolean("Layout", "swap_screen", false); Settings::values.swap_screen = sdl2_config->GetBoolean("Layout", "swap_screen", false);
Settings::values.upright_screen = sdl2_config->GetBoolean("Layout", "upright_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_layout = sdl2_config->GetBoolean("Layout", "custom_layout", false);
Settings::values.custom_top_left = Settings::values.custom_top_left =
static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_left", 0)); static_cast<u16>(sdl2_config->GetInteger("Layout", "custom_top_left", 0));

View File

@ -216,6 +216,10 @@ swap_screen =
# 0 (default): Off, 1: On # 0 (default): Off, 1: On
upright_screen = 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]/. # Dumps textures as PNG to dump/textures/[Title ID]/.
# 0 (default): Off, 1: On # 0 (default): Off, 1: On
dump_textures = dump_textures =

View File

@ -487,6 +487,7 @@ void Config::ReadLayoutValues() {
ReadGlobalSetting(Settings::values.layout_option); ReadGlobalSetting(Settings::values.layout_option);
ReadGlobalSetting(Settings::values.swap_screen); ReadGlobalSetting(Settings::values.swap_screen);
ReadGlobalSetting(Settings::values.upright_screen); ReadGlobalSetting(Settings::values.upright_screen);
ReadGlobalSetting(Settings::values.large_screen_proportion);
if (global) { if (global) {
ReadBasicSetting(Settings::values.mono_render_option); ReadBasicSetting(Settings::values.mono_render_option);
@ -1004,6 +1005,7 @@ void Config::SaveLayoutValues() {
WriteGlobalSetting(Settings::values.layout_option); WriteGlobalSetting(Settings::values.layout_option);
WriteGlobalSetting(Settings::values.swap_screen); WriteGlobalSetting(Settings::values.swap_screen);
WriteGlobalSetting(Settings::values.upright_screen); WriteGlobalSetting(Settings::values.upright_screen);
WriteGlobalSetting(Settings::values.large_screen_proportion);
if (global) { if (global) {
WriteBasicSetting(Settings::values.mono_render_option); WriteBasicSetting(Settings::values.mono_render_option);

View File

@ -5,6 +5,7 @@
#include <QDesktopServices> #include <QDesktopServices>
#include <QMessageBox> #include <QMessageBox>
#include <QUrl> #include <QUrl>
#include "citra_qt/configuration/configuration_shared.h"
#include "citra_qt/configuration/configure_debug.h" #include "citra_qt/configuration/configure_debug.h"
#include "citra_qt/debugger/console.h" #include "citra_qt/debugger/console.h"
#include "citra_qt/uisettings.h" #include "citra_qt/uisettings.h"
@ -16,6 +17,17 @@
#include "ui_configure_debug.h" #include "ui_configure_debug.h"
#include "video_core/renderer_vulkan/vk_instance.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) ConfigureDebug::ConfigureDebug(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureDebug>()) { : QWidget(parent), ui(std::make_unique<Ui::ConfigureDebug>()) {
ui->setupUi(this); ui->setupUi(this);
@ -60,6 +72,19 @@ ConfigureDebug::ConfigureDebug(QWidget* parent)
ui->toggle_cpu_jit->setEnabled(!is_powered_on); ui->toggle_cpu_jit->setEnabled(!is_powered_on);
ui->toggle_renderer_debug->setEnabled(!is_powered_on); ui->toggle_renderer_debug->setEnabled(!is_powered_on);
ui->toggle_dump_command_buffers->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; ConfigureDebug::~ConfigureDebug() = default;
@ -74,6 +99,23 @@ void ConfigureDebug::SetConfiguration() {
ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue()); ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue());
ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug.GetValue()); ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug.GetValue());
ui->toggle_dump_command_buffers->setChecked(Settings::values.dump_command_buffers.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() { void ConfigureDebug::ApplyConfiguration() {
@ -88,6 +130,27 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked();
Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked(); Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked();
Settings::values.dump_command_buffers = ui->toggle_dump_command_buffers->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<int>(&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() { void ConfigureDebug::RetranslateUI() {

View File

@ -21,6 +21,7 @@ public:
void ApplyConfiguration(); void ApplyConfiguration();
void RetranslateUI(); void RetranslateUI();
void SetConfiguration(); void SetConfiguration();
void SetupPerGameUI();
private: private:
std::unique_ptr<Ui::ConfigureDebug> ui; std::unique_ptr<Ui::ConfigureDebug> ui;

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>454</width> <width>443</width>
<height>356</height> <height>358</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -107,12 +107,80 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QGroupBox" name="groupBox_3"> <widget class="QGroupBox" name="groupBox_4">
<property name="title"> <property name="title">
<string>Miscellaneous</string> <string>CPU</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_4"> <layout class="QGridLayout" name="gridLayout_2">
<item> <item row="1" column="0">
<widget class="QWidget" name="clock_speed_widget" native="true">
<layout class="QHBoxLayout" name="clock_speed_layout">
<property name="spacing">
<number>7</number>
</property>
<item>
<widget class="QComboBox" name="clock_speed_combo">
<item>
<property name="text">
<string>Use global clock speed</string>
</property>
</item>
<item>
<property name="text">
<string>Set clock speed:</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="clock_speed_label">
<property name="text">
<string>CPU Clock Speed</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="slider_clock_speed">
<property name="toolTip">
<string>&lt;html&gt;&lt;body&gt;Changes the emulated CPU clock frequency.&lt;br&gt;Underclocking can increase performance but may cause the game to freeze.&lt;br&gt;Overclocking may reduce in game lag but also might cause freezes&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>79</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="pageStep">
<number>15</number>
</property>
<property name="value">
<number>25</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="clock_display_label">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="toggle_cpu_jit"> <widget class="QCheckBox" name="toggle_cpu_jit">
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enables the use of the ARM JIT compiler for emulating the 3DS CPUs. Don't disable unless for debugging purposes&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -142,6 +210,16 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QLabel" name="label_cpu_clock_info">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;CPU Clock Speed Information&lt;br/&gt;Underclocking can increase performance but may cause the game to freeze.&lt;br/&gt;Overclocking may reduce in game lag but also might cause freezes&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
<item> <item>
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
@ -163,7 +241,6 @@
<tabstop>log_filter_edit</tabstop> <tabstop>log_filter_edit</tabstop>
<tabstop>toggle_console</tabstop> <tabstop>toggle_console</tabstop>
<tabstop>open_log_button</tabstop> <tabstop>open_log_button</tabstop>
<tabstop>toggle_cpu_jit</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>
<connections> <connections>

View File

@ -69,6 +69,7 @@ void ConfigureEnhancements::SetConfiguration() {
static_cast<int>(Settings::values.layout_option.GetValue())); static_cast<int>(Settings::values.layout_option.GetValue()));
ui->swap_screen->setChecked(Settings::values.swap_screen.GetValue()); ui->swap_screen->setChecked(Settings::values.swap_screen.GetValue());
ui->upright_screen->setChecked(Settings::values.upright_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_dump_textures->setChecked(Settings::values.dump_textures.GetValue());
ui->toggle_custom_textures->setChecked(Settings::values.custom_textures.GetValue()); ui->toggle_custom_textures->setChecked(Settings::values.custom_textures.GetValue());
ui->toggle_preload_textures->setChecked(Settings::values.preload_textures.GetValue()); ui->toggle_preload_textures->setChecked(Settings::values.preload_textures.GetValue());
@ -122,6 +123,7 @@ void ConfigureEnhancements::ApplyConfiguration() {
static_cast<Settings::LayoutOption>(ui->layout_combobox->currentIndex()); static_cast<Settings::LayoutOption>(ui->layout_combobox->currentIndex());
Settings::values.swap_screen = ui->swap_screen->isChecked(); Settings::values.swap_screen = ui->swap_screen->isChecked();
Settings::values.upright_screen = ui->upright_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.dump_textures = ui->toggle_dump_textures->isChecked();
Settings::values.custom_textures = ui->toggle_custom_textures->isChecked(); Settings::values.custom_textures = ui->toggle_custom_textures->isChecked();
Settings::values.preload_textures = ui->toggle_preload_textures->isChecked(); Settings::values.preload_textures = ui->toggle_preload_textures->isChecked();

View File

@ -295,6 +295,30 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Large Screen Proportion:</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="large_screen_proportion">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>16</number>
</property>
<property name="value">
<number>4</number>
</property>
</widget>
</item>
</layout>
</item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_6"> <layout class="QHBoxLayout" name="horizontalLayout_6">
<item> <item>
@ -384,6 +408,7 @@
<tabstop>layout_combobox</tabstop> <tabstop>layout_combobox</tabstop>
<tabstop>swap_screen</tabstop> <tabstop>swap_screen</tabstop>
<tabstop>upright_screen</tabstop> <tabstop>upright_screen</tabstop>
<tabstop>large_screen_proportion</tabstop>
<tabstop>bg_button</tabstop> <tabstop>bg_button</tabstop>
<tabstop>toggle_custom_textures</tabstop> <tabstop>toggle_custom_textures</tabstop>
<tabstop>toggle_dump_textures</tabstop> <tabstop>toggle_dump_textures</tabstop>

View File

@ -9,6 +9,7 @@
#include <fmt/format.h> #include <fmt/format.h>
#include "citra_qt/configuration/config.h" #include "citra_qt/configuration/config.h"
#include "citra_qt/configuration/configure_audio.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_general.h"
#include "citra_qt/configuration/configure_graphics.h" #include "citra_qt/configuration/configure_graphics.h"
#include "citra_qt/configuration/configure_per_game.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<ConfigureGeneral>(this); general_tab = std::make_unique<ConfigureGeneral>(this);
graphics_tab = std::make_unique<ConfigureGraphics>(this); graphics_tab = std::make_unique<ConfigureGraphics>(this);
system_tab = std::make_unique<ConfigureSystem>(this); system_tab = std::make_unique<ConfigureSystem>(this);
debug_tab = std::make_unique<ConfigureDebug>(this);
ui->setupUi(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(system_tab.get(), tr("System"));
ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics")); ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics"));
ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); ui->tabWidget->addTab(audio_tab.get(), tr("Audio"));
ui->tabWidget->addTab(debug_tab.get(), tr("Debug"));
setFocusPolicy(Qt::ClickFocus); setFocusPolicy(Qt::ClickFocus);
setWindowTitle(tr("Properties")); setWindowTitle(tr("Properties"));
@ -80,6 +83,7 @@ void ConfigurePerGame::ApplyConfiguration() {
system_tab->ApplyConfiguration(); system_tab->ApplyConfiguration();
graphics_tab->ApplyConfiguration(); graphics_tab->ApplyConfiguration();
audio_tab->ApplyConfiguration(); audio_tab->ApplyConfiguration();
debug_tab->ApplyConfiguration();
Settings::LogSettings(); Settings::LogSettings();

View File

@ -17,6 +17,7 @@ class ConfigureAudio;
class ConfigureGeneral; class ConfigureGeneral;
class ConfigureGraphics; class ConfigureGraphics;
class ConfigureSystem; class ConfigureSystem;
class ConfigureDebug;
class QGraphicsScene; class QGraphicsScene;
class QStandardItem; class QStandardItem;
@ -66,4 +67,5 @@ private:
std::unique_ptr<ConfigureGeneral> general_tab; std::unique_ptr<ConfigureGeneral> general_tab;
std::unique_ptr<ConfigureGraphics> graphics_tab; std::unique_ptr<ConfigureGraphics> graphics_tab;
std::unique_ptr<ConfigureSystem> system_tab; std::unique_ptr<ConfigureSystem> system_tab;
std::unique_ptr<ConfigureDebug> debug_tab;
}; };

View File

@ -225,17 +225,6 @@ static const std::array<const char*, 187> country_names = {
QT_TRANSLATE_NOOP("ConfigureSystem", "Bermuda"), // 180-186 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) ConfigureSystem::ConfigureSystem(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureSystem>()) { : QWidget(parent), ui(std::make_unique<Ui::ConfigureSystem>()) {
ui->setupUi(this); 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(); SetupPerGameUI();
ui->combo_download_mode->setCurrentIndex(1); // set to Recommended ui->combo_download_mode->setCurrentIndex(1); // set to Recommended
@ -325,22 +303,6 @@ void ConfigureSystem::SetConfiguration() {
ui->label_disable_info->hide(); 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->toggle_new_3ds->setChecked(Settings::values.is_new_3ds.GetValue());
ui->plugin_loader->setChecked(Settings::values.plugin_loader_enabled.GetValue()); ui->plugin_loader->setChecked(Settings::values.plugin_loader_enabled.GetValue());
ui->allow_plugin_loader->setChecked(Settings::values.allow_plugin_loader.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.plugin_loader_enabled.SetValue(ui->plugin_loader->isChecked());
Settings::values.allow_plugin_loader.SetValue(ui->allow_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) { 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 // Block the global settings if a game is currently running that overrides them
if (Settings::IsConfiguringGlobal()) { if (Settings::IsConfiguringGlobal()) {
ui->toggle_new_3ds->setEnabled(Settings::values.is_new_3ds.UsingGlobal()); ui->toggle_new_3ds->setEnabled(Settings::values.is_new_3ds.UsingGlobal());
ui->slider_clock_speed->setEnabled(Settings::values.cpu_clock_percentage.UsingGlobal());
return; return;
} }
@ -568,11 +525,6 @@ void ConfigureSystem::SetupPerGameUI() {
ui->plugin_loader->setVisible(false); ui->plugin_loader->setVisible(false);
ui->allow_plugin_loader->setVisible(false); ui->allow_plugin_loader->setVisible(false);
connect(ui->clock_speed_combo, qOverload<int>(&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, ConfigurationShared::SetColoredTristate(ui->toggle_new_3ds, Settings::values.is_new_3ds,
is_new_3ds); is_new_3ds);
} }

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>525</width> <width>535</width>
<height>619</height> <height>619</height>
</rect> </rect>
</property> </property>
@ -274,7 +274,7 @@
</item> </item>
<item row="8" column="1"> <item row="8" column="1">
<layout class="QGridLayout" name="edit_init_time_offset_grid"> <layout class="QGridLayout" name="edit_init_time_offset_grid">
<item column="0"> <item row="0" column="0">
<widget class="QSpinBox" name="edit_init_time_offset_days"> <widget class="QSpinBox" name="edit_init_time_offset_days">
<property name="suffix"> <property name="suffix">
<string> days</string> <string> days</string>
@ -287,7 +287,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item column="1"> <item row="0" column="1">
<widget class="QTimeEdit" name="edit_init_time_offset_time"> <widget class="QTimeEdit" name="edit_init_time_offset_time">
<property name="displayFormat"> <property name="displayFormat">
<string>HH:mm:ss</string> <string>HH:mm:ss</string>
@ -341,25 +341,25 @@
</widget> </widget>
</item> </item>
<item row="13" column="0"> <item row="13" column="0">
<widget class="QLabel" name="label_plugin_loader"> <widget class="QLabel" name="label_plugin_loader">
<property name="text"> <property name="text">
<string>3GX Plugin Loader:</string> <string>3GX Plugin Loader:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="13" column="1"> <item row="13" column="1">
<widget class="QCheckBox" name="plugin_loader"> <widget class="QCheckBox" name="plugin_loader">
<property name="text"> <property name="text">
<string>Enable 3GX plugin loader</string> <string>Enable 3GX plugin loader</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="14" column="1"> <item row="14" column="1">
<widget class="QCheckBox" name="allow_plugin_loader"> <widget class="QCheckBox" name="allow_plugin_loader">
<property name="text"> <property name="text">
<string>Allow games to change plugin loader state</string> <string>Allow games to change plugin loader state</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="15" column="0"> <item row="15" column="0">
<widget class="QLabel" name="label_nus_download"> <widget class="QLabel" name="label_nus_download">
@ -410,86 +410,6 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QGroupBox" name="group_advanced">
<property name="title">
<string>Advanced</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QWidget" name="clock_speed_widget" native="true">
<layout class="QHBoxLayout" name="clock_speed_layout">
<property name="spacing">
<number>7</number>
</property>
<item>
<widget class="QComboBox" name="clock_speed_combo">
<item>
<property name="text">
<string>Use global clock speed</string>
</property>
</item>
<item>
<property name="text">
<string>Set clock speed:</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="clock_speed_label">
<property name="text">
<string>CPU Clock Speed</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="slider_clock_speed">
<property name="toolTip">
<string>&lt;html&gt;&lt;body&gt;Changes the emulated CPU clock frequency.&lt;br&gt;Underclocking can increase performance but may cause the game to freeze.&lt;br&gt;Overclocking may reduce in game lag but also might cause freezes&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>79</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="pageStep">
<number>15</number>
</property>
<property name="value">
<number>25</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="clock_display_label">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2"/>
</item>
<item> <item>
<widget class="QLabel" name="label_disable_info"> <widget class="QLabel" name="label_disable_info">
<property name="text"> <property name="text">
@ -500,16 +420,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QLabel" name="label_cpu_clock_info">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;CPU Clock Speed Information&lt;br/&gt;Underclocking can increase performance but may cause the game to freeze.&lt;br/&gt;Overclocking may reduce in game lag but also might cause freezes&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
<item> <item>
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">

View File

@ -100,8 +100,6 @@
<addaction name="menu_Load_State"/> <addaction name="menu_Load_State"/>
<addaction name="menu_Save_State"/> <addaction name="menu_Save_State"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="action_Report_Compatibility"/>
<addaction name="separator"/>
<addaction name="action_Configure"/> <addaction name="action_Configure"/>
<addaction name="action_Configure_Current_Game"/> <addaction name="action_Configure_Current_Game"/>
<addaction name="action_Cheats"/> <addaction name="action_Cheats"/>
@ -189,6 +187,8 @@
<addaction name="action_Check_For_Updates"/> <addaction name="action_Check_For_Updates"/>
<addaction name="action_Open_Maintenance_Tool"/> <addaction name="action_Open_Maintenance_Tool"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="action_Report_Compatibility"/>
<addaction name="separator"/>
<addaction name="action_FAQ"/> <addaction name="action_FAQ"/>
<addaction name="action_About"/> <addaction name="action_About"/>
</widget> </widget>

View File

@ -127,6 +127,7 @@ void LogSettings() {
log_setting("Layout_LayoutOption", values.layout_option.GetValue()); log_setting("Layout_LayoutOption", values.layout_option.GetValue());
log_setting("Layout_SwapScreen", values.swap_screen.GetValue()); log_setting("Layout_SwapScreen", values.swap_screen.GetValue());
log_setting("Layout_UprightScreen", values.upright_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_DumpTextures", values.dump_textures.GetValue());
log_setting("Utility_CustomTextures", values.custom_textures.GetValue()); log_setting("Utility_CustomTextures", values.custom_textures.GetValue());
log_setting("Utility_UseDiskShaderCache", values.use_disk_shader_cache.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.layout_option.SetGlobal(true);
values.swap_screen.SetGlobal(true); values.swap_screen.SetGlobal(true);
values.upright_screen.SetGlobal(true); values.upright_screen.SetGlobal(true);
values.large_screen_proportion.SetGlobal(true);
values.bg_red.SetGlobal(true); values.bg_red.SetGlobal(true);
values.bg_green.SetGlobal(true); values.bg_green.SetGlobal(true);
values.bg_blue.SetGlobal(true); values.bg_blue.SetGlobal(true);

View File

@ -466,6 +466,8 @@ struct Values {
SwitchableSetting<LayoutOption> layout_option{LayoutOption::Default, "layout_option"}; SwitchableSetting<LayoutOption> layout_option{LayoutOption::Default, "layout_option"};
SwitchableSetting<bool> swap_screen{false, "swap_screen"}; SwitchableSetting<bool> swap_screen{false, "swap_screen"};
SwitchableSetting<bool> upright_screen{false, "upright_screen"}; SwitchableSetting<bool> upright_screen{false, "upright_screen"};
SwitchableSetting<float, true> large_screen_proportion{4.f, 1.f, 16.f,
"large_screen_proportion"};
Setting<bool> custom_layout{false, "custom_layout"}; Setting<bool> custom_layout{false, "custom_layout"};
Setting<u16> custom_top_left{0, "custom_top_left"}; Setting<u16> custom_top_left{0, "custom_top_left"};
Setting<u16> custom_top_top{0, "custom_top_top"}; Setting<u16> custom_top_top{0, "custom_top_top"};

View File

@ -197,7 +197,8 @@ void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height,
case Settings::LayoutOption::LargeScreen: case Settings::LayoutOption::LargeScreen:
layout = layout =
Layout::LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), 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; break;
case Settings::LayoutOption::SideScreen: case Settings::LayoutOption::SideScreen:
layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen.GetValue(), layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen.GetValue(),

View File

@ -231,7 +231,8 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool up
return res; 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(width > 0);
ASSERT(height > 0); ASSERT(height > 0);
@ -244,25 +245,29 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr
float small_screen_aspect_ratio; float small_screen_aspect_ratio;
if (upright) { if (upright) {
if (swapped) { if (swapped) {
emulation_aspect_ratio = (Core::kScreenBottomWidth * 4.0f + Core::kScreenTopWidth) / emulation_aspect_ratio =
(Core::kScreenBottomHeight * 4); (Core::kScreenBottomWidth * scale_factor + Core::kScreenTopWidth) /
(Core::kScreenBottomHeight * scale_factor);
large_screen_aspect_ratio = BOT_SCREEN_UPRIGHT_ASPECT_RATIO; large_screen_aspect_ratio = BOT_SCREEN_UPRIGHT_ASPECT_RATIO;
small_screen_aspect_ratio = TOP_SCREEN_UPRIGHT_ASPECT_RATIO; small_screen_aspect_ratio = TOP_SCREEN_UPRIGHT_ASPECT_RATIO;
} else { } else {
emulation_aspect_ratio = (Core::kScreenTopWidth * 4.0f + Core::kScreenBottomWidth) / emulation_aspect_ratio =
(Core::kScreenTopHeight * 4); (Core::kScreenTopWidth * scale_factor + Core::kScreenBottomWidth) /
(Core::kScreenTopHeight * scale_factor);
large_screen_aspect_ratio = TOP_SCREEN_UPRIGHT_ASPECT_RATIO; large_screen_aspect_ratio = TOP_SCREEN_UPRIGHT_ASPECT_RATIO;
small_screen_aspect_ratio = BOT_SCREEN_UPRIGHT_ASPECT_RATIO; small_screen_aspect_ratio = BOT_SCREEN_UPRIGHT_ASPECT_RATIO;
} }
} else { } else {
if (swapped) { if (swapped) {
emulation_aspect_ratio = Core::kScreenBottomHeight * 4 / emulation_aspect_ratio =
(Core::kScreenBottomWidth * 4.0f + Core::kScreenTopWidth); Core::kScreenBottomHeight * scale_factor /
(Core::kScreenBottomWidth * scale_factor + Core::kScreenTopWidth);
large_screen_aspect_ratio = BOT_SCREEN_ASPECT_RATIO; large_screen_aspect_ratio = BOT_SCREEN_ASPECT_RATIO;
small_screen_aspect_ratio = TOP_SCREEN_ASPECT_RATIO; small_screen_aspect_ratio = TOP_SCREEN_ASPECT_RATIO;
} else { } else {
emulation_aspect_ratio = Core::kScreenTopHeight * 4 / emulation_aspect_ratio =
(Core::kScreenTopWidth * 4.0f + Core::kScreenBottomWidth); Core::kScreenTopHeight * scale_factor /
(Core::kScreenTopWidth * scale_factor + Core::kScreenBottomWidth);
large_screen_aspect_ratio = TOP_SCREEN_ASPECT_RATIO; large_screen_aspect_ratio = TOP_SCREEN_ASPECT_RATIO;
small_screen_aspect_ratio = BOT_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<u32> screen_window_area{0, 0, width, height}; Common::Rectangle<u32> screen_window_area{0, 0, width, height};
Common::Rectangle<u32> total_rect = maxRectangle(screen_window_area, emulation_aspect_ratio); Common::Rectangle<u32> total_rect = maxRectangle(screen_window_area, emulation_aspect_ratio);
Common::Rectangle<u32> large_screen = maxRectangle(total_rect, large_screen_aspect_ratio); Common::Rectangle<u32> large_screen = maxRectangle(total_rect, large_screen_aspect_ratio);
Common::Rectangle<u32> fourth_size_rect = total_rect.Scale(.25f); Common::Rectangle<u32> fourth_size_rect = total_rect.Scale(1.f / scale_factor);
Common::Rectangle<u32> small_screen = maxRectangle(fourth_size_rect, small_screen_aspect_ratio); Common::Rectangle<u32> small_screen = maxRectangle(fourth_size_rect, small_screen_aspect_ratio);
if (window_aspect_ratio < emulation_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.upright_screen.GetValue()) {
if (Settings::values.swap_screen.GetValue()) { if (Settings::values.swap_screen.GetValue()) {
width = Core::kScreenBottomHeight * res_scale; 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 { } else {
width = Core::kScreenTopHeight * res_scale; 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 { } else {
if (Settings::values.swap_screen.GetValue()) { 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; height = Core::kScreenBottomHeight * res_scale;
} else { } 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; height = Core::kScreenTopHeight * res_scale;
} }
} }
layout = LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), 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; break;
case Settings::LayoutOption::SideScreen: case Settings::LayoutOption::SideScreen:
if (Settings::values.upright_screen.GetValue()) { if (Settings::values.upright_screen.GetValue()) {
@ -576,9 +594,12 @@ std::pair<unsigned, unsigned> GetMinimumSizeFromLayout(Settings::LayoutOption la
min_height = Core::kScreenBottomHeight; min_height = Core::kScreenBottomHeight;
break; break;
case Settings::LayoutOption::LargeScreen: case Settings::LayoutOption::LargeScreen:
min_width = Settings::values.swap_screen min_width =
? Core::kScreenTopWidth / 4 + Core::kScreenBottomWidth Settings::values.swap_screen
: Core::kScreenTopWidth + Core::kScreenBottomWidth / 4; ? Core::kScreenTopWidth / Settings::values.large_screen_proportion.GetValue() +
Core::kScreenBottomWidth
: Core::kScreenTopWidth + Core::kScreenBottomWidth /
Settings::values.large_screen_proportion.GetValue();
min_height = Core::kScreenBottomHeight; min_height = Core::kScreenBottomHeight;
break; break;
case Settings::LayoutOption::SideScreen: case Settings::LayoutOption::SideScreen:

View File

@ -83,9 +83,11 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool is_swapped, bool
* @param width Window framebuffer width in pixels * @param width Window framebuffer width in pixels
* @param height Window framebuffer height in pixels * @param height Window framebuffer height in pixels
* @param is_swapped if true, the bottom screen will be the large display * @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 * @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 * Factory method for constructing a Frame with the Top screen and bottom

View File

@ -1087,7 +1087,11 @@ void Module::Interface::DeleteTicket(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetNumTickets(Kernel::HLERequestContext& ctx) { void Module::Interface::GetNumTickets(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x0008, 0, 0); // 0x00080000 IPC::RequestParser rp(ctx, 0x0008, 0, 0); // 0x00080000
u32 ticket_count = 0; u32 ticket_count = 0;
for (const auto& title_list : am->am_title_list) {
ticket_count += static_cast<u32>(title_list.size());
}
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
@ -1101,9 +1105,18 @@ void Module::Interface::GetTicketList(Kernel::HLERequestContext& ctx) {
u32 ticket_index = rp.Pop<u32>(); u32 ticket_index = rp.Pop<u32>();
auto& ticket_tids_out = rp.PopMappedBuffer(); 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<u32>(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); IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
rb.Push(ticket_list_count); rb.Push(tickets_written);
rb.PushMappedBuffer(ticket_tids_out); rb.PushMappedBuffer(ticket_tids_out);
LOG_WARNING(Service_AM, "(STUBBED) ticket_list_count=0x{:08x}, ticket_index=0x{:08x}", LOG_WARNING(Service_AM, "(STUBBED) ticket_list_count=0x{:08x}, ticket_index=0x{:08x}",
ticket_list_count, ticket_index); ticket_list_count, ticket_index);

View File

@ -3,9 +3,9 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "common/archives.h" #include "common/archives.h"
#include "common/string_util.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/ipc_helpers.h" #include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/nim/nim_u.h" #include "core/hle/service/nim/nim_u.h"
SERVICE_CONSTRUCT_IMPL(Service::NIM::NIM_U) SERVICE_CONSTRUCT_IMPL(Service::NIM::NIM_U)
@ -13,37 +13,736 @@ SERIALIZE_EXPORT_IMPL(Service::NIM::NIM_U)
namespace Service::NIM { 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<u16, 0x48> title_name;
u16 title_name_terminator;
std::array<u16, 0x24> 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<u16, 0x48> title_name;
u16 title_name_terminator;
std::array<u16, 0x24> 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) { NIM_U::NIM_U(Core::System& system) : ServiceFramework("nim:u", 2) {
const FunctionInfo functions[] = { const FunctionInfo functions[] = {
{0x00010000, nullptr, "StartSysUpdate"}, {0x00010000, &NIM_U::StartNetworkUpdate, "StartNetworkUpdate"},
{0x00020000, nullptr, "GetUpdateDownloadProgress"}, {0x00020000, &NIM_U::GetProgress, "GetProgress"},
{0x00040000, nullptr, "FinishTitlesInstall"}, {0x00030000, &NIM_U::Cancel, "Cancel"},
{0x00050000, &NIM_U::CheckForSysUpdateEvent, "CheckForSysUpdateEvent"}, {0x00040000, &NIM_U::CommitSystemTitles, "CommitSystemTitles"},
{0x00090000, &NIM_U::CheckSysUpdateAvailable, "CheckSysUpdateAvailable"}, {0x00050000, &NIM_U::GetBackgroundEventForMenu, "GetBackgroundEventForMenu"},
{0x000A0000, nullptr, "GetState"}, {0x00060000, &NIM_U::GetBackgroundEventForNews, "GetBackgroundEventForNews"},
{0x000B0000, nullptr, "GetSystemTitleHash"}, {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); RegisterHandlers(functions);
nim_system_update_event = nim_system_update_event_for_menu =
system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "NIM System Update Event"); 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; NIM_U::~NIM_U() = default;
void NIM_U::CheckForSysUpdateEvent(Kernel::HLERequestContext& ctx) { void NIM_U::StartNetworkUpdate(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x5, 0, 0); // 0x50000 IPC::RequestParser rp(ctx, 0x1, 0, 0); // 0x10000
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS); 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::RequestParser rp(ctx, 0x9, 0, 0); // 0x90000
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS); 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<char, 0x24> 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<u64>();
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<u64>();
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<u64>();
std::vector<u8> 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<u32>();
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<u32>();
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<u64>();
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<u32>();
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<u32>();
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<u32>();
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<u64>(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<u32>();
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<u32>();
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<u64>(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<u32>();
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<TitleDownloadConfig>();
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<TitleDownloadConfig>();
const u32 unknown_1 = rp.Pop<u32>();
const u32 unknown_2 = rp.Pop<u32>();
const u32 unknown_3 = rp.Pop<u32>();
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<u64>();
const u16 country_code = rp.Pop<u16>();
// 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"); LOG_WARNING(Service_NIM, "(STUBBED) called");
} }

View File

@ -20,32 +20,522 @@ public:
private: private:
/** /**
* NIM::CheckForSysUpdateEvent service function * NIM::StartNetworkUpdate service function
* Inputs: * Inputs:
* 1 : None * 1 : None
* Outputs: * Outputs:
* 1 : Result of function, 0 on success, otherwise error code * 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: * Inputs:
* 1 : None * 1 : None
* Outputs: * Outputs:
* 1 : Result of function, 0 on success, otherwise error code * 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<Kernel::Event> 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<Kernel::Event> nim_system_update_event_for_menu;
std::shared_ptr<Kernel::Event> nim_system_update_event_for_news;
std::shared_ptr<Kernel::Event> nim_async_completion_event;
template <class Archive> template <class Archive>
void serialize(Archive& ar, const unsigned int) { void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this); ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*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; friend class boost::serialization::access;
}; };

View File

@ -233,7 +233,7 @@ void LoadNativeFirmKeysOld3DS() {
firm->Read(0, firm_buffer.size(), firm_buffer.data()); firm->Read(0, firm_buffer.size(), firm_buffer.data());
firm->Close(); 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; constexpr std::size_t SLOT_0x25_KEY_X_SECRET_SIZE = 64;
std::vector<u8> secret_data(SLOT_0x25_KEY_X_SECRET_SIZE); std::vector<u8> secret_data(SLOT_0x25_KEY_X_SECRET_SIZE);
std::memcpy(secret_data.data(), firm_buffer.data() + SLOT_0x25_KEY_X_SECRET_OFFSET, std::memcpy(secret_data.data(), firm_buffer.data() + SLOT_0x25_KEY_X_SECRET_OFFSET,

View File

@ -876,7 +876,7 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f
} }
glUniform1i(uniform_layer, 0); glUniform1i(uniform_layer, 0);
if (!Settings::values.swap_screen) { if (!Settings::values.swap_screen.GetValue()) {
DrawTopScreen(layout, top_screen, stereo_single_screen); DrawTopScreen(layout, top_screen, stereo_single_screen);
glUniform1i(uniform_layer, 0); glUniform1i(uniform_layer, 0);
ApplySecondLayerOpacity(); ApplySecondLayerOpacity();
@ -887,13 +887,12 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f
ApplySecondLayerOpacity(); ApplySecondLayerOpacity();
DrawTopScreen(layout, top_screen, stereo_single_screen); DrawTopScreen(layout, top_screen, stereo_single_screen);
} }
state.blend.enabled = false; ResetSecondLayerOpacity();
} }
void RendererOpenGL::ApplySecondLayerOpacity() { void RendererOpenGL::ApplySecondLayerOpacity() {
if (Settings::values.custom_layout && if (Settings::values.custom_layout &&
Settings::values.custom_second_layer_opacity.GetValue() < 100) { Settings::values.custom_second_layer_opacity.GetValue() < 100) {
state.blend.enabled = true;
state.blend.src_rgb_func = GL_CONSTANT_ALPHA; state.blend.src_rgb_func = GL_CONSTANT_ALPHA;
state.blend.src_a_func = GL_CONSTANT_ALPHA; state.blend.src_a_func = GL_CONSTANT_ALPHA;
state.blend.dst_a_func = GL_ONE_MINUS_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, void RendererOpenGL::DrawTopScreen(const Layout::FramebufferLayout& layout,
const Common::Rectangle<u32>& top_screen, const Common::Rectangle<u32>& top_screen,
const bool stereo_single_screen) { const bool stereo_single_screen) {
@ -911,8 +921,10 @@ void RendererOpenGL::DrawTopScreen(const Layout::FramebufferLayout& layout,
if (layout.is_rotated) { if (layout.is_rotated) {
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) { if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) {
DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left, (float)top_screen.top, int eye = static_cast<int>(Settings::values.mono_render_option.GetValue());
(float)top_screen.GetWidth(), (float)top_screen.GetHeight()); 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() == } else if (Settings::values.render_3d.GetValue() ==
Settings::StereoRenderOption::SideBySide) { Settings::StereoRenderOption::SideBySide) {
DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left / 2, DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left / 2,
@ -938,7 +950,8 @@ void RendererOpenGL::DrawTopScreen(const Layout::FramebufferLayout& layout,
} }
} else { } else {
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) { 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<int>(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()); (float)top_screen.GetWidth(), (float)top_screen.GetHeight());
} else if (Settings::values.render_3d.GetValue() == } else if (Settings::values.render_3d.GetValue() ==
Settings::StereoRenderOption::SideBySide) { Settings::StereoRenderOption::SideBySide) {

View File

@ -93,6 +93,7 @@ private:
*/ */
void DrawScreens(const Layout::FramebufferLayout& layout, bool flipped); void DrawScreens(const Layout::FramebufferLayout& layout, bool flipped);
void ApplySecondLayerOpacity(); void ApplySecondLayerOpacity();
void ResetSecondLayerOpacity();
void DrawBottomScreen(const Layout::FramebufferLayout& layout, void DrawBottomScreen(const Layout::FramebufferLayout& layout,
const Common::Rectangle<u32>& bottom_screen, const Common::Rectangle<u32>& bottom_screen,
const bool stereo_single_screen); const bool stereo_single_screen);