diff options
Diffstat (limited to 'src')
50 files changed, 1204 insertions, 329 deletions
diff --git a/src/audio_core/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp index 75cc0d6dd..4b66cd826 100644 --- a/src/audio_core/sdl2_sink.cpp +++ b/src/audio_core/sdl2_sink.cpp | |||
| @@ -25,7 +25,7 @@ struct SDL2Sink::Impl { | |||
| 25 | 25 | ||
| 26 | SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) { | 26 | SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) { |
| 27 | if (SDL_Init(SDL_INIT_AUDIO) < 0) { | 27 | if (SDL_Init(SDL_INIT_AUDIO) < 0) { |
| 28 | LOG_CRITICAL(Audio_Sink, "SDL_Init(SDL_INIT_AUDIO) failed"); | 28 | LOG_CRITICAL(Audio_Sink, "SDL_Init(SDL_INIT_AUDIO) failed with: %s", SDL_GetError()); |
| 29 | impl->audio_device_id = 0; | 29 | impl->audio_device_id = 0; |
| 30 | return; | 30 | return; |
| 31 | } | 31 | } |
| @@ -45,7 +45,7 @@ SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) { | |||
| 45 | impl->audio_device_id = | 45 | impl->audio_device_id = |
| 46 | SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0); | 46 | SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0); |
| 47 | if (impl->audio_device_id <= 0) { | 47 | if (impl->audio_device_id <= 0) { |
| 48 | LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed"); | 48 | LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed with: %s", SDL_GetError()); |
| 49 | return; | 49 | return; |
| 50 | } | 50 | } |
| 51 | 51 | ||
diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 05eabfa3d..fd30bfc85 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp | |||
| @@ -72,6 +72,11 @@ void Config::ReadValues() { | |||
| 72 | Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0); | 72 | Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0); |
| 73 | Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 1.0); | 73 | Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 1.0); |
| 74 | 74 | ||
| 75 | // Layout | ||
| 76 | Settings::values.layout_option = | ||
| 77 | static_cast<Settings::LayoutOption>(sdl2_config->GetInteger("Layout", "layout_option", 0)); | ||
| 78 | Settings::values.swap_screen = sdl2_config->GetBoolean("Layout", "swap_screen", false); | ||
| 79 | |||
| 75 | // Audio | 80 | // Audio |
| 76 | Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); | 81 | Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); |
| 77 | Settings::values.enable_audio_stretching = | 82 | Settings::values.enable_audio_stretching = |
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 0b49e0230..b22627a2f 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h | |||
| @@ -63,6 +63,16 @@ use_scaled_resolution = | |||
| 63 | # 0 (default): Off, 1: On | 63 | # 0 (default): Off, 1: On |
| 64 | use_vsync = | 64 | use_vsync = |
| 65 | 65 | ||
| 66 | [Layout] | ||
| 67 | # Layout for the screen inside the render window. | ||
| 68 | # 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen | ||
| 69 | layout_option = | ||
| 70 | |||
| 71 | # Swaps the prominent screen with the other screen. | ||
| 72 | # For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen. | ||
| 73 | # 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent | ||
| 74 | swap_screen = | ||
| 75 | |||
| 66 | # The clear color for the renderer. What shows up on the sides of the bottom screen. | 76 | # The clear color for the renderer. What shows up on the sides of the bottom screen. |
| 67 | # Must be in range of 0.0-1.0. Defaults to 1.0 for all. | 77 | # Must be in range of 0.0-1.0. Defaults to 1.0 for all. |
| 68 | bg_red = | 78 | bg_red = |
diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp index 7df054208..8abe48984 100644 --- a/src/citra/emu_window/emu_window_sdl2.cpp +++ b/src/citra/emu_window/emu_window_sdl2.cpp | |||
| @@ -46,11 +46,8 @@ bool EmuWindow_SDL2::IsOpen() const { | |||
| 46 | 46 | ||
| 47 | void EmuWindow_SDL2::OnResize() { | 47 | void EmuWindow_SDL2::OnResize() { |
| 48 | int width, height; | 48 | int width, height; |
| 49 | |||
| 50 | SDL_GetWindowSize(render_window, &width, &height); | 49 | SDL_GetWindowSize(render_window, &width, &height); |
| 51 | 50 | UpdateCurrentFramebufferLayout(width, height); | |
| 52 | NotifyFramebufferLayoutChanged( | ||
| 53 | EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height)); | ||
| 54 | } | 51 | } |
| 55 | 52 | ||
| 56 | EmuWindow_SDL2::EmuWindow_SDL2() { | 53 | EmuWindow_SDL2::EmuWindow_SDL2() { |
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 0abae86c3..7699ca8d0 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp | |||
| @@ -161,9 +161,7 @@ void GRenderWindow::OnFramebufferSizeChanged() { | |||
| 161 | qreal pixelRatio = windowPixelRatio(); | 161 | qreal pixelRatio = windowPixelRatio(); |
| 162 | unsigned width = child->QPaintDevice::width() * pixelRatio; | 162 | unsigned width = child->QPaintDevice::width() * pixelRatio; |
| 163 | unsigned height = child->QPaintDevice::height() * pixelRatio; | 163 | unsigned height = child->QPaintDevice::height() * pixelRatio; |
| 164 | 164 | UpdateCurrentFramebufferLayout(width, height); | |
| 165 | NotifyFramebufferLayoutChanged( | ||
| 166 | EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height)); | ||
| 167 | } | 165 | } |
| 168 | 166 | ||
| 169 | void GRenderWindow::BackupGeometry() { | 167 | void GRenderWindow::BackupGeometry() { |
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index 0b46ca6bb..3d2312619 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp | |||
| @@ -54,6 +54,12 @@ void Config::ReadValues() { | |||
| 54 | Settings::values.bg_blue = qt_config->value("bg_blue", 1.0).toFloat(); | 54 | Settings::values.bg_blue = qt_config->value("bg_blue", 1.0).toFloat(); |
| 55 | qt_config->endGroup(); | 55 | qt_config->endGroup(); |
| 56 | 56 | ||
| 57 | qt_config->beginGroup("Layout"); | ||
| 58 | Settings::values.layout_option = | ||
| 59 | static_cast<Settings::LayoutOption>(qt_config->value("layout_option").toInt()); | ||
| 60 | Settings::values.swap_screen = qt_config->value("swap_screen", false).toBool(); | ||
| 61 | qt_config->endGroup(); | ||
| 62 | |||
| 57 | qt_config->beginGroup("Audio"); | 63 | qt_config->beginGroup("Audio"); |
| 58 | Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); | 64 | Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); |
| 59 | Settings::values.enable_audio_stretching = | 65 | Settings::values.enable_audio_stretching = |
| @@ -155,6 +161,11 @@ void Config::SaveValues() { | |||
| 155 | qt_config->setValue("bg_blue", (double)Settings::values.bg_blue); | 161 | qt_config->setValue("bg_blue", (double)Settings::values.bg_blue); |
| 156 | qt_config->endGroup(); | 162 | qt_config->endGroup(); |
| 157 | 163 | ||
| 164 | qt_config->beginGroup("Layout"); | ||
| 165 | qt_config->setValue("layout_option", static_cast<int>(Settings::values.layout_option)); | ||
| 166 | qt_config->setValue("swap_screen", Settings::values.swap_screen); | ||
| 167 | qt_config->endGroup(); | ||
| 168 | |||
| 158 | qt_config->beginGroup("Audio"); | 169 | qt_config->beginGroup("Audio"); |
| 159 | qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id)); | 170 | qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id)); |
| 160 | qt_config->setValue("enable_audio_stretching", Settings::values.enable_audio_stretching); | 171 | qt_config->setValue("enable_audio_stretching", Settings::values.enable_audio_stretching); |
diff --git a/src/citra_qt/configure_dialog.cpp b/src/citra_qt/configure_dialog.cpp index 446ad04a1..525a7cc4e 100644 --- a/src/citra_qt/configure_dialog.cpp +++ b/src/citra_qt/configure_dialog.cpp | |||
| @@ -23,4 +23,5 @@ void ConfigureDialog::applyConfiguration() { | |||
| 23 | ui->graphicsTab->applyConfiguration(); | 23 | ui->graphicsTab->applyConfiguration(); |
| 24 | ui->audioTab->applyConfiguration(); | 24 | ui->audioTab->applyConfiguration(); |
| 25 | ui->debugTab->applyConfiguration(); | 25 | ui->debugTab->applyConfiguration(); |
| 26 | Settings::Apply(); | ||
| 26 | } | 27 | } |
diff --git a/src/citra_qt/configure_graphics.cpp b/src/citra_qt/configure_graphics.cpp index 19c1f75c2..29834e11b 100644 --- a/src/citra_qt/configure_graphics.cpp +++ b/src/citra_qt/configure_graphics.cpp | |||
| @@ -23,6 +23,8 @@ void ConfigureGraphics::setConfiguration() { | |||
| 23 | ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit); | 23 | ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit); |
| 24 | ui->toggle_scaled_resolution->setChecked(Settings::values.use_scaled_resolution); | 24 | ui->toggle_scaled_resolution->setChecked(Settings::values.use_scaled_resolution); |
| 25 | ui->toggle_vsync->setChecked(Settings::values.use_vsync); | 25 | ui->toggle_vsync->setChecked(Settings::values.use_vsync); |
| 26 | ui->layout_combobox->setCurrentIndex(static_cast<int>(Settings::values.layout_option)); | ||
| 27 | ui->swap_screen->setChecked(Settings::values.swap_screen); | ||
| 26 | } | 28 | } |
| 27 | 29 | ||
| 28 | void ConfigureGraphics::applyConfiguration() { | 30 | void ConfigureGraphics::applyConfiguration() { |
| @@ -30,5 +32,8 @@ void ConfigureGraphics::applyConfiguration() { | |||
| 30 | Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked(); | 32 | Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked(); |
| 31 | Settings::values.use_scaled_resolution = ui->toggle_scaled_resolution->isChecked(); | 33 | Settings::values.use_scaled_resolution = ui->toggle_scaled_resolution->isChecked(); |
| 32 | Settings::values.use_vsync = ui->toggle_vsync->isChecked(); | 34 | Settings::values.use_vsync = ui->toggle_vsync->isChecked(); |
| 35 | Settings::values.layout_option = | ||
| 36 | static_cast<Settings::LayoutOption>(ui->layout_combobox->currentIndex()); | ||
| 37 | Settings::values.swap_screen = ui->swap_screen->isChecked(); | ||
| 33 | Settings::Apply(); | 38 | Settings::Apply(); |
| 34 | } | 39 | } |
diff --git a/src/citra_qt/configure_graphics.ui b/src/citra_qt/configure_graphics.ui index da6e19ce1..af16a4292 100644 --- a/src/citra_qt/configure_graphics.ui +++ b/src/citra_qt/configure_graphics.ui | |||
| @@ -22,38 +22,88 @@ | |||
| 22 | <string>Graphics</string> | 22 | <string>Graphics</string> |
| 23 | </property> | 23 | </property> |
| 24 | <layout class="QVBoxLayout" name="verticalLayout_2"> | 24 | <layout class="QVBoxLayout" name="verticalLayout_2"> |
| 25 | <item> | 25 | <item> |
| 26 | <widget class="QCheckBox" name="toggle_hw_renderer"> | 26 | <widget class="QCheckBox" name="toggle_hw_renderer"> |
| 27 | <property name="text"> | 27 | <property name="text"> |
| 28 | <string>Enable hardware renderer</string> | 28 | <string>Enable hardware renderer</string> |
| 29 | </property> | 29 | </property> |
| 30 | </widget> | ||
| 31 | </item> | ||
| 32 | <item> | ||
| 33 | <widget class="QCheckBox" name="toggle_shader_jit"> | ||
| 34 | <property name="text"> | ||
| 35 | <string>Enable shader JIT</string> | ||
| 36 | </property> | ||
| 37 | </widget> | ||
| 38 | </item> | ||
| 39 | <item> | ||
| 40 | <widget class="QCheckBox" name="toggle_scaled_resolution"> | ||
| 41 | <property name="text"> | ||
| 42 | <string>Enable scaled resolution</string> | ||
| 43 | </property> | ||
| 44 | </widget> | ||
| 45 | </item> | ||
| 46 | <item> | ||
| 47 | <widget class="QCheckBox" name="toggle_vsync"> | ||
| 48 | <property name="text"> | ||
| 49 | <string>Enable V-Sync</string> | ||
| 50 | </property> | ||
| 51 | </widget> | ||
| 52 | </item> | ||
| 53 | </layout> | ||
| 54 | </widget> | ||
| 55 | </item> | ||
| 56 | </layout> | ||
| 57 | </item> | ||
| 58 | <item> | ||
| 59 | <widget class="QGroupBox" name="groupBox2"> | ||
| 60 | <property name="title"> | ||
| 61 | <string>Layout</string> | ||
| 62 | </property> | ||
| 63 | <layout class="QHBoxLayout" name="horizontalLayout_3"> | ||
| 64 | <item> | ||
| 65 | <layout class="QVBoxLayout" name="verticalLayout_2"> | ||
| 66 | <item> | ||
| 67 | <layout class="QHBoxLayout" name="horizontalLayout_3"> | ||
| 68 | <item> | ||
| 69 | <widget class="QLabel" name="label1"> | ||
| 70 | <property name="text"> | ||
| 71 | <string>Screen Layout:</string> | ||
| 72 | </property> | ||
| 30 | </widget> | 73 | </widget> |
| 31 | </item> | 74 | </item> |
| 32 | <item> | 75 | <item> |
| 33 | <widget class="QCheckBox" name="toggle_shader_jit"> | 76 | <widget class="QComboBox" name="layout_combobox"> |
| 77 | <item> | ||
| 34 | <property name="text"> | 78 | <property name="text"> |
| 35 | <string>Enable shader JIT</string> | 79 | <string notr="true">Default</string> |
| 36 | </property> | 80 | </property> |
| 37 | </widget> | 81 | </item> |
| 38 | </item> | 82 | <item> |
| 39 | <item> | ||
| 40 | <widget class="QCheckBox" name="toggle_scaled_resolution"> | ||
| 41 | <property name="text"> | 83 | <property name="text"> |
| 42 | <string>Enable scaled resolution</string> | 84 | <string notr="true">Single Screen</string> |
| 43 | </property> | 85 | </property> |
| 44 | </widget> | 86 | </item> |
| 45 | </item> | 87 | <item> |
| 46 | <item> | ||
| 47 | <widget class="QCheckBox" name="toggle_vsync"> | ||
| 48 | <property name="text"> | 88 | <property name="text"> |
| 49 | <string>Enable V-Sync</string> | 89 | <string notr="true">Large Screen</string> |
| 50 | </property> | 90 | </property> |
| 91 | </item> | ||
| 51 | </widget> | 92 | </widget> |
| 52 | </item> | 93 | </item> |
| 94 | </layout> | ||
| 95 | </item> | ||
| 96 | <item> | ||
| 97 | <widget class="QCheckBox" name="swap_screen"> | ||
| 98 | <property name="text"> | ||
| 99 | <string>Swap Screens</string> | ||
| 100 | </property> | ||
| 101 | </widget> | ||
| 102 | </item> | ||
| 53 | </layout> | 103 | </layout> |
| 54 | </widget> | 104 | </item> |
| 55 | </item> | 105 | </layout> |
| 56 | </layout> | 106 | </widget> |
| 57 | </item> | 107 | </item> |
| 58 | <item> | 108 | <item> |
| 59 | <spacer name="verticalSpacer"> | 109 | <spacer name="verticalSpacer"> |
| @@ -71,22 +121,5 @@ | |||
| 71 | </layout> | 121 | </layout> |
| 72 | </widget> | 122 | </widget> |
| 73 | <resources/> | 123 | <resources/> |
| 74 | <connections> | 124 | <connections/> |
| 75 | <connection> | ||
| 76 | <sender>toggle_gdbstub</sender> | ||
| 77 | <signal>toggled(bool)</signal> | ||
| 78 | <receiver>gdbport_spinbox</receiver> | ||
| 79 | <slot>setEnabled(bool)</slot> | ||
| 80 | <hints> | ||
| 81 | <hint type="sourcelabel"> | ||
| 82 | <x>84</x> | ||
| 83 | <y>157</y> | ||
| 84 | </hint> | ||
| 85 | <hint type="destinationlabel"> | ||
| 86 | <x>342</x> | ||
| 87 | <y>158</y> | ||
| 88 | </hint> | ||
| 89 | </hints> | ||
| 90 | </connection> | ||
| 91 | </connections> | ||
| 92 | </ui> | 125 | </ui> |
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 8322e2305..0bf9f48d6 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp | |||
| @@ -48,6 +48,10 @@ | |||
| 48 | #include "qhexedit.h" | 48 | #include "qhexedit.h" |
| 49 | #include "video_core/video_core.h" | 49 | #include "video_core/video_core.h" |
| 50 | 50 | ||
| 51 | #ifdef QT_STATICPLUGIN | ||
| 52 | Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); | ||
| 53 | #endif | ||
| 54 | |||
| 51 | GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { | 55 | GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { |
| 52 | Pica::g_debug_context = Pica::DebugContext::Construct(); | 56 | Pica::g_debug_context = Pica::DebugContext::Construct(); |
| 53 | 57 | ||
| @@ -101,7 +105,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { | |||
| 101 | addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget); | 105 | addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget); |
| 102 | graphicsTracingWidget->hide(); | 106 | graphicsTracingWidget->hide(); |
| 103 | 107 | ||
| 104 | auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica surface viewer"), this); | 108 | auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica Surface Viewer"), this); |
| 105 | connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this, | 109 | connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this, |
| 106 | SLOT(OnCreateGraphicsSurfaceViewer())); | 110 | SLOT(OnCreateGraphicsSurfaceViewer())); |
| 107 | 111 | ||
| @@ -196,6 +200,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { | |||
| 196 | 200 | ||
| 197 | // Setup hotkeys | 201 | // Setup hotkeys |
| 198 | RegisterHotkey("Main Window", "Load File", QKeySequence::Open); | 202 | RegisterHotkey("Main Window", "Load File", QKeySequence::Open); |
| 203 | RegisterHotkey("Main Window", "Swap Screens", QKeySequence::NextChild); | ||
| 199 | RegisterHotkey("Main Window", "Start Emulation"); | 204 | RegisterHotkey("Main Window", "Start Emulation"); |
| 200 | LoadHotkeys(); | 205 | LoadHotkeys(); |
| 201 | 206 | ||
| @@ -203,6 +208,8 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { | |||
| 203 | SLOT(OnMenuLoadFile())); | 208 | SLOT(OnMenuLoadFile())); |
| 204 | connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, | 209 | connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, |
| 205 | SLOT(OnStartGame())); | 210 | SLOT(OnStartGame())); |
| 211 | connect(GetHotkey("Main Window", "Swap Screens", this), SIGNAL(activated()), this, | ||
| 212 | SLOT(OnSwapScreens())); | ||
| 206 | 213 | ||
| 207 | std::string window_title = | 214 | std::string window_title = |
| 208 | Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); | 215 | Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); |
| @@ -550,6 +557,11 @@ void GMainWindow::OnConfigure() { | |||
| 550 | } | 557 | } |
| 551 | } | 558 | } |
| 552 | 559 | ||
| 560 | void GMainWindow::OnSwapScreens() { | ||
| 561 | Settings::values.swap_screen = !Settings::values.swap_screen; | ||
| 562 | Settings::Apply(); | ||
| 563 | } | ||
| 564 | |||
| 553 | void GMainWindow::OnCreateGraphicsSurfaceViewer() { | 565 | void GMainWindow::OnCreateGraphicsSurfaceViewer() { |
| 554 | auto graphicsSurfaceViewerWidget = new GraphicsSurfaceWidget(Pica::g_debug_context, this); | 566 | auto graphicsSurfaceViewerWidget = new GraphicsSurfaceWidget(Pica::g_debug_context, this); |
| 555 | addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceViewerWidget); | 567 | addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceViewerWidget); |
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 2cf308d80..82eb90aae 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h | |||
| @@ -105,6 +105,7 @@ private slots: | |||
| 105 | /// Called whenever a user selects the "File->Select Game List Root" menu item | 105 | /// Called whenever a user selects the "File->Select Game List Root" menu item |
| 106 | void OnMenuSelectGameListRoot(); | 106 | void OnMenuSelectGameListRoot(); |
| 107 | void OnMenuRecentFile(); | 107 | void OnMenuRecentFile(); |
| 108 | void OnSwapScreens(); | ||
| 108 | void OnConfigure(); | 109 | void OnConfigure(); |
| 109 | void OnDisplayTitleBars(bool); | 110 | void OnDisplayTitleBars(bool); |
| 110 | void ToggleWindowMode(); | 111 | void ToggleWindowMode(); |
diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui index 441e0b81e..adfa3689e 100644 --- a/src/citra_qt/main.ui +++ b/src/citra_qt/main.ui | |||
| @@ -148,7 +148,7 @@ | |||
| 148 | </action> | 148 | </action> |
| 149 | <action name="action_Configure"> | 149 | <action name="action_Configure"> |
| 150 | <property name="text"> | 150 | <property name="text"> |
| 151 | <string>Configure ...</string> | 151 | <string>Configure...</string> |
| 152 | </property> | 152 | </property> |
| 153 | </action> | 153 | </action> |
| 154 | <action name="actionDisplay_widget_title_bars"> | 154 | <action name="actionDisplay_widget_title_bars"> |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index aa6eee2a3..74a271f08 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -5,6 +5,7 @@ set(SRCS | |||
| 5 | break_points.cpp | 5 | break_points.cpp |
| 6 | emu_window.cpp | 6 | emu_window.cpp |
| 7 | file_util.cpp | 7 | file_util.cpp |
| 8 | framebuffer_layout.cpp | ||
| 8 | hash.cpp | 9 | hash.cpp |
| 9 | key_map.cpp | 10 | key_map.cpp |
| 10 | logging/filter.cpp | 11 | logging/filter.cpp |
| @@ -35,6 +36,7 @@ set(HEADERS | |||
| 35 | common_types.h | 36 | common_types.h |
| 36 | emu_window.h | 37 | emu_window.h |
| 37 | file_util.h | 38 | file_util.h |
| 39 | framebuffer_layout.h | ||
| 38 | hash.h | 40 | hash.h |
| 39 | key_map.h | 41 | key_map.h |
| 40 | linear_disk_cache.h | 42 | linear_disk_cache.h |
diff --git a/src/common/common_paths.h b/src/common/common_paths.h index a5342a610..37304d236 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h | |||
| @@ -19,7 +19,7 @@ | |||
| 19 | #define EMU_DATA_DIR USER_DIR | 19 | #define EMU_DATA_DIR USER_DIR |
| 20 | #else | 20 | #else |
| 21 | #ifdef _WIN32 | 21 | #ifdef _WIN32 |
| 22 | #define EMU_DATA_DIR "Citra Emulator" | 22 | #define EMU_DATA_DIR "Citra" |
| 23 | #else | 23 | #else |
| 24 | #define EMU_DATA_DIR "citra-emu" | 24 | #define EMU_DATA_DIR "citra-emu" |
| 25 | #endif | 25 | #endif |
diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp index 122f1c212..e3a9e08e6 100644 --- a/src/common/emu_window.cpp +++ b/src/common/emu_window.cpp | |||
| @@ -40,7 +40,7 @@ void EmuWindow::CirclePadUpdated(float x, float y) { | |||
| 40 | * @param framebuffer_y Framebuffer y-coordinate to check | 40 | * @param framebuffer_y Framebuffer y-coordinate to check |
| 41 | * @return True if the coordinates are within the touchpad, otherwise false | 41 | * @return True if the coordinates are within the touchpad, otherwise false |
| 42 | */ | 42 | */ |
| 43 | static bool IsWithinTouchscreen(const EmuWindow::FramebufferLayout& layout, unsigned framebuffer_x, | 43 | static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, unsigned framebuffer_x, |
| 44 | unsigned framebuffer_y) { | 44 | unsigned framebuffer_y) { |
| 45 | return ( | 45 | return ( |
| 46 | framebuffer_y >= layout.bottom_screen.top && framebuffer_y < layout.bottom_screen.bottom && | 46 | framebuffer_y >= layout.bottom_screen.top && framebuffer_y < layout.bottom_screen.bottom && |
| @@ -89,57 +89,19 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { | |||
| 89 | TouchPressed(framebuffer_x, framebuffer_y); | 89 | TouchPressed(framebuffer_x, framebuffer_y); |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, | 92 | void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { |
| 93 | unsigned height) { | 93 | Layout::FramebufferLayout layout; |
| 94 | // When hiding the widget, the function receives a size of 0 | 94 | switch (Settings::values.layout_option) { |
| 95 | if (width == 0) | 95 | case Settings::LayoutOption::SingleScreen: |
| 96 | width = 1; | 96 | layout = Layout::SingleFrameLayout(width, height, Settings::values.swap_screen); |
| 97 | if (height == 0) | 97 | break; |
| 98 | height = 1; | 98 | case Settings::LayoutOption::LargeScreen: |
| 99 | 99 | layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen); | |
| 100 | EmuWindow::FramebufferLayout res = {width, height, {}, {}}; | 100 | break; |
| 101 | 101 | case Settings::LayoutOption::Default: | |
| 102 | float window_aspect_ratio = static_cast<float>(height) / width; | 102 | default: |
| 103 | float emulation_aspect_ratio = | 103 | layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen); |
| 104 | static_cast<float>(VideoCore::kScreenTopHeight * 2) / VideoCore::kScreenTopWidth; | 104 | break; |
| 105 | |||
| 106 | if (window_aspect_ratio > emulation_aspect_ratio) { | ||
| 107 | // Window is narrower than the emulation content => apply borders to the top and bottom | ||
| 108 | int viewport_height = static_cast<int>(std::round(emulation_aspect_ratio * width)); | ||
| 109 | |||
| 110 | res.top_screen.left = 0; | ||
| 111 | res.top_screen.right = res.top_screen.left + width; | ||
| 112 | res.top_screen.top = (height - viewport_height) / 2; | ||
| 113 | res.top_screen.bottom = res.top_screen.top + viewport_height / 2; | ||
| 114 | |||
| 115 | int bottom_width = static_cast<int>( | ||
| 116 | (static_cast<float>(VideoCore::kScreenBottomWidth) / VideoCore::kScreenTopWidth) * | ||
| 117 | (res.top_screen.right - res.top_screen.left)); | ||
| 118 | int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2; | ||
| 119 | |||
| 120 | res.bottom_screen.left = bottom_border; | ||
| 121 | res.bottom_screen.right = res.bottom_screen.left + bottom_width; | ||
| 122 | res.bottom_screen.top = res.top_screen.bottom; | ||
| 123 | res.bottom_screen.bottom = res.bottom_screen.top + viewport_height / 2; | ||
| 124 | } else { | ||
| 125 | // Otherwise, apply borders to the left and right sides of the window. | ||
| 126 | int viewport_width = static_cast<int>(std::round(height / emulation_aspect_ratio)); | ||
| 127 | |||
| 128 | res.top_screen.left = (width - viewport_width) / 2; | ||
| 129 | res.top_screen.right = res.top_screen.left + viewport_width; | ||
| 130 | res.top_screen.top = 0; | ||
| 131 | res.top_screen.bottom = res.top_screen.top + height / 2; | ||
| 132 | |||
| 133 | int bottom_width = static_cast<int>( | ||
| 134 | (static_cast<float>(VideoCore::kScreenBottomWidth) / VideoCore::kScreenTopWidth) * | ||
| 135 | (res.top_screen.right - res.top_screen.left)); | ||
| 136 | int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2; | ||
| 137 | |||
| 138 | res.bottom_screen.left = res.top_screen.left + bottom_border; | ||
| 139 | res.bottom_screen.right = res.bottom_screen.left + bottom_width; | ||
| 140 | res.bottom_screen.top = res.top_screen.bottom; | ||
| 141 | res.bottom_screen.bottom = res.bottom_screen.top + height / 2; | ||
| 142 | } | 105 | } |
| 143 | 106 | NotifyFramebufferLayoutChanged(layout); | |
| 144 | return res; | ||
| 145 | } | 107 | } |
diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 67df63e06..835c4d500 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <tuple> | 7 | #include <tuple> |
| 8 | #include <utility> | 8 | #include <utility> |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/framebuffer_layout.h" | ||
| 10 | #include "common/math_util.h" | 11 | #include "common/math_util.h" |
| 11 | #include "core/hle/service/hid/hid.h" | 12 | #include "core/hle/service/hid/hid.h" |
| 12 | 13 | ||
| @@ -38,23 +39,6 @@ public: | |||
| 38 | std::pair<unsigned, unsigned> min_client_area_size; | 39 | std::pair<unsigned, unsigned> min_client_area_size; |
| 39 | }; | 40 | }; |
| 40 | 41 | ||
| 41 | /// Describes the layout of the window framebuffer (size and top/bottom screen positions) | ||
| 42 | struct FramebufferLayout { | ||
| 43 | |||
| 44 | /** | ||
| 45 | * Factory method for constructing a default FramebufferLayout | ||
| 46 | * @param width Window framebuffer width in pixels | ||
| 47 | * @param height Window framebuffer height in pixels | ||
| 48 | * @return Newly created FramebufferLayout object with default screen regions initialized | ||
| 49 | */ | ||
| 50 | static FramebufferLayout DefaultScreenLayout(unsigned width, unsigned height); | ||
| 51 | |||
| 52 | unsigned width; | ||
| 53 | unsigned height; | ||
| 54 | MathUtil::Rectangle<unsigned> top_screen; | ||
| 55 | MathUtil::Rectangle<unsigned> bottom_screen; | ||
| 56 | }; | ||
| 57 | |||
| 58 | /// Swap buffers to display the next frame | 42 | /// Swap buffers to display the next frame |
| 59 | virtual void SwapBuffers() = 0; | 43 | virtual void SwapBuffers() = 0; |
| 60 | 44 | ||
| @@ -211,10 +195,16 @@ public: | |||
| 211 | * Gets the framebuffer layout (width, height, and screen regions) | 195 | * Gets the framebuffer layout (width, height, and screen regions) |
| 212 | * @note This method is thread-safe | 196 | * @note This method is thread-safe |
| 213 | */ | 197 | */ |
| 214 | const FramebufferLayout& GetFramebufferLayout() const { | 198 | const Layout::FramebufferLayout& GetFramebufferLayout() const { |
| 215 | return framebuffer_layout; | 199 | return framebuffer_layout; |
| 216 | } | 200 | } |
| 217 | 201 | ||
| 202 | /** | ||
| 203 | * Convenience method to update the current frame layout | ||
| 204 | * Read from the current settings to determine which layout to use. | ||
| 205 | */ | ||
| 206 | void UpdateCurrentFramebufferLayout(unsigned width, unsigned height); | ||
| 207 | |||
| 218 | protected: | 208 | protected: |
| 219 | EmuWindow() { | 209 | EmuWindow() { |
| 220 | // TODO: Find a better place to set this. | 210 | // TODO: Find a better place to set this. |
| @@ -250,7 +240,7 @@ protected: | |||
| 250 | * Update framebuffer layout with the given parameter. | 240 | * Update framebuffer layout with the given parameter. |
| 251 | * @note EmuWindow implementations will usually use this in window resize event handlers. | 241 | * @note EmuWindow implementations will usually use this in window resize event handlers. |
| 252 | */ | 242 | */ |
| 253 | void NotifyFramebufferLayoutChanged(const FramebufferLayout& layout) { | 243 | void NotifyFramebufferLayoutChanged(const Layout::FramebufferLayout& layout) { |
| 254 | framebuffer_layout = layout; | 244 | framebuffer_layout = layout; |
| 255 | } | 245 | } |
| 256 | 246 | ||
| @@ -274,7 +264,7 @@ private: | |||
| 274 | // By default, ignore this request and do nothing. | 264 | // By default, ignore this request and do nothing. |
| 275 | } | 265 | } |
| 276 | 266 | ||
| 277 | FramebufferLayout framebuffer_layout; ///< Current framebuffer layout | 267 | Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout |
| 278 | 268 | ||
| 279 | unsigned client_area_width; ///< Current client width, should be set by window impl. | 269 | unsigned client_area_width; ///< Current client width, should be set by window impl. |
| 280 | unsigned client_area_height; ///< Current client height, should be set by window impl. | 270 | unsigned client_area_height; ///< Current client height, should be set by window impl. |
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 407ed047a..413a8e7e5 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp | |||
| @@ -26,6 +26,9 @@ | |||
| 26 | #define stat _stat64 | 26 | #define stat _stat64 |
| 27 | #define fstat _fstat64 | 27 | #define fstat _fstat64 |
| 28 | #define fileno _fileno | 28 | #define fileno _fileno |
| 29 | // Windows version, at least Vista is required to obtain AppData Path | ||
| 30 | #define WINVER 0x0600 | ||
| 31 | #define _WIN32_WINNT 0x0600 | ||
| 29 | #else | 32 | #else |
| 30 | #ifdef __APPLE__ | 33 | #ifdef __APPLE__ |
| 31 | #include <sys/param.h> | 34 | #include <sys/param.h> |
| @@ -594,6 +597,15 @@ std::string& GetExeDirectory() { | |||
| 594 | } | 597 | } |
| 595 | return exe_path; | 598 | return exe_path; |
| 596 | } | 599 | } |
| 600 | |||
| 601 | std::string AppDataRoamingDirectory() { | ||
| 602 | PWSTR pw_local_path = nullptr; | ||
| 603 | // Only supported by Windows Vista or later | ||
| 604 | SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &pw_local_path); | ||
| 605 | std::string local_path = Common::UTF16ToUTF8(pw_local_path); | ||
| 606 | CoTaskMemFree(pw_local_path); | ||
| 607 | return local_path; | ||
| 608 | } | ||
| 597 | #else | 609 | #else |
| 598 | /** | 610 | /** |
| 599 | * @return The user’s home directory on POSIX systems | 611 | * @return The user’s home directory on POSIX systems |
| @@ -671,6 +683,10 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string& new | |||
| 671 | if (paths[D_USER_IDX].empty()) { | 683 | if (paths[D_USER_IDX].empty()) { |
| 672 | #ifdef _WIN32 | 684 | #ifdef _WIN32 |
| 673 | paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; | 685 | paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; |
| 686 | if (!FileUtil::IsDirectory(paths[D_USER_IDX])) { | ||
| 687 | paths[D_USER_IDX] = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP; | ||
| 688 | } | ||
| 689 | |||
| 674 | paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; | 690 | paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; |
| 675 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | 691 | paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; |
| 676 | #else | 692 | #else |
diff --git a/src/common/file_util.h b/src/common/file_util.h index 204b06f14..ac58607c5 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h | |||
| @@ -154,6 +154,7 @@ std::string GetBundleDirectory(); | |||
| 154 | 154 | ||
| 155 | #ifdef _WIN32 | 155 | #ifdef _WIN32 |
| 156 | std::string& GetExeDirectory(); | 156 | std::string& GetExeDirectory(); |
| 157 | std::string AppDataRoamingDirectory(); | ||
| 157 | #endif | 158 | #endif |
| 158 | 159 | ||
| 159 | size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename); | 160 | size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename); |
diff --git a/src/common/framebuffer_layout.cpp b/src/common/framebuffer_layout.cpp new file mode 100644 index 000000000..46c008d9c --- /dev/null +++ b/src/common/framebuffer_layout.cpp | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cmath> | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/framebuffer_layout.h" | ||
| 9 | #include "video_core/video_core.h" | ||
| 10 | |||
| 11 | namespace Layout { | ||
| 12 | |||
| 13 | static const float TOP_SCREEN_ASPECT_RATIO = | ||
| 14 | static_cast<float>(VideoCore::kScreenTopHeight) / VideoCore::kScreenTopWidth; | ||
| 15 | static const float BOT_SCREEN_ASPECT_RATIO = | ||
| 16 | static_cast<float>(VideoCore::kScreenBottomHeight) / VideoCore::kScreenBottomWidth; | ||
| 17 | |||
| 18 | // Finds the largest size subrectangle contained in window area that is confined to the aspect ratio | ||
| 19 | template <class T> | ||
| 20 | static MathUtil::Rectangle<T> maxRectangle(MathUtil::Rectangle<T> window_area, | ||
| 21 | float screen_aspect_ratio) { | ||
| 22 | float scale = std::min(static_cast<float>(window_area.GetWidth()), | ||
| 23 | window_area.GetHeight() / screen_aspect_ratio); | ||
| 24 | return MathUtil::Rectangle<T>{0, 0, static_cast<T>(std::round(scale)), | ||
| 25 | static_cast<T>(std::round(scale * screen_aspect_ratio))}; | ||
| 26 | } | ||
| 27 | |||
| 28 | FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height, bool swapped) { | ||
| 29 | ASSERT(width > 0); | ||
| 30 | ASSERT(height > 0); | ||
| 31 | |||
| 32 | FramebufferLayout res{width, height, true, true, {}, {}}; | ||
| 33 | // Default layout gives equal screen sizes to the top and bottom screen | ||
| 34 | MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height / 2}; | ||
| 35 | MathUtil::Rectangle<unsigned> top_screen = | ||
| 36 | maxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO); | ||
| 37 | MathUtil::Rectangle<unsigned> bot_screen = | ||
| 38 | maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO); | ||
| 39 | |||
| 40 | float window_aspect_ratio = static_cast<float>(height) / width; | ||
| 41 | // both screens height are taken into account by multiplying by 2 | ||
| 42 | float emulation_aspect_ratio = TOP_SCREEN_ASPECT_RATIO * 2; | ||
| 43 | |||
| 44 | if (window_aspect_ratio < emulation_aspect_ratio) { | ||
| 45 | // Apply borders to the left and right sides of the window. | ||
| 46 | top_screen = | ||
| 47 | top_screen.TranslateX((screen_window_area.GetWidth() - top_screen.GetWidth()) / 2); | ||
| 48 | bot_screen = | ||
| 49 | bot_screen.TranslateX((screen_window_area.GetWidth() - bot_screen.GetWidth()) / 2); | ||
| 50 | } else { | ||
| 51 | // Window is narrower than the emulation content => apply borders to the top and bottom | ||
| 52 | // Recalculate the bottom screen to account for the width difference between top and bottom | ||
| 53 | screen_window_area = {0, 0, width, top_screen.GetHeight()}; | ||
| 54 | bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO); | ||
| 55 | bot_screen = bot_screen.TranslateX((top_screen.GetWidth() - bot_screen.GetWidth()) / 2); | ||
| 56 | if (swapped) { | ||
| 57 | bot_screen = bot_screen.TranslateY(height / 2 - bot_screen.GetHeight()); | ||
| 58 | } else { | ||
| 59 | top_screen = top_screen.TranslateY(height / 2 - top_screen.GetHeight()); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | // Move the top screen to the bottom if we are swapped. | ||
| 63 | res.top_screen = swapped ? top_screen.TranslateY(height / 2) : top_screen; | ||
| 64 | res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateY(height / 2); | ||
| 65 | return res; | ||
| 66 | } | ||
| 67 | |||
| 68 | FramebufferLayout SingleFrameLayout(unsigned width, unsigned height, bool swapped) { | ||
| 69 | ASSERT(width > 0); | ||
| 70 | ASSERT(height > 0); | ||
| 71 | // The drawing code needs at least somewhat valid values for both screens | ||
| 72 | // so just calculate them both even if the other isn't showing. | ||
| 73 | FramebufferLayout res{width, height, !swapped, swapped, {}, {}}; | ||
| 74 | |||
| 75 | MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height}; | ||
| 76 | MathUtil::Rectangle<unsigned> top_screen = | ||
| 77 | maxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO); | ||
| 78 | MathUtil::Rectangle<unsigned> bot_screen = | ||
| 79 | maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO); | ||
| 80 | |||
| 81 | float window_aspect_ratio = static_cast<float>(height) / width; | ||
| 82 | float emulation_aspect_ratio = (swapped) ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO; | ||
| 83 | |||
| 84 | if (window_aspect_ratio < emulation_aspect_ratio) { | ||
| 85 | top_screen = | ||
| 86 | top_screen.TranslateX((screen_window_area.GetWidth() - top_screen.GetWidth()) / 2); | ||
| 87 | bot_screen = | ||
| 88 | bot_screen.TranslateX((screen_window_area.GetWidth() - bot_screen.GetWidth()) / 2); | ||
| 89 | } else { | ||
| 90 | top_screen = top_screen.TranslateY((height - top_screen.GetHeight()) / 2); | ||
| 91 | bot_screen = bot_screen.TranslateY((height - bot_screen.GetHeight()) / 2); | ||
| 92 | } | ||
| 93 | res.top_screen = top_screen; | ||
| 94 | res.bottom_screen = bot_screen; | ||
| 95 | return res; | ||
| 96 | } | ||
| 97 | |||
| 98 | FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool swapped) { | ||
| 99 | ASSERT(width > 0); | ||
| 100 | ASSERT(height > 0); | ||
| 101 | |||
| 102 | FramebufferLayout res{width, height, true, true, {}, {}}; | ||
| 103 | // Split the window into two parts. Give 4x width to the main screen and 1x width to the small | ||
| 104 | // To do that, find the total emulation box and maximize that based on window size | ||
| 105 | float window_aspect_ratio = static_cast<float>(height) / width; | ||
| 106 | float emulation_aspect_ratio = | ||
| 107 | swapped | ||
| 108 | ? VideoCore::kScreenBottomHeight * 4 / | ||
| 109 | (VideoCore::kScreenBottomWidth * 4.0f + VideoCore::kScreenTopWidth) | ||
| 110 | : VideoCore::kScreenTopHeight * 4 / | ||
| 111 | (VideoCore::kScreenTopWidth * 4.0f + VideoCore::kScreenBottomWidth); | ||
| 112 | float large_screen_aspect_ratio = swapped ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO; | ||
| 113 | float small_screen_aspect_ratio = swapped ? TOP_SCREEN_ASPECT_RATIO : BOT_SCREEN_ASPECT_RATIO; | ||
| 114 | |||
| 115 | MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height}; | ||
| 116 | MathUtil::Rectangle<unsigned> total_rect = | ||
| 117 | maxRectangle(screen_window_area, emulation_aspect_ratio); | ||
| 118 | MathUtil::Rectangle<unsigned> large_screen = | ||
| 119 | maxRectangle(total_rect, large_screen_aspect_ratio); | ||
| 120 | MathUtil::Rectangle<unsigned> fourth_size_rect = total_rect.Scale(.25f); | ||
| 121 | MathUtil::Rectangle<unsigned> small_screen = | ||
| 122 | maxRectangle(fourth_size_rect, small_screen_aspect_ratio); | ||
| 123 | |||
| 124 | if (window_aspect_ratio < emulation_aspect_ratio) { | ||
| 125 | large_screen = | ||
| 126 | large_screen.TranslateX((screen_window_area.GetWidth() - total_rect.GetWidth()) / 2); | ||
| 127 | } else { | ||
| 128 | large_screen = large_screen.TranslateY((height - total_rect.GetHeight()) / 2); | ||
| 129 | } | ||
| 130 | // Shift the small screen to the bottom right corner | ||
| 131 | small_screen = | ||
| 132 | small_screen.TranslateX(large_screen.right) | ||
| 133 | .TranslateY(large_screen.GetHeight() + large_screen.top - small_screen.GetHeight()); | ||
| 134 | res.top_screen = swapped ? small_screen : large_screen; | ||
| 135 | res.bottom_screen = swapped ? large_screen : small_screen; | ||
| 136 | return res; | ||
| 137 | } | ||
| 138 | } | ||
diff --git a/src/common/framebuffer_layout.h b/src/common/framebuffer_layout.h new file mode 100644 index 000000000..a125646a3 --- /dev/null +++ b/src/common/framebuffer_layout.h | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/math_util.h" | ||
| 8 | namespace Layout { | ||
| 9 | /// Describes the layout of the window framebuffer (size and top/bottom screen positions) | ||
| 10 | struct FramebufferLayout { | ||
| 11 | unsigned width; | ||
| 12 | unsigned height; | ||
| 13 | bool top_screen_enabled; | ||
| 14 | bool bottom_screen_enabled; | ||
| 15 | MathUtil::Rectangle<unsigned> top_screen; | ||
| 16 | MathUtil::Rectangle<unsigned> bottom_screen; | ||
| 17 | }; | ||
| 18 | |||
| 19 | /** | ||
| 20 | * Factory method for constructing a default FramebufferLayout | ||
| 21 | * @param width Window framebuffer width in pixels | ||
| 22 | * @param height Window framebuffer height in pixels | ||
| 23 | * @param is_swapped if true, the bottom screen will be displayed above the top screen | ||
| 24 | * @return Newly created FramebufferLayout object with default screen regions initialized | ||
| 25 | */ | ||
| 26 | FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height, bool is_swapped); | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Factory method for constructing a FramebufferLayout with only the top or bottom screen | ||
| 30 | * @param width Window framebuffer width in pixels | ||
| 31 | * @param height Window framebuffer height in pixels | ||
| 32 | * @param is_swapped if true, the bottom screen will be displayed (and the top won't be displayed) | ||
| 33 | * @return Newly created FramebufferLayout object with default screen regions initialized | ||
| 34 | */ | ||
| 35 | FramebufferLayout SingleFrameLayout(unsigned width, unsigned height, bool is_swapped); | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Factory method for constructing a Frame with the a 4x size Top screen with a 1x size bottom | ||
| 39 | * screen on the right | ||
| 40 | * This is useful in particular because it matches well with a 1920x1080 resolution monitor | ||
| 41 | * @param width Window framebuffer width in pixels | ||
| 42 | * @param height Window framebuffer height in pixels | ||
| 43 | * @param is_swapped if true, the bottom screen will be the large display | ||
| 44 | * @return Newly created FramebufferLayout object with default screen regions initialized | ||
| 45 | */ | ||
| 46 | FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped); | ||
| 47 | } | ||
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 88209081d..7fd397fe5 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -43,6 +43,7 @@ namespace Log { | |||
| 43 | SUB(Service, AM) \ | 43 | SUB(Service, AM) \ |
| 44 | SUB(Service, PTM) \ | 44 | SUB(Service, PTM) \ |
| 45 | SUB(Service, LDR) \ | 45 | SUB(Service, LDR) \ |
| 46 | SUB(Service, MIC) \ | ||
| 46 | SUB(Service, NDM) \ | 47 | SUB(Service, NDM) \ |
| 47 | SUB(Service, NIM) \ | 48 | SUB(Service, NIM) \ |
| 48 | SUB(Service, NWM) \ | 49 | SUB(Service, NWM) \ |
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 8d3a2d03e..8011534b8 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -60,6 +60,7 @@ enum class Class : ClassType { | |||
| 60 | Service_AM, ///< The AM (Application manager) service | 60 | Service_AM, ///< The AM (Application manager) service |
| 61 | Service_PTM, ///< The PTM (Power status & misc.) service | 61 | Service_PTM, ///< The PTM (Power status & misc.) service |
| 62 | Service_LDR, ///< The LDR (3ds dll loader) service | 62 | Service_LDR, ///< The LDR (3ds dll loader) service |
| 63 | Service_MIC, ///< The MIC (microphone) service | ||
| 63 | Service_NDM, ///< The NDM (Network daemon manager) service | 64 | Service_NDM, ///< The NDM (Network daemon manager) service |
| 64 | Service_NIM, ///< The NIM (Network interface manager) service | 65 | Service_NIM, ///< The NIM (Network interface manager) service |
| 65 | Service_NWM, ///< The NWM (Network wlan manager) service | 66 | Service_NWM, ///< The NWM (Network wlan manager) service |
diff --git a/src/common/math_util.h b/src/common/math_util.h index 41d89666c..cdeaeb733 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h | |||
| @@ -38,6 +38,16 @@ struct Rectangle { | |||
| 38 | T GetHeight() const { | 38 | T GetHeight() const { |
| 39 | return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top)); | 39 | return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top)); |
| 40 | } | 40 | } |
| 41 | Rectangle<T> TranslateX(const T x) const { | ||
| 42 | return Rectangle{left + x, top, right + x, bottom}; | ||
| 43 | } | ||
| 44 | Rectangle<T> TranslateY(const T y) const { | ||
| 45 | return Rectangle{left, top + y, right, bottom + y}; | ||
| 46 | } | ||
| 47 | Rectangle<T> Scale(const float s) const { | ||
| 48 | return Rectangle{left, top, static_cast<T>(left + GetWidth() * s), | ||
| 49 | static_cast<T>(top + GetHeight() * s)}; | ||
| 50 | } | ||
| 41 | }; | 51 | }; |
| 42 | 52 | ||
| 43 | } // namespace MathUtil | 53 | } // namespace MathUtil |
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 596ae01bf..df1008180 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp | |||
| @@ -11,7 +11,8 @@ | |||
| 11 | #include "common/common_paths.h" | 11 | #include "common/common_paths.h" |
| 12 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 13 | #include "common/string_util.h" | 13 | #include "common/string_util.h" |
| 14 | #ifdef _MSC_VER | 14 | |
| 15 | #ifdef _WIN32 | ||
| 15 | #include <codecvt> | 16 | #include <codecvt> |
| 16 | #include <Windows.h> | 17 | #include <Windows.h> |
| 17 | #include "common/common_funcs.h" | 18 | #include "common/common_funcs.h" |
| @@ -270,7 +271,7 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st | |||
| 270 | return result; | 271 | return result; |
| 271 | } | 272 | } |
| 272 | 273 | ||
| 273 | #ifdef _MSC_VER | 274 | #ifdef _WIN32 |
| 274 | 275 | ||
| 275 | std::string UTF16ToUTF8(const std::u16string& input) { | 276 | std::string UTF16ToUTF8(const std::u16string& input) { |
| 276 | #if _MSC_VER >= 1900 | 277 | #if _MSC_VER >= 1900 |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index b4444c869..ca8a94ee9 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp | |||
| @@ -52,6 +52,7 @@ static Dynarmic::UserCallbacks GetUserCallbacks(ARMul_State* interpeter_state) { | |||
| 52 | user_callbacks.MemoryWrite16 = &Memory::Write16; | 52 | user_callbacks.MemoryWrite16 = &Memory::Write16; |
| 53 | user_callbacks.MemoryWrite32 = &Memory::Write32; | 53 | user_callbacks.MemoryWrite32 = &Memory::Write32; |
| 54 | user_callbacks.MemoryWrite64 = &Memory::Write64; | 54 | user_callbacks.MemoryWrite64 = &Memory::Write64; |
| 55 | user_callbacks.page_table = Memory::GetCurrentPageTablePointers(); | ||
| 55 | return user_callbacks; | 56 | return user_callbacks; |
| 56 | } | 57 | } |
| 57 | 58 | ||
| @@ -130,9 +131,9 @@ MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); | |||
| 130 | void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { | 131 | void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { |
| 131 | MICROPROFILE_SCOPE(ARM_Jit); | 132 | MICROPROFILE_SCOPE(ARM_Jit); |
| 132 | 133 | ||
| 133 | jit->Run(static_cast<unsigned>(num_instructions)); | 134 | unsigned ticks_executed = jit->Run(static_cast<unsigned>(num_instructions)); |
| 134 | 135 | ||
| 135 | AddTicks(num_instructions); | 136 | AddTicks(ticks_executed); |
| 136 | } | 137 | } |
| 137 | 138 | ||
| 138 | void ARM_Dynarmic::SaveContext(Core::ThreadContext& ctx) { | 139 | void ARM_Dynarmic::SaveContext(Core::ThreadContext& ctx) { |
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index ceb993ea1..aea43e92b 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | #include <numeric> | 14 | #include <numeric> |
| 15 | #include <fcntl.h> | 15 | #include <fcntl.h> |
| 16 | 16 | ||
| 17 | #ifdef _MSC_VER | 17 | #ifdef _WIN32 |
| 18 | #include <WinSock2.h> | 18 | #include <WinSock2.h> |
| 19 | #include <common/x64/abi.h> | 19 | #include <common/x64/abi.h> |
| 20 | #include <io.h> | 20 | #include <io.h> |
diff --git a/src/core/hle/applets/erreula.cpp b/src/core/hle/applets/erreula.cpp index 14964427b..e1379ac4d 100644 --- a/src/core/hle/applets/erreula.cpp +++ b/src/core/hle/applets/erreula.cpp | |||
| @@ -10,7 +10,7 @@ namespace HLE { | |||
| 10 | namespace Applets { | 10 | namespace Applets { |
| 11 | 11 | ||
| 12 | ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& parameter) { | 12 | ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& parameter) { |
| 13 | if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { | 13 | if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) { |
| 14 | LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); | 14 | LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); |
| 15 | UNIMPLEMENTED(); | 15 | UNIMPLEMENTED(); |
| 16 | // TODO(Subv): Find the right error code | 16 | // TODO(Subv): Find the right error code |
| @@ -36,7 +36,7 @@ ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& param | |||
| 36 | 36 | ||
| 37 | // Send the response message with the newly created SharedMemory | 37 | // Send the response message with the newly created SharedMemory |
| 38 | Service::APT::MessageParameter result; | 38 | Service::APT::MessageParameter result; |
| 39 | result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); | 39 | result.signal = static_cast<u32>(Service::APT::SignalType::Response); |
| 40 | result.buffer.clear(); | 40 | result.buffer.clear(); |
| 41 | result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); | 41 | result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); |
| 42 | result.sender_id = static_cast<u32>(id); | 42 | result.sender_id = static_cast<u32>(id); |
| @@ -57,7 +57,7 @@ ResultCode ErrEula::StartImpl(const Service::APT::AppletStartupParameter& parame | |||
| 57 | Service::APT::MessageParameter message; | 57 | Service::APT::MessageParameter message; |
| 58 | message.buffer.resize(parameter.buffer.size()); | 58 | message.buffer.resize(parameter.buffer.size()); |
| 59 | std::fill(message.buffer.begin(), message.buffer.end(), 0); | 59 | std::fill(message.buffer.begin(), message.buffer.end(), 0); |
| 60 | message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); | 60 | message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit); |
| 61 | message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); | 61 | message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); |
| 62 | message.sender_id = static_cast<u32>(id); | 62 | message.sender_id = static_cast<u32>(id); |
| 63 | Service::APT::SendParameter(message); | 63 | Service::APT::SendParameter(message); |
diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp index 53a8683a4..3455b9201 100644 --- a/src/core/hle/applets/mii_selector.cpp +++ b/src/core/hle/applets/mii_selector.cpp | |||
| @@ -19,7 +19,7 @@ namespace HLE { | |||
| 19 | namespace Applets { | 19 | namespace Applets { |
| 20 | 20 | ||
| 21 | ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& parameter) { | 21 | ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& parameter) { |
| 22 | if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { | 22 | if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) { |
| 23 | LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); | 23 | LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); |
| 24 | UNIMPLEMENTED(); | 24 | UNIMPLEMENTED(); |
| 25 | // TODO(Subv): Find the right error code | 25 | // TODO(Subv): Find the right error code |
| @@ -44,7 +44,7 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p | |||
| 44 | 44 | ||
| 45 | // Send the response message with the newly created SharedMemory | 45 | // Send the response message with the newly created SharedMemory |
| 46 | Service::APT::MessageParameter result; | 46 | Service::APT::MessageParameter result; |
| 47 | result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); | 47 | result.signal = static_cast<u32>(Service::APT::SignalType::Response); |
| 48 | result.buffer.clear(); | 48 | result.buffer.clear(); |
| 49 | result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); | 49 | result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); |
| 50 | result.sender_id = static_cast<u32>(id); | 50 | result.sender_id = static_cast<u32>(id); |
| @@ -73,7 +73,7 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa | |||
| 73 | Service::APT::MessageParameter message; | 73 | Service::APT::MessageParameter message; |
| 74 | message.buffer.resize(sizeof(MiiResult)); | 74 | message.buffer.resize(sizeof(MiiResult)); |
| 75 | std::memcpy(message.buffer.data(), &result, message.buffer.size()); | 75 | std::memcpy(message.buffer.data(), &result, message.buffer.size()); |
| 76 | message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); | 76 | message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit); |
| 77 | message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); | 77 | message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); |
| 78 | message.sender_id = static_cast<u32>(id); | 78 | message.sender_id = static_cast<u32>(id); |
| 79 | Service::APT::SendParameter(message); | 79 | Service::APT::SendParameter(message); |
diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp index 06ddf538b..1e21337f5 100644 --- a/src/core/hle/applets/swkbd.cpp +++ b/src/core/hle/applets/swkbd.cpp | |||
| @@ -22,7 +22,7 @@ namespace HLE { | |||
| 22 | namespace Applets { | 22 | namespace Applets { |
| 23 | 23 | ||
| 24 | ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) { | 24 | ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) { |
| 25 | if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { | 25 | if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) { |
| 26 | LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); | 26 | LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); |
| 27 | UNIMPLEMENTED(); | 27 | UNIMPLEMENTED(); |
| 28 | // TODO(Subv): Find the right error code | 28 | // TODO(Subv): Find the right error code |
| @@ -47,7 +47,7 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con | |||
| 47 | 47 | ||
| 48 | // Send the response message with the newly created SharedMemory | 48 | // Send the response message with the newly created SharedMemory |
| 49 | Service::APT::MessageParameter result; | 49 | Service::APT::MessageParameter result; |
| 50 | result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); | 50 | result.signal = static_cast<u32>(Service::APT::SignalType::Response); |
| 51 | result.buffer.clear(); | 51 | result.buffer.clear(); |
| 52 | result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); | 52 | result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); |
| 53 | result.sender_id = static_cast<u32>(id); | 53 | result.sender_id = static_cast<u32>(id); |
| @@ -108,7 +108,7 @@ void SoftwareKeyboard::Finalize() { | |||
| 108 | Service::APT::MessageParameter message; | 108 | Service::APT::MessageParameter message; |
| 109 | message.buffer.resize(sizeof(SoftwareKeyboardConfig)); | 109 | message.buffer.resize(sizeof(SoftwareKeyboardConfig)); |
| 110 | std::memcpy(message.buffer.data(), &config, message.buffer.size()); | 110 | std::memcpy(message.buffer.data(), &config, message.buffer.size()); |
| 111 | message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); | 111 | message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit); |
| 112 | message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); | 112 | message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); |
| 113 | message.sender_id = static_cast<u32>(id); | 113 | message.sender_id = static_cast<u32>(id); |
| 114 | Service::APT::SendParameter(message); | 114 | Service::APT::SendParameter(message); |
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 1489c7002..3e116e3df 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp | |||
| @@ -22,6 +22,11 @@ SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) { | |||
| 22 | evt->reset_type = reset_type; | 22 | evt->reset_type = reset_type; |
| 23 | evt->name = std::move(name); | 23 | evt->name = std::move(name); |
| 24 | 24 | ||
| 25 | if (reset_type == ResetType::Pulse) { | ||
| 26 | LOG_ERROR(Kernel, "Unimplemented event reset type Pulse"); | ||
| 27 | UNIMPLEMENTED(); | ||
| 28 | } | ||
| 29 | |||
| 25 | return evt; | 30 | return evt; |
| 26 | } | 31 | } |
| 27 | 32 | ||
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index a9f98223c..eac181f4e 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp | |||
| @@ -31,6 +31,11 @@ SharedPtr<Timer> Timer::Create(ResetType reset_type, std::string name) { | |||
| 31 | timer->interval_delay = 0; | 31 | timer->interval_delay = 0; |
| 32 | timer->callback_handle = timer_callback_handle_table.Create(timer).MoveFrom(); | 32 | timer->callback_handle = timer_callback_handle_table.Create(timer).MoveFrom(); |
| 33 | 33 | ||
| 34 | if (reset_type == ResetType::Pulse) { | ||
| 35 | LOG_ERROR(Kernel, "Unimplemented timer reset type Pulse"); | ||
| 36 | UNIMPLEMENTED(); | ||
| 37 | } | ||
| 38 | |||
| 34 | return timer; | 39 | return timer; |
| 35 | } | 40 | } |
| 36 | 41 | ||
diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp index 12d94f37a..18026975f 100644 --- a/src/core/hle/service/ac_u.cpp +++ b/src/core/hle/service/ac_u.cpp | |||
| @@ -11,11 +11,85 @@ | |||
| 11 | 11 | ||
| 12 | namespace AC_U { | 12 | namespace AC_U { |
| 13 | 13 | ||
| 14 | struct ACConfig { | ||
| 15 | std::array<u8, 0x200> data; | ||
| 16 | }; | ||
| 17 | |||
| 18 | static ACConfig default_config{}; | ||
| 19 | |||
| 20 | static bool ac_connected = false; | ||
| 21 | |||
| 22 | static Kernel::SharedPtr<Kernel::Event> close_event; | ||
| 23 | static Kernel::SharedPtr<Kernel::Event> connect_event; | ||
| 24 | static Kernel::SharedPtr<Kernel::Event> disconnect_event; | ||
| 25 | |||
| 26 | /** | ||
| 27 | * AC_U::CreateDefaultConfig service function | ||
| 28 | * Inputs: | ||
| 29 | * 64 : ACConfig size << 14 | 2 | ||
| 30 | * 65 : pointer to ACConfig struct | ||
| 31 | * Outputs: | ||
| 32 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 33 | */ | ||
| 34 | static void CreateDefaultConfig(Service::Interface* self) { | ||
| 35 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 36 | |||
| 37 | u32 ac_config_addr = cmd_buff[65]; | ||
| 38 | |||
| 39 | ASSERT_MSG(cmd_buff[64] == (sizeof(ACConfig) << 14 | 2), | ||
| 40 | "Output buffer size not equal ACConfig size"); | ||
| 41 | |||
| 42 | Memory::WriteBlock(ac_config_addr, &default_config, sizeof(ACConfig)); | ||
| 43 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 44 | |||
| 45 | LOG_WARNING(Service_AC, "(STUBBED) called"); | ||
| 46 | } | ||
| 47 | |||
| 48 | /** | ||
| 49 | * AC_U::ConnectAsync service function | ||
| 50 | * Inputs: | ||
| 51 | * 1 : ProcessId Header | ||
| 52 | * 3 : Copy Handle Header | ||
| 53 | * 4 : Connection Event handle | ||
| 54 | * 5 : ACConfig size << 14 | 2 | ||
| 55 | * 6 : pointer to ACConfig struct | ||
| 56 | * Outputs: | ||
| 57 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 58 | */ | ||
| 59 | static void ConnectAsync(Service::Interface* self) { | ||
| 60 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 61 | |||
| 62 | connect_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); | ||
| 63 | if (connect_event) { | ||
| 64 | connect_event->name = "AC_U:connect_event"; | ||
| 65 | connect_event->Signal(); | ||
| 66 | ac_connected = true; | ||
| 67 | } | ||
| 68 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 69 | |||
| 70 | LOG_WARNING(Service_AC, "(STUBBED) called"); | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * AC_U::GetConnectResult service function | ||
| 75 | * Inputs: | ||
| 76 | * 1 : ProcessId Header | ||
| 77 | * Outputs: | ||
| 78 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 79 | */ | ||
| 80 | static void GetConnectResult(Service::Interface* self) { | ||
| 81 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 82 | |||
| 83 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 84 | |||
| 85 | LOG_WARNING(Service_AC, "(STUBBED) called"); | ||
| 86 | } | ||
| 87 | |||
| 14 | /** | 88 | /** |
| 15 | * AC_U::CloseAsync service function | 89 | * AC_U::CloseAsync service function |
| 16 | * Inputs: | 90 | * Inputs: |
| 17 | * 1 : Always 0x20 | 91 | * 1 : ProcessId Header |
| 18 | * 3 : Always 0 | 92 | * 3 : Copy Handle Header |
| 19 | * 4 : Event handle, should be signaled when AC connection is closed | 93 | * 4 : Event handle, should be signaled when AC connection is closed |
| 20 | * Outputs: | 94 | * Outputs: |
| 21 | * 1 : Result of function, 0 on success, otherwise error code | 95 | * 1 : Result of function, 0 on success, otherwise error code |
| @@ -23,16 +97,37 @@ namespace AC_U { | |||
| 23 | static void CloseAsync(Service::Interface* self) { | 97 | static void CloseAsync(Service::Interface* self) { |
| 24 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 98 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 25 | 99 | ||
| 26 | auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); | 100 | if (ac_connected && disconnect_event) { |
| 101 | disconnect_event->Signal(); | ||
| 102 | } | ||
| 27 | 103 | ||
| 28 | if (evt) { | 104 | close_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); |
| 29 | evt->name = "AC_U:close_event"; | 105 | if (close_event) { |
| 30 | evt->Signal(); | 106 | close_event->name = "AC_U:close_event"; |
| 107 | close_event->Signal(); | ||
| 31 | } | 108 | } |
| 109 | |||
| 110 | ac_connected = false; | ||
| 111 | |||
| 112 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 113 | LOG_WARNING(Service_AC, "(STUBBED) called"); | ||
| 114 | } | ||
| 115 | |||
| 116 | /** | ||
| 117 | * AC_U::GetCloseResult service function | ||
| 118 | * Inputs: | ||
| 119 | * 1 : ProcessId Header | ||
| 120 | * Outputs: | ||
| 121 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 122 | */ | ||
| 123 | static void GetCloseResult(Service::Interface* self) { | ||
| 124 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 125 | |||
| 32 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 126 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 33 | 127 | ||
| 34 | LOG_WARNING(Service_AC, "(STUBBED) called"); | 128 | LOG_WARNING(Service_AC, "(STUBBED) called"); |
| 35 | } | 129 | } |
| 130 | |||
| 36 | /** | 131 | /** |
| 37 | * AC_U::GetWifiStatus service function | 132 | * AC_U::GetWifiStatus service function |
| 38 | * Outputs: | 133 | * Outputs: |
| @@ -52,6 +147,75 @@ static void GetWifiStatus(Service::Interface* self) { | |||
| 52 | } | 147 | } |
| 53 | 148 | ||
| 54 | /** | 149 | /** |
| 150 | * AC_U::GetInfraPriority service function | ||
| 151 | * Inputs: | ||
| 152 | * 1 : ACConfig size << 14 | 2 | ||
| 153 | * 2 : pointer to ACConfig struct | ||
| 154 | * Outputs: | ||
| 155 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 156 | * 2 : Infra Priority | ||
| 157 | */ | ||
| 158 | static void GetInfraPriority(Service::Interface* self) { | ||
| 159 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 160 | |||
| 161 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 162 | cmd_buff[2] = 0; // Infra Priority, default 0 | ||
| 163 | |||
| 164 | LOG_WARNING(Service_AC, "(STUBBED) called"); | ||
| 165 | } | ||
| 166 | |||
| 167 | /** | ||
| 168 | * AC_U::SetRequestEulaVersion service function | ||
| 169 | * Inputs: | ||
| 170 | * 1 : Eula Version major | ||
| 171 | * 2 : Eula Version minor | ||
| 172 | * 3 : ACConfig size << 14 | 2 | ||
| 173 | * 4 : Input pointer to ACConfig struct | ||
| 174 | * 64 : ACConfig size << 14 | 2 | ||
| 175 | * 65 : Output pointer to ACConfig struct | ||
| 176 | * Outputs: | ||
| 177 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 178 | * 2 : Infra Priority | ||
| 179 | */ | ||
| 180 | static void SetRequestEulaVersion(Service::Interface* self) { | ||
| 181 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 182 | |||
| 183 | u32 major = cmd_buff[1] & 0xFF; | ||
| 184 | u32 minor = cmd_buff[2] & 0xFF; | ||
| 185 | |||
| 186 | ASSERT_MSG(cmd_buff[3] == (sizeof(ACConfig) << 14 | 2), | ||
| 187 | "Input buffer size not equal ACConfig size"); | ||
| 188 | ASSERT_MSG(cmd_buff[64] == (sizeof(ACConfig) << 14 | 2), | ||
| 189 | "Output buffer size not equal ACConfig size"); | ||
| 190 | |||
| 191 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 192 | cmd_buff[2] = 0; // Infra Priority | ||
| 193 | |||
| 194 | LOG_WARNING(Service_AC, "(STUBBED) called, major=%u, minor=%u", major, minor); | ||
| 195 | } | ||
| 196 | |||
| 197 | /** | ||
| 198 | * AC_U::RegisterDisconnectEvent service function | ||
| 199 | * Inputs: | ||
| 200 | * 1 : ProcessId Header | ||
| 201 | * 3 : Copy Handle Header | ||
| 202 | * 4 : Event handle, should be signaled when AC connection is closed | ||
| 203 | * Outputs: | ||
| 204 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 205 | */ | ||
| 206 | static void RegisterDisconnectEvent(Service::Interface* self) { | ||
| 207 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 208 | |||
| 209 | disconnect_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); | ||
| 210 | if (disconnect_event) { | ||
| 211 | disconnect_event->name = "AC_U:disconnect_event"; | ||
| 212 | } | ||
| 213 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 214 | |||
| 215 | LOG_WARNING(Service_AC, "(STUBBED) called"); | ||
| 216 | } | ||
| 217 | |||
| 218 | /** | ||
| 55 | * AC_U::IsConnected service function | 219 | * AC_U::IsConnected service function |
| 56 | * Outputs: | 220 | * Outputs: |
| 57 | * 1 : Result of function, 0 on success, otherwise error code | 221 | * 1 : Result of function, 0 on success, otherwise error code |
| @@ -61,26 +225,29 @@ static void IsConnected(Service::Interface* self) { | |||
| 61 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 225 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 62 | 226 | ||
| 63 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 227 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 64 | cmd_buff[2] = false; // Not connected to ac:u service | 228 | cmd_buff[2] = ac_connected; |
| 65 | 229 | ||
| 66 | LOG_WARNING(Service_AC, "(STUBBED) called"); | 230 | LOG_WARNING(Service_AC, "(STUBBED) called"); |
| 67 | } | 231 | } |
| 68 | 232 | ||
| 69 | const Interface::FunctionInfo FunctionTable[] = { | 233 | const Interface::FunctionInfo FunctionTable[] = { |
| 70 | {0x00010000, nullptr, "CreateDefaultConfig"}, | 234 | {0x00010000, CreateDefaultConfig, "CreateDefaultConfig"}, |
| 71 | {0x00040006, nullptr, "ConnectAsync"}, | 235 | {0x00040006, ConnectAsync, "ConnectAsync"}, |
| 72 | {0x00050002, nullptr, "GetConnectResult"}, | 236 | {0x00050002, GetConnectResult, "GetConnectResult"}, |
| 237 | {0x00070002, nullptr, "CancelConnectAsync"}, | ||
| 73 | {0x00080004, CloseAsync, "CloseAsync"}, | 238 | {0x00080004, CloseAsync, "CloseAsync"}, |
| 74 | {0x00090002, nullptr, "GetCloseResult"}, | 239 | {0x00090002, GetCloseResult, "GetCloseResult"}, |
| 75 | {0x000A0000, nullptr, "GetLastErrorCode"}, | 240 | {0x000A0000, nullptr, "GetLastErrorCode"}, |
| 241 | {0x000C0000, nullptr, "GetStatus"}, | ||
| 76 | {0x000D0000, GetWifiStatus, "GetWifiStatus"}, | 242 | {0x000D0000, GetWifiStatus, "GetWifiStatus"}, |
| 77 | {0x000E0042, nullptr, "GetCurrentAPInfo"}, | 243 | {0x000E0042, nullptr, "GetCurrentAPInfo"}, |
| 78 | {0x00100042, nullptr, "GetCurrentNZoneInfo"}, | 244 | {0x00100042, nullptr, "GetCurrentNZoneInfo"}, |
| 79 | {0x00110042, nullptr, "GetNZoneApNumService"}, | 245 | {0x00110042, nullptr, "GetNZoneApNumService"}, |
| 246 | {0x001D0042, nullptr, "ScanAPs"}, | ||
| 80 | {0x00240042, nullptr, "AddDenyApType"}, | 247 | {0x00240042, nullptr, "AddDenyApType"}, |
| 81 | {0x00270002, nullptr, "GetInfraPriority"}, | 248 | {0x00270002, GetInfraPriority, "GetInfraPriority"}, |
| 82 | {0x002D0082, nullptr, "SetRequestEulaVersion"}, | 249 | {0x002D0082, SetRequestEulaVersion, "SetRequestEulaVersion"}, |
| 83 | {0x00300004, nullptr, "RegisterDisconnectEvent"}, | 250 | {0x00300004, RegisterDisconnectEvent, "RegisterDisconnectEvent"}, |
| 84 | {0x003C0042, nullptr, "GetAPSSIDList"}, | 251 | {0x003C0042, nullptr, "GetAPSSIDList"}, |
| 85 | {0x003E0042, IsConnected, "IsConnected"}, | 252 | {0x003E0042, IsConnected, "IsConnected"}, |
| 86 | {0x00400042, nullptr, "SetClientVersion"}, | 253 | {0x00400042, nullptr, "SetClientVersion"}, |
| @@ -91,6 +258,18 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 91 | 258 | ||
| 92 | Interface::Interface() { | 259 | Interface::Interface() { |
| 93 | Register(FunctionTable); | 260 | Register(FunctionTable); |
| 261 | |||
| 262 | ac_connected = false; | ||
| 263 | |||
| 264 | close_event = nullptr; | ||
| 265 | connect_event = nullptr; | ||
| 266 | disconnect_event = nullptr; | ||
| 267 | } | ||
| 268 | |||
| 269 | Interface::~Interface() { | ||
| 270 | close_event = nullptr; | ||
| 271 | connect_event = nullptr; | ||
| 272 | disconnect_event = nullptr; | ||
| 94 | } | 273 | } |
| 95 | 274 | ||
| 96 | } // namespace | 275 | } // namespace |
diff --git a/src/core/hle/service/ac_u.h b/src/core/hle/service/ac_u.h index f1d26ebe8..6592b21c9 100644 --- a/src/core/hle/service/ac_u.h +++ b/src/core/hle/service/ac_u.h | |||
| @@ -16,6 +16,7 @@ namespace AC_U { | |||
| 16 | class Interface : public Service::Interface { | 16 | class Interface : public Service::Interface { |
| 17 | public: | 17 | public: |
| 18 | Interface(); | 18 | Interface(); |
| 19 | ~Interface(); | ||
| 19 | 20 | ||
| 20 | std::string GetPortName() const override { | 21 | std::string GetPortName() const override { |
| 21 | return "ac:u"; | 22 | return "ac:u"; |
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index c4bd65986..31e5e07b2 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp | |||
| @@ -396,6 +396,15 @@ void StartLibraryApplet(Service::Interface* self) { | |||
| 396 | cmd_buff[1] = applet->Start(parameter).raw; | 396 | cmd_buff[1] = applet->Start(parameter).raw; |
| 397 | } | 397 | } |
| 398 | 398 | ||
| 399 | void CancelLibraryApplet(Service::Interface* self) { | ||
| 400 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 401 | u32 exiting = cmd_buff[1] & 0xFF; | ||
| 402 | |||
| 403 | cmd_buff[1] = 1; // TODO: Find the return code meaning | ||
| 404 | |||
| 405 | LOG_WARNING(Service_APT, "(STUBBED) called exiting=%u", exiting); | ||
| 406 | } | ||
| 407 | |||
| 399 | void SetScreenCapPostPermission(Service::Interface* self) { | 408 | void SetScreenCapPostPermission(Service::Interface* self) { |
| 400 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 409 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 401 | 410 | ||
| @@ -523,7 +532,7 @@ void Init() { | |||
| 523 | notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification"); | 532 | notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification"); |
| 524 | parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start"); | 533 | parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start"); |
| 525 | 534 | ||
| 526 | next_parameter.signal = static_cast<u32>(SignalType::AppJustStarted); | 535 | next_parameter.signal = static_cast<u32>(SignalType::Wakeup); |
| 527 | next_parameter.destination_id = 0x300; | 536 | next_parameter.destination_id = 0x300; |
| 528 | } | 537 | } |
| 529 | 538 | ||
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index a118cda1f..44dbd8757 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h | |||
| @@ -46,12 +46,23 @@ static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has i | |||
| 46 | /// Signals used by APT functions | 46 | /// Signals used by APT functions |
| 47 | enum class SignalType : u32 { | 47 | enum class SignalType : u32 { |
| 48 | None = 0x0, | 48 | None = 0x0, |
| 49 | AppJustStarted = 0x1, | 49 | Wakeup = 0x1, |
| 50 | LibAppJustStarted = 0x2, | 50 | Request = 0x2, |
| 51 | LibAppFinished = 0x3, | 51 | Response = 0x3, |
| 52 | LibAppClosed = 0xA, | 52 | Exit = 0x4, |
| 53 | ReturningToApp = 0xB, | 53 | Message = 0x5, |
| 54 | ExitingApp = 0xC, | 54 | HomeButtonSingle = 0x6, |
| 55 | HomeButtonDouble = 0x7, | ||
| 56 | DspSleep = 0x8, | ||
| 57 | DspWakeup = 0x9, | ||
| 58 | WakeupByExit = 0xA, | ||
| 59 | WakeupByPause = 0xB, | ||
| 60 | WakeupByCancel = 0xC, | ||
| 61 | WakeupByCancelAll = 0xD, | ||
| 62 | WakeupByPowerButtonClick = 0xE, | ||
| 63 | WakeupToJumpHome = 0xF, | ||
| 64 | RequestForSysApplet = 0x10, | ||
| 65 | WakeupToLaunchApplication = 0x11, | ||
| 55 | }; | 66 | }; |
| 56 | 67 | ||
| 57 | /// App Id's used by APT functions | 68 | /// App Id's used by APT functions |
| @@ -381,6 +392,17 @@ void PreloadLibraryApplet(Service::Interface* self); | |||
| 381 | void StartLibraryApplet(Service::Interface* self); | 392 | void StartLibraryApplet(Service::Interface* self); |
| 382 | 393 | ||
| 383 | /** | 394 | /** |
| 395 | * APT::CancelLibraryApplet service function | ||
| 396 | * Inputs: | ||
| 397 | * 0 : Command header [0x003B0040] | ||
| 398 | * 1 : u8, Application exiting (0 = not exiting, 1 = exiting) | ||
| 399 | * Outputs: | ||
| 400 | * 0 : Header code | ||
| 401 | * 1 : Result code | ||
| 402 | */ | ||
| 403 | void CancelLibraryApplet(Service::Interface* self); | ||
| 404 | |||
| 405 | /** | ||
| 384 | * APT::GetStartupArgument service function | 406 | * APT::GetStartupArgument service function |
| 385 | * Inputs: | 407 | * Inputs: |
| 386 | * 1 : Parameter Size (capped to 0x300) | 408 | * 1 : Parameter Size (capped to 0x300) |
diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index f27ad91b7..a7a0c8a41 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp | |||
| @@ -25,7 +25,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 25 | {0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"}, | 25 | {0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"}, |
| 26 | {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, | 26 | {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, |
| 27 | {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, | 27 | {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, |
| 28 | {0x003B0040, nullptr, "CancelLibraryApplet?"}, | 28 | {0x003B0040, CancelLibraryApplet, "CancelLibraryApplet"}, |
| 29 | {0x003E0080, nullptr, "ReplySleepQuery"}, | 29 | {0x003E0080, nullptr, "ReplySleepQuery"}, |
| 30 | {0x00430040, NotifyToWait, "NotifyToWait?"}, | 30 | {0x00430040, NotifyToWait, "NotifyToWait?"}, |
| 31 | {0x00440000, GetSharedFont, "GetSharedFont?"}, | 31 | {0x00440000, GetSharedFont, "GetSharedFont?"}, |
diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index d6ad42e21..a731c39f6 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp | |||
| @@ -67,7 +67,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 67 | {0x00380040, nullptr, "PreloadResidentApplet"}, | 67 | {0x00380040, nullptr, "PreloadResidentApplet"}, |
| 68 | {0x00390040, nullptr, "PrepareToStartResidentApplet"}, | 68 | {0x00390040, nullptr, "PrepareToStartResidentApplet"}, |
| 69 | {0x003A0044, nullptr, "StartResidentApplet"}, | 69 | {0x003A0044, nullptr, "StartResidentApplet"}, |
| 70 | {0x003B0040, nullptr, "CancelLibraryApplet"}, | 70 | {0x003B0040, CancelLibraryApplet, "CancelLibraryApplet"}, |
| 71 | {0x003C0042, nullptr, "SendDspSleep"}, | 71 | {0x003C0042, nullptr, "SendDspSleep"}, |
| 72 | {0x003D0042, nullptr, "SendDspWakeUp"}, | 72 | {0x003D0042, nullptr, "SendDspWakeUp"}, |
| 73 | {0x003E0080, nullptr, "ReplySleepQuery"}, | 73 | {0x003E0080, nullptr, "ReplySleepQuery"}, |
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 849dab707..d554c3f54 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp | |||
| @@ -45,7 +45,8 @@ static_assert(sizeof(SaveFileConfig) == 0x455C, | |||
| 45 | enum ConfigBlockID { | 45 | enum ConfigBlockID { |
| 46 | StereoCameraSettingsBlockID = 0x00050005, | 46 | StereoCameraSettingsBlockID = 0x00050005, |
| 47 | SoundOutputModeBlockID = 0x00070001, | 47 | SoundOutputModeBlockID = 0x00070001, |
| 48 | ConsoleUniqueIDBlockID = 0x00090001, | 48 | ConsoleUniqueID1BlockID = 0x00090000, |
| 49 | ConsoleUniqueID2BlockID = 0x00090001, | ||
| 49 | UsernameBlockID = 0x000A0000, | 50 | UsernameBlockID = 0x000A0000, |
| 50 | BirthdayBlockID = 0x000A0001, | 51 | BirthdayBlockID = 0x000A0001, |
| 51 | LanguageBlockID = 0x000A0002, | 52 | LanguageBlockID = 0x000A0002, |
| @@ -410,7 +411,12 @@ ResultCode FormatConfig() { | |||
| 410 | if (!res.IsSuccess()) | 411 | if (!res.IsSuccess()) |
| 411 | return res; | 412 | return res; |
| 412 | 413 | ||
| 413 | res = CreateConfigInfoBlk(ConsoleUniqueIDBlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, | 414 | res = CreateConfigInfoBlk(ConsoleUniqueID1BlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, |
| 415 | &CONSOLE_UNIQUE_ID); | ||
| 416 | if (!res.IsSuccess()) | ||
| 417 | return res; | ||
| 418 | |||
| 419 | res = CreateConfigInfoBlk(ConsoleUniqueID2BlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, | ||
| 414 | &CONSOLE_UNIQUE_ID); | 420 | &CONSOLE_UNIQUE_ID); |
| 415 | if (!res.IsSuccess()) | 421 | if (!res.IsSuccess()) |
| 416 | return res; | 422 | return res; |
diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp index 3ca4f98de..9905757c7 100644 --- a/src/core/hle/service/err_f.cpp +++ b/src/core/hle/service/err_f.cpp | |||
| @@ -2,9 +2,15 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <array> | ||
| 6 | #include <chrono> | ||
| 7 | #include <iomanip> | ||
| 8 | #include <sstream> | ||
| 9 | |||
| 5 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| 6 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 7 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 13 | #include "core/hle/result.h" | ||
| 8 | #include "core/hle/service/err_f.h" | 14 | #include "core/hle/service/err_f.h" |
| 9 | 15 | ||
| 10 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -12,12 +18,45 @@ | |||
| 12 | 18 | ||
| 13 | namespace ERR_F { | 19 | namespace ERR_F { |
| 14 | 20 | ||
| 15 | enum { | 21 | enum class FatalErrType : u32 { |
| 16 | ErrSpecifier0 = 0, | 22 | Generic = 0, |
| 17 | ErrSpecifier1 = 1, | 23 | Corrupted = 1, |
| 18 | ErrSpecifier3 = 3, | 24 | CardRemoved = 2, |
| 19 | ErrSpecifier4 = 4, | 25 | Exception = 3, |
| 26 | ResultFailure = 4, | ||
| 27 | Logged = 5, | ||
| 28 | }; | ||
| 29 | |||
| 30 | enum class ExceptionType : u32 { | ||
| 31 | PrefetchAbort = 0, | ||
| 32 | DataAbort = 1, | ||
| 33 | Undefined = 2, | ||
| 34 | VectorFP = 3, | ||
| 35 | }; | ||
| 36 | |||
| 37 | struct ExceptionInfo { | ||
| 38 | u8 exception_type; | ||
| 39 | INSERT_PADDING_BYTES(3); | ||
| 40 | u32 sr; | ||
| 41 | u32 ar; | ||
| 42 | u32 fpexc; | ||
| 43 | u32 fpinst; | ||
| 44 | u32 fpinst2; | ||
| 45 | }; | ||
| 46 | static_assert(sizeof(ExceptionInfo) == 0x18, "ExceptionInfo struct has incorrect size"); | ||
| 47 | |||
| 48 | struct ExceptionContext final { | ||
| 49 | std::array<u32, 16> arm_regs; | ||
| 50 | u32 cpsr; | ||
| 20 | }; | 51 | }; |
| 52 | static_assert(sizeof(ExceptionContext) == 0x44, "ExceptionContext struct has incorrect size"); | ||
| 53 | |||
| 54 | struct ExceptionData { | ||
| 55 | ExceptionInfo exception_info; | ||
| 56 | ExceptionContext exception_context; | ||
| 57 | INSERT_PADDING_WORDS(1); | ||
| 58 | }; | ||
| 59 | static_assert(sizeof(ExceptionData) == 0x60, "ExceptionData struct has incorrect size"); | ||
| 21 | 60 | ||
| 22 | // This is used instead of ResultCode from result.h | 61 | // This is used instead of ResultCode from result.h |
| 23 | // because we can't have non-trivial data members in unions. | 62 | // because we can't have non-trivial data members in unions. |
| @@ -30,150 +69,191 @@ union RSL { | |||
| 30 | BitField<27, 5, u32> level; | 69 | BitField<27, 5, u32> level; |
| 31 | }; | 70 | }; |
| 32 | 71 | ||
| 33 | union ErrInfo { | 72 | struct ErrInfo { |
| 34 | u8 specifier; | 73 | struct ErrInfoCommon { |
| 35 | 74 | u8 specifier; // 0x0 | |
| 36 | struct { | 75 | u8 rev_high; // 0x1 |
| 37 | u8 specifier; // 0x0 | 76 | u16 rev_low; // 0x2 |
| 38 | u8 rev_high; // 0x1 | 77 | RSL result_code; // 0x4 |
| 39 | u16 rev_low; // 0x2 | 78 | u32 pc_address; // 0x8 |
| 40 | RSL result_code; // 0x4 | 79 | u32 pid; // 0xC |
| 41 | u32 address; // 0x8 | 80 | u32 title_id_low; // 0x10 |
| 42 | INSERT_PADDING_BYTES(4); // 0xC | 81 | u32 title_id_high; // 0x14 |
| 43 | u32 pid_low; // 0x10 | 82 | u32 app_title_id_low; // 0x18 |
| 44 | u32 pid_high; // 0x14 | 83 | u32 app_title_id_high; // 0x1C |
| 45 | u32 aid_low; // 0x18 | 84 | } errinfo_common; |
| 46 | u32 aid_high; // 0x1C | 85 | static_assert(sizeof(ErrInfoCommon) == 0x20, "ErrInfoCommon struct has incorrect size"); |
| 47 | } errtype1; | 86 | |
| 48 | 87 | union { | |
| 49 | struct { | 88 | struct { |
| 50 | u8 specifier; // 0x0 | 89 | char data[0x60]; // 0x20 |
| 51 | u8 rev_high; // 0x1 | 90 | } generic; |
| 52 | u16 rev_low; // 0x2 | 91 | |
| 53 | INSERT_PADDING_BYTES(0xC); // 0x4 | 92 | struct { |
| 54 | u32 pid_low; // 0x10 | 93 | ExceptionData exception_data; // 0x20 |
| 55 | u32 pid_high; // 0x14 | 94 | } exception; |
| 56 | u32 aid_low; // 0x18 | 95 | |
| 57 | u32 aid_high; // 0x1C | 96 | struct { |
| 58 | u8 error_type; // 0x20 | 97 | char message[0x60]; // 0x20 |
| 59 | INSERT_PADDING_BYTES(3); // 0x21 | 98 | } result_failure; |
| 60 | u32 fault_status_reg; // 0x24 | 99 | }; |
| 61 | u32 fault_addr; // 0x28 | ||
| 62 | u32 fpexc; // 0x2C | ||
| 63 | u32 finst; // 0x30 | ||
| 64 | u32 finst2; // 0x34 | ||
| 65 | INSERT_PADDING_BYTES(0x34); // 0x38 | ||
| 66 | u32 sp; // 0x6C | ||
| 67 | u32 pc; // 0x70 | ||
| 68 | u32 lr; // 0x74 | ||
| 69 | u32 cpsr; // 0x78 | ||
| 70 | } errtype3; | ||
| 71 | |||
| 72 | struct { | ||
| 73 | u8 specifier; // 0x0 | ||
| 74 | u8 rev_high; // 0x1 | ||
| 75 | u16 rev_low; // 0x2 | ||
| 76 | RSL result_code; // 0x4 | ||
| 77 | INSERT_PADDING_BYTES(8); // 0x8 | ||
| 78 | u32 pid_low; // 0x10 | ||
| 79 | u32 pid_high; // 0x14 | ||
| 80 | u32 aid_low; // 0x18 | ||
| 81 | u32 aid_high; // 0x1C | ||
| 82 | char debug_string1[0x2E]; // 0x20 | ||
| 83 | char debug_string2[0x2E]; // 0x4E | ||
| 84 | } errtype4; | ||
| 85 | }; | 100 | }; |
| 86 | 101 | ||
| 87 | enum { PrefetchAbort = 0, DataAbort = 1, UndefInstr = 2, VectorFP = 3 }; | 102 | static std::string GetErrType(u8 type_code) { |
| 103 | switch (static_cast<FatalErrType>(type_code)) { | ||
| 104 | case FatalErrType::Generic: | ||
| 105 | return "Generic"; | ||
| 106 | case FatalErrType::Corrupted: | ||
| 107 | return "Corrupted"; | ||
| 108 | case FatalErrType::CardRemoved: | ||
| 109 | return "CardRemoved"; | ||
| 110 | case FatalErrType::Exception: | ||
| 111 | return "Exception"; | ||
| 112 | case FatalErrType::ResultFailure: | ||
| 113 | return "ResultFailure"; | ||
| 114 | case FatalErrType::Logged: | ||
| 115 | return "Logged"; | ||
| 116 | default: | ||
| 117 | return "Unknown Error Type"; | ||
| 118 | } | ||
| 119 | } | ||
| 88 | 120 | ||
| 89 | static std::string GetErrInfo3Type(u8 type_code) { | 121 | static std::string GetExceptionType(u8 type_code) { |
| 90 | switch (type_code) { | 122 | switch (static_cast<ExceptionType>(type_code)) { |
| 91 | case PrefetchAbort: | 123 | case ExceptionType::PrefetchAbort: |
| 92 | return "Prefetch Abort"; | 124 | return "Prefetch Abort"; |
| 93 | case DataAbort: | 125 | case ExceptionType::DataAbort: |
| 94 | return "Data Abort"; | 126 | return "Data Abort"; |
| 95 | case UndefInstr: | 127 | case ExceptionType::Undefined: |
| 96 | return "Undefined Instruction"; | 128 | return "Undefined Exception"; |
| 97 | case VectorFP: | 129 | case ExceptionType::VectorFP: |
| 98 | return "Vector Floating Point"; | 130 | return "Vector Floating Point Exception"; |
| 99 | default: | 131 | default: |
| 100 | return "unknown"; | 132 | return "Unknown Exception Type"; |
| 101 | } | 133 | } |
| 102 | } | 134 | } |
| 103 | 135 | ||
| 136 | static std::string GetCurrentSystemTime() { | ||
| 137 | auto now = std::chrono::system_clock::now(); | ||
| 138 | auto time = std::chrono::system_clock::to_time_t(now); | ||
| 139 | |||
| 140 | std::stringstream time_stream; | ||
| 141 | time_stream << std::put_time(std::localtime(&time), "%Y/%m/%d %H:%M:%S"); | ||
| 142 | return time_stream.str(); | ||
| 143 | } | ||
| 144 | |||
| 145 | static void LogGenericInfo(const ErrInfo::ErrInfoCommon& errinfo_common) { | ||
| 146 | LOG_CRITICAL(Service_ERR, "PID: 0x%08X", errinfo_common.pid); | ||
| 147 | LOG_CRITICAL(Service_ERR, "REV: 0x%08X_0x%08X", errinfo_common.rev_high, | ||
| 148 | errinfo_common.rev_low); | ||
| 149 | LOG_CRITICAL(Service_ERR, "TID: 0x%08X_0x%08X", errinfo_common.title_id_high, | ||
| 150 | errinfo_common.title_id_low); | ||
| 151 | LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errinfo_common.app_title_id_high, | ||
| 152 | errinfo_common.app_title_id_low); | ||
| 153 | LOG_CRITICAL(Service_ERR, "ADR: 0x%08X", errinfo_common.pc_address); | ||
| 154 | |||
| 155 | LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errinfo_common.result_code.raw); | ||
| 156 | LOG_CRITICAL(Service_ERR, " Level: %u", errinfo_common.result_code.level.Value()); | ||
| 157 | LOG_CRITICAL(Service_ERR, " Summary: %u", errinfo_common.result_code.summary.Value()); | ||
| 158 | LOG_CRITICAL(Service_ERR, " Module: %u", errinfo_common.result_code.module.Value()); | ||
| 159 | LOG_CRITICAL(Service_ERR, " Desc: %u", errinfo_common.result_code.description.Value()); | ||
| 160 | } | ||
| 161 | |||
| 162 | /* ThrowFatalError function | ||
| 163 | * Inputs: | ||
| 164 | * 0 : Header code [0x00010800] | ||
| 165 | * 1-32 : FatalErrInfo | ||
| 166 | * Outputs: | ||
| 167 | * 0 : Header code | ||
| 168 | * 1 : Result code | ||
| 169 | */ | ||
| 104 | static void ThrowFatalError(Service::Interface* self) { | 170 | static void ThrowFatalError(Service::Interface* self) { |
| 105 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 171 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 106 | 172 | ||
| 107 | LOG_CRITICAL(Service_ERR, "Fatal error!"); | 173 | LOG_CRITICAL(Service_ERR, "Fatal error"); |
| 108 | const ErrInfo* errinfo = reinterpret_cast<ErrInfo*>(&cmd_buff[1]); | 174 | const ErrInfo* errinfo = reinterpret_cast<ErrInfo*>(&cmd_buff[1]); |
| 175 | LOG_CRITICAL(Service_ERR, "Fatal error type: %s", | ||
| 176 | GetErrType(errinfo->errinfo_common.specifier).c_str()); | ||
| 109 | 177 | ||
| 110 | switch (errinfo->specifier) { | 178 | // Generic Info |
| 111 | case ErrSpecifier0: | 179 | LogGenericInfo(errinfo->errinfo_common); |
| 112 | case ErrSpecifier1: { | 180 | |
| 113 | const auto& errtype = errinfo->errtype1; | 181 | switch (static_cast<FatalErrType>(errinfo->errinfo_common.specifier)) { |
| 114 | LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high); | 182 | case FatalErrType::Generic: |
| 115 | LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16)); | 183 | case FatalErrType::Corrupted: |
| 116 | LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high); | 184 | case FatalErrType::CardRemoved: |
| 117 | LOG_CRITICAL(Service_ERR, "ADR: 0x%08X", errtype.address); | 185 | case FatalErrType::Logged: { |
| 118 | 186 | LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str()); | |
| 119 | LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errtype.result_code.raw); | ||
| 120 | LOG_CRITICAL(Service_ERR, " Level: %u", errtype.result_code.level.Value()); | ||
| 121 | LOG_CRITICAL(Service_ERR, " Summary: %u", errtype.result_code.summary.Value()); | ||
| 122 | LOG_CRITICAL(Service_ERR, " Module: %u", errtype.result_code.module.Value()); | ||
| 123 | LOG_CRITICAL(Service_ERR, " Desc: %u", errtype.result_code.description.Value()); | ||
| 124 | break; | 187 | break; |
| 125 | } | 188 | } |
| 189 | case FatalErrType::Exception: { | ||
| 190 | const auto& errtype = errinfo->exception; | ||
| 191 | |||
| 192 | // Register Info | ||
| 193 | LOG_CRITICAL(Service_ERR, "ARM Registers:"); | ||
| 194 | for (u32 index = 0; index < errtype.exception_data.exception_context.arm_regs.size(); | ||
| 195 | ++index) { | ||
| 196 | if (index < 13) { | ||
| 197 | LOG_DEBUG(Service_ERR, "r%u=0x%08X", index, | ||
| 198 | errtype.exception_data.exception_context.arm_regs.at(index)); | ||
| 199 | } else if (index == 13) { | ||
| 200 | LOG_CRITICAL(Service_ERR, "SP=0x%08X", | ||
| 201 | errtype.exception_data.exception_context.arm_regs.at(index)); | ||
| 202 | } else if (index == 14) { | ||
| 203 | LOG_CRITICAL(Service_ERR, "LR=0x%08X", | ||
| 204 | errtype.exception_data.exception_context.arm_regs.at(index)); | ||
| 205 | } else if (index == 15) { | ||
| 206 | LOG_CRITICAL(Service_ERR, "PC=0x%08X", | ||
| 207 | errtype.exception_data.exception_context.arm_regs.at(index)); | ||
| 208 | } | ||
| 209 | } | ||
| 210 | LOG_CRITICAL(Service_ERR, "CPSR=0x%08X", errtype.exception_data.exception_context.cpsr); | ||
| 126 | 211 | ||
| 127 | case ErrSpecifier3: { | 212 | // Exception Info |
| 128 | const auto& errtype = errinfo->errtype3; | 213 | LOG_CRITICAL( |
| 129 | LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high); | 214 | Service_ERR, "EXCEPTION TYPE: %s", |
| 130 | LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16)); | 215 | GetExceptionType(errtype.exception_data.exception_info.exception_type).c_str()); |
| 131 | LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high); | 216 | switch (static_cast<ExceptionType>(errtype.exception_data.exception_info.exception_type)) { |
| 132 | LOG_CRITICAL(Service_ERR, "TYPE: %s", GetErrInfo3Type(errtype.error_type).c_str()); | 217 | case ExceptionType::PrefetchAbort: |
| 133 | 218 | LOG_CRITICAL(Service_ERR, "IFSR: 0x%08X", errtype.exception_data.exception_info.sr); | |
| 134 | LOG_CRITICAL(Service_ERR, "PC: 0x%08X", errtype.pc); | 219 | LOG_CRITICAL(Service_ERR, "r15: 0x%08X", errtype.exception_data.exception_info.ar); |
| 135 | LOG_CRITICAL(Service_ERR, "LR: 0x%08X", errtype.lr); | 220 | case ExceptionType::DataAbort: |
| 136 | LOG_CRITICAL(Service_ERR, "SP: 0x%08X", errtype.sp); | 221 | LOG_CRITICAL(Service_ERR, "DFSR: 0x%08X", errtype.exception_data.exception_info.sr); |
| 137 | LOG_CRITICAL(Service_ERR, "CPSR: 0x%08X", errtype.cpsr); | 222 | LOG_CRITICAL(Service_ERR, "DFAR: 0x%08X", errtype.exception_data.exception_info.ar); |
| 138 | |||
| 139 | switch (errtype.error_type) { | ||
| 140 | case PrefetchAbort: | ||
| 141 | case DataAbort: | ||
| 142 | LOG_CRITICAL(Service_ERR, "Fault Address: 0x%08X", errtype.fault_addr); | ||
| 143 | LOG_CRITICAL(Service_ERR, "Fault Status Register: 0x%08X", errtype.fault_status_reg); | ||
| 144 | break; | 223 | break; |
| 145 | case VectorFP: | 224 | case ExceptionType::VectorFP: |
| 146 | LOG_CRITICAL(Service_ERR, "FPEXC: 0x%08X", errtype.fpexc); | 225 | LOG_CRITICAL(Service_ERR, "FPEXC: 0x%08X", |
| 147 | LOG_CRITICAL(Service_ERR, "FINST: 0x%08X", errtype.finst); | 226 | errtype.exception_data.exception_info.fpinst); |
| 148 | LOG_CRITICAL(Service_ERR, "FINST2: 0x%08X", errtype.finst2); | 227 | LOG_CRITICAL(Service_ERR, "FINST: 0x%08X", |
| 228 | errtype.exception_data.exception_info.fpinst); | ||
| 229 | LOG_CRITICAL(Service_ERR, "FINST2: 0x%08X", | ||
| 230 | errtype.exception_data.exception_info.fpinst2); | ||
| 149 | break; | 231 | break; |
| 150 | } | 232 | } |
| 233 | LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str()); | ||
| 151 | break; | 234 | break; |
| 152 | } | 235 | } |
| 153 | 236 | ||
| 154 | case ErrSpecifier4: { | 237 | case FatalErrType::ResultFailure: { |
| 155 | const auto& errtype = errinfo->errtype4; | 238 | const auto& errtype = errinfo->result_failure; |
| 156 | LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high); | ||
| 157 | LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16)); | ||
| 158 | LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high); | ||
| 159 | 239 | ||
| 160 | LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errtype.result_code.raw); | 240 | // Failure Message |
| 161 | LOG_CRITICAL(Service_ERR, " Level: %u", errtype.result_code.level.Value()); | 241 | LOG_CRITICAL(Service_ERR, "Failure Message: %s", errtype.message); |
| 162 | LOG_CRITICAL(Service_ERR, " Summary: %u", errtype.result_code.summary.Value()); | 242 | LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str()); |
| 163 | LOG_CRITICAL(Service_ERR, " Module: %u", errtype.result_code.module.Value()); | ||
| 164 | LOG_CRITICAL(Service_ERR, " Desc: %u", errtype.result_code.description.Value()); | ||
| 165 | |||
| 166 | LOG_CRITICAL(Service_ERR, "%s", errtype.debug_string1); | ||
| 167 | LOG_CRITICAL(Service_ERR, "%s", errtype.debug_string2); | ||
| 168 | break; | 243 | break; |
| 169 | } | 244 | } |
| 170 | } | ||
| 171 | 245 | ||
| 172 | cmd_buff[1] = 0; // No error | 246 | } // switch FatalErrType |
| 247 | |||
| 248 | cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); | ||
| 249 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 173 | } | 250 | } |
| 174 | 251 | ||
| 175 | const Interface::FunctionInfo FunctionTable[] = { | 252 | const Interface::FunctionInfo FunctionTable[] = { |
| 253 | // clang-format off | ||
| 176 | {0x00010800, ThrowFatalError, "ThrowFatalError"}, | 254 | {0x00010800, ThrowFatalError, "ThrowFatalError"}, |
| 255 | {0x00020042, nullptr, "SetUserString"}, | ||
| 256 | // clang-format on | ||
| 177 | }; | 257 | }; |
| 178 | 258 | ||
| 179 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 259 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index edd1ea97b..563341504 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp | |||
| @@ -2,6 +2,9 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/hle/kernel/event.h" | ||
| 7 | #include "core/hle/kernel/shared_memory.h" | ||
| 5 | #include "core/hle/service/mic_u.h" | 8 | #include "core/hle/service/mic_u.h" |
| 6 | 9 | ||
| 7 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 10 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -9,23 +12,298 @@ | |||
| 9 | 12 | ||
| 10 | namespace MIC_U { | 13 | namespace MIC_U { |
| 11 | 14 | ||
| 15 | enum class Encoding : u8 { | ||
| 16 | PCM8 = 0, | ||
| 17 | PCM16 = 1, | ||
| 18 | PCM8Signed = 2, | ||
| 19 | PCM16Signed = 3, | ||
| 20 | }; | ||
| 21 | |||
| 22 | enum class SampleRate : u8 { | ||
| 23 | SampleRate32730 = 0, | ||
| 24 | SampleRate16360 = 1, | ||
| 25 | SampleRate10910 = 2, | ||
| 26 | SampleRate8180 = 3 | ||
| 27 | }; | ||
| 28 | |||
| 29 | static Kernel::SharedPtr<Kernel::Event> buffer_full_event; | ||
| 30 | static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; | ||
| 31 | static u8 mic_gain = 0; | ||
| 32 | static bool mic_power = false; | ||
| 33 | static bool is_sampling = false; | ||
| 34 | static bool allow_shell_closed; | ||
| 35 | static bool clamp = false; | ||
| 36 | static Encoding encoding; | ||
| 37 | static SampleRate sample_rate; | ||
| 38 | static s32 audio_buffer_offset; | ||
| 39 | static u32 audio_buffer_size; | ||
| 40 | static bool audio_buffer_loop; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * MIC::MapSharedMem service function | ||
| 44 | * Inputs: | ||
| 45 | * 0 : Header Code[0x00010042] | ||
| 46 | * 1 : Shared-mem size | ||
| 47 | * 2 : CopyHandleDesc | ||
| 48 | * 3 : Shared-mem handle | ||
| 49 | * Outputs: | ||
| 50 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 51 | */ | ||
| 52 | static void MapSharedMem(Service::Interface* self) { | ||
| 53 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 54 | u32 size = cmd_buff[1]; | ||
| 55 | Handle mem_handle = cmd_buff[3]; | ||
| 56 | shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(mem_handle); | ||
| 57 | if (shared_memory) { | ||
| 58 | shared_memory->name = "MIC_U:shared_memory"; | ||
| 59 | } | ||
| 60 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 61 | LOG_WARNING(Service_MIC, "called, size=0x%X, mem_handle=0x%08X", size, mem_handle); | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * MIC::UnmapSharedMem service function | ||
| 66 | * Inputs: | ||
| 67 | * 0 : Header Code[0x00020000] | ||
| 68 | * Outputs: | ||
| 69 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 70 | */ | ||
| 71 | static void UnmapSharedMem(Service::Interface* self) { | ||
| 72 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 73 | |||
| 74 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 75 | LOG_WARNING(Service_MIC, "called"); | ||
| 76 | } | ||
| 77 | |||
| 78 | /** | ||
| 79 | * MIC::StartSampling service function | ||
| 80 | * Inputs: | ||
| 81 | * 0 : Header Code[0x00030140] | ||
| 82 | * 1 : Encoding | ||
| 83 | * 2 : SampleRate | ||
| 84 | * 3 : Base offset for audio data in sharedmem | ||
| 85 | * 4 : Size of the audio data in sharedmem | ||
| 86 | * 5 : Loop at end of buffer | ||
| 87 | * Outputs: | ||
| 88 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 89 | */ | ||
| 90 | static void StartSampling(Service::Interface* self) { | ||
| 91 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 92 | |||
| 93 | encoding = static_cast<Encoding>(cmd_buff[1] & 0xFF); | ||
| 94 | sample_rate = static_cast<SampleRate>(cmd_buff[2] & 0xFF); | ||
| 95 | audio_buffer_offset = cmd_buff[3]; | ||
| 96 | audio_buffer_size = cmd_buff[4]; | ||
| 97 | audio_buffer_loop = static_cast<bool>(cmd_buff[5] & 0xFF); | ||
| 98 | |||
| 99 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 100 | is_sampling = true; | ||
| 101 | LOG_WARNING(Service_MIC, "(STUBBED) called, encoding=%u, sample_rate=%u, " | ||
| 102 | "audio_buffer_offset=%d, audio_buffer_size=%u, audio_buffer_loop=%u", | ||
| 103 | encoding, sample_rate, audio_buffer_offset, audio_buffer_size, audio_buffer_loop); | ||
| 104 | } | ||
| 105 | |||
| 106 | /** | ||
| 107 | * MIC::AdjustSampling service function | ||
| 108 | * Inputs: | ||
| 109 | * 0 : Header Code[0x00040040] | ||
| 110 | * 1 : SampleRate | ||
| 111 | * Outputs: | ||
| 112 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 113 | */ | ||
| 114 | static void AdjustSampling(Service::Interface* self) { | ||
| 115 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 116 | sample_rate = static_cast<SampleRate>(cmd_buff[1] & 0xFF); | ||
| 117 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 118 | LOG_WARNING(Service_MIC, "(STUBBED) called, sample_rate=%u", sample_rate); | ||
| 119 | } | ||
| 120 | |||
| 121 | /** | ||
| 122 | * MIC::StopSampling service function | ||
| 123 | * Inputs: | ||
| 124 | * 0 : Header Code[0x00050000] | ||
| 125 | * Outputs: | ||
| 126 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 127 | */ | ||
| 128 | static void StopSampling(Service::Interface* self) { | ||
| 129 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 130 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 131 | is_sampling = false; | ||
| 132 | LOG_WARNING(Service_MIC, "(STUBBED) called"); | ||
| 133 | } | ||
| 134 | |||
| 135 | /** | ||
| 136 | * MIC::IsSampling service function | ||
| 137 | * Inputs: | ||
| 138 | * 0 : Header Code[0x00060000] | ||
| 139 | * Outputs: | ||
| 140 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 141 | * 2 : 0 = sampling, non-zero = sampling | ||
| 142 | */ | ||
| 143 | static void IsSampling(Service::Interface* self) { | ||
| 144 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 145 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 146 | cmd_buff[2] = is_sampling; | ||
| 147 | LOG_WARNING(Service_MIC, "(STUBBED) called"); | ||
| 148 | } | ||
| 149 | |||
| 150 | /** | ||
| 151 | * MIC::GetBufferFullEvent service function | ||
| 152 | * Inputs: | ||
| 153 | * 0 : Header Code[0x00070000] | ||
| 154 | * Outputs: | ||
| 155 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 156 | * 3 : Event handle | ||
| 157 | */ | ||
| 158 | static void GetBufferFullEvent(Service::Interface* self) { | ||
| 159 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 160 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 161 | cmd_buff[3] = Kernel::g_handle_table.Create(buffer_full_event).MoveFrom(); | ||
| 162 | LOG_WARNING(Service_MIC, "(STUBBED) called"); | ||
| 163 | } | ||
| 164 | |||
| 165 | /** | ||
| 166 | * MIC::SetGain service function | ||
| 167 | * Inputs: | ||
| 168 | * 0 : Header Code[0x00080040] | ||
| 169 | * 1 : Gain | ||
| 170 | * Outputs: | ||
| 171 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 172 | */ | ||
| 173 | static void SetGain(Service::Interface* self) { | ||
| 174 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 175 | mic_gain = cmd_buff[1] & 0xFF; | ||
| 176 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 177 | LOG_WARNING(Service_MIC, "(STUBBED) called, mic_gain=%u", mic_gain); | ||
| 178 | } | ||
| 179 | |||
| 180 | /** | ||
| 181 | * MIC::GetGain service function | ||
| 182 | * Inputs: | ||
| 183 | * 0 : Header Code[0x00090000] | ||
| 184 | * Outputs: | ||
| 185 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 186 | * 2 : Gain | ||
| 187 | */ | ||
| 188 | static void GetGain(Service::Interface* self) { | ||
| 189 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 190 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 191 | cmd_buff[2] = mic_gain; | ||
| 192 | LOG_WARNING(Service_MIC, "(STUBBED) called"); | ||
| 193 | } | ||
| 194 | |||
| 195 | /** | ||
| 196 | * MIC::SetPower service function | ||
| 197 | * Inputs: | ||
| 198 | * 0 : Header Code[0x000A0040] | ||
| 199 | * 1 : Power (0 = off, 1 = on) | ||
| 200 | * Outputs: | ||
| 201 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 202 | */ | ||
| 203 | static void SetPower(Service::Interface* self) { | ||
| 204 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 205 | mic_power = static_cast<bool>(cmd_buff[1] & 0xFF); | ||
| 206 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 207 | LOG_WARNING(Service_MIC, "(STUBBED) called, mic_power=%u", mic_power); | ||
| 208 | } | ||
| 209 | |||
| 210 | /** | ||
| 211 | * MIC::GetPower service function | ||
| 212 | * Inputs: | ||
| 213 | * 0 : Header Code[0x000B0000] | ||
| 214 | * Outputs: | ||
| 215 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 216 | * 2 : Power | ||
| 217 | */ | ||
| 218 | static void GetPower(Service::Interface* self) { | ||
| 219 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 220 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 221 | cmd_buff[2] = mic_power; | ||
| 222 | LOG_WARNING(Service_MIC, "(STUBBED) called"); | ||
| 223 | } | ||
| 224 | |||
| 225 | /** | ||
| 226 | * MIC::SetIirFilterMic service function | ||
| 227 | * Inputs: | ||
| 228 | * 0 : Header Code[0x000C0042] | ||
| 229 | * 1 : Size | ||
| 230 | * 2 : (Size << 4) | 0xA | ||
| 231 | * 3 : Pointer to IIR Filter Data | ||
| 232 | * Outputs: | ||
| 233 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 234 | */ | ||
| 235 | static void SetIirFilterMic(Service::Interface* self) { | ||
| 236 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 237 | |||
| 238 | u32 size = cmd_buff[1]; | ||
| 239 | VAddr buffer = cmd_buff[3]; | ||
| 240 | |||
| 241 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 242 | LOG_WARNING(Service_MIC, "(STUBBED) called, size=0x%X, buffer=0x%08X", size, buffer); | ||
| 243 | } | ||
| 244 | |||
| 245 | /** | ||
| 246 | * MIC::SetClamp service function | ||
| 247 | * Inputs: | ||
| 248 | * 0 : Header Code[0x000D0040] | ||
| 249 | * 1 : Clamp (0 = don't clamp, non-zero = clamp) | ||
| 250 | * Outputs: | ||
| 251 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 252 | */ | ||
| 253 | static void SetClamp(Service::Interface* self) { | ||
| 254 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 255 | clamp = static_cast<bool>(cmd_buff[1] & 0xFF); | ||
| 256 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 257 | LOG_WARNING(Service_MIC, "(STUBBED) called, clamp=%u", clamp); | ||
| 258 | } | ||
| 259 | |||
| 260 | /** | ||
| 261 | * MIC::GetClamp service function | ||
| 262 | * Inputs: | ||
| 263 | * 0 : Header Code[0x000E0000] | ||
| 264 | * Outputs: | ||
| 265 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 266 | * 2 : Clamp (0 = don't clamp, non-zero = clamp) | ||
| 267 | */ | ||
| 268 | static void GetClamp(Service::Interface* self) { | ||
| 269 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 270 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 271 | cmd_buff[2] = clamp; | ||
| 272 | LOG_WARNING(Service_MIC, "(STUBBED) called"); | ||
| 273 | } | ||
| 274 | |||
| 275 | /** | ||
| 276 | * MIC::SetAllowShellClosed service function | ||
| 277 | * Inputs: | ||
| 278 | * 0 : Header Code[0x000D0040] | ||
| 279 | * 1 : Sampling allowed while shell closed (0 = disallow, non-zero = allow) | ||
| 280 | * Outputs: | ||
| 281 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 282 | */ | ||
| 283 | static void SetAllowShellClosed(Service::Interface* self) { | ||
| 284 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 285 | allow_shell_closed = static_cast<bool>(cmd_buff[1] & 0xFF); | ||
| 286 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 287 | LOG_WARNING(Service_MIC, "(STUBBED) called, allow_shell_closed=%u", allow_shell_closed); | ||
| 288 | } | ||
| 289 | |||
| 12 | const Interface::FunctionInfo FunctionTable[] = { | 290 | const Interface::FunctionInfo FunctionTable[] = { |
| 13 | {0x00010042, nullptr, "MapSharedMem"}, | 291 | {0x00010042, MapSharedMem, "MapSharedMem"}, |
| 14 | {0x00020000, nullptr, "UnmapSharedMem"}, | 292 | {0x00020000, UnmapSharedMem, "UnmapSharedMem"}, |
| 15 | {0x00030140, nullptr, "Initialize"}, | 293 | {0x00030140, StartSampling, "StartSampling"}, |
| 16 | {0x00040040, nullptr, "AdjustSampling"}, | 294 | {0x00040040, AdjustSampling, "AdjustSampling"}, |
| 17 | {0x00050000, nullptr, "StopSampling"}, | 295 | {0x00050000, StopSampling, "StopSampling"}, |
| 18 | {0x00060000, nullptr, "IsSampling"}, | 296 | {0x00060000, IsSampling, "IsSampling"}, |
| 19 | {0x00070000, nullptr, "GetEventHandle"}, | 297 | {0x00070000, GetBufferFullEvent, "GetBufferFullEvent"}, |
| 20 | {0x00080040, nullptr, "SetGain"}, | 298 | {0x00080040, SetGain, "SetGain"}, |
| 21 | {0x00090000, nullptr, "GetGain"}, | 299 | {0x00090000, GetGain, "GetGain"}, |
| 22 | {0x000A0040, nullptr, "SetPower"}, | 300 | {0x000A0040, SetPower, "SetPower"}, |
| 23 | {0x000B0000, nullptr, "GetPower"}, | 301 | {0x000B0000, GetPower, "GetPower"}, |
| 24 | {0x000C0042, nullptr, "size"}, | 302 | {0x000C0042, SetIirFilterMic, "SetIirFilterMic"}, |
| 25 | {0x000D0040, nullptr, "SetClamp"}, | 303 | {0x000D0040, SetClamp, "SetClamp"}, |
| 26 | {0x000E0000, nullptr, "GetClamp"}, | 304 | {0x000E0000, GetClamp, "GetClamp"}, |
| 27 | {0x000F0040, nullptr, "SetAllowShellClosed"}, | 305 | {0x000F0040, SetAllowShellClosed, "SetAllowShellClosed"}, |
| 28 | {0x00100040, nullptr, "unknown_input2"}, | 306 | {0x00100040, nullptr, "SetClientSDKVersion"}, |
| 29 | }; | 307 | }; |
| 30 | 308 | ||
| 31 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 309 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -33,6 +311,18 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 33 | 311 | ||
| 34 | Interface::Interface() { | 312 | Interface::Interface() { |
| 35 | Register(FunctionTable); | 313 | Register(FunctionTable); |
| 314 | shared_memory = nullptr; | ||
| 315 | buffer_full_event = | ||
| 316 | Kernel::Event::Create(Kernel::ResetType::OneShot, "MIC_U::buffer_full_event"); | ||
| 317 | mic_gain = 0; | ||
| 318 | mic_power = false; | ||
| 319 | is_sampling = false; | ||
| 320 | clamp = false; | ||
| 321 | } | ||
| 322 | |||
| 323 | Interface::~Interface() { | ||
| 324 | shared_memory = nullptr; | ||
| 325 | buffer_full_event = nullptr; | ||
| 36 | } | 326 | } |
| 37 | 327 | ||
| 38 | } // namespace | 328 | } // namespace |
diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h index dc795d14c..1cff7390e 100644 --- a/src/core/hle/service/mic_u.h +++ b/src/core/hle/service/mic_u.h | |||
| @@ -16,6 +16,7 @@ namespace MIC_U { | |||
| 16 | class Interface : public Service::Interface { | 16 | class Interface : public Service::Interface { |
| 17 | public: | 17 | public: |
| 18 | Interface(); | 18 | Interface(); |
| 19 | ~Interface(); | ||
| 19 | 20 | ||
| 20 | std::string GetPortName() const override { | 21 | std::string GetPortName() const override { |
| 21 | return "mic:u"; | 22 | return "mic:u"; |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 64c388374..65e4bba85 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -45,13 +45,11 @@ struct SpecialRegion { | |||
| 45 | * requires an indexed fetch and a check for NULL. | 45 | * requires an indexed fetch and a check for NULL. |
| 46 | */ | 46 | */ |
| 47 | struct PageTable { | 47 | struct PageTable { |
| 48 | static const size_t NUM_ENTRIES = 1 << (32 - PAGE_BITS); | ||
| 49 | |||
| 50 | /** | 48 | /** |
| 51 | * Array of memory pointers backing each page. An entry can only be non-null if the | 49 | * Array of memory pointers backing each page. An entry can only be non-null if the |
| 52 | * corresponding entry in the `attributes` array is of type `Memory`. | 50 | * corresponding entry in the `attributes` array is of type `Memory`. |
| 53 | */ | 51 | */ |
| 54 | std::array<u8*, NUM_ENTRIES> pointers; | 52 | std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers; |
| 55 | 53 | ||
| 56 | /** | 54 | /** |
| 57 | * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of | 55 | * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of |
| @@ -63,13 +61,13 @@ struct PageTable { | |||
| 63 | * Array of fine grained page attributes. If it is set to any value other than `Memory`, then | 61 | * Array of fine grained page attributes. If it is set to any value other than `Memory`, then |
| 64 | * the corresponding entry in `pointers` MUST be set to null. | 62 | * the corresponding entry in `pointers` MUST be set to null. |
| 65 | */ | 63 | */ |
| 66 | std::array<PageType, NUM_ENTRIES> attributes; | 64 | std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes; |
| 67 | 65 | ||
| 68 | /** | 66 | /** |
| 69 | * Indicates the number of externally cached resources touching a page that should be | 67 | * Indicates the number of externally cached resources touching a page that should be |
| 70 | * flushed before the memory is accessed | 68 | * flushed before the memory is accessed |
| 71 | */ | 69 | */ |
| 72 | std::array<u8, NUM_ENTRIES> cached_res_count; | 70 | std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count; |
| 73 | }; | 71 | }; |
| 74 | 72 | ||
| 75 | /// Singular page table used for the singleton process | 73 | /// Singular page table used for the singleton process |
| @@ -77,6 +75,10 @@ static PageTable main_page_table; | |||
| 77 | /// Currently active page table | 75 | /// Currently active page table |
| 78 | static PageTable* current_page_table = &main_page_table; | 76 | static PageTable* current_page_table = &main_page_table; |
| 79 | 77 | ||
| 78 | std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() { | ||
| 79 | return ¤t_page_table->pointers; | ||
| 80 | } | ||
| 81 | |||
| 80 | static void MapPages(u32 base, u32 size, u8* memory, PageType type) { | 82 | static void MapPages(u32 base, u32 size, u8* memory, PageType type) { |
| 81 | LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, | 83 | LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, |
| 82 | (base + size) * PAGE_SIZE); | 84 | (base + size) * PAGE_SIZE); |
| @@ -84,7 +86,7 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { | |||
| 84 | u32 end = base + size; | 86 | u32 end = base + size; |
| 85 | 87 | ||
| 86 | while (base != end) { | 88 | while (base != end) { |
| 87 | ASSERT_MSG(base < PageTable::NUM_ENTRIES, "out of range mapping at %08X", base); | 89 | ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base); |
| 88 | 90 | ||
| 89 | // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be | 91 | // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be |
| 90 | // null here | 92 | // null here |
diff --git a/src/core/memory.h b/src/core/memory.h index 8fd3080ff..903b58a22 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 7 | #include <cstddef> | 8 | #include <cstddef> |
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| @@ -17,6 +18,7 @@ namespace Memory { | |||
| 17 | const u32 PAGE_SIZE = 0x1000; | 18 | const u32 PAGE_SIZE = 0x1000; |
| 18 | const u32 PAGE_MASK = PAGE_SIZE - 1; | 19 | const u32 PAGE_MASK = PAGE_SIZE - 1; |
| 19 | const int PAGE_BITS = 12; | 20 | const int PAGE_BITS = 12; |
| 21 | const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS); | ||
| 20 | 22 | ||
| 21 | /// Physical memory regions as seen from the ARM11 | 23 | /// Physical memory regions as seen from the ARM11 |
| 22 | enum : PAddr { | 24 | enum : PAddr { |
| @@ -166,4 +168,11 @@ void RasterizerFlushRegion(PAddr start, u32 size); | |||
| 166 | * Flushes and invalidates any externally cached rasterizer resources touching the given region. | 168 | * Flushes and invalidates any externally cached rasterizer resources touching the given region. |
| 167 | */ | 169 | */ |
| 168 | void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size); | 170 | void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size); |
| 171 | |||
| 172 | /** | ||
| 173 | * Dynarmic has an optimization to memory accesses when the pointer to the page exists that | ||
| 174 | * can be used by setting up the current page table as a callback. This function is used to | ||
| 175 | * retrieve the current page table for that purpose. | ||
| 176 | */ | ||
| 177 | std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers(); | ||
| 169 | } | 178 | } |
diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 4a0969b00..05f41f798 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp | |||
| @@ -7,6 +7,8 @@ | |||
| 7 | #include "settings.h" | 7 | #include "settings.h" |
| 8 | #include "video_core/video_core.h" | 8 | #include "video_core/video_core.h" |
| 9 | 9 | ||
| 10 | #include "common/emu_window.h" | ||
| 11 | |||
| 10 | namespace Settings { | 12 | namespace Settings { |
| 11 | 13 | ||
| 12 | Values values = {}; | 14 | Values values = {}; |
| @@ -20,6 +22,11 @@ void Apply() { | |||
| 20 | VideoCore::g_shader_jit_enabled = values.use_shader_jit; | 22 | VideoCore::g_shader_jit_enabled = values.use_shader_jit; |
| 21 | VideoCore::g_scaled_resolution_enabled = values.use_scaled_resolution; | 23 | VideoCore::g_scaled_resolution_enabled = values.use_scaled_resolution; |
| 22 | 24 | ||
| 25 | if (VideoCore::g_emu_window) { | ||
| 26 | auto layout = VideoCore::g_emu_window->GetFramebufferLayout(); | ||
| 27 | VideoCore::g_emu_window->UpdateCurrentFramebufferLayout(layout.width, layout.height); | ||
| 28 | } | ||
| 29 | |||
| 23 | AudioCore::SelectSink(values.sink_id); | 30 | AudioCore::SelectSink(values.sink_id); |
| 24 | AudioCore::EnableStretching(values.enable_audio_stretching); | 31 | AudioCore::EnableStretching(values.enable_audio_stretching); |
| 25 | } | 32 | } |
diff --git a/src/core/settings.h b/src/core/settings.h index 5a64f8018..e931953d7 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -10,7 +10,15 @@ | |||
| 10 | 10 | ||
| 11 | namespace Settings { | 11 | namespace Settings { |
| 12 | 12 | ||
| 13 | enum class LayoutOption { | ||
| 14 | Default, | ||
| 15 | SingleScreen, | ||
| 16 | LargeScreen, | ||
| 17 | Custom, | ||
| 18 | }; | ||
| 19 | |||
| 13 | namespace NativeInput { | 20 | namespace NativeInput { |
| 21 | |||
| 14 | enum Values { | 22 | enum Values { |
| 15 | // directly mapped keys | 23 | // directly mapped keys |
| 16 | A, | 24 | A, |
| @@ -84,6 +92,9 @@ struct Values { | |||
| 84 | bool use_scaled_resolution; | 92 | bool use_scaled_resolution; |
| 85 | bool use_vsync; | 93 | bool use_vsync; |
| 86 | 94 | ||
| 95 | LayoutOption layout_option; | ||
| 96 | bool swap_screen; | ||
| 97 | |||
| 87 | float bg_red; | 98 | float bg_red; |
| 88 | float bg_green; | 99 | float bg_green; |
| 89 | float bg_blue; | 100 | float bg_blue; |
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index fda91e29c..b7c32035e 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp | |||
| @@ -215,18 +215,17 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 215 | 215 | ||
| 216 | PrimitiveAssembler<Shader::OutputVertex>& primitive_assembler = g_state.primitive_assembler; | 216 | PrimitiveAssembler<Shader::OutputVertex>& primitive_assembler = g_state.primitive_assembler; |
| 217 | 217 | ||
| 218 | if (g_debug_context) { | 218 | if (g_debug_context && g_debug_context->recorder) { |
| 219 | for (int i = 0; i < 3; ++i) { | 219 | for (int i = 0; i < 3; ++i) { |
| 220 | const auto texture = regs.GetTextures()[i]; | 220 | const auto texture = regs.GetTextures()[i]; |
| 221 | if (!texture.enabled) | 221 | if (!texture.enabled) |
| 222 | continue; | 222 | continue; |
| 223 | 223 | ||
| 224 | u8* texture_data = Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress()); | 224 | u8* texture_data = Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress()); |
| 225 | if (g_debug_context && Pica::g_debug_context->recorder) | 225 | g_debug_context->recorder->MemoryAccessed( |
| 226 | g_debug_context->recorder->MemoryAccessed( | 226 | texture_data, Pica::Regs::NibblesPerPixel(texture.format) * |
| 227 | texture_data, Pica::Regs::NibblesPerPixel(texture.format) * | 227 | texture.config.width / 2 * texture.config.height, |
| 228 | texture.config.width / 2 * texture.config.height, | 228 | texture.config.GetPhysicalAddress()); |
| 229 | texture.config.GetPhysicalAddress()); | ||
| 230 | } | 229 | } |
| 231 | } | 230 | } |
| 232 | 231 | ||
| @@ -236,7 +235,8 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 236 | // The size has been tuned for optimal balance between hit-rate and the cost of lookup | 235 | // The size has been tuned for optimal balance between hit-rate and the cost of lookup |
| 237 | const size_t VERTEX_CACHE_SIZE = 32; | 236 | const size_t VERTEX_CACHE_SIZE = 32; |
| 238 | std::array<u16, VERTEX_CACHE_SIZE> vertex_cache_ids; | 237 | std::array<u16, VERTEX_CACHE_SIZE> vertex_cache_ids; |
| 239 | std::array<Shader::OutputRegisters, VERTEX_CACHE_SIZE> vertex_cache; | 238 | std::array<Shader::OutputVertex, VERTEX_CACHE_SIZE> vertex_cache; |
| 239 | Shader::OutputVertex output_vertex; | ||
| 240 | 240 | ||
| 241 | unsigned int vertex_cache_pos = 0; | 241 | unsigned int vertex_cache_pos = 0; |
| 242 | vertex_cache_ids.fill(-1); | 242 | vertex_cache_ids.fill(-1); |
| @@ -266,7 +266,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 266 | 266 | ||
| 267 | for (unsigned int i = 0; i < VERTEX_CACHE_SIZE; ++i) { | 267 | for (unsigned int i = 0; i < VERTEX_CACHE_SIZE; ++i) { |
| 268 | if (vertex == vertex_cache_ids[i]) { | 268 | if (vertex == vertex_cache_ids[i]) { |
| 269 | output_registers = vertex_cache[i]; | 269 | output_vertex = vertex_cache[i]; |
| 270 | vertex_cache_hit = true; | 270 | vertex_cache_hit = true; |
| 271 | break; | 271 | break; |
| 272 | } | 272 | } |
| @@ -285,16 +285,16 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 285 | g_state.vs.Run(shader_unit, input, loader.GetNumTotalAttributes()); | 285 | g_state.vs.Run(shader_unit, input, loader.GetNumTotalAttributes()); |
| 286 | output_registers = shader_unit.output_registers; | 286 | output_registers = shader_unit.output_registers; |
| 287 | 287 | ||
| 288 | // Retrieve vertex from register data | ||
| 289 | output_vertex = output_registers.ToVertex(regs.vs); | ||
| 290 | |||
| 288 | if (is_indexed) { | 291 | if (is_indexed) { |
| 289 | vertex_cache[vertex_cache_pos] = output_registers; | 292 | vertex_cache[vertex_cache_pos] = output_vertex; |
| 290 | vertex_cache_ids[vertex_cache_pos] = vertex; | 293 | vertex_cache_ids[vertex_cache_pos] = vertex; |
| 291 | vertex_cache_pos = (vertex_cache_pos + 1) % VERTEX_CACHE_SIZE; | 294 | vertex_cache_pos = (vertex_cache_pos + 1) % VERTEX_CACHE_SIZE; |
| 292 | } | 295 | } |
| 293 | } | 296 | } |
| 294 | 297 | ||
| 295 | // Retrieve vertex from register data | ||
| 296 | Shader::OutputVertex output_vertex = output_registers.ToVertex(regs.vs); | ||
| 297 | |||
| 298 | // Send to renderer | 298 | // Send to renderer |
| 299 | using Pica::Shader::OutputVertex; | 299 | using Pica::Shader::OutputVertex; |
| 300 | auto AddTriangle = [](const OutputVertex& v0, const OutputVertex& v1, | 300 | auto AddTriangle = [](const OutputVertex& v0, const OutputVertex& v1, |
diff --git a/src/video_core/pica.h b/src/video_core/pica.h index b2db609ec..99bd59a69 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h | |||
| @@ -40,7 +40,7 @@ namespace Pica { | |||
| 40 | // field offset. Otherwise, the compiler will fail to compile this code. | 40 | // field offset. Otherwise, the compiler will fail to compile this code. |
| 41 | #define PICA_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) \ | 41 | #define PICA_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) \ |
| 42 | ((typename std::enable_if<backup_workaround_index == PICA_REG_INDEX(field_name), \ | 42 | ((typename std::enable_if<backup_workaround_index == PICA_REG_INDEX(field_name), \ |
| 43 | size_t>::type)PICA_REG_INDEX(field_name)) | 43 | size_t>::type) PICA_REG_INDEX(field_name)) |
| 44 | #endif // _MSC_VER | 44 | #endif // _MSC_VER |
| 45 | 45 | ||
| 46 | struct Regs { | 46 | struct Regs { |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 7cc3b407a..d4d5903ce 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -893,7 +893,7 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) | |||
| 893 | value_float = config.value_32bit / 16777215.0f; // 2^24 - 1 | 893 | value_float = config.value_32bit / 16777215.0f; // 2^24 - 1 |
| 894 | } | 894 | } |
| 895 | 895 | ||
| 896 | cur_state.depth.write_mask = true; | 896 | cur_state.depth.write_mask = GL_TRUE; |
| 897 | cur_state.Apply(); | 897 | cur_state.Apply(); |
| 898 | glClearBufferfv(GL_DEPTH, 0, &value_float); | 898 | glClearBufferfv(GL_DEPTH, 0, &value_float); |
| 899 | } else if (dst_type == SurfaceType::DepthStencil) { | 899 | } else if (dst_type == SurfaceType::DepthStencil) { |
| @@ -908,8 +908,8 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) | |||
| 908 | GLfloat value_float = (config.value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1 | 908 | GLfloat value_float = (config.value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1 |
| 909 | GLint value_int = (config.value_32bit >> 24); | 909 | GLint value_int = (config.value_32bit >> 24); |
| 910 | 910 | ||
| 911 | cur_state.depth.write_mask = true; | 911 | cur_state.depth.write_mask = GL_TRUE; |
| 912 | cur_state.stencil.write_mask = true; | 912 | cur_state.stencil.write_mask = 0xFF; |
| 913 | cur_state.Apply(); | 913 | cur_state.Apply(); |
| 914 | glClearBufferfi(GL_DEPTH_STENCIL, 0, value_float, value_int); | 914 | glClearBufferfi(GL_DEPTH_STENCIL, 0, value_float, value_int); |
| 915 | } | 915 | } |
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index ed84cadea..2a731f483 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -27,8 +27,8 @@ OpenGLState::OpenGLState() { | |||
| 27 | stencil.test_enabled = false; | 27 | stencil.test_enabled = false; |
| 28 | stencil.test_func = GL_ALWAYS; | 28 | stencil.test_func = GL_ALWAYS; |
| 29 | stencil.test_ref = 0; | 29 | stencil.test_ref = 0; |
| 30 | stencil.test_mask = -1; | 30 | stencil.test_mask = 0xFF; |
| 31 | stencil.write_mask = -1; | 31 | stencil.write_mask = 0xFF; |
| 32 | stencil.action_depth_fail = GL_KEEP; | 32 | stencil.action_depth_fail = GL_KEEP; |
| 33 | stencil.action_depth_pass = GL_KEEP; | 33 | stencil.action_depth_pass = GL_KEEP; |
| 34 | stencil.action_stencil_fail = GL_KEEP; | 34 | stencil.action_stencil_fail = GL_KEEP; |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 03a588364..93f0ac105 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -390,6 +390,8 @@ void RendererOpenGL::DrawSingleScreenRotated(const ScreenInfo& screen_info, floa | |||
| 390 | */ | 390 | */ |
| 391 | void RendererOpenGL::DrawScreens() { | 391 | void RendererOpenGL::DrawScreens() { |
| 392 | auto layout = render_window->GetFramebufferLayout(); | 392 | auto layout = render_window->GetFramebufferLayout(); |
| 393 | const auto& top_screen = layout.top_screen; | ||
| 394 | const auto& bottom_screen = layout.bottom_screen; | ||
| 393 | 395 | ||
| 394 | glViewport(0, 0, layout.width, layout.height); | 396 | glViewport(0, 0, layout.width, layout.height); |
| 395 | glClear(GL_COLOR_BUFFER_BIT); | 397 | glClear(GL_COLOR_BUFFER_BIT); |
| @@ -403,12 +405,15 @@ void RendererOpenGL::DrawScreens() { | |||
| 403 | glActiveTexture(GL_TEXTURE0); | 405 | glActiveTexture(GL_TEXTURE0); |
| 404 | glUniform1i(uniform_color_texture, 0); | 406 | glUniform1i(uniform_color_texture, 0); |
| 405 | 407 | ||
| 406 | DrawSingleScreenRotated(screen_infos[0], (float)layout.top_screen.left, | 408 | if (layout.top_screen_enabled) { |
| 407 | (float)layout.top_screen.top, (float)layout.top_screen.GetWidth(), | 409 | DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left, (float)top_screen.top, |
| 408 | (float)layout.top_screen.GetHeight()); | 410 | (float)top_screen.GetWidth(), (float)top_screen.GetHeight()); |
| 409 | DrawSingleScreenRotated(screen_infos[1], (float)layout.bottom_screen.left, | 411 | } |
| 410 | (float)layout.bottom_screen.top, (float)layout.bottom_screen.GetWidth(), | 412 | if (layout.bottom_screen_enabled) { |
| 411 | (float)layout.bottom_screen.GetHeight()); | 413 | DrawSingleScreenRotated(screen_infos[1], (float)bottom_screen.left, |
| 414 | (float)bottom_screen.top, (float)bottom_screen.GetWidth(), | ||
| 415 | (float)bottom_screen.GetHeight()); | ||
| 416 | } | ||
| 412 | 417 | ||
| 413 | m_current_frame++; | 418 | m_current_frame++; |
| 414 | } | 419 | } |