summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-x.travis-deps.sh6
-rwxr-xr-x.travis-upload.sh2
-rw-r--r--.travis.yml1
-rw-r--r--CMakeLists.txt13
-rw-r--r--CONTRIBUTING.md14
-rw-r--r--README.md4
-rw-r--r--dist/citra.xml24
-rw-r--r--externals/microprofile/microprofile.h4
-rw-r--r--src/audio_core/sdl2_sink.cpp4
-rw-r--r--src/citra/config.cpp5
-rw-r--r--src/citra/default_ini.h10
-rw-r--r--src/citra/emu_window/emu_window_sdl2.cpp5
-rw-r--r--src/citra_qt/bootmanager.cpp4
-rw-r--r--src/citra_qt/config.cpp11
-rw-r--r--src/citra_qt/configure_dialog.cpp1
-rw-r--r--src/citra_qt/configure_graphics.cpp5
-rw-r--r--src/citra_qt/configure_graphics.ui115
-rw-r--r--src/citra_qt/main.cpp14
-rw-r--r--src/citra_qt/main.h1
-rw-r--r--src/citra_qt/main.ui2
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/common_paths.h2
-rw-r--r--src/common/emu_window.cpp68
-rw-r--r--src/common/emu_window.h30
-rw-r--r--src/common/file_util.cpp16
-rw-r--r--src/common/file_util.h1
-rw-r--r--src/common/framebuffer_layout.cpp138
-rw-r--r--src/common/framebuffer_layout.h47
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/common/math_util.h10
-rw-r--r--src/common/string_util.cpp5
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp5
-rw-r--r--src/core/gdbstub/gdbstub.cpp2
-rw-r--r--src/core/hle/applets/erreula.cpp6
-rw-r--r--src/core/hle/applets/mii_selector.cpp6
-rw-r--r--src/core/hle/applets/swkbd.cpp6
-rw-r--r--src/core/hle/kernel/event.cpp5
-rw-r--r--src/core/hle/kernel/timer.cpp5
-rw-r--r--src/core/hle/service/ac_u.cpp207
-rw-r--r--src/core/hle/service/ac_u.h1
-rw-r--r--src/core/hle/service/apt/apt.cpp11
-rw-r--r--src/core/hle/service/apt/apt.h34
-rw-r--r--src/core/hle/service/apt/apt_a.cpp2
-rw-r--r--src/core/hle/service/apt/apt_u.cpp2
-rw-r--r--src/core/hle/service/cfg/cfg.cpp10
-rw-r--r--src/core/hle/service/err_f.cpp316
-rw-r--r--src/core/hle/service/mic_u.cpp322
-rw-r--r--src/core/hle/service/mic_u.h1
-rw-r--r--src/core/memory.cpp14
-rw-r--r--src/core/memory.h9
-rw-r--r--src/core/settings.cpp7
-rw-r--r--src/core/settings.h11
-rw-r--r--src/video_core/command_processor.cpp24
-rw-r--r--src/video_core/pica.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp4
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp17
58 files changed, 1242 insertions, 359 deletions
diff --git a/.travis-deps.sh b/.travis-deps.sh
index 8538098c1..9fd21cc57 100755
--- a/.travis-deps.sh
+++ b/.travis-deps.sh
@@ -19,8 +19,8 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
if [ ! -e $HOME/.local/lib/libSDL2.la ]; then
echo "SDL2 not found in cache, get and build it..."
- wget http://libsdl.org/release/SDL2-2.0.4.tar.gz -O - | tar xz
- cd SDL2-2.0.4
+ wget http://libsdl.org/release/SDL2-2.0.5.tar.gz -O - | tar xz
+ cd SDL2-2.0.5
./configure --prefix=$HOME/.local
make -j4 && make install
else
@@ -29,5 +29,5 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
brew update
- brew install cmake qt5 sdl2 dylibbundler
+ brew install qt5 sdl2 dylibbundler
fi
diff --git a/.travis-upload.sh b/.travis-upload.sh
index 1bec74b3d..2eeda4c50 100755
--- a/.travis-upload.sh
+++ b/.travis-upload.sh
@@ -14,7 +14,6 @@ if [ "$TRAVIS_EVENT_TYPE" = "push" ]&&[ "$TRAVIS_BRANCH" = "master" ]; then
UPLOAD_DIR="/citra/nightly/osx-amd64"
mkdir "$REV_NAME"
- brew install lftp
cp build/src/citra/Release/citra "$REV_NAME"
cp -r build/src/citra_qt/Release/citra-qt.app "$REV_NAME"
@@ -122,5 +121,4 @@ EOL
ARCHIVE_NAME="${REV_NAME}.tar.xz"
tar -cJvf "$ARCHIVE_NAME" "$REV_NAME"
- lftp -c "open -u citra-builds,$BUILD_PASSWORD sftp://builds.citra-emu.org; set sftp:auto-confirm yes; put -O '$UPLOAD_DIR' '$ARCHIVE_NAME'"
fi
diff --git a/.travis.yml b/.travis.yml
index ea99be2c4..a9e7aadd2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,7 +25,6 @@ addons:
- libqt5opengl5-dev
- xorg-dev
- lib32stdc++6 # For CMake
- - lftp # To upload builds
- clang-format-4.0
cache:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 26dec8f86..5c9b7f86a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -68,6 +68,15 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-attributes")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
+
+ if (MINGW)
+ add_definitions(-DMINGW_HAS_SECURE_API)
+ if (MINGW_STATIC_BUILD)
+ add_definitions(-DQT_STATICPLUGIN)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
+ endif()
+ endif()
else()
# Silence "deprecation" warnings
add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_DEPRECATE /D_SCL_SECURE_NO_WARNINGS)
@@ -147,7 +156,7 @@ if (ENABLE_SDL2)
if (CITRA_USE_BUNDLED_SDL2)
# Detect toolchain and platform
if (MSVC14 AND ARCHITECTURE_x86_64)
- set(SDL2_VER "SDL2-2.0.4")
+ set(SDL2_VER "SDL2-2.0.5")
else()
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable CITRA_USE_BUNDLED_SDL2 and provide your own.")
endif()
@@ -175,7 +184,7 @@ IF (APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
ELSEIF(MINGW)
# PSAPI is the Process Status API
- set(PLATFORM_LIBRARIES winmm ws2_32 psapi)
+ set(PLATFORM_LIBRARIES winmm ws2_32 psapi imm32 version)
# WSAPoll functionality doesn't exist before WinNT 6.x (Vista and up)
add_definitions(-D_WIN32_WINNT=0x0600)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3d6a87651..7a21eebf8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -52,8 +52,8 @@ namespace Example {
// Namespace contents are not indented
// Declare globals at the top
-int g_foo = 0;
-char* g_some_pointer; // Pointer * and reference & stick to the type name
+int g_foo{}; // {} can be used to initialize types as 0, false, or nullptr
+char* g_some_pointer{}; // Pointer * and reference & stick to the type name, and make sure to initialize as nullptr!
/// A colorful enum.
enum SomeEnum {
@@ -67,13 +67,15 @@ enum SomeEnum {
* Note that the asterisks are indented by one space to align to the first line.
*/
struct Position {
- int x, y;
+ int x{}, y{}; // Always intitialize member variables!
};
// Use "typename" rather than "class" here
template <typename T>
void FooBar() {
- int some_array[] = {
+ const std::string some_string{ "prefer uniform initialization" };
+
+ int some_array[]{
5,
25,
7,
@@ -87,7 +89,7 @@ void FooBar() {
}
// Place a single space after the for loop semicolons, prefer pre-increment
- for (int i = 0; i != 25; ++i) {
+ for (int i{}; i != 25; ++i) {
// This is how we write loops
}
@@ -105,7 +107,7 @@ void FooBar() {
switch (var) {
// No indentation for case label
case 1: {
- int case_var = var + 3;
+ int case_var{ var + 3 };
DoSomething(case_var);
break;
}
diff --git a/README.md b/README.md
index 92e2d04a5..7d1e1de0d 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ Citra Emulator
[![Travis CI Build Status](https://travis-ci.org/citra-emu/citra.svg?branch=master)](https://travis-ci.org/citra-emu/citra)
[![AppVeyor CI Build Status](https://ci.appveyor.com/api/projects/status/sdf1o4kh3g1e68m9?svg=true)](https://ci.appveyor.com/project/bunnei/citra)
-Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and OS X. Citra only emulates a subset of 3DS hardware, and therefore is generally only useful for running/debugging homebrew applications. At this time, Citra is even able to boot several commercial games! Most of these do not run to a playable state, but we are working every day to advance the project forward.
+Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and macOS. Citra only emulates a subset of 3DS hardware, and therefore is generally only useful for running/debugging homebrew applications. At this time, Citra is even able to boot several commercial games! Most of these do not run to a playable state, but we are working every day to advance the project forward.
Citra is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. Please read the [FAQ](https://github.com/citra-emu/citra/wiki/FAQ) before getting started with the project.
@@ -23,7 +23,7 @@ If you want to contribute please take a look at the [Contributor's Guide](CONTRI
* __Windows__: [Windows Build](https://github.com/citra-emu/citra/wiki/Building-For-Windows)
* __Linux__: [Linux Build](https://github.com/citra-emu/citra/wiki/Building-For-Linux)
-* __OSX__: [OS X Build](https://github.com/citra-emu/citra/wiki/Building-For-OS-X)
+* __macOS__: [macOS Build](https://github.com/citra-emu/citra/wiki/Building-for-macOS)
### Support
diff --git a/dist/citra.xml b/dist/citra.xml
index bcb9acd87..6d47c8760 100644
--- a/dist/citra.xml
+++ b/dist/citra.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-ctr-3dsx">
- <comment>3DS homebrew executable</comment>
- <comment xml:lang="fr">Exécutable 3DS homebrew</comment>
+ <comment>Nintendo 3DS homebrew executable</comment>
+ <comment xml:lang="fr">Exécutable non-officiel pour Nintendo 3DS </comment>
<acronym>3DSX</acronym>
<icon name="citra"/>
<glob pattern="*.3dsx"/>
@@ -10,8 +10,8 @@
</mime-type>
<mime-type type="application/x-ctr-cci">
- <comment>3DS cartridge image</comment>
- <comment xml:lang="fr">Image de cartouche 3DS</comment>
+ <comment>Nintendo 3DS cartridge image</comment>
+ <comment xml:lang="fr">Image de cartouche Nintendo 3DS</comment>
<acronym>CCI</acronym>
<expanded-acronym>CTR Cart Image</expanded-acronym>
<icon name="citra"/>
@@ -21,8 +21,8 @@
</mime-type>
<mime-type type="application/x-ctr-cxi">
- <comment>3DS executable</comment>
- <comment xml:lang="fr">Exécutable 3DS</comment>
+ <comment>Nintendo 3DS executable</comment>
+ <comment xml:lang="fr">Exécutable Nintendo 3DS</comment>
<acronym>CXI</acronym>
<expanded-acronym>CTR eXecutable Image</expanded-acronym>
<icon name="citra"/>
@@ -31,8 +31,8 @@
</mime-type>
<mime-type type="application/x-ctr-cia">
- <comment>3DS importable archive</comment>
- <comment xml:lang="fr">Archive importable 3DS</comment>
+ <comment>Nintendo 3DS importable archive</comment>
+ <comment xml:lang="fr">Archive installable Nintendo 3DS</comment>
<acronym>CIA</acronym>
<expanded-acronym>CTR Importable Archive</expanded-acronym>
<icon name="citra"/>
@@ -40,8 +40,8 @@
</mime-type>
<mime-type type="application/x-ctr-smdh">
- <comment>3DS icon</comment>
- <comment xml:lang="fr">Icône 3DS</comment>
+ <comment>Nintendo 3DS icon and metadata</comment>
+ <comment xml:lang="fr">Icône et métadonnées Nintendo 3DS</comment>
<acronym>SMDH</acronym>
<expanded-acronym>System Menu Data Header</expanded-acronym>
<glob pattern="*.smdh"/>
@@ -49,8 +49,8 @@
</mime-type>
<mime-type type="application/x-ctr-cbmd">
- <comment>3DS banner</comment>
- <comment xml:lang="fr">Bannière 3DS</comment>
+ <comment>Nintendo 3DS banner</comment>
+ <comment xml:lang="fr">Bannière Nintendo 3DS</comment>
<acronym>CBMD</acronym>
<expanded-acronym>CTR Banner Model Data</expanded-acronym>
<glob pattern="*.cbmd"/>
diff --git a/externals/microprofile/microprofile.h b/externals/microprofile/microprofile.h
index 30613b3b0..f45c9ba82 100644
--- a/externals/microprofile/microprofile.h
+++ b/externals/microprofile/microprofile.h
@@ -512,7 +512,7 @@ typedef int MpSocket;
#ifndef _WIN32
typedef pthread_t MicroProfileThread;
-#elif defined(_WIN32)
+#elif defined(_MSC_VER)
typedef HANDLE MicroProfileThread;
#else
typedef std::thread* MicroProfileThread;
@@ -921,7 +921,7 @@ void MicroProfileThreadJoin(MicroProfileThread* pThread)
int r = pthread_join(*pThread, 0);
MP_ASSERT(r == 0);
}
-#elif defined(_WIN32)
+#elif defined(_MSC_VER)
typedef HANDLE MicroProfileThread;
DWORD _stdcall ThreadTrampoline(void* pFunc)
{
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 &current_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++;
}