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 { SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) { if (SDL_Init(SDL_INIT_AUDIO) < 0) { - LOG_CRITICAL(Audio_Sink, "SDL_Init(SDL_INIT_AUDIO) failed"); + LOG_CRITICAL(Audio_Sink, "SDL_Init(SDL_INIT_AUDIO) failed with: %s", SDL_GetError()); impl->audio_device_id = 0; return; } @@ -45,7 +45,7 @@ SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) { impl->audio_device_id = SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0); if (impl->audio_device_id <= 0) { - LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed"); + LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed with: %s", SDL_GetError()); return; } 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() { Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0); Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 1.0); + // Layout + Settings::values.layout_option = + static_cast<Settings::LayoutOption>(sdl2_config->GetInteger("Layout", "layout_option", 0)); + Settings::values.swap_screen = sdl2_config->GetBoolean("Layout", "swap_screen", false); + // Audio Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); 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 = # 0 (default): Off, 1: On use_vsync = +[Layout] +# Layout for the screen inside the render window. +# 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen +layout_option = + +# Swaps the prominent screen with the other screen. +# For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen. +# 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent +swap_screen = + # The clear color for the renderer. What shows up on the sides of the bottom screen. # Must be in range of 0.0-1.0. Defaults to 1.0 for all. 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 { void EmuWindow_SDL2::OnResize() { int width, height; - SDL_GetWindowSize(render_window, &width, &height); - - NotifyFramebufferLayoutChanged( - EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height)); + UpdateCurrentFramebufferLayout(width, height); } 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() { qreal pixelRatio = windowPixelRatio(); unsigned width = child->QPaintDevice::width() * pixelRatio; unsigned height = child->QPaintDevice::height() * pixelRatio; - - NotifyFramebufferLayoutChanged( - EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height)); + UpdateCurrentFramebufferLayout(width, height); } 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() { Settings::values.bg_blue = qt_config->value("bg_blue", 1.0).toFloat(); qt_config->endGroup(); + qt_config->beginGroup("Layout"); + Settings::values.layout_option = + static_cast<Settings::LayoutOption>(qt_config->value("layout_option").toInt()); + Settings::values.swap_screen = qt_config->value("swap_screen", false).toBool(); + qt_config->endGroup(); + qt_config->beginGroup("Audio"); Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); Settings::values.enable_audio_stretching = @@ -155,6 +161,11 @@ void Config::SaveValues() { qt_config->setValue("bg_blue", (double)Settings::values.bg_blue); qt_config->endGroup(); + qt_config->beginGroup("Layout"); + qt_config->setValue("layout_option", static_cast<int>(Settings::values.layout_option)); + qt_config->setValue("swap_screen", Settings::values.swap_screen); + qt_config->endGroup(); + qt_config->beginGroup("Audio"); qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id)); 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() { ui->graphicsTab->applyConfiguration(); ui->audioTab->applyConfiguration(); ui->debugTab->applyConfiguration(); + Settings::Apply(); } 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() { ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit); ui->toggle_scaled_resolution->setChecked(Settings::values.use_scaled_resolution); ui->toggle_vsync->setChecked(Settings::values.use_vsync); + ui->layout_combobox->setCurrentIndex(static_cast<int>(Settings::values.layout_option)); + ui->swap_screen->setChecked(Settings::values.swap_screen); } void ConfigureGraphics::applyConfiguration() { @@ -30,5 +32,8 @@ void ConfigureGraphics::applyConfiguration() { Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked(); Settings::values.use_scaled_resolution = ui->toggle_scaled_resolution->isChecked(); Settings::values.use_vsync = ui->toggle_vsync->isChecked(); + Settings::values.layout_option = + static_cast<Settings::LayoutOption>(ui->layout_combobox->currentIndex()); + Settings::values.swap_screen = ui->swap_screen->isChecked(); Settings::Apply(); } 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 @@ <string>Graphics</string> </property> <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <widget class="QCheckBox" name="toggle_hw_renderer"> - <property name="text"> - <string>Enable hardware renderer</string> - </property> + <item> + <widget class="QCheckBox" name="toggle_hw_renderer"> + <property name="text"> + <string>Enable hardware renderer</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="toggle_shader_jit"> + <property name="text"> + <string>Enable shader JIT</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="toggle_scaled_resolution"> + <property name="text"> + <string>Enable scaled resolution</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="toggle_vsync"> + <property name="text"> + <string>Enable V-Sync</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QGroupBox" name="groupBox2"> + <property name="title"> + <string>Layout</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="label1"> + <property name="text"> + <string>Screen Layout:</string> + </property> </widget> - </item> - <item> - <widget class="QCheckBox" name="toggle_shader_jit"> + </item> + <item> + <widget class="QComboBox" name="layout_combobox"> + <item> <property name="text"> - <string>Enable shader JIT</string> + <string notr="true">Default</string> </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="toggle_scaled_resolution"> + </item> + <item> <property name="text"> - <string>Enable scaled resolution</string> + <string notr="true">Single Screen</string> </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="toggle_vsync"> + </item> + <item> <property name="text"> - <string>Enable V-Sync</string> + <string notr="true">Large Screen</string> </property> + </item> </widget> - </item> + </item> + </layout> + </item> + <item> + <widget class="QCheckBox" name="swap_screen"> + <property name="text"> + <string>Swap Screens</string> + </property> + </widget> + </item> </layout> - </widget> - </item> - </layout> + </item> + </layout> + </widget> </item> <item> <spacer name="verticalSpacer"> @@ -71,22 +121,5 @@ </layout> </widget> <resources/> - <connections> - <connection> - <sender>toggle_gdbstub</sender> - <signal>toggled(bool)</signal> - <receiver>gdbport_spinbox</receiver> - <slot>setEnabled(bool)</slot> - <hints> - <hint type="sourcelabel"> - <x>84</x> - <y>157</y> - </hint> - <hint type="destinationlabel"> - <x>342</x> - <y>158</y> - </hint> - </hints> - </connection> - </connections> + <connections/> </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 @@ #include "qhexedit.h" #include "video_core/video_core.h" +#ifdef QT_STATICPLUGIN +Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); +#endif + GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { Pica::g_debug_context = Pica::DebugContext::Construct(); @@ -101,7 +105,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { addDockWidget(Qt::RightDockWidgetArea, graphicsTracingWidget); graphicsTracingWidget->hide(); - auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica surface viewer"), this); + auto graphicsSurfaceViewerAction = new QAction(tr("Create Pica Surface Viewer"), this); connect(graphicsSurfaceViewerAction, SIGNAL(triggered()), this, SLOT(OnCreateGraphicsSurfaceViewer())); @@ -196,6 +200,7 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { // Setup hotkeys RegisterHotkey("Main Window", "Load File", QKeySequence::Open); + RegisterHotkey("Main Window", "Swap Screens", QKeySequence::NextChild); RegisterHotkey("Main Window", "Start Emulation"); LoadHotkeys(); @@ -203,6 +208,8 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { SLOT(OnMenuLoadFile())); connect(GetHotkey("Main Window", "Start Emulation", this), SIGNAL(activated()), this, SLOT(OnStartGame())); + connect(GetHotkey("Main Window", "Swap Screens", this), SIGNAL(activated()), this, + SLOT(OnSwapScreens())); std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); @@ -550,6 +557,11 @@ void GMainWindow::OnConfigure() { } } +void GMainWindow::OnSwapScreens() { + Settings::values.swap_screen = !Settings::values.swap_screen; + Settings::Apply(); +} + void GMainWindow::OnCreateGraphicsSurfaceViewer() { auto graphicsSurfaceViewerWidget = new GraphicsSurfaceWidget(Pica::g_debug_context, this); 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: /// Called whenever a user selects the "File->Select Game List Root" menu item void OnMenuSelectGameListRoot(); void OnMenuRecentFile(); + void OnSwapScreens(); void OnConfigure(); void OnDisplayTitleBars(bool); 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 @@ </action> <action name="action_Configure"> <property name="text"> - <string>Configure ...</string> + <string>Configure...</string> </property> </action> <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 break_points.cpp emu_window.cpp file_util.cpp + framebuffer_layout.cpp hash.cpp key_map.cpp logging/filter.cpp @@ -35,6 +36,7 @@ set(HEADERS common_types.h emu_window.h file_util.h + framebuffer_layout.h hash.h key_map.h 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 @@ #define EMU_DATA_DIR USER_DIR #else #ifdef _WIN32 -#define EMU_DATA_DIR "Citra Emulator" +#define EMU_DATA_DIR "Citra" #else #define EMU_DATA_DIR "citra-emu" #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) { * @param framebuffer_y Framebuffer y-coordinate to check * @return True if the coordinates are within the touchpad, otherwise false */ -static bool IsWithinTouchscreen(const EmuWindow::FramebufferLayout& layout, unsigned framebuffer_x, +static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, unsigned framebuffer_x, unsigned framebuffer_y) { return ( 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) { TouchPressed(framebuffer_x, framebuffer_y); } -EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, - unsigned height) { - // When hiding the widget, the function receives a size of 0 - if (width == 0) - width = 1; - if (height == 0) - height = 1; - - EmuWindow::FramebufferLayout res = {width, height, {}, {}}; - - float window_aspect_ratio = static_cast<float>(height) / width; - float emulation_aspect_ratio = - static_cast<float>(VideoCore::kScreenTopHeight * 2) / VideoCore::kScreenTopWidth; - - if (window_aspect_ratio > emulation_aspect_ratio) { - // Window is narrower than the emulation content => apply borders to the top and bottom - int viewport_height = static_cast<int>(std::round(emulation_aspect_ratio * width)); - - res.top_screen.left = 0; - res.top_screen.right = res.top_screen.left + width; - res.top_screen.top = (height - viewport_height) / 2; - res.top_screen.bottom = res.top_screen.top + viewport_height / 2; - - int bottom_width = static_cast<int>( - (static_cast<float>(VideoCore::kScreenBottomWidth) / VideoCore::kScreenTopWidth) * - (res.top_screen.right - res.top_screen.left)); - int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2; - - res.bottom_screen.left = bottom_border; - res.bottom_screen.right = res.bottom_screen.left + bottom_width; - res.bottom_screen.top = res.top_screen.bottom; - res.bottom_screen.bottom = res.bottom_screen.top + viewport_height / 2; - } else { - // Otherwise, apply borders to the left and right sides of the window. - int viewport_width = static_cast<int>(std::round(height / emulation_aspect_ratio)); - - res.top_screen.left = (width - viewport_width) / 2; - res.top_screen.right = res.top_screen.left + viewport_width; - res.top_screen.top = 0; - res.top_screen.bottom = res.top_screen.top + height / 2; - - int bottom_width = static_cast<int>( - (static_cast<float>(VideoCore::kScreenBottomWidth) / VideoCore::kScreenTopWidth) * - (res.top_screen.right - res.top_screen.left)); - int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2; - - res.bottom_screen.left = res.top_screen.left + bottom_border; - res.bottom_screen.right = res.bottom_screen.left + bottom_width; - res.bottom_screen.top = res.top_screen.bottom; - res.bottom_screen.bottom = res.bottom_screen.top + height / 2; +void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { + Layout::FramebufferLayout layout; + switch (Settings::values.layout_option) { + case Settings::LayoutOption::SingleScreen: + layout = Layout::SingleFrameLayout(width, height, Settings::values.swap_screen); + break; + case Settings::LayoutOption::LargeScreen: + layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen); + break; + case Settings::LayoutOption::Default: + default: + layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen); + break; } - - return res; + NotifyFramebufferLayoutChanged(layout); } 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 @@ #include <tuple> #include <utility> #include "common/common_types.h" +#include "common/framebuffer_layout.h" #include "common/math_util.h" #include "core/hle/service/hid/hid.h" @@ -38,23 +39,6 @@ public: std::pair<unsigned, unsigned> min_client_area_size; }; - /// Describes the layout of the window framebuffer (size and top/bottom screen positions) - struct FramebufferLayout { - - /** - * Factory method for constructing a default FramebufferLayout - * @param width Window framebuffer width in pixels - * @param height Window framebuffer height in pixels - * @return Newly created FramebufferLayout object with default screen regions initialized - */ - static FramebufferLayout DefaultScreenLayout(unsigned width, unsigned height); - - unsigned width; - unsigned height; - MathUtil::Rectangle<unsigned> top_screen; - MathUtil::Rectangle<unsigned> bottom_screen; - }; - /// Swap buffers to display the next frame virtual void SwapBuffers() = 0; @@ -211,10 +195,16 @@ public: * Gets the framebuffer layout (width, height, and screen regions) * @note This method is thread-safe */ - const FramebufferLayout& GetFramebufferLayout() const { + const Layout::FramebufferLayout& GetFramebufferLayout() const { return framebuffer_layout; } + /** + * Convenience method to update the current frame layout + * Read from the current settings to determine which layout to use. + */ + void UpdateCurrentFramebufferLayout(unsigned width, unsigned height); + protected: EmuWindow() { // TODO: Find a better place to set this. @@ -250,7 +240,7 @@ protected: * Update framebuffer layout with the given parameter. * @note EmuWindow implementations will usually use this in window resize event handlers. */ - void NotifyFramebufferLayoutChanged(const FramebufferLayout& layout) { + void NotifyFramebufferLayoutChanged(const Layout::FramebufferLayout& layout) { framebuffer_layout = layout; } @@ -274,7 +264,7 @@ private: // By default, ignore this request and do nothing. } - FramebufferLayout framebuffer_layout; ///< Current framebuffer layout + Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout unsigned client_area_width; ///< Current client width, should be set by window impl. 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 @@ #define stat _stat64 #define fstat _fstat64 #define fileno _fileno +// Windows version, at least Vista is required to obtain AppData Path +#define WINVER 0x0600 +#define _WIN32_WINNT 0x0600 #else #ifdef __APPLE__ #include <sys/param.h> @@ -594,6 +597,15 @@ std::string& GetExeDirectory() { } return exe_path; } + +std::string AppDataRoamingDirectory() { + PWSTR pw_local_path = nullptr; + // Only supported by Windows Vista or later + SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &pw_local_path); + std::string local_path = Common::UTF16ToUTF8(pw_local_path); + CoTaskMemFree(pw_local_path); + return local_path; +} #else /** * @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 if (paths[D_USER_IDX].empty()) { #ifdef _WIN32 paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; + if (!FileUtil::IsDirectory(paths[D_USER_IDX])) { + paths[D_USER_IDX] = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP; + } + paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; #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(); #ifdef _WIN32 std::string& GetExeDirectory(); +std::string AppDataRoamingDirectory(); #endif 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 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cmath> + +#include "common/assert.h" +#include "common/framebuffer_layout.h" +#include "video_core/video_core.h" + +namespace Layout { + +static const float TOP_SCREEN_ASPECT_RATIO = + static_cast<float>(VideoCore::kScreenTopHeight) / VideoCore::kScreenTopWidth; +static const float BOT_SCREEN_ASPECT_RATIO = + static_cast<float>(VideoCore::kScreenBottomHeight) / VideoCore::kScreenBottomWidth; + +// Finds the largest size subrectangle contained in window area that is confined to the aspect ratio +template <class T> +static MathUtil::Rectangle<T> maxRectangle(MathUtil::Rectangle<T> window_area, + float screen_aspect_ratio) { + float scale = std::min(static_cast<float>(window_area.GetWidth()), + window_area.GetHeight() / screen_aspect_ratio); + return MathUtil::Rectangle<T>{0, 0, static_cast<T>(std::round(scale)), + static_cast<T>(std::round(scale * screen_aspect_ratio))}; +} + +FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height, bool swapped) { + ASSERT(width > 0); + ASSERT(height > 0); + + FramebufferLayout res{width, height, true, true, {}, {}}; + // Default layout gives equal screen sizes to the top and bottom screen + MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height / 2}; + MathUtil::Rectangle<unsigned> top_screen = + maxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO); + MathUtil::Rectangle<unsigned> bot_screen = + maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO); + + float window_aspect_ratio = static_cast<float>(height) / width; + // both screens height are taken into account by multiplying by 2 + float emulation_aspect_ratio = TOP_SCREEN_ASPECT_RATIO * 2; + + if (window_aspect_ratio < emulation_aspect_ratio) { + // Apply borders to the left and right sides of the window. + top_screen = + top_screen.TranslateX((screen_window_area.GetWidth() - top_screen.GetWidth()) / 2); + bot_screen = + bot_screen.TranslateX((screen_window_area.GetWidth() - bot_screen.GetWidth()) / 2); + } else { + // Window is narrower than the emulation content => apply borders to the top and bottom + // Recalculate the bottom screen to account for the width difference between top and bottom + screen_window_area = {0, 0, width, top_screen.GetHeight()}; + bot_screen = maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO); + bot_screen = bot_screen.TranslateX((top_screen.GetWidth() - bot_screen.GetWidth()) / 2); + if (swapped) { + bot_screen = bot_screen.TranslateY(height / 2 - bot_screen.GetHeight()); + } else { + top_screen = top_screen.TranslateY(height / 2 - top_screen.GetHeight()); + } + } + // Move the top screen to the bottom if we are swapped. + res.top_screen = swapped ? top_screen.TranslateY(height / 2) : top_screen; + res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateY(height / 2); + return res; +} + +FramebufferLayout SingleFrameLayout(unsigned width, unsigned height, bool swapped) { + ASSERT(width > 0); + ASSERT(height > 0); + // The drawing code needs at least somewhat valid values for both screens + // so just calculate them both even if the other isn't showing. + FramebufferLayout res{width, height, !swapped, swapped, {}, {}}; + + MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height}; + MathUtil::Rectangle<unsigned> top_screen = + maxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO); + MathUtil::Rectangle<unsigned> bot_screen = + maxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO); + + float window_aspect_ratio = static_cast<float>(height) / width; + float emulation_aspect_ratio = (swapped) ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO; + + if (window_aspect_ratio < emulation_aspect_ratio) { + top_screen = + top_screen.TranslateX((screen_window_area.GetWidth() - top_screen.GetWidth()) / 2); + bot_screen = + bot_screen.TranslateX((screen_window_area.GetWidth() - bot_screen.GetWidth()) / 2); + } else { + top_screen = top_screen.TranslateY((height - top_screen.GetHeight()) / 2); + bot_screen = bot_screen.TranslateY((height - bot_screen.GetHeight()) / 2); + } + res.top_screen = top_screen; + res.bottom_screen = bot_screen; + return res; +} + +FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool swapped) { + ASSERT(width > 0); + ASSERT(height > 0); + + FramebufferLayout res{width, height, true, true, {}, {}}; + // Split the window into two parts. Give 4x width to the main screen and 1x width to the small + // To do that, find the total emulation box and maximize that based on window size + float window_aspect_ratio = static_cast<float>(height) / width; + float emulation_aspect_ratio = + swapped + ? VideoCore::kScreenBottomHeight * 4 / + (VideoCore::kScreenBottomWidth * 4.0f + VideoCore::kScreenTopWidth) + : VideoCore::kScreenTopHeight * 4 / + (VideoCore::kScreenTopWidth * 4.0f + VideoCore::kScreenBottomWidth); + float large_screen_aspect_ratio = swapped ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO; + float small_screen_aspect_ratio = swapped ? TOP_SCREEN_ASPECT_RATIO : BOT_SCREEN_ASPECT_RATIO; + + MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height}; + MathUtil::Rectangle<unsigned> total_rect = + maxRectangle(screen_window_area, emulation_aspect_ratio); + MathUtil::Rectangle<unsigned> large_screen = + maxRectangle(total_rect, large_screen_aspect_ratio); + MathUtil::Rectangle<unsigned> fourth_size_rect = total_rect.Scale(.25f); + MathUtil::Rectangle<unsigned> small_screen = + maxRectangle(fourth_size_rect, small_screen_aspect_ratio); + + if (window_aspect_ratio < emulation_aspect_ratio) { + large_screen = + large_screen.TranslateX((screen_window_area.GetWidth() - total_rect.GetWidth()) / 2); + } else { + large_screen = large_screen.TranslateY((height - total_rect.GetHeight()) / 2); + } + // Shift the small screen to the bottom right corner + small_screen = + small_screen.TranslateX(large_screen.right) + .TranslateY(large_screen.GetHeight() + large_screen.top - small_screen.GetHeight()); + res.top_screen = swapped ? small_screen : large_screen; + res.bottom_screen = swapped ? large_screen : small_screen; + return res; +} +} 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 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/math_util.h" +namespace Layout { +/// Describes the layout of the window framebuffer (size and top/bottom screen positions) +struct FramebufferLayout { + unsigned width; + unsigned height; + bool top_screen_enabled; + bool bottom_screen_enabled; + MathUtil::Rectangle<unsigned> top_screen; + MathUtil::Rectangle<unsigned> bottom_screen; +}; + +/** + * Factory method for constructing a default FramebufferLayout + * @param width Window framebuffer width in pixels + * @param height Window framebuffer height in pixels + * @param is_swapped if true, the bottom screen will be displayed above the top screen + * @return Newly created FramebufferLayout object with default screen regions initialized + */ +FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height, bool is_swapped); + +/** + * Factory method for constructing a FramebufferLayout with only the top or bottom screen + * @param width Window framebuffer width in pixels + * @param height Window framebuffer height in pixels + * @param is_swapped if true, the bottom screen will be displayed (and the top won't be displayed) + * @return Newly created FramebufferLayout object with default screen regions initialized + */ +FramebufferLayout SingleFrameLayout(unsigned width, unsigned height, bool is_swapped); + +/** + * Factory method for constructing a Frame with the a 4x size Top screen with a 1x size bottom + * screen on the right + * This is useful in particular because it matches well with a 1920x1080 resolution monitor + * @param width Window framebuffer width in pixels + * @param height Window framebuffer height in pixels + * @param is_swapped if true, the bottom screen will be the large display + * @return Newly created FramebufferLayout object with default screen regions initialized + */ +FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped); +} 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 { SUB(Service, AM) \ SUB(Service, PTM) \ SUB(Service, LDR) \ + SUB(Service, MIC) \ SUB(Service, NDM) \ SUB(Service, NIM) \ 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 { Service_AM, ///< The AM (Application manager) service Service_PTM, ///< The PTM (Power status & misc.) service Service_LDR, ///< The LDR (3ds dll loader) service + Service_MIC, ///< The MIC (microphone) service Service_NDM, ///< The NDM (Network daemon manager) service Service_NIM, ///< The NIM (Network interface manager) service 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 { T GetHeight() const { return std::abs(static_cast<typename std::make_signed<T>::type>(bottom - top)); } + Rectangle<T> TranslateX(const T x) const { + return Rectangle{left + x, top, right + x, bottom}; + } + Rectangle<T> TranslateY(const T y) const { + return Rectangle{left, top + y, right, bottom + y}; + } + Rectangle<T> Scale(const float s) const { + return Rectangle{left, top, static_cast<T>(left + GetWidth() * s), + static_cast<T>(top + GetHeight() * s)}; + } }; } // 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 @@ #include "common/common_paths.h" #include "common/logging/log.h" #include "common/string_util.h" -#ifdef _MSC_VER + +#ifdef _WIN32 #include <codecvt> #include <Windows.h> #include "common/common_funcs.h" @@ -270,7 +271,7 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st return result; } -#ifdef _MSC_VER +#ifdef _WIN32 std::string UTF16ToUTF8(const std::u16string& input) { #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) { user_callbacks.MemoryWrite16 = &Memory::Write16; user_callbacks.MemoryWrite32 = &Memory::Write32; user_callbacks.MemoryWrite64 = &Memory::Write64; + user_callbacks.page_table = Memory::GetCurrentPageTablePointers(); return user_callbacks; } @@ -130,9 +131,9 @@ MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { MICROPROFILE_SCOPE(ARM_Jit); - jit->Run(static_cast<unsigned>(num_instructions)); + unsigned ticks_executed = jit->Run(static_cast<unsigned>(num_instructions)); - AddTicks(num_instructions); + AddTicks(ticks_executed); } 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 @@ #include <numeric> #include <fcntl.h> -#ifdef _MSC_VER +#ifdef _WIN32 #include <WinSock2.h> #include <common/x64/abi.h> #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 { namespace Applets { ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& parameter) { - if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { + if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) { LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); UNIMPLEMENTED(); // TODO(Subv): Find the right error code @@ -36,7 +36,7 @@ ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& param // Send the response message with the newly created SharedMemory Service::APT::MessageParameter result; - result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); + result.signal = static_cast<u32>(Service::APT::SignalType::Response); result.buffer.clear(); result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); result.sender_id = static_cast<u32>(id); @@ -57,7 +57,7 @@ ResultCode ErrEula::StartImpl(const Service::APT::AppletStartupParameter& parame Service::APT::MessageParameter message; message.buffer.resize(parameter.buffer.size()); std::fill(message.buffer.begin(), message.buffer.end(), 0); - message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); + message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit); message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); message.sender_id = static_cast<u32>(id); 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 { namespace Applets { ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& parameter) { - if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { + if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) { LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); UNIMPLEMENTED(); // TODO(Subv): Find the right error code @@ -44,7 +44,7 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p // Send the response message with the newly created SharedMemory Service::APT::MessageParameter result; - result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); + result.signal = static_cast<u32>(Service::APT::SignalType::Response); result.buffer.clear(); result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); result.sender_id = static_cast<u32>(id); @@ -73,7 +73,7 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa Service::APT::MessageParameter message; message.buffer.resize(sizeof(MiiResult)); std::memcpy(message.buffer.data(), &result, message.buffer.size()); - message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); + message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit); message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); message.sender_id = static_cast<u32>(id); 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 { namespace Applets { ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) { - if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { + if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) { LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); UNIMPLEMENTED(); // TODO(Subv): Find the right error code @@ -47,7 +47,7 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con // Send the response message with the newly created SharedMemory Service::APT::MessageParameter result; - result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); + result.signal = static_cast<u32>(Service::APT::SignalType::Response); result.buffer.clear(); result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); result.sender_id = static_cast<u32>(id); @@ -108,7 +108,7 @@ void SoftwareKeyboard::Finalize() { Service::APT::MessageParameter message; message.buffer.resize(sizeof(SoftwareKeyboardConfig)); std::memcpy(message.buffer.data(), &config, message.buffer.size()); - message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); + message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit); message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); message.sender_id = static_cast<u32>(id); 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) { evt->reset_type = reset_type; evt->name = std::move(name); + if (reset_type == ResetType::Pulse) { + LOG_ERROR(Kernel, "Unimplemented event reset type Pulse"); + UNIMPLEMENTED(); + } + return evt; } 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) { timer->interval_delay = 0; timer->callback_handle = timer_callback_handle_table.Create(timer).MoveFrom(); + if (reset_type == ResetType::Pulse) { + LOG_ERROR(Kernel, "Unimplemented timer reset type Pulse"); + UNIMPLEMENTED(); + } + return timer; } 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 @@ namespace AC_U { +struct ACConfig { + std::array<u8, 0x200> data; +}; + +static ACConfig default_config{}; + +static bool ac_connected = false; + +static Kernel::SharedPtr<Kernel::Event> close_event; +static Kernel::SharedPtr<Kernel::Event> connect_event; +static Kernel::SharedPtr<Kernel::Event> disconnect_event; + +/** + * AC_U::CreateDefaultConfig service function + * Inputs: + * 64 : ACConfig size << 14 | 2 + * 65 : pointer to ACConfig struct + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void CreateDefaultConfig(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 ac_config_addr = cmd_buff[65]; + + ASSERT_MSG(cmd_buff[64] == (sizeof(ACConfig) << 14 | 2), + "Output buffer size not equal ACConfig size"); + + Memory::WriteBlock(ac_config_addr, &default_config, sizeof(ACConfig)); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + +/** + * AC_U::ConnectAsync service function + * Inputs: + * 1 : ProcessId Header + * 3 : Copy Handle Header + * 4 : Connection Event handle + * 5 : ACConfig size << 14 | 2 + * 6 : pointer to ACConfig struct + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void ConnectAsync(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + connect_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); + if (connect_event) { + connect_event->name = "AC_U:connect_event"; + connect_event->Signal(); + ac_connected = true; + } + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + +/** + * AC_U::GetConnectResult service function + * Inputs: + * 1 : ProcessId Header + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void GetConnectResult(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + /** * AC_U::CloseAsync service function * Inputs: - * 1 : Always 0x20 - * 3 : Always 0 + * 1 : ProcessId Header + * 3 : Copy Handle Header * 4 : Event handle, should be signaled when AC connection is closed * Outputs: * 1 : Result of function, 0 on success, otherwise error code @@ -23,16 +97,37 @@ namespace AC_U { static void CloseAsync(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); + if (ac_connected && disconnect_event) { + disconnect_event->Signal(); + } - if (evt) { - evt->name = "AC_U:close_event"; - evt->Signal(); + close_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); + if (close_event) { + close_event->name = "AC_U:close_event"; + close_event->Signal(); } + + ac_connected = false; + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + +/** + * AC_U::GetCloseResult service function + * Inputs: + * 1 : ProcessId Header + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void GetCloseResult(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_AC, "(STUBBED) called"); } + /** * AC_U::GetWifiStatus service function * Outputs: @@ -52,6 +147,75 @@ static void GetWifiStatus(Service::Interface* self) { } /** + * AC_U::GetInfraPriority service function + * Inputs: + * 1 : ACConfig size << 14 | 2 + * 2 : pointer to ACConfig struct + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Infra Priority + */ +static void GetInfraPriority(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = 0; // Infra Priority, default 0 + + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + +/** + * AC_U::SetRequestEulaVersion service function + * Inputs: + * 1 : Eula Version major + * 2 : Eula Version minor + * 3 : ACConfig size << 14 | 2 + * 4 : Input pointer to ACConfig struct + * 64 : ACConfig size << 14 | 2 + * 65 : Output pointer to ACConfig struct + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Infra Priority + */ +static void SetRequestEulaVersion(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 major = cmd_buff[1] & 0xFF; + u32 minor = cmd_buff[2] & 0xFF; + + ASSERT_MSG(cmd_buff[3] == (sizeof(ACConfig) << 14 | 2), + "Input buffer size not equal ACConfig size"); + ASSERT_MSG(cmd_buff[64] == (sizeof(ACConfig) << 14 | 2), + "Output buffer size not equal ACConfig size"); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = 0; // Infra Priority + + LOG_WARNING(Service_AC, "(STUBBED) called, major=%u, minor=%u", major, minor); +} + +/** + * AC_U::RegisterDisconnectEvent service function + * Inputs: + * 1 : ProcessId Header + * 3 : Copy Handle Header + * 4 : Event handle, should be signaled when AC connection is closed + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void RegisterDisconnectEvent(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + disconnect_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); + if (disconnect_event) { + disconnect_event->name = "AC_U:disconnect_event"; + } + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + +/** * AC_U::IsConnected service function * Outputs: * 1 : Result of function, 0 on success, otherwise error code @@ -61,26 +225,29 @@ static void IsConnected(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = false; // Not connected to ac:u service + cmd_buff[2] = ac_connected; LOG_WARNING(Service_AC, "(STUBBED) called"); } const Interface::FunctionInfo FunctionTable[] = { - {0x00010000, nullptr, "CreateDefaultConfig"}, - {0x00040006, nullptr, "ConnectAsync"}, - {0x00050002, nullptr, "GetConnectResult"}, + {0x00010000, CreateDefaultConfig, "CreateDefaultConfig"}, + {0x00040006, ConnectAsync, "ConnectAsync"}, + {0x00050002, GetConnectResult, "GetConnectResult"}, + {0x00070002, nullptr, "CancelConnectAsync"}, {0x00080004, CloseAsync, "CloseAsync"}, - {0x00090002, nullptr, "GetCloseResult"}, + {0x00090002, GetCloseResult, "GetCloseResult"}, {0x000A0000, nullptr, "GetLastErrorCode"}, + {0x000C0000, nullptr, "GetStatus"}, {0x000D0000, GetWifiStatus, "GetWifiStatus"}, {0x000E0042, nullptr, "GetCurrentAPInfo"}, {0x00100042, nullptr, "GetCurrentNZoneInfo"}, {0x00110042, nullptr, "GetNZoneApNumService"}, + {0x001D0042, nullptr, "ScanAPs"}, {0x00240042, nullptr, "AddDenyApType"}, - {0x00270002, nullptr, "GetInfraPriority"}, - {0x002D0082, nullptr, "SetRequestEulaVersion"}, - {0x00300004, nullptr, "RegisterDisconnectEvent"}, + {0x00270002, GetInfraPriority, "GetInfraPriority"}, + {0x002D0082, SetRequestEulaVersion, "SetRequestEulaVersion"}, + {0x00300004, RegisterDisconnectEvent, "RegisterDisconnectEvent"}, {0x003C0042, nullptr, "GetAPSSIDList"}, {0x003E0042, IsConnected, "IsConnected"}, {0x00400042, nullptr, "SetClientVersion"}, @@ -91,6 +258,18 @@ const Interface::FunctionInfo FunctionTable[] = { Interface::Interface() { Register(FunctionTable); + + ac_connected = false; + + close_event = nullptr; + connect_event = nullptr; + disconnect_event = nullptr; +} + +Interface::~Interface() { + close_event = nullptr; + connect_event = nullptr; + disconnect_event = nullptr; } } // 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 { class Interface : public Service::Interface { public: Interface(); + ~Interface(); std::string GetPortName() const override { 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) { cmd_buff[1] = applet->Start(parameter).raw; } +void CancelLibraryApplet(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 exiting = cmd_buff[1] & 0xFF; + + cmd_buff[1] = 1; // TODO: Find the return code meaning + + LOG_WARNING(Service_APT, "(STUBBED) called exiting=%u", exiting); +} + void SetScreenCapPostPermission(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); @@ -523,7 +532,7 @@ void Init() { notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification"); parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start"); - next_parameter.signal = static_cast<u32>(SignalType::AppJustStarted); + next_parameter.signal = static_cast<u32>(SignalType::Wakeup); next_parameter.destination_id = 0x300; } 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 /// Signals used by APT functions enum class SignalType : u32 { None = 0x0, - AppJustStarted = 0x1, - LibAppJustStarted = 0x2, - LibAppFinished = 0x3, - LibAppClosed = 0xA, - ReturningToApp = 0xB, - ExitingApp = 0xC, + Wakeup = 0x1, + Request = 0x2, + Response = 0x3, + Exit = 0x4, + Message = 0x5, + HomeButtonSingle = 0x6, + HomeButtonDouble = 0x7, + DspSleep = 0x8, + DspWakeup = 0x9, + WakeupByExit = 0xA, + WakeupByPause = 0xB, + WakeupByCancel = 0xC, + WakeupByCancelAll = 0xD, + WakeupByPowerButtonClick = 0xE, + WakeupToJumpHome = 0xF, + RequestForSysApplet = 0x10, + WakeupToLaunchApplication = 0x11, }; /// App Id's used by APT functions @@ -381,6 +392,17 @@ void PreloadLibraryApplet(Service::Interface* self); void StartLibraryApplet(Service::Interface* self); /** + * APT::CancelLibraryApplet service function + * Inputs: + * 0 : Command header [0x003B0040] + * 1 : u8, Application exiting (0 = not exiting, 1 = exiting) + * Outputs: + * 0 : Header code + * 1 : Result code + */ +void CancelLibraryApplet(Service::Interface* self); + +/** * APT::GetStartupArgument service function * Inputs: * 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[] = { {0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"}, {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, - {0x003B0040, nullptr, "CancelLibraryApplet?"}, + {0x003B0040, CancelLibraryApplet, "CancelLibraryApplet"}, {0x003E0080, nullptr, "ReplySleepQuery"}, {0x00430040, NotifyToWait, "NotifyToWait?"}, {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[] = { {0x00380040, nullptr, "PreloadResidentApplet"}, {0x00390040, nullptr, "PrepareToStartResidentApplet"}, {0x003A0044, nullptr, "StartResidentApplet"}, - {0x003B0040, nullptr, "CancelLibraryApplet"}, + {0x003B0040, CancelLibraryApplet, "CancelLibraryApplet"}, {0x003C0042, nullptr, "SendDspSleep"}, {0x003D0042, nullptr, "SendDspWakeUp"}, {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, enum ConfigBlockID { StereoCameraSettingsBlockID = 0x00050005, SoundOutputModeBlockID = 0x00070001, - ConsoleUniqueIDBlockID = 0x00090001, + ConsoleUniqueID1BlockID = 0x00090000, + ConsoleUniqueID2BlockID = 0x00090001, UsernameBlockID = 0x000A0000, BirthdayBlockID = 0x000A0001, LanguageBlockID = 0x000A0002, @@ -410,7 +411,12 @@ ResultCode FormatConfig() { if (!res.IsSuccess()) return res; - res = CreateConfigInfoBlk(ConsoleUniqueIDBlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, + res = CreateConfigInfoBlk(ConsoleUniqueID1BlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, + &CONSOLE_UNIQUE_ID); + if (!res.IsSuccess()) + return res; + + res = CreateConfigInfoBlk(ConsoleUniqueID2BlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID); if (!res.IsSuccess()) 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 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> +#include <chrono> +#include <iomanip> +#include <sstream> + #include "common/bit_field.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/result.h" #include "core/hle/service/err_f.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -12,12 +18,45 @@ namespace ERR_F { -enum { - ErrSpecifier0 = 0, - ErrSpecifier1 = 1, - ErrSpecifier3 = 3, - ErrSpecifier4 = 4, +enum class FatalErrType : u32 { + Generic = 0, + Corrupted = 1, + CardRemoved = 2, + Exception = 3, + ResultFailure = 4, + Logged = 5, +}; + +enum class ExceptionType : u32 { + PrefetchAbort = 0, + DataAbort = 1, + Undefined = 2, + VectorFP = 3, +}; + +struct ExceptionInfo { + u8 exception_type; + INSERT_PADDING_BYTES(3); + u32 sr; + u32 ar; + u32 fpexc; + u32 fpinst; + u32 fpinst2; +}; +static_assert(sizeof(ExceptionInfo) == 0x18, "ExceptionInfo struct has incorrect size"); + +struct ExceptionContext final { + std::array<u32, 16> arm_regs; + u32 cpsr; }; +static_assert(sizeof(ExceptionContext) == 0x44, "ExceptionContext struct has incorrect size"); + +struct ExceptionData { + ExceptionInfo exception_info; + ExceptionContext exception_context; + INSERT_PADDING_WORDS(1); +}; +static_assert(sizeof(ExceptionData) == 0x60, "ExceptionData struct has incorrect size"); // This is used instead of ResultCode from result.h // because we can't have non-trivial data members in unions. @@ -30,150 +69,191 @@ union RSL { BitField<27, 5, u32> level; }; -union ErrInfo { - u8 specifier; - - struct { - u8 specifier; // 0x0 - u8 rev_high; // 0x1 - u16 rev_low; // 0x2 - RSL result_code; // 0x4 - u32 address; // 0x8 - INSERT_PADDING_BYTES(4); // 0xC - u32 pid_low; // 0x10 - u32 pid_high; // 0x14 - u32 aid_low; // 0x18 - u32 aid_high; // 0x1C - } errtype1; - - struct { - u8 specifier; // 0x0 - u8 rev_high; // 0x1 - u16 rev_low; // 0x2 - INSERT_PADDING_BYTES(0xC); // 0x4 - u32 pid_low; // 0x10 - u32 pid_high; // 0x14 - u32 aid_low; // 0x18 - u32 aid_high; // 0x1C - u8 error_type; // 0x20 - INSERT_PADDING_BYTES(3); // 0x21 - u32 fault_status_reg; // 0x24 - u32 fault_addr; // 0x28 - u32 fpexc; // 0x2C - u32 finst; // 0x30 - u32 finst2; // 0x34 - INSERT_PADDING_BYTES(0x34); // 0x38 - u32 sp; // 0x6C - u32 pc; // 0x70 - u32 lr; // 0x74 - u32 cpsr; // 0x78 - } errtype3; - - struct { - u8 specifier; // 0x0 - u8 rev_high; // 0x1 - u16 rev_low; // 0x2 - RSL result_code; // 0x4 - INSERT_PADDING_BYTES(8); // 0x8 - u32 pid_low; // 0x10 - u32 pid_high; // 0x14 - u32 aid_low; // 0x18 - u32 aid_high; // 0x1C - char debug_string1[0x2E]; // 0x20 - char debug_string2[0x2E]; // 0x4E - } errtype4; +struct ErrInfo { + struct ErrInfoCommon { + u8 specifier; // 0x0 + u8 rev_high; // 0x1 + u16 rev_low; // 0x2 + RSL result_code; // 0x4 + u32 pc_address; // 0x8 + u32 pid; // 0xC + u32 title_id_low; // 0x10 + u32 title_id_high; // 0x14 + u32 app_title_id_low; // 0x18 + u32 app_title_id_high; // 0x1C + } errinfo_common; + static_assert(sizeof(ErrInfoCommon) == 0x20, "ErrInfoCommon struct has incorrect size"); + + union { + struct { + char data[0x60]; // 0x20 + } generic; + + struct { + ExceptionData exception_data; // 0x20 + } exception; + + struct { + char message[0x60]; // 0x20 + } result_failure; + }; }; -enum { PrefetchAbort = 0, DataAbort = 1, UndefInstr = 2, VectorFP = 3 }; +static std::string GetErrType(u8 type_code) { + switch (static_cast<FatalErrType>(type_code)) { + case FatalErrType::Generic: + return "Generic"; + case FatalErrType::Corrupted: + return "Corrupted"; + case FatalErrType::CardRemoved: + return "CardRemoved"; + case FatalErrType::Exception: + return "Exception"; + case FatalErrType::ResultFailure: + return "ResultFailure"; + case FatalErrType::Logged: + return "Logged"; + default: + return "Unknown Error Type"; + } +} -static std::string GetErrInfo3Type(u8 type_code) { - switch (type_code) { - case PrefetchAbort: +static std::string GetExceptionType(u8 type_code) { + switch (static_cast<ExceptionType>(type_code)) { + case ExceptionType::PrefetchAbort: return "Prefetch Abort"; - case DataAbort: + case ExceptionType::DataAbort: return "Data Abort"; - case UndefInstr: - return "Undefined Instruction"; - case VectorFP: - return "Vector Floating Point"; + case ExceptionType::Undefined: + return "Undefined Exception"; + case ExceptionType::VectorFP: + return "Vector Floating Point Exception"; default: - return "unknown"; + return "Unknown Exception Type"; } } +static std::string GetCurrentSystemTime() { + auto now = std::chrono::system_clock::now(); + auto time = std::chrono::system_clock::to_time_t(now); + + std::stringstream time_stream; + time_stream << std::put_time(std::localtime(&time), "%Y/%m/%d %H:%M:%S"); + return time_stream.str(); +} + +static void LogGenericInfo(const ErrInfo::ErrInfoCommon& errinfo_common) { + LOG_CRITICAL(Service_ERR, "PID: 0x%08X", errinfo_common.pid); + LOG_CRITICAL(Service_ERR, "REV: 0x%08X_0x%08X", errinfo_common.rev_high, + errinfo_common.rev_low); + LOG_CRITICAL(Service_ERR, "TID: 0x%08X_0x%08X", errinfo_common.title_id_high, + errinfo_common.title_id_low); + LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errinfo_common.app_title_id_high, + errinfo_common.app_title_id_low); + LOG_CRITICAL(Service_ERR, "ADR: 0x%08X", errinfo_common.pc_address); + + LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errinfo_common.result_code.raw); + LOG_CRITICAL(Service_ERR, " Level: %u", errinfo_common.result_code.level.Value()); + LOG_CRITICAL(Service_ERR, " Summary: %u", errinfo_common.result_code.summary.Value()); + LOG_CRITICAL(Service_ERR, " Module: %u", errinfo_common.result_code.module.Value()); + LOG_CRITICAL(Service_ERR, " Desc: %u", errinfo_common.result_code.description.Value()); +} + +/* ThrowFatalError function + * Inputs: + * 0 : Header code [0x00010800] + * 1-32 : FatalErrInfo + * Outputs: + * 0 : Header code + * 1 : Result code + */ static void ThrowFatalError(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - LOG_CRITICAL(Service_ERR, "Fatal error!"); + LOG_CRITICAL(Service_ERR, "Fatal error"); const ErrInfo* errinfo = reinterpret_cast<ErrInfo*>(&cmd_buff[1]); + LOG_CRITICAL(Service_ERR, "Fatal error type: %s", + GetErrType(errinfo->errinfo_common.specifier).c_str()); - switch (errinfo->specifier) { - case ErrSpecifier0: - case ErrSpecifier1: { - const auto& errtype = errinfo->errtype1; - LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high); - LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16)); - LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high); - LOG_CRITICAL(Service_ERR, "ADR: 0x%08X", errtype.address); - - LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errtype.result_code.raw); - LOG_CRITICAL(Service_ERR, " Level: %u", errtype.result_code.level.Value()); - LOG_CRITICAL(Service_ERR, " Summary: %u", errtype.result_code.summary.Value()); - LOG_CRITICAL(Service_ERR, " Module: %u", errtype.result_code.module.Value()); - LOG_CRITICAL(Service_ERR, " Desc: %u", errtype.result_code.description.Value()); + // Generic Info + LogGenericInfo(errinfo->errinfo_common); + + switch (static_cast<FatalErrType>(errinfo->errinfo_common.specifier)) { + case FatalErrType::Generic: + case FatalErrType::Corrupted: + case FatalErrType::CardRemoved: + case FatalErrType::Logged: { + LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str()); break; } + case FatalErrType::Exception: { + const auto& errtype = errinfo->exception; + + // Register Info + LOG_CRITICAL(Service_ERR, "ARM Registers:"); + for (u32 index = 0; index < errtype.exception_data.exception_context.arm_regs.size(); + ++index) { + if (index < 13) { + LOG_DEBUG(Service_ERR, "r%u=0x%08X", index, + errtype.exception_data.exception_context.arm_regs.at(index)); + } else if (index == 13) { + LOG_CRITICAL(Service_ERR, "SP=0x%08X", + errtype.exception_data.exception_context.arm_regs.at(index)); + } else if (index == 14) { + LOG_CRITICAL(Service_ERR, "LR=0x%08X", + errtype.exception_data.exception_context.arm_regs.at(index)); + } else if (index == 15) { + LOG_CRITICAL(Service_ERR, "PC=0x%08X", + errtype.exception_data.exception_context.arm_regs.at(index)); + } + } + LOG_CRITICAL(Service_ERR, "CPSR=0x%08X", errtype.exception_data.exception_context.cpsr); - case ErrSpecifier3: { - const auto& errtype = errinfo->errtype3; - LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high); - LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16)); - LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high); - LOG_CRITICAL(Service_ERR, "TYPE: %s", GetErrInfo3Type(errtype.error_type).c_str()); - - LOG_CRITICAL(Service_ERR, "PC: 0x%08X", errtype.pc); - LOG_CRITICAL(Service_ERR, "LR: 0x%08X", errtype.lr); - LOG_CRITICAL(Service_ERR, "SP: 0x%08X", errtype.sp); - LOG_CRITICAL(Service_ERR, "CPSR: 0x%08X", errtype.cpsr); - - switch (errtype.error_type) { - case PrefetchAbort: - case DataAbort: - LOG_CRITICAL(Service_ERR, "Fault Address: 0x%08X", errtype.fault_addr); - LOG_CRITICAL(Service_ERR, "Fault Status Register: 0x%08X", errtype.fault_status_reg); + // Exception Info + LOG_CRITICAL( + Service_ERR, "EXCEPTION TYPE: %s", + GetExceptionType(errtype.exception_data.exception_info.exception_type).c_str()); + switch (static_cast<ExceptionType>(errtype.exception_data.exception_info.exception_type)) { + case ExceptionType::PrefetchAbort: + LOG_CRITICAL(Service_ERR, "IFSR: 0x%08X", errtype.exception_data.exception_info.sr); + LOG_CRITICAL(Service_ERR, "r15: 0x%08X", errtype.exception_data.exception_info.ar); + case ExceptionType::DataAbort: + LOG_CRITICAL(Service_ERR, "DFSR: 0x%08X", errtype.exception_data.exception_info.sr); + LOG_CRITICAL(Service_ERR, "DFAR: 0x%08X", errtype.exception_data.exception_info.ar); break; - case VectorFP: - LOG_CRITICAL(Service_ERR, "FPEXC: 0x%08X", errtype.fpexc); - LOG_CRITICAL(Service_ERR, "FINST: 0x%08X", errtype.finst); - LOG_CRITICAL(Service_ERR, "FINST2: 0x%08X", errtype.finst2); + case ExceptionType::VectorFP: + LOG_CRITICAL(Service_ERR, "FPEXC: 0x%08X", + errtype.exception_data.exception_info.fpinst); + LOG_CRITICAL(Service_ERR, "FINST: 0x%08X", + errtype.exception_data.exception_info.fpinst); + LOG_CRITICAL(Service_ERR, "FINST2: 0x%08X", + errtype.exception_data.exception_info.fpinst2); break; } + LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str()); break; } - case ErrSpecifier4: { - const auto& errtype = errinfo->errtype4; - LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high); - LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16)); - LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high); + case FatalErrType::ResultFailure: { + const auto& errtype = errinfo->result_failure; - LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errtype.result_code.raw); - LOG_CRITICAL(Service_ERR, " Level: %u", errtype.result_code.level.Value()); - LOG_CRITICAL(Service_ERR, " Summary: %u", errtype.result_code.summary.Value()); - LOG_CRITICAL(Service_ERR, " Module: %u", errtype.result_code.module.Value()); - LOG_CRITICAL(Service_ERR, " Desc: %u", errtype.result_code.description.Value()); - - LOG_CRITICAL(Service_ERR, "%s", errtype.debug_string1); - LOG_CRITICAL(Service_ERR, "%s", errtype.debug_string2); + // Failure Message + LOG_CRITICAL(Service_ERR, "Failure Message: %s", errtype.message); + LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str()); break; } - } - cmd_buff[1] = 0; // No error + } // switch FatalErrType + + cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error } const Interface::FunctionInfo FunctionTable[] = { + // clang-format off {0x00010800, ThrowFatalError, "ThrowFatalError"}, + {0x00020042, nullptr, "SetUserString"}, + // clang-format on }; //////////////////////////////////////////////////////////////////////////////////////////////////// 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 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/shared_memory.h" #include "core/hle/service/mic_u.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -9,23 +12,298 @@ namespace MIC_U { +enum class Encoding : u8 { + PCM8 = 0, + PCM16 = 1, + PCM8Signed = 2, + PCM16Signed = 3, +}; + +enum class SampleRate : u8 { + SampleRate32730 = 0, + SampleRate16360 = 1, + SampleRate10910 = 2, + SampleRate8180 = 3 +}; + +static Kernel::SharedPtr<Kernel::Event> buffer_full_event; +static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; +static u8 mic_gain = 0; +static bool mic_power = false; +static bool is_sampling = false; +static bool allow_shell_closed; +static bool clamp = false; +static Encoding encoding; +static SampleRate sample_rate; +static s32 audio_buffer_offset; +static u32 audio_buffer_size; +static bool audio_buffer_loop; + +/** + * MIC::MapSharedMem service function + * Inputs: + * 0 : Header Code[0x00010042] + * 1 : Shared-mem size + * 2 : CopyHandleDesc + * 3 : Shared-mem handle + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void MapSharedMem(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 size = cmd_buff[1]; + Handle mem_handle = cmd_buff[3]; + shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(mem_handle); + if (shared_memory) { + shared_memory->name = "MIC_U:shared_memory"; + } + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_MIC, "called, size=0x%X, mem_handle=0x%08X", size, mem_handle); +} + +/** + * MIC::UnmapSharedMem service function + * Inputs: + * 0 : Header Code[0x00020000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void UnmapSharedMem(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_MIC, "called"); +} + +/** + * MIC::StartSampling service function + * Inputs: + * 0 : Header Code[0x00030140] + * 1 : Encoding + * 2 : SampleRate + * 3 : Base offset for audio data in sharedmem + * 4 : Size of the audio data in sharedmem + * 5 : Loop at end of buffer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void StartSampling(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + encoding = static_cast<Encoding>(cmd_buff[1] & 0xFF); + sample_rate = static_cast<SampleRate>(cmd_buff[2] & 0xFF); + audio_buffer_offset = cmd_buff[3]; + audio_buffer_size = cmd_buff[4]; + audio_buffer_loop = static_cast<bool>(cmd_buff[5] & 0xFF); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + is_sampling = true; + LOG_WARNING(Service_MIC, "(STUBBED) called, encoding=%u, sample_rate=%u, " + "audio_buffer_offset=%d, audio_buffer_size=%u, audio_buffer_loop=%u", + encoding, sample_rate, audio_buffer_offset, audio_buffer_size, audio_buffer_loop); +} + +/** + * MIC::AdjustSampling service function + * Inputs: + * 0 : Header Code[0x00040040] + * 1 : SampleRate + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void AdjustSampling(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + sample_rate = static_cast<SampleRate>(cmd_buff[1] & 0xFF); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_MIC, "(STUBBED) called, sample_rate=%u", sample_rate); +} + +/** + * MIC::StopSampling service function + * Inputs: + * 0 : Header Code[0x00050000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void StopSampling(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + is_sampling = false; + LOG_WARNING(Service_MIC, "(STUBBED) called"); +} + +/** + * MIC::IsSampling service function + * Inputs: + * 0 : Header Code[0x00060000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : 0 = sampling, non-zero = sampling + */ +static void IsSampling(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = is_sampling; + LOG_WARNING(Service_MIC, "(STUBBED) called"); +} + +/** + * MIC::GetBufferFullEvent service function + * Inputs: + * 0 : Header Code[0x00070000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 3 : Event handle + */ +static void GetBufferFullEvent(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[3] = Kernel::g_handle_table.Create(buffer_full_event).MoveFrom(); + LOG_WARNING(Service_MIC, "(STUBBED) called"); +} + +/** + * MIC::SetGain service function + * Inputs: + * 0 : Header Code[0x00080040] + * 1 : Gain + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetGain(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + mic_gain = cmd_buff[1] & 0xFF; + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_MIC, "(STUBBED) called, mic_gain=%u", mic_gain); +} + +/** + * MIC::GetGain service function + * Inputs: + * 0 : Header Code[0x00090000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Gain + */ +static void GetGain(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = mic_gain; + LOG_WARNING(Service_MIC, "(STUBBED) called"); +} + +/** + * MIC::SetPower service function + * Inputs: + * 0 : Header Code[0x000A0040] + * 1 : Power (0 = off, 1 = on) + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetPower(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + mic_power = static_cast<bool>(cmd_buff[1] & 0xFF); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_MIC, "(STUBBED) called, mic_power=%u", mic_power); +} + +/** + * MIC::GetPower service function + * Inputs: + * 0 : Header Code[0x000B0000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Power + */ +static void GetPower(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = mic_power; + LOG_WARNING(Service_MIC, "(STUBBED) called"); +} + +/** + * MIC::SetIirFilterMic service function + * Inputs: + * 0 : Header Code[0x000C0042] + * 1 : Size + * 2 : (Size << 4) | 0xA + * 3 : Pointer to IIR Filter Data + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetIirFilterMic(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 size = cmd_buff[1]; + VAddr buffer = cmd_buff[3]; + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_MIC, "(STUBBED) called, size=0x%X, buffer=0x%08X", size, buffer); +} + +/** + * MIC::SetClamp service function + * Inputs: + * 0 : Header Code[0x000D0040] + * 1 : Clamp (0 = don't clamp, non-zero = clamp) + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetClamp(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + clamp = static_cast<bool>(cmd_buff[1] & 0xFF); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_MIC, "(STUBBED) called, clamp=%u", clamp); +} + +/** + * MIC::GetClamp service function + * Inputs: + * 0 : Header Code[0x000E0000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Clamp (0 = don't clamp, non-zero = clamp) + */ +static void GetClamp(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = clamp; + LOG_WARNING(Service_MIC, "(STUBBED) called"); +} + +/** + * MIC::SetAllowShellClosed service function + * Inputs: + * 0 : Header Code[0x000D0040] + * 1 : Sampling allowed while shell closed (0 = disallow, non-zero = allow) + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetAllowShellClosed(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + allow_shell_closed = static_cast<bool>(cmd_buff[1] & 0xFF); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_MIC, "(STUBBED) called, allow_shell_closed=%u", allow_shell_closed); +} + const Interface::FunctionInfo FunctionTable[] = { - {0x00010042, nullptr, "MapSharedMem"}, - {0x00020000, nullptr, "UnmapSharedMem"}, - {0x00030140, nullptr, "Initialize"}, - {0x00040040, nullptr, "AdjustSampling"}, - {0x00050000, nullptr, "StopSampling"}, - {0x00060000, nullptr, "IsSampling"}, - {0x00070000, nullptr, "GetEventHandle"}, - {0x00080040, nullptr, "SetGain"}, - {0x00090000, nullptr, "GetGain"}, - {0x000A0040, nullptr, "SetPower"}, - {0x000B0000, nullptr, "GetPower"}, - {0x000C0042, nullptr, "size"}, - {0x000D0040, nullptr, "SetClamp"}, - {0x000E0000, nullptr, "GetClamp"}, - {0x000F0040, nullptr, "SetAllowShellClosed"}, - {0x00100040, nullptr, "unknown_input2"}, + {0x00010042, MapSharedMem, "MapSharedMem"}, + {0x00020000, UnmapSharedMem, "UnmapSharedMem"}, + {0x00030140, StartSampling, "StartSampling"}, + {0x00040040, AdjustSampling, "AdjustSampling"}, + {0x00050000, StopSampling, "StopSampling"}, + {0x00060000, IsSampling, "IsSampling"}, + {0x00070000, GetBufferFullEvent, "GetBufferFullEvent"}, + {0x00080040, SetGain, "SetGain"}, + {0x00090000, GetGain, "GetGain"}, + {0x000A0040, SetPower, "SetPower"}, + {0x000B0000, GetPower, "GetPower"}, + {0x000C0042, SetIirFilterMic, "SetIirFilterMic"}, + {0x000D0040, SetClamp, "SetClamp"}, + {0x000E0000, GetClamp, "GetClamp"}, + {0x000F0040, SetAllowShellClosed, "SetAllowShellClosed"}, + {0x00100040, nullptr, "SetClientSDKVersion"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -33,6 +311,18 @@ const Interface::FunctionInfo FunctionTable[] = { Interface::Interface() { Register(FunctionTable); + shared_memory = nullptr; + buffer_full_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "MIC_U::buffer_full_event"); + mic_gain = 0; + mic_power = false; + is_sampling = false; + clamp = false; +} + +Interface::~Interface() { + shared_memory = nullptr; + buffer_full_event = nullptr; } } // 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 { class Interface : public Service::Interface { public: Interface(); + ~Interface(); std::string GetPortName() const override { 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 { * requires an indexed fetch and a check for NULL. */ struct PageTable { - static const size_t NUM_ENTRIES = 1 << (32 - PAGE_BITS); - /** * Array of memory pointers backing each page. An entry can only be non-null if the * corresponding entry in the `attributes` array is of type `Memory`. */ - std::array<u8*, NUM_ENTRIES> pointers; + std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers; /** * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of @@ -63,13 +61,13 @@ struct PageTable { * Array of fine grained page attributes. If it is set to any value other than `Memory`, then * the corresponding entry in `pointers` MUST be set to null. */ - std::array<PageType, NUM_ENTRIES> attributes; + std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes; /** * Indicates the number of externally cached resources touching a page that should be * flushed before the memory is accessed */ - std::array<u8, NUM_ENTRIES> cached_res_count; + std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count; }; /// Singular page table used for the singleton process @@ -77,6 +75,10 @@ static PageTable main_page_table; /// Currently active page table static PageTable* current_page_table = &main_page_table; +std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() { + return ¤t_page_table->pointers; +} + static void MapPages(u32 base, u32 size, u8* memory, PageType type) { LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, (base + size) * PAGE_SIZE); @@ -84,7 +86,7 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { u32 end = base + size; while (base != end) { - ASSERT_MSG(base < PageTable::NUM_ENTRIES, "out of range mapping at %08X", base); + ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base); // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be // 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 @@ #pragma once +#include <array> #include <cstddef> #include <string> #include "common/common_types.h" @@ -17,6 +18,7 @@ namespace Memory { const u32 PAGE_SIZE = 0x1000; const u32 PAGE_MASK = PAGE_SIZE - 1; const int PAGE_BITS = 12; +const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS); /// Physical memory regions as seen from the ARM11 enum : PAddr { @@ -166,4 +168,11 @@ void RasterizerFlushRegion(PAddr start, u32 size); * Flushes and invalidates any externally cached rasterizer resources touching the given region. */ void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size); + +/** + * Dynarmic has an optimization to memory accesses when the pointer to the page exists that + * can be used by setting up the current page table as a callback. This function is used to + * retrieve the current page table for that purpose. + */ +std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers(); } 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 @@ #include "settings.h" #include "video_core/video_core.h" +#include "common/emu_window.h" + namespace Settings { Values values = {}; @@ -20,6 +22,11 @@ void Apply() { VideoCore::g_shader_jit_enabled = values.use_shader_jit; VideoCore::g_scaled_resolution_enabled = values.use_scaled_resolution; + if (VideoCore::g_emu_window) { + auto layout = VideoCore::g_emu_window->GetFramebufferLayout(); + VideoCore::g_emu_window->UpdateCurrentFramebufferLayout(layout.width, layout.height); + } + AudioCore::SelectSink(values.sink_id); AudioCore::EnableStretching(values.enable_audio_stretching); } 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 @@ namespace Settings { +enum class LayoutOption { + Default, + SingleScreen, + LargeScreen, + Custom, +}; + namespace NativeInput { + enum Values { // directly mapped keys A, @@ -84,6 +92,9 @@ struct Values { bool use_scaled_resolution; bool use_vsync; + LayoutOption layout_option; + bool swap_screen; + float bg_red; float bg_green; 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) { PrimitiveAssembler<Shader::OutputVertex>& primitive_assembler = g_state.primitive_assembler; - if (g_debug_context) { + if (g_debug_context && g_debug_context->recorder) { for (int i = 0; i < 3; ++i) { const auto texture = regs.GetTextures()[i]; if (!texture.enabled) continue; u8* texture_data = Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress()); - if (g_debug_context && Pica::g_debug_context->recorder) - g_debug_context->recorder->MemoryAccessed( - texture_data, Pica::Regs::NibblesPerPixel(texture.format) * - texture.config.width / 2 * texture.config.height, - texture.config.GetPhysicalAddress()); + g_debug_context->recorder->MemoryAccessed( + texture_data, Pica::Regs::NibblesPerPixel(texture.format) * + texture.config.width / 2 * texture.config.height, + texture.config.GetPhysicalAddress()); } } @@ -236,7 +235,8 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { // The size has been tuned for optimal balance between hit-rate and the cost of lookup const size_t VERTEX_CACHE_SIZE = 32; std::array<u16, VERTEX_CACHE_SIZE> vertex_cache_ids; - std::array<Shader::OutputRegisters, VERTEX_CACHE_SIZE> vertex_cache; + std::array<Shader::OutputVertex, VERTEX_CACHE_SIZE> vertex_cache; + Shader::OutputVertex output_vertex; unsigned int vertex_cache_pos = 0; vertex_cache_ids.fill(-1); @@ -266,7 +266,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { for (unsigned int i = 0; i < VERTEX_CACHE_SIZE; ++i) { if (vertex == vertex_cache_ids[i]) { - output_registers = vertex_cache[i]; + output_vertex = vertex_cache[i]; vertex_cache_hit = true; break; } @@ -285,16 +285,16 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { g_state.vs.Run(shader_unit, input, loader.GetNumTotalAttributes()); output_registers = shader_unit.output_registers; + // Retrieve vertex from register data + output_vertex = output_registers.ToVertex(regs.vs); + if (is_indexed) { - vertex_cache[vertex_cache_pos] = output_registers; + vertex_cache[vertex_cache_pos] = output_vertex; vertex_cache_ids[vertex_cache_pos] = vertex; vertex_cache_pos = (vertex_cache_pos + 1) % VERTEX_CACHE_SIZE; } } - // Retrieve vertex from register data - Shader::OutputVertex output_vertex = output_registers.ToVertex(regs.vs); - // Send to renderer using Pica::Shader::OutputVertex; 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 { // field offset. Otherwise, the compiler will fail to compile this code. #define PICA_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) \ ((typename std::enable_if<backup_workaround_index == PICA_REG_INDEX(field_name), \ - size_t>::type)PICA_REG_INDEX(field_name)) + size_t>::type) PICA_REG_INDEX(field_name)) #endif // _MSC_VER 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) value_float = config.value_32bit / 16777215.0f; // 2^24 - 1 } - cur_state.depth.write_mask = true; + cur_state.depth.write_mask = GL_TRUE; cur_state.Apply(); glClearBufferfv(GL_DEPTH, 0, &value_float); } else if (dst_type == SurfaceType::DepthStencil) { @@ -908,8 +908,8 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) GLfloat value_float = (config.value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1 GLint value_int = (config.value_32bit >> 24); - cur_state.depth.write_mask = true; - cur_state.stencil.write_mask = true; + cur_state.depth.write_mask = GL_TRUE; + cur_state.stencil.write_mask = 0xFF; cur_state.Apply(); glClearBufferfi(GL_DEPTH_STENCIL, 0, value_float, value_int); } 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() { stencil.test_enabled = false; stencil.test_func = GL_ALWAYS; stencil.test_ref = 0; - stencil.test_mask = -1; - stencil.write_mask = -1; + stencil.test_mask = 0xFF; + stencil.write_mask = 0xFF; stencil.action_depth_fail = GL_KEEP; stencil.action_depth_pass = GL_KEEP; 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 */ void RendererOpenGL::DrawScreens() { auto layout = render_window->GetFramebufferLayout(); + const auto& top_screen = layout.top_screen; + const auto& bottom_screen = layout.bottom_screen; glViewport(0, 0, layout.width, layout.height); glClear(GL_COLOR_BUFFER_BIT); @@ -403,12 +405,15 @@ void RendererOpenGL::DrawScreens() { glActiveTexture(GL_TEXTURE0); glUniform1i(uniform_color_texture, 0); - DrawSingleScreenRotated(screen_infos[0], (float)layout.top_screen.left, - (float)layout.top_screen.top, (float)layout.top_screen.GetWidth(), - (float)layout.top_screen.GetHeight()); - DrawSingleScreenRotated(screen_infos[1], (float)layout.bottom_screen.left, - (float)layout.bottom_screen.top, (float)layout.bottom_screen.GetWidth(), - (float)layout.bottom_screen.GetHeight()); + if (layout.top_screen_enabled) { + DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left, (float)top_screen.top, + (float)top_screen.GetWidth(), (float)top_screen.GetHeight()); + } + if (layout.bottom_screen_enabled) { + DrawSingleScreenRotated(screen_infos[1], (float)bottom_screen.left, + (float)bottom_screen.top, (float)bottom_screen.GetWidth(), + (float)bottom_screen.GetHeight()); + } m_current_frame++; } |