summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt44
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/alignment.h12
-rw-r--r--src/common/file_util.cpp13
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/common/memory_util.cpp177
-rw-r--r--src/common/memory_util.h21
-rw-r--r--src/core/CMakeLists.txt24
-rw-r--r--src/core/core.cpp3
-rw-r--r--src/core/crypto/partition_data_manager.cpp3
-rw-r--r--src/core/file_sys/card_image.cpp10
-rw-r--r--src/core/file_sys/card_image.h2
-rw-r--r--src/core/file_sys/content_archive.cpp531
-rw-r--r--src/core/file_sys/content_archive.h19
-rw-r--r--src/core/file_sys/savedata_factory.cpp13
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp25
-rw-r--r--src/core/hle/kernel/hle_ipc.h13
-rw-r--r--src/core/hle/kernel/kernel.cpp10
-rw-r--r--src/core/hle/kernel/kernel.h6
-rw-r--r--src/core/hle/kernel/process.h14
-rw-r--r--src/core/hle/kernel/server_session.cpp3
-rw-r--r--src/core/hle/kernel/svc.cpp189
-rw-r--r--src/core/hle/kernel/svc.h4
-rw-r--r--src/core/hle/kernel/thread.cpp2
-rw-r--r--src/core/hle/kernel/vm_manager.cpp55
-rw-r--r--src/core/hle/kernel/vm_manager.h15
-rw-r--r--src/core/hle/service/am/am.cpp92
-rw-r--r--src/core/hle/service/am/am.h4
-rw-r--r--src/core/hle/service/am/applet_ae.cpp4
-rw-r--r--src/core/hle/service/am/applet_oe.cpp3
-rw-r--r--src/core/hle/service/am/idle.cpp6
-rw-r--r--src/core/hle/service/am/omm.cpp34
-rw-r--r--src/core/hle/service/am/tcap.cpp23
-rw-r--r--src/core/hle/service/am/tcap.h17
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp15
-rw-r--r--src/core/hle/service/aoc/aoc_u.h2
-rw-r--r--src/core/hle/service/audio/audren_u.cpp37
-rw-r--r--src/core/hle/service/es/es.cpp18
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp30
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h45
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp42
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h56
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h63
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h50
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h50
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp619
-rw-r--r--src/core/hle/service/hid/controllers/npad.h289
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp39
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h34
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp65
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h63
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp45
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h60
-rw-r--r--src/core/hle/service/hid/hid.cpp518
-rw-r--r--src/core/hle/service/hid/hid.h402
-rw-r--r--src/core/hle/service/lbl/lbl.cpp56
-rw-r--r--src/core/hle/service/mm/mm_u.cpp50
-rw-r--r--src/core/hle/service/nfp/nfp.cpp2
-rw-r--r--src/core/hle/service/nifm/nifm.cpp1
-rw-r--r--src/core/hle/service/nim/nim.cpp17
-rw-r--r--src/core/hle/service/npns/npns.cpp88
-rw-r--r--src/core/hle/service/npns/npns.h15
-rw-r--r--src/core/hle/service/ns/ns.cpp55
-rw-r--r--src/core/hle/service/prepo/prepo.cpp21
-rw-r--r--src/core/hle/service/ptm/psm.cpp71
-rw-r--r--src/core/hle/service/ptm/psm.h15
-rw-r--r--src/core/hle/service/service.cpp8
-rw-r--r--src/core/hle/service/set/set_cal.cpp3
-rw-r--r--src/core/loader/xci.cpp3
-rw-r--r--src/core/settings.h2
-rw-r--r--src/core/telemetry_session.cpp4
-rw-r--r--src/video_core/engines/fermi_2d.cpp9
-rw-r--r--src/video_core/engines/kepler_memory.cpp11
-rw-r--r--src/video_core/engines/kepler_memory.h7
-rw-r--r--src/video_core/engines/maxwell_3d.cpp6
-rw-r--r--src/video_core/engines/maxwell_3d.h9
-rw-r--r--src/video_core/engines/maxwell_compute.cpp6
-rw-r--r--src/video_core/engines/maxwell_dma.cpp80
-rw-r--r--src/video_core/engines/maxwell_dma.h8
-rw-r--r--src/video_core/engines/shader_bytecode.h179
-rw-r--r--src/video_core/gpu.cpp4
-rw-r--r--src/video_core/memory_manager.cpp10
-rw-r--r--src/video_core/memory_manager.h1
-rw-r--r--src/video_core/rasterizer_cache.h127
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h9
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp59
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h6
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp328
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h84
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h11
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp696
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp36
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp11
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h8
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h16
-rw-r--r--src/video_core/textures/decoders.cpp40
-rw-r--r--src/video_core/textures/decoders.h9
-rw-r--r--src/web_service/CMakeLists.txt4
-rw-r--r--src/yuzu/CMakeLists.txt10
-rw-r--r--src/yuzu/configuration/config.cpp6
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp4
-rw-r--r--src/yuzu/configuration/configure_graphics.ui4
-rw-r--r--src/yuzu/debugger/wait_tree.cpp4
-rw-r--r--src/yuzu/main.ui8
-rw-r--r--src/yuzu_cmd/config.cpp4
-rw-r--r--src/yuzu_cmd/default_ini.h4
110 files changed, 4482 insertions, 1814 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6021a8054..918cf5372 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -23,21 +23,21 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
23 23
24option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) 24option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
25 25
26if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit) 26if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
27 message(STATUS "Copying pre-commit hook") 27 message(STATUS "Copying pre-commit hook")
28 file(COPY hooks/pre-commit 28 file(COPY hooks/pre-commit
29 DESTINATION ${CMAKE_SOURCE_DIR}/.git/hooks) 29 DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks)
30endif() 30endif()
31 31
32# Sanity check : Check that all submodules are present 32# Sanity check : Check that all submodules are present
33# ======================================================================= 33# =======================================================================
34 34
35function(check_submodules_present) 35function(check_submodules_present)
36 file(READ "${CMAKE_SOURCE_DIR}/.gitmodules" gitmodules) 36 file(READ "${PROJECT_SOURCE_DIR}/.gitmodules" gitmodules)
37 string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules}) 37 string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules})
38 foreach(module ${gitmodules}) 38 foreach(module ${gitmodules})
39 string(REGEX REPLACE "path *= *" "" module ${module}) 39 string(REGEX REPLACE "path *= *" "" module ${module})
40 if (NOT EXISTS "${CMAKE_SOURCE_DIR}/${module}/.git") 40 if (NOT EXISTS "${PROJECT_SOURCE_DIR}/${module}/.git")
41 message(FATAL_ERROR "Git submodule ${module} not found. " 41 message(FATAL_ERROR "Git submodule ${module} not found. "
42 "Please run: git submodule update --init --recursive") 42 "Please run: git submodule update --init --recursive")
43 endif() 43 endif()
@@ -45,17 +45,17 @@ function(check_submodules_present)
45endfunction() 45endfunction()
46check_submodules_present() 46check_submodules_present()
47 47
48configure_file(${CMAKE_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc 48configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
49 ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc 49 ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
50 COPYONLY) 50 COPYONLY)
51if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) 51if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
52 message(STATUS "Downloading compatibility list for yuzu...") 52 message(STATUS "Downloading compatibility list for yuzu...")
53 file(DOWNLOAD 53 file(DOWNLOAD
54 https://api.yuzu-emu.org/gamedb/ 54 https://api.yuzu-emu.org/gamedb/
55 "${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS) 55 "${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
56endif() 56endif()
57if (NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) 57if (NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
58 file(WRITE ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "") 58 file(WRITE ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
59endif() 59endif()
60 60
61# Detect current compilation architecture and create standard definitions 61# Detect current compilation architecture and create standard definitions
@@ -170,7 +170,7 @@ endif()
170# On modern Unixes, this is typically already the case. The lone exception is 170# On modern Unixes, this is typically already the case. The lone exception is
171# glibc, which may default to 32 bits. glibc allows this to be configured 171# glibc, which may default to 32 bits. glibc allows this to be configured
172# by setting _FILE_OFFSET_BITS. 172# by setting _FILE_OFFSET_BITS.
173if(CMAKE_SYSTEM_NAME STREQUAL "Linux") 173if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
174 add_definitions(-D_FILE_OFFSET_BITS=64) 174 add_definitions(-D_FILE_OFFSET_BITS=64)
175endif() 175endif()
176 176
@@ -178,10 +178,6 @@ endif()
178set_property(DIRECTORY APPEND PROPERTY 178set_property(DIRECTORY APPEND PROPERTY
179 COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>) 179 COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
180 180
181
182math(EXPR EMU_ARCH_BITS ${CMAKE_SIZEOF_VOID_P}*8)
183add_definitions(-DEMU_ARCH_BITS=${EMU_ARCH_BITS})
184
185# System imported libraries 181# System imported libraries
186# ====================== 182# ======================
187 183
@@ -189,13 +185,13 @@ find_package(Boost 1.63.0 QUIET)
189if (NOT Boost_FOUND) 185if (NOT Boost_FOUND)
190 message(STATUS "Boost 1.63.0 or newer not found, falling back to externals") 186 message(STATUS "Boost 1.63.0 or newer not found, falling back to externals")
191 187
192 set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost") 188 set(BOOST_ROOT "${PROJECT_SOURCE_DIR}/externals/boost")
193 set(Boost_NO_SYSTEM_PATHS OFF) 189 set(Boost_NO_SYSTEM_PATHS OFF)
194 find_package(Boost QUIET REQUIRED) 190 find_package(Boost QUIET REQUIRED)
195endif() 191endif()
196 192
197# Output binaries to bin/ 193# Output binaries to bin/
198set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 194set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
199 195
200# Prefer the -pthread flag on Linux. 196# Prefer the -pthread flag on Linux.
201set(THREADS_PREFER_PTHREAD_FLAG ON) 197set(THREADS_PREFER_PTHREAD_FLAG ON)
@@ -264,7 +260,7 @@ if (YUZU_USE_BUNDLED_UNICORN)
264 endif() 260 endif()
265 261
266 set(UNICORN_FOUND YES) 262 set(UNICORN_FOUND YES)
267 set(UNICORN_PREFIX ${CMAKE_SOURCE_DIR}/externals/unicorn) 263 set(UNICORN_PREFIX ${PROJECT_SOURCE_DIR}/externals/unicorn)
268 set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/${UNICORN_LIB_NAME}" CACHE PATH "Path to Unicorn library" FORCE) 264 set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/${UNICORN_LIB_NAME}" CACHE PATH "Path to Unicorn library" FORCE)
269 set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE) 265 set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE)
270 set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/" CACHE PATH "Path to unicorn dynamic library" FORCE) 266 set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/" CACHE PATH "Path to unicorn dynamic library" FORCE)
@@ -356,12 +352,12 @@ set(CLANG_FORMAT_POSTFIX "-6.0")
356find_program(CLANG_FORMAT 352find_program(CLANG_FORMAT
357 NAMES clang-format${CLANG_FORMAT_POSTFIX} 353 NAMES clang-format${CLANG_FORMAT_POSTFIX}
358 clang-format 354 clang-format
359 PATHS ${CMAKE_BINARY_DIR}/externals) 355 PATHS ${PROJECT_BINARY_DIR}/externals)
360# if find_program doesn't find it, try to download from externals 356# if find_program doesn't find it, try to download from externals
361if (NOT CLANG_FORMAT) 357if (NOT CLANG_FORMAT)
362 if (WIN32) 358 if (WIN32)
363 message(STATUS "Clang format not found! Downloading...") 359 message(STATUS "Clang format not found! Downloading...")
364 set(CLANG_FORMAT "${CMAKE_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe") 360 set(CLANG_FORMAT "${PROJECT_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe")
365 file(DOWNLOAD 361 file(DOWNLOAD
366 https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe 362 https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe
367 "${CLANG_FORMAT}" SHOW_PROGRESS 363 "${CLANG_FORMAT}" SHOW_PROGRESS
@@ -377,7 +373,7 @@ if (NOT CLANG_FORMAT)
377endif() 373endif()
378 374
379if (CLANG_FORMAT) 375if (CLANG_FORMAT)
380 set(SRCS ${CMAKE_SOURCE_DIR}/src) 376 set(SRCS ${PROJECT_SOURCE_DIR}/src)
381 set(CCOMMENT "Running clang format against all the .h and .cpp files in src/") 377 set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
382 if (WIN32) 378 if (WIN32)
383 add_custom_target(clang-format 379 add_custom_target(clang-format
@@ -450,10 +446,10 @@ endif()
450# http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html 446# http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
451# http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html 447# http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
452if(ENABLE_QT AND UNIX AND NOT APPLE) 448if(ENABLE_QT AND UNIX AND NOT APPLE)
453 install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.desktop" 449 install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.desktop"
454 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications") 450 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications")
455 install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.svg" 451 install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.svg"
456 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps") 452 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps")
457 install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.xml" 453 install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.xml"
458 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages") 454 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages")
459endif() 455endif()
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index d0e506689..eccd8f64a 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -64,8 +64,6 @@ add_library(common STATIC
64 logging/text_formatter.cpp 64 logging/text_formatter.cpp
65 logging/text_formatter.h 65 logging/text_formatter.h
66 math_util.h 66 math_util.h
67 memory_util.cpp
68 memory_util.h
69 microprofile.cpp 67 microprofile.cpp
70 microprofile.h 68 microprofile.h
71 microprofileui.h 69 microprofileui.h
diff --git a/src/common/alignment.h b/src/common/alignment.h
index 225770fab..d94a2291f 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -19,4 +19,16 @@ constexpr T AlignDown(T value, std::size_t size) {
19 return static_cast<T>(value - value % size); 19 return static_cast<T>(value - value % size);
20} 20}
21 21
22template <typename T>
23constexpr bool Is4KBAligned(T value) {
24 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
25 return (value & 0xFFF) == 0;
26}
27
28template <typename T>
29constexpr bool IsWordAligned(T value) {
30 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
31 return (value & 0b11) == 0;
32}
33
22} // namespace Common 34} // namespace Common
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 548463787..b52492da6 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -15,21 +15,24 @@
15#ifdef _WIN32 15#ifdef _WIN32
16#include <windows.h> 16#include <windows.h>
17// windows.h needs to be included before other windows headers 17// windows.h needs to be included before other windows headers
18#include <commdlg.h> // for GetSaveFileName 18#include <direct.h> // getcwd
19#include <direct.h> // getcwd
20#include <io.h> 19#include <io.h>
21#include <shellapi.h> 20#include <shellapi.h>
22#include <shlobj.h> // for SHGetFolderPath 21#include <shlobj.h> // for SHGetFolderPath
23#include <tchar.h> 22#include <tchar.h>
24#include "common/string_util.h" 23#include "common/string_util.h"
25 24
26// 64 bit offsets for windows 25#ifdef _MSC_VER
26// 64 bit offsets for MSVC
27#define fseeko _fseeki64 27#define fseeko _fseeki64
28#define ftello _ftelli64 28#define ftello _ftelli64
29#define atoll _atoi64 29#define fileno _fileno
30#endif
31
32// 64 bit offsets for MSVC and MinGW. MinGW also needs this for using _wstat64
30#define stat _stat64 33#define stat _stat64
31#define fstat _fstat64 34#define fstat _fstat64
32#define fileno _fileno 35
33#else 36#else
34#ifdef __APPLE__ 37#ifdef __APPLE__
35#include <sys/param.h> 38#include <sys/param.h>
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index abbd056ee..c9161155a 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -91,6 +91,7 @@ enum class Class : ClassType {
91 Service_PM, ///< The PM service 91 Service_PM, ///< The PM service
92 Service_PREPO, ///< The PREPO (Play report) service 92 Service_PREPO, ///< The PREPO (Play report) service
93 Service_PSC, ///< The PSC service 93 Service_PSC, ///< The PSC service
94 Service_PSM, ///< The PSM service
94 Service_SET, ///< The SET (Settings) service 95 Service_SET, ///< The SET (Settings) service
95 Service_SM, ///< The SM (Service manager) service 96 Service_SM, ///< The SM (Service manager) service
96 Service_SPL, ///< The SPL service 97 Service_SPL, ///< The SPL service
diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp
deleted file mode 100644
index 9736fb12a..000000000
--- a/src/common/memory_util.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "common/memory_util.h"
7
8#ifdef _WIN32
9#include <windows.h>
10// Windows.h needs to be included before psapi.h
11#include <psapi.h>
12#include "common/common_funcs.h"
13#include "common/string_util.h"
14#else
15#include <cstdlib>
16#include <sys/mman.h>
17#endif
18
19#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
20#include <unistd.h>
21#define PAGE_MASK (getpagesize() - 1)
22#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
23#endif
24
25// This is purposely not a full wrapper for virtualalloc/mmap, but it
26// provides exactly the primitive operations that Dolphin needs.
27
28void* AllocateExecutableMemory(std::size_t size, bool low) {
29#if defined(_WIN32)
30 void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
31#else
32 static char* map_hint = nullptr;
33#if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
34 // This OS has no flag to enforce allocation below the 4 GB boundary,
35 // but if we hint that we want a low address it is very likely we will
36 // get one.
37 // An older version of this code used MAP_FIXED, but that has the side
38 // effect of discarding already mapped pages that happen to be in the
39 // requested virtual memory range (such as the emulated RAM, sometimes).
40 if (low && (!map_hint))
41 map_hint = (char*)round_page(512 * 1024 * 1024); /* 0.5 GB rounded up to the next page */
42#endif
43 void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
44 MAP_ANON | MAP_PRIVATE
45#if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT)
46 | (low ? MAP_32BIT : 0)
47#endif
48 ,
49 -1, 0);
50#endif /* defined(_WIN32) */
51
52#ifdef _WIN32
53 if (ptr == nullptr) {
54#else
55 if (ptr == MAP_FAILED) {
56 ptr = nullptr;
57#endif
58 LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
59 }
60#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
61 else {
62 if (low) {
63 map_hint += size;
64 map_hint = (char*)round_page(map_hint); /* round up to the next page */
65 }
66 }
67#endif
68
69#if EMU_ARCH_BITS == 64
70 if ((u64)ptr >= 0x80000000 && low == true)
71 LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!");
72#endif
73
74 return ptr;
75}
76
77void* AllocateMemoryPages(std::size_t size) {
78#ifdef _WIN32
79 void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
80#else
81 void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
82
83 if (ptr == MAP_FAILED)
84 ptr = nullptr;
85#endif
86
87 if (ptr == nullptr)
88 LOG_ERROR(Common_Memory, "Failed to allocate raw memory");
89
90 return ptr;
91}
92
93void* AllocateAlignedMemory(std::size_t size, std::size_t alignment) {
94#ifdef _WIN32
95 void* ptr = _aligned_malloc(size, alignment);
96#else
97 void* ptr = nullptr;
98#ifdef ANDROID
99 ptr = memalign(alignment, size);
100#else
101 if (posix_memalign(&ptr, alignment, size) != 0)
102 LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
103#endif
104#endif
105
106 if (ptr == nullptr)
107 LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
108
109 return ptr;
110}
111
112void FreeMemoryPages(void* ptr, std::size_t size) {
113 if (ptr) {
114#ifdef _WIN32
115 if (!VirtualFree(ptr, 0, MEM_RELEASE))
116 LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg());
117#else
118 munmap(ptr, size);
119#endif
120 }
121}
122
123void FreeAlignedMemory(void* ptr) {
124 if (ptr) {
125#ifdef _WIN32
126 _aligned_free(ptr);
127#else
128 free(ptr);
129#endif
130 }
131}
132
133void WriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
134#ifdef _WIN32
135 DWORD oldValue;
136 if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
137 LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg());
138#else
139 mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
140#endif
141}
142
143void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
144#ifdef _WIN32
145 DWORD oldValue;
146 if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
147 &oldValue))
148 LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg());
149#else
150 mprotect(ptr, size,
151 allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ);
152#endif
153}
154
155std::string MemUsage() {
156#ifdef _WIN32
157#pragma comment(lib, "psapi")
158 DWORD processID = GetCurrentProcessId();
159 HANDLE hProcess;
160 PROCESS_MEMORY_COUNTERS pmc;
161 std::string Ret;
162
163 // Print information about the memory usage of the process.
164
165 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
166 if (nullptr == hProcess)
167 return "MemUsage Error";
168
169 if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
170 Ret = fmt::format("{} K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7));
171
172 CloseHandle(hProcess);
173 return Ret;
174#else
175 return "";
176#endif
177}
diff --git a/src/common/memory_util.h b/src/common/memory_util.h
deleted file mode 100644
index aad071979..000000000
--- a/src/common/memory_util.h
+++ /dev/null
@@ -1,21 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstddef>
8#include <string>
9
10void* AllocateExecutableMemory(std::size_t size, bool low = true);
11void* AllocateMemoryPages(std::size_t size);
12void FreeMemoryPages(void* ptr, std::size_t size);
13void* AllocateAlignedMemory(std::size_t size, std::size_t alignment);
14void FreeAlignedMemory(void* ptr);
15void WriteProtectMemory(void* ptr, std::size_t size, bool executable = false);
16void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute = false);
17std::string MemUsage();
18
19inline int GetPageSize() {
20 return 4096;
21}
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 78986deb5..64fdf38cd 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -156,6 +156,8 @@ add_library(core STATIC
156 hle/service/am/omm.h 156 hle/service/am/omm.h
157 hle/service/am/spsm.cpp 157 hle/service/am/spsm.cpp
158 hle/service/am/spsm.h 158 hle/service/am/spsm.h
159 hle/service/am/tcap.cpp
160 hle/service/am/tcap.h
159 hle/service/aoc/aoc_u.cpp 161 hle/service/aoc/aoc_u.cpp
160 hle/service/aoc/aoc_u.h 162 hle/service/aoc/aoc_u.h
161 hle/service/apm/apm.cpp 163 hle/service/apm/apm.cpp
@@ -236,6 +238,24 @@ add_library(core STATIC
236 hle/service/hid/irs.h 238 hle/service/hid/irs.h
237 hle/service/hid/xcd.cpp 239 hle/service/hid/xcd.cpp
238 hle/service/hid/xcd.h 240 hle/service/hid/xcd.h
241 hle/service/hid/controllers/controller_base.cpp
242 hle/service/hid/controllers/controller_base.h
243 hle/service/hid/controllers/debug_pad.cpp
244 hle/service/hid/controllers/debug_pad.h
245 hle/service/hid/controllers/gesture.cpp
246 hle/service/hid/controllers/gesture.h
247 hle/service/hid/controllers/keyboard.cpp
248 hle/service/hid/controllers/keyboard.h
249 hle/service/hid/controllers/mouse.cpp
250 hle/service/hid/controllers/mouse.h
251 hle/service/hid/controllers/npad.cpp
252 hle/service/hid/controllers/npad.h
253 hle/service/hid/controllers/stubbed.cpp
254 hle/service/hid/controllers/stubbed.h
255 hle/service/hid/controllers/touchscreen.cpp
256 hle/service/hid/controllers/touchscreen.h
257 hle/service/hid/controllers/xpad.cpp
258 hle/service/hid/controllers/xpad.h
239 hle/service/lbl/lbl.cpp 259 hle/service/lbl/lbl.cpp
240 hle/service/lbl/lbl.h 260 hle/service/lbl/lbl.h
241 hle/service/ldn/ldn.cpp 261 hle/service/ldn/ldn.cpp
@@ -262,6 +282,8 @@ add_library(core STATIC
262 hle/service/nifm/nifm.h 282 hle/service/nifm/nifm.h
263 hle/service/nim/nim.cpp 283 hle/service/nim/nim.cpp
264 hle/service/nim/nim.h 284 hle/service/nim/nim.h
285 hle/service/npns/npns.cpp
286 hle/service/npns/npns.h
265 hle/service/ns/ns.cpp 287 hle/service/ns/ns.cpp
266 hle/service/ns/ns.h 288 hle/service/ns/ns.h
267 hle/service/ns/pl_u.cpp 289 hle/service/ns/pl_u.cpp
@@ -309,6 +331,8 @@ add_library(core STATIC
309 hle/service/prepo/prepo.h 331 hle/service/prepo/prepo.h
310 hle/service/psc/psc.cpp 332 hle/service/psc/psc.cpp
311 hle/service/psc/psc.h 333 hle/service/psc/psc.h
334 hle/service/ptm/psm.cpp
335 hle/service/ptm/psm.h
312 hle/service/service.cpp 336 hle/service/service.cpp
313 hle/service/service.h 337 hle/service/service.h
314 hle/service/set/set.cpp 338 hle/service/set/set.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 3c57a62ec..7cb86ed92 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -375,8 +375,7 @@ const Kernel::Process* System::CurrentProcess() const {
375} 375}
376 376
377ARM_Interface& System::ArmInterface(std::size_t core_index) { 377ARM_Interface& System::ArmInterface(std::size_t core_index) {
378 ASSERT(core_index < NUM_CPU_CORES); 378 return CpuCore(core_index).ArmInterface();
379 return impl->cpu_cores[core_index]->ArmInterface();
380} 379}
381 380
382Cpu& System::CpuCore(std::size_t core_index) { 381Cpu& System::CpuCore(std::size_t core_index) {
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index 25cee1f3a..ed0775444 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -516,7 +516,8 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
516 out.insert(out.end(), rodata.begin(), rodata.end()); 516 out.insert(out.end(), rodata.begin(), rodata.end());
517 out.insert(out.end(), data.begin(), data.end()); 517 out.insert(out.end(), data.begin(), data.end());
518 518
519 offset += sizeof(KIPHeader) + out.size(); 519 offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
520 kip.sections[1].size_compressed + kip.sections[2].size_compressed;
520 521
521 if (name == "FS") 522 if (name == "FS")
522 package2_fs[static_cast<size_t>(type)] = std::move(out); 523 package2_fs[static_cast<size_t>(type)] = std::move(out);
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 8f5142a07..ecdd7505b 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -122,14 +122,16 @@ u64 XCI::GetProgramTitleID() const {
122 return secure_partition->GetProgramTitleID(); 122 return secure_partition->GetProgramTitleID();
123} 123}
124 124
125std::shared_ptr<NCA> XCI::GetProgramNCA() const { 125bool XCI::HasProgramNCA() const {
126 return program; 126 return program != nullptr;
127} 127}
128 128
129VirtualFile XCI::GetProgramNCAFile() const { 129VirtualFile XCI::GetProgramNCAFile() const {
130 if (GetProgramNCA() == nullptr) 130 if (!HasProgramNCA()) {
131 return nullptr; 131 return nullptr;
132 return GetProgramNCA()->GetBaseFile(); 132 }
133
134 return program->GetBaseFile();
133} 135}
134 136
135const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const { 137const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index ce514dfa0..48cbef666 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -80,7 +80,7 @@ public:
80 80
81 u64 GetProgramTitleID() const; 81 u64 GetProgramTitleID() const;
82 82
83 std::shared_ptr<NCA> GetProgramNCA() const; 83 bool HasProgramNCA() const;
84 VirtualFile GetProgramNCAFile() const; 84 VirtualFile GetProgramNCAFile() const;
85 const std::vector<std::shared_ptr<NCA>>& GetNCAs() const; 85 const std::vector<std::shared_ptr<NCA>>& GetNCAs() const;
86 std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const; 86 std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 6dcec7816..6c356d85d 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -97,11 +97,288 @@ union NCASectionHeader {
97}; 97};
98static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); 98static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
99 99
100bool IsValidNCA(const NCAHeader& header) { 100static bool IsValidNCA(const NCAHeader& header) {
101 // TODO(DarkLordZach): Add NCA2/NCA0 support. 101 // TODO(DarkLordZach): Add NCA2/NCA0 support.
102 return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); 102 return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
103} 103}
104 104
105NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
106 : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
107 if (file == nullptr) {
108 status = Loader::ResultStatus::ErrorNullFile;
109 return;
110 }
111
112 if (sizeof(NCAHeader) != file->ReadObject(&header)) {
113 LOG_ERROR(Loader, "File reader errored out during header read.");
114 status = Loader::ResultStatus::ErrorBadNCAHeader;
115 return;
116 }
117
118 if (!HandlePotentialHeaderDecryption()) {
119 return;
120 }
121
122 has_rights_id = std::any_of(header.rights_id.begin(), header.rights_id.end(),
123 [](char c) { return c != '\0'; });
124
125 const std::vector<NCASectionHeader> sections = ReadSectionHeaders();
126 is_update = std::any_of(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
127 return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
128 });
129
130 if (!ReadSections(sections, bktr_base_ivfc_offset)) {
131 return;
132 }
133
134 status = Loader::ResultStatus::Success;
135}
136
137NCA::~NCA() = default;
138
139bool NCA::CheckSupportedNCA(const NCAHeader& nca_header) {
140 if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
141 status = Loader::ResultStatus::ErrorNCA2;
142 return false;
143 }
144
145 if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
146 status = Loader::ResultStatus::ErrorNCA0;
147 return false;
148 }
149
150 return true;
151}
152
153bool NCA::HandlePotentialHeaderDecryption() {
154 if (IsValidNCA(header)) {
155 return true;
156 }
157
158 if (!CheckSupportedNCA(header)) {
159 return false;
160 }
161
162 NCAHeader dec_header{};
163 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
164 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
165 cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
166 Core::Crypto::Op::Decrypt);
167 if (IsValidNCA(dec_header)) {
168 header = dec_header;
169 encrypted = true;
170 } else {
171 if (!CheckSupportedNCA(dec_header)) {
172 return false;
173 }
174
175 if (keys.HasKey(Core::Crypto::S256KeyType::Header)) {
176 status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
177 } else {
178 status = Loader::ResultStatus::ErrorMissingHeaderKey;
179 }
180 return false;
181 }
182
183 return true;
184}
185
186std::vector<NCASectionHeader> NCA::ReadSectionHeaders() const {
187 const std::ptrdiff_t number_sections =
188 std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
189 [](NCASectionTableEntry entry) { return entry.media_offset > 0; });
190
191 std::vector<NCASectionHeader> sections(number_sections);
192 const auto length_sections = SECTION_HEADER_SIZE * number_sections;
193
194 if (encrypted) {
195 auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
196 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
197 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
198 cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
199 Core::Crypto::Op::Decrypt);
200 } else {
201 file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
202 }
203
204 return sections;
205}
206
207bool NCA::ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset) {
208 for (std::size_t i = 0; i < sections.size(); ++i) {
209 const auto& section = sections[i];
210
211 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
212 if (!ReadRomFSSection(section, header.section_tables[i], bktr_base_ivfc_offset)) {
213 return false;
214 }
215 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
216 if (!ReadPFS0Section(section, header.section_tables[i])) {
217 return false;
218 }
219 }
220 }
221
222 return true;
223}
224
225bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
226 u64 bktr_base_ivfc_offset) {
227 const std::size_t base_offset = entry.media_offset * MEDIA_OFFSET_MULTIPLIER;
228 ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
229 const std::size_t romfs_offset = base_offset + ivfc_offset;
230 const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
231 auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
232 auto dec = Decrypt(section, raw, romfs_offset);
233
234 if (dec == nullptr) {
235 if (status != Loader::ResultStatus::Success)
236 return false;
237 if (has_rights_id)
238 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
239 else
240 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
241 return false;
242 }
243
244 if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
245 if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
246 section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
247 status = Loader::ResultStatus::ErrorBadBKTRHeader;
248 return false;
249 }
250
251 if (section.bktr.relocation.offset + section.bktr.relocation.size !=
252 section.bktr.subsection.offset) {
253 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
254 return false;
255 }
256
257 const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
258 if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
259 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
260 return false;
261 }
262
263 const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
264 RelocationBlock relocation_block{};
265 if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
266 sizeof(RelocationBlock)) {
267 status = Loader::ResultStatus::ErrorBadRelocationBlock;
268 return false;
269 }
270 SubsectionBlock subsection_block{};
271 if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
272 sizeof(RelocationBlock)) {
273 status = Loader::ResultStatus::ErrorBadSubsectionBlock;
274 return false;
275 }
276
277 std::vector<RelocationBucketRaw> relocation_buckets_raw(
278 (section.bktr.relocation.size - sizeof(RelocationBlock)) / sizeof(RelocationBucketRaw));
279 if (dec->ReadBytes(relocation_buckets_raw.data(),
280 section.bktr.relocation.size - sizeof(RelocationBlock),
281 section.bktr.relocation.offset + sizeof(RelocationBlock) - offset) !=
282 section.bktr.relocation.size - sizeof(RelocationBlock)) {
283 status = Loader::ResultStatus::ErrorBadRelocationBuckets;
284 return false;
285 }
286
287 std::vector<SubsectionBucketRaw> subsection_buckets_raw(
288 (section.bktr.subsection.size - sizeof(SubsectionBlock)) / sizeof(SubsectionBucketRaw));
289 if (dec->ReadBytes(subsection_buckets_raw.data(),
290 section.bktr.subsection.size - sizeof(SubsectionBlock),
291 section.bktr.subsection.offset + sizeof(SubsectionBlock) - offset) !=
292 section.bktr.subsection.size - sizeof(SubsectionBlock)) {
293 status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
294 return false;
295 }
296
297 std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
298 std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
299 relocation_buckets.begin(), &ConvertRelocationBucketRaw);
300 std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
301 std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
302 subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
303
304 u32 ctr_low;
305 std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
306 subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
307 subsection_buckets.back().entries.push_back({size, {0}, 0});
308
309 boost::optional<Core::Crypto::Key128> key = boost::none;
310 if (encrypted) {
311 if (has_rights_id) {
312 status = Loader::ResultStatus::Success;
313 key = GetTitlekey();
314 if (key == boost::none) {
315 status = Loader::ResultStatus::ErrorMissingTitlekey;
316 return false;
317 }
318 } else {
319 key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
320 if (key == boost::none) {
321 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
322 return false;
323 }
324 }
325 }
326
327 if (bktr_base_romfs == nullptr) {
328 status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
329 return false;
330 }
331
332 auto bktr = std::make_shared<BKTR>(
333 bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
334 relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted,
335 encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
336 section.raw.section_ctr);
337
338 // BKTR applies to entire IVFC, so make an offset version to level 6
339 files.push_back(std::make_shared<OffsetVfsFile>(
340 bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
341 } else {
342 files.push_back(std::move(dec));
343 }
344
345 romfs = files.back();
346 return true;
347}
348
349bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry) {
350 const u64 offset = (static_cast<u64>(entry.media_offset) * MEDIA_OFFSET_MULTIPLIER) +
351 section.pfs0.pfs0_header_offset;
352 const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
353
354 auto dec = Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
355 if (dec != nullptr) {
356 auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
357
358 if (npfs->GetStatus() == Loader::ResultStatus::Success) {
359 dirs.push_back(std::move(npfs));
360 if (IsDirectoryExeFS(dirs.back()))
361 exefs = dirs.back();
362 } else {
363 if (has_rights_id)
364 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
365 else
366 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
367 return false;
368 }
369 } else {
370 if (status != Loader::ResultStatus::Success)
371 return false;
372 if (has_rights_id)
373 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
374 else
375 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
376 return false;
377 }
378
379 return true;
380}
381
105u8 NCA::GetCryptoRevision() const { 382u8 NCA::GetCryptoRevision() const {
106 u8 master_key_id = header.crypto_type; 383 u8 master_key_id = header.crypto_type;
107 if (header.crypto_type_2 > master_key_id) 384 if (header.crypto_type_2 > master_key_id)
@@ -167,7 +444,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
167 return titlekey; 444 return titlekey;
168} 445}
169 446
170VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) { 447VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 starting_offset) {
171 if (!encrypted) 448 if (!encrypted)
172 return in; 449 return in;
173 450
@@ -215,256 +492,6 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
215 } 492 }
216} 493}
217 494
218NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
219 : file(std::move(file_)),
220 bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) {
221 status = Loader::ResultStatus::Success;
222
223 if (file == nullptr) {
224 status = Loader::ResultStatus::ErrorNullFile;
225 return;
226 }
227
228 if (sizeof(NCAHeader) != file->ReadObject(&header)) {
229 LOG_ERROR(Loader, "File reader errored out during header read.");
230 status = Loader::ResultStatus::ErrorBadNCAHeader;
231 return;
232 }
233
234 encrypted = false;
235
236 if (!IsValidNCA(header)) {
237 if (header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
238 status = Loader::ResultStatus::ErrorNCA2;
239 return;
240 }
241 if (header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
242 status = Loader::ResultStatus::ErrorNCA0;
243 return;
244 }
245
246 NCAHeader dec_header{};
247 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
248 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
249 cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
250 Core::Crypto::Op::Decrypt);
251 if (IsValidNCA(dec_header)) {
252 header = dec_header;
253 encrypted = true;
254 } else {
255 if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
256 status = Loader::ResultStatus::ErrorNCA2;
257 return;
258 }
259 if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
260 status = Loader::ResultStatus::ErrorNCA0;
261 return;
262 }
263
264 if (!keys.HasKey(Core::Crypto::S256KeyType::Header))
265 status = Loader::ResultStatus::ErrorMissingHeaderKey;
266 else
267 status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
268 return;
269 }
270 }
271
272 has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
273 [](char c) { return c == '\0'; }) != header.rights_id.end();
274
275 const std::ptrdiff_t number_sections =
276 std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
277 [](NCASectionTableEntry entry) { return entry.media_offset > 0; });
278
279 std::vector<NCASectionHeader> sections(number_sections);
280 const auto length_sections = SECTION_HEADER_SIZE * number_sections;
281
282 if (encrypted) {
283 auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
284 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
285 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
286 cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
287 Core::Crypto::Op::Decrypt);
288 } else {
289 file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
290 }
291
292 is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
293 return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
294 }) != sections.end();
295 ivfc_offset = 0;
296
297 for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
298 auto section = sections[i];
299
300 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
301 const std::size_t base_offset =
302 header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
303 ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
304 const std::size_t romfs_offset = base_offset + ivfc_offset;
305 const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
306 auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
307 auto dec = Decrypt(section, raw, romfs_offset);
308
309 if (dec == nullptr) {
310 if (status != Loader::ResultStatus::Success)
311 return;
312 if (has_rights_id)
313 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
314 else
315 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
316 return;
317 }
318
319 if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
320 if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
321 section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
322 status = Loader::ResultStatus::ErrorBadBKTRHeader;
323 return;
324 }
325
326 if (section.bktr.relocation.offset + section.bktr.relocation.size !=
327 section.bktr.subsection.offset) {
328 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
329 return;
330 }
331
332 const u64 size =
333 MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
334 header.section_tables[i].media_offset);
335 if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
336 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
337 return;
338 }
339
340 const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
341 RelocationBlock relocation_block{};
342 if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
343 sizeof(RelocationBlock)) {
344 status = Loader::ResultStatus::ErrorBadRelocationBlock;
345 return;
346 }
347 SubsectionBlock subsection_block{};
348 if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
349 sizeof(RelocationBlock)) {
350 status = Loader::ResultStatus::ErrorBadSubsectionBlock;
351 return;
352 }
353
354 std::vector<RelocationBucketRaw> relocation_buckets_raw(
355 (section.bktr.relocation.size - sizeof(RelocationBlock)) /
356 sizeof(RelocationBucketRaw));
357 if (dec->ReadBytes(relocation_buckets_raw.data(),
358 section.bktr.relocation.size - sizeof(RelocationBlock),
359 section.bktr.relocation.offset + sizeof(RelocationBlock) -
360 offset) !=
361 section.bktr.relocation.size - sizeof(RelocationBlock)) {
362 status = Loader::ResultStatus::ErrorBadRelocationBuckets;
363 return;
364 }
365
366 std::vector<SubsectionBucketRaw> subsection_buckets_raw(
367 (section.bktr.subsection.size - sizeof(SubsectionBlock)) /
368 sizeof(SubsectionBucketRaw));
369 if (dec->ReadBytes(subsection_buckets_raw.data(),
370 section.bktr.subsection.size - sizeof(SubsectionBlock),
371 section.bktr.subsection.offset + sizeof(SubsectionBlock) -
372 offset) !=
373 section.bktr.subsection.size - sizeof(SubsectionBlock)) {
374 status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
375 return;
376 }
377
378 std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
379 std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
380 relocation_buckets.begin(), &ConvertRelocationBucketRaw);
381 std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
382 std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
383 subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
384
385 u32 ctr_low;
386 std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
387 subsection_buckets.back().entries.push_back(
388 {section.bktr.relocation.offset, {0}, ctr_low});
389 subsection_buckets.back().entries.push_back({size, {0}, 0});
390
391 boost::optional<Core::Crypto::Key128> key = boost::none;
392 if (encrypted) {
393 if (has_rights_id) {
394 status = Loader::ResultStatus::Success;
395 key = GetTitlekey();
396 if (key == boost::none) {
397 status = Loader::ResultStatus::ErrorMissingTitlekey;
398 return;
399 }
400 } else {
401 key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
402 if (key == boost::none) {
403 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
404 return;
405 }
406 }
407 }
408
409 if (bktr_base_romfs == nullptr) {
410 status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
411 return;
412 }
413
414 auto bktr = std::make_shared<BKTR>(
415 bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
416 relocation_block, relocation_buckets, subsection_block, subsection_buckets,
417 encrypted, encrypted ? key.get() : Core::Crypto::Key128{}, base_offset,
418 bktr_base_ivfc_offset, section.raw.section_ctr);
419
420 // BKTR applies to entire IVFC, so make an offset version to level 6
421
422 files.push_back(std::make_shared<OffsetVfsFile>(
423 bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
424 romfs = files.back();
425 } else {
426 files.push_back(std::move(dec));
427 romfs = files.back();
428 }
429 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
430 u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
431 MEDIA_OFFSET_MULTIPLIER) +
432 section.pfs0.pfs0_header_offset;
433 u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
434 header.section_tables[i].media_offset);
435 auto dec =
436 Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
437 if (dec != nullptr) {
438 auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
439
440 if (npfs->GetStatus() == Loader::ResultStatus::Success) {
441 dirs.push_back(std::move(npfs));
442 if (IsDirectoryExeFS(dirs.back()))
443 exefs = dirs.back();
444 } else {
445 if (has_rights_id)
446 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
447 else
448 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
449 return;
450 }
451 } else {
452 if (status != Loader::ResultStatus::Success)
453 return;
454 if (has_rights_id)
455 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
456 else
457 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
458 return;
459 }
460 }
461 }
462
463 status = Loader::ResultStatus::Success;
464}
465
466NCA::~NCA() = default;
467
468Loader::ResultStatus NCA::GetStatus() const { 495Loader::ResultStatus NCA::GetStatus() const {
469 return status; 496 return status;
470} 497}
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index f9f66cae9..1c903cd3f 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -73,8 +73,6 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
73 return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; 73 return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
74} 74}
75 75
76bool IsValidNCA(const NCAHeader& header);
77
78// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. 76// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
79// After construction, use GetStatus to determine if the file is valid and ready to be used. 77// After construction, use GetStatus to determine if the file is valid and ready to be used.
80class NCA : public ReadOnlyVfsDirectory { 78class NCA : public ReadOnlyVfsDirectory {
@@ -106,10 +104,19 @@ protected:
106 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 104 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
107 105
108private: 106private:
107 bool CheckSupportedNCA(const NCAHeader& header);
108 bool HandlePotentialHeaderDecryption();
109
110 std::vector<NCASectionHeader> ReadSectionHeaders() const;
111 bool ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset);
112 bool ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
113 u64 bktr_base_ivfc_offset);
114 bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry);
115
109 u8 GetCryptoRevision() const; 116 u8 GetCryptoRevision() const;
110 boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; 117 boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
111 boost::optional<Core::Crypto::Key128> GetTitlekey(); 118 boost::optional<Core::Crypto::Key128> GetTitlekey();
112 VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset); 119 VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset);
113 120
114 std::vector<VirtualDir> dirs; 121 std::vector<VirtualDir> dirs;
115 std::vector<VirtualFile> files; 122 std::vector<VirtualFile> files;
@@ -118,15 +125,15 @@ private:
118 VirtualDir exefs = nullptr; 125 VirtualDir exefs = nullptr;
119 VirtualFile file; 126 VirtualFile file;
120 VirtualFile bktr_base_romfs; 127 VirtualFile bktr_base_romfs;
121 u64 ivfc_offset; 128 u64 ivfc_offset = 0;
122 129
123 NCAHeader header{}; 130 NCAHeader header{};
124 bool has_rights_id{}; 131 bool has_rights_id{};
125 132
126 Loader::ResultStatus status{}; 133 Loader::ResultStatus status{};
127 134
128 bool encrypted; 135 bool encrypted = false;
129 bool is_update; 136 bool is_update = false;
130 137
131 Core::Crypto::KeyManager keys; 138 Core::Crypto::KeyManager keys;
132}; 139};
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 47f2ab9e0..ef1aaebbb 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -51,6 +51,13 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
51 meta.title_id); 51 meta.title_id);
52 } 52 }
53 53
54 if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) {
55 LOG_WARNING(Service_FS,
56 "Possibly incorrect SaveDataDescriptor, type is DeviceSaveData but user_id is "
57 "non-zero ({:016X}{:016X})",
58 meta.user_id[1], meta.user_id[0]);
59 }
60
54 std::string save_directory = 61 std::string save_directory =
55 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); 62 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
56 63
@@ -92,6 +99,9 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
92 case SaveDataSpaceId::NandUser: 99 case SaveDataSpaceId::NandUser:
93 out = "/user/"; 100 out = "/user/";
94 break; 101 break;
102 case SaveDataSpaceId::TemporaryStorage:
103 out = "/temp/";
104 break;
95 default: 105 default:
96 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); 106 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
97 } 107 }
@@ -100,10 +110,11 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
100 case SaveDataType::SystemSaveData: 110 case SaveDataType::SystemSaveData:
101 return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]); 111 return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
102 case SaveDataType::SaveData: 112 case SaveDataType::SaveData:
113 case SaveDataType::DeviceSaveData:
103 return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], 114 return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
104 title_id); 115 title_id);
105 case SaveDataType::TemporaryStorage: 116 case SaveDataType::TemporaryStorage:
106 return fmt::format("{}temp/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], 117 return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
107 title_id); 118 title_id);
108 default: 119 default:
109 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); 120 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index edad5f1b1..68d5376cb 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -77,7 +77,8 @@ HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_ses
77 77
78HLERequestContext::~HLERequestContext() = default; 78HLERequestContext::~HLERequestContext() = default;
79 79
80void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { 80void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf,
81 bool incoming) {
81 IPC::RequestParser rp(src_cmdbuf); 82 IPC::RequestParser rp(src_cmdbuf);
82 command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>()); 83 command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
83 84
@@ -94,8 +95,6 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
94 rp.Skip(2, false); 95 rp.Skip(2, false);
95 } 96 }
96 if (incoming) { 97 if (incoming) {
97 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
98
99 // Populate the object lists with the data in the IPC request. 98 // Populate the object lists with the data in the IPC request.
100 for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { 99 for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
101 copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>())); 100 copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>()));
@@ -189,10 +188,9 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
189 rp.Skip(1, false); // The command is actually an u64, but we don't use the high part. 188 rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
190} 189}
191 190
192ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, 191ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
193 Process& src_process, 192 u32_le* src_cmdbuf) {
194 HandleTable& src_table) { 193 ParseCommandBuffer(handle_table, src_cmdbuf, true);
195 ParseCommandBuffer(src_cmdbuf, true);
196 if (command_header->type == IPC::CommandType::Close) { 194 if (command_header->type == IPC::CommandType::Close) {
197 // Close does not populate the rest of the IPC header 195 // Close does not populate the rest of the IPC header
198 return RESULT_SUCCESS; 196 return RESULT_SUCCESS;
@@ -207,14 +205,17 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
207 return RESULT_SUCCESS; 205 return RESULT_SUCCESS;
208} 206}
209 207
210ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) { 208ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
209 auto& owner_process = *thread.GetOwnerProcess();
210 auto& handle_table = owner_process.GetHandleTable();
211
211 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; 212 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
212 Memory::ReadBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(), 213 Memory::ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
213 dst_cmdbuf.size() * sizeof(u32)); 214 dst_cmdbuf.size() * sizeof(u32));
214 215
215 // The header was already built in the internal command buffer. Attempt to parse it to verify 216 // The header was already built in the internal command buffer. Attempt to parse it to verify
216 // the integrity and then copy it over to the target command buffer. 217 // the integrity and then copy it over to the target command buffer.
217 ParseCommandBuffer(cmd_buf.data(), false); 218 ParseCommandBuffer(handle_table, cmd_buf.data(), false);
218 219
219 // The data_size already includes the payload header, the padding and the domain header. 220 // The data_size already includes the payload header, the padding and the domain header.
220 std::size_t size = data_payload_offset + command_header->data_size - 221 std::size_t size = data_payload_offset + command_header->data_size -
@@ -236,8 +237,6 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
236 ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy); 237 ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
237 ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move); 238 ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move);
238 239
239 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
240
241 // We don't make a distinction between copy and move handles when translating since HLE 240 // We don't make a distinction between copy and move handles when translating since HLE
242 // services don't deal with handles directly. However, the guest applications might check 241 // services don't deal with handles directly. However, the guest applications might check
243 // for specific values in each of these descriptors. 242 // for specific values in each of these descriptors.
@@ -268,7 +267,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
268 } 267 }
269 268
270 // Copy the translated command buffer back into the thread's command buffer area. 269 // Copy the translated command buffer back into the thread's command buffer area.
271 Memory::WriteBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(), 270 Memory::WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
272 dst_cmdbuf.size() * sizeof(u32)); 271 dst_cmdbuf.size() * sizeof(u32));
273 272
274 return RESULT_SUCCESS; 273 return RESULT_SUCCESS;
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 894479ee0..f01491daa 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -24,10 +24,10 @@ class ServiceFrameworkBase;
24namespace Kernel { 24namespace Kernel {
25 25
26class Domain; 26class Domain;
27class Event;
27class HandleTable; 28class HandleTable;
28class HLERequestContext; 29class HLERequestContext;
29class Process; 30class Process;
30class Event;
31 31
32/** 32/**
33 * Interface implemented by HLE Session handlers. 33 * Interface implemented by HLE Session handlers.
@@ -126,13 +126,12 @@ public:
126 u64 timeout, WakeupCallback&& callback, 126 u64 timeout, WakeupCallback&& callback,
127 Kernel::SharedPtr<Kernel::Event> event = nullptr); 127 Kernel::SharedPtr<Kernel::Event> event = nullptr);
128 128
129 void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
130
131 /// Populates this context with data from the requesting process/thread. 129 /// Populates this context with data from the requesting process/thread.
132 ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process, 130 ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
133 HandleTable& src_table); 131 u32_le* src_cmdbuf);
132
134 /// Writes data from this context back to the requesting process/thread. 133 /// Writes data from this context back to the requesting process/thread.
135 ResultCode WriteToOutgoingCommandBuffer(const Thread& thread); 134 ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
136 135
137 u32_le GetCommand() const { 136 u32_le GetCommand() const {
138 return command; 137 return command;
@@ -255,6 +254,8 @@ public:
255 std::string Description() const; 254 std::string Description() const;
256 255
257private: 256private:
257 void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
258
258 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; 259 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
259 SharedPtr<Kernel::ServerSession> server_session; 260 SharedPtr<Kernel::ServerSession> server_session;
260 // TODO(yuriks): Check common usage of this and optimize size accordingly 261 // TODO(yuriks): Check common usage of this and optimize size accordingly
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index bd680adfe..4b6b32dd5 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -118,7 +118,6 @@ struct KernelCore::Impl {
118 process_list.clear(); 118 process_list.clear();
119 current_process = nullptr; 119 current_process = nullptr;
120 120
121 handle_table.Clear();
122 resource_limits.fill(nullptr); 121 resource_limits.fill(nullptr);
123 122
124 thread_wakeup_callback_handle_table.Clear(); 123 thread_wakeup_callback_handle_table.Clear();
@@ -209,7 +208,6 @@ struct KernelCore::Impl {
209 std::vector<SharedPtr<Process>> process_list; 208 std::vector<SharedPtr<Process>> process_list;
210 Process* current_process = nullptr; 209 Process* current_process = nullptr;
211 210
212 Kernel::HandleTable handle_table;
213 std::array<SharedPtr<ResourceLimit>, 4> resource_limits; 211 std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
214 212
215 /// The event type of the generic timer callback event 213 /// The event type of the generic timer callback event
@@ -241,14 +239,6 @@ void KernelCore::Shutdown() {
241 impl->Shutdown(); 239 impl->Shutdown();
242} 240}
243 241
244Kernel::HandleTable& KernelCore::HandleTable() {
245 return impl->handle_table;
246}
247
248const Kernel::HandleTable& KernelCore::HandleTable() const {
249 return impl->handle_table;
250}
251
252SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory( 242SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory(
253 ResourceLimitCategory category) const { 243 ResourceLimitCategory category) const {
254 return impl->resource_limits.at(static_cast<std::size_t>(category)); 244 return impl->resource_limits.at(static_cast<std::size_t>(category));
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 41554821f..7f822d524 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -47,12 +47,6 @@ public:
47 /// Clears all resources in use by the kernel instance. 47 /// Clears all resources in use by the kernel instance.
48 void Shutdown(); 48 void Shutdown();
49 49
50 /// Provides a reference to the handle table.
51 Kernel::HandleTable& HandleTable();
52
53 /// Provides a const reference to the handle table.
54 const Kernel::HandleTable& HandleTable() const;
55
56 /// Retrieves a shared pointer to a ResourceLimit identified by the given category. 50 /// Retrieves a shared pointer to a ResourceLimit identified by the given category.
57 SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const; 51 SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const;
58 52
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index f2816943a..148478488 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -13,6 +13,7 @@
13#include <boost/container/static_vector.hpp> 13#include <boost/container/static_vector.hpp>
14#include "common/bit_field.h" 14#include "common/bit_field.h"
15#include "common/common_types.h" 15#include "common/common_types.h"
16#include "core/hle/kernel/handle_table.h"
16#include "core/hle/kernel/object.h" 17#include "core/hle/kernel/object.h"
17#include "core/hle/kernel/thread.h" 18#include "core/hle/kernel/thread.h"
18#include "core/hle/kernel/vm_manager.h" 19#include "core/hle/kernel/vm_manager.h"
@@ -142,6 +143,16 @@ public:
142 return vm_manager; 143 return vm_manager;
143 } 144 }
144 145
146 /// Gets a reference to the process' handle table.
147 HandleTable& GetHandleTable() {
148 return handle_table;
149 }
150
151 /// Gets a const reference to the process' handle table.
152 const HandleTable& GetHandleTable() const {
153 return handle_table;
154 }
155
145 /// Gets the current status of the process 156 /// Gets the current status of the process
146 ProcessStatus GetStatus() const { 157 ProcessStatus GetStatus() const {
147 return status; 158 return status;
@@ -294,6 +305,9 @@ private:
294 /// specified by metadata provided to the process during loading. 305 /// specified by metadata provided to the process during loading.
295 bool is_64bit_process = true; 306 bool is_64bit_process = true;
296 307
308 /// Per-process handle table for storing created object handles in.
309 HandleTable handle_table;
310
297 std::string name; 311 std::string name;
298}; 312};
299 313
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 1ece691c7..5fc320403 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -107,8 +107,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
107 // similar. 107 // similar.
108 Kernel::HLERequestContext context(this); 108 Kernel::HLERequestContext context(this);
109 u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); 109 u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
110 context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(), 110 context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
111 kernel.HandleTable());
112 111
113 ResultCode result = RESULT_SUCCESS; 112 ResultCode result = RESULT_SUCCESS;
114 // If the session has been converted to a domain, handle the domain request 113 // If the session has been converted to a domain, handle the domain request
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 3e5f11f2b..67ea67666 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -8,6 +8,7 @@
8#include <mutex> 8#include <mutex>
9#include <vector> 9#include <vector>
10 10
11#include "common/alignment.h"
11#include "common/assert.h" 12#include "common/assert.h"
12#include "common/logging/log.h" 13#include "common/logging/log.h"
13#include "common/microprofile.h" 14#include "common/microprofile.h"
@@ -36,9 +37,6 @@
36 37
37namespace Kernel { 38namespace Kernel {
38namespace { 39namespace {
39constexpr bool Is4KBAligned(VAddr address) {
40 return (address & 0xFFF) == 0;
41}
42 40
43// Checks if address + size is greater than the given address 41// Checks if address + size is greater than the given address
44// This can return false if the size causes an overflow of a 64-bit type 42// This can return false if the size causes an overflow of a 64-bit type
@@ -69,11 +67,11 @@ bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
69// in the same order. 67// in the same order.
70ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr, 68ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr,
71 u64 size) { 69 u64 size) {
72 if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { 70 if (!Common::Is4KBAligned(dst_addr) || !Common::Is4KBAligned(src_addr)) {
73 return ERR_INVALID_ADDRESS; 71 return ERR_INVALID_ADDRESS;
74 } 72 }
75 73
76 if (size == 0 || !Is4KBAligned(size)) { 74 if (size == 0 || !Common::Is4KBAligned(size)) {
77 return ERR_INVALID_SIZE; 75 return ERR_INVALID_SIZE;
78 } 76 }
79 77
@@ -191,14 +189,15 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
191 CASCADE_RESULT(client_session, client_port->Connect()); 189 CASCADE_RESULT(client_session, client_port->Connect());
192 190
193 // Return the client session 191 // Return the client session
194 CASCADE_RESULT(*out_handle, kernel.HandleTable().Create(client_session)); 192 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
193 CASCADE_RESULT(*out_handle, handle_table.Create(client_session));
195 return RESULT_SUCCESS; 194 return RESULT_SUCCESS;
196} 195}
197 196
198/// Makes a blocking IPC call to an OS service. 197/// Makes a blocking IPC call to an OS service.
199static ResultCode SendSyncRequest(Handle handle) { 198static ResultCode SendSyncRequest(Handle handle) {
200 auto& kernel = Core::System::GetInstance().Kernel(); 199 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
201 SharedPtr<ClientSession> session = kernel.HandleTable().Get<ClientSession>(handle); 200 SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle);
202 if (!session) { 201 if (!session) {
203 LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); 202 LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle);
204 return ERR_INVALID_HANDLE; 203 return ERR_INVALID_HANDLE;
@@ -217,8 +216,8 @@ static ResultCode SendSyncRequest(Handle handle) {
217static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { 216static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
218 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); 217 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
219 218
220 auto& kernel = Core::System::GetInstance().Kernel(); 219 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
221 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 220 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
222 if (!thread) { 221 if (!thread) {
223 return ERR_INVALID_HANDLE; 222 return ERR_INVALID_HANDLE;
224 } 223 }
@@ -231,8 +230,8 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
231static ResultCode GetProcessId(u32* process_id, Handle process_handle) { 230static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
232 LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); 231 LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle);
233 232
234 auto& kernel = Core::System::GetInstance().Kernel(); 233 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
235 const SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle); 234 const SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
236 if (!process) { 235 if (!process) {
237 return ERR_INVALID_HANDLE; 236 return ERR_INVALID_HANDLE;
238 } 237 }
@@ -275,11 +274,11 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
275 274
276 using ObjectPtr = Thread::ThreadWaitObjects::value_type; 275 using ObjectPtr = Thread::ThreadWaitObjects::value_type;
277 Thread::ThreadWaitObjects objects(handle_count); 276 Thread::ThreadWaitObjects objects(handle_count);
278 auto& kernel = Core::System::GetInstance().Kernel(); 277 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
279 278
280 for (u64 i = 0; i < handle_count; ++i) { 279 for (u64 i = 0; i < handle_count; ++i) {
281 const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); 280 const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle));
282 const auto object = kernel.HandleTable().Get<WaitObject>(handle); 281 const auto object = handle_table.Get<WaitObject>(handle);
283 282
284 if (object == nullptr) { 283 if (object == nullptr) {
285 return ERR_INVALID_HANDLE; 284 return ERR_INVALID_HANDLE;
@@ -327,8 +326,8 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
327static ResultCode CancelSynchronization(Handle thread_handle) { 326static ResultCode CancelSynchronization(Handle thread_handle) {
328 LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); 327 LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
329 328
330 auto& kernel = Core::System::GetInstance().Kernel(); 329 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
331 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 330 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
332 if (!thread) { 331 if (!thread) {
333 return ERR_INVALID_HANDLE; 332 return ERR_INVALID_HANDLE;
334 } 333 }
@@ -352,7 +351,11 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
352 return ERR_INVALID_ADDRESS_STATE; 351 return ERR_INVALID_ADDRESS_STATE;
353 } 352 }
354 353
355 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); 354 if (!Common::IsWordAligned(mutex_addr)) {
355 return ERR_INVALID_ADDRESS;
356 }
357
358 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
356 return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, 359 return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle,
357 requesting_thread_handle); 360 requesting_thread_handle);
358} 361}
@@ -365,6 +368,10 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
365 return ERR_INVALID_ADDRESS_STATE; 368 return ERR_INVALID_ADDRESS_STATE;
366 } 369 }
367 370
371 if (!Common::IsWordAligned(mutex_addr)) {
372 return ERR_INVALID_ADDRESS;
373 }
374
368 return Mutex::Release(mutex_addr); 375 return Mutex::Release(mutex_addr);
369} 376}
370 377
@@ -448,25 +455,12 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
448 case GetInfoType::RandomEntropy: 455 case GetInfoType::RandomEntropy:
449 *result = 0; 456 *result = 0;
450 break; 457 break;
451 case GetInfoType::AddressSpaceBaseAddr: 458 case GetInfoType::ASLRRegionBaseAddr:
452 *result = vm_manager.GetCodeRegionBaseAddress(); 459 *result = vm_manager.GetASLRRegionBaseAddress();
453 break; 460 break;
454 case GetInfoType::AddressSpaceSize: { 461 case GetInfoType::ASLRRegionSize:
455 const u64 width = vm_manager.GetAddressSpaceWidth(); 462 *result = vm_manager.GetASLRRegionSize();
456
457 switch (width) {
458 case 32:
459 *result = 0xFFE00000;
460 break;
461 case 36:
462 *result = 0xFF8000000;
463 break;
464 case 39:
465 *result = 0x7FF8000000;
466 break;
467 }
468 break; 463 break;
469 }
470 case GetInfoType::NewMapRegionBaseAddr: 464 case GetInfoType::NewMapRegionBaseAddr:
471 *result = vm_manager.GetNewMapRegionBaseAddress(); 465 *result = vm_manager.GetNewMapRegionBaseAddress();
472 break; 466 break;
@@ -506,13 +500,12 @@ static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
506static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { 500static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
507 LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); 501 LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle);
508 502
509 auto& kernel = Core::System::GetInstance().Kernel(); 503 const auto* current_process = Core::CurrentProcess();
510 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); 504 const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
511 if (!thread) { 505 if (!thread) {
512 return ERR_INVALID_HANDLE; 506 return ERR_INVALID_HANDLE;
513 } 507 }
514 508
515 const auto* current_process = Core::CurrentProcess();
516 if (thread->GetOwnerProcess() != current_process) { 509 if (thread->GetOwnerProcess() != current_process) {
517 return ERR_INVALID_HANDLE; 510 return ERR_INVALID_HANDLE;
518 } 511 }
@@ -538,10 +531,11 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
538 531
539/// Gets the priority for the specified thread 532/// Gets the priority for the specified thread
540static ResultCode GetThreadPriority(u32* priority, Handle handle) { 533static ResultCode GetThreadPriority(u32* priority, Handle handle) {
541 auto& kernel = Core::System::GetInstance().Kernel(); 534 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
542 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); 535 const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle);
543 if (!thread) 536 if (!thread) {
544 return ERR_INVALID_HANDLE; 537 return ERR_INVALID_HANDLE;
538 }
545 539
546 *priority = thread->GetPriority(); 540 *priority = thread->GetPriority();
547 return RESULT_SUCCESS; 541 return RESULT_SUCCESS;
@@ -553,14 +547,15 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
553 return ERR_INVALID_THREAD_PRIORITY; 547 return ERR_INVALID_THREAD_PRIORITY;
554 } 548 }
555 549
556 auto& kernel = Core::System::GetInstance().Kernel(); 550 const auto* const current_process = Core::CurrentProcess();
557 SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); 551 SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
558 if (!thread) 552 if (!thread) {
559 return ERR_INVALID_HANDLE; 553 return ERR_INVALID_HANDLE;
554 }
560 555
561 // Note: The kernel uses the current process's resource limit instead of 556 // Note: The kernel uses the current process's resource limit instead of
562 // the one from the thread owner's resource limit. 557 // the one from the thread owner's resource limit.
563 const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); 558 const ResourceLimit& resource_limit = current_process->GetResourceLimit();
564 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { 559 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
565 return ERR_NOT_AUTHORIZED; 560 return ERR_NOT_AUTHORIZED;
566 } 561 }
@@ -583,14 +578,18 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
583 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", 578 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
584 shared_memory_handle, addr, size, permissions); 579 shared_memory_handle, addr, size, permissions);
585 580
586 if (!Is4KBAligned(addr)) { 581 if (!Common::Is4KBAligned(addr)) {
587 return ERR_INVALID_ADDRESS; 582 return ERR_INVALID_ADDRESS;
588 } 583 }
589 584
590 if (size == 0 || !Is4KBAligned(size)) { 585 if (size == 0 || !Common::Is4KBAligned(size)) {
591 return ERR_INVALID_SIZE; 586 return ERR_INVALID_SIZE;
592 } 587 }
593 588
589 if (!IsValidAddressRange(addr, size)) {
590 return ERR_INVALID_ADDRESS_STATE;
591 }
592
594 const auto permissions_type = static_cast<MemoryPermission>(permissions); 593 const auto permissions_type = static_cast<MemoryPermission>(permissions);
595 if (permissions_type != MemoryPermission::Read && 594 if (permissions_type != MemoryPermission::Read &&
596 permissions_type != MemoryPermission::ReadWrite) { 595 permissions_type != MemoryPermission::ReadWrite) {
@@ -598,46 +597,61 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
598 return ERR_INVALID_MEMORY_PERMISSIONS; 597 return ERR_INVALID_MEMORY_PERMISSIONS;
599 } 598 }
600 599
601 auto& kernel = Core::System::GetInstance().Kernel(); 600 auto* const current_process = Core::CurrentProcess();
602 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); 601 auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
603 if (!shared_memory) { 602 if (!shared_memory) {
604 return ERR_INVALID_HANDLE; 603 return ERR_INVALID_HANDLE;
605 } 604 }
606 605
607 return shared_memory->Map(Core::CurrentProcess(), addr, permissions_type, 606 const auto& vm_manager = current_process->VMManager();
608 MemoryPermission::DontCare); 607 if (!vm_manager.IsWithinASLRRegion(addr, size)) {
608 return ERR_INVALID_MEMORY_RANGE;
609 }
610
611 return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare);
609} 612}
610 613
611static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { 614static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
612 LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", 615 LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}",
613 shared_memory_handle, addr, size); 616 shared_memory_handle, addr, size);
614 617
615 if (!Is4KBAligned(addr)) { 618 if (!Common::Is4KBAligned(addr)) {
616 return ERR_INVALID_ADDRESS; 619 return ERR_INVALID_ADDRESS;
617 } 620 }
618 621
619 if (size == 0 || !Is4KBAligned(size)) { 622 if (size == 0 || !Common::Is4KBAligned(size)) {
620 return ERR_INVALID_SIZE; 623 return ERR_INVALID_SIZE;
621 } 624 }
622 625
623 auto& kernel = Core::System::GetInstance().Kernel(); 626 if (!IsValidAddressRange(addr, size)) {
624 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); 627 return ERR_INVALID_ADDRESS_STATE;
628 }
629
630 auto* const current_process = Core::CurrentProcess();
631 auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
632 if (!shared_memory) {
633 return ERR_INVALID_HANDLE;
634 }
635
636 const auto& vm_manager = current_process->VMManager();
637 if (!vm_manager.IsWithinASLRRegion(addr, size)) {
638 return ERR_INVALID_MEMORY_RANGE;
639 }
625 640
626 return shared_memory->Unmap(Core::CurrentProcess(), addr); 641 return shared_memory->Unmap(current_process, addr);
627} 642}
628 643
629/// Query process memory 644/// Query process memory
630static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, 645static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/,
631 Handle process_handle, u64 addr) { 646 Handle process_handle, u64 addr) {
632 647 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
633 auto& kernel = Core::System::GetInstance().Kernel(); 648 SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
634 SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle);
635 if (!process) { 649 if (!process) {
636 return ERR_INVALID_HANDLE; 650 return ERR_INVALID_HANDLE;
637 } 651 }
638 auto vma = process->VMManager().FindVMA(addr); 652 auto vma = process->VMManager().FindVMA(addr);
639 memory_info->attributes = 0; 653 memory_info->attributes = 0;
640 if (vma == Core::CurrentProcess()->VMManager().vma_map.end()) { 654 if (vma == process->VMManager().vma_map.end()) {
641 memory_info->base_address = 0; 655 memory_info->base_address = 0;
642 memory_info->permission = static_cast<u32>(VMAPermission::None); 656 memory_info->permission = static_cast<u32>(VMAPermission::None);
643 memory_info->size = 0; 657 memory_info->size = 0;
@@ -678,20 +692,19 @@ static void ExitProcess() {
678/// Creates a new thread 692/// Creates a new thread
679static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, 693static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top,
680 u32 priority, s32 processor_id) { 694 u32 priority, s32 processor_id) {
681 std::string name = fmt::format("thread-{:X}", entry_point);
682
683 if (priority > THREADPRIO_LOWEST) { 695 if (priority > THREADPRIO_LOWEST) {
684 return ERR_INVALID_THREAD_PRIORITY; 696 return ERR_INVALID_THREAD_PRIORITY;
685 } 697 }
686 698
687 const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); 699 auto* const current_process = Core::CurrentProcess();
700 const ResourceLimit& resource_limit = current_process->GetResourceLimit();
688 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { 701 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
689 return ERR_NOT_AUTHORIZED; 702 return ERR_NOT_AUTHORIZED;
690 } 703 }
691 704
692 if (processor_id == THREADPROCESSORID_DEFAULT) { 705 if (processor_id == THREADPROCESSORID_DEFAULT) {
693 // Set the target CPU to the one specified in the process' exheader. 706 // Set the target CPU to the one specified in the process' exheader.
694 processor_id = Core::CurrentProcess()->GetDefaultProcessorID(); 707 processor_id = current_process->GetDefaultProcessorID();
695 ASSERT(processor_id != THREADPROCESSORID_DEFAULT); 708 ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
696 } 709 }
697 710
@@ -706,11 +719,13 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
706 return ERR_INVALID_PROCESSOR_ID; 719 return ERR_INVALID_PROCESSOR_ID;
707 } 720 }
708 721
722 const std::string name = fmt::format("thread-{:X}", entry_point);
709 auto& kernel = Core::System::GetInstance().Kernel(); 723 auto& kernel = Core::System::GetInstance().Kernel();
710 CASCADE_RESULT(SharedPtr<Thread> thread, 724 CASCADE_RESULT(SharedPtr<Thread> thread,
711 Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, 725 Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top,
712 *Core::CurrentProcess())); 726 *current_process));
713 const auto new_guest_handle = kernel.HandleTable().Create(thread); 727
728 const auto new_guest_handle = current_process->GetHandleTable().Create(thread);
714 if (new_guest_handle.Failed()) { 729 if (new_guest_handle.Failed()) {
715 return new_guest_handle.Code(); 730 return new_guest_handle.Code();
716 } 731 }
@@ -731,8 +746,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
731static ResultCode StartThread(Handle thread_handle) { 746static ResultCode StartThread(Handle thread_handle) {
732 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); 747 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
733 748
734 auto& kernel = Core::System::GetInstance().Kernel(); 749 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
735 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 750 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
736 if (!thread) { 751 if (!thread) {
737 return ERR_INVALID_HANDLE; 752 return ERR_INVALID_HANDLE;
738 } 753 }
@@ -779,8 +794,8 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
779 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", 794 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
780 mutex_addr, condition_variable_addr, thread_handle, nano_seconds); 795 mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
781 796
782 auto& kernel = Core::System::GetInstance().Kernel(); 797 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
783 SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 798 SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
784 ASSERT(thread); 799 ASSERT(thread);
785 800
786 CASCADE_CODE(Mutex::Release(mutex_addr)); 801 CASCADE_CODE(Mutex::Release(mutex_addr));
@@ -891,9 +906,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
891 mutex_val | Mutex::MutexHasWaitersFlag)); 906 mutex_val | Mutex::MutexHasWaitersFlag));
892 907
893 // The mutex is already owned by some other thread, make this thread wait on it. 908 // The mutex is already owned by some other thread, make this thread wait on it.
894 auto& kernel = Core::System::GetInstance().Kernel(); 909 const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
895 Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); 910 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
896 auto owner = kernel.HandleTable().Get<Thread>(owner_handle); 911 auto owner = handle_table.Get<Thread>(owner_handle);
897 ASSERT(owner); 912 ASSERT(owner);
898 ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); 913 ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
899 thread->InvalidateWakeupCallback(); 914 thread->InvalidateWakeupCallback();
@@ -972,16 +987,16 @@ static u64 GetSystemTick() {
972static ResultCode CloseHandle(Handle handle) { 987static ResultCode CloseHandle(Handle handle) {
973 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); 988 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
974 989
975 auto& kernel = Core::System::GetInstance().Kernel(); 990 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
976 return kernel.HandleTable().Close(handle); 991 return handle_table.Close(handle);
977} 992}
978 993
979/// Reset an event 994/// Reset an event
980static ResultCode ResetSignal(Handle handle) { 995static ResultCode ResetSignal(Handle handle) {
981 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); 996 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle);
982 997
983 auto& kernel = Core::System::GetInstance().Kernel(); 998 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
984 auto event = kernel.HandleTable().Get<Event>(handle); 999 auto event = handle_table.Get<Event>(handle);
985 1000
986 ASSERT(event != nullptr); 1001 ASSERT(event != nullptr);
987 1002
@@ -1000,8 +1015,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
1000static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { 1015static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) {
1001 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); 1016 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
1002 1017
1003 auto& kernel = Core::System::GetInstance().Kernel(); 1018 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1004 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 1019 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1005 if (!thread) { 1020 if (!thread) {
1006 return ERR_INVALID_HANDLE; 1021 return ERR_INVALID_HANDLE;
1007 } 1022 }
@@ -1016,8 +1031,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
1016 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle, 1031 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle,
1017 mask, core); 1032 mask, core);
1018 1033
1019 auto& kernel = Core::System::GetInstance().Kernel(); 1034 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1020 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 1035 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1021 if (!thread) { 1036 if (!thread) {
1022 return ERR_INVALID_HANDLE; 1037 return ERR_INVALID_HANDLE;
1023 } 1038 }
@@ -1078,7 +1093,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
1078 } 1093 }
1079 1094
1080 auto& kernel = Core::System::GetInstance().Kernel(); 1095 auto& kernel = Core::System::GetInstance().Kernel();
1081 auto& handle_table = kernel.HandleTable(); 1096 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1082 auto shared_mem_handle = 1097 auto shared_mem_handle =
1083 SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, 1098 SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size,
1084 local_perms, remote_perms); 1099 local_perms, remote_perms);
@@ -1090,10 +1105,12 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
1090static ResultCode ClearEvent(Handle handle) { 1105static ResultCode ClearEvent(Handle handle) {
1091 LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); 1106 LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
1092 1107
1093 auto& kernel = Core::System::GetInstance().Kernel(); 1108 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1094 SharedPtr<Event> evt = kernel.HandleTable().Get<Event>(handle); 1109 SharedPtr<Event> evt = handle_table.Get<Event>(handle);
1095 if (evt == nullptr) 1110 if (evt == nullptr) {
1096 return ERR_INVALID_HANDLE; 1111 return ERR_INVALID_HANDLE;
1112 }
1113
1097 evt->Clear(); 1114 evt->Clear();
1098 return RESULT_SUCCESS; 1115 return RESULT_SUCCESS;
1099} 1116}
@@ -1106,8 +1123,8 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
1106 Status, 1123 Status,
1107 }; 1124 };
1108 1125
1109 const auto& kernel = Core::System::GetInstance().Kernel(); 1126 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1110 const auto process = kernel.HandleTable().Get<Process>(process_handle); 1127 const auto process = handle_table.Get<Process>(process_handle);
1111 if (!process) { 1128 if (!process) {
1112 return ERR_INVALID_HANDLE; 1129 return ERR_INVALID_HANDLE;
1113 } 1130 }
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h
index 70148c4fe..554a5e328 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -41,8 +41,8 @@ enum class GetInfoType : u64 {
41 RandomEntropy = 11, 41 RandomEntropy = 11,
42 PerformanceCounter = 0xF0000002, 42 PerformanceCounter = 0xF0000002,
43 // 2.0.0+ 43 // 2.0.0+
44 AddressSpaceBaseAddr = 12, 44 ASLRRegionBaseAddr = 12,
45 AddressSpaceSize = 13, 45 ASLRRegionSize = 13,
46 NewMapRegionBaseAddr = 14, 46 NewMapRegionBaseAddr = 14,
47 NewMapRegionSize = 15, 47 NewMapRegionSize = 15,
48 // 3.0.0+ 48 // 3.0.0+
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 35ec98c1a..59bc9e0af 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -266,7 +266,7 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri
266 SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); 266 SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
267 267
268 // Register 1 must be a handle to the main thread 268 // Register 1 must be a handle to the main thread
269 const Handle guest_handle = kernel.HandleTable().Create(thread).Unwrap(); 269 const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
270 thread->SetGuestHandle(guest_handle); 270 thread->SetGuestHandle(guest_handle);
271 thread->GetContext().cpu_registers[1] = guest_handle; 271 thread->GetContext().cpu_registers[1] = guest_handle;
272 272
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index e412309fd..e1a34eef1 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -393,30 +393,35 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
393 393
394 switch (type) { 394 switch (type) {
395 case FileSys::ProgramAddressSpaceType::Is32Bit: 395 case FileSys::ProgramAddressSpaceType::Is32Bit:
396 case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
396 address_space_width = 32; 397 address_space_width = 32;
397 code_region_base = 0x200000; 398 code_region_base = 0x200000;
398 code_region_end = code_region_base + 0x3FE00000; 399 code_region_end = code_region_base + 0x3FE00000;
399 map_region_size = 0x40000000; 400 aslr_region_base = 0x200000;
400 heap_region_size = 0x40000000; 401 aslr_region_end = aslr_region_base + 0xFFE00000;
402 if (type == FileSys::ProgramAddressSpaceType::Is32Bit) {
403 map_region_size = 0x40000000;
404 heap_region_size = 0x40000000;
405 } else {
406 map_region_size = 0;
407 heap_region_size = 0x80000000;
408 }
401 break; 409 break;
402 case FileSys::ProgramAddressSpaceType::Is36Bit: 410 case FileSys::ProgramAddressSpaceType::Is36Bit:
403 address_space_width = 36; 411 address_space_width = 36;
404 code_region_base = 0x8000000; 412 code_region_base = 0x8000000;
405 code_region_end = code_region_base + 0x78000000; 413 code_region_end = code_region_base + 0x78000000;
414 aslr_region_base = 0x8000000;
415 aslr_region_end = aslr_region_base + 0xFF8000000;
406 map_region_size = 0x180000000; 416 map_region_size = 0x180000000;
407 heap_region_size = 0x180000000; 417 heap_region_size = 0x180000000;
408 break; 418 break;
409 case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
410 address_space_width = 32;
411 code_region_base = 0x200000;
412 code_region_end = code_region_base + 0x3FE00000;
413 map_region_size = 0;
414 heap_region_size = 0x80000000;
415 break;
416 case FileSys::ProgramAddressSpaceType::Is39Bit: 419 case FileSys::ProgramAddressSpaceType::Is39Bit:
417 address_space_width = 39; 420 address_space_width = 39;
418 code_region_base = 0x8000000; 421 code_region_base = 0x8000000;
419 code_region_end = code_region_base + 0x80000000; 422 code_region_end = code_region_base + 0x80000000;
423 aslr_region_base = 0x8000000;
424 aslr_region_end = aslr_region_base + 0x7FF8000000;
420 map_region_size = 0x1000000000; 425 map_region_size = 0x1000000000;
421 heap_region_size = 0x180000000; 426 heap_region_size = 0x180000000;
422 new_map_region_size = 0x80000000; 427 new_map_region_size = 0x80000000;
@@ -490,6 +495,38 @@ u64 VMManager::GetAddressSpaceWidth() const {
490 return address_space_width; 495 return address_space_width;
491} 496}
492 497
498VAddr VMManager::GetASLRRegionBaseAddress() const {
499 return aslr_region_base;
500}
501
502VAddr VMManager::GetASLRRegionEndAddress() const {
503 return aslr_region_end;
504}
505
506u64 VMManager::GetASLRRegionSize() const {
507 return aslr_region_end - aslr_region_base;
508}
509
510bool VMManager::IsWithinASLRRegion(VAddr begin, u64 size) const {
511 const VAddr range_end = begin + size;
512 const VAddr aslr_start = GetASLRRegionBaseAddress();
513 const VAddr aslr_end = GetASLRRegionEndAddress();
514
515 if (aslr_start > begin || begin > range_end || range_end - 1 > aslr_end - 1) {
516 return false;
517 }
518
519 if (range_end > heap_region_base && heap_region_end > begin) {
520 return false;
521 }
522
523 if (range_end > map_region_base && map_region_end > begin) {
524 return false;
525 }
526
527 return true;
528}
529
493VAddr VMManager::GetCodeRegionBaseAddress() const { 530VAddr VMManager::GetCodeRegionBaseAddress() const {
494 return code_region_base; 531 return code_region_base;
495} 532}
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 015559a64..84c890224 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -205,6 +205,18 @@ public:
205 /// Gets the address space width in bits. 205 /// Gets the address space width in bits.
206 u64 GetAddressSpaceWidth() const; 206 u64 GetAddressSpaceWidth() const;
207 207
208 /// Gets the base address of the ASLR region.
209 VAddr GetASLRRegionBaseAddress() const;
210
211 /// Gets the end address of the ASLR region.
212 VAddr GetASLRRegionEndAddress() const;
213
214 /// Determines whether or not the specified address range is within the ASLR region.
215 bool IsWithinASLRRegion(VAddr address, u64 size) const;
216
217 /// Gets the size of the ASLR region
218 u64 GetASLRRegionSize() const;
219
208 /// Gets the base address of the code region. 220 /// Gets the base address of the code region.
209 VAddr GetCodeRegionBaseAddress() const; 221 VAddr GetCodeRegionBaseAddress() const;
210 222
@@ -306,6 +318,9 @@ private:
306 VAddr address_space_base = 0; 318 VAddr address_space_base = 0;
307 VAddr address_space_end = 0; 319 VAddr address_space_end = 0;
308 320
321 VAddr aslr_region_base = 0;
322 VAddr aslr_region_end = 0;
323
309 VAddr code_region_base = 0; 324 VAddr code_region_base = 0;
310 VAddr code_region_end = 0; 325 VAddr code_region_end = 0;
311 326
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 69bfce1c1..ecf72ae24 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -15,6 +15,7 @@
15#include "core/hle/service/am/idle.h" 15#include "core/hle/service/am/idle.h"
16#include "core/hle/service/am/omm.h" 16#include "core/hle/service/am/omm.h"
17#include "core/hle/service/am/spsm.h" 17#include "core/hle/service/am/spsm.h"
18#include "core/hle/service/am/tcap.h"
18#include "core/hle/service/apm/apm.h" 19#include "core/hle/service/apm/apm.h"
19#include "core/hle/service/filesystem/filesystem.h" 20#include "core/hle/service/filesystem/filesystem.h"
20#include "core/hle/service/nvflinger/nvflinger.h" 21#include "core/hle/service/nvflinger/nvflinger.h"
@@ -26,13 +27,18 @@
26namespace Service::AM { 27namespace Service::AM {
27 28
28IWindowController::IWindowController() : ServiceFramework("IWindowController") { 29IWindowController::IWindowController() : ServiceFramework("IWindowController") {
30 // clang-format off
29 static const FunctionInfo functions[] = { 31 static const FunctionInfo functions[] = {
30 {0, nullptr, "CreateWindow"}, 32 {0, nullptr, "CreateWindow"},
31 {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, 33 {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
32 {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, 34 {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
33 {11, nullptr, "ReleaseForegroundRights"}, 35 {11, nullptr, "ReleaseForegroundRights"},
34 {12, nullptr, "RejectToChangeIntoBackground"}, 36 {12, nullptr, "RejectToChangeIntoBackground"},
37 {20, nullptr, "SetAppletWindowVisibility"},
38 {21, nullptr, "SetAppletGpuTimeSlice"},
35 }; 39 };
40 // clang-format on
41
36 RegisterHandlers(functions); 42 RegisterHandlers(functions);
37} 43}
38 44
@@ -87,6 +93,7 @@ void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestCo
87} 93}
88 94
89IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") { 95IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {
96 // clang-format off
90 static const FunctionInfo functions[] = { 97 static const FunctionInfo functions[] = {
91 {0, nullptr, "GetLastForegroundCaptureImage"}, 98 {0, nullptr, "GetLastForegroundCaptureImage"},
92 {1, nullptr, "UpdateLastForegroundCaptureImage"}, 99 {1, nullptr, "UpdateLastForegroundCaptureImage"},
@@ -117,7 +124,11 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
117 {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, 124 {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"},
118 {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"}, 125 {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"},
119 {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"}, 126 {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"},
127 // 6.0.0+
128 {28, nullptr, "TakeScreenShotOfOwnLayerEx"},
120 }; 129 };
130 // clang-format on
131
121 RegisterHandlers(functions); 132 RegisterHandlers(functions);
122} 133}
123 134
@@ -128,6 +139,7 @@ IDebugFunctions::~IDebugFunctions() = default;
128 139
129ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 140ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
130 : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) { 141 : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) {
142 // clang-format off
131 static const FunctionInfo functions[] = { 143 static const FunctionInfo functions[] = {
132 {0, nullptr, "Exit"}, 144 {0, nullptr, "Exit"},
133 {1, &ISelfController::LockExit, "LockExit"}, 145 {1, &ISelfController::LockExit, "LockExit"},
@@ -136,10 +148,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
136 {4, nullptr, "LeaveFatalSection"}, 148 {4, nullptr, "LeaveFatalSection"},
137 {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"}, 149 {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
138 {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"}, 150 {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
139 {11, &ISelfController::SetOperationModeChangedNotification, 151 {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"},
140 "SetOperationModeChangedNotification"}, 152 {12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"},
141 {12, &ISelfController::SetPerformanceModeChangedNotification,
142 "SetPerformanceModeChangedNotification"},
143 {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"}, 153 {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"},
144 {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"}, 154 {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"},
145 {15, nullptr, "SetScreenShotAppletIdentityInfo"}, 155 {15, nullptr, "SetScreenShotAppletIdentityInfo"},
@@ -165,7 +175,12 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
165 {69, nullptr, "IsAutoSleepDisabled"}, 175 {69, nullptr, "IsAutoSleepDisabled"},
166 {70, nullptr, "ReportMultimediaError"}, 176 {70, nullptr, "ReportMultimediaError"},
167 {80, nullptr, "SetWirelessPriorityMode"}, 177 {80, nullptr, "SetWirelessPriorityMode"},
178 {90, nullptr, "GetAccumulatedSuspendedTickValue"},
179 {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"},
180 {1000, nullptr, "GetDebugStorageChannel"},
168 }; 181 };
182 // clang-format on
183
169 RegisterHandlers(functions); 184 RegisterHandlers(functions);
170 185
171 auto& kernel = Core::System::GetInstance().Kernel(); 186 auto& kernel = Core::System::GetInstance().Kernel();
@@ -312,6 +327,7 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
312} 327}
313 328
314ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { 329ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") {
330 // clang-format off
315 static const FunctionInfo functions[] = { 331 static const FunctionInfo functions[] = {
316 {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, 332 {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
317 {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"}, 333 {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"},
@@ -336,11 +352,12 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter"
336 {52, nullptr, "SwitchLcdBacklight"}, 352 {52, nullptr, "SwitchLcdBacklight"},
337 {55, nullptr, "IsInControllerFirmwareUpdateSection"}, 353 {55, nullptr, "IsInControllerFirmwareUpdateSection"},
338 {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, 354 {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
339 {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, 355 {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
340 "GetDefaultDisplayResolutionChangeEvent"},
341 {62, nullptr, "GetHdcpAuthenticationState"}, 356 {62, nullptr, "GetHdcpAuthenticationState"},
342 {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, 357 {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"},
343 }; 358 };
359 // clang-format on
360
344 RegisterHandlers(functions); 361 RegisterHandlers(functions);
345 362
346 auto& kernel = Core::System::GetInstance().Kernel(); 363 auto& kernel = Core::System::GetInstance().Kernel();
@@ -432,11 +449,14 @@ class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
432public: 449public:
433 explicit IStorageAccessor(std::vector<u8> buffer) 450 explicit IStorageAccessor(std::vector<u8> buffer)
434 : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) { 451 : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
452 // clang-format off
435 static const FunctionInfo functions[] = { 453 static const FunctionInfo functions[] = {
436 {0, &IStorageAccessor::GetSize, "GetSize"}, 454 {0, &IStorageAccessor::GetSize, "GetSize"},
437 {10, &IStorageAccessor::Write, "Write"}, 455 {10, &IStorageAccessor::Write, "Write"},
438 {11, &IStorageAccessor::Read, "Read"}, 456 {11, &IStorageAccessor::Read, "Read"},
439 }; 457 };
458 // clang-format on
459
440 RegisterHandlers(functions); 460 RegisterHandlers(functions);
441 } 461 }
442 462
@@ -489,10 +509,13 @@ class IStorage final : public ServiceFramework<IStorage> {
489public: 509public:
490 explicit IStorage(std::vector<u8> buffer) 510 explicit IStorage(std::vector<u8> buffer)
491 : ServiceFramework("IStorage"), buffer(std::move(buffer)) { 511 : ServiceFramework("IStorage"), buffer(std::move(buffer)) {
512 // clang-format off
492 static const FunctionInfo functions[] = { 513 static const FunctionInfo functions[] = {
493 {0, &IStorage::Open, "Open"}, 514 {0, &IStorage::Open, "Open"},
494 {1, nullptr, "OpenTransferStorage"}, 515 {1, nullptr, "OpenTransferStorage"},
495 }; 516 };
517 // clang-format on
518
496 RegisterHandlers(functions); 519 RegisterHandlers(functions);
497 } 520 }
498 521
@@ -512,6 +535,7 @@ private:
512class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { 535class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
513public: 536public:
514 explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") { 537 explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
538 // clang-format off
515 static const FunctionInfo functions[] = { 539 static const FunctionInfo functions[] = {
516 {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, 540 {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
517 {1, nullptr, "IsCompleted"}, 541 {1, nullptr, "IsCompleted"},
@@ -532,6 +556,8 @@ public:
532 {150, nullptr, "RequestForAppletToGetForeground"}, 556 {150, nullptr, "RequestForAppletToGetForeground"},
533 {160, nullptr, "GetIndirectLayerConsumerHandle"}, 557 {160, nullptr, "GetIndirectLayerConsumerHandle"},
534 }; 558 };
559 // clang-format on
560
535 RegisterHandlers(functions); 561 RegisterHandlers(functions);
536 562
537 auto& kernel = Core::System::GetInstance().Kernel(); 563 auto& kernel = Core::System::GetInstance().Kernel();
@@ -624,13 +650,13 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
624} 650}
625 651
626IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { 652IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
653 // clang-format off
627 static const FunctionInfo functions[] = { 654 static const FunctionInfo functions[] = {
628 {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, 655 {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
629 {10, nullptr, "CreateApplicationAndPushAndRequestToStart"}, 656 {10, nullptr, "CreateApplicationAndPushAndRequestToStart"},
630 {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"}, 657 {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
631 {12, nullptr, "CreateApplicationAndRequestToStart"}, 658 {12, nullptr, "CreateApplicationAndRequestToStart"},
632 {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, 659 {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"},
633 "CreateApplicationAndRequestToStartForQuest"},
634 {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, 660 {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
635 {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, 661 {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
636 {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, 662 {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
@@ -638,10 +664,10 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
638 {24, nullptr, "GetLaunchStorageInfoForDebug"}, 664 {24, nullptr, "GetLaunchStorageInfoForDebug"},
639 {25, nullptr, "ExtendSaveData"}, 665 {25, nullptr, "ExtendSaveData"},
640 {26, nullptr, "GetSaveDataSize"}, 666 {26, nullptr, "GetSaveDataSize"},
641 {30, nullptr, "BeginBlockingHomeButtonShortAndLongPressed"}, 667 {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
642 {31, nullptr, "EndBlockingHomeButtonShortAndLongPressed"}, 668 {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
643 {32, nullptr, "BeginBlockingHomeButton"}, 669 {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
644 {33, nullptr, "EndBlockingHomeButton"}, 670 {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
645 {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, 671 {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
646 {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, 672 {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
647 {60, nullptr, "SetMediaPlaybackStateForApplication"}, 673 {60, nullptr, "SetMediaPlaybackStateForApplication"},
@@ -664,11 +690,39 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
664 {1000, nullptr, "CreateMovieMaker"}, 690 {1000, nullptr, "CreateMovieMaker"},
665 {1001, nullptr, "PrepareForJit"}, 691 {1001, nullptr, "PrepareForJit"},
666 }; 692 };
693 // clang-format on
694
667 RegisterHandlers(functions); 695 RegisterHandlers(functions);
668} 696}
669 697
670IApplicationFunctions::~IApplicationFunctions() = default; 698IApplicationFunctions::~IApplicationFunctions() = default;
671 699
700void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
701 Kernel::HLERequestContext& ctx) {
702 IPC::ResponseBuilder rb{ctx, 2};
703 rb.Push(RESULT_SUCCESS);
704 LOG_WARNING(Service_AM, "(STUBBED) called");
705}
706
707void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(
708 Kernel::HLERequestContext& ctx) {
709 IPC::ResponseBuilder rb{ctx, 2};
710 rb.Push(RESULT_SUCCESS);
711 LOG_WARNING(Service_AM, "(STUBBED) called");
712}
713
714void IApplicationFunctions::BeginBlockingHomeButton(Kernel::HLERequestContext& ctx) {
715 IPC::ResponseBuilder rb{ctx, 2};
716 rb.Push(RESULT_SUCCESS);
717 LOG_WARNING(Service_AM, "(STUBBED) called");
718}
719
720void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx) {
721 IPC::ResponseBuilder rb{ctx, 2};
722 rb.Push(RESULT_SUCCESS);
723 LOG_WARNING(Service_AM, "(STUBBED) called");
724}
725
672void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { 726void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
673 constexpr std::array<u8, 0x88> data{{ 727 constexpr std::array<u8, 0x88> data{{
674 0xca, 0x97, 0x94, 0xc7, // Magic 728 0xca, 0x97, 0x94, 0xc7, // Magic
@@ -776,9 +830,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager,
776 std::make_shared<IdleSys>()->InstallAsService(service_manager); 830 std::make_shared<IdleSys>()->InstallAsService(service_manager);
777 std::make_shared<OMM>()->InstallAsService(service_manager); 831 std::make_shared<OMM>()->InstallAsService(service_manager);
778 std::make_shared<SPSM>()->InstallAsService(service_manager); 832 std::make_shared<SPSM>()->InstallAsService(service_manager);
833 std::make_shared<TCAP>()->InstallAsService(service_manager);
779} 834}
780 835
781IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") { 836IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") {
837 // clang-format off
782 static const FunctionInfo functions[] = { 838 static const FunctionInfo functions[] = {
783 {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"}, 839 {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"},
784 {11, nullptr, "LockForeground"}, 840 {11, nullptr, "LockForeground"},
@@ -787,7 +843,10 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions"
787 {21, nullptr, "GetPopFromGeneralChannelEvent"}, 843 {21, nullptr, "GetPopFromGeneralChannelEvent"},
788 {30, nullptr, "GetHomeButtonWriterLockAccessor"}, 844 {30, nullptr, "GetHomeButtonWriterLockAccessor"},
789 {31, nullptr, "GetWriterLockAccessorEx"}, 845 {31, nullptr, "GetWriterLockAccessorEx"},
846 {100, nullptr, "PopRequestLaunchApplicationForDebug"},
790 }; 847 };
848 // clang-format on
849
791 RegisterHandlers(functions); 850 RegisterHandlers(functions);
792} 851}
793 852
@@ -800,6 +859,7 @@ void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx)
800} 859}
801 860
802IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") { 861IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") {
862 // clang-format off
803 static const FunctionInfo functions[] = { 863 static const FunctionInfo functions[] = {
804 {0, nullptr, "RequestToEnterSleep"}, 864 {0, nullptr, "RequestToEnterSleep"},
805 {1, nullptr, "EnterSleep"}, 865 {1, nullptr, "EnterSleep"},
@@ -813,18 +873,23 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat
813 {14, nullptr, "ShouldSleepOnBoot"}, 873 {14, nullptr, "ShouldSleepOnBoot"},
814 {15, nullptr, "GetHdcpAuthenticationFailedEvent"}, 874 {15, nullptr, "GetHdcpAuthenticationFailedEvent"},
815 }; 875 };
876 // clang-format on
877
816 RegisterHandlers(functions); 878 RegisterHandlers(functions);
817} 879}
818 880
819IGlobalStateController::~IGlobalStateController() = default; 881IGlobalStateController::~IGlobalStateController() = default;
820 882
821IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") { 883IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") {
884 // clang-format off
822 static const FunctionInfo functions[] = { 885 static const FunctionInfo functions[] = {
823 {0, nullptr, "CreateApplication"}, 886 {0, nullptr, "CreateApplication"},
824 {1, nullptr, "PopLaunchRequestedApplication"}, 887 {1, nullptr, "PopLaunchRequestedApplication"},
825 {10, nullptr, "CreateSystemApplication"}, 888 {10, nullptr, "CreateSystemApplication"},
826 {100, nullptr, "PopFloatingApplicationForDevelopment"}, 889 {100, nullptr, "PopFloatingApplicationForDevelopment"},
827 }; 890 };
891 // clang-format on
892
828 RegisterHandlers(functions); 893 RegisterHandlers(functions);
829} 894}
830 895
@@ -832,6 +897,7 @@ IApplicationCreator::~IApplicationCreator() = default;
832 897
833IProcessWindingController::IProcessWindingController() 898IProcessWindingController::IProcessWindingController()
834 : ServiceFramework("IProcessWindingController") { 899 : ServiceFramework("IProcessWindingController") {
900 // clang-format off
835 static const FunctionInfo functions[] = { 901 static const FunctionInfo functions[] = {
836 {0, nullptr, "GetLaunchReason"}, 902 {0, nullptr, "GetLaunchReason"},
837 {11, nullptr, "OpenCallingLibraryApplet"}, 903 {11, nullptr, "OpenCallingLibraryApplet"},
@@ -842,6 +908,8 @@ IProcessWindingController::IProcessWindingController()
842 {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"}, 908 {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"},
843 {41, nullptr, "ReserveToStartAndWait"}, 909 {41, nullptr, "ReserveToStartAndWait"},
844 }; 910 };
911 // clang-format on
912
845 RegisterHandlers(functions); 913 RegisterHandlers(functions);
846} 914}
847 915
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index b39b0d838..095f94851 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -154,6 +154,10 @@ private:
154 void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx); 154 void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
155 void NotifyRunning(Kernel::HLERequestContext& ctx); 155 void NotifyRunning(Kernel::HLERequestContext& ctx);
156 void GetPseudoDeviceId(Kernel::HLERequestContext& ctx); 156 void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
157 void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
158 void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
159 void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
160 void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
157}; 161};
158 162
159class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { 163class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index 4296c255e..68ea778e8 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -211,6 +211,7 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
211 211
212AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 212AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
213 : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) { 213 : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) {
214 // clang-format off
214 static const FunctionInfo functions[] = { 215 static const FunctionInfo functions[] = {
215 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, 216 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
216 {200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"}, 217 {200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"},
@@ -218,7 +219,10 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
218 {300, nullptr, "OpenOverlayAppletProxy"}, 219 {300, nullptr, "OpenOverlayAppletProxy"},
219 {350, nullptr, "OpenSystemApplicationProxy"}, 220 {350, nullptr, "OpenSystemApplicationProxy"},
220 {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"}, 221 {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
222 {401, nullptr, "GetSystemAppletControllerForDebug"},
221 }; 223 };
224 // clang-format on
225
222 RegisterHandlers(functions); 226 RegisterHandlers(functions);
223} 227}
224 228
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index e45cf6e20..60717afd9 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -14,6 +14,7 @@ class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
14public: 14public:
15 explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 15 explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
16 : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) { 16 : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) {
17 // clang-format off
17 static const FunctionInfo functions[] = { 18 static const FunctionInfo functions[] = {
18 {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, 19 {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
19 {1, &IApplicationProxy::GetSelfController, "GetSelfController"}, 20 {1, &IApplicationProxy::GetSelfController, "GetSelfController"},
@@ -25,6 +26,8 @@ public:
25 {20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"}, 26 {20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"},
26 {1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"}, 27 {1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"},
27 }; 28 };
29 // clang-format on
30
28 RegisterHandlers(functions); 31 RegisterHandlers(functions);
29 } 32 }
30 33
diff --git a/src/core/hle/service/am/idle.cpp b/src/core/hle/service/am/idle.cpp
index 0e3088bc8..f814fe2c0 100644
--- a/src/core/hle/service/am/idle.cpp
+++ b/src/core/hle/service/am/idle.cpp
@@ -12,9 +12,9 @@ IdleSys::IdleSys() : ServiceFramework{"idle:sys"} {
12 {0, nullptr, "GetAutoPowerDownEvent"}, 12 {0, nullptr, "GetAutoPowerDownEvent"},
13 {1, nullptr, "Unknown1"}, 13 {1, nullptr, "Unknown1"},
14 {2, nullptr, "Unknown2"}, 14 {2, nullptr, "Unknown2"},
15 {3, nullptr, "Unknown3"}, 15 {3, nullptr, "SetHandlingContext"},
16 {4, nullptr, "Unknown4"}, 16 {4, nullptr, "LoadAndApplySettings"},
17 {5, nullptr, "Unknown5"}, 17 {5, nullptr, "ReportUserIsActive"},
18 }; 18 };
19 // clang-format on 19 // clang-format on
20 20
diff --git a/src/core/hle/service/am/omm.cpp b/src/core/hle/service/am/omm.cpp
index 1c37f849f..6ab3fb906 100644
--- a/src/core/hle/service/am/omm.cpp
+++ b/src/core/hle/service/am/omm.cpp
@@ -17,22 +17,24 @@ OMM::OMM() : ServiceFramework{"omm"} {
17 {5, nullptr, "GetCradleStatus"}, 17 {5, nullptr, "GetCradleStatus"},
18 {6, nullptr, "FadeInDisplay"}, 18 {6, nullptr, "FadeInDisplay"},
19 {7, nullptr, "FadeOutDisplay"}, 19 {7, nullptr, "FadeOutDisplay"},
20 {8, nullptr, "Unknown1"}, 20 {8, nullptr, "GetCradleFwVersion"},
21 {9, nullptr, "Unknown2"}, 21 {9, nullptr, "NotifyCecSettingsChanged"},
22 {10, nullptr, "Unknown3"}, 22 {10, nullptr, "SetOperationModePolicy"},
23 {11, nullptr, "Unknown4"}, 23 {11, nullptr, "GetDefaultDisplayResolution"},
24 {12, nullptr, "Unknown5"}, 24 {12, nullptr, "GetDefaultDisplayResolutionChangeEvent"},
25 {13, nullptr, "Unknown6"}, 25 {13, nullptr, "UpdateDefaultDisplayResolution"},
26 {14, nullptr, "Unknown7"}, 26 {14, nullptr, "ShouldSleepOnBoot"},
27 {15, nullptr, "Unknown8"}, 27 {15, nullptr, "NotifyHdcpApplicationExecutionStarted"},
28 {16, nullptr, "Unknown9"}, 28 {16, nullptr, "NotifyHdcpApplicationExecutionFinished"},
29 {17, nullptr, "Unknown10"}, 29 {17, nullptr, "NotifyHdcpApplicationDrawingStarted"},
30 {18, nullptr, "Unknown11"}, 30 {18, nullptr, "NotifyHdcpApplicationDrawingFinished"},
31 {19, nullptr, "Unknown12"}, 31 {19, nullptr, "GetHdcpAuthenticationFailedEvent"},
32 {20, nullptr, "Unknown13"}, 32 {20, nullptr, "GetHdcpAuthenticationFailedEmulationEnabled"},
33 {21, nullptr, "Unknown14"}, 33 {21, nullptr, "SetHdcpAuthenticationFailedEmulation"},
34 {22, nullptr, "Unknown15"}, 34 {22, nullptr, "GetHdcpStateChangeEvent"},
35 {23, nullptr, "Unknown16"}, 35 {23, nullptr, "GetHdcpState"},
36 {24, nullptr, "ShowCardUpdateProcessing"},
37 {25, nullptr, "SetApplicationCecSettingsAndNotifyChanged"},
36 }; 38 };
37 // clang-format on 39 // clang-format on
38 40
diff --git a/src/core/hle/service/am/tcap.cpp b/src/core/hle/service/am/tcap.cpp
new file mode 100644
index 000000000..a75cbdda8
--- /dev/null
+++ b/src/core/hle/service/am/tcap.cpp
@@ -0,0 +1,23 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/am/tcap.h"
6
7namespace Service::AM {
8
9TCAP::TCAP() : ServiceFramework{"tcap"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, nullptr, "GetContinuousHighSkinTemperatureEvent"},
13 {1, nullptr, "SetOperationMode"},
14 {2, nullptr, "LoadAndApplySettings"},
15 };
16 // clang-format on
17
18 RegisterHandlers(functions);
19}
20
21TCAP::~TCAP() = default;
22
23} // namespace Service::AM
diff --git a/src/core/hle/service/am/tcap.h b/src/core/hle/service/am/tcap.h
new file mode 100644
index 000000000..2021b55d1
--- /dev/null
+++ b/src/core/hle/service/am/tcap.h
@@ -0,0 +1,17 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9namespace Service::AM {
10
11class TCAP final : public ServiceFramework<TCAP> {
12public:
13 explicit TCAP();
14 ~TCAP() override;
15};
16
17} // namespace Service::AM
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 518161bf7..428069df2 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -13,6 +13,7 @@
13#include "core/file_sys/patch_manager.h" 13#include "core/file_sys/patch_manager.h"
14#include "core/file_sys/registered_cache.h" 14#include "core/file_sys/registered_cache.h"
15#include "core/hle/ipc_helpers.h" 15#include "core/hle/ipc_helpers.h"
16#include "core/hle/kernel/event.h"
16#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
17#include "core/hle/service/aoc/aoc_u.h" 18#include "core/hle/service/aoc/aoc_u.h"
18#include "core/hle/service/filesystem/filesystem.h" 19#include "core/hle/service/filesystem/filesystem.h"
@@ -55,9 +56,13 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
55 {5, &AOC_U::GetAddOnContentBaseId, "GetAddOnContentBaseId"}, 56 {5, &AOC_U::GetAddOnContentBaseId, "GetAddOnContentBaseId"},
56 {6, nullptr, "PrepareAddOnContentByApplicationId"}, 57 {6, nullptr, "PrepareAddOnContentByApplicationId"},
57 {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, 58 {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"},
58 {8, nullptr, "GetAddOnContentListChangedEvent"}, 59 {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"},
59 }; 60 };
60 RegisterHandlers(functions); 61 RegisterHandlers(functions);
62
63 auto& kernel = Core::System::GetInstance().Kernel();
64 aoc_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky,
65 "GetAddOnContentListChanged:Event");
61} 66}
62 67
63AOC_U::~AOC_U() = default; 68AOC_U::~AOC_U() = default;
@@ -130,6 +135,14 @@ void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
130 rb.Push(RESULT_SUCCESS); 135 rb.Push(RESULT_SUCCESS);
131} 136}
132 137
138void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) {
139 LOG_WARNING(Service_AOC, "(STUBBED) called");
140
141 IPC::ResponseBuilder rb{ctx, 2, 1};
142 rb.Push(RESULT_SUCCESS);
143 rb.PushCopyObjects(aoc_change_event);
144}
145
133void InstallInterfaces(SM::ServiceManager& service_manager) { 146void InstallInterfaces(SM::ServiceManager& service_manager) {
134 std::make_shared<AOC_U>()->InstallAsService(service_manager); 147 std::make_shared<AOC_U>()->InstallAsService(service_manager);
135} 148}
diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h
index b3c7cab7a..68d94fdaa 100644
--- a/src/core/hle/service/aoc/aoc_u.h
+++ b/src/core/hle/service/aoc/aoc_u.h
@@ -18,8 +18,10 @@ private:
18 void ListAddOnContent(Kernel::HLERequestContext& ctx); 18 void ListAddOnContent(Kernel::HLERequestContext& ctx);
19 void GetAddOnContentBaseId(Kernel::HLERequestContext& ctx); 19 void GetAddOnContentBaseId(Kernel::HLERequestContext& ctx);
20 void PrepareAddOnContent(Kernel::HLERequestContext& ctx); 20 void PrepareAddOnContent(Kernel::HLERequestContext& ctx);
21 void GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx);
21 22
22 std::vector<u64> add_on_content; 23 std::vector<u64> add_on_content;
24 Kernel::SharedPtr<Kernel::Event> aoc_change_event;
23}; 25};
24 26
25/// Registers all AOC services with the specified service manager. 27/// Registers all AOC services with the specified service manager.
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 6073f4ecd..fac6785a5 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -22,20 +22,22 @@ class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
22public: 22public:
23 explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params) 23 explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params)
24 : ServiceFramework("IAudioRenderer") { 24 : ServiceFramework("IAudioRenderer") {
25 // clang-format off
25 static const FunctionInfo functions[] = { 26 static const FunctionInfo functions[] = {
26 {0, &IAudioRenderer::GetAudioRendererSampleRate, "GetAudioRendererSampleRate"}, 27 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
27 {1, &IAudioRenderer::GetAudioRendererSampleCount, "GetAudioRendererSampleCount"}, 28 {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
28 {2, &IAudioRenderer::GetAudioRendererMixBufferCount, "GetAudioRendererMixBufferCount"}, 29 {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
29 {3, &IAudioRenderer::GetAudioRendererState, "GetAudioRendererState"}, 30 {3, &IAudioRenderer::GetState, "GetState"},
30 {4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"}, 31 {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
31 {5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"}, 32 {5, &IAudioRenderer::Start, "Start"},
32 {6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"}, 33 {6, &IAudioRenderer::Stop, "Stop"},
33 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, 34 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
34 {8, nullptr, "SetAudioRendererRenderingTimeLimit"}, 35 {8, nullptr, "SetRenderingTimeLimit"},
35 {9, nullptr, "GetAudioRendererRenderingTimeLimit"}, 36 {9, nullptr, "GetRenderingTimeLimit"},
36 {10, nullptr, "RequestUpdateAudioRendererAuto"}, 37 {10, nullptr, "RequestUpdateAuto"},
37 {11, nullptr, "ExecuteAudioRendererRendering"}, 38 {11, nullptr, "ExecuteAudioRendererRendering"},
38 }; 39 };
40 // clang-format on
39 RegisterHandlers(functions); 41 RegisterHandlers(functions);
40 42
41 auto& kernel = Core::System::GetInstance().Kernel(); 43 auto& kernel = Core::System::GetInstance().Kernel();
@@ -49,42 +51,42 @@ private:
49 system_event->Signal(); 51 system_event->Signal();
50 } 52 }
51 53
52 void GetAudioRendererSampleRate(Kernel::HLERequestContext& ctx) { 54 void GetSampleRate(Kernel::HLERequestContext& ctx) {
53 IPC::ResponseBuilder rb{ctx, 3}; 55 IPC::ResponseBuilder rb{ctx, 3};
54 rb.Push(RESULT_SUCCESS); 56 rb.Push(RESULT_SUCCESS);
55 rb.Push<u32>(renderer->GetSampleRate()); 57 rb.Push<u32>(renderer->GetSampleRate());
56 LOG_DEBUG(Service_Audio, "called"); 58 LOG_DEBUG(Service_Audio, "called");
57 } 59 }
58 60
59 void GetAudioRendererSampleCount(Kernel::HLERequestContext& ctx) { 61 void GetSampleCount(Kernel::HLERequestContext& ctx) {
60 IPC::ResponseBuilder rb{ctx, 3}; 62 IPC::ResponseBuilder rb{ctx, 3};
61 rb.Push(RESULT_SUCCESS); 63 rb.Push(RESULT_SUCCESS);
62 rb.Push<u32>(renderer->GetSampleCount()); 64 rb.Push<u32>(renderer->GetSampleCount());
63 LOG_DEBUG(Service_Audio, "called"); 65 LOG_DEBUG(Service_Audio, "called");
64 } 66 }
65 67
66 void GetAudioRendererState(Kernel::HLERequestContext& ctx) { 68 void GetState(Kernel::HLERequestContext& ctx) {
67 IPC::ResponseBuilder rb{ctx, 3}; 69 IPC::ResponseBuilder rb{ctx, 3};
68 rb.Push(RESULT_SUCCESS); 70 rb.Push(RESULT_SUCCESS);
69 rb.Push<u32>(static_cast<u32>(renderer->GetStreamState())); 71 rb.Push<u32>(static_cast<u32>(renderer->GetStreamState()));
70 LOG_DEBUG(Service_Audio, "called"); 72 LOG_DEBUG(Service_Audio, "called");
71 } 73 }
72 74
73 void GetAudioRendererMixBufferCount(Kernel::HLERequestContext& ctx) { 75 void GetMixBufferCount(Kernel::HLERequestContext& ctx) {
74 IPC::ResponseBuilder rb{ctx, 3}; 76 IPC::ResponseBuilder rb{ctx, 3};
75 rb.Push(RESULT_SUCCESS); 77 rb.Push(RESULT_SUCCESS);
76 rb.Push<u32>(renderer->GetMixBufferCount()); 78 rb.Push<u32>(renderer->GetMixBufferCount());
77 LOG_DEBUG(Service_Audio, "called"); 79 LOG_DEBUG(Service_Audio, "called");
78 } 80 }
79 81
80 void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { 82 void RequestUpdate(Kernel::HLERequestContext& ctx) {
81 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); 83 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
82 IPC::ResponseBuilder rb{ctx, 2}; 84 IPC::ResponseBuilder rb{ctx, 2};
83 rb.Push(RESULT_SUCCESS); 85 rb.Push(RESULT_SUCCESS);
84 LOG_WARNING(Service_Audio, "(STUBBED) called"); 86 LOG_WARNING(Service_Audio, "(STUBBED) called");
85 } 87 }
86 88
87 void StartAudioRenderer(Kernel::HLERequestContext& ctx) { 89 void Start(Kernel::HLERequestContext& ctx) {
88 IPC::ResponseBuilder rb{ctx, 2}; 90 IPC::ResponseBuilder rb{ctx, 2};
89 91
90 rb.Push(RESULT_SUCCESS); 92 rb.Push(RESULT_SUCCESS);
@@ -92,7 +94,7 @@ private:
92 LOG_WARNING(Service_Audio, "(STUBBED) called"); 94 LOG_WARNING(Service_Audio, "(STUBBED) called");
93 } 95 }
94 96
95 void StopAudioRenderer(Kernel::HLERequestContext& ctx) { 97 void Stop(Kernel::HLERequestContext& ctx) {
96 IPC::ResponseBuilder rb{ctx, 2}; 98 IPC::ResponseBuilder rb{ctx, 2};
97 99
98 rb.Push(RESULT_SUCCESS); 100 rb.Push(RESULT_SUCCESS);
@@ -129,6 +131,7 @@ public:
129 {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"}, 131 {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
130 {11, nullptr, "QueryAudioDeviceInputEvent"}, 132 {11, nullptr, "QueryAudioDeviceInputEvent"},
131 {12, nullptr, "QueryAudioDeviceOutputEvent"}, 133 {12, nullptr, "QueryAudioDeviceOutputEvent"},
134 {13, nullptr, "GetAudioSystemMasterVolumeSetting"},
132 }; 135 };
133 RegisterHandlers(functions); 136 RegisterHandlers(functions);
134 137
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index d40f18565..6701cb913 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -9,6 +9,7 @@ namespace Service::ES {
9class ETicket final : public ServiceFramework<ETicket> { 9class ETicket final : public ServiceFramework<ETicket> {
10public: 10public:
11 explicit ETicket() : ServiceFramework{"es"} { 11 explicit ETicket() : ServiceFramework{"es"} {
12 // clang-format off
12 static const FunctionInfo functions[] = { 13 static const FunctionInfo functions[] = {
13 {1, nullptr, "ImportTicket"}, 14 {1, nullptr, "ImportTicket"},
14 {2, nullptr, "ImportTicketCertificateSet"}, 15 {2, nullptr, "ImportTicketCertificateSet"},
@@ -37,15 +38,18 @@ public:
37 {25, nullptr, "DeletePrepurchaseRecord"}, 38 {25, nullptr, "DeletePrepurchaseRecord"},
38 {26, nullptr, "DeleteAllPrepurchaseRecord"}, 39 {26, nullptr, "DeleteAllPrepurchaseRecord"},
39 {27, nullptr, "CountPrepurchaseRecord"}, 40 {27, nullptr, "CountPrepurchaseRecord"},
40 {28, nullptr, "ListPrepurchaseRecord"}, 41 {28, nullptr, "ListPrepurchaseRecordRightsIds"},
41 {29, nullptr, "ListPrepurchaseRecordInfo"}, 42 {29, nullptr, "ListPrepurchaseRecordInfo"},
42 {30, nullptr, "Unknown1"}, 43 {30, nullptr, "CountTicket"},
43 {31, nullptr, "Unknown2"}, 44 {31, nullptr, "ListTicketRightsIds"},
44 {32, nullptr, "Unknown3"}, 45 {32, nullptr, "CountPrepurchaseRecordEx"},
45 {33, nullptr, "Unknown4"}, 46 {33, nullptr, "ListPrepurchaseRecordRightsIdsEx"},
46 {34, nullptr, "Unknown5"}, 47 {34, nullptr, "GetEncryptedTicketSize"},
47 {35, nullptr, "Unknown6"}, 48 {35, nullptr, "GetEncryptedTicketData"},
49 {36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"},
50 {503, nullptr, "GetTitleKey"},
48 }; 51 };
52 // clang-format on
49 RegisterHandlers(functions); 53 RegisterHandlers(functions);
50 } 54 }
51}; 55};
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
new file mode 100644
index 000000000..0993a7815
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -0,0 +1,30 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/hid/controllers/controller_base.h"
6
7namespace Service::HID {
8
9ControllerBase::ControllerBase() = default;
10ControllerBase::~ControllerBase() = default;
11
12void ControllerBase::ActivateController() {
13 if (is_activated) {
14 OnRelease();
15 }
16 is_activated = true;
17 OnInit();
18}
19
20void ControllerBase::DeactivateController() {
21 if (is_activated) {
22 OnRelease();
23 }
24 is_activated = false;
25}
26
27bool ControllerBase::IsControllerActivated() const {
28 return is_activated;
29}
30} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
new file mode 100644
index 000000000..f0e092b1b
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -0,0 +1,45 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "common/swap.h"
9
10namespace Service::HID {
11class ControllerBase {
12public:
13 ControllerBase();
14 virtual ~ControllerBase();
15
16 // Called when the controller is initialized
17 virtual void OnInit() = 0;
18
19 // When the controller is released
20 virtual void OnRelease() = 0;
21
22 // When the controller is requesting an update for the shared memory
23 virtual void OnUpdate(u8* data, std::size_t size) = 0;
24
25 // Called when input devices should be loaded
26 virtual void OnLoadInputDevices() = 0;
27
28 void ActivateController();
29
30 void DeactivateController();
31
32 bool IsControllerActivated() const;
33
34protected:
35 bool is_activated{false};
36
37 struct CommonHeader {
38 s64_le timestamp;
39 s64_le total_entry_count;
40 s64_le last_entry_index;
41 s64_le entry_count;
42 };
43 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
44};
45} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
new file mode 100644
index 000000000..3d100763f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -0,0 +1,42 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/debug_pad.h"
9
10namespace Service::HID {
11
12Controller_DebugPad::Controller_DebugPad() = default;
13Controller_DebugPad::~Controller_DebugPad() = default;
14
15void Controller_DebugPad::OnInit() {}
16
17void Controller_DebugPad::OnRelease() {}
18
19void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
20 shared_memory.header.timestamp = CoreTiming::GetTicks();
21 shared_memory.header.total_entry_count = 17;
22
23 if (!IsControllerActivated()) {
24 shared_memory.header.entry_count = 0;
25 shared_memory.header.last_entry_index = 0;
26 return;
27 }
28 shared_memory.header.entry_count = 16;
29
30 const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
31 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
32 auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
33
34 cur_entry.sampling_number = last_entry.sampling_number + 1;
35 cur_entry.sampling_number2 = cur_entry.sampling_number;
36 // TODO(ogniK): Update debug pad states
37
38 std::memcpy(data, &shared_memory, sizeof(SharedMemory));
39}
40
41void Controller_DebugPad::OnLoadInputDevices() {}
42} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
new file mode 100644
index 000000000..62b4f2682
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -0,0 +1,56 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/hid/controllers/controller_base.h"
12
13namespace Service::HID {
14class Controller_DebugPad final : public ControllerBase {
15public:
16 Controller_DebugPad();
17 ~Controller_DebugPad() override;
18
19 // Called when the controller is initialized
20 void OnInit() override;
21
22 // When the controller is released
23 void OnRelease() override;
24
25 // When the controller is requesting an update for the shared memory
26 void OnUpdate(u8* data, std::size_t size) override;
27
28 // Called when input devices should be loaded
29 void OnLoadInputDevices() override;
30
31private:
32 struct AnalogStick {
33 s32_le x;
34 s32_le y;
35 };
36 static_assert(sizeof(AnalogStick) == 0x8);
37
38 struct PadStates {
39 s64_le sampling_number;
40 s64_le sampling_number2;
41 u32_le attribute;
42 u32_le button_state;
43 AnalogStick r_stick;
44 AnalogStick l_stick;
45 };
46 static_assert(sizeof(PadStates) == 0x28, "PadStates is an invalid state");
47
48 struct SharedMemory {
49 CommonHeader header;
50 std::array<PadStates, 17> pad_states;
51 INSERT_PADDING_BYTES(0x138);
52 };
53 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
54 SharedMemory shared_memory{};
55};
56} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
new file mode 100644
index 000000000..898572277
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -0,0 +1,43 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/gesture.h"
9
10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
12
13Controller_Gesture::Controller_Gesture() = default;
14Controller_Gesture::~Controller_Gesture() = default;
15
16void Controller_Gesture::OnInit() {}
17
18void Controller_Gesture::OnRelease() {}
19
20void Controller_Gesture::OnUpdate(u8* data, std::size_t size) {
21 shared_memory.header.timestamp = CoreTiming::GetTicks();
22 shared_memory.header.total_entry_count = 17;
23
24 if (!IsControllerActivated()) {
25 shared_memory.header.entry_count = 0;
26 shared_memory.header.last_entry_index = 0;
27 return;
28 }
29 shared_memory.header.entry_count = 16;
30
31 const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
32 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
33 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
34
35 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update gesture states
38
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40}
41
42void Controller_Gesture::OnLoadInputDevices() {}
43} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
new file mode 100644
index 000000000..1056ffbcd
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -0,0 +1,63 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_types.h"
9#include "common/swap.h"
10#include "core/hle/service/hid/controllers/controller_base.h"
11
12namespace Service::HID {
13class Controller_Gesture final : public ControllerBase {
14public:
15 Controller_Gesture();
16 ~Controller_Gesture() override;
17
18 // Called when the controller is initialized
19 void OnInit() override;
20
21 // When the controller is released
22 void OnRelease() override;
23
24 // When the controller is requesting an update for the shared memory
25 void OnUpdate(u8* data, size_t size) override;
26
27 // Called when input devices should be loaded
28 void OnLoadInputDevices() override;
29
30private:
31 struct Locations {
32 s32_le x;
33 s32_le y;
34 };
35
36 struct GestureState {
37 s64_le sampling_number;
38 s64_le sampling_number2;
39
40 s64_le detection_count;
41 s32_le type;
42 s32_le dir;
43 s32_le x;
44 s32_le y;
45 s32_le delta_x;
46 s32_le delta_y;
47 f32 vel_x;
48 f32 vel_y;
49 s32_le attributes;
50 f32 scale;
51 f32 rotation;
52 s32_le location_count;
53 std::array<Locations, 4> locations;
54 };
55 static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size");
56
57 struct SharedMemory {
58 CommonHeader header;
59 std::array<GestureState, 17> gesture_states;
60 };
61 SharedMemory shared_memory{};
62};
63} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
new file mode 100644
index 000000000..ccfbce9ac
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -0,0 +1,43 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/keyboard.h"
9
10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
12
13Controller_Keyboard::Controller_Keyboard() = default;
14Controller_Keyboard::~Controller_Keyboard() = default;
15
16void Controller_Keyboard::OnInit() {}
17
18void Controller_Keyboard::OnRelease() {}
19
20void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
21 shared_memory.header.timestamp = CoreTiming::GetTicks();
22 shared_memory.header.total_entry_count = 17;
23
24 if (!IsControllerActivated()) {
25 shared_memory.header.entry_count = 0;
26 shared_memory.header.last_entry_index = 0;
27 return;
28 }
29 shared_memory.header.entry_count = 16;
30
31 const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
32 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
33 auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
34
35 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update keyboard states
38
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40}
41
42void Controller_Keyboard::OnLoadInputDevices() {}
43} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
new file mode 100644
index 000000000..493e68fce
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -0,0 +1,50 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/hid/controllers/controller_base.h"
12
13namespace Service::HID {
14class Controller_Keyboard final : public ControllerBase {
15public:
16 Controller_Keyboard();
17 ~Controller_Keyboard() override;
18
19 // Called when the controller is initialized
20 void OnInit() override;
21
22 // When the controller is released
23 void OnRelease() override;
24
25 // When the controller is requesting an update for the shared memory
26 void OnUpdate(u8* data, std::size_t size) override;
27
28 // Called when input devices should be loaded
29 void OnLoadInputDevices() override;
30
31private:
32 struct KeyboardState {
33 s64_le sampling_number;
34 s64_le sampling_number2;
35
36 s32_le modifier;
37 s32_le attribute;
38 std::array<u8, 32> key;
39 };
40 static_assert(sizeof(KeyboardState) == 0x38, "KeyboardState is an invalid size");
41
42 struct SharedMemory {
43 CommonHeader header;
44 std::array<KeyboardState, 17> pad_states;
45 INSERT_PADDING_BYTES(0x28);
46 };
47 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
48 SharedMemory shared_memory{};
49};
50} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
new file mode 100644
index 000000000..4e246a57d
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -0,0 +1,43 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/mouse.h"
9
10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
12
13Controller_Mouse::Controller_Mouse() = default;
14Controller_Mouse::~Controller_Mouse() = default;
15
16void Controller_Mouse::OnInit() {}
17
18void Controller_Mouse::OnRelease() {}
19
20void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
21 shared_memory.header.timestamp = CoreTiming::GetTicks();
22 shared_memory.header.total_entry_count = 17;
23
24 if (!IsControllerActivated()) {
25 shared_memory.header.entry_count = 0;
26 shared_memory.header.last_entry_index = 0;
27 return;
28 }
29 shared_memory.header.entry_count = 16;
30
31 auto& last_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
32 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
33 auto& cur_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
34
35 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update mouse states
38
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40}
41
42void Controller_Mouse::OnLoadInputDevices() {}
43} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
new file mode 100644
index 000000000..543b0b71f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -0,0 +1,50 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_types.h"
9#include "common/swap.h"
10#include "core/hle/service/hid/controllers/controller_base.h"
11
12namespace Service::HID {
13class Controller_Mouse final : public ControllerBase {
14public:
15 Controller_Mouse();
16 ~Controller_Mouse() override;
17
18 // Called when the controller is initialized
19 void OnInit() override;
20
21 // When the controller is released
22 void OnRelease() override;
23
24 // When the controller is requesting an update for the shared memory
25 void OnUpdate(u8* data, std::size_t size) override;
26
27 // Called when input devices should be loaded
28 void OnLoadInputDevices() override;
29
30private:
31 struct MouseState {
32 s64_le sampling_number;
33 s64_le sampling_number2;
34 s32_le x;
35 s32_le y;
36 s32_le delta_x;
37 s32_le delta_y;
38 s32_le mouse_wheel;
39 s32_le button;
40 s32_le attribute;
41 };
42 static_assert(sizeof(MouseState) == 0x30, "MouseState is an invalid size");
43
44 struct SharedMemory {
45 CommonHeader header;
46 std::array<MouseState, 17> mouse_states;
47 };
48 SharedMemory shared_memory{};
49};
50} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
new file mode 100644
index 000000000..b06e65a77
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -0,0 +1,619 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <array>
7#include <cstring>
8#include "common/assert.h"
9#include "common/bit_field.h"
10#include "common/common_types.h"
11#include "common/logging/log.h"
12#include "core/core.h"
13#include "core/core_timing.h"
14#include "core/frontend/input.h"
15#include "core/hle/kernel/event.h"
16#include "core/hle/service/hid/controllers/npad.h"
17#include "core/settings.h"
18
19namespace Service::HID {
20
21constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
22constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
23constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
24constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
25constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
26constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
27constexpr std::size_t NPAD_OFFSET = 0x9A00;
28constexpr u32 BATTERY_FULL = 2;
29constexpr u32 NPAD_HANDHELD = 32;
30constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
31constexpr u32 MAX_NPAD_ID = 7;
32constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER =
33 Controller_NPad::NPadControllerType::JoyDual;
34constexpr std::array<u32, 10> npad_id_list{
35 0, 1, 2, 3, 4, 5, 6, 7, 32, 16,
36};
37
38enum class JoystickId : std::size_t {
39 Joystick_Left,
40 Joystick_Right,
41};
42
43Controller_NPad::Controller_NPad() = default;
44Controller_NPad::~Controller_NPad() = default;
45
46void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
47 const auto controller_type = connected_controllers[controller_idx].type;
48 auto& controller = shared_memory_entries[controller_idx];
49 if (controller_type == NPadControllerType::None) {
50 return;
51 }
52 controller.joy_styles.raw = 0; // Zero out
53 controller.device_type.raw = 0;
54 switch (controller_type) {
55 case NPadControllerType::Handheld:
56 controller.joy_styles.handheld.Assign(1);
57 controller.device_type.handheld.Assign(1);
58 controller.pad_assignment = NPadAssignments::Dual;
59 break;
60 case NPadControllerType::JoyDual:
61 controller.joy_styles.joycon_dual.Assign(1);
62 controller.device_type.joycon_left.Assign(1);
63 controller.device_type.joycon_right.Assign(1);
64 controller.pad_assignment = NPadAssignments::Dual;
65 break;
66 case NPadControllerType::JoyLeft:
67 controller.joy_styles.joycon_left.Assign(1);
68 controller.device_type.joycon_left.Assign(1);
69 controller.pad_assignment = NPadAssignments::Dual;
70 break;
71 case NPadControllerType::JoyRight:
72 controller.joy_styles.joycon_right.Assign(1);
73 controller.device_type.joycon_right.Assign(1);
74 controller.pad_assignment = NPadAssignments::Dual;
75 break;
76 case NPadControllerType::Pokeball:
77 controller.joy_styles.pokeball.Assign(1);
78 controller.device_type.pokeball.Assign(1);
79 controller.pad_assignment = NPadAssignments::Single;
80 break;
81 case NPadControllerType::ProController:
82 controller.joy_styles.pro_controller.Assign(1);
83 controller.device_type.pro_controller.Assign(1);
84 controller.pad_assignment = NPadAssignments::Single;
85 break;
86 }
87
88 controller.single_color_error = ColorReadError::ReadOk;
89 controller.single_color.body_color = 0;
90 controller.single_color.button_color = 0;
91
92 controller.dual_color_error = ColorReadError::ReadOk;
93 controller.left_color.body_color = JOYCON_BODY_NEON_BLUE;
94 controller.left_color.button_color = JOYCON_BUTTONS_NEON_BLUE;
95 controller.right_color.body_color = JOYCON_BODY_NEON_RED;
96 controller.right_color.button_color = JOYCON_BUTTONS_NEON_RED;
97
98 controller.properties.is_vertical.Assign(1); // TODO(ogniK): Swap joycons orientations
99 controller.properties.use_plus.Assign(1);
100 controller.properties.use_minus.Assign(1);
101 controller.battery_level[0] = BATTERY_FULL;
102 controller.battery_level[1] = BATTERY_FULL;
103 controller.battery_level[2] = BATTERY_FULL;
104}
105
106void Controller_NPad::OnInit() {
107 auto& kernel = Core::System::GetInstance().Kernel();
108 styleset_changed_event =
109 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
110
111 if (!IsControllerActivated())
112 return;
113 std::size_t controller{};
114 if (style.raw == 0) {
115 // We want to support all controllers
116 style.handheld.Assign(1);
117 style.joycon_left.Assign(1);
118 style.joycon_right.Assign(1);
119 style.joycon_dual.Assign(1);
120 style.pro_controller.Assign(1);
121 style.pokeball.Assign(1);
122 }
123 if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
124 [](const ControllerHolder& controller) { return controller.is_connected; })) {
125 supported_npad_id_types.resize(npad_id_list.size());
126 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
127 npad_id_list.size() * sizeof(u32));
128 AddNewController(PREFERRED_CONTROLLER);
129 }
130}
131
132void Controller_NPad::OnLoadInputDevices() {
133 std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
134 Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
135 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
136 std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
137 Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
138 sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
139}
140
141void Controller_NPad::OnRelease() {}
142
143void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
144 if (!IsControllerActivated())
145 return;
146 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
147 auto& npad = shared_memory_entries[i];
148 const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
149 &npad.handheld_states,
150 &npad.dual_states,
151 &npad.left_joy_states,
152 &npad.right_joy_states,
153 &npad.pokeball_states,
154 &npad.libnx};
155
156 for (auto* main_controller : controller_npads) {
157 main_controller->common.entry_count = 16;
158 main_controller->common.total_entry_count = 17;
159
160 const auto& last_entry =
161 main_controller->npad[main_controller->common.last_entry_index];
162
163 main_controller->common.timestamp = CoreTiming::GetTicks();
164 main_controller->common.last_entry_index =
165 (main_controller->common.last_entry_index + 1) % 17;
166
167 auto& cur_entry = main_controller->npad[main_controller->common.last_entry_index];
168
169 cur_entry.timestamp = last_entry.timestamp + 1;
170 cur_entry.timestamp2 = cur_entry.timestamp;
171 }
172
173 const auto& controller_type = connected_controllers[i].type;
174
175 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
176 continue;
177 }
178
179 // Pad states
180 ControllerPadState pad_state{};
181 using namespace Settings::NativeButton;
182 pad_state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
183 pad_state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
184 pad_state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
185 pad_state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
186 pad_state.l_stick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
187 pad_state.r_stick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
188 pad_state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
189 pad_state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
190 pad_state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
191 pad_state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
192 pad_state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
193 pad_state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
194
195 pad_state.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
196 pad_state.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
197 pad_state.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
198 pad_state.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
199
200 pad_state.l_stick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
201 pad_state.l_stick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
202 pad_state.l_stick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
203 pad_state.l_stick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
204
205 pad_state.r_stick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
206 pad_state.r_stick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
207 pad_state.r_stick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
208 pad_state.r_stick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
209
210 pad_state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
211 pad_state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
212
213 AnalogPosition lstick_entry{};
214 AnalogPosition rstick_entry{};
215
216 const auto [stick_l_x_f, stick_l_y_f] =
217 sticks[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
218 const auto [stick_r_x_f, stick_r_y_f] =
219 sticks[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
220 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
221 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
222 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
223 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
224
225 if (controller_type == NPadControllerType::JoyLeft ||
226 controller_type == NPadControllerType::JoyRight) {
227 if (npad.properties.is_horizontal) {
228 ControllerPadState state{};
229 AnalogPosition temp_lstick_entry{};
230 AnalogPosition temp_rstick_entry{};
231 if (controller_type == NPadControllerType::JoyLeft) {
232 state.d_down.Assign(pad_state.d_left.Value());
233 state.d_left.Assign(pad_state.d_up.Value());
234 state.d_right.Assign(pad_state.d_down.Value());
235 state.d_up.Assign(pad_state.d_right.Value());
236 state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
237 state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
238
239 state.zl.Assign(pad_state.zl.Value());
240 state.plus.Assign(pad_state.minus.Value());
241
242 temp_lstick_entry = lstick_entry;
243 temp_rstick_entry = rstick_entry;
244 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
245 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
246 temp_lstick_entry.y *= -1;
247 } else if (controller_type == NPadControllerType::JoyRight) {
248 state.x.Assign(pad_state.a.Value());
249 state.a.Assign(pad_state.b.Value());
250 state.b.Assign(pad_state.y.Value());
251 state.y.Assign(pad_state.b.Value());
252
253 state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
254 state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
255 state.zr.Assign(pad_state.zr.Value());
256 state.plus.Assign(pad_state.plus.Value());
257
258 temp_lstick_entry = lstick_entry;
259 temp_rstick_entry = rstick_entry;
260 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
261 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
262 temp_rstick_entry.x *= -1;
263 }
264 pad_state.raw = state.raw;
265 lstick_entry = temp_lstick_entry;
266 rstick_entry = temp_rstick_entry;
267 }
268 }
269
270 auto& main_controller =
271 npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
272 auto& handheld_entry =
273 npad.handheld_states.npad[npad.handheld_states.common.last_entry_index];
274 auto& dual_entry = npad.dual_states.npad[npad.dual_states.common.last_entry_index];
275 auto& left_entry = npad.left_joy_states.npad[npad.left_joy_states.common.last_entry_index];
276 auto& right_entry =
277 npad.right_joy_states.npad[npad.right_joy_states.common.last_entry_index];
278 auto& pokeball_entry =
279 npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index];
280 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
281
282 if (hold_type == NpadHoldType::Horizontal) {
283 // TODO(ogniK): Remap buttons for different orientations
284 }
285 libnx_entry.connection_status.raw = 0;
286
287 switch (controller_type) {
288 case NPadControllerType::Handheld:
289 handheld_entry.connection_status.raw = 0;
290 handheld_entry.connection_status.IsConnected.Assign(1);
291 if (!Settings::values.use_docked_mode) {
292 handheld_entry.connection_status.IsWired.Assign(1);
293 }
294 handheld_entry.pad_states.raw = pad_state.raw;
295 handheld_entry.l_stick = lstick_entry;
296 handheld_entry.r_stick = rstick_entry;
297 break;
298 case NPadControllerType::JoyDual:
299 dual_entry.connection_status.raw = 0;
300
301 dual_entry.connection_status.IsLeftJoyConnected.Assign(1);
302 dual_entry.connection_status.IsRightJoyConnected.Assign(1);
303 dual_entry.connection_status.IsConnected.Assign(1);
304
305 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
306 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
307 libnx_entry.connection_status.IsConnected.Assign(1);
308
309 dual_entry.pad_states.raw = pad_state.raw;
310 dual_entry.l_stick = lstick_entry;
311 dual_entry.r_stick = rstick_entry;
312 case NPadControllerType::JoyLeft:
313 left_entry.connection_status.raw = 0;
314
315 left_entry.connection_status.IsConnected.Assign(1);
316 left_entry.pad_states.raw = pad_state.raw;
317 left_entry.l_stick = lstick_entry;
318 left_entry.r_stick = rstick_entry;
319 break;
320 case NPadControllerType::JoyRight:
321 right_entry.connection_status.raw = 0;
322
323 right_entry.connection_status.IsConnected.Assign(1);
324 right_entry.pad_states.raw = pad_state.raw;
325 right_entry.l_stick = lstick_entry;
326 right_entry.r_stick = rstick_entry;
327 break;
328 case NPadControllerType::Pokeball:
329 pokeball_entry.connection_status.raw = 0;
330
331 pokeball_entry.connection_status.IsConnected.Assign(1);
332 pokeball_entry.connection_status.IsWired.Assign(1);
333
334 pokeball_entry.pad_states.raw = pad_state.raw;
335 pokeball_entry.l_stick = lstick_entry;
336 pokeball_entry.r_stick = rstick_entry;
337 break;
338 case NPadControllerType::ProController:
339 main_controller.connection_status.raw = 0;
340
341 main_controller.connection_status.IsConnected.Assign(1);
342 main_controller.connection_status.IsWired.Assign(1);
343 main_controller.pad_states.raw = pad_state.raw;
344 main_controller.l_stick = lstick_entry;
345 main_controller.r_stick = rstick_entry;
346 break;
347 }
348
349 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
350 // any controllers.
351 libnx_entry.pad_states.raw = pad_state.raw;
352 libnx_entry.l_stick = lstick_entry;
353 libnx_entry.r_stick = rstick_entry;
354 }
355 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
356 shared_memory_entries.size() * sizeof(NPadEntry));
357} // namespace Service::HID
358
359void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
360 style.raw = style_set.raw;
361}
362
363Controller_NPad::NPadType Controller_NPad::GetSupportedStyleSet() const {
364 return style;
365}
366
367void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
368 ASSERT(length > 0 && (length % sizeof(u32)) == 0);
369 supported_npad_id_types.clear();
370 supported_npad_id_types.resize(length / sizeof(u32));
371 std::memcpy(supported_npad_id_types.data(), data, length);
372 for (std::size_t i = 0; i < connected_controllers.size(); i++) {
373 auto& controller = connected_controllers[i];
374 if (!controller.is_connected) {
375 continue;
376 }
377 if (!IsControllerSupported(PREFERRED_CONTROLLER)) {
378 controller.type = DecideBestController(PREFERRED_CONTROLLER);
379 InitNewlyAddedControler(i);
380 }
381 }
382}
383
384void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
385 ASSERT(max_length < supported_npad_id_types.size());
386 std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size());
387}
388
389std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
390 return supported_npad_id_types.size();
391}
392
393void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
394 hold_type = joy_hold_type;
395}
396Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
397 return hold_type;
398}
399
400void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
401 ASSERT(npad_id < shared_memory_entries.size());
402 shared_memory_entries[npad_id].pad_assignment = assignment_mode;
403}
404
405void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
406 const std::vector<Vibration>& vibrations) {
407 if (!can_controllers_vibrate) {
408 return;
409 }
410 for (std::size_t i = 0; i < controller_ids.size(); i++) {
411 std::size_t controller_pos = i;
412 // Handheld controller conversion
413 if (controller_pos == NPAD_HANDHELD) {
414 controller_pos = 8;
415 }
416 // Unknown controller conversion
417 if (controller_pos == NPAD_UNKNOWN) {
418 controller_pos = 9;
419 }
420 if (connected_controllers[controller_pos].is_connected) {
421 // TODO(ogniK): Vibrate the physical controller
422 }
423 }
424 LOG_WARNING(Service_HID, "(STUBBED) called");
425 last_processed_vibration = vibrations.back();
426}
427
428Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const {
429 return styleset_changed_event;
430}
431
432Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
433 return last_processed_vibration;
434}
435void Controller_NPad::AddNewController(NPadControllerType controller) {
436 if (controller == NPadControllerType::Handheld) {
437 connected_controllers[8] = {controller, true};
438 InitNewlyAddedControler(8);
439 return;
440 }
441 const auto pos =
442 std::find_if(connected_controllers.begin(), connected_controllers.end() - 2,
443 [](const ControllerHolder& holder) { return !holder.is_connected; });
444 if (pos == connected_controllers.end() - 2) {
445 LOG_ERROR(Service_HID, "Cannot connect any more controllers!");
446 return;
447 }
448 const auto controller_id = std::distance(connected_controllers.begin(), pos);
449 connected_controllers[controller_id] = {controller, true};
450 InitNewlyAddedControler(controller_id);
451}
452
453void Controller_NPad::ConnectNPad(u32 npad_id) {
454 if (npad_id >= connected_controllers.size())
455 return;
456 connected_controllers[npad_id].is_connected = true;
457}
458
459void Controller_NPad::DisconnectNPad(u32 npad_id) {
460 if (npad_id >= connected_controllers.size())
461 return;
462 connected_controllers[npad_id].is_connected = false;
463}
464
465Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
466 if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) {
467 // These are controllers without led patterns
468 return LedPattern{0, 0, 0, 0};
469 }
470 switch (npad_id) {
471 case 0:
472 return LedPattern{1, 0, 0, 0};
473 case 1:
474 return LedPattern{0, 1, 0, 0};
475 case 2:
476 return LedPattern{0, 0, 1, 0};
477 case 3:
478 return LedPattern{0, 0, 0, 1};
479 case 4:
480 return LedPattern{1, 0, 0, 1};
481 case 5:
482 return LedPattern{1, 0, 1, 0};
483 case 6:
484 return LedPattern{1, 0, 1, 1};
485 case 7:
486 return LedPattern{0, 1, 1, 0};
487 default:
488 UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id);
489 return LedPattern{0, 0, 0, 0};
490 };
491}
492void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
493 can_controllers_vibrate = can_vibrate;
494}
495
496bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
497 const bool support_handheld =
498 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) !=
499 supported_npad_id_types.end();
500 if (controller == NPadControllerType::Handheld) {
501 // Handheld is not even a supported type, lets stop here
502 if (!support_handheld) {
503 return false;
504 }
505 // Handheld should not be supported in docked mode
506 if (Settings::values.use_docked_mode) {
507 return false;
508 }
509
510 return true;
511 }
512 if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(),
513 [](u32 npad_id) { return npad_id <= MAX_NPAD_ID; })) {
514 switch (controller) {
515 case NPadControllerType::ProController:
516 return style.pro_controller;
517 case NPadControllerType::JoyDual:
518 return style.joycon_dual;
519 case NPadControllerType::JoyLeft:
520 return style.joycon_left;
521 case NPadControllerType::JoyRight:
522 return style.joycon_right;
523 case NPadControllerType::Pokeball:
524 return style.pokeball;
525 default:
526 return false;
527 }
528 }
529 return false;
530}
531
532Controller_NPad::NPadControllerType Controller_NPad::DecideBestController(
533 NPadControllerType priority) const {
534 if (IsControllerSupported(priority)) {
535 return priority;
536 }
537 const auto is_docked = Settings::values.use_docked_mode;
538 if (is_docked && priority == NPadControllerType::Handheld) {
539 priority = NPadControllerType::JoyDual;
540 if (IsControllerSupported(priority)) {
541 return priority;
542 }
543 }
544 std::vector<NPadControllerType> priority_list;
545 switch (priority) {
546 case NPadControllerType::ProController:
547 priority_list.push_back(NPadControllerType::JoyDual);
548 if (!is_docked) {
549 priority_list.push_back(NPadControllerType::Handheld);
550 }
551 priority_list.push_back(NPadControllerType::JoyLeft);
552 priority_list.push_back(NPadControllerType::JoyRight);
553 priority_list.push_back(NPadControllerType::Pokeball);
554 break;
555 case NPadControllerType::Handheld:
556 priority_list.push_back(NPadControllerType::JoyDual);
557 priority_list.push_back(NPadControllerType::ProController);
558 priority_list.push_back(NPadControllerType::JoyLeft);
559 priority_list.push_back(NPadControllerType::JoyRight);
560 priority_list.push_back(NPadControllerType::Pokeball);
561 break;
562 case NPadControllerType::JoyDual:
563 if (!is_docked) {
564 priority_list.push_back(NPadControllerType::Handheld);
565 }
566 priority_list.push_back(NPadControllerType::ProController);
567 priority_list.push_back(NPadControllerType::JoyLeft);
568 priority_list.push_back(NPadControllerType::JoyRight);
569 priority_list.push_back(NPadControllerType::Pokeball);
570 break;
571 case NPadControllerType::JoyLeft:
572 priority_list.push_back(NPadControllerType::JoyRight);
573 priority_list.push_back(NPadControllerType::JoyDual);
574 if (!is_docked) {
575 priority_list.push_back(NPadControllerType::Handheld);
576 }
577 priority_list.push_back(NPadControllerType::ProController);
578 priority_list.push_back(NPadControllerType::Pokeball);
579 break;
580 case NPadControllerType::JoyRight:
581 priority_list.push_back(NPadControllerType::JoyLeft);
582 priority_list.push_back(NPadControllerType::JoyDual);
583 if (!is_docked) {
584 priority_list.push_back(NPadControllerType::Handheld);
585 }
586 priority_list.push_back(NPadControllerType::ProController);
587 priority_list.push_back(NPadControllerType::Pokeball);
588 break;
589 case NPadControllerType::Pokeball:
590 priority_list.push_back(NPadControllerType::JoyLeft);
591 priority_list.push_back(NPadControllerType::JoyRight);
592 priority_list.push_back(NPadControllerType::JoyDual);
593 if (!is_docked) {
594 priority_list.push_back(NPadControllerType::Handheld);
595 }
596 priority_list.push_back(NPadControllerType::ProController);
597 break;
598 default:
599 priority_list.push_back(NPadControllerType::JoyDual);
600 if (!is_docked) {
601 priority_list.push_back(NPadControllerType::Handheld);
602 }
603 priority_list.push_back(NPadControllerType::ProController);
604 priority_list.push_back(NPadControllerType::JoyLeft);
605 priority_list.push_back(NPadControllerType::JoyRight);
606 priority_list.push_back(NPadControllerType::JoyDual);
607 }
608
609 const auto iter = std::find_if(priority_list.begin(), priority_list.end(),
610 [this](auto type) { return IsControllerSupported(type); });
611 if (iter == priority_list.end()) {
612 UNIMPLEMENTED_MSG("Could not find supported controller!");
613 return priority;
614 }
615
616 return *iter;
617}
618
619} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
new file mode 100644
index 000000000..ac86985ff
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -0,0 +1,289 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_types.h"
9#include "core/frontend/input.h"
10#include "core/hle/service/hid/controllers/controller_base.h"
11#include "core/settings.h"
12
13namespace Service::HID {
14
15class Controller_NPad final : public ControllerBase {
16public:
17 Controller_NPad();
18 ~Controller_NPad() override;
19
20 // Called when the controller is initialized
21 void OnInit() override;
22
23 // When the controller is released
24 void OnRelease() override;
25
26 // When the controller is requesting an update for the shared memory
27 void OnUpdate(u8* data, std::size_t size) override;
28
29 // Called when input devices should be loaded
30 void OnLoadInputDevices() override;
31
32 struct NPadType {
33 union {
34 u32_le raw{};
35
36 BitField<0, 1, u32_le> pro_controller;
37 BitField<1, 1, u32_le> handheld;
38 BitField<2, 1, u32_le> joycon_dual;
39 BitField<3, 1, u32_le> joycon_left;
40 BitField<4, 1, u32_le> joycon_right;
41
42 BitField<6, 1, u32_le> pokeball; // TODO(ogniK): Confirm when possible
43 };
44 };
45 static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
46
47 struct Vibration {
48 f32 amp_low;
49 f32 freq_low;
50 f32 amp_high;
51 f32 freq_high;
52 };
53 static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size");
54
55 enum class NpadHoldType : u64 {
56 Vertical = 0,
57 Horizontal = 1,
58 };
59
60 enum class NPadAssignments : u32_le {
61 Dual = 0,
62 Single = 1,
63 };
64
65 enum class NPadControllerType {
66 None,
67 ProController,
68 Handheld,
69 JoyDual,
70 JoyLeft,
71 JoyRight,
72 Pokeball,
73 };
74
75 struct LedPattern {
76 explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
77 position1.Assign(light1);
78 position1.Assign(light2);
79 position1.Assign(light3);
80 position1.Assign(light4);
81 }
82 union {
83 u64 raw{};
84 BitField<0, 1, u64> position1;
85 BitField<1, 1, u64> position2;
86 BitField<2, 1, u64> position3;
87 BitField<3, 1, u64> position4;
88 };
89 };
90
91 void SetSupportedStyleSet(NPadType style_set);
92 NPadType GetSupportedStyleSet() const;
93
94 void SetSupportedNPadIdTypes(u8* data, std::size_t length);
95 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
96 std::size_t GetSupportedNPadIdTypesSize() const;
97
98 void SetHoldType(NpadHoldType joy_hold_type);
99 NpadHoldType GetHoldType() const;
100
101 void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode);
102
103 void VibrateController(const std::vector<u32>& controller_ids,
104 const std::vector<Vibration>& vibrations);
105
106 Kernel::SharedPtr<Kernel::Event> GetStyleSetChangedEvent() const;
107 Vibration GetLastVibration() const;
108
109 void AddNewController(NPadControllerType controller);
110
111 void ConnectNPad(u32 npad_id);
112 void DisconnectNPad(u32 npad_id);
113 LedPattern GetLedPattern(u32 npad_id);
114 void SetVibrationEnabled(bool can_vibrate);
115
116private:
117 struct CommonHeader {
118 s64_le timestamp;
119 s64_le total_entry_count;
120 s64_le last_entry_index;
121 s64_le entry_count;
122 };
123 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
124
125 struct ControllerColor {
126 u32_le body_color;
127 u32_le button_color;
128 };
129 static_assert(sizeof(ControllerColor) == 8, "ControllerColor is an invalid size");
130
131 struct ControllerPadState {
132 union {
133 u64_le raw{};
134 // Button states
135 BitField<0, 1, u64_le> a;
136 BitField<1, 1, u64_le> b;
137 BitField<2, 1, u64_le> x;
138 BitField<3, 1, u64_le> y;
139 BitField<4, 1, u64_le> l_stick;
140 BitField<5, 1, u64_le> r_stick;
141 BitField<6, 1, u64_le> l;
142 BitField<7, 1, u64_le> r;
143 BitField<8, 1, u64_le> zl;
144 BitField<9, 1, u64_le> zr;
145 BitField<10, 1, u64_le> plus;
146 BitField<11, 1, u64_le> minus;
147
148 // D-Pad
149 BitField<12, 1, u64_le> d_left;
150 BitField<13, 1, u64_le> d_up;
151 BitField<14, 1, u64_le> d_right;
152 BitField<15, 1, u64_le> d_down;
153
154 // Left JoyStick
155 BitField<16, 1, u64_le> l_stick_left;
156 BitField<17, 1, u64_le> l_stick_up;
157 BitField<18, 1, u64_le> l_stick_right;
158 BitField<19, 1, u64_le> l_stick_down;
159
160 // Right JoyStick
161 BitField<20, 1, u64_le> r_stick_left;
162 BitField<21, 1, u64_le> r_stick_up;
163 BitField<22, 1, u64_le> r_stick_right;
164 BitField<23, 1, u64_le> r_stick_down;
165
166 // Not always active?
167 BitField<24, 1, u64_le> sl;
168 BitField<25, 1, u64_le> sr;
169 };
170 };
171 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
172
173 struct AnalogPosition {
174 s32_le x;
175 s32_le y;
176 };
177 static_assert(sizeof(AnalogPosition) == 8, "AnalogPosition is an invalid size");
178
179 struct ConnectionState {
180 union {
181 u32_le raw{};
182 BitField<0, 1, u32_le> IsConnected;
183 BitField<1, 1, u32_le> IsWired;
184 BitField<2, 1, u32_le> IsLeftJoyConnected;
185 BitField<3, 1, u32_le> IsLeftJoyWired;
186 BitField<4, 1, u32_le> IsRightJoyConnected;
187 BitField<5, 1, u32_le> IsRightJoyWired;
188 };
189 };
190 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
191
192 struct GenericStates {
193 s64_le timestamp;
194 s64_le timestamp2;
195 ControllerPadState pad_states;
196 AnalogPosition l_stick;
197 AnalogPosition r_stick;
198 ConnectionState connection_status;
199 };
200 static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size");
201
202 struct NPadGeneric {
203 CommonHeader common;
204 std::array<GenericStates, 17> npad;
205 };
206 static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size");
207
208 enum class ColorReadError : u32_le {
209 ReadOk = 0,
210 ColorDoesntExist = 1,
211 NoController = 2,
212 };
213
214 struct NPadProperties {
215 union {
216 s64_le raw{};
217 BitField<11, 1, s64_le> is_vertical;
218 BitField<12, 1, s64_le> is_horizontal;
219 BitField<13, 1, s64_le> use_plus;
220 BitField<14, 1, s64_le> use_minus;
221 };
222 };
223
224 struct NPadDevice {
225 union {
226 u32_le raw{};
227 BitField<0, 1, s32_le> pro_controller;
228 BitField<1, 1, s32_le> handheld;
229 BitField<2, 1, s32_le> handheld_left;
230 BitField<3, 1, s32_le> handheld_right;
231 BitField<4, 1, s32_le> joycon_left;
232 BitField<5, 1, s32_le> joycon_right;
233 BitField<6, 1, s32_le> pokeball;
234 };
235 };
236
237 struct NPadEntry {
238 NPadType joy_styles;
239 NPadAssignments pad_assignment;
240
241 ColorReadError single_color_error;
242 ControllerColor single_color;
243
244 ColorReadError dual_color_error;
245 ControllerColor left_color;
246 ControllerColor right_color;
247
248 NPadGeneric main_controller_states;
249 NPadGeneric handheld_states;
250 NPadGeneric dual_states;
251 NPadGeneric left_joy_states;
252 NPadGeneric right_joy_states;
253 NPadGeneric pokeball_states;
254 NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be
255 // relying on this for the time being
256 INSERT_PADDING_BYTES(
257 0x708 *
258 6); // TODO(ogniK): SixAxis states, require more information before implementation
259 NPadDevice device_type;
260 NPadProperties properties;
261 INSERT_PADDING_WORDS(1);
262 std::array<u32, 3> battery_level;
263 INSERT_PADDING_BYTES(0x5c);
264 INSERT_PADDING_BYTES(0xdf8);
265 };
266 static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
267
268 struct ControllerHolder {
269 Controller_NPad::NPadControllerType type;
270 bool is_connected;
271 };
272
273 NPadType style{};
274 std::array<NPadEntry, 10> shared_memory_entries{};
275 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
276 buttons;
277 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
278 std::vector<u32> supported_npad_id_types{};
279 NpadHoldType hold_type{NpadHoldType::Vertical};
280 Kernel::SharedPtr<Kernel::Event> styleset_changed_event;
281 Vibration last_processed_vibration{};
282 std::array<ControllerHolder, 10> connected_controllers{};
283 bool can_controllers_vibrate{true};
284
285 void InitNewlyAddedControler(std::size_t controller_idx);
286 bool IsControllerSupported(NPadControllerType controller) const;
287 NPadControllerType DecideBestController(NPadControllerType priority) const;
288};
289} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
new file mode 100644
index 000000000..02fcfadd9
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -0,0 +1,39 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/stubbed.h"
9
10namespace Service::HID {
11
12Controller_Stubbed::Controller_Stubbed() = default;
13Controller_Stubbed::~Controller_Stubbed() = default;
14
15void Controller_Stubbed::OnInit() {}
16
17void Controller_Stubbed::OnRelease() {}
18
19void Controller_Stubbed::OnUpdate(u8* data, std::size_t size) {
20 if (!smart_update) {
21 return;
22 }
23
24 CommonHeader header{};
25 header.timestamp = CoreTiming::GetTicks();
26 header.total_entry_count = 17;
27 header.entry_count = 0;
28 header.last_entry_index = 0;
29
30 std::memcpy(data + common_offset, &header, sizeof(CommonHeader));
31}
32
33void Controller_Stubbed::OnLoadInputDevices() {}
34
35void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
36 common_offset = off;
37 smart_update = true;
38}
39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
new file mode 100644
index 000000000..4a21c643e
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -0,0 +1,34 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/hle/service/hid/controllers/controller_base.h"
9
10namespace Service::HID {
11class Controller_Stubbed final : public ControllerBase {
12public:
13 Controller_Stubbed();
14 ~Controller_Stubbed() override;
15
16 // Called when the controller is initialized
17 void OnInit() override;
18
19 // When the controller is released
20 void OnRelease() override;
21
22 // When the controller is requesting an update for the shared memory
23 void OnUpdate(u8* data, std::size_t size) override;
24
25 // Called when input devices should be loaded
26 void OnLoadInputDevices() override;
27
28 void SetCommonHeaderOffset(std::size_t off);
29
30private:
31 bool smart_update{};
32 std::size_t common_offset{};
33};
34} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
new file mode 100644
index 000000000..43efef803
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -0,0 +1,65 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/frontend/emu_window.h"
9#include "core/frontend/input.h"
10#include "core/hle/service/hid/controllers/touchscreen.h"
11#include "core/settings.h"
12
13namespace Service::HID {
14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
15
16Controller_Touchscreen::Controller_Touchscreen() = default;
17Controller_Touchscreen::~Controller_Touchscreen() = default;
18
19void Controller_Touchscreen::OnInit() {}
20
21void Controller_Touchscreen::OnRelease() {}
22
23void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
24 shared_memory.header.timestamp = CoreTiming::GetTicks();
25 shared_memory.header.total_entry_count = 17;
26
27 if (!IsControllerActivated()) {
28 shared_memory.header.entry_count = 0;
29 shared_memory.header.last_entry_index = 0;
30 return;
31 }
32 shared_memory.header.entry_count = 16;
33
34 const auto& last_entry =
35 shared_memory.shared_memory_entries[shared_memory.header.last_entry_index];
36 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
37 auto& cur_entry = shared_memory.shared_memory_entries[shared_memory.header.last_entry_index];
38
39 cur_entry.sampling_number = last_entry.sampling_number + 1;
40 cur_entry.sampling_number2 = cur_entry.sampling_number;
41
42 const auto [x, y, pressed] = touch_device->GetStatus();
43 auto& touch_entry = cur_entry.states[0];
44 if (pressed) {
45 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
46 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
47 touch_entry.diameter_x = 15;
48 touch_entry.diameter_y = 15;
49 touch_entry.rotation_angle = 0;
50 const u64 tick = CoreTiming::GetTicks();
51 touch_entry.delta_time = tick - last_touch;
52 last_touch = tick;
53 touch_entry.finger = 0;
54 cur_entry.entry_count = 1;
55 } else {
56 cur_entry.entry_count = 0;
57 }
58
59 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
60}
61
62void Controller_Touchscreen::OnLoadInputDevices() {
63 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
64}
65} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
new file mode 100644
index 000000000..e5db6e6ba
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -0,0 +1,63 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9#include "common/swap.h"
10#include "core/frontend/input.h"
11#include "core/hle/service/hid/controllers/controller_base.h"
12
13namespace Service::HID {
14class Controller_Touchscreen final : public ControllerBase {
15public:
16 Controller_Touchscreen();
17 ~Controller_Touchscreen() override;
18
19 // Called when the controller is initialized
20 void OnInit() override;
21
22 // When the controller is released
23 void OnRelease() override;
24
25 // When the controller is requesting an update for the shared memory
26 void OnUpdate(u8* data, std::size_t size) override;
27
28 // Called when input devices should be loaded
29 void OnLoadInputDevices() override;
30
31private:
32 struct TouchState {
33 u64_le delta_time;
34 u32_le attribute;
35 u32_le finger;
36 u32_le x;
37 u32_le y;
38 u32_le diameter_x;
39 u32_le diameter_y;
40 u32_le rotation_angle;
41 };
42 static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
43
44 struct TouchScreenEntry {
45 s64_le sampling_number;
46 s64_le sampling_number2;
47 s32_le entry_count;
48 std::array<TouchState, 16> states;
49 };
50 static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size");
51
52 struct TouchScreenSharedMemory {
53 CommonHeader header;
54 std::array<TouchScreenEntry, 17> shared_memory_entries{};
55 INSERT_PADDING_BYTES(0x3c8);
56 };
57 static_assert(sizeof(TouchScreenSharedMemory) == 0x3000,
58 "TouchScreenSharedMemory is an invalid size");
59 TouchScreenSharedMemory shared_memory{};
60 std::unique_ptr<Input::TouchDevice> touch_device;
61 s64_le last_touch{};
62};
63} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
new file mode 100644
index 000000000..cd397c70b
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -0,0 +1,45 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/xpad.h"
9
10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
12
13Controller_XPad::Controller_XPad() = default;
14Controller_XPad::~Controller_XPad() = default;
15
16void Controller_XPad::OnInit() {}
17
18void Controller_XPad::OnRelease() {}
19
20void Controller_XPad::OnUpdate(u8* data, std::size_t size) {
21 for (auto& xpad_entry : shared_memory.shared_memory_entries) {
22 xpad_entry.header.timestamp = CoreTiming::GetTicks();
23 xpad_entry.header.total_entry_count = 17;
24
25 if (!IsControllerActivated()) {
26 xpad_entry.header.entry_count = 0;
27 xpad_entry.header.last_entry_index = 0;
28 return;
29 }
30 xpad_entry.header.entry_count = 16;
31
32 const auto& last_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
33 xpad_entry.header.last_entry_index = (xpad_entry.header.last_entry_index + 1) % 17;
34 auto& cur_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
35
36 cur_entry.sampling_number = last_entry.sampling_number + 1;
37 cur_entry.sampling_number2 = cur_entry.sampling_number;
38 }
39 // TODO(ogniK): Update xpad states
40
41 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
42}
43
44void Controller_XPad::OnLoadInputDevices() {}
45} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
new file mode 100644
index 000000000..ff836989f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -0,0 +1,60 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9#include "common/swap.h"
10#include "core/hle/service/hid/controllers/controller_base.h"
11
12namespace Service::HID {
13class Controller_XPad final : public ControllerBase {
14public:
15 Controller_XPad();
16 ~Controller_XPad() override;
17
18 // Called when the controller is initialized
19 void OnInit() override;
20
21 // When the controller is released
22 void OnRelease() override;
23
24 // When the controller is requesting an update for the shared memory
25 void OnUpdate(u8* data, std::size_t size) override;
26
27 // Called when input devices should be loaded
28 void OnLoadInputDevices() override;
29
30private:
31 struct AnalogStick {
32 s32_le x;
33 s32_le y;
34 };
35 static_assert(sizeof(AnalogStick) == 0x8, "AnalogStick is an invalid size");
36
37 struct XPadState {
38 s64_le sampling_number;
39 s64_le sampling_number2;
40 s32_le attributes;
41 u32_le pad_states;
42 AnalogStick x_stick;
43 AnalogStick y_stick;
44 };
45 static_assert(sizeof(XPadState) == 0x28, "XPadState is an invalid size");
46
47 struct XPadEntry {
48 CommonHeader header;
49 std::array<XPadState, 17> pad_states{};
50 INSERT_PADDING_BYTES(0x138);
51 };
52 static_assert(sizeof(XPadEntry) == 0x400, "XPadEntry is an invalid size");
53
54 struct SharedMemory {
55 std::array<XPadEntry, 4> shared_memory_entries{};
56 };
57 static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
58 SharedMemory shared_memory{};
59};
60} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 7c6b0a4e6..a9aa9ec78 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
6#include "common/common_types.h"
5#include "common/logging/log.h" 7#include "common/logging/log.h"
6#include "core/core.h" 8#include "core/core.h"
7#include "core/core_timing.h" 9#include "core/core_timing.h"
@@ -19,6 +21,16 @@
19#include "core/hle/service/service.h" 21#include "core/hle/service/service.h"
20#include "core/settings.h" 22#include "core/settings.h"
21 23
24#include "core/hle/service/hid/controllers/controller_base.h"
25#include "core/hle/service/hid/controllers/debug_pad.h"
26#include "core/hle/service/hid/controllers/gesture.h"
27#include "core/hle/service/hid/controllers/keyboard.h"
28#include "core/hle/service/hid/controllers/mouse.h"
29#include "core/hle/service/hid/controllers/npad.h"
30#include "core/hle/service/hid/controllers/stubbed.h"
31#include "core/hle/service/hid/controllers/touchscreen.h"
32#include "core/hle/service/hid/controllers/xpad.h"
33
22namespace Service::HID { 34namespace Service::HID {
23 35
24// Updating period for each HID device. 36// Updating period for each HID device.
@@ -26,6 +38,22 @@ namespace Service::HID {
26constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 38constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
27constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 39constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
28constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 40constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
42enum class HidController : std::size_t {
43 DebugPad,
44 Touchscreen,
45 Mouse,
46 Keyboard,
47 XPad,
48 Unknown1,
49 Unknown2,
50 Unknown3,
51 SixAxisSensor,
52 NPad,
53 Gesture,
54
55 MaxControllers,
56};
29 57
30class IAppletResource final : public ServiceFramework<IAppletResource> { 58class IAppletResource final : public ServiceFramework<IAppletResource> {
31public: 59public:
@@ -37,19 +65,57 @@ public:
37 65
38 auto& kernel = Core::System::GetInstance().Kernel(); 66 auto& kernel = Core::System::GetInstance().Kernel();
39 shared_mem = Kernel::SharedMemory::Create( 67 shared_mem = Kernel::SharedMemory::Create(
40 kernel, nullptr, 0x40000, Kernel::MemoryPermission::ReadWrite, 68 kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
41 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory"); 69 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
42 70
71 MakeController<Controller_DebugPad>(HidController::DebugPad);
72 MakeController<Controller_Touchscreen>(HidController::Touchscreen);
73 MakeController<Controller_Mouse>(HidController::Mouse);
74 MakeController<Controller_Keyboard>(HidController::Keyboard);
75 MakeController<Controller_XPad>(HidController::XPad);
76 MakeController<Controller_Stubbed>(HidController::Unknown1);
77 MakeController<Controller_Stubbed>(HidController::Unknown2);
78 MakeController<Controller_Stubbed>(HidController::Unknown3);
79 MakeController<Controller_Stubbed>(HidController::SixAxisSensor);
80 MakeController<Controller_NPad>(HidController::NPad);
81 MakeController<Controller_Gesture>(HidController::Gesture);
82
83 // Homebrew doesn't try to activate some controllers, so we activate them by default
84 GetController<Controller_NPad>(HidController::NPad).ActivateController();
85 GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController();
86
87 GetController<Controller_Stubbed>(HidController::Unknown1).SetCommonHeaderOffset(0x4c00);
88 GetController<Controller_Stubbed>(HidController::Unknown2).SetCommonHeaderOffset(0x4e00);
89 GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
90
43 // Register update callbacks 91 // Register update callbacks
44 pad_update_event = CoreTiming::RegisterEvent( 92 pad_update_event = CoreTiming::RegisterEvent(
45 "HID::UpdatePadCallback", 93 "HID::UpdatePadCallback",
46 [this](u64 userdata, int cycles_late) { UpdatePadCallback(userdata, cycles_late); }); 94 [this](u64 userdata, int cycles_late) { UpdateControllers(userdata, cycles_late); });
47 95
48 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) 96 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
49 97
50 CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); 98 CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
51 } 99 }
52 100
101 void ActivateController(HidController controller) {
102 controllers[static_cast<size_t>(controller)]->ActivateController();
103 }
104
105 void DeactivateController(HidController controller) {
106 controllers[static_cast<size_t>(controller)]->DeactivateController();
107 }
108
109 template <typename T>
110 void MakeController(HidController controller) {
111 controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>();
112 }
113
114 template <typename T>
115 T& GetController(HidController controller) {
116 return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
117 }
118
53 ~IAppletResource() { 119 ~IAppletResource() {
54 CoreTiming::UnscheduleEvent(pad_update_event, 0); 120 CoreTiming::UnscheduleEvent(pad_update_event, 0);
55 } 121 }
@@ -62,200 +128,15 @@ private:
62 LOG_DEBUG(Service_HID, "called"); 128 LOG_DEBUG(Service_HID, "called");
63 } 129 }
64 130
65 void LoadInputDevices() { 131 void UpdateControllers(u64 userdata, int cycles_late) {
66 std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, 132 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
67 Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END, 133 for (const auto& controller : controllers) {
68 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); 134 if (should_reload) {
69 std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, 135 controller->OnLoadInputDevices();
70 Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
71 sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
72 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
73 // TODO(shinyquagsire23): gyro, mouse, keyboard
74 }
75
76 void UpdatePadCallback(u64 userdata, int cycles_late) {
77 SharedMemory mem{};
78 std::memcpy(&mem, shared_mem->GetPointer(), sizeof(SharedMemory));
79
80 if (Settings::values.is_device_reload_pending.exchange(false))
81 LoadInputDevices();
82
83 // Set up controllers as neon red+blue Joy-Con attached to console
84 ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header;
85 controller_header.type = ControllerType_Handheld;
86 controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent;
87 controller_header.right_color_body = JOYCON_BODY_NEON_RED;
88 controller_header.right_color_buttons = JOYCON_BUTTONS_NEON_RED;
89 controller_header.left_color_body = JOYCON_BODY_NEON_BLUE;
90 controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE;
91
92 for (std::size_t controller = 0; controller < mem.controllers.size(); controller++) {
93 for (auto& layout : mem.controllers[controller].layouts) {
94 layout.header.num_entries = HID_NUM_ENTRIES;
95 layout.header.max_entry_index = HID_NUM_ENTRIES - 1;
96
97 // HID shared memory stores the state of the past 17 samples in a circlular buffer,
98 // each with a timestamp in number of samples since boot.
99 const ControllerInputEntry& last_entry = layout.entries[layout.header.latest_entry];
100
101 layout.header.timestamp_ticks = CoreTiming::GetTicks();
102 layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES;
103
104 ControllerInputEntry& entry = layout.entries[layout.header.latest_entry];
105 entry.timestamp = last_entry.timestamp + 1;
106 // TODO(shinyquagsire23): Is this always identical to timestamp?
107 entry.timestamp_2 = entry.timestamp;
108
109 // TODO(shinyquagsire23): More than just handheld input
110 if (controller != Controller_Handheld)
111 continue;
112
113 entry.connection_state = ConnectionState_Connected | ConnectionState_Wired;
114
115 // TODO(shinyquagsire23): Set up some LUTs for each layout mapping in the future?
116 // For now everything is just the default handheld layout, but split Joy-Con will
117 // rotate the face buttons and directions for certain layouts.
118 ControllerPadState& state = entry.buttons;
119 using namespace Settings::NativeButton;
120 state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
121 state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
122 state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
123 state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
124 state.lstick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
125 state.rstick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
126 state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
127 state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
128 state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
129 state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
130 state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
131 state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
132
133 state.dleft.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
134 state.dup.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
135 state.dright.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
136 state.ddown.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
137
138 state.lstick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
139 state.lstick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
140 state.lstick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
141 state.lstick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
142
143 state.rstick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
144 state.rstick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
145 state.rstick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
146 state.rstick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
147
148 state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
149 state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
150
151 const auto [stick_l_x_f, stick_l_y_f] = sticks[Joystick_Left]->GetStatus();
152 const auto [stick_r_x_f, stick_r_y_f] = sticks[Joystick_Right]->GetStatus();
153 entry.joystick_left_x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
154 entry.joystick_left_y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
155 entry.joystick_right_x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
156 entry.joystick_right_y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
157 } 136 }
137 controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
158 } 138 }
159 139
160 TouchScreen& touchscreen = mem.touchscreen;
161 const u64 last_entry = touchscreen.header.latest_entry;
162 const u64 curr_entry = (last_entry + 1) % touchscreen.entries.size();
163 const u64 timestamp = CoreTiming::GetTicks();
164 const u64 sample_counter = touchscreen.entries[last_entry].header.timestamp + 1;
165 touchscreen.header.timestamp_ticks = timestamp;
166 touchscreen.header.num_entries = touchscreen.entries.size();
167 touchscreen.header.latest_entry = curr_entry;
168 touchscreen.header.max_entry_index = touchscreen.entries.size();
169 touchscreen.header.timestamp = timestamp;
170 touchscreen.entries[curr_entry].header.timestamp = sample_counter;
171
172 TouchScreenEntryTouch touch_entry{};
173 auto [x, y, pressed] = touch_device->GetStatus();
174 touch_entry.timestamp = timestamp;
175 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
176 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
177 touch_entry.touch_index = 0;
178
179 // TODO(DarkLordZach): Maybe try to derive these from EmuWindow?
180 touch_entry.diameter_x = 15;
181 touch_entry.diameter_y = 15;
182 touch_entry.angle = 0;
183
184 // TODO(DarkLordZach): Implement multi-touch support
185 if (pressed) {
186 touchscreen.entries[curr_entry].header.num_touches = 1;
187 touchscreen.entries[curr_entry].touches[0] = touch_entry;
188 } else {
189 touchscreen.entries[curr_entry].header.num_touches = 0;
190 }
191
192 // TODO(shinyquagsire23): Properly implement mouse
193 Mouse& mouse = mem.mouse;
194 const u64 last_mouse_entry = mouse.header.latest_entry;
195 const u64 curr_mouse_entry = (mouse.header.latest_entry + 1) % mouse.entries.size();
196 const u64 mouse_sample_counter = mouse.entries[last_mouse_entry].timestamp + 1;
197 mouse.header.timestamp_ticks = timestamp;
198 mouse.header.num_entries = mouse.entries.size();
199 mouse.header.max_entry_index = mouse.entries.size();
200 mouse.header.latest_entry = curr_mouse_entry;
201
202 mouse.entries[curr_mouse_entry].timestamp = mouse_sample_counter;
203 mouse.entries[curr_mouse_entry].timestamp_2 = mouse_sample_counter;
204
205 // TODO(shinyquagsire23): Properly implement keyboard
206 Keyboard& keyboard = mem.keyboard;
207 const u64 last_keyboard_entry = keyboard.header.latest_entry;
208 const u64 curr_keyboard_entry =
209 (keyboard.header.latest_entry + 1) % keyboard.entries.size();
210 const u64 keyboard_sample_counter = keyboard.entries[last_keyboard_entry].timestamp + 1;
211 keyboard.header.timestamp_ticks = timestamp;
212 keyboard.header.num_entries = keyboard.entries.size();
213 keyboard.header.latest_entry = last_keyboard_entry;
214 keyboard.header.max_entry_index = keyboard.entries.size();
215
216 keyboard.entries[curr_keyboard_entry].timestamp = keyboard_sample_counter;
217 keyboard.entries[curr_keyboard_entry].timestamp_2 = keyboard_sample_counter;
218
219 // TODO(shinyquagsire23): Figure out what any of these are
220 for (auto& input : mem.unk_input_1) {
221 const u64 last_input_entry = input.header.latest_entry;
222 const u64 curr_input_entry = (input.header.latest_entry + 1) % input.entries.size();
223 const u64 input_sample_counter = input.entries[last_input_entry].timestamp + 1;
224
225 input.header.timestamp_ticks = timestamp;
226 input.header.num_entries = input.entries.size();
227 input.header.latest_entry = last_input_entry;
228 input.header.max_entry_index = input.entries.size();
229
230 input.entries[curr_input_entry].timestamp = input_sample_counter;
231 input.entries[curr_input_entry].timestamp_2 = input_sample_counter;
232 }
233
234 for (auto& input : mem.unk_input_2) {
235 input.header.timestamp_ticks = timestamp;
236 input.header.num_entries = 17;
237 input.header.latest_entry = 0;
238 input.header.max_entry_index = 0;
239 }
240
241 UnkInput3& input = mem.unk_input_3;
242 const u64 last_input_entry = input.header.latest_entry;
243 const u64 curr_input_entry = (input.header.latest_entry + 1) % input.entries.size();
244 const u64 input_sample_counter = input.entries[last_input_entry].timestamp + 1;
245
246 input.header.timestamp_ticks = timestamp;
247 input.header.num_entries = input.entries.size();
248 input.header.latest_entry = last_input_entry;
249 input.header.max_entry_index = input.entries.size();
250
251 input.entries[curr_input_entry].timestamp = input_sample_counter;
252 input.entries[curr_input_entry].timestamp_2 = input_sample_counter;
253
254 // TODO(shinyquagsire23): Signal events
255
256 std::memcpy(shared_mem->GetPointer(), &mem, sizeof(SharedMemory));
257
258 // Reschedule recurrent event
259 CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); 140 CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
260 } 141 }
261 142
@@ -265,11 +146,8 @@ private:
265 // CoreTiming update events 146 // CoreTiming update events
266 CoreTiming::EventType* pad_update_event; 147 CoreTiming::EventType* pad_update_event;
267 148
268 // Stored input state info 149 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
269 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> 150 controllers{};
270 buttons;
271 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
272 std::unique_ptr<Input::TouchDevice> touch_device;
273}; 151};
274 152
275class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { 153class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
@@ -299,9 +177,10 @@ public:
299 {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"}, 177 {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"},
300 {21, &Hid::ActivateMouse, "ActivateMouse"}, 178 {21, &Hid::ActivateMouse, "ActivateMouse"},
301 {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, 179 {31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
180 {32, nullptr, "SendKeyboardLockKeyEvent"},
302 {40, nullptr, "AcquireXpadIdEventHandle"}, 181 {40, nullptr, "AcquireXpadIdEventHandle"},
303 {41, nullptr, "ReleaseXpadIdEventHandle"}, 182 {41, nullptr, "ReleaseXpadIdEventHandle"},
304 {51, nullptr, "ActivateXpad"}, 183 {51, &Hid::ActivateXpad, "ActivateXpad"},
305 {55, nullptr, "GetXpadIds"}, 184 {55, nullptr, "GetXpadIds"},
306 {56, nullptr, "ActivateJoyXpad"}, 185 {56, nullptr, "ActivateJoyXpad"},
307 {58, nullptr, "GetJoyXpadLifoHandle"}, 186 {58, nullptr, "GetJoyXpadLifoHandle"},
@@ -329,6 +208,7 @@ public:
329 {80, nullptr, "GetGyroscopeZeroDriftMode"}, 208 {80, nullptr, "GetGyroscopeZeroDriftMode"},
330 {81, nullptr, "ResetGyroscopeZeroDriftMode"}, 209 {81, nullptr, "ResetGyroscopeZeroDriftMode"},
331 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, 210 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
211 {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"},
332 {91, &Hid::ActivateGesture, "ActivateGesture"}, 212 {91, &Hid::ActivateGesture, "ActivateGesture"},
333 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, 213 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
334 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, 214 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
@@ -362,8 +242,8 @@ public:
362 {206, &Hid::SendVibrationValues, "SendVibrationValues"}, 242 {206, &Hid::SendVibrationValues, "SendVibrationValues"},
363 {207, nullptr, "SendVibrationGcErmCommand"}, 243 {207, nullptr, "SendVibrationGcErmCommand"},
364 {208, nullptr, "GetActualVibrationGcErmCommand"}, 244 {208, nullptr, "GetActualVibrationGcErmCommand"},
365 {209, nullptr, "BeginPermitVibrationSession"}, 245 {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
366 {210, nullptr, "EndPermitVibrationSession"}, 246 {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
367 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, 247 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
368 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, 248 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
369 {302, nullptr, "StopConsoleSixAxisSensor"}, 249 {302, nullptr, "StopConsoleSixAxisSensor"},
@@ -374,6 +254,7 @@ public:
374 {307, nullptr, "FinalizeSevenSixAxisSensor"}, 254 {307, nullptr, "FinalizeSevenSixAxisSensor"},
375 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, 255 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
376 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, 256 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
257 {310, nullptr, "ResetSevenSixAxisSensorTimestamp"},
377 {400, nullptr, "IsUsbFullKeyControllerEnabled"}, 258 {400, nullptr, "IsUsbFullKeyControllerEnabled"},
378 {401, nullptr, "EnableUsbFullKeyController"}, 259 {401, nullptr, "EnableUsbFullKeyController"},
379 {402, nullptr, "IsUsbFullKeyControllerConnected"}, 260 {402, nullptr, "IsUsbFullKeyControllerConnected"},
@@ -389,28 +270,35 @@ public:
389 {505, nullptr, "SetPalmaFrModeType"}, 270 {505, nullptr, "SetPalmaFrModeType"},
390 {506, nullptr, "ReadPalmaStep"}, 271 {506, nullptr, "ReadPalmaStep"},
391 {507, nullptr, "EnablePalmaStep"}, 272 {507, nullptr, "EnablePalmaStep"},
392 {508, nullptr, "SuspendPalmaStep"}, 273 {508, nullptr, "ResetPalmaStep"},
393 {509, nullptr, "ResetPalmaStep"}, 274 {509, nullptr, "ReadPalmaApplicationSection"},
394 {510, nullptr, "ReadPalmaApplicationSection"}, 275 {510, nullptr, "WritePalmaApplicationSection"},
395 {511, nullptr, "WritePalmaApplicationSection"}, 276 {511, nullptr, "ReadPalmaUniqueCode"},
396 {512, nullptr, "ReadPalmaUniqueCode"}, 277 {512, nullptr, "SetPalmaUniqueCodeInvalid"},
397 {513, nullptr, "SetPalmaUniqueCodeInvalid"}, 278 {513, nullptr, "WritePalmaActivityEntry"},
279 {514, nullptr, "WritePalmaRgbLedPatternEntry"},
280 {515, nullptr, "WritePalmaWaveEntry"},
281 {516, nullptr, "SetPalmaDataBaseIdentificationVersion"},
282 {517, nullptr, "GetPalmaDataBaseIdentificationVersion"},
283 {518, nullptr, "SuspendPalmaFeature"},
284 {519, nullptr, "GetPalmaOperationResult"},
285 {520, nullptr, "ReadPalmaPlayLog"},
286 {521, nullptr, "ResetPalmaPlayLog"},
287 {522, nullptr, "SetIsPalmaAllConnectable"},
288 {523, nullptr, "SetIsPalmaPairedConnectable"},
289 {524, nullptr, "PairPalma"},
290 {525, nullptr, "SetPalmaBoostMode"},
398 {1000, nullptr, "SetNpadCommunicationMode"}, 291 {1000, nullptr, "SetNpadCommunicationMode"},
399 {1001, nullptr, "GetNpadCommunicationMode"}, 292 {1001, nullptr, "GetNpadCommunicationMode"},
400 }; 293 };
401 // clang-format on 294 // clang-format on
402 295
403 RegisterHandlers(functions); 296 RegisterHandlers(functions);
404
405 auto& kernel = Core::System::GetInstance().Kernel();
406 event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "hid:EventHandle");
407 } 297 }
408 ~Hid() = default; 298 ~Hid() = default;
409 299
410private: 300private:
411 std::shared_ptr<IAppletResource> applet_resource; 301 std::shared_ptr<IAppletResource> applet_resource;
412 u32 joy_hold_type{0};
413 Kernel::SharedPtr<Kernel::Event> event;
414 302
415 void CreateAppletResource(Kernel::HLERequestContext& ctx) { 303 void CreateAppletResource(Kernel::HLERequestContext& ctx) {
416 if (applet_resource == nullptr) { 304 if (applet_resource == nullptr) {
@@ -423,31 +311,59 @@ private:
423 LOG_DEBUG(Service_HID, "called"); 311 LOG_DEBUG(Service_HID, "called");
424 } 312 }
425 313
314 void ActivateXpad(Kernel::HLERequestContext& ctx) {
315 applet_resource->ActivateController(HidController::XPad);
316 IPC::ResponseBuilder rb{ctx, 2};
317 rb.Push(RESULT_SUCCESS);
318 LOG_DEBUG(Service_HID, "called");
319 }
320
426 void ActivateDebugPad(Kernel::HLERequestContext& ctx) { 321 void ActivateDebugPad(Kernel::HLERequestContext& ctx) {
322 applet_resource->ActivateController(HidController::DebugPad);
427 IPC::ResponseBuilder rb{ctx, 2}; 323 IPC::ResponseBuilder rb{ctx, 2};
428 rb.Push(RESULT_SUCCESS); 324 rb.Push(RESULT_SUCCESS);
429 LOG_WARNING(Service_HID, "(STUBBED) called"); 325 LOG_DEBUG(Service_HID, "called");
430 } 326 }
431 327
432 void ActivateTouchScreen(Kernel::HLERequestContext& ctx) { 328 void ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
329 applet_resource->ActivateController(HidController::Touchscreen);
433 IPC::ResponseBuilder rb{ctx, 2}; 330 IPC::ResponseBuilder rb{ctx, 2};
434 rb.Push(RESULT_SUCCESS); 331 rb.Push(RESULT_SUCCESS);
435 LOG_WARNING(Service_HID, "(STUBBED) called"); 332 LOG_DEBUG(Service_HID, "called");
436 } 333 }
437 334
438 void ActivateMouse(Kernel::HLERequestContext& ctx) { 335 void ActivateMouse(Kernel::HLERequestContext& ctx) {
336 applet_resource->ActivateController(HidController::Mouse);
439 IPC::ResponseBuilder rb{ctx, 2}; 337 IPC::ResponseBuilder rb{ctx, 2};
440 rb.Push(RESULT_SUCCESS); 338 rb.Push(RESULT_SUCCESS);
441 LOG_WARNING(Service_HID, "(STUBBED) called"); 339 LOG_DEBUG(Service_HID, "called");
442 } 340 }
443 341
444 void ActivateKeyboard(Kernel::HLERequestContext& ctx) { 342 void ActivateKeyboard(Kernel::HLERequestContext& ctx) {
343 applet_resource->ActivateController(HidController::Keyboard);
445 IPC::ResponseBuilder rb{ctx, 2}; 344 IPC::ResponseBuilder rb{ctx, 2};
446 rb.Push(RESULT_SUCCESS); 345 rb.Push(RESULT_SUCCESS);
447 LOG_WARNING(Service_HID, "(STUBBED) called"); 346 LOG_DEBUG(Service_HID, "called");
347 }
348
349 void ActivateGesture(Kernel::HLERequestContext& ctx) {
350 applet_resource->ActivateController(HidController::Gesture);
351 IPC::ResponseBuilder rb{ctx, 2};
352 rb.Push(RESULT_SUCCESS);
353 LOG_DEBUG(Service_HID, "called");
354 }
355
356 void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
357 // Should have no effect with how our npad sets up the data
358 applet_resource->ActivateController(HidController::NPad);
359 IPC::ResponseBuilder rb{ctx, 2};
360 rb.Push(RESULT_SUCCESS);
361 LOG_DEBUG(Service_HID, "called");
448 } 362 }
449 363
450 void StartSixAxisSensor(Kernel::HLERequestContext& ctx) { 364 void StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
365 IPC::RequestParser rp{ctx};
366 auto handle = rp.PopRaw<u32>();
451 IPC::ResponseBuilder rb{ctx, 2}; 367 IPC::ResponseBuilder rb{ctx, 2};
452 rb.Push(RESULT_SUCCESS); 368 rb.Push(RESULT_SUCCESS);
453 LOG_WARNING(Service_HID, "(STUBBED) called"); 369 LOG_WARNING(Service_HID, "(STUBBED) called");
@@ -468,84 +384,168 @@ private:
468 } 384 }
469 385
470 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 386 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
387 IPC::RequestParser rp{ctx};
388 auto supported_styleset = rp.PopRaw<u32>();
389 applet_resource->GetController<Controller_NPad>(HidController::NPad)
390 .SetSupportedStyleSet({supported_styleset});
391
471 IPC::ResponseBuilder rb{ctx, 2}; 392 IPC::ResponseBuilder rb{ctx, 2};
472 rb.Push(RESULT_SUCCESS); 393 rb.Push(RESULT_SUCCESS);
473 LOG_WARNING(Service_HID, "(STUBBED) called"); 394
395 LOG_DEBUG(Service_HID, "called");
474 } 396 }
475 397
476 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 398 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
399 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
400
477 IPC::ResponseBuilder rb{ctx, 3}; 401 IPC::ResponseBuilder rb{ctx, 3};
478 rb.Push(RESULT_SUCCESS); 402 rb.Push(RESULT_SUCCESS);
479 rb.Push<u32>(0); 403 rb.Push<u32>(controller.GetSupportedStyleSet().raw);
480 LOG_WARNING(Service_HID, "(STUBBED) called"); 404 LOG_DEBUG(Service_HID, "called");
481 } 405 }
482 406
483 void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { 407 void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
408 applet_resource->GetController<Controller_NPad>(HidController::NPad)
409 .SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
484 IPC::ResponseBuilder rb{ctx, 2}; 410 IPC::ResponseBuilder rb{ctx, 2};
485 rb.Push(RESULT_SUCCESS); 411 rb.Push(RESULT_SUCCESS);
486 LOG_WARNING(Service_HID, "(STUBBED) called"); 412 LOG_DEBUG(Service_HID, "called");
487 } 413 }
488 414
489 void ActivateNpad(Kernel::HLERequestContext& ctx) { 415 void ActivateNpad(Kernel::HLERequestContext& ctx) {
490 IPC::ResponseBuilder rb{ctx, 2}; 416 IPC::ResponseBuilder rb{ctx, 2};
491 rb.Push(RESULT_SUCCESS); 417 rb.Push(RESULT_SUCCESS);
492 LOG_WARNING(Service_HID, "(STUBBED) called"); 418 applet_resource->ActivateController(HidController::NPad);
419 LOG_DEBUG(Service_HID, "called");
493 } 420 }
494 421
495 void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { 422 void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
423 IPC::RequestParser rp{ctx};
424 auto npad_id = rp.PopRaw<u32>();
496 IPC::ResponseBuilder rb{ctx, 2, 1}; 425 IPC::ResponseBuilder rb{ctx, 2, 1};
497 rb.Push(RESULT_SUCCESS); 426 rb.Push(RESULT_SUCCESS);
498 rb.PushCopyObjects(event); 427 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
499 LOG_WARNING(Service_HID, "(STUBBED) called"); 428 .GetStyleSetChangedEvent());
429 LOG_DEBUG(Service_HID, "called");
500 } 430 }
501 431
502 void DisconnectNpad(Kernel::HLERequestContext& ctx) { 432 void DisconnectNpad(Kernel::HLERequestContext& ctx) {
433 IPC::RequestParser rp{ctx};
434 auto npad_id = rp.PopRaw<u32>();
435 applet_resource->GetController<Controller_NPad>(HidController::NPad)
436 .DisconnectNPad(npad_id);
503 IPC::ResponseBuilder rb{ctx, 2}; 437 IPC::ResponseBuilder rb{ctx, 2};
504 rb.Push(RESULT_SUCCESS); 438 rb.Push(RESULT_SUCCESS);
505 LOG_WARNING(Service_HID, "(STUBBED) called"); 439 LOG_DEBUG(Service_HID, "called");
506 } 440 }
507 441
508 void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { 442 void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
509 IPC::ResponseBuilder rb{ctx, 2}; 443 IPC::RequestParser rp{ctx};
444 auto npad_id = rp.PopRaw<u32>();
445 IPC::ResponseBuilder rb{ctx, 4};
510 rb.Push(RESULT_SUCCESS); 446 rb.Push(RESULT_SUCCESS);
511 LOG_WARNING(Service_HID, "(STUBBED) called"); 447 rb.PushRaw<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad)
448 .GetLedPattern(npad_id)
449 .raw);
450 LOG_DEBUG(Service_HID, "called");
512 } 451 }
513 452
514 void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { 453 void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
454 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
455 IPC::RequestParser rp{ctx};
456 const auto hold_type = rp.PopRaw<u64>();
457 controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type});
458
515 IPC::ResponseBuilder rb{ctx, 2}; 459 IPC::ResponseBuilder rb{ctx, 2};
516 rb.Push(RESULT_SUCCESS); 460 rb.Push(RESULT_SUCCESS);
517 LOG_WARNING(Service_HID, "(STUBBED) called"); 461 LOG_DEBUG(Service_HID, "called");
518 } 462 }
519 463
520 void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { 464 void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
521 IPC::ResponseBuilder rb{ctx, 3}; 465 const auto& controller =
466 applet_resource->GetController<Controller_NPad>(HidController::NPad);
467 IPC::ResponseBuilder rb{ctx, 4};
522 rb.Push(RESULT_SUCCESS); 468 rb.Push(RESULT_SUCCESS);
523 rb.Push(joy_hold_type); 469 rb.Push<u64>(static_cast<u64>(controller.GetHoldType()));
524 LOG_WARNING(Service_HID, "(STUBBED) called"); 470 LOG_DEBUG(Service_HID, "called");
525 } 471 }
526 472
527 void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { 473 void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
474 IPC::RequestParser rp{ctx};
475 auto npad_id = rp.PopRaw<u32>();
528 IPC::ResponseBuilder rb{ctx, 2}; 476 IPC::ResponseBuilder rb{ctx, 2};
529 rb.Push(RESULT_SUCCESS); 477 rb.Push(RESULT_SUCCESS);
530 LOG_WARNING(Service_HID, "(STUBBED) called"); 478 LOG_WARNING(Service_HID, "(STUBBED) called");
531 } 479 }
532 480
481 void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
482 applet_resource->GetController<Controller_NPad>(HidController::NPad)
483 .SetVibrationEnabled(true);
484 IPC::ResponseBuilder rb{ctx, 2};
485 rb.Push(RESULT_SUCCESS);
486 LOG_DEBUG(Service_HID, "called");
487 }
488
489 void EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
490 applet_resource->GetController<Controller_NPad>(HidController::NPad)
491 .SetVibrationEnabled(false);
492 IPC::ResponseBuilder rb{ctx, 2};
493 rb.Push(RESULT_SUCCESS);
494 LOG_DEBUG(Service_HID, "called");
495 }
496
533 void SendVibrationValue(Kernel::HLERequestContext& ctx) { 497 void SendVibrationValue(Kernel::HLERequestContext& ctx) {
498 IPC::RequestParser rp{ctx};
499 const auto controller_id = rp.PopRaw<u32>();
500 const auto vibration_values = rp.PopRaw<Controller_NPad::Vibration>();
501
534 IPC::ResponseBuilder rb{ctx, 2}; 502 IPC::ResponseBuilder rb{ctx, 2};
535 rb.Push(RESULT_SUCCESS); 503 rb.Push(RESULT_SUCCESS);
536 LOG_WARNING(Service_HID, "(STUBBED) called"); 504
505 applet_resource->GetController<Controller_NPad>(HidController::NPad)
506 .VibrateController({controller_id}, {vibration_values});
507 LOG_DEBUG(Service_HID, "called");
537 } 508 }
538 509
539 void GetActualVibrationValue(Kernel::HLERequestContext& ctx) { 510 void SendVibrationValues(Kernel::HLERequestContext& ctx) {
511 const auto controllers = ctx.ReadBuffer(0);
512 const auto vibrations = ctx.ReadBuffer(1);
513
514 std::vector<u32> controller_list(controllers.size() / sizeof(u32));
515 std::vector<Controller_NPad::Vibration> vibration_list(vibrations.size() /
516 sizeof(Controller_NPad::Vibration));
517
518 std::memcpy(controller_list.data(), controllers.data(), controllers.size());
519 std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size());
520 std::transform(controller_list.begin(), controller_list.end(), controller_list.begin(),
521 [](u32 controller_id) { return controller_id - 3; });
522
523 applet_resource->GetController<Controller_NPad>(HidController::NPad)
524 .VibrateController(controller_list, vibration_list);
525
540 IPC::ResponseBuilder rb{ctx, 2}; 526 IPC::ResponseBuilder rb{ctx, 2};
541 rb.Push(RESULT_SUCCESS); 527 rb.Push(RESULT_SUCCESS);
542 LOG_WARNING(Service_HID, "(STUBBED) called"); 528 LOG_DEBUG(Service_HID, "called");
529 }
530
531 void GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
532 IPC::ResponseBuilder rb{ctx, 6};
533 rb.Push(RESULT_SUCCESS);
534 rb.PushRaw<Controller_NPad::Vibration>(
535 applet_resource->GetController<Controller_NPad>(HidController::NPad)
536 .GetLastVibration());
537 LOG_DEBUG(Service_HID, "called");
543 } 538 }
544 539
545 void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { 540 void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
541 IPC::RequestParser rp{ctx};
542 const auto npad_id = rp.PopRaw<u32>();
543 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
544 controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
545
546 IPC::ResponseBuilder rb{ctx, 2}; 546 IPC::ResponseBuilder rb{ctx, 2};
547 rb.Push(RESULT_SUCCESS); 547 rb.Push(RESULT_SUCCESS);
548 LOG_WARNING(Service_HID, "(STUBBED) called"); 548 LOG_DEBUG(Service_HID, "called");
549 } 549 }
550 550
551 void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { 551 void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
@@ -555,6 +555,8 @@ private:
555 } 555 }
556 556
557 void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { 557 void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
558 IPC::RequestParser rp{ctx};
559 auto mode = rp.PopRaw<u32>();
558 IPC::ResponseBuilder rb{ctx, 2}; 560 IPC::ResponseBuilder rb{ctx, 2};
559 rb.Push(RESULT_SUCCESS); 561 rb.Push(RESULT_SUCCESS);
560 LOG_WARNING(Service_HID, "(STUBBED) called"); 562 LOG_WARNING(Service_HID, "(STUBBED) called");
@@ -563,8 +565,9 @@ private:
563 void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { 565 void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
564 IPC::ResponseBuilder rb{ctx, 4}; 566 IPC::ResponseBuilder rb{ctx, 4};
565 rb.Push(RESULT_SUCCESS); 567 rb.Push(RESULT_SUCCESS);
566 rb.Push<u64>(0); 568 rb.Push<u32>(1);
567 LOG_WARNING(Service_HID, "(STUBBED) called"); 569 rb.Push<u32>(0);
570 LOG_DEBUG(Service_HID, "called");
568 } 571 }
569 572
570 void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { 573 void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
@@ -574,12 +577,6 @@ private:
574 LOG_DEBUG(Service_HID, "called"); 577 LOG_DEBUG(Service_HID, "called");
575 } 578 }
576 579
577 void SendVibrationValues(Kernel::HLERequestContext& ctx) {
578 IPC::ResponseBuilder rb{ctx, 2};
579 rb.Push(RESULT_SUCCESS);
580 LOG_WARNING(Service_HID, "(STUBBED) called");
581 }
582
583 void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 580 void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
584 IPC::ResponseBuilder rb{ctx, 2}; 581 IPC::ResponseBuilder rb{ctx, 2};
585 rb.Push(RESULT_SUCCESS); 582 rb.Push(RESULT_SUCCESS);
@@ -597,18 +594,6 @@ private:
597 rb.Push(RESULT_SUCCESS); 594 rb.Push(RESULT_SUCCESS);
598 LOG_WARNING(Service_HID, "(STUBBED) called"); 595 LOG_WARNING(Service_HID, "(STUBBED) called");
599 } 596 }
600
601 void ActivateGesture(Kernel::HLERequestContext& ctx) {
602 IPC::ResponseBuilder rb{ctx, 2};
603 rb.Push(RESULT_SUCCESS);
604 LOG_WARNING(Service_HID, "(STUBBED) called");
605 }
606
607 void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
608 IPC::ResponseBuilder rb{ctx, 2};
609 rb.Push(RESULT_SUCCESS);
610 LOG_WARNING(Service_HID, "(STUBBED) called");
611 }
612}; 597};
613 598
614class HidDbg final : public ServiceFramework<HidDbg> { 599class HidDbg final : public ServiceFramework<HidDbg> {
@@ -650,6 +635,7 @@ public:
650 {140, nullptr, "DeactivateConsoleSixAxisSensor"}, 635 {140, nullptr, "DeactivateConsoleSixAxisSensor"},
651 {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"}, 636 {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
652 {142, nullptr, "DeactivateSevenSixAxisSensor"}, 637 {142, nullptr, "DeactivateSevenSixAxisSensor"},
638 {143, nullptr, "GetConsoleSixAxisSensorCountStates"},
653 {201, nullptr, "ActivateFirmwareUpdate"}, 639 {201, nullptr, "ActivateFirmwareUpdate"},
654 {202, nullptr, "DeactivateFirmwareUpdate"}, 640 {202, nullptr, "DeactivateFirmwareUpdate"},
655 {203, nullptr, "StartFirmwareUpdate"}, 641 {203, nullptr, "StartFirmwareUpdate"},
@@ -660,12 +646,23 @@ public:
660 {208, nullptr, "StartFirmwareUpdateForRevert"}, 646 {208, nullptr, "StartFirmwareUpdateForRevert"},
661 {209, nullptr, "GetAvailableFirmwareVersionForRevert"}, 647 {209, nullptr, "GetAvailableFirmwareVersionForRevert"},
662 {210, nullptr, "IsFirmwareUpdatingDevice"}, 648 {210, nullptr, "IsFirmwareUpdatingDevice"},
649 {211, nullptr, "StartFirmwareUpdateIndividual"},
650 {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
651 {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
663 {221, nullptr, "UpdateControllerColor"}, 652 {221, nullptr, "UpdateControllerColor"},
664 {222, nullptr, "ConnectUsbPadsAsync"}, 653 {222, nullptr, "ConnectUsbPadsAsync"},
665 {223, nullptr, "DisconnectUsbPadsAsync"}, 654 {223, nullptr, "DisconnectUsbPadsAsync"},
666 {224, nullptr, "UpdateDesignInfo"}, 655 {224, nullptr, "UpdateDesignInfo"},
667 {225, nullptr, "GetUniquePadDriverState"}, 656 {225, nullptr, "GetUniquePadDriverState"},
668 {226, nullptr, "GetSixAxisSensorDriverStates"}, 657 {226, nullptr, "GetSixAxisSensorDriverStates"},
658 {227, nullptr, "GetRxPacketHistory"},
659 {228, nullptr, "AcquireOperationEventHandle"},
660 {229, nullptr, "ReadSerialFlash"},
661 {230, nullptr, "WriteSerialFlash"},
662 {231, nullptr, "GetOperationResult"},
663 {232, nullptr, "EnableShipmentMode"},
664 {233, nullptr, "ClearPairingInfo"},
665 {234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
669 {301, nullptr, "GetAbstractedPadHandles"}, 666 {301, nullptr, "GetAbstractedPadHandles"},
670 {302, nullptr, "GetAbstractedPadState"}, 667 {302, nullptr, "GetAbstractedPadState"},
671 {303, nullptr, "GetAbstractedPadsState"}, 668 {303, nullptr, "GetAbstractedPadsState"},
@@ -673,6 +670,8 @@ public:
673 {322, nullptr, "UnsetAutoPilotVirtualPadState"}, 670 {322, nullptr, "UnsetAutoPilotVirtualPadState"},
674 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, 671 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
675 {350, nullptr, "AddRegisteredDevice"}, 672 {350, nullptr, "AddRegisteredDevice"},
673 {400, nullptr, "DisableExternalMcuOnNxDevice"},
674 {401, nullptr, "DisableRailDeviceFiltering"},
676 }; 675 };
677 // clang-format on 676 // clang-format on
678 677
@@ -708,7 +707,9 @@ public:
708 {307, nullptr, "GetNpadSystemExtStyle"}, 707 {307, nullptr, "GetNpadSystemExtStyle"},
709 {308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, 708 {308, nullptr, "ApplyNpadSystemCommonPolicyFull"},
710 {309, nullptr, "GetNpadFullKeyGripColor"}, 709 {309, nullptr, "GetNpadFullKeyGripColor"},
710 {310, nullptr, "GetMaskedSupportedNpadStyleSet"},
711 {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, 711 {311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
712 {312, nullptr, "SetSupportedNpadStyleSetAll"},
712 {321, nullptr, "GetUniquePadsFromNpad"}, 713 {321, nullptr, "GetUniquePadsFromNpad"},
713 {322, nullptr, "GetIrSensorState"}, 714 {322, nullptr, "GetIrSensorState"},
714 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, 715 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
@@ -733,6 +734,7 @@ public:
733 {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"}, 734 {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"},
734 {547, nullptr, "GetAllowedBluetoothLinksCount"}, 735 {547, nullptr, "GetAllowedBluetoothLinksCount"},
735 {548, nullptr, "GetRegisteredDevices"}, 736 {548, nullptr, "GetRegisteredDevices"},
737 {549, nullptr, "GetConnectableRegisteredDevices"},
736 {700, nullptr, "ActivateUniquePad"}, 738 {700, nullptr, "ActivateUniquePad"},
737 {702, nullptr, "AcquireUniquePadConnectionEventHandle"}, 739 {702, nullptr, "AcquireUniquePadConnectionEventHandle"},
738 {703, nullptr, "GetUniquePadIds"}, 740 {703, nullptr, "GetUniquePadIds"},
@@ -761,6 +763,7 @@ public:
761 {850, nullptr, "IsUsbFullKeyControllerEnabled"}, 763 {850, nullptr, "IsUsbFullKeyControllerEnabled"},
762 {851, nullptr, "EnableUsbFullKeyController"}, 764 {851, nullptr, "EnableUsbFullKeyController"},
763 {852, nullptr, "IsUsbConnected"}, 765 {852, nullptr, "IsUsbConnected"},
766 {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
764 {900, nullptr, "ActivateInputDetector"}, 767 {900, nullptr, "ActivateInputDetector"},
765 {901, nullptr, "NotifyInputDetector"}, 768 {901, nullptr, "NotifyInputDetector"},
766 {1000, nullptr, "InitializeFirmwareUpdate"}, 769 {1000, nullptr, "InitializeFirmwareUpdate"},
@@ -780,6 +783,12 @@ public:
780 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, 783 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
781 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, 784 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
782 {1100, nullptr, "GetHidbusSystemServiceObject"}, 785 {1100, nullptr, "GetHidbusSystemServiceObject"},
786 {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"},
787 {1130, nullptr, "InitializeUsbFirmwareUpdate"},
788 {1131, nullptr, "FinalizeUsbFirmwareUpdate"},
789 {1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
790 {1133, nullptr, "StartUsbFirmwareUpdate"},
791 {1134, nullptr, "GetUsbFirmwareUpdateState"},
783 }; 792 };
784 // clang-format on 793 // clang-format on
785 794
@@ -818,6 +827,7 @@ public:
818 {11, nullptr, "EnableJoyPollingReceiveMode"}, 827 {11, nullptr, "EnableJoyPollingReceiveMode"},
819 {12, nullptr, "DisableJoyPollingReceiveMode"}, 828 {12, nullptr, "DisableJoyPollingReceiveMode"},
820 {13, nullptr, "GetPollingData"}, 829 {13, nullptr, "GetPollingData"},
830 {14, nullptr, "SetStatusManagerType"},
821 }; 831 };
822 // clang-format on 832 // clang-format on
823 833
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 88d926808..773035460 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -4,408 +4,12 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array> 7namespace SM {
8#include "common/bit_field.h" 8class ServiceManager;
9#include "common/common_types.h" 9}
10#include "core/hle/service/service.h"
11 10
12namespace Service::HID { 11namespace Service::HID {
13 12
14// Begin enums and output structs
15
16constexpr u32 HID_NUM_ENTRIES = 17;
17constexpr u32 HID_NUM_LAYOUTS = 7;
18constexpr s32 HID_JOYSTICK_MAX = 0x8000;
19constexpr s32 HID_JOYSTICK_MIN = -0x8000;
20
21constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
22constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
23constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
24constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
25
26enum ControllerType : u32 {
27 ControllerType_ProController = 1 << 0,
28 ControllerType_Handheld = 1 << 1,
29 ControllerType_JoyconPair = 1 << 2,
30 ControllerType_JoyconLeft = 1 << 3,
31 ControllerType_JoyconRight = 1 << 4,
32};
33
34enum ControllerLayoutType : u32 {
35 Layout_ProController = 0, // Pro Controller or HID gamepad
36 Layout_Handheld = 1, // Two Joy-Con docked to rails
37 Layout_Single = 2, // Horizontal single Joy-Con or pair of Joy-Con, adjusted for orientation
38 Layout_Left = 3, // Only raw left Joy-Con state, no orientation adjustment
39 Layout_Right = 4, // Only raw right Joy-Con state, no orientation adjustment
40 Layout_DefaultDigital = 5, // Same as next, but sticks have 8-direction values only
41 Layout_Default = 6, // Safe default, single Joy-Con have buttons/sticks rotated for orientation
42};
43
44enum ControllerColorDescription {
45 ColorDesc_ColorsNonexistent = 1 << 1,
46};
47
48enum ControllerConnectionState {
49 ConnectionState_Connected = 1 << 0,
50 ConnectionState_Wired = 1 << 1,
51};
52
53enum ControllerJoystick {
54 Joystick_Left = 0,
55 Joystick_Right = 1,
56};
57
58enum ControllerID {
59 Controller_Player1 = 0,
60 Controller_Player2 = 1,
61 Controller_Player3 = 2,
62 Controller_Player4 = 3,
63 Controller_Player5 = 4,
64 Controller_Player6 = 5,
65 Controller_Player7 = 6,
66 Controller_Player8 = 7,
67 Controller_Handheld = 8,
68 Controller_Unknown = 9,
69};
70
71// End enums and output structs
72
73// Begin UnkInput3
74
75struct UnkInput3Header {
76 u64 timestamp_ticks;
77 u64 num_entries;
78 u64 latest_entry;
79 u64 max_entry_index;
80};
81static_assert(sizeof(UnkInput3Header) == 0x20, "HID UnkInput3 header structure has incorrect size");
82
83struct UnkInput3Entry {
84 u64 timestamp;
85 u64 timestamp_2;
86 u64 unk_8;
87 u64 unk_10;
88 u64 unk_18;
89};
90static_assert(sizeof(UnkInput3Entry) == 0x28, "HID UnkInput3 entry structure has incorrect size");
91
92struct UnkInput3 {
93 UnkInput3Header header;
94 std::array<UnkInput3Entry, 17> entries;
95 std::array<u8, 0x138> padding;
96};
97static_assert(sizeof(UnkInput3) == 0x400, "HID UnkInput3 structure has incorrect size");
98
99// End UnkInput3
100
101// Begin TouchScreen
102
103struct TouchScreenHeader {
104 u64 timestamp_ticks;
105 u64 num_entries;
106 u64 latest_entry;
107 u64 max_entry_index;
108 u64 timestamp;
109};
110static_assert(sizeof(TouchScreenHeader) == 0x28,
111 "HID touch screen header structure has incorrect size");
112
113struct TouchScreenEntryHeader {
114 u64 timestamp;
115 u64 num_touches;
116};
117static_assert(sizeof(TouchScreenEntryHeader) == 0x10,
118 "HID touch screen entry header structure has incorrect size");
119
120struct TouchScreenEntryTouch {
121 u64 timestamp;
122 u32 padding;
123 u32 touch_index;
124 u32 x;
125 u32 y;
126 u32 diameter_x;
127 u32 diameter_y;
128 u32 angle;
129 u32 padding_2;
130};
131static_assert(sizeof(TouchScreenEntryTouch) == 0x28,
132 "HID touch screen touch structure has incorrect size");
133
134struct TouchScreenEntry {
135 TouchScreenEntryHeader header;
136 std::array<TouchScreenEntryTouch, 16> touches;
137 u64 unk;
138};
139static_assert(sizeof(TouchScreenEntry) == 0x298,
140 "HID touch screen entry structure has incorrect size");
141
142struct TouchScreen {
143 TouchScreenHeader header;
144 std::array<TouchScreenEntry, 17> entries;
145 std::array<u8, 0x3c0> padding;
146};
147static_assert(sizeof(TouchScreen) == 0x3000, "HID touch screen structure has incorrect size");
148
149// End TouchScreen
150
151// Begin Mouse
152
153struct MouseHeader {
154 u64 timestamp_ticks;
155 u64 num_entries;
156 u64 latest_entry;
157 u64 max_entry_index;
158};
159static_assert(sizeof(MouseHeader) == 0x20, "HID mouse header structure has incorrect size");
160
161struct MouseButtonState {
162 union {
163 u64 hex{};
164
165 // Buttons
166 BitField<0, 1, u64> left;
167 BitField<1, 1, u64> right;
168 BitField<2, 1, u64> middle;
169 BitField<3, 1, u64> forward;
170 BitField<4, 1, u64> back;
171 };
172};
173
174struct MouseEntry {
175 u64 timestamp;
176 u64 timestamp_2;
177 u32 x;
178 u32 y;
179 u32 velocity_x;
180 u32 velocity_y;
181 u32 scroll_velocity_x;
182 u32 scroll_velocity_y;
183 MouseButtonState buttons;
184};
185static_assert(sizeof(MouseEntry) == 0x30, "HID mouse entry structure has incorrect size");
186
187struct Mouse {
188 MouseHeader header;
189 std::array<MouseEntry, 17> entries;
190 std::array<u8, 0xB0> padding;
191};
192static_assert(sizeof(Mouse) == 0x400, "HID mouse structure has incorrect size");
193
194// End Mouse
195
196// Begin Keyboard
197
198struct KeyboardHeader {
199 u64 timestamp_ticks;
200 u64 num_entries;
201 u64 latest_entry;
202 u64 max_entry_index;
203};
204static_assert(sizeof(KeyboardHeader) == 0x20, "HID keyboard header structure has incorrect size");
205
206struct KeyboardModifierKeyState {
207 union {
208 u64 hex{};
209
210 // Buttons
211 BitField<0, 1, u64> lctrl;
212 BitField<1, 1, u64> lshift;
213 BitField<2, 1, u64> lalt;
214 BitField<3, 1, u64> lmeta;
215 BitField<4, 1, u64> rctrl;
216 BitField<5, 1, u64> rshift;
217 BitField<6, 1, u64> ralt;
218 BitField<7, 1, u64> rmeta;
219 BitField<8, 1, u64> capslock;
220 BitField<9, 1, u64> scrolllock;
221 BitField<10, 1, u64> numlock;
222 };
223};
224
225struct KeyboardEntry {
226 u64 timestamp;
227 u64 timestamp_2;
228 KeyboardModifierKeyState modifier;
229 u32 keys[8];
230};
231static_assert(sizeof(KeyboardEntry) == 0x38, "HID keyboard entry structure has incorrect size");
232
233struct Keyboard {
234 KeyboardHeader header;
235 std::array<KeyboardEntry, 17> entries;
236 std::array<u8, 0x28> padding;
237};
238static_assert(sizeof(Keyboard) == 0x400, "HID keyboard structure has incorrect size");
239
240// End Keyboard
241
242// Begin UnkInput1
243
244struct UnkInput1Header {
245 u64 timestamp_ticks;
246 u64 num_entries;
247 u64 latest_entry;
248 u64 max_entry_index;
249};
250static_assert(sizeof(UnkInput1Header) == 0x20, "HID UnkInput1 header structure has incorrect size");
251
252struct UnkInput1Entry {
253 u64 timestamp;
254 u64 timestamp_2;
255 u64 unk_8;
256 u64 unk_10;
257 u64 unk_18;
258};
259static_assert(sizeof(UnkInput1Entry) == 0x28, "HID UnkInput1 entry structure has incorrect size");
260
261struct UnkInput1 {
262 UnkInput1Header header;
263 std::array<UnkInput1Entry, 17> entries;
264 std::array<u8, 0x138> padding;
265};
266static_assert(sizeof(UnkInput1) == 0x400, "HID UnkInput1 structure has incorrect size");
267
268// End UnkInput1
269
270// Begin UnkInput2
271
272struct UnkInput2Header {
273 u64 timestamp_ticks;
274 u64 num_entries;
275 u64 latest_entry;
276 u64 max_entry_index;
277};
278static_assert(sizeof(UnkInput2Header) == 0x20, "HID UnkInput2 header structure has incorrect size");
279
280struct UnkInput2 {
281 UnkInput2Header header;
282 std::array<u8, 0x1E0> padding;
283};
284static_assert(sizeof(UnkInput2) == 0x200, "HID UnkInput2 structure has incorrect size");
285
286// End UnkInput2
287
288// Begin Controller
289
290struct ControllerMAC {
291 u64 timestamp;
292 std::array<u8, 0x8> mac;
293 u64 unk;
294 u64 timestamp_2;
295};
296static_assert(sizeof(ControllerMAC) == 0x20, "HID controller MAC structure has incorrect size");
297
298struct ControllerHeader {
299 u32 type;
300 u32 is_half;
301 u32 single_colors_descriptor;
302 u32 single_color_body;
303 u32 single_color_buttons;
304 u32 split_colors_descriptor;
305 u32 left_color_body;
306 u32 left_color_buttons;
307 u32 right_color_body;
308 u32 right_color_buttons;
309};
310static_assert(sizeof(ControllerHeader) == 0x28,
311 "HID controller header structure has incorrect size");
312
313struct ControllerLayoutHeader {
314 u64 timestamp_ticks;
315 u64 num_entries;
316 u64 latest_entry;
317 u64 max_entry_index;
318};
319static_assert(sizeof(ControllerLayoutHeader) == 0x20,
320 "HID controller layout header structure has incorrect size");
321
322struct ControllerPadState {
323 union {
324 u64 hex{};
325
326 // Buttons
327 BitField<0, 1, u64> a;
328 BitField<1, 1, u64> b;
329 BitField<2, 1, u64> x;
330 BitField<3, 1, u64> y;
331 BitField<4, 1, u64> lstick;
332 BitField<5, 1, u64> rstick;
333 BitField<6, 1, u64> l;
334 BitField<7, 1, u64> r;
335 BitField<8, 1, u64> zl;
336 BitField<9, 1, u64> zr;
337 BitField<10, 1, u64> plus;
338 BitField<11, 1, u64> minus;
339
340 // D-pad buttons
341 BitField<12, 1, u64> dleft;
342 BitField<13, 1, u64> dup;
343 BitField<14, 1, u64> dright;
344 BitField<15, 1, u64> ddown;
345
346 // Left stick directions
347 BitField<16, 1, u64> lstick_left;
348 BitField<17, 1, u64> lstick_up;
349 BitField<18, 1, u64> lstick_right;
350 BitField<19, 1, u64> lstick_down;
351
352 // Right stick directions
353 BitField<20, 1, u64> rstick_left;
354 BitField<21, 1, u64> rstick_up;
355 BitField<22, 1, u64> rstick_right;
356 BitField<23, 1, u64> rstick_down;
357
358 BitField<24, 1, u64> sl;
359 BitField<25, 1, u64> sr;
360 };
361};
362
363struct ControllerInputEntry {
364 u64 timestamp;
365 u64 timestamp_2;
366 ControllerPadState buttons;
367 s32 joystick_left_x;
368 s32 joystick_left_y;
369 s32 joystick_right_x;
370 s32 joystick_right_y;
371 u64 connection_state;
372};
373static_assert(sizeof(ControllerInputEntry) == 0x30,
374 "HID controller input entry structure has incorrect size");
375
376struct ControllerLayout {
377 ControllerLayoutHeader header;
378 std::array<ControllerInputEntry, 17> entries;
379};
380static_assert(sizeof(ControllerLayout) == 0x350,
381 "HID controller layout structure has incorrect size");
382
383struct Controller {
384 ControllerHeader header;
385 std::array<ControllerLayout, HID_NUM_LAYOUTS> layouts;
386 std::array<u8, 0x2a70> unk_1;
387 ControllerMAC mac_left;
388 ControllerMAC mac_right;
389 std::array<u8, 0xdf8> unk_2;
390};
391static_assert(sizeof(Controller) == 0x5000, "HID controller structure has incorrect size");
392
393// End Controller
394
395struct SharedMemory {
396 UnkInput3 unk_input_3;
397 TouchScreen touchscreen;
398 Mouse mouse;
399 Keyboard keyboard;
400 std::array<UnkInput1, 4> unk_input_1;
401 std::array<UnkInput2, 3> unk_input_2;
402 std::array<u8, 0x800> unk_section_8;
403 std::array<u8, 0x4000> controller_serials;
404 std::array<Controller, 10> controllers;
405 std::array<u8, 0x4600> unk_section_9;
406};
407static_assert(sizeof(SharedMemory) == 0x40000, "HID Shared Memory structure has incorrect size");
408
409/// Reload input devices. Used when input configuration changed 13/// Reload input devices. Used when input configuration changed
410void ReloadInputDevices(); 14void ReloadInputDevices();
411 15
diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp
index 7321584e1..164c57e18 100644
--- a/src/core/hle/service/lbl/lbl.cpp
+++ b/src/core/hle/service/lbl/lbl.cpp
@@ -18,35 +18,35 @@ public:
18 explicit LBL() : ServiceFramework{"lbl"} { 18 explicit LBL() : ServiceFramework{"lbl"} {
19 // clang-format off 19 // clang-format off
20 static const FunctionInfo functions[] = { 20 static const FunctionInfo functions[] = {
21 {0, nullptr, "Unknown1"}, 21 {0, nullptr, "SaveCurrentSetting"},
22 {1, nullptr, "Unknown2"}, 22 {1, nullptr, "LoadCurrentSetting"},
23 {2, nullptr, "Unknown3"}, 23 {2, nullptr, "SetCurrentBrightnessSetting"},
24 {3, nullptr, "GetCurrentBacklightLevel"}, 24 {3, nullptr, "GetCurrentBrightnessSetting"},
25 {4, nullptr, "Unknown4"}, 25 {4, nullptr, "ApplyCurrentBrightnessSettingToBacklight"},
26 {5, nullptr, "GetAlsComputedBacklightLevel"}, 26 {5, nullptr, "GetBrightnessSettingAppliedToBacklight"},
27 {6, nullptr, "TurnOffBacklight"}, 27 {6, nullptr, "SwitchBacklightOn"},
28 {7, nullptr, "TurnOnBacklight"}, 28 {7, nullptr, "SwitchBacklightOff"},
29 {8, nullptr, "GetBacklightStatus"}, 29 {8, nullptr, "GetBacklightSwitchStatus"},
30 {9, nullptr, "Unknown5"}, 30 {9, nullptr, "EnableDimming"},
31 {10, nullptr, "Unknown6"}, 31 {10, nullptr, "DisableDimming"},
32 {11, nullptr, "Unknown7"}, 32 {11, nullptr, "IsDimmingEnabled"},
33 {12, nullptr, "Unknown8"}, 33 {12, nullptr, "EnableAutoBrightnessControl"},
34 {13, nullptr, "Unknown9"}, 34 {13, nullptr, "DisableAutoBrightnessControl"},
35 {14, nullptr, "Unknown10"}, 35 {14, nullptr, "IsAutoBrightnessControlEnabled"},
36 {15, nullptr, "GetAutoBrightnessSetting"}, 36 {15, nullptr, "SetAmbientLightSensorValue"},
37 {16, nullptr, "ReadRawLightSensor"}, 37 {16, nullptr, "GetAmbientLightSensorValue"},
38 {17, nullptr, "Unknown11"}, 38 {17, nullptr, "SetBrightnessReflectionDelayLevel"},
39 {18, nullptr, "Unknown12"}, 39 {18, nullptr, "GetBrightnessReflectionDelayLevel"},
40 {19, nullptr, "Unknown13"}, 40 {19, nullptr, "SetCurrentBrightnessMapping"},
41 {20, nullptr, "Unknown14"}, 41 {20, nullptr, "GetCurrentBrightnessMapping"},
42 {21, nullptr, "Unknown15"}, 42 {21, nullptr, "SetCurrentAmbientLightSensorMapping"},
43 {22, nullptr, "Unknown16"}, 43 {22, nullptr, "GetCurrentAmbientLightSensorMapping"},
44 {23, nullptr, "Unknown17"}, 44 {23, nullptr, "IsAmbientLightSensorAvailable"},
45 {24, nullptr, "Unknown18"}, 45 {24, nullptr, "SetCurrentBrightnessSettingForVrMode"},
46 {25, nullptr, "Unknown19"}, 46 {25, nullptr, "GetCurrentBrightnessSettingForVrMode"},
47 {26, &LBL::EnableVrMode, "EnableVrMode"}, 47 {26, &LBL::EnableVrMode, "EnableVrMode"},
48 {27, &LBL::DisableVrMode, "DisableVrMode"}, 48 {27, &LBL::DisableVrMode, "DisableVrMode"},
49 {28, &LBL::GetVrMode, "GetVrMode"}, 49 {28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"},
50 }; 50 };
51 // clang-format on 51 // clang-format on
52 52
@@ -72,7 +72,7 @@ private:
72 LOG_DEBUG(Service_LBL, "called"); 72 LOG_DEBUG(Service_LBL, "called");
73 } 73 }
74 74
75 void GetVrMode(Kernel::HLERequestContext& ctx) { 75 void IsVrModeEnabled(Kernel::HLERequestContext& ctx) {
76 IPC::ResponseBuilder rb{ctx, 3}; 76 IPC::ResponseBuilder rb{ctx, 3};
77 rb.Push(RESULT_SUCCESS); 77 rb.Push(RESULT_SUCCESS);
78 rb.Push(vr_mode_enabled); 78 rb.Push(vr_mode_enabled);
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp
index 7b91bb258..e1f17a926 100644
--- a/src/core/hle/service/mm/mm_u.cpp
+++ b/src/core/hle/service/mm/mm_u.cpp
@@ -14,14 +14,14 @@ public:
14 explicit MM_U() : ServiceFramework{"mm:u"} { 14 explicit MM_U() : ServiceFramework{"mm:u"} {
15 // clang-format off 15 // clang-format off
16 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
17 {0, &MM_U::Initialize, "InitializeOld"}, 17 {0, &MM_U::Initialize, "Initialize"},
18 {1, &MM_U::Finalize, "FinalizeOld"}, 18 {1, &MM_U::Finalize, "Finalize"},
19 {2, &MM_U::SetAndWait, "SetAndWaitOld"}, 19 {2, &MM_U::SetAndWait, "SetAndWait"},
20 {3, &MM_U::Get, "GetOld"}, 20 {3, &MM_U::Get, "Get"},
21 {4, &MM_U::Initialize, "Initialize"}, 21 {4, &MM_U::InitializeWithId, "InitializeWithId"},
22 {5, &MM_U::Finalize, "Finalize"}, 22 {5, &MM_U::FinalizeWithId, "FinalizeWithId"},
23 {6, &MM_U::SetAndWait, "SetAndWait"}, 23 {6, &MM_U::SetAndWaitWithId, "SetAndWaitWithId"},
24 {7, &MM_U::Get, "Get"}, 24 {7, &MM_U::GetWithId, "GetWithId"},
25 }; 25 };
26 // clang-format on 26 // clang-format on
27 27
@@ -59,9 +59,43 @@ private:
59 rb.Push(current); 59 rb.Push(current);
60 } 60 }
61 61
62 void InitializeWithId(Kernel::HLERequestContext& ctx) {
63 LOG_WARNING(Service_MM, "(STUBBED) called");
64 IPC::ResponseBuilder rb{ctx, 3};
65 rb.Push(RESULT_SUCCESS);
66 rb.Push<u32>(id); // Any non zero value
67 }
68
69 void FinalizeWithId(Kernel::HLERequestContext& ctx) {
70 LOG_WARNING(Service_MM, "(STUBBED) called");
71 IPC::ResponseBuilder rb{ctx, 2};
72 rb.Push(RESULT_SUCCESS);
73 }
74
75 void SetAndWaitWithId(Kernel::HLERequestContext& ctx) {
76 IPC::RequestParser rp{ctx};
77 u32 input_id = rp.Pop<u32>();
78 min = rp.Pop<u32>();
79 max = rp.Pop<u32>();
80 current = min;
81
82 LOG_WARNING(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}",
83 input_id, min, max);
84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(RESULT_SUCCESS);
86 }
87
88 void GetWithId(Kernel::HLERequestContext& ctx) {
89 LOG_WARNING(Service_MM, "(STUBBED) called");
90 IPC::ResponseBuilder rb{ctx, 3};
91 rb.Push(RESULT_SUCCESS);
92 rb.Push(current);
93 }
94
62 u32 min{0}; 95 u32 min{0};
63 u32 max{0}; 96 u32 max{0};
64 u32 current{0}; 97 u32 current{0};
98 u32 id{1};
65}; 99};
66 100
67void InstallInterfaces(SM::ServiceManager& service_manager) { 101void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 8c07a05c2..39c0c1e63 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -144,7 +144,7 @@ private:
144 } 144 }
145 145
146 const u64 device_handle{0xDEAD}; 146 const u64 device_handle{0xDEAD};
147 const HID::ControllerID npad_id{HID::Controller_Player1}; 147 const u32 npad_id{0}; // This is the first player controller id
148 State state{State::NonInitialized}; 148 State state{State::NonInitialized};
149 DeviceState device_state{DeviceState::Initialized}; 149 DeviceState device_state{DeviceState::Initialized};
150 Kernel::SharedPtr<Kernel::Event> activate_event; 150 Kernel::SharedPtr<Kernel::Event> activate_event;
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 10611ed6a..75dcd94a3 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -219,6 +219,7 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
219 {35, nullptr, "GetScanData"}, 219 {35, nullptr, "GetScanData"},
220 {36, nullptr, "GetCurrentAccessPoint"}, 220 {36, nullptr, "GetCurrentAccessPoint"},
221 {37, nullptr, "Shutdown"}, 221 {37, nullptr, "Shutdown"},
222 {38, nullptr, "GetAllowedChannels"},
222 }; 223 };
223 RegisterHandlers(functions); 224 RegisterHandlers(functions);
224} 225}
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index 261ad539c..18091c9bb 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -71,6 +71,22 @@ public:
71 } 71 }
72}; 72};
73 73
74class NIM_ECA final : public ServiceFramework<NIM_ECA> {
75public:
76 explicit NIM_ECA() : ServiceFramework{"nim:eca"} {
77 // clang-format off
78 static const FunctionInfo functions[] = {
79 {0, nullptr, "CreateServerInterface"},
80 {1, nullptr, "RefreshDebugAvailability"},
81 {2, nullptr, "ClearDebugResponse"},
82 {3, nullptr, "RegisterDebugResponse"},
83 };
84 // clang-format on
85
86 RegisterHandlers(functions);
87 }
88};
89
74class NIM_SHP final : public ServiceFramework<NIM_SHP> { 90class NIM_SHP final : public ServiceFramework<NIM_SHP> {
75public: 91public:
76 explicit NIM_SHP() : ServiceFramework{"nim:shp"} { 92 explicit NIM_SHP() : ServiceFramework{"nim:shp"} {
@@ -214,6 +230,7 @@ private:
214 230
215void InstallInterfaces(SM::ServiceManager& sm) { 231void InstallInterfaces(SM::ServiceManager& sm) {
216 std::make_shared<NIM>()->InstallAsService(sm); 232 std::make_shared<NIM>()->InstallAsService(sm);
233 std::make_shared<NIM_ECA>()->InstallAsService(sm);
217 std::make_shared<NIM_SHP>()->InstallAsService(sm); 234 std::make_shared<NIM_SHP>()->InstallAsService(sm);
218 std::make_shared<NTC>()->InstallAsService(sm); 235 std::make_shared<NTC>()->InstallAsService(sm);
219} 236}
diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp
new file mode 100644
index 000000000..ccb6f9da9
--- /dev/null
+++ b/src/core/hle/service/npns/npns.cpp
@@ -0,0 +1,88 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6
7#include "core/hle/service/npns/npns.h"
8#include "core/hle/service/service.h"
9#include "core/hle/service/sm/sm.h"
10
11namespace Service::NPNS {
12
13class NPNS_S final : public ServiceFramework<NPNS_S> {
14public:
15 explicit NPNS_S() : ServiceFramework{"npns:s"} {
16 // clang-format off
17 static const FunctionInfo functions[] = {
18 {1, nullptr, "ListenAll"},
19 {2, nullptr, "ListenTo"},
20 {3, nullptr, "Receive"},
21 {4, nullptr, "ReceiveRaw"},
22 {5, nullptr, "GetReceiveEvent"},
23 {6, nullptr, "ListenUndelivered"},
24 {7, nullptr, "GetStateChangeEVent"},
25 {11, nullptr, "SubscribeTopic"},
26 {12, nullptr, "UnsubscribeTopic"},
27 {13, nullptr, "QueryIsTopicExist"},
28 {21, nullptr, "CreateToken"},
29 {22, nullptr, "CreateTokenWithApplicationId"},
30 {23, nullptr, "DestroyToken"},
31 {24, nullptr, "DestroyTokenWithApplicationId"},
32 {25, nullptr, "QueryIsTokenValid"},
33 {31, nullptr, "UploadTokenToBaaS"},
34 {32, nullptr, "DestroyTokenForBaaS"},
35 {33, nullptr, "CreateTokenForBaaS"},
36 {34, nullptr, "SetBaaSDeviceAccountIdList"},
37 {101, nullptr, "Suspend"},
38 {102, nullptr, "Resume"},
39 {103, nullptr, "GetState"},
40 {104, nullptr, "GetStatistics"},
41 {105, nullptr, "GetPlayReportRequestEvent"},
42 {111, nullptr, "GetJid"},
43 {112, nullptr, "CreateJid"},
44 {113, nullptr, "DestroyJid"},
45 {114, nullptr, "AttachJid"},
46 {115, nullptr, "DetachJid"},
47 {201, nullptr, "RequestChangeStateForceTimed"},
48 {102, nullptr, "RequestChangeStateForceAsync"},
49 };
50 // clang-format on
51
52 RegisterHandlers(functions);
53 }
54};
55
56class NPNS_U final : public ServiceFramework<NPNS_U> {
57public:
58 explicit NPNS_U() : ServiceFramework{"npns:u"} {
59 // clang-format off
60 static const FunctionInfo functions[] = {
61 {1, nullptr, "ListenAll"},
62 {2, nullptr, "ListenTo"},
63 {3, nullptr, "Receive"},
64 {4, nullptr, "ReceiveRaw"},
65 {5, nullptr, "GetReceiveEvent"},
66 {7, nullptr, "GetStateChangeEVent"},
67 {21, nullptr, "CreateToken"},
68 {23, nullptr, "DestroyToken"},
69 {25, nullptr, "QueryIsTokenValid"},
70 {26, nullptr, "ListenToMyApplicationId"},
71 {101, nullptr, "Suspend"},
72 {102, nullptr, "Resume"},
73 {103, nullptr, "GetState"},
74 {104, nullptr, "GetStatistics"},
75 {111, nullptr, "GetJid"},
76 };
77 // clang-format on
78
79 RegisterHandlers(functions);
80 }
81};
82
83void InstallInterfaces(SM::ServiceManager& sm) {
84 std::make_shared<NPNS_S>()->InstallAsService(sm);
85 std::make_shared<NPNS_U>()->InstallAsService(sm);
86}
87
88} // namespace Service::NPNS
diff --git a/src/core/hle/service/npns/npns.h b/src/core/hle/service/npns/npns.h
new file mode 100644
index 000000000..861cd3e48
--- /dev/null
+++ b/src/core/hle/service/npns/npns.h
@@ -0,0 +1,15 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service::SM {
8class ServiceManager;
9}
10
11namespace Service::NPNS {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::NPNS
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 98017267c..07c1381fe 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -93,13 +93,23 @@ public:
93 {86, nullptr, "EnableApplicationCrashReport"}, 93 {86, nullptr, "EnableApplicationCrashReport"},
94 {87, nullptr, "IsApplicationCrashReportEnabled"}, 94 {87, nullptr, "IsApplicationCrashReportEnabled"},
95 {90, nullptr, "BoostSystemMemoryResourceLimit"}, 95 {90, nullptr, "BoostSystemMemoryResourceLimit"},
96 {91, nullptr, "Unknown1"},
97 {92, nullptr, "Unknown2"},
98 {93, nullptr, "GetMainApplicationProgramIndex"},
99 {94, nullptr, "LaunchApplication2"},
100 {95, nullptr, "GetApplicationLaunchInfo"},
101 {96, nullptr, "AcquireApplicationLaunchInfo"},
102 {97, nullptr, "GetMainApplicationProgramIndex2"},
103 {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
96 {100, nullptr, "ResetToFactorySettings"}, 104 {100, nullptr, "ResetToFactorySettings"},
97 {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, 105 {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
98 {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, 106 {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
99 {200, nullptr, "CalculateUserSaveDataStatistics"}, 107 {200, nullptr, "CalculateUserSaveDataStatistics"},
100 {201, nullptr, "DeleteUserSaveDataAll"}, 108 {201, nullptr, "DeleteUserSaveDataAll"},
101 {210, nullptr, "DeleteUserSystemSaveData"}, 109 {210, nullptr, "DeleteUserSystemSaveData"},
110 {211, nullptr, "DeleteSaveData"},
102 {220, nullptr, "UnregisterNetworkServiceAccount"}, 111 {220, nullptr, "UnregisterNetworkServiceAccount"},
112 {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"},
103 {300, nullptr, "GetApplicationShellEvent"}, 113 {300, nullptr, "GetApplicationShellEvent"},
104 {301, nullptr, "PopApplicationShellEventInfo"}, 114 {301, nullptr, "PopApplicationShellEventInfo"},
105 {302, nullptr, "LaunchLibraryApplet"}, 115 {302, nullptr, "LaunchLibraryApplet"},
@@ -114,6 +124,7 @@ public:
114 {403, nullptr, "GetMaxApplicationControlCacheCount"}, 124 {403, nullptr, "GetMaxApplicationControlCacheCount"},
115 {404, nullptr, "InvalidateApplicationControlCache"}, 125 {404, nullptr, "InvalidateApplicationControlCache"},
116 {405, nullptr, "ListApplicationControlCacheEntryInfo"}, 126 {405, nullptr, "ListApplicationControlCacheEntryInfo"},
127 {406, nullptr, "GetApplicationControlProperty"},
117 {502, nullptr, "RequestCheckGameCardRegistration"}, 128 {502, nullptr, "RequestCheckGameCardRegistration"},
118 {503, nullptr, "RequestGameCardRegistrationGoldPoint"}, 129 {503, nullptr, "RequestGameCardRegistrationGoldPoint"},
119 {504, nullptr, "RequestRegisterGameCard"}, 130 {504, nullptr, "RequestRegisterGameCard"},
@@ -129,6 +140,7 @@ public:
129 {604, nullptr, "RegisterContentsExternalKey"}, 140 {604, nullptr, "RegisterContentsExternalKey"},
130 {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, 141 {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
131 {606, nullptr, "GetContentMetaStorage"}, 142 {606, nullptr, "GetContentMetaStorage"},
143 {607, nullptr, "ListAvailableAddOnContent"},
132 {700, nullptr, "PushDownloadTaskList"}, 144 {700, nullptr, "PushDownloadTaskList"},
133 {701, nullptr, "ClearTaskStatusList"}, 145 {701, nullptr, "ClearTaskStatusList"},
134 {702, nullptr, "RequestDownloadTaskList"}, 146 {702, nullptr, "RequestDownloadTaskList"},
@@ -148,6 +160,9 @@ public:
148 {907, nullptr, "WithdrawApplicationUpdateRequest"}, 160 {907, nullptr, "WithdrawApplicationUpdateRequest"},
149 {908, nullptr, "ListApplicationRecordInstalledContentMeta"}, 161 {908, nullptr, "ListApplicationRecordInstalledContentMeta"},
150 {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"}, 162 {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
163 {910, nullptr, "Unknown3"},
164 {911, nullptr, "SetPreInstalledApplication"},
165 {912, nullptr, "ClearPreInstalledApplicationFlag"},
151 {1000, nullptr, "RequestVerifyApplicationDeprecated"}, 166 {1000, nullptr, "RequestVerifyApplicationDeprecated"},
152 {1001, nullptr, "CorruptApplicationForDebug"}, 167 {1001, nullptr, "CorruptApplicationForDebug"},
153 {1002, nullptr, "RequestVerifyAddOnContentsRights"}, 168 {1002, nullptr, "RequestVerifyAddOnContentsRights"},
@@ -162,6 +177,8 @@ public:
162 {1305, nullptr, "TryDeleteRunningApplicationEntity"}, 177 {1305, nullptr, "TryDeleteRunningApplicationEntity"},
163 {1306, nullptr, "TryDeleteRunningApplicationCompletely"}, 178 {1306, nullptr, "TryDeleteRunningApplicationCompletely"},
164 {1307, nullptr, "TryDeleteRunningApplicationContentEntities"}, 179 {1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
180 {1308, nullptr, "DeleteApplicationCompletelyForDebug"},
181 {1309, nullptr, "CleanupUnavailableAddOnContents"},
165 {1400, nullptr, "PrepareShutdown"}, 182 {1400, nullptr, "PrepareShutdown"},
166 {1500, nullptr, "FormatSdCard"}, 183 {1500, nullptr, "FormatSdCard"},
167 {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"}, 184 {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
@@ -199,6 +216,28 @@ public:
199 {2015, nullptr, "CompareSystemDeliveryInfo"}, 216 {2015, nullptr, "CompareSystemDeliveryInfo"},
200 {2016, nullptr, "ListNotCommittedContentMeta"}, 217 {2016, nullptr, "ListNotCommittedContentMeta"},
201 {2017, nullptr, "CreateDownloadTask"}, 218 {2017, nullptr, "CreateDownloadTask"},
219 {2018, nullptr, "Unknown4"},
220 {2050, nullptr, "Unknown5"},
221 {2100, nullptr, "Unknown6"},
222 {2101, nullptr, "Unknown7"},
223 {2150, nullptr, "CreateRightsEnvironment"},
224 {2151, nullptr, "DestroyRightsEnvironment"},
225 {2152, nullptr, "ActivateRightsEnvironment"},
226 {2153, nullptr, "DeactivateRightsEnvironment"},
227 {2154, nullptr, "ForceActivateRightsContextForExit"},
228 {2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
229 {2161, nullptr, "SetUsersToRightsEnvironment"},
230 {2170, nullptr, "GetRightsEnvironmentStatus"},
231 {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
232 {2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
233 {2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
234 {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
235 {2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
236 {2199, nullptr, "GetRightsEnvironmentCountForDebug"},
237 {2200, nullptr, "Unknown8"},
238 {2201, nullptr, "Unknown9"},
239 {2250, nullptr, "Unknown10"},
240 {2300, nullptr, "Unknown11"},
202 }; 241 };
203 // clang-format on 242 // clang-format on
204 243
@@ -348,12 +387,15 @@ public:
348 {0, nullptr, "LaunchProgram"}, 387 {0, nullptr, "LaunchProgram"},
349 {1, nullptr, "TerminateProcess"}, 388 {1, nullptr, "TerminateProcess"},
350 {2, nullptr, "TerminateProgram"}, 389 {2, nullptr, "TerminateProgram"},
351 {3, nullptr, "GetShellEventHandle"}, 390 {4, nullptr, "GetShellEventHandle"},
352 {4, nullptr, "GetShellEventInfo"}, 391 {5, nullptr, "GetShellEventInfo"},
353 {5, nullptr, "TerminateApplication"}, 392 {6, nullptr, "TerminateApplication"},
354 {6, nullptr, "PrepareLaunchProgramFromHost"}, 393 {7, nullptr, "PrepareLaunchProgramFromHost"},
355 {7, nullptr, "LaunchApplication"}, 394 {8, nullptr, "LaunchApplication"},
356 {8, nullptr, "LaunchApplicationWithStorageId"}, 395 {9, nullptr, "LaunchApplicationWithStorageId"},
396 {10, nullptr, "TerminateApplication2"},
397 {11, nullptr, "GetRunningApplicationProcessId"},
398 {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"},
357 }; 399 };
358 // clang-format on 400 // clang-format on
359 401
@@ -388,6 +430,7 @@ public:
388 {19, nullptr, "GetReceivedEulaDataSize"}, 430 {19, nullptr, "GetReceivedEulaDataSize"},
389 {20, nullptr, "GetReceivedEulaData"}, 431 {20, nullptr, "GetReceivedEulaData"},
390 {21, nullptr, "SetupToReceiveSystemUpdate"}, 432 {21, nullptr, "SetupToReceiveSystemUpdate"},
433 {22, nullptr, "RequestCheckLatestUpdateIncludesRebootlessUpdate"},
391 }; 434 };
392 // clang-format on 435 // clang-format on
393 436
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index 6a9eccfb5..e4fcee9f8 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -14,20 +14,24 @@ public:
14 explicit PlayReport(const char* name) : ServiceFramework{name} { 14 explicit PlayReport(const char* name) : ServiceFramework{name} {
15 // clang-format off 15 // clang-format off
16 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
17 {10100, nullptr, "SaveReport"}, 17 {10100, nullptr, "SaveReportOld"},
18 {10101, &PlayReport::SaveReportWithUser, "SaveReportWithUser"}, 18 {10101, &PlayReport::SaveReportWithUserOld, "SaveReportWithUserOld"},
19 {10102, nullptr, "SaveReport"},
20 {10103, nullptr, "SaveReportWithUser"},
19 {10200, nullptr, "RequestImmediateTransmission"}, 21 {10200, nullptr, "RequestImmediateTransmission"},
20 {10300, nullptr, "GetTransmissionStatus"}, 22 {10300, nullptr, "GetTransmissionStatus"},
21 {20100, nullptr, "SaveSystemReport"}, 23 {20100, nullptr, "SaveSystemReport"},
22 {20200, nullptr, "SetOperationMode"},
23 {20101, nullptr, "SaveSystemReportWithUser"}, 24 {20101, nullptr, "SaveSystemReportWithUser"},
25 {20200, nullptr, "SetOperationMode"},
24 {30100, nullptr, "ClearStorage"}, 26 {30100, nullptr, "ClearStorage"},
27 {30200, nullptr, "ClearStatistics"},
28 {30300, nullptr, "GetStorageUsage"},
29 {30400, nullptr, "GetStatistics"},
30 {30401, nullptr, "GetThroughputHistory"},
31 {30500, nullptr, "GetLastUploadError"},
25 {40100, nullptr, "IsUserAgreementCheckEnabled"}, 32 {40100, nullptr, "IsUserAgreementCheckEnabled"},
26 {40101, nullptr, "SetUserAgreementCheckEnabled"}, 33 {40101, nullptr, "SetUserAgreementCheckEnabled"},
27 {90100, nullptr, "GetStorageUsage"}, 34 {90100, nullptr, "ReadAllReportFiles"},
28 {90200, nullptr, "GetStatistics"},
29 {90201, nullptr, "GetThroughputHistory"},
30 {90300, nullptr, "GetLastUploadError"},
31 }; 35 };
32 // clang-format on 36 // clang-format on
33 37
@@ -35,7 +39,7 @@ public:
35 } 39 }
36 40
37private: 41private:
38 void SaveReportWithUser(Kernel::HLERequestContext& ctx) { 42 void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) {
39 // TODO(ogniK): Do we want to add play report? 43 // TODO(ogniK): Do we want to add play report?
40 LOG_WARNING(Service_PREPO, "(STUBBED) called"); 44 LOG_WARNING(Service_PREPO, "(STUBBED) called");
41 45
@@ -46,6 +50,7 @@ private:
46 50
47void InstallInterfaces(SM::ServiceManager& service_manager) { 51void InstallInterfaces(SM::ServiceManager& service_manager) {
48 std::make_shared<PlayReport>("prepo:a")->InstallAsService(service_manager); 52 std::make_shared<PlayReport>("prepo:a")->InstallAsService(service_manager);
53 std::make_shared<PlayReport>("prepo:a2")->InstallAsService(service_manager);
49 std::make_shared<PlayReport>("prepo:m")->InstallAsService(service_manager); 54 std::make_shared<PlayReport>("prepo:m")->InstallAsService(service_manager);
50 std::make_shared<PlayReport>("prepo:s")->InstallAsService(service_manager); 55 std::make_shared<PlayReport>("prepo:s")->InstallAsService(service_manager);
51 std::make_shared<PlayReport>("prepo:u")->InstallAsService(service_manager); 56 std::make_shared<PlayReport>("prepo:u")->InstallAsService(service_manager);
diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp
new file mode 100644
index 000000000..c2d5fda94
--- /dev/null
+++ b/src/core/hle/service/ptm/psm.cpp
@@ -0,0 +1,71 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6
7#include "common/logging/log.h"
8#include "core/hle/ipc_helpers.h"
9#include "core/hle/service/ptm/psm.h"
10#include "core/hle/service/service.h"
11#include "core/hle/service/sm/sm.h"
12
13namespace Service::PSM {
14
15constexpr u32 BATTERY_FULLY_CHARGED = 100; // 100% Full
16constexpr u32 BATTERY_CURRENTLY_CHARGING = 1; // Plugged into an official dock
17
18class PSM final : public ServiceFramework<PSM> {
19public:
20 explicit PSM() : ServiceFramework{"psm"} {
21 // clang-format off
22 static const FunctionInfo functions[] = {
23 {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"},
24 {1, &PSM::GetChargerType, "GetChargerType"},
25 {2, nullptr, "EnableBatteryCharging"},
26 {3, nullptr, "DisableBatteryCharging"},
27 {4, nullptr, "IsBatteryChargingEnabled"},
28 {5, nullptr, "AcquireControllerPowerSupply"},
29 {6, nullptr, "ReleaseControllerPowerSupply"},
30 {7, nullptr, "OpenSession"},
31 {8, nullptr, "EnableEnoughPowerChargeEmulation"},
32 {9, nullptr, "DisableEnoughPowerChargeEmulation"},
33 {10, nullptr, "EnableFastBatteryCharging"},
34 {11, nullptr, "DisableFastBatteryCharging"},
35 {12, nullptr, "GetBatteryVoltageState"},
36 {13, nullptr, "GetRawBatteryChargePercentage"},
37 {14, nullptr, "IsEnoughPowerSupplied"},
38 {15, nullptr, "GetBatteryAgePercentage"},
39 {16, nullptr, "GetBatteryChargeInfoEvent"},
40 {17, nullptr, "GetBatteryChargeInfoFields"},
41 };
42 // clang-format on
43
44 RegisterHandlers(functions);
45 }
46
47 ~PSM() override = default;
48
49private:
50 void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
51 LOG_WARNING(Service_PSM, "(STUBBED) called");
52
53 IPC::ResponseBuilder rb{ctx, 3};
54 rb.Push(RESULT_SUCCESS);
55 rb.Push<u32>(BATTERY_FULLY_CHARGED);
56 }
57
58 void GetChargerType(Kernel::HLERequestContext& ctx) {
59 LOG_WARNING(Service_PSM, "(STUBBED) called");
60
61 IPC::ResponseBuilder rb{ctx, 3};
62 rb.Push(RESULT_SUCCESS);
63 rb.Push<u32>(BATTERY_CURRENTLY_CHARGING);
64 }
65};
66
67void InstallInterfaces(SM::ServiceManager& sm) {
68 std::make_shared<PSM>()->InstallAsService(sm);
69}
70
71} // namespace Service::PSM
diff --git a/src/core/hle/service/ptm/psm.h b/src/core/hle/service/ptm/psm.h
new file mode 100644
index 000000000..a286793ae
--- /dev/null
+++ b/src/core/hle/service/ptm/psm.h
@@ -0,0 +1,15 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service::SM {
8class ServiceManager;
9}
10
11namespace Service::PSM {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::PSM
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index a225cb4cb..a4cf45267 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -22,7 +22,7 @@
22#include "core/hle/service/apm/apm.h" 22#include "core/hle/service/apm/apm.h"
23#include "core/hle/service/arp/arp.h" 23#include "core/hle/service/arp/arp.h"
24#include "core/hle/service/audio/audio.h" 24#include "core/hle/service/audio/audio.h"
25#include "core/hle/service/bcat/bcat.h" 25#include "core/hle/service/bcat/module.h"
26#include "core/hle/service/bpc/bpc.h" 26#include "core/hle/service/bpc/bpc.h"
27#include "core/hle/service/btdrv/btdrv.h" 27#include "core/hle/service/btdrv/btdrv.h"
28#include "core/hle/service/btm/btm.h" 28#include "core/hle/service/btm/btm.h"
@@ -48,15 +48,17 @@
48#include "core/hle/service/nfp/nfp.h" 48#include "core/hle/service/nfp/nfp.h"
49#include "core/hle/service/nifm/nifm.h" 49#include "core/hle/service/nifm/nifm.h"
50#include "core/hle/service/nim/nim.h" 50#include "core/hle/service/nim/nim.h"
51#include "core/hle/service/npns/npns.h"
51#include "core/hle/service/ns/ns.h" 52#include "core/hle/service/ns/ns.h"
52#include "core/hle/service/nvdrv/nvdrv.h" 53#include "core/hle/service/nvdrv/nvdrv.h"
53#include "core/hle/service/nvflinger/nvflinger.h" 54#include "core/hle/service/nvflinger/nvflinger.h"
54#include "core/hle/service/pcie/pcie.h" 55#include "core/hle/service/pcie/pcie.h"
55#include "core/hle/service/pctl/pctl.h" 56#include "core/hle/service/pctl/module.h"
56#include "core/hle/service/pcv/pcv.h" 57#include "core/hle/service/pcv/pcv.h"
57#include "core/hle/service/pm/pm.h" 58#include "core/hle/service/pm/pm.h"
58#include "core/hle/service/prepo/prepo.h" 59#include "core/hle/service/prepo/prepo.h"
59#include "core/hle/service/psc/psc.h" 60#include "core/hle/service/psc/psc.h"
61#include "core/hle/service/ptm/psm.h"
60#include "core/hle/service/service.h" 62#include "core/hle/service/service.h"
61#include "core/hle/service/set/settings.h" 63#include "core/hle/service/set/settings.h"
62#include "core/hle/service/sm/sm.h" 64#include "core/hle/service/sm/sm.h"
@@ -236,6 +238,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs)
236 NFP::InstallInterfaces(*sm); 238 NFP::InstallInterfaces(*sm);
237 NIFM::InstallInterfaces(*sm); 239 NIFM::InstallInterfaces(*sm);
238 NIM::InstallInterfaces(*sm); 240 NIM::InstallInterfaces(*sm);
241 NPNS::InstallInterfaces(*sm);
239 NS::InstallInterfaces(*sm); 242 NS::InstallInterfaces(*sm);
240 Nvidia::InstallInterfaces(*sm, *nv_flinger); 243 Nvidia::InstallInterfaces(*sm, *nv_flinger);
241 PCIe::InstallInterfaces(*sm); 244 PCIe::InstallInterfaces(*sm);
@@ -244,6 +247,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs)
244 PlayReport::InstallInterfaces(*sm); 247 PlayReport::InstallInterfaces(*sm);
245 PM::InstallInterfaces(*sm); 248 PM::InstallInterfaces(*sm);
246 PSC::InstallInterfaces(*sm); 249 PSC::InstallInterfaces(*sm);
250 PSM::InstallInterfaces(*sm);
247 Set::InstallInterfaces(*sm); 251 Set::InstallInterfaces(*sm);
248 Sockets::InstallInterfaces(*sm); 252 Sockets::InstallInterfaces(*sm);
249 SPL::InstallInterfaces(*sm); 253 SPL::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp
index 5af356d10..34654bb07 100644
--- a/src/core/hle/service/set/set_cal.cpp
+++ b/src/core/hle/service/set/set_cal.cpp
@@ -39,7 +39,8 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") {
39 {29, nullptr, "GetAmiiboEcqvBlsKey"}, 39 {29, nullptr, "GetAmiiboEcqvBlsKey"},
40 {30, nullptr, "GetAmiiboEcqvBlsCertificate"}, 40 {30, nullptr, "GetAmiiboEcqvBlsCertificate"},
41 {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"}, 41 {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"},
42 {32, nullptr, "GetUnknownId"}, 42 {32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"},
43 {33, nullptr, "GetBatteryVersion"},
43 }; 44 };
44 RegisterHandlers(functions); 45 RegisterHandlers(functions);
45} 46}
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 7a619acb4..461607c95 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -59,8 +59,7 @@ ResultStatus AppLoader_XCI::Load(Kernel::Process& process) {
59 if (xci->GetProgramNCAStatus() != ResultStatus::Success) 59 if (xci->GetProgramNCAStatus() != ResultStatus::Success)
60 return xci->GetProgramNCAStatus(); 60 return xci->GetProgramNCAStatus();
61 61
62 const auto nca = xci->GetProgramNCA(); 62 if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false))
63 if (nca == nullptr && !Core::Crypto::KeyManager::KeyFileExists(false))
64 return ResultStatus::ErrorMissingProductionKeyFile; 63 return ResultStatus::ErrorMissingProductionKeyFile;
65 64
66 const auto result = nca_loader->Load(process); 65 const auto result = nca_loader->Load(process);
diff --git a/src/core/settings.h b/src/core/settings.h
index 83b9a04c8..8f2da01c8 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -136,7 +136,7 @@ struct Values {
136 float resolution_factor; 136 float resolution_factor;
137 bool use_frame_limit; 137 bool use_frame_limit;
138 u16 frame_limit; 138 u16 frame_limit;
139 bool use_accurate_framebuffers; 139 bool use_accurate_gpu_emulation;
140 140
141 float bg_red; 141 float bg_red;
142 float bg_green; 142 float bg_green;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 7b04792b5..0de13edd3 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -163,8 +163,8 @@ TelemetrySession::TelemetrySession() {
163 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit", 163 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit",
164 Settings::values.use_frame_limit); 164 Settings::values.use_frame_limit);
165 AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit); 165 AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit);
166 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateFramebuffers", 166 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation",
167 Settings::values.use_accurate_framebuffers); 167 Settings::values.use_accurate_gpu_emulation);
168 AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode", 168 AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode",
169 Settings::values.use_docked_mode); 169 Settings::values.use_docked_mode);
170} 170}
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 597b279b9..74e44c7fe 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -47,9 +47,12 @@ void Fermi2D::HandleSurfaceCopy() {
47 u32 dst_bytes_per_pixel = RenderTargetBytesPerPixel(regs.dst.format); 47 u32 dst_bytes_per_pixel = RenderTargetBytesPerPixel(regs.dst.format);
48 48
49 if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst)) { 49 if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst)) {
50 // TODO(bunnei): The below implementation currently will not get hit, as 50 rasterizer.FlushRegion(source_cpu, src_bytes_per_pixel * regs.src.width * regs.src.height);
51 // AccelerateSurfaceCopy tries to always copy and will always return success. This should be 51 // We have to invalidate the destination region to evict any outdated surfaces from the
52 // changed once we properly support flushing. 52 // cache. We do this before actually writing the new data because the destination address
53 // might contain a dirty surface that will have to be written back to memory.
54 rasterizer.InvalidateRegion(dest_cpu,
55 dst_bytes_per_pixel * regs.dst.width * regs.dst.height);
53 56
54 if (regs.src.linear == regs.dst.linear) { 57 if (regs.src.linear == regs.dst.linear) {
55 // If the input layout and the output layout are the same, just perform a raw copy. 58 // If the input layout and the output layout are the same, just perform a raw copy.
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index 66ae6332d..585290d9f 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -5,10 +5,14 @@
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/memory.h" 6#include "core/memory.h"
7#include "video_core/engines/kepler_memory.h" 7#include "video_core/engines/kepler_memory.h"
8#include "video_core/rasterizer_interface.h"
8 9
9namespace Tegra::Engines { 10namespace Tegra::Engines {
10 11
11KeplerMemory::KeplerMemory(MemoryManager& memory_manager) : memory_manager(memory_manager) {} 12KeplerMemory::KeplerMemory(VideoCore::RasterizerInterface& rasterizer,
13 MemoryManager& memory_manager)
14 : memory_manager(memory_manager), rasterizer{rasterizer} {}
15
12KeplerMemory::~KeplerMemory() = default; 16KeplerMemory::~KeplerMemory() = default;
13 17
14void KeplerMemory::WriteReg(u32 method, u32 value) { 18void KeplerMemory::WriteReg(u32 method, u32 value) {
@@ -37,6 +41,11 @@ void KeplerMemory::ProcessData(u32 data) {
37 VAddr dest_address = 41 VAddr dest_address =
38 *memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32)); 42 *memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32));
39 43
44 // We have to invalidate the destination region to evict any outdated surfaces from the cache.
45 // We do this before actually writing the new data because the destination address might contain
46 // a dirty surface that will have to be written back to memory.
47 rasterizer.InvalidateRegion(dest_address, sizeof(u32));
48
40 Memory::Write32(dest_address, data); 49 Memory::Write32(dest_address, data);
41 50
42 state.write_offset++; 51 state.write_offset++;
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h
index b0d0078cf..bf4a13cff 100644
--- a/src/video_core/engines/kepler_memory.h
+++ b/src/video_core/engines/kepler_memory.h
@@ -11,6 +11,10 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/memory_manager.h" 12#include "video_core/memory_manager.h"
13 13
14namespace VideoCore {
15class RasterizerInterface;
16}
17
14namespace Tegra::Engines { 18namespace Tegra::Engines {
15 19
16#define KEPLERMEMORY_REG_INDEX(field_name) \ 20#define KEPLERMEMORY_REG_INDEX(field_name) \
@@ -18,7 +22,7 @@ namespace Tegra::Engines {
18 22
19class KeplerMemory final { 23class KeplerMemory final {
20public: 24public:
21 KeplerMemory(MemoryManager& memory_manager); 25 KeplerMemory(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
22 ~KeplerMemory(); 26 ~KeplerMemory();
23 27
24 /// Write the value to the register identified by method. 28 /// Write the value to the register identified by method.
@@ -72,6 +76,7 @@ public:
72 76
73private: 77private:
74 MemoryManager& memory_manager; 78 MemoryManager& memory_manager;
79 VideoCore::RasterizerInterface& rasterizer;
75 80
76 void ProcessData(u32 data); 81 void ProcessData(u32 data);
77}; 82};
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 8afd26fe9..bca014a4a 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -13,8 +13,7 @@
13#include "video_core/renderer_base.h" 13#include "video_core/renderer_base.h"
14#include "video_core/textures/texture.h" 14#include "video_core/textures/texture.h"
15 15
16namespace Tegra { 16namespace Tegra::Engines {
17namespace Engines {
18 17
19/// First register id that is actually a Macro call. 18/// First register id that is actually a Macro call.
20constexpr u32 MacroRegistersStart = 0xE00; 19constexpr u32 MacroRegistersStart = 0xE00;
@@ -408,5 +407,4 @@ void Maxwell3D::ProcessClearBuffers() {
408 rasterizer.Clear(); 407 rasterizer.Clear();
409} 408}
410 409
411} // namespace Engines 410} // namespace Tegra::Engines
412} // namespace Tegra
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index c8d1b6478..0e09a7ee5 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -448,7 +448,10 @@ public:
448 BitField<8, 3, u32> block_depth; 448 BitField<8, 3, u32> block_depth;
449 BitField<12, 1, InvMemoryLayout> type; 449 BitField<12, 1, InvMemoryLayout> type;
450 } memory_layout; 450 } memory_layout;
451 u32 array_mode; 451 union {
452 BitField<0, 16, u32> array_mode;
453 BitField<16, 1, u32> volume;
454 };
452 u32 layer_stride; 455 u32 layer_stride;
453 u32 base_layer; 456 u32 base_layer;
454 INSERT_PADDING_WORDS(7); 457 INSERT_PADDING_WORDS(7);
@@ -640,8 +643,10 @@ public:
640 u32 d3d_cull_mode; 643 u32 d3d_cull_mode;
641 644
642 ComparisonOp depth_test_func; 645 ComparisonOp depth_test_func;
646 float alpha_test_ref;
647 ComparisonOp alpha_test_func;
643 648
644 INSERT_PADDING_WORDS(0xB); 649 INSERT_PADDING_WORDS(0x9);
645 650
646 struct { 651 struct {
647 u32 separate_alpha; 652 u32 separate_alpha;
diff --git a/src/video_core/engines/maxwell_compute.cpp b/src/video_core/engines/maxwell_compute.cpp
index 59e28b22d..8b5f08351 100644
--- a/src/video_core/engines/maxwell_compute.cpp
+++ b/src/video_core/engines/maxwell_compute.cpp
@@ -6,8 +6,7 @@
6#include "core/core.h" 6#include "core/core.h"
7#include "video_core/engines/maxwell_compute.h" 7#include "video_core/engines/maxwell_compute.h"
8 8
9namespace Tegra { 9namespace Tegra::Engines {
10namespace Engines {
11 10
12void MaxwellCompute::WriteReg(u32 method, u32 value) { 11void MaxwellCompute::WriteReg(u32 method, u32 value) {
13 ASSERT_MSG(method < Regs::NUM_REGS, 12 ASSERT_MSG(method < Regs::NUM_REGS,
@@ -26,5 +25,4 @@ void MaxwellCompute::WriteReg(u32 method, u32 value) {
26 } 25 }
27} 26}
28 27
29} // namespace Engines 28} // namespace Tegra::Engines
30} // namespace Tegra
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index bf2a21bb6..b8a78cf82 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -4,12 +4,13 @@
4 4
5#include "core/memory.h" 5#include "core/memory.h"
6#include "video_core/engines/maxwell_dma.h" 6#include "video_core/engines/maxwell_dma.h"
7#include "video_core/rasterizer_interface.h"
7#include "video_core/textures/decoders.h" 8#include "video_core/textures/decoders.h"
8 9
9namespace Tegra { 10namespace Tegra::Engines {
10namespace Engines {
11 11
12MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager) : memory_manager(memory_manager) {} 12MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
13 : memory_manager(memory_manager), rasterizer{rasterizer} {}
13 14
14void MaxwellDMA::WriteReg(u32 method, u32 value) { 15void MaxwellDMA::WriteReg(u32 method, u32 value) {
15 ASSERT_MSG(method < Regs::NUM_REGS, 16 ASSERT_MSG(method < Regs::NUM_REGS,
@@ -44,40 +45,77 @@ void MaxwellDMA::HandleCopy() {
44 ASSERT(regs.exec.query_mode == Regs::QueryMode::None); 45 ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
45 ASSERT(regs.exec.query_intr == Regs::QueryIntr::None); 46 ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
46 ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2); 47 ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
47 ASSERT(regs.src_params.pos_x == 0);
48 ASSERT(regs.src_params.pos_y == 0);
49 ASSERT(regs.dst_params.pos_x == 0); 48 ASSERT(regs.dst_params.pos_x == 0);
50 ASSERT(regs.dst_params.pos_y == 0); 49 ASSERT(regs.dst_params.pos_y == 0);
51 50
52 if (regs.exec.is_dst_linear == regs.exec.is_src_linear) { 51 if (!regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
53 std::size_t copy_size = regs.x_count; 52 // If both the source and the destination are in block layout, assert.
53 UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented");
54 return;
55 }
54 56
57 if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
55 // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D 58 // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
56 // buffer of length `x_count`, otherwise we copy a 2D buffer of size (x_count, y_count). 59 // buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
57 if (regs.exec.enable_2d) { 60 // y_count).
58 copy_size = copy_size * regs.y_count; 61 if (!regs.exec.enable_2d) {
62 Memory::CopyBlock(dest_cpu, source_cpu, regs.x_count);
63 return;
59 } 64 }
60 65
61 Memory::CopyBlock(dest_cpu, source_cpu, copy_size); 66 // If both the source and the destination are in linear layout, perform a line-by-line
67 // copy. We're going to take a subrect of size (x_count, y_count) from the source
68 // rectangle. There is no need to manually flush/invalidate the regions because
69 // CopyBlock does that for us.
70 for (u32 line = 0; line < regs.y_count; ++line) {
71 const VAddr source_line = source_cpu + line * regs.src_pitch;
72 const VAddr dest_line = dest_cpu + line * regs.dst_pitch;
73 Memory::CopyBlock(dest_line, source_line, regs.x_count);
74 }
62 return; 75 return;
63 } 76 }
64 77
65 ASSERT(regs.exec.enable_2d == 1); 78 ASSERT(regs.exec.enable_2d == 1);
66 u8* src_buffer = Memory::GetPointer(source_cpu); 79
67 u8* dst_buffer = Memory::GetPointer(dest_cpu); 80 const std::size_t copy_size = regs.x_count * regs.y_count;
81
82 const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) {
83 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
84 // copying.
85 rasterizer.FlushRegion(source_cpu, src_size);
86
87 // We have to invalidate the destination region to evict any outdated surfaces from the
88 // cache. We do this before actually writing the new data because the destination address
89 // might contain a dirty surface that will have to be written back to memory.
90 rasterizer.InvalidateRegion(dest_cpu, dst_size);
91 };
68 92
69 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { 93 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
94 ASSERT(regs.src_params.size_z == 1);
70 // If the input is tiled and the output is linear, deswizzle the input and copy it over. 95 // If the input is tiled and the output is linear, deswizzle the input and copy it over.
71 Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, 96
72 regs.src_params.size_z, 1, 1, src_buffer, dst_buffer, true, 97 const u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x;
73 regs.src_params.BlockHeight(), regs.src_params.BlockDepth()); 98
99 FlushAndInvalidate(regs.src_pitch * regs.src_params.size_y,
100 copy_size * src_bytes_per_pixel);
101
102 Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
103 regs.src_params.size_x, src_bytes_per_pixel, source_cpu, dest_cpu,
104 regs.src_params.BlockHeight(), regs.src_params.pos_x,
105 regs.src_params.pos_y);
74 } else { 106 } else {
107 ASSERT(regs.dst_params.size_z == 1);
108 ASSERT(regs.src_pitch == regs.x_count);
109
110 const u32 src_bpp = regs.src_pitch / regs.x_count;
111
112 FlushAndInvalidate(regs.src_pitch * regs.y_count,
113 regs.dst_params.size_x * regs.dst_params.size_y * src_bpp);
114
75 // If the input is linear and the output is tiled, swizzle the input and copy it over. 115 // If the input is linear and the output is tiled, swizzle the input and copy it over.
76 Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, 116 Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x,
77 regs.dst_params.size_z, 1, 1, dst_buffer, src_buffer, false, 117 src_bpp, dest_cpu, source_cpu, regs.dst_params.BlockHeight());
78 regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());
79 } 118 }
80} 119}
81 120
82} // namespace Engines 121} // namespace Tegra::Engines
83} // namespace Tegra
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index df19e02e2..5f3704f05 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -12,11 +12,15 @@
12#include "video_core/gpu.h" 12#include "video_core/gpu.h"
13#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
14 14
15namespace VideoCore {
16class RasterizerInterface;
17}
18
15namespace Tegra::Engines { 19namespace Tegra::Engines {
16 20
17class MaxwellDMA final { 21class MaxwellDMA final {
18public: 22public:
19 explicit MaxwellDMA(MemoryManager& memory_manager); 23 explicit MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
20 ~MaxwellDMA() = default; 24 ~MaxwellDMA() = default;
21 25
22 /// Write the value to the register identified by method. 26 /// Write the value to the register identified by method.
@@ -133,6 +137,8 @@ public:
133 MemoryManager& memory_manager; 137 MemoryManager& memory_manager;
134 138
135private: 139private:
140 VideoCore::RasterizerInterface& rasterizer;
141
136 /// Performs the copy from the source buffer to the destination buffer as configured in the 142 /// Performs the copy from the source buffer to the destination buffer as configured in the
137 /// registers. 143 /// registers.
138 void HandleCopy(); 144 void HandleCopy();
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index f356f9a03..6cd08d28b 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -214,7 +214,7 @@ enum class IMinMaxExchange : u64 {
214 XHi = 3, 214 XHi = 3,
215}; 215};
216 216
217enum class VmadType : u64 { 217enum class VideoType : u64 {
218 Size16_Low = 0, 218 Size16_Low = 0,
219 Size16_High = 1, 219 Size16_High = 1,
220 Size32 = 2, 220 Size32 = 2,
@@ -335,6 +335,26 @@ enum class IsberdMode : u64 {
335 335
336enum class IsberdShift : u64 { None = 0, U16 = 1, B32 = 2 }; 336enum class IsberdShift : u64 { None = 0, U16 = 1, B32 = 2 };
337 337
338enum class HalfType : u64 {
339 H0_H1 = 0,
340 F32 = 1,
341 H0_H0 = 2,
342 H1_H1 = 3,
343};
344
345enum class HalfMerge : u64 {
346 H0_H1 = 0,
347 F32 = 1,
348 Mrg_H0 = 2,
349 Mrg_H1 = 3,
350};
351
352enum class HalfPrecision : u64 {
353 None = 0,
354 FTZ = 1,
355 FMZ = 2,
356};
357
338enum class IpaInterpMode : u64 { 358enum class IpaInterpMode : u64 {
339 Linear = 0, 359 Linear = 0,
340 Perspective = 1, 360 Perspective = 1,
@@ -544,6 +564,10 @@ union Instruction {
544 } fmul; 564 } fmul;
545 565
546 union { 566 union {
567 BitField<55, 1, u64> saturate;
568 } fmul32;
569
570 union {
547 BitField<48, 1, u64> is_signed; 571 BitField<48, 1, u64> is_signed;
548 } shift; 572 } shift;
549 573
@@ -554,6 +578,70 @@ union Instruction {
554 } alu_integer; 578 } alu_integer;
555 579
556 union { 580 union {
581 BitField<39, 1, u64> ftz;
582 BitField<32, 1, u64> saturate;
583 BitField<49, 2, HalfMerge> merge;
584
585 BitField<43, 1, u64> negate_a;
586 BitField<44, 1, u64> abs_a;
587 BitField<47, 2, HalfType> type_a;
588
589 BitField<31, 1, u64> negate_b;
590 BitField<30, 1, u64> abs_b;
591 BitField<47, 2, HalfType> type_b;
592
593 BitField<35, 2, HalfType> type_c;
594 } alu_half;
595
596 union {
597 BitField<39, 2, HalfPrecision> precision;
598 BitField<39, 1, u64> ftz;
599 BitField<52, 1, u64> saturate;
600 BitField<49, 2, HalfMerge> merge;
601
602 BitField<43, 1, u64> negate_a;
603 BitField<44, 1, u64> abs_a;
604 BitField<47, 2, HalfType> type_a;
605 } alu_half_imm;
606
607 union {
608 BitField<29, 1, u64> first_negate;
609 BitField<20, 9, u64> first;
610
611 BitField<56, 1, u64> second_negate;
612 BitField<30, 9, u64> second;
613
614 u32 PackImmediates() const {
615 // Immediates are half floats shifted.
616 constexpr u32 imm_shift = 6;
617 return static_cast<u32>((first << imm_shift) | (second << (16 + imm_shift)));
618 }
619 } half_imm;
620
621 union {
622 union {
623 BitField<37, 2, HalfPrecision> precision;
624 BitField<32, 1, u64> saturate;
625
626 BitField<30, 1, u64> negate_c;
627 BitField<35, 2, HalfType> type_c;
628 } rr;
629
630 BitField<57, 2, HalfPrecision> precision;
631 BitField<52, 1, u64> saturate;
632
633 BitField<49, 2, HalfMerge> merge;
634
635 BitField<47, 2, HalfType> type_a;
636
637 BitField<56, 1, u64> negate_b;
638 BitField<28, 2, HalfType> type_b;
639
640 BitField<51, 1, u64> negate_c;
641 BitField<53, 2, HalfType> type_reg39;
642 } hfma2;
643
644 union {
557 BitField<40, 1, u64> invert; 645 BitField<40, 1, u64> invert;
558 } popc; 646 } popc;
559 647
@@ -669,7 +757,6 @@ union Instruction {
669 BitField<45, 2, PredOperation> op; 757 BitField<45, 2, PredOperation> op;
670 BitField<47, 1, u64> ftz; 758 BitField<47, 1, u64> ftz;
671 BitField<48, 4, PredCondition> cond; 759 BitField<48, 4, PredCondition> cond;
672 BitField<56, 1, u64> neg_b;
673 } fsetp; 760 } fsetp;
674 761
675 union { 762 union {
@@ -696,6 +783,14 @@ union Instruction {
696 } psetp; 783 } psetp;
697 784
698 union { 785 union {
786 BitField<43, 4, PredCondition> cond;
787 BitField<45, 2, PredOperation> op;
788 BitField<3, 3, u64> pred3;
789 BitField<0, 3, u64> pred0;
790 BitField<39, 3, u64> pred39;
791 } vsetp;
792
793 union {
699 BitField<12, 3, u64> pred12; 794 BitField<12, 3, u64> pred12;
700 BitField<15, 1, u64> neg_pred12; 795 BitField<15, 1, u64> neg_pred12;
701 BitField<24, 2, PredOperation> cond; 796 BitField<24, 2, PredOperation> cond;
@@ -717,6 +812,23 @@ union Instruction {
717 } csetp; 812 } csetp;
718 813
719 union { 814 union {
815 BitField<35, 4, PredCondition> cond;
816 BitField<49, 1, u64> h_and;
817 BitField<6, 1, u64> ftz;
818 BitField<45, 2, PredOperation> op;
819 BitField<3, 3, u64> pred3;
820 BitField<0, 3, u64> pred0;
821 BitField<43, 1, u64> negate_a;
822 BitField<44, 1, u64> abs_a;
823 BitField<47, 2, HalfType> type_a;
824 BitField<31, 1, u64> negate_b;
825 BitField<30, 1, u64> abs_b;
826 BitField<28, 2, HalfType> type_b;
827 BitField<42, 1, u64> neg_pred;
828 BitField<39, 3, u64> pred39;
829 } hsetp2;
830
831 union {
720 BitField<39, 3, u64> pred39; 832 BitField<39, 3, u64> pred39;
721 BitField<42, 1, u64> neg_pred; 833 BitField<42, 1, u64> neg_pred;
722 BitField<43, 1, u64> neg_a; 834 BitField<43, 1, u64> neg_a;
@@ -727,10 +839,24 @@ union Instruction {
727 BitField<53, 1, u64> neg_b; 839 BitField<53, 1, u64> neg_b;
728 BitField<54, 1, u64> abs_a; 840 BitField<54, 1, u64> abs_a;
729 BitField<55, 1, u64> ftz; 841 BitField<55, 1, u64> ftz;
730 BitField<56, 1, u64> neg_imm;
731 } fset; 842 } fset;
732 843
733 union { 844 union {
845 BitField<49, 1, u64> bf;
846 BitField<35, 3, PredCondition> cond;
847 BitField<50, 1, u64> ftz;
848 BitField<45, 2, PredOperation> op;
849 BitField<43, 1, u64> negate_a;
850 BitField<44, 1, u64> abs_a;
851 BitField<47, 2, HalfType> type_a;
852 BitField<31, 1, u64> negate_b;
853 BitField<30, 1, u64> abs_b;
854 BitField<28, 2, HalfType> type_b;
855 BitField<42, 1, u64> neg_pred;
856 BitField<39, 3, u64> pred39;
857 } hset2;
858
859 union {
734 BitField<39, 3, u64> pred39; 860 BitField<39, 3, u64> pred39;
735 BitField<42, 1, u64> neg_pred; 861 BitField<42, 1, u64> neg_pred;
736 BitField<44, 1, u64> bf; 862 BitField<44, 1, u64> bf;
@@ -1036,15 +1162,17 @@ union Instruction {
1036 union { 1162 union {
1037 BitField<48, 1, u64> signed_a; 1163 BitField<48, 1, u64> signed_a;
1038 BitField<38, 1, u64> is_byte_chunk_a; 1164 BitField<38, 1, u64> is_byte_chunk_a;
1039 BitField<36, 2, VmadType> type_a; 1165 BitField<36, 2, VideoType> type_a;
1040 BitField<36, 2, u64> byte_height_a; 1166 BitField<36, 2, u64> byte_height_a;
1041 1167
1042 BitField<49, 1, u64> signed_b; 1168 BitField<49, 1, u64> signed_b;
1043 BitField<50, 1, u64> use_register_b; 1169 BitField<50, 1, u64> use_register_b;
1044 BitField<30, 1, u64> is_byte_chunk_b; 1170 BitField<30, 1, u64> is_byte_chunk_b;
1045 BitField<28, 2, VmadType> type_b; 1171 BitField<28, 2, VideoType> type_b;
1046 BitField<28, 2, u64> byte_height_b; 1172 BitField<28, 2, u64> byte_height_b;
1173 } video;
1047 1174
1175 union {
1048 BitField<51, 2, VmadShr> shr; 1176 BitField<51, 2, VmadShr> shr;
1049 BitField<55, 1, u64> saturate; // Saturates the result (a * b + c) 1177 BitField<55, 1, u64> saturate; // Saturates the result (a * b + c)
1050 BitField<47, 1, u64> cc; 1178 BitField<47, 1, u64> cc;
@@ -1095,11 +1223,13 @@ public:
1095 KIL, 1223 KIL,
1096 SSY, 1224 SSY,
1097 SYNC, 1225 SYNC,
1226 BRK,
1098 DEPBAR, 1227 DEPBAR,
1099 BFE_C, 1228 BFE_C,
1100 BFE_R, 1229 BFE_R,
1101 BFE_IMM, 1230 BFE_IMM,
1102 BRA, 1231 BRA,
1232 PBK,
1103 LD_A, 1233 LD_A,
1104 LD_C, 1234 LD_C,
1105 ST_A, 1235 ST_A,
@@ -1118,6 +1248,7 @@ public:
1118 OUT_R, // Emit vertex/primitive 1248 OUT_R, // Emit vertex/primitive
1119 ISBERD, 1249 ISBERD,
1120 VMAD, 1250 VMAD,
1251 VSETP,
1121 FFMA_IMM, // Fused Multiply and Add 1252 FFMA_IMM, // Fused Multiply and Add
1122 FFMA_CR, 1253 FFMA_CR,
1123 FFMA_RC, 1254 FFMA_RC,
@@ -1145,6 +1276,18 @@ public:
1145 LEA_RZ, 1276 LEA_RZ,
1146 LEA_IMM, 1277 LEA_IMM,
1147 LEA_HI, 1278 LEA_HI,
1279 HADD2_C,
1280 HADD2_R,
1281 HADD2_IMM,
1282 HMUL2_C,
1283 HMUL2_R,
1284 HMUL2_IMM,
1285 HFMA2_CR,
1286 HFMA2_RC,
1287 HFMA2_RR,
1288 HFMA2_IMM_R,
1289 HSETP2_R,
1290 HSET2_R,
1148 POPC_C, 1291 POPC_C,
1149 POPC_R, 1292 POPC_R,
1150 POPC_IMM, 1293 POPC_IMM,
@@ -1218,9 +1361,12 @@ public:
1218 ArithmeticImmediate, 1361 ArithmeticImmediate,
1219 ArithmeticInteger, 1362 ArithmeticInteger,
1220 ArithmeticIntegerImmediate, 1363 ArithmeticIntegerImmediate,
1364 ArithmeticHalf,
1365 ArithmeticHalfImmediate,
1221 Bfe, 1366 Bfe,
1222 Shift, 1367 Shift,
1223 Ffma, 1368 Ffma,
1369 Hfma2,
1224 Flow, 1370 Flow,
1225 Synch, 1371 Synch,
1226 Memory, 1372 Memory,
@@ -1228,6 +1374,8 @@ public:
1228 FloatSetPredicate, 1374 FloatSetPredicate,
1229 IntegerSet, 1375 IntegerSet,
1230 IntegerSetPredicate, 1376 IntegerSetPredicate,
1377 HalfSet,
1378 HalfSetPredicate,
1231 PredicateSetPredicate, 1379 PredicateSetPredicate,
1232 PredicateSetRegister, 1380 PredicateSetRegister,
1233 Conversion, 1381 Conversion,
@@ -1239,7 +1387,7 @@ public:
1239 /// conditionally executed). 1387 /// conditionally executed).
1240 static bool IsPredicatedInstruction(Id opcode) { 1388 static bool IsPredicatedInstruction(Id opcode) {
1241 // TODO(Subv): Add the rest of unpredicated instructions. 1389 // TODO(Subv): Add the rest of unpredicated instructions.
1242 return opcode != Id::SSY; 1390 return opcode != Id::SSY && opcode != Id::PBK;
1243 } 1391 }
1244 1392
1245 class Matcher { 1393 class Matcher {
@@ -1335,9 +1483,11 @@ private:
1335#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name) 1483#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name)
1336 INST("111000110011----", Id::KIL, Type::Flow, "KIL"), 1484 INST("111000110011----", Id::KIL, Type::Flow, "KIL"),
1337 INST("111000101001----", Id::SSY, Type::Flow, "SSY"), 1485 INST("111000101001----", Id::SSY, Type::Flow, "SSY"),
1486 INST("111000101010----", Id::PBK, Type::Flow, "PBK"),
1338 INST("111000100100----", Id::BRA, Type::Flow, "BRA"), 1487 INST("111000100100----", Id::BRA, Type::Flow, "BRA"),
1488 INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"),
1489 INST("111000110100---", Id::BRK, Type::Flow, "BRK"),
1339 INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"), 1490 INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
1340 INST("1111000011111---", Id::SYNC, Type::Synch, "SYNC"),
1341 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), 1491 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
1342 INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"), 1492 INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"),
1343 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), 1493 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
@@ -1356,6 +1506,7 @@ private:
1356 INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"), 1506 INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
1357 INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"), 1507 INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
1358 INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"), 1508 INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"),
1509 INST("0101000011110---", Id::VSETP, Type::Trivial, "VSETP"),
1359 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), 1510 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
1360 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), 1511 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
1361 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), 1512 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
@@ -1389,6 +1540,18 @@ private:
1389 INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"), 1540 INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"),
1390 INST("010010111101----", Id::LEA_RZ, Type::ArithmeticInteger, "LEA_RZ"), 1541 INST("010010111101----", Id::LEA_RZ, Type::ArithmeticInteger, "LEA_RZ"),
1391 INST("00011000--------", Id::LEA_HI, Type::ArithmeticInteger, "LEA_HI"), 1542 INST("00011000--------", Id::LEA_HI, Type::ArithmeticInteger, "LEA_HI"),
1543 INST("0111101-1-------", Id::HADD2_C, Type::ArithmeticHalf, "HADD2_C"),
1544 INST("0101110100010---", Id::HADD2_R, Type::ArithmeticHalf, "HADD2_R"),
1545 INST("0111101-0-------", Id::HADD2_IMM, Type::ArithmeticHalfImmediate, "HADD2_IMM"),
1546 INST("0111100-1-------", Id::HMUL2_C, Type::ArithmeticHalf, "HMUL2_C"),
1547 INST("0101110100001---", Id::HMUL2_R, Type::ArithmeticHalf, "HMUL2_R"),
1548 INST("0111100-0-------", Id::HMUL2_IMM, Type::ArithmeticHalfImmediate, "HMUL2_IMM"),
1549 INST("01110---1-------", Id::HFMA2_CR, Type::Hfma2, "HFMA2_CR"),
1550 INST("01100---1-------", Id::HFMA2_RC, Type::Hfma2, "HFMA2_RC"),
1551 INST("0101110100000---", Id::HFMA2_RR, Type::Hfma2, "HFMA2_RR"),
1552 INST("01110---0-------", Id::HFMA2_IMM_R, Type::Hfma2, "HFMA2_R_IMM"),
1553 INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP_R"),
1554 INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"),
1392 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), 1555 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
1393 INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), 1556 INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"),
1394 INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), 1557 INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"),
@@ -1463,4 +1626,4 @@ private:
1463 } 1626 }
1464}; 1627};
1465 1628
1466} // namespace Tegra::Shader 1629} // namespace Tegra::Shader \ No newline at end of file
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 9ba7e3533..83c7e5b0b 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -27,8 +27,8 @@ GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
27 maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager); 27 maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
28 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager); 28 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager);
29 maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); 29 maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
30 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager); 30 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(rasterizer, *memory_manager);
31 kepler_memory = std::make_unique<Engines::KeplerMemory>(*memory_manager); 31 kepler_memory = std::make_unique<Engines::KeplerMemory>(rasterizer, *memory_manager);
32} 32}
33 33
34GPU::~GPU() = default; 34GPU::~GPU() = default;
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index ca923d17d..022d4ab74 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -87,6 +87,16 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
87 return gpu_addr; 87 return gpu_addr;
88} 88}
89 89
90GPUVAddr MemoryManager::GetRegionEnd(GPUVAddr region_start) const {
91 for (const auto& region : mapped_regions) {
92 const GPUVAddr region_end{region.gpu_addr + region.size};
93 if (region_start >= region.gpu_addr && region_start < region_end) {
94 return region_end;
95 }
96 }
97 return {};
98}
99
90boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) { 100boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) {
91 GPUVAddr gpu_addr = 0; 101 GPUVAddr gpu_addr = 0;
92 u64 free_space = 0; 102 u64 free_space = 0;
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 86765e72a..caf80093f 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -26,6 +26,7 @@ public:
26 GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size); 26 GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size);
27 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size); 27 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size);
28 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size); 28 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size);
29 GPUVAddr GetRegionEnd(GPUVAddr region_start) const;
29 boost::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr); 30 boost::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr);
30 std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const; 31 std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const;
31 32
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h
index 083b283b0..0a3b3951e 100644
--- a/src/video_core/rasterizer_cache.h
+++ b/src/video_core/rasterizer_cache.h
@@ -11,32 +11,77 @@
11 11
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "core/core.h" 13#include "core/core.h"
14#include "core/settings.h"
14#include "video_core/rasterizer_interface.h" 15#include "video_core/rasterizer_interface.h"
15#include "video_core/renderer_base.h" 16#include "video_core/renderer_base.h"
16 17
18class RasterizerCacheObject {
19public:
20 /// Gets the address of the shader in guest memory, required for cache management
21 virtual VAddr GetAddr() const = 0;
22
23 /// Gets the size of the shader in guest memory, required for cache management
24 virtual std::size_t GetSizeInBytes() const = 0;
25
26 /// Wriets any cached resources back to memory
27 virtual void Flush() = 0;
28
29 /// Sets whether the cached object should be considered registered
30 void SetIsRegistered(bool registered) {
31 is_registered = registered;
32 }
33
34 /// Returns true if the cached object is registered
35 bool IsRegistered() const {
36 return is_registered;
37 }
38
39 /// Returns true if the cached object is dirty
40 bool IsDirty() const {
41 return is_dirty;
42 }
43
44 /// Returns ticks from when this cached object was last modified
45 u64 GetLastModifiedTicks() const {
46 return last_modified_ticks;
47 }
48
49 /// Marks an object as recently modified, used to specify whether it is clean or dirty
50 template <class T>
51 void MarkAsModified(bool dirty, T& cache) {
52 is_dirty = dirty;
53 last_modified_ticks = cache.GetModifiedTicks();
54 }
55
56private:
57 bool is_registered{}; ///< Whether the object is currently registered with the cache
58 bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory)
59 u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing
60};
61
17template <class T> 62template <class T>
18class RasterizerCache : NonCopyable { 63class RasterizerCache : NonCopyable {
64 friend class RasterizerCacheObject;
65
19public: 66public:
67 /// Write any cached resources overlapping the specified region back to memory
68 void FlushRegion(Tegra::GPUVAddr addr, size_t size) {
69 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
70 for (auto& object : objects) {
71 FlushObject(object);
72 }
73 }
74
20 /// Mark the specified region as being invalidated 75 /// Mark the specified region as being invalidated
21 void InvalidateRegion(VAddr addr, u64 size) { 76 void InvalidateRegion(VAddr addr, u64 size) {
22 if (size == 0) 77 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
23 return; 78 for (auto& object : objects) {
24 79 if (!object->IsRegistered()) {
25 const ObjectInterval interval{addr, addr + size}; 80 // Skip duplicates
26 for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) { 81 continue;
27 for (auto& cached_object : pair.second) {
28 if (!cached_object)
29 continue;
30
31 remove_objects.emplace(cached_object);
32 } 82 }
83 Unregister(object);
33 } 84 }
34
35 for (auto& remove_object : remove_objects) {
36 Unregister(remove_object);
37 }
38
39 remove_objects.clear();
40 } 85 }
41 86
42 /// Invalidates everything in the cache 87 /// Invalidates everything in the cache
@@ -62,6 +107,7 @@ protected:
62 107
63 /// Register an object into the cache 108 /// Register an object into the cache
64 void Register(const T& object) { 109 void Register(const T& object) {
110 object->SetIsRegistered(true);
65 object_cache.add({GetInterval(object), ObjectSet{object}}); 111 object_cache.add({GetInterval(object), ObjectSet{object}});
66 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer(); 112 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
67 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1); 113 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1);
@@ -69,12 +115,57 @@ protected:
69 115
70 /// Unregisters an object from the cache 116 /// Unregisters an object from the cache
71 void Unregister(const T& object) { 117 void Unregister(const T& object) {
118 object->SetIsRegistered(false);
72 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer(); 119 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
73 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1); 120 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1);
121
122 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
123 if (Settings::values.use_accurate_gpu_emulation) {
124 FlushObject(object);
125 }
126
74 object_cache.subtract({GetInterval(object), ObjectSet{object}}); 127 object_cache.subtract({GetInterval(object), ObjectSet{object}});
75 } 128 }
76 129
130 /// Returns a ticks counter used for tracking when cached objects were last modified
131 u64 GetModifiedTicks() {
132 return ++modified_ticks;
133 }
134
77private: 135private:
136 /// Returns a list of cached objects from the specified memory region, ordered by access time
137 std::vector<T> GetSortedObjectsFromRegion(VAddr addr, u64 size) {
138 if (size == 0) {
139 return {};
140 }
141
142 std::vector<T> objects;
143 const ObjectInterval interval{addr, addr + size};
144 for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) {
145 for (auto& cached_object : pair.second) {
146 if (!cached_object) {
147 continue;
148 }
149 objects.push_back(cached_object);
150 }
151 }
152
153 std::sort(objects.begin(), objects.end(), [](const T& a, const T& b) -> bool {
154 return a->GetLastModifiedTicks() < b->GetLastModifiedTicks();
155 });
156
157 return objects;
158 }
159
160 /// Flushes the specified object, updating appropriate cache state as needed
161 void FlushObject(const T& object) {
162 if (!object->IsDirty()) {
163 return;
164 }
165 object->Flush();
166 object->MarkAsModified(false, *this);
167 }
168
78 using ObjectSet = std::set<T>; 169 using ObjectSet = std::set<T>;
79 using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>; 170 using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>;
80 using ObjectInterval = typename ObjectCache::interval_type; 171 using ObjectInterval = typename ObjectCache::interval_type;
@@ -84,6 +175,6 @@ private:
84 object->GetAddr() + object->GetSizeInBytes()); 175 object->GetAddr() + object->GetSizeInBytes());
85 } 176 }
86 177
87 ObjectCache object_cache; 178 ObjectCache object_cache; ///< Cache of objects
88 ObjectSet remove_objects; 179 u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
89}; 180};
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 965976334..be29dc8be 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -15,15 +15,18 @@
15 15
16namespace OpenGL { 16namespace OpenGL {
17 17
18struct CachedBufferEntry final { 18struct CachedBufferEntry final : public RasterizerCacheObject {
19 VAddr GetAddr() const { 19 VAddr GetAddr() const override {
20 return addr; 20 return addr;
21 } 21 }
22 22
23 std::size_t GetSizeInBytes() const { 23 std::size_t GetSizeInBytes() const override {
24 return size; 24 return size;
25 } 25 }
26 26
27 // We do not have to flush this cache as things in it are never modified by us.
28 void Flush() override {}
29
27 VAddr addr; 30 VAddr addr;
28 std::size_t size; 31 std::size_t size;
29 GLintptr offset; 32 GLintptr offset;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 8d5f277e2..be51c5215 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -424,6 +424,13 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
424 // Used when just a single color attachment is enabled, e.g. for clearing a color buffer 424 // Used when just a single color attachment is enabled, e.g. for clearing a color buffer
425 Surface color_surface = 425 Surface color_surface =
426 res_cache.GetColorBufferSurface(*single_color_target, preserve_contents); 426 res_cache.GetColorBufferSurface(*single_color_target, preserve_contents);
427
428 if (color_surface) {
429 // Assume that a surface will be written to if it is used as a framebuffer, even if
430 // the shader doesn't actually write to it.
431 color_surface->MarkAsModified(true, res_cache);
432 }
433
427 glFramebufferTexture2D( 434 glFramebufferTexture2D(
428 GL_DRAW_FRAMEBUFFER, 435 GL_DRAW_FRAMEBUFFER,
429 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D, 436 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D,
@@ -434,6 +441,13 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
434 std::array<GLenum, Maxwell::NumRenderTargets> buffers; 441 std::array<GLenum, Maxwell::NumRenderTargets> buffers;
435 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { 442 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
436 Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents); 443 Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents);
444
445 if (color_surface) {
446 // Assume that a surface will be written to if it is used as a framebuffer, even
447 // if the shader doesn't actually write to it.
448 color_surface->MarkAsModified(true, res_cache);
449 }
450
437 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); 451 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
438 glFramebufferTexture2D( 452 glFramebufferTexture2D(
439 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), 453 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index),
@@ -453,6 +467,10 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
453 } 467 }
454 468
455 if (depth_surface) { 469 if (depth_surface) {
470 // Assume that a surface will be written to if it is used as a framebuffer, even if
471 // the shader doesn't actually write to it.
472 depth_surface->MarkAsModified(true, res_cache);
473
456 if (regs.stencil_enable) { 474 if (regs.stencil_enable) {
457 // Attach both depth and stencil 475 // Attach both depth and stencil
458 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 476 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
@@ -552,10 +570,11 @@ void RasterizerOpenGL::DrawArrays() {
552 SyncBlendState(); 570 SyncBlendState();
553 SyncLogicOpState(); 571 SyncLogicOpState();
554 SyncCullMode(); 572 SyncCullMode();
555 SyncAlphaTest();
556 SyncScissorTest(); 573 SyncScissorTest();
574 // Alpha Testing is synced on shaders.
557 SyncTransformFeedback(); 575 SyncTransformFeedback();
558 SyncPointState(); 576 SyncPointState();
577 CheckAlphaTests();
559 578
560 // TODO(bunnei): Sync framebuffer_scale uniform here 579 // TODO(bunnei): Sync framebuffer_scale uniform here
561 // TODO(bunnei): Sync scissorbox uniform(s) here 580 // TODO(bunnei): Sync scissorbox uniform(s) here
@@ -617,7 +636,14 @@ void RasterizerOpenGL::DrawArrays() {
617 636
618void RasterizerOpenGL::FlushAll() {} 637void RasterizerOpenGL::FlushAll() {}
619 638
620void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {} 639void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
640 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
641
642 if (Settings::values.use_accurate_gpu_emulation) {
643 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
644 res_cache.FlushRegion(addr, size);
645 }
646}
621 647
622void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { 648void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
623 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 649 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
@@ -627,12 +653,19 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
627} 653}
628 654
629void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { 655void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
656 FlushRegion(addr, size);
630 InvalidateRegion(addr, size); 657 InvalidateRegion(addr, size);
631} 658}
632 659
633bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, 660bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
634 const Tegra::Engines::Fermi2D::Regs::Surface& dst) { 661 const Tegra::Engines::Fermi2D::Regs::Surface& dst) {
635 MICROPROFILE_SCOPE(OpenGL_Blits); 662 MICROPROFILE_SCOPE(OpenGL_Blits);
663
664 if (Settings::values.use_accurate_gpu_emulation) {
665 // Skip the accelerated copy and perform a slow but more accurate copy
666 return false;
667 }
668
636 res_cache.FermiCopySurface(src, dst); 669 res_cache.FermiCopySurface(src, dst);
637 return true; 670 return true;
638} 671}
@@ -975,17 +1008,6 @@ void RasterizerOpenGL::SyncLogicOpState() {
975 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); 1008 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation);
976} 1009}
977 1010
978void RasterizerOpenGL::SyncAlphaTest() {
979 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
980
981 // TODO(Rodrigo): Alpha testing is a legacy OpenGL feature, but it can be
982 // implemented with a test+discard in fragment shaders.
983 if (regs.alpha_test_enabled != 0) {
984 LOG_CRITICAL(Render_OpenGL, "Alpha testing is not implemented");
985 UNREACHABLE();
986 }
987}
988
989void RasterizerOpenGL::SyncScissorTest() { 1011void RasterizerOpenGL::SyncScissorTest() {
990 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1012 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
991 1013
@@ -1020,4 +1042,15 @@ void RasterizerOpenGL::SyncPointState() {
1020 state.point.size = regs.point_size == 0 ? 1 : regs.point_size; 1042 state.point.size = regs.point_size == 0 ? 1 : regs.point_size;
1021} 1043}
1022 1044
1045void RasterizerOpenGL::CheckAlphaTests() {
1046 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1047
1048 if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) {
1049 LOG_CRITICAL(
1050 Render_OpenGL,
1051 "Alpha Testing is enabled with Multiple Render Targets, this behavior is undefined.");
1052 UNREACHABLE();
1053 }
1054}
1055
1023} // namespace OpenGL 1056} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index b1f7ccc7e..0e90a31f5 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -162,9 +162,6 @@ private:
162 /// Syncs the LogicOp state to match the guest state 162 /// Syncs the LogicOp state to match the guest state
163 void SyncLogicOpState(); 163 void SyncLogicOpState();
164 164
165 /// Syncs the alpha test state to match the guest state
166 void SyncAlphaTest();
167
168 /// Syncs the scissor test state to match the guest state 165 /// Syncs the scissor test state to match the guest state
169 void SyncScissorTest(); 166 void SyncScissorTest();
170 167
@@ -174,6 +171,9 @@ private:
174 /// Syncs the point state to match the guest state 171 /// Syncs the point state to match the guest state
175 void SyncPointState(); 172 void SyncPointState();
176 173
174 /// Check asserts for alpha testing.
175 void CheckAlphaTests();
176
177 bool has_ARB_direct_state_access = false; 177 bool has_ARB_direct_state_access = false;
178 bool has_ARB_multi_bind = false; 178 bool has_ARB_multi_bind = false;
179 bool has_ARB_separate_shader_objects = false; 179 bool has_ARB_separate_shader_objects = false;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 801d45144..9c8925383 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -34,16 +34,53 @@ struct FormatTuple {
34 bool compressed; 34 bool compressed;
35}; 35};
36 36
37static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { 37static bool IsPixelFormatASTC(PixelFormat format) {
38 auto& gpu{Core::System::GetInstance().GPU()}; 38 switch (format) {
39 const auto cpu_addr{gpu.MemoryManager().GpuToCpuAddress(gpu_addr)}; 39 case PixelFormat::ASTC_2D_4X4:
40 return cpu_addr ? *cpu_addr : 0; 40 case PixelFormat::ASTC_2D_5X4:
41 case PixelFormat::ASTC_2D_8X8:
42 case PixelFormat::ASTC_2D_8X5:
43 return true;
44 default:
45 return false;
46 }
47}
48
49static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
50 switch (format) {
51 case PixelFormat::ASTC_2D_4X4:
52 return {4, 4};
53 case PixelFormat::ASTC_2D_5X4:
54 return {5, 4};
55 case PixelFormat::ASTC_2D_8X8:
56 return {8, 8};
57 case PixelFormat::ASTC_2D_8X5:
58 return {8, 5};
59 default:
60 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
61 UNREACHABLE();
62 }
63}
64
65void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
66 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
67 const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)};
68
69 addr = cpu_addr ? *cpu_addr : 0;
70 gpu_addr = gpu_addr_;
71 size_in_bytes = SizeInBytesRaw();
72
73 if (IsPixelFormatASTC(pixel_format)) {
74 // ASTC is uncompressed in software, in emulated as RGBA8
75 size_in_bytes_gl = width * height * depth * 4;
76 } else {
77 size_in_bytes_gl = SizeInBytesGL();
78 }
41} 79}
42 80
43/*static*/ SurfaceParams SurfaceParams::CreateForTexture( 81/*static*/ SurfaceParams SurfaceParams::CreateForTexture(
44 const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) { 82 const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) {
45 SurfaceParams params{}; 83 SurfaceParams params{};
46 params.addr = TryGetCpuAddr(config.tic.Address());
47 params.is_tiled = config.tic.IsTiled(); 84 params.is_tiled = config.tic.IsTiled();
48 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0, 85 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
49 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, 86 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
@@ -87,18 +124,18 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
87 break; 124 break;
88 } 125 }
89 126
90 params.size_in_bytes_total = params.SizeInBytesTotal();
91 params.size_in_bytes_2d = params.SizeInBytes2D();
92 params.max_mip_level = config.tic.max_mip_level + 1; 127 params.max_mip_level = config.tic.max_mip_level + 1;
93 params.rt = {}; 128 params.rt = {};
94 129
130 params.InitCacheParameters(config.tic.Address());
131
95 return params; 132 return params;
96} 133}
97 134
98/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(std::size_t index) { 135/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(std::size_t index) {
99 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]}; 136 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]};
100 SurfaceParams params{}; 137 SurfaceParams params{};
101 params.addr = TryGetCpuAddr(config.Address()); 138
102 params.is_tiled = 139 params.is_tiled =
103 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; 140 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
104 params.block_width = 1 << config.memory_layout.block_width; 141 params.block_width = 1 << config.memory_layout.block_width;
@@ -112,16 +149,17 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
112 params.unaligned_height = config.height; 149 params.unaligned_height = config.height;
113 params.target = SurfaceTarget::Texture2D; 150 params.target = SurfaceTarget::Texture2D;
114 params.depth = 1; 151 params.depth = 1;
115 params.size_in_bytes_total = params.SizeInBytesTotal();
116 params.size_in_bytes_2d = params.SizeInBytes2D();
117 params.max_mip_level = 0; 152 params.max_mip_level = 0;
118 153
119 // Render target specific parameters, not used for caching 154 // Render target specific parameters, not used for caching
120 params.rt.index = static_cast<u32>(index); 155 params.rt.index = static_cast<u32>(index);
121 params.rt.array_mode = config.array_mode; 156 params.rt.array_mode = config.array_mode;
122 params.rt.layer_stride = config.layer_stride; 157 params.rt.layer_stride = config.layer_stride;
158 params.rt.volume = config.volume;
123 params.rt.base_layer = config.base_layer; 159 params.rt.base_layer = config.base_layer;
124 160
161 params.InitCacheParameters(config.Address());
162
125 return params; 163 return params;
126} 164}
127 165
@@ -130,7 +168,7 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
130 u32 block_width, u32 block_height, u32 block_depth, 168 u32 block_width, u32 block_height, u32 block_depth,
131 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) { 169 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
132 SurfaceParams params{}; 170 SurfaceParams params{};
133 params.addr = TryGetCpuAddr(zeta_address); 171
134 params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; 172 params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
135 params.block_width = 1 << std::min(block_width, 5U); 173 params.block_width = 1 << std::min(block_width, 5U);
136 params.block_height = 1 << std::min(block_height, 5U); 174 params.block_height = 1 << std::min(block_height, 5U);
@@ -143,18 +181,18 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
143 params.unaligned_height = zeta_height; 181 params.unaligned_height = zeta_height;
144 params.target = SurfaceTarget::Texture2D; 182 params.target = SurfaceTarget::Texture2D;
145 params.depth = 1; 183 params.depth = 1;
146 params.size_in_bytes_total = params.SizeInBytesTotal();
147 params.size_in_bytes_2d = params.SizeInBytes2D();
148 params.max_mip_level = 0; 184 params.max_mip_level = 0;
149 params.rt = {}; 185 params.rt = {};
150 186
187 params.InitCacheParameters(zeta_address);
188
151 return params; 189 return params;
152} 190}
153 191
154/*static*/ SurfaceParams SurfaceParams::CreateForFermiCopySurface( 192/*static*/ SurfaceParams SurfaceParams::CreateForFermiCopySurface(
155 const Tegra::Engines::Fermi2D::Regs::Surface& config) { 193 const Tegra::Engines::Fermi2D::Regs::Surface& config) {
156 SurfaceParams params{}; 194 SurfaceParams params{};
157 params.addr = TryGetCpuAddr(config.Address()); 195
158 params.is_tiled = !config.linear; 196 params.is_tiled = !config.linear;
159 params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0, 197 params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0,
160 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0, 198 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
@@ -167,11 +205,11 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
167 params.unaligned_height = config.height; 205 params.unaligned_height = config.height;
168 params.target = SurfaceTarget::Texture2D; 206 params.target = SurfaceTarget::Texture2D;
169 params.depth = 1; 207 params.depth = 1;
170 params.size_in_bytes_total = params.SizeInBytesTotal();
171 params.size_in_bytes_2d = params.SizeInBytes2D();
172 params.max_mip_level = 0; 208 params.max_mip_level = 0;
173 params.rt = {}; 209 params.rt = {};
174 210
211 params.InitCacheParameters(config.Address());
212
175 return params; 213 return params;
176} 214}
177 215
@@ -276,34 +314,6 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType
276 return format; 314 return format;
277} 315}
278 316
279static bool IsPixelFormatASTC(PixelFormat format) {
280 switch (format) {
281 case PixelFormat::ASTC_2D_4X4:
282 case PixelFormat::ASTC_2D_5X4:
283 case PixelFormat::ASTC_2D_8X8:
284 case PixelFormat::ASTC_2D_8X5:
285 return true;
286 default:
287 return false;
288 }
289}
290
291static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
292 switch (format) {
293 case PixelFormat::ASTC_2D_4X4:
294 return {4, 4};
295 case PixelFormat::ASTC_2D_5X4:
296 return {5, 4};
297 case PixelFormat::ASTC_2D_8X8:
298 return {8, 8};
299 case PixelFormat::ASTC_2D_8X5:
300 return {8, 5};
301 default:
302 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
303 UNREACHABLE();
304 }
305}
306
307MathUtil::Rectangle<u32> SurfaceParams::GetRect() const { 317MathUtil::Rectangle<u32> SurfaceParams::GetRect() const {
308 u32 actual_height{unaligned_height}; 318 u32 actual_height{unaligned_height};
309 if (IsPixelFormatASTC(pixel_format)) { 319 if (IsPixelFormatASTC(pixel_format)) {
@@ -333,23 +343,21 @@ static bool IsFormatBCn(PixelFormat format) {
333template <bool morton_to_gl, PixelFormat format> 343template <bool morton_to_gl, PixelFormat format>
334void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer, 344void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer,
335 std::size_t gl_buffer_size, VAddr addr) { 345 std::size_t gl_buffer_size, VAddr addr) {
336 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; 346 constexpr u32 bytes_per_pixel = SurfaceParams::GetBytesPerPixel(format);
337 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); 347
348 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
349 // pixel values.
350 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
338 351
339 if (morton_to_gl) { 352 if (morton_to_gl) {
340 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
341 // pixel values.
342 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
343 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( 353 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture(
344 addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth); 354 addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth);
345 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())}; 355 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
346 memcpy(gl_buffer, data.data(), size_to_copy); 356 memcpy(gl_buffer, data.data(), size_to_copy);
347 } else { 357 } else {
348 // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should 358 Tegra::Texture::CopySwizzledData(stride / tile_size, height / tile_size, depth,
349 // check the configuration for this and perform more generic un/swizzle 359 bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr),
350 LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); 360 gl_buffer, false, block_height, block_depth);
351 VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel,
352 Memory::GetPointer(addr), gl_buffer, morton_to_gl);
353 } 361 }
354} 362}
355 363
@@ -430,17 +438,16 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t,
430 MortonCopy<false, PixelFormat::RGBA16UI>, 438 MortonCopy<false, PixelFormat::RGBA16UI>,
431 MortonCopy<false, PixelFormat::R11FG11FB10F>, 439 MortonCopy<false, PixelFormat::R11FG11FB10F>,
432 MortonCopy<false, PixelFormat::RGBA32UI>, 440 MortonCopy<false, PixelFormat::RGBA32UI>,
433 // TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/DXN2/BC7U/BC6H_UF16/BC6H_SF16/ASTC_2D_4X4 441 MortonCopy<false, PixelFormat::DXT1>,
434 // formats are not supported 442 MortonCopy<false, PixelFormat::DXT23>,
435 nullptr, 443 MortonCopy<false, PixelFormat::DXT45>,
436 nullptr, 444 MortonCopy<false, PixelFormat::DXN1>,
437 nullptr, 445 MortonCopy<false, PixelFormat::DXN2UNORM>,
438 nullptr, 446 MortonCopy<false, PixelFormat::DXN2SNORM>,
439 nullptr, 447 MortonCopy<false, PixelFormat::BC7U>,
440 nullptr, 448 MortonCopy<false, PixelFormat::BC6H_UF16>,
441 nullptr, 449 MortonCopy<false, PixelFormat::BC6H_SF16>,
442 nullptr, 450 // TODO(Subv): Swizzling ASTC formats are not supported
443 nullptr,
444 nullptr, 451 nullptr,
445 MortonCopy<false, PixelFormat::G8R8U>, 452 MortonCopy<false, PixelFormat::G8R8U>,
446 MortonCopy<false, PixelFormat::G8R8S>, 453 MortonCopy<false, PixelFormat::G8R8S>,
@@ -626,22 +633,21 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
626 auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type); 633 auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type);
627 auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type); 634 auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type);
628 635
629 std::size_t buffer_size = 636 std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);
630 std::max(src_params.size_in_bytes_total, dst_params.size_in_bytes_total);
631 637
632 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle); 638 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);
633 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); 639 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
634 if (source_format.compressed) { 640 if (source_format.compressed) {
635 glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment, 641 glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment,
636 static_cast<GLsizei>(src_params.size_in_bytes_total), nullptr); 642 static_cast<GLsizei>(src_params.size_in_bytes), nullptr);
637 } else { 643 } else {
638 glGetTextureImage(src_surface->Texture().handle, src_attachment, source_format.format, 644 glGetTextureImage(src_surface->Texture().handle, src_attachment, source_format.format,
639 source_format.type, static_cast<GLsizei>(src_params.size_in_bytes_total), 645 source_format.type, static_cast<GLsizei>(src_params.size_in_bytes),
640 nullptr); 646 nullptr);
641 } 647 }
642 // If the new texture is bigger than the previous one, we need to fill in the rest with data 648 // If the new texture is bigger than the previous one, we need to fill in the rest with data
643 // from the CPU. 649 // from the CPU.
644 if (src_params.size_in_bytes_total < dst_params.size_in_bytes_total) { 650 if (src_params.size_in_bytes < dst_params.size_in_bytes) {
645 // Upload the rest of the memory. 651 // Upload the rest of the memory.
646 if (dst_params.is_tiled) { 652 if (dst_params.is_tiled) {
647 // TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest 653 // TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest
@@ -651,12 +657,12 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
651 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during " 657 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
652 "reinterpretation but the texture is tiled."); 658 "reinterpretation but the texture is tiled.");
653 } 659 }
654 std::size_t remaining_size = 660 std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
655 dst_params.size_in_bytes_total - src_params.size_in_bytes_total;
656 std::vector<u8> data(remaining_size); 661 std::vector<u8> data(remaining_size);
657 Memory::ReadBlock(dst_params.addr + src_params.size_in_bytes_total, data.data(), 662 std::memcpy(data.data(), Memory::GetPointer(dst_params.addr + src_params.size_in_bytes),
658 data.size()); 663 data.size());
659 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes_total, remaining_size, 664
665 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,
660 data.data()); 666 data.data());
661 } 667 }
662 668
@@ -702,7 +708,8 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
702} 708}
703 709
704CachedSurface::CachedSurface(const SurfaceParams& params) 710CachedSurface::CachedSurface(const SurfaceParams& params)
705 : params(params), gl_target(SurfaceTargetToGL(params.target)) { 711 : params(params), gl_target(SurfaceTargetToGL(params.target)),
712 cached_size_in_bytes(params.size_in_bytes) {
706 texture.Create(); 713 texture.Create();
707 const auto& rect{params.GetRect()}; 714 const auto& rect{params.GetRect()};
708 715
@@ -752,9 +759,21 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
752 759
753 VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, 760 VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr,
754 SurfaceParams::SurfaceTargetName(params.target)); 761 SurfaceParams::SurfaceTargetName(params.target));
762
763 // Clamp size to mapped GPU memory region
764 // TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000
765 // R32F render buffer. We do not yet know if this is a game bug or something else, but this
766 // check is necessary to prevent flushing from overwriting unmapped memory.
767
768 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
769 const u64 max_size{memory_manager.GetRegionEnd(params.gpu_addr) - params.gpu_addr};
770 if (cached_size_in_bytes > max_size) {
771 LOG_ERROR(HW_GPU, "Surface size {} exceeds region size {}", params.size_in_bytes, max_size);
772 cached_size_in_bytes = max_size;
773 }
755} 774}
756 775
757static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { 776static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bool reverse) {
758 union S8Z24 { 777 union S8Z24 {
759 BitField<0, 24, u32> z24; 778 BitField<0, 24, u32> z24;
760 BitField<24, 8, u32> s8; 779 BitField<24, 8, u32> s8;
@@ -767,22 +786,29 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
767 }; 786 };
768 static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size"); 787 static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size");
769 788
770 S8Z24 input_pixel{}; 789 S8Z24 s8z24_pixel{};
771 Z24S8 output_pixel{}; 790 Z24S8 z24s8_pixel{};
772 constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)}; 791 constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::S8Z24)};
773 for (std::size_t y = 0; y < height; ++y) { 792 for (std::size_t y = 0; y < height; ++y) {
774 for (std::size_t x = 0; x < width; ++x) { 793 for (std::size_t x = 0; x < width; ++x) {
775 const std::size_t offset{bpp * (y * width + x)}; 794 const std::size_t offset{bpp * (y * width + x)};
776 std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24)); 795 if (reverse) {
777 output_pixel.s8.Assign(input_pixel.s8); 796 std::memcpy(&z24s8_pixel, &data[offset], sizeof(Z24S8));
778 output_pixel.z24.Assign(input_pixel.z24); 797 s8z24_pixel.s8.Assign(z24s8_pixel.s8);
779 std::memcpy(&data[offset], &output_pixel, sizeof(Z24S8)); 798 s8z24_pixel.z24.Assign(z24s8_pixel.z24);
799 std::memcpy(&data[offset], &s8z24_pixel, sizeof(S8Z24));
800 } else {
801 std::memcpy(&s8z24_pixel, &data[offset], sizeof(S8Z24));
802 z24s8_pixel.s8.Assign(s8z24_pixel.s8);
803 z24s8_pixel.z24.Assign(s8z24_pixel.z24);
804 std::memcpy(&data[offset], &z24s8_pixel, sizeof(Z24S8));
805 }
780 } 806 }
781 } 807 }
782} 808}
783 809
784static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { 810static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
785 constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8U)}; 811 constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::G8R8U)};
786 for (std::size_t y = 0; y < height; ++y) { 812 for (std::size_t y = 0; y < height; ++y) {
787 for (std::size_t x = 0; x < width; ++x) { 813 for (std::size_t x = 0; x < width; ++x) {
788 const std::size_t offset{bpp * (y * width + x)}; 814 const std::size_t offset{bpp * (y * width + x)};
@@ -814,7 +840,7 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
814 } 840 }
815 case PixelFormat::S8Z24: 841 case PixelFormat::S8Z24:
816 // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24. 842 // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24.
817 ConvertS8Z24ToZ24S8(data, width, height); 843 ConvertS8Z24ToZ24S8(data, width, height, false);
818 break; 844 break;
819 845
820 case PixelFormat::G8R8U: 846 case PixelFormat::G8R8U:
@@ -825,22 +851,36 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
825 } 851 }
826} 852}
827 853
854/**
855 * Helper function to perform software conversion (as needed) when flushing a buffer from OpenGL to
856 * Switch memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or
857 * with typical desktop GPUs.
858 */
859static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
860 u32 width, u32 height) {
861 switch (pixel_format) {
862 case PixelFormat::G8R8U:
863 case PixelFormat::G8R8S:
864 case PixelFormat::ASTC_2D_4X4:
865 case PixelFormat::ASTC_2D_8X8: {
866 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
867 static_cast<u32>(pixel_format));
868 UNREACHABLE();
869 break;
870 }
871 case PixelFormat::S8Z24:
872 // Convert the Z24S8 depth format to S8Z24, as OpenGL does not support S8Z24.
873 ConvertS8Z24ToZ24S8(data, width, height, true);
874 break;
875 }
876}
877
828MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); 878MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192));
829void CachedSurface::LoadGLBuffer() { 879void CachedSurface::LoadGLBuffer() {
830 ASSERT(params.type != SurfaceType::Fill);
831
832 const u8* const texture_src_data = Memory::GetPointer(params.addr);
833
834 ASSERT(texture_src_data);
835
836 const u32 bytes_per_pixel = GetGLBytesPerPixel(params.pixel_format);
837 const u32 copy_size = params.width * params.height * bytes_per_pixel;
838 const std::size_t total_size = copy_size * params.depth;
839
840 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); 880 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
841 881
882 gl_buffer.resize(params.size_in_bytes_gl);
842 if (params.is_tiled) { 883 if (params.is_tiled) {
843 gl_buffer.resize(total_size);
844 u32 depth = params.depth; 884 u32 depth = params.depth;
845 u32 block_depth = params.block_depth; 885 u32 block_depth = params.block_depth;
846 886
@@ -853,13 +893,12 @@ void CachedSurface::LoadGLBuffer() {
853 block_depth = 1U; 893 block_depth = 1U;
854 } 894 }
855 895
856 const std::size_t size = copy_size * depth;
857
858 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( 896 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
859 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(), 897 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
860 size, params.addr); 898 gl_buffer.size(), params.addr);
861 } else { 899 } else {
862 const u8* const texture_src_data_end{texture_src_data + total_size}; 900 const auto texture_src_data{Memory::GetPointer(params.addr)};
901 const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl};
863 gl_buffer.assign(texture_src_data, texture_src_data_end); 902 gl_buffer.assign(texture_src_data, texture_src_data_end);
864 } 903 }
865 904
@@ -868,7 +907,44 @@ void CachedSurface::LoadGLBuffer() {
868 907
869MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); 908MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
870void CachedSurface::FlushGLBuffer() { 909void CachedSurface::FlushGLBuffer() {
871 ASSERT_MSG(false, "Unimplemented"); 910 MICROPROFILE_SCOPE(OpenGL_SurfaceFlush);
911
912 ASSERT_MSG(!IsPixelFormatASTC(params.pixel_format), "Unimplemented");
913
914 // OpenGL temporary buffer needs to be big enough to store raw texture size
915 gl_buffer.resize(GetSizeInBytes());
916
917 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
918 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
919 ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0);
920 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
921 ASSERT(!tuple.compressed);
922 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
923 glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, gl_buffer.size(),
924 gl_buffer.data());
925 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
926 ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width,
927 params.height);
928 ASSERT(params.type != SurfaceType::Fill);
929 const u8* const texture_src_data = Memory::GetPointer(params.addr);
930 ASSERT(texture_src_data);
931 if (params.is_tiled) {
932 u32 depth = params.depth;
933 u32 block_depth = params.block_depth;
934
935 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
936 params.block_width, static_cast<u32>(params.target));
937
938 if (params.target == SurfaceParams::SurfaceTarget::Texture2D) {
939 // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
940 depth = 1U;
941 }
942 gl_to_morton_fns[static_cast<size_t>(params.pixel_format)](
943 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
944 gl_buffer.size(), GetAddr());
945 } else {
946 std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes());
947 }
872} 948}
873 949
874MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); 950MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
@@ -878,9 +954,6 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
878 954
879 MICROPROFILE_SCOPE(OpenGL_TextureUL); 955 MICROPROFILE_SCOPE(OpenGL_TextureUL);
880 956
881 ASSERT(gl_buffer.size() == static_cast<std::size_t>(params.width) * params.height *
882 GetGLBytesPerPixel(params.pixel_format) * params.depth);
883
884 const auto& rect{params.GetRect()}; 957 const auto& rect{params.GetRect()};
885 958
886 // Load data from memory to the surface 959 // Load data from memory to the surface
@@ -889,7 +962,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
889 std::size_t buffer_offset = 962 std::size_t buffer_offset =
890 static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width + 963 static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width +
891 static_cast<std::size_t>(x0)) * 964 static_cast<std::size_t>(x0)) *
892 GetGLBytesPerPixel(params.pixel_format); 965 SurfaceParams::GetBytesPerPixel(params.pixel_format);
893 966
894 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); 967 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
895 const GLuint target_tex = texture.handle; 968 const GLuint target_tex = texture.handle;
@@ -905,7 +978,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
905 cur_state.Apply(); 978 cur_state.Apply();
906 979
907 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT 980 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
908 ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0); 981 ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0);
909 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width)); 982 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width));
910 983
911 glActiveTexture(GL_TEXTURE0); 984 glActiveTexture(GL_TEXTURE0);
@@ -915,7 +988,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
915 glCompressedTexImage2D( 988 glCompressedTexImage2D(
916 SurfaceTargetToGL(params.target), 0, tuple.internal_format, 989 SurfaceTargetToGL(params.target), 0, tuple.internal_format,
917 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0, 990 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0,
918 static_cast<GLsizei>(params.size_in_bytes_2d), &gl_buffer[buffer_offset]); 991 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);
919 break; 992 break;
920 case SurfaceParams::SurfaceTarget::Texture3D: 993 case SurfaceParams::SurfaceTarget::Texture3D:
921 case SurfaceParams::SurfaceTarget::Texture2DArray: 994 case SurfaceParams::SurfaceTarget::Texture2DArray:
@@ -923,16 +996,16 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
923 SurfaceTargetToGL(params.target), 0, tuple.internal_format, 996 SurfaceTargetToGL(params.target), 0, tuple.internal_format,
924 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 997 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height),
925 static_cast<GLsizei>(params.depth), 0, 998 static_cast<GLsizei>(params.depth), 0,
926 static_cast<GLsizei>(params.size_in_bytes_total), &gl_buffer[buffer_offset]); 999 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);
927 break; 1000 break;
928 case SurfaceParams::SurfaceTarget::TextureCubemap: 1001 case SurfaceParams::SurfaceTarget::TextureCubemap:
929 for (std::size_t face = 0; face < params.depth; ++face) { 1002 for (std::size_t face = 0; face < params.depth; ++face) {
930 glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), 1003 glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face),
931 0, tuple.internal_format, static_cast<GLsizei>(params.width), 1004 0, tuple.internal_format, static_cast<GLsizei>(params.width),
932 static_cast<GLsizei>(params.height), 0, 1005 static_cast<GLsizei>(params.height), 0,
933 static_cast<GLsizei>(params.size_in_bytes_2d), 1006 static_cast<GLsizei>(params.SizeInBytesCubeFaceGL()),
934 &gl_buffer[buffer_offset]); 1007 &gl_buffer[buffer_offset]);
935 buffer_offset += params.size_in_bytes_2d; 1008 buffer_offset += params.SizeInBytesCubeFace();
936 } 1009 }
937 break; 1010 break;
938 default: 1011 default:
@@ -942,7 +1015,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
942 glCompressedTexImage2D( 1015 glCompressedTexImage2D(
943 GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width), 1016 GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width),
944 static_cast<GLsizei>(params.height), 0, 1017 static_cast<GLsizei>(params.height), 0,
945 static_cast<GLsizei>(params.size_in_bytes_2d), &gl_buffer[buffer_offset]); 1018 static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);
946 } 1019 }
947 } else { 1020 } else {
948 1021
@@ -971,7 +1044,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
971 y0, static_cast<GLsizei>(rect.GetWidth()), 1044 y0, static_cast<GLsizei>(rect.GetWidth()),
972 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, 1045 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
973 &gl_buffer[buffer_offset]); 1046 &gl_buffer[buffer_offset]);
974 buffer_offset += params.size_in_bytes_2d; 1047 buffer_offset += params.SizeInBytesCubeFace();
975 } 1048 }
976 break; 1049 break;
977 default: 1050 default:
@@ -1033,10 +1106,7 @@ Surface RasterizerCacheOpenGL::GetColorBufferSurface(std::size_t index, bool pre
1033void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) { 1106void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
1034 surface->LoadGLBuffer(); 1107 surface->LoadGLBuffer();
1035 surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle); 1108 surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
1036} 1109 surface->MarkAsModified(false, *this);
1037
1038void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) {
1039 surface->FlushGLBuffer();
1040} 1110}
1041 1111
1042Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) { 1112Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) {
@@ -1053,8 +1123,8 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres
1053 } else if (preserve_contents) { 1123 } else if (preserve_contents) {
1054 // If surface parameters changed and we care about keeping the previous data, recreate 1124 // If surface parameters changed and we care about keeping the previous data, recreate
1055 // the surface from the old one 1125 // the surface from the old one
1056 Unregister(surface);
1057 Surface new_surface{RecreateSurface(surface, params)}; 1126 Surface new_surface{RecreateSurface(surface, params)};
1127 Unregister(surface);
1058 Register(new_surface); 1128 Register(new_surface);
1059 return new_surface; 1129 return new_surface;
1060 } else { 1130 } else {
@@ -1105,6 +1175,14 @@ void RasterizerCacheOpenGL::FermiCopySurface(
1105 FastCopySurface(GetSurface(src_params, true), GetSurface(dst_params, false)); 1175 FastCopySurface(GetSurface(src_params, true), GetSurface(dst_params, false));
1106} 1176}
1107 1177
1178void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface,
1179 const Surface& dst_surface) {
1180 const auto& src_params{src_surface->GetSurfaceParams()};
1181 const auto& dst_params{dst_surface->GetSurfaceParams()};
1182 FlushRegion(src_params.addr, dst_params.size_in_bytes);
1183 LoadSurface(dst_surface);
1184}
1185
1108Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, 1186Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1109 const SurfaceParams& new_params) { 1187 const SurfaceParams& new_params) {
1110 // Verify surface is compatible for blitting 1188 // Verify surface is compatible for blitting
@@ -1113,6 +1191,12 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1113 // Get a new surface with the new parameters, and blit the previous surface to it 1191 // Get a new surface with the new parameters, and blit the previous surface to it
1114 Surface new_surface{GetUncachedSurface(new_params)}; 1192 Surface new_surface{GetUncachedSurface(new_params)};
1115 1193
1194 // With use_accurate_gpu_emulation enabled, do an accurate surface copy
1195 if (Settings::values.use_accurate_gpu_emulation) {
1196 AccurateCopySurface(old_surface, new_surface);
1197 return new_surface;
1198 }
1199
1116 // For compatible surfaces, we can just do fast glCopyImageSubData based copy 1200 // For compatible surfaces, we can just do fast glCopyImageSubData based copy
1117 if (old_params.target == new_params.target && old_params.type == new_params.type && 1201 if (old_params.target == new_params.target && old_params.type == new_params.type &&
1118 old_params.depth == new_params.depth && old_params.depth == 1 && 1202 old_params.depth == new_params.depth && old_params.depth == 1 &&
@@ -1124,11 +1208,10 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1124 1208
1125 // If the format is the same, just do a framebuffer blit. This is significantly faster than 1209 // If the format is the same, just do a framebuffer blit. This is significantly faster than
1126 // using PBOs. The is also likely less accurate, as textures will be converted rather than 1210 // using PBOs. The is also likely less accurate, as textures will be converted rather than
1127 // reinterpreted. When use_accurate_framebuffers setting is enabled, perform a more accurate 1211 // reinterpreted. When use_accurate_gpu_emulation setting is enabled, perform a more accurate
1128 // surface copy, where pixels are reinterpreted as a new format (without conversion). This 1212 // surface copy, where pixels are reinterpreted as a new format (without conversion). This
1129 // code path uses OpenGL PBOs and is quite slow. 1213 // code path uses OpenGL PBOs and is quite slow.
1130 const bool is_blit{old_params.pixel_format == new_params.pixel_format || 1214 const bool is_blit{old_params.pixel_format == new_params.pixel_format};
1131 !Settings::values.use_accurate_framebuffers};
1132 1215
1133 switch (new_params.target) { 1216 switch (new_params.target) {
1134 case SurfaceParams::SurfaceTarget::Texture2D: 1217 case SurfaceParams::SurfaceTarget::Texture2D:
@@ -1138,6 +1221,9 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1138 CopySurface(old_surface, new_surface, copy_pbo.handle); 1221 CopySurface(old_surface, new_surface, copy_pbo.handle);
1139 } 1222 }
1140 break; 1223 break;
1224 case SurfaceParams::SurfaceTarget::Texture3D:
1225 AccurateCopySurface(old_surface, new_surface);
1226 break;
1141 case SurfaceParams::SurfaceTarget::TextureCubemap: { 1227 case SurfaceParams::SurfaceTarget::TextureCubemap: {
1142 if (old_params.rt.array_mode != 1) { 1228 if (old_params.rt.array_mode != 1) {
1143 // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this 1229 // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 0b8ae3eb4..0dd0d90a3 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -18,6 +18,7 @@
18#include "video_core/rasterizer_cache.h" 18#include "video_core/rasterizer_cache.h"
19#include "video_core/renderer_opengl/gl_resource_manager.h" 19#include "video_core/renderer_opengl/gl_resource_manager.h"
20#include "video_core/renderer_opengl/gl_shader_gen.h" 20#include "video_core/renderer_opengl/gl_shader_gen.h"
21#include "video_core/textures/decoders.h"
21#include "video_core/textures/texture.h" 22#include "video_core/textures/texture.h"
22 23
23namespace OpenGL { 24namespace OpenGL {
@@ -131,6 +132,8 @@ struct SurfaceParams {
131 case Tegra::Texture::TextureType::Texture2D: 132 case Tegra::Texture::TextureType::Texture2D:
132 case Tegra::Texture::TextureType::Texture2DNoMipmap: 133 case Tegra::Texture::TextureType::Texture2DNoMipmap:
133 return SurfaceTarget::Texture2D; 134 return SurfaceTarget::Texture2D;
135 case Tegra::Texture::TextureType::Texture3D:
136 return SurfaceTarget::Texture3D;
134 case Tegra::Texture::TextureType::TextureCubemap: 137 case Tegra::Texture::TextureType::TextureCubemap:
135 return SurfaceTarget::TextureCubemap; 138 return SurfaceTarget::TextureCubemap;
136 case Tegra::Texture::TextureType::Texture1DArray: 139 case Tegra::Texture::TextureType::Texture1DArray:
@@ -701,21 +704,42 @@ struct SurfaceParams {
701 return SurfaceType::Invalid; 704 return SurfaceType::Invalid;
702 } 705 }
703 706
707 /// Returns the sizer in bytes of the specified pixel format
708 static constexpr u32 GetBytesPerPixel(PixelFormat pixel_format) {
709 if (pixel_format == SurfaceParams::PixelFormat::Invalid) {
710 return 0;
711 }
712 return GetFormatBpp(pixel_format) / CHAR_BIT;
713 }
714
704 /// Returns the rectangle corresponding to this surface 715 /// Returns the rectangle corresponding to this surface
705 MathUtil::Rectangle<u32> GetRect() const; 716 MathUtil::Rectangle<u32> GetRect() const;
706 717
707 /// Returns the size of this surface as a 2D texture in bytes, adjusted for compression 718 /// Returns the total size of this surface in bytes, adjusted for compression
708 std::size_t SizeInBytes2D() const { 719 std::size_t SizeInBytesRaw(bool ignore_tiled = false) const {
709 const u32 compression_factor{GetCompressionFactor(pixel_format)}; 720 const u32 compression_factor{GetCompressionFactor(pixel_format)};
710 ASSERT(width % compression_factor == 0); 721 const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)};
711 ASSERT(height % compression_factor == 0); 722 const size_t uncompressed_size{
712 return (width / compression_factor) * (height / compression_factor) * 723 Tegra::Texture::CalculateSize((ignore_tiled ? false : is_tiled), bytes_per_pixel, width,
713 GetFormatBpp(pixel_format) / CHAR_BIT; 724 height, depth, block_height, block_depth)};
725
726 // Divide by compression_factor^2, as height and width are factored by this
727 return uncompressed_size / (compression_factor * compression_factor);
714 } 728 }
715 729
716 /// Returns the total size of this surface in bytes, adjusted for compression 730 /// Returns the size of this surface as an OpenGL texture in bytes
717 std::size_t SizeInBytesTotal() const { 731 std::size_t SizeInBytesGL() const {
718 return SizeInBytes2D() * depth; 732 return SizeInBytesRaw(true);
733 }
734
735 /// Returns the size of this surface as a cube face in bytes
736 std::size_t SizeInBytesCubeFace() const {
737 return size_in_bytes / 6;
738 }
739
740 /// Returns the size of this surface as an OpenGL cube face in bytes
741 std::size_t SizeInBytesCubeFaceGL() const {
742 return size_in_bytes_gl / 6;
719 } 743 }
720 744
721 /// Creates SurfaceParams from a texture configuration 745 /// Creates SurfaceParams from a texture configuration
@@ -742,7 +766,9 @@ struct SurfaceParams {
742 other.depth); 766 other.depth);
743 } 767 }
744 768
745 VAddr addr; 769 /// Initializes parameters for caching, should be called after everything has been initialized
770 void InitCacheParameters(Tegra::GPUVAddr gpu_addr);
771
746 bool is_tiled; 772 bool is_tiled;
747 u32 block_width; 773 u32 block_width;
748 u32 block_height; 774 u32 block_height;
@@ -754,15 +780,20 @@ struct SurfaceParams {
754 u32 height; 780 u32 height;
755 u32 depth; 781 u32 depth;
756 u32 unaligned_height; 782 u32 unaligned_height;
757 std::size_t size_in_bytes_total;
758 std::size_t size_in_bytes_2d;
759 SurfaceTarget target; 783 SurfaceTarget target;
760 u32 max_mip_level; 784 u32 max_mip_level;
761 785
786 // Parameters used for caching
787 VAddr addr;
788 Tegra::GPUVAddr gpu_addr;
789 std::size_t size_in_bytes;
790 std::size_t size_in_bytes_gl;
791
762 // Render target specific parameters, not used in caching 792 // Render target specific parameters, not used in caching
763 struct { 793 struct {
764 u32 index; 794 u32 index;
765 u32 array_mode; 795 u32 array_mode;
796 u32 volume;
766 u32 layer_stride; 797 u32 layer_stride;
767 u32 base_layer; 798 u32 base_layer;
768 } rt; 799 } rt;
@@ -775,7 +806,8 @@ struct SurfaceReserveKey : Common::HashableStruct<OpenGL::SurfaceParams> {
775 static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) { 806 static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) {
776 SurfaceReserveKey res; 807 SurfaceReserveKey res;
777 res.state = params; 808 res.state = params;
778 res.state.rt = {}; // Ignore rt config in caching 809 res.state.gpu_addr = {}; // Ignore GPU vaddr in caching
810 res.state.rt = {}; // Ignore rt config in caching
779 return res; 811 return res;
780 } 812 }
781}; 813};
@@ -790,16 +822,20 @@ struct hash<SurfaceReserveKey> {
790 822
791namespace OpenGL { 823namespace OpenGL {
792 824
793class CachedSurface final { 825class CachedSurface final : public RasterizerCacheObject {
794public: 826public:
795 CachedSurface(const SurfaceParams& params); 827 CachedSurface(const SurfaceParams& params);
796 828
797 VAddr GetAddr() const { 829 VAddr GetAddr() const override {
798 return params.addr; 830 return params.addr;
799 } 831 }
800 832
801 std::size_t GetSizeInBytes() const { 833 std::size_t GetSizeInBytes() const override {
802 return params.size_in_bytes_total; 834 return cached_size_in_bytes;
835 }
836
837 void Flush() override {
838 FlushGLBuffer();
803 } 839 }
804 840
805 const OGLTexture& Texture() const { 841 const OGLTexture& Texture() const {
@@ -810,13 +846,6 @@ public:
810 return gl_target; 846 return gl_target;
811 } 847 }
812 848
813 static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) {
814 if (format == SurfaceParams::PixelFormat::Invalid)
815 return 0;
816
817 return SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
818 }
819
820 const SurfaceParams& GetSurfaceParams() const { 849 const SurfaceParams& GetSurfaceParams() const {
821 return params; 850 return params;
822 } 851 }
@@ -833,6 +862,7 @@ private:
833 std::vector<u8> gl_buffer; 862 std::vector<u8> gl_buffer;
834 SurfaceParams params; 863 SurfaceParams params;
835 GLenum gl_target; 864 GLenum gl_target;
865 std::size_t cached_size_in_bytes;
836}; 866};
837 867
838class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { 868class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
@@ -849,9 +879,6 @@ public:
849 /// Get the color surface based on the framebuffer configuration and the specified render target 879 /// Get the color surface based on the framebuffer configuration and the specified render target
850 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents); 880 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents);
851 881
852 /// Flushes the surface to Switch memory
853 void FlushSurface(const Surface& surface);
854
855 /// Tries to find a framebuffer using on the provided CPU address 882 /// Tries to find a framebuffer using on the provided CPU address
856 Surface TryFindFramebufferSurface(VAddr addr) const; 883 Surface TryFindFramebufferSurface(VAddr addr) const;
857 884
@@ -875,6 +902,9 @@ private:
875 /// Tries to get a reserved surface for the specified parameters 902 /// Tries to get a reserved surface for the specified parameters
876 Surface TryGetReservedSurface(const SurfaceParams& params); 903 Surface TryGetReservedSurface(const SurfaceParams& params);
877 904
905 /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data
906 void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface);
907
878 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have 908 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
879 /// previously been used. This is to prevent surfaces from being constantly created and 909 /// previously been used. This is to prevent surfaces from being constantly created and
880 /// destroyed when used with different surface parameters. 910 /// destroyed when used with different surface parameters.
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 7bb287f56..a210f1731 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -19,20 +19,21 @@ class CachedShader;
19using Shader = std::shared_ptr<CachedShader>; 19using Shader = std::shared_ptr<CachedShader>;
20using Maxwell = Tegra::Engines::Maxwell3D::Regs; 20using Maxwell = Tegra::Engines::Maxwell3D::Regs;
21 21
22class CachedShader final { 22class CachedShader final : public RasterizerCacheObject {
23public: 23public:
24 CachedShader(VAddr addr, Maxwell::ShaderProgram program_type); 24 CachedShader(VAddr addr, Maxwell::ShaderProgram program_type);
25 25
26 /// Gets the address of the shader in guest memory, required for cache management 26 VAddr GetAddr() const override {
27 VAddr GetAddr() const {
28 return addr; 27 return addr;
29 } 28 }
30 29
31 /// Gets the size of the shader in guest memory, required for cache management 30 std::size_t GetSizeInBytes() const override {
32 std::size_t GetSizeInBytes() const {
33 return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64); 31 return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64);
34 } 32 }
35 33
34 // We do not have to flush this cache as things in it are never modified by us.
35 void Flush() override {}
36
36 /// Gets the shader entries for the shader 37 /// Gets the shader entries for the shader
37 const GLShader::ShaderEntries& GetShaderEntries() const { 38 const GLShader::ShaderEntries& GetShaderEntries() const {
38 return entries; 39 return entries;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index ca063d90d..fe4d1bd83 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -30,8 +30,6 @@ using Tegra::Shader::SubOp;
30constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; 30constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
31constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); 31constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header);
32 32
33enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
34
35constexpr u32 MAX_GEOMETRY_BUFFERS = 6; 33constexpr u32 MAX_GEOMETRY_BUFFERS = 6;
36constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested 34constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested
37 35
@@ -165,10 +163,11 @@ private:
165 const ExitMethod jmp = Scan(target, end, labels); 163 const ExitMethod jmp = Scan(target, end, labels);
166 return exit_method = ParallelExit(no_jmp, jmp); 164 return exit_method = ParallelExit(no_jmp, jmp);
167 } 165 }
168 case OpCode::Id::SSY: { 166 case OpCode::Id::SSY:
169 // The SSY instruction uses a similar encoding as the BRA instruction. 167 case OpCode::Id::PBK: {
168 // The SSY and PBK use a similar encoding as the BRA instruction.
170 ASSERT_MSG(instr.bra.constant_buffer == 0, 169 ASSERT_MSG(instr.bra.constant_buffer == 0,
171 "Constant buffer SSY is not supported"); 170 "Constant buffer branching is not supported");
172 const u32 target = offset + instr.bra.GetBranchTarget(); 171 const u32 target = offset + instr.bra.GetBranchTarget();
173 labels.insert(target); 172 labels.insert(target);
174 // Continue scanning for an exit method. 173 // Continue scanning for an exit method.
@@ -376,11 +375,55 @@ public:
376 } 375 }
377 376
378 /** 377 /**
378 * Writes code that does a register assignment to a half float value operation.
379 * @param reg The destination register to use.
380 * @param elem The element to use for the operation.
381 * @param value The code representing the value to assign. Type has to be half float.
382 * @param merge Half float kind of assignment.
383 * @param dest_num_components Number of components in the destination.
384 * @param value_num_components Number of components in the value.
385 * @param is_saturated Optional, when True, saturates the provided value.
386 * @param dest_elem Optional, the destination element to use for the operation.
387 */
388 void SetRegisterToHalfFloat(const Register& reg, u64 elem, const std::string& value,
389 Tegra::Shader::HalfMerge merge, u64 dest_num_components,
390 u64 value_num_components, bool is_saturated = false,
391 u64 dest_elem = 0) {
392 ASSERT_MSG(!is_saturated, "Unimplemented");
393
394 const std::string result = [&]() {
395 switch (merge) {
396 case Tegra::Shader::HalfMerge::H0_H1:
397 return "uintBitsToFloat(packHalf2x16(" + value + "))";
398 case Tegra::Shader::HalfMerge::F32:
399 // Half float instructions take the first component when doing a float cast.
400 return "float(" + value + ".x)";
401 case Tegra::Shader::HalfMerge::Mrg_H0:
402 // TODO(Rodrigo): I guess Mrg_H0 and Mrg_H1 take their respective component from the
403 // pack. I couldn't test this on hardware but it shouldn't really matter since most
404 // of the time when a Mrg_* flag is used both components will be mirrored. That
405 // being said, it deserves a test.
406 return "((" + GetRegisterAsInteger(reg, 0, false) +
407 " & 0xffff0000) | (packHalf2x16(" + value + ") & 0x0000ffff))";
408 case Tegra::Shader::HalfMerge::Mrg_H1:
409 return "((" + GetRegisterAsInteger(reg, 0, false) +
410 " & 0x0000ffff) | (packHalf2x16(" + value + ") & 0xffff0000))";
411 default:
412 UNREACHABLE();
413 return std::string("0");
414 }
415 }();
416
417 SetRegister(reg, elem, result, dest_num_components, value_num_components, dest_elem);
418 }
419
420 /**
379 * Writes code that does a register assignment to input attribute operation. Input attributes 421 * Writes code that does a register assignment to input attribute operation. Input attributes
380 * are stored as floats, so this may require conversion. 422 * are stored as floats, so this may require conversion.
381 * @param reg The destination register to use. 423 * @param reg The destination register to use.
382 * @param elem The element to use for the operation. 424 * @param elem The element to use for the operation.
383 * @param attribute The input attribute to use as the source value. 425 * @param attribute The input attribute to use as the source value.
426 * @param input_mode The input mode.
384 * @param vertex The register that decides which vertex to read from (used in GS). 427 * @param vertex The register that decides which vertex to read from (used in GS).
385 */ 428 */
386 void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute, 429 void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute,
@@ -548,13 +591,6 @@ private:
548 591
549 /// Generates declarations for input attributes. 592 /// Generates declarations for input attributes.
550 void GenerateInputAttrs() { 593 void GenerateInputAttrs() {
551 if (stage != Maxwell3D::Regs::ShaderStage::Vertex) {
552 const std::string attr =
553 stage == Maxwell3D::Regs::ShaderStage::Geometry ? "gs_position[]" : "position";
554 declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) +
555 ") in vec4 " + attr + ';');
556 }
557
558 for (const auto element : declr_input_attribute) { 594 for (const auto element : declr_input_attribute) {
559 // TODO(bunnei): Use proper number of elements for these 595 // TODO(bunnei): Use proper number of elements for these
560 u32 idx = 596 u32 idx =
@@ -577,10 +613,6 @@ private:
577 613
578 /// Generates declarations for output attributes. 614 /// Generates declarations for output attributes.
579 void GenerateOutputAttrs() { 615 void GenerateOutputAttrs() {
580 if (stage != Maxwell3D::Regs::ShaderStage::Fragment) {
581 declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) +
582 ") out vec4 position;");
583 }
584 for (const auto& index : declr_output_attribute) { 616 for (const auto& index : declr_output_attribute) {
585 // TODO(bunnei): Use proper number of elements for these 617 // TODO(bunnei): Use proper number of elements for these
586 const u32 idx = static_cast<u32>(index) - 618 const u32 idx = static_cast<u32>(index) -
@@ -877,6 +909,19 @@ private:
877 return fmt::format("uintBitsToFloat({})", instr.alu.GetImm20_32()); 909 return fmt::format("uintBitsToFloat({})", instr.alu.GetImm20_32());
878 } 910 }
879 911
912 /// Generates code representing a vec2 pair unpacked from a half float immediate
913 static std::string UnpackHalfImmediate(const Instruction& instr, bool negate) {
914 const std::string immediate = GetHalfFloat(std::to_string(instr.half_imm.PackImmediates()));
915 if (!negate) {
916 return immediate;
917 }
918 const std::string negate_first = instr.half_imm.first_negate != 0 ? "-" : "";
919 const std::string negate_second = instr.half_imm.second_negate != 0 ? "-" : "";
920 const std::string negate_vec = "vec2(" + negate_first + "1, " + negate_second + "1)";
921
922 return '(' + immediate + " * " + negate_vec + ')';
923 }
924
880 /// Generates code representing a texture sampler. 925 /// Generates code representing a texture sampler.
881 std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array, 926 std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array,
882 bool is_shadow) { 927 bool is_shadow) {
@@ -908,7 +953,7 @@ private:
908 // Can't assign to the constant predicate. 953 // Can't assign to the constant predicate.
909 ASSERT(pred != static_cast<u64>(Pred::UnusedIndex)); 954 ASSERT(pred != static_cast<u64>(Pred::UnusedIndex));
910 955
911 const std::string variable = 'p' + std::to_string(pred) + '_' + suffix; 956 std::string variable = 'p' + std::to_string(pred) + '_' + suffix;
912 shader.AddLine(variable + " = " + value + ';'); 957 shader.AddLine(variable + " = " + value + ';');
913 declr_predicates.insert(std::move(variable)); 958 declr_predicates.insert(std::move(variable));
914 } 959 }
@@ -1013,6 +1058,41 @@ private:
1013 } 1058 }
1014 1059
1015 /* 1060 /*
1061 * Transforms the input string GLSL operand into an unpacked half float pair.
1062 * @note This function returns a float type pair instead of a half float pair. This is because
1063 * real half floats are not standardized in GLSL but unpackHalf2x16 (which returns a vec2) is.
1064 * @param operand Input operand. It has to be an unsigned integer.
1065 * @param type How to unpack the unsigned integer to a half float pair.
1066 * @param abs Get the absolute value of unpacked half floats.
1067 * @param neg Get the negative value of unpacked half floats.
1068 * @returns String corresponding to a half float pair.
1069 */
1070 static std::string GetHalfFloat(const std::string& operand,
1071 Tegra::Shader::HalfType type = Tegra::Shader::HalfType::H0_H1,
1072 bool abs = false, bool neg = false) {
1073 // "vec2" calls emitted in this function are intended to alias components.
1074 const std::string value = [&]() {
1075 switch (type) {
1076 case Tegra::Shader::HalfType::H0_H1:
1077 return "unpackHalf2x16(" + operand + ')';
1078 case Tegra::Shader::HalfType::F32:
1079 return "vec2(uintBitsToFloat(" + operand + "))";
1080 case Tegra::Shader::HalfType::H0_H0:
1081 case Tegra::Shader::HalfType::H1_H1: {
1082 const bool high = type == Tegra::Shader::HalfType::H1_H1;
1083 const char unpack_index = "xy"[high ? 1 : 0];
1084 return "vec2(unpackHalf2x16(" + operand + ")." + unpack_index + ')';
1085 }
1086 default:
1087 UNREACHABLE();
1088 return std::string("vec2(0)");
1089 }
1090 }();
1091
1092 return GetOperandAbsNeg(value, abs, neg);
1093 }
1094
1095 /*
1016 * Returns whether the instruction at the specified offset is a 'sched' instruction. 1096 * Returns whether the instruction at the specified offset is a 'sched' instruction.
1017 * Sched instructions always appear before a sequence of 3 instructions. 1097 * Sched instructions always appear before a sequence of 3 instructions.
1018 */ 1098 */
@@ -1142,6 +1222,7 @@ private:
1142 case Tegra::Shader::TextureType::Texture2D: { 1222 case Tegra::Shader::TextureType::Texture2D: {
1143 return 2; 1223 return 2;
1144 } 1224 }
1225 case Tegra::Shader::TextureType::Texture3D:
1145 case Tegra::Shader::TextureType::TextureCube: { 1226 case Tegra::Shader::TextureType::TextureCube: {
1146 return 3; 1227 return 3;
1147 } 1228 }
@@ -1153,27 +1234,27 @@ private:
1153 } 1234 }
1154 1235
1155 /* 1236 /*
1156 * Emits code to push the input target address to the SSY address stack, incrementing the stack 1237 * Emits code to push the input target address to the flow address stack, incrementing the stack
1157 * top. 1238 * top.
1158 */ 1239 */
1159 void EmitPushToSSYStack(u32 target) { 1240 void EmitPushToFlowStack(u32 target) {
1160 shader.AddLine('{'); 1241 shader.AddLine('{');
1161 ++shader.scope; 1242 ++shader.scope;
1162 shader.AddLine("ssy_stack[ssy_stack_top] = " + std::to_string(target) + "u;"); 1243 shader.AddLine("flow_stack[flow_stack_top] = " + std::to_string(target) + "u;");
1163 shader.AddLine("ssy_stack_top++;"); 1244 shader.AddLine("flow_stack_top++;");
1164 --shader.scope; 1245 --shader.scope;
1165 shader.AddLine('}'); 1246 shader.AddLine('}');
1166 } 1247 }
1167 1248
1168 /* 1249 /*
1169 * Emits code to pop an address from the SSY address stack, setting the jump address to the 1250 * Emits code to pop an address from the flow address stack, setting the jump address to the
1170 * popped address and decrementing the stack top. 1251 * popped address and decrementing the stack top.
1171 */ 1252 */
1172 void EmitPopFromSSYStack() { 1253 void EmitPopFromFlowStack() {
1173 shader.AddLine('{'); 1254 shader.AddLine('{');
1174 ++shader.scope; 1255 ++shader.scope;
1175 shader.AddLine("ssy_stack_top--;"); 1256 shader.AddLine("flow_stack_top--;");
1176 shader.AddLine("jmp_to = ssy_stack[ssy_stack_top];"); 1257 shader.AddLine("jmp_to = flow_stack[flow_stack_top];");
1177 shader.AddLine("break;"); 1258 shader.AddLine("break;");
1178 --shader.scope; 1259 --shader.scope;
1179 shader.AddLine('}'); 1260 shader.AddLine('}');
@@ -1185,9 +1266,29 @@ private:
1185 1266
1186 ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented"); 1267 ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented");
1187 1268
1269 shader.AddLine("if (alpha_test[0] != 0) {");
1270 ++shader.scope;
1271 // We start on the register containing the alpha value in the first RT.
1272 u32 current_reg = 3;
1273 for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets;
1274 ++render_target) {
1275 // TODO(Blinkhawk): verify the behavior of alpha testing on hardware when
1276 // multiple render targets are used.
1277 if (header.ps.IsColorComponentOutputEnabled(render_target, 0) ||
1278 header.ps.IsColorComponentOutputEnabled(render_target, 1) ||
1279 header.ps.IsColorComponentOutputEnabled(render_target, 2) ||
1280 header.ps.IsColorComponentOutputEnabled(render_target, 3)) {
1281 shader.AddLine(fmt::format("if (!AlphaFunc({})) discard;",
1282 regs.GetRegisterAsFloat(current_reg)));
1283 current_reg += 4;
1284 }
1285 }
1286 --shader.scope;
1287 shader.AddLine('}');
1288
1188 // Write the color outputs using the data in the shader registers, disabled 1289 // Write the color outputs using the data in the shader registers, disabled
1189 // rendertargets/components are skipped in the register assignment. 1290 // rendertargets/components are skipped in the register assignment.
1190 u32 current_reg = 0; 1291 current_reg = 0;
1191 for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets; 1292 for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets;
1192 ++render_target) { 1293 ++render_target) {
1193 // TODO(Subv): Figure out how dual-source blending is configured in the Switch. 1294 // TODO(Subv): Figure out how dual-source blending is configured in the Switch.
@@ -1211,6 +1312,63 @@ private:
1211 } 1312 }
1212 } 1313 }
1213 1314
1315 /// Unpacks a video instruction operand (e.g. VMAD).
1316 std::string GetVideoOperand(const std::string& op, bool is_chunk, bool is_signed,
1317 Tegra::Shader::VideoType type, u64 byte_height) {
1318 const std::string value = [&]() {
1319 if (!is_chunk) {
1320 const auto offset = static_cast<u32>(byte_height * 8);
1321 return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)";
1322 }
1323 const std::string zero = "0";
1324
1325 switch (type) {
1326 case Tegra::Shader::VideoType::Size16_Low:
1327 return '(' + op + " & 0xffff)";
1328 case Tegra::Shader::VideoType::Size16_High:
1329 return '(' + op + " >> 16)";
1330 case Tegra::Shader::VideoType::Size32:
1331 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
1332 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
1333 // explanation is found: assert.
1334 UNIMPLEMENTED();
1335 return zero;
1336 case Tegra::Shader::VideoType::Invalid:
1337 UNREACHABLE_MSG("Invalid instruction encoding");
1338 return zero;
1339 default:
1340 UNREACHABLE();
1341 return zero;
1342 }
1343 }();
1344
1345 if (is_signed) {
1346 return "int(" + value + ')';
1347 }
1348 return value;
1349 };
1350
1351 /// Gets the A operand for a video instruction.
1352 std::string GetVideoOperandA(Instruction instr) {
1353 return GetVideoOperand(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
1354 instr.video.is_byte_chunk_a != 0, instr.video.signed_a,
1355 instr.video.type_a, instr.video.byte_height_a);
1356 }
1357
1358 /// Gets the B operand for a video instruction.
1359 std::string GetVideoOperandB(Instruction instr) {
1360 if (instr.video.use_register_b) {
1361 return GetVideoOperand(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
1362 instr.video.is_byte_chunk_b != 0, instr.video.signed_b,
1363 instr.video.type_b, instr.video.byte_height_b);
1364 } else {
1365 return '(' +
1366 std::to_string(instr.video.signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
1367 : instr.alu.GetImm20_16()) +
1368 ')';
1369 }
1370 }
1371
1214 /** 1372 /**
1215 * Compiles a single instruction from Tegra to GLSL. 1373 * Compiles a single instruction from Tegra to GLSL.
1216 * @param offset the offset of the Tegra shader instruction. 1374 * @param offset the offset of the Tegra shader instruction.
@@ -1380,9 +1538,10 @@ private:
1380 break; 1538 break;
1381 } 1539 }
1382 case OpCode::Id::FMUL32_IMM: { 1540 case OpCode::Id::FMUL32_IMM: {
1383 regs.SetRegisterToFloat( 1541 regs.SetRegisterToFloat(instr.gpr0, 0,
1384 instr.gpr0, 0, 1542 regs.GetRegisterAsFloat(instr.gpr8) + " * " +
1385 regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1); 1543 GetImmediate32(instr),
1544 1, 1, instr.fmul32.saturate);
1386 break; 1545 break;
1387 } 1546 }
1388 case OpCode::Id::FADD32I: { 1547 case OpCode::Id::FADD32I: {
@@ -1747,6 +1906,86 @@ private:
1747 1906
1748 break; 1907 break;
1749 } 1908 }
1909 case OpCode::Type::ArithmeticHalf: {
1910 if (opcode->GetId() == OpCode::Id::HADD2_C || opcode->GetId() == OpCode::Id::HADD2_R) {
1911 ASSERT_MSG(instr.alu_half.ftz == 0, "Unimplemented");
1912 }
1913 const bool negate_a =
1914 opcode->GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0;
1915 const bool negate_b =
1916 opcode->GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0;
1917
1918 const std::string op_a =
1919 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.alu_half.type_a,
1920 instr.alu_half.abs_a != 0, negate_a);
1921
1922 std::string op_b;
1923 switch (opcode->GetId()) {
1924 case OpCode::Id::HADD2_C:
1925 case OpCode::Id::HMUL2_C:
1926 op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
1927 GLSLRegister::Type::UnsignedInteger);
1928 break;
1929 case OpCode::Id::HADD2_R:
1930 case OpCode::Id::HMUL2_R:
1931 op_b = regs.GetRegisterAsInteger(instr.gpr20, 0, false);
1932 break;
1933 default:
1934 UNREACHABLE();
1935 op_b = "0";
1936 break;
1937 }
1938 op_b = GetHalfFloat(op_b, instr.alu_half.type_b, instr.alu_half.abs_b != 0, negate_b);
1939
1940 const std::string result = [&]() {
1941 switch (opcode->GetId()) {
1942 case OpCode::Id::HADD2_C:
1943 case OpCode::Id::HADD2_R:
1944 return '(' + op_a + " + " + op_b + ')';
1945 case OpCode::Id::HMUL2_C:
1946 case OpCode::Id::HMUL2_R:
1947 return '(' + op_a + " * " + op_b + ')';
1948 default:
1949 LOG_CRITICAL(HW_GPU, "Unhandled half float instruction: {}", opcode->GetName());
1950 UNREACHABLE();
1951 return std::string("0");
1952 }
1953 }();
1954
1955 regs.SetRegisterToHalfFloat(instr.gpr0, 0, result, instr.alu_half.merge, 1, 1,
1956 instr.alu_half.saturate != 0);
1957 break;
1958 }
1959 case OpCode::Type::ArithmeticHalfImmediate: {
1960 if (opcode->GetId() == OpCode::Id::HADD2_IMM) {
1961 ASSERT_MSG(instr.alu_half_imm.ftz == 0, "Unimplemented");
1962 } else {
1963 ASSERT_MSG(instr.alu_half_imm.precision == Tegra::Shader::HalfPrecision::None,
1964 "Unimplemented");
1965 }
1966
1967 const std::string op_a = GetHalfFloat(
1968 regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.alu_half_imm.type_a,
1969 instr.alu_half_imm.abs_a != 0, instr.alu_half_imm.negate_a != 0);
1970
1971 const std::string op_b = UnpackHalfImmediate(instr, true);
1972
1973 const std::string result = [&]() {
1974 switch (opcode->GetId()) {
1975 case OpCode::Id::HADD2_IMM:
1976 return op_a + " + " + op_b;
1977 case OpCode::Id::HMUL2_IMM:
1978 return op_a + " * " + op_b;
1979 default:
1980 UNREACHABLE();
1981 return std::string("0");
1982 }
1983 }();
1984
1985 regs.SetRegisterToHalfFloat(instr.gpr0, 0, result, instr.alu_half_imm.merge, 1, 1,
1986 instr.alu_half_imm.saturate != 0);
1987 break;
1988 }
1750 case OpCode::Type::Ffma: { 1989 case OpCode::Type::Ffma: {
1751 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 1990 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
1752 std::string op_b = instr.ffma.negate_b ? "-" : ""; 1991 std::string op_b = instr.ffma.negate_b ? "-" : "";
@@ -1791,6 +2030,59 @@ private:
1791 instr.alu.saturate_d); 2030 instr.alu.saturate_d);
1792 break; 2031 break;
1793 } 2032 }
2033 case OpCode::Type::Hfma2: {
2034 if (opcode->GetId() == OpCode::Id::HFMA2_RR) {
2035 ASSERT_MSG(instr.hfma2.rr.precision == Tegra::Shader::HalfPrecision::None,
2036 "Unimplemented");
2037 } else {
2038 ASSERT_MSG(instr.hfma2.precision == Tegra::Shader::HalfPrecision::None,
2039 "Unimplemented");
2040 }
2041 const bool saturate = opcode->GetId() == OpCode::Id::HFMA2_RR
2042 ? instr.hfma2.rr.saturate != 0
2043 : instr.hfma2.saturate != 0;
2044
2045 const std::string op_a =
2046 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hfma2.type_a);
2047 std::string op_b, op_c;
2048
2049 switch (opcode->GetId()) {
2050 case OpCode::Id::HFMA2_CR:
2051 op_b = GetHalfFloat(regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
2052 GLSLRegister::Type::UnsignedInteger),
2053 instr.hfma2.type_b, false, instr.hfma2.negate_b);
2054 op_c = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false),
2055 instr.hfma2.type_reg39, false, instr.hfma2.negate_c);
2056 break;
2057 case OpCode::Id::HFMA2_RC:
2058 op_b = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false),
2059 instr.hfma2.type_reg39, false, instr.hfma2.negate_b);
2060 op_c = GetHalfFloat(regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
2061 GLSLRegister::Type::UnsignedInteger),
2062 instr.hfma2.type_b, false, instr.hfma2.negate_c);
2063 break;
2064 case OpCode::Id::HFMA2_RR:
2065 op_b = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
2066 instr.hfma2.type_b, false, instr.hfma2.negate_b);
2067 op_c = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false),
2068 instr.hfma2.rr.type_c, false, instr.hfma2.rr.negate_c);
2069 break;
2070 case OpCode::Id::HFMA2_IMM_R:
2071 op_b = UnpackHalfImmediate(instr, true);
2072 op_c = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false),
2073 instr.hfma2.type_reg39, false, instr.hfma2.negate_c);
2074 break;
2075 default:
2076 UNREACHABLE();
2077 op_c = op_b = "vec2(0)";
2078 break;
2079 }
2080
2081 const std::string result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
2082
2083 regs.SetRegisterToHalfFloat(instr.gpr0, 0, result, instr.hfma2.merge, 1, 1, saturate);
2084 break;
2085 }
1794 case OpCode::Type::Conversion: { 2086 case OpCode::Type::Conversion: {
1795 switch (opcode->GetId()) { 2087 switch (opcode->GetId()) {
1796 case OpCode::Id::I2I_R: { 2088 case OpCode::Id::I2I_R: {
@@ -2036,9 +2328,9 @@ private:
2036 break; 2328 break;
2037 } 2329 }
2038 case OpCode::Id::TEX: { 2330 case OpCode::Id::TEX: {
2039 ASSERT_MSG(instr.tex.array == 0, "TEX arrays unimplemented");
2040 Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; 2331 Tegra::Shader::TextureType texture_type{instr.tex.texture_type};
2041 std::string coord; 2332 std::string coord;
2333 const bool is_array = instr.tex.array != 0;
2042 2334
2043 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2335 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
2044 "NODEP is not implemented"); 2336 "NODEP is not implemented");
@@ -2053,21 +2345,59 @@ private:
2053 2345
2054 switch (num_coordinates) { 2346 switch (num_coordinates) {
2055 case 1: { 2347 case 1: {
2056 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2348 if (is_array) {
2057 coord = "float coords = " + x + ';'; 2349 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2350 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2351 coord = "vec2 coords = vec2(" + x + ", " + index + ");";
2352 } else {
2353 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2354 coord = "float coords = " + x + ';';
2355 }
2058 break; 2356 break;
2059 } 2357 }
2060 case 2: { 2358 case 2: {
2061 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2359 if (is_array) {
2062 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2360 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2063 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2361 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2362 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2363 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");";
2364 } else {
2365 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2366 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2367 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2368 }
2064 break; 2369 break;
2065 } 2370 }
2066 case 3: { 2371 case 3: {
2067 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2372 if (depth_compare) {
2068 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2373 if (is_array) {
2069 const std::string z = regs.GetRegisterAsFloat(instr.gpr20); 2374 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2070 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; 2375 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2376 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2377 const std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2378 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index +
2379 ");";
2380 } else {
2381 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2382 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2383 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2384 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2385 }
2386 } else {
2387 if (is_array) {
2388 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2389 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2390 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2391 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 3);
2392 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index +
2393 ");";
2394 } else {
2395 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2396 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2397 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2398 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2399 }
2400 }
2071 break; 2401 break;
2072 } 2402 }
2073 default: 2403 default:
@@ -2086,7 +2416,7 @@ private:
2086 std::string op_c; 2416 std::string op_c;
2087 2417
2088 const std::string sampler = 2418 const std::string sampler =
2089 GetSampler(instr.sampler, texture_type, false, depth_compare); 2419 GetSampler(instr.sampler, texture_type, is_array, depth_compare);
2090 // Add an extra scope and declare the texture coords inside to prevent 2420 // Add an extra scope and declare the texture coords inside to prevent
2091 // overwriting them in case they are used as outputs of the texs instruction. 2421 // overwriting them in case they are used as outputs of the texs instruction.
2092 2422
@@ -2106,10 +2436,13 @@ private:
2106 } 2436 }
2107 case Tegra::Shader::TextureProcessMode::LB: 2437 case Tegra::Shader::TextureProcessMode::LB:
2108 case Tegra::Shader::TextureProcessMode::LBA: { 2438 case Tegra::Shader::TextureProcessMode::LBA: {
2109 if (num_coordinates <= 2) { 2439 if (depth_compare) {
2110 op_c = regs.GetRegisterAsFloat(instr.gpr20); 2440 if (is_array)
2441 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 2);
2442 else
2443 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2111 } else { 2444 } else {
2112 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); 2445 op_c = regs.GetRegisterAsFloat(instr.gpr20);
2113 } 2446 }
2114 // TODO: Figure if A suffix changes the equation at all. 2447 // TODO: Figure if A suffix changes the equation at all.
2115 texture = "texture(" + sampler + ", coords, " + op_c + ')'; 2448 texture = "texture(" + sampler + ", coords, " + op_c + ')';
@@ -2252,6 +2585,8 @@ private:
2252 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), 2585 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ),
2253 "MZ is not implemented"); 2586 "MZ is not implemented");
2254 2587
2588 u32 op_c_offset = 0;
2589
2255 switch (texture_type) { 2590 switch (texture_type) {
2256 case Tegra::Shader::TextureType::Texture1D: { 2591 case Tegra::Shader::TextureType::Texture1D: {
2257 const std::string x = regs.GetRegisterAsInteger(instr.gpr8); 2592 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
@@ -2266,6 +2601,7 @@ private:
2266 const std::string x = regs.GetRegisterAsInteger(instr.gpr8); 2601 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
2267 const std::string y = regs.GetRegisterAsInteger(instr.gpr20); 2602 const std::string y = regs.GetRegisterAsInteger(instr.gpr20);
2268 coord = "ivec2 coords = ivec2(" + x + ", " + y + ");"; 2603 coord = "ivec2 coords = ivec2(" + x + ", " + y + ");";
2604 op_c_offset = 1;
2269 } 2605 }
2270 break; 2606 break;
2271 } 2607 }
@@ -2277,13 +2613,14 @@ private:
2277 const std::string sampler = 2613 const std::string sampler =
2278 GetSampler(instr.sampler, texture_type, is_array, false); 2614 GetSampler(instr.sampler, texture_type, is_array, false);
2279 std::string texture = "texelFetch(" + sampler + ", coords, 0)"; 2615 std::string texture = "texelFetch(" + sampler + ", coords, 0)";
2280 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr20.Value() + 1);
2281 switch (instr.tlds.GetTextureProcessMode()) { 2616 switch (instr.tlds.GetTextureProcessMode()) {
2282 case Tegra::Shader::TextureProcessMode::LZ: { 2617 case Tegra::Shader::TextureProcessMode::LZ: {
2283 texture = "texelFetch(" + sampler + ", coords, 0)"; 2618 texture = "texelFetch(" + sampler + ", coords, 0)";
2284 break; 2619 break;
2285 } 2620 }
2286 case Tegra::Shader::TextureProcessMode::LL: { 2621 case Tegra::Shader::TextureProcessMode::LL: {
2622 const std::string op_c =
2623 regs.GetRegisterAsInteger(instr.gpr20.Value() + op_c_offset);
2287 texture = "texelFetch(" + sampler + ", coords, " + op_c + ')'; 2624 texture = "texelFetch(" + sampler + ", coords, " + op_c + ')';
2288 break; 2625 break;
2289 } 2626 }
@@ -2479,20 +2816,13 @@ private:
2479 break; 2816 break;
2480 } 2817 }
2481 case OpCode::Type::FloatSetPredicate: { 2818 case OpCode::Type::FloatSetPredicate: {
2482 std::string op_a = instr.fsetp.neg_a ? "-" : ""; 2819 const std::string op_a =
2483 op_a += regs.GetRegisterAsFloat(instr.gpr8); 2820 GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8), instr.fsetp.abs_a != 0,
2484 2821 instr.fsetp.neg_a != 0);
2485 if (instr.fsetp.abs_a) {
2486 op_a = "abs(" + op_a + ')';
2487 }
2488 2822
2489 std::string op_b{}; 2823 std::string op_b;
2490 2824
2491 if (instr.is_b_imm) { 2825 if (instr.is_b_imm) {
2492 if (instr.fsetp.neg_b) {
2493 // Only the immediate version of fsetp has a neg_b bit.
2494 op_b += '-';
2495 }
2496 op_b += '(' + GetImmediate19(instr) + ')'; 2826 op_b += '(' + GetImmediate19(instr) + ')';
2497 } else { 2827 } else {
2498 if (instr.is_b_gpr) { 2828 if (instr.is_b_gpr) {
@@ -2565,6 +2895,51 @@ private:
2565 } 2895 }
2566 break; 2896 break;
2567 } 2897 }
2898 case OpCode::Type::HalfSetPredicate: {
2899 ASSERT_MSG(instr.hsetp2.ftz == 0, "Unimplemented");
2900
2901 const std::string op_a =
2902 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hsetp2.type_a,
2903 instr.hsetp2.abs_a, instr.hsetp2.negate_a);
2904
2905 const std::string op_b = [&]() {
2906 switch (opcode->GetId()) {
2907 case OpCode::Id::HSETP2_R:
2908 return GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
2909 instr.hsetp2.type_b, instr.hsetp2.abs_a,
2910 instr.hsetp2.negate_b);
2911 default:
2912 UNREACHABLE();
2913 return std::string("vec2(0)");
2914 }
2915 }();
2916
2917 // We can't use the constant predicate as destination.
2918 ASSERT(instr.hsetp2.pred3 != static_cast<u64>(Pred::UnusedIndex));
2919
2920 const std::string second_pred =
2921 GetPredicateCondition(instr.hsetp2.pred39, instr.hsetp2.neg_pred != 0);
2922
2923 const std::string combiner = GetPredicateCombiner(instr.hsetp2.op);
2924
2925 const std::string component_combiner = instr.hsetp2.h_and ? "&&" : "||";
2926 const std::string predicate =
2927 '(' + GetPredicateComparison(instr.hsetp2.cond, op_a + ".x", op_b + ".x") + ' ' +
2928 component_combiner + ' ' +
2929 GetPredicateComparison(instr.hsetp2.cond, op_a + ".y", op_b + ".y") + ')';
2930
2931 // Set the primary predicate to the result of Predicate OP SecondPredicate
2932 SetPredicate(instr.hsetp2.pred3,
2933 '(' + predicate + ") " + combiner + " (" + second_pred + ')');
2934
2935 if (instr.hsetp2.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
2936 // Set the secondary predicate to the result of !Predicate OP SecondPredicate,
2937 // if enabled
2938 SetPredicate(instr.hsetp2.pred0,
2939 "!(" + predicate + ") " + combiner + " (" + second_pred + ')');
2940 }
2941 break;
2942 }
2568 case OpCode::Type::PredicateSetRegister: { 2943 case OpCode::Type::PredicateSetRegister: {
2569 const std::string op_a = 2944 const std::string op_a =
2570 GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0); 2945 GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0);
@@ -2643,33 +3018,24 @@ private:
2643 break; 3018 break;
2644 } 3019 }
2645 case OpCode::Type::FloatSet: { 3020 case OpCode::Type::FloatSet: {
2646 std::string op_a = instr.fset.neg_a ? "-" : ""; 3021 const std::string op_a = GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8),
2647 op_a += regs.GetRegisterAsFloat(instr.gpr8); 3022 instr.fset.abs_a != 0, instr.fset.neg_a != 0);
2648 3023
2649 if (instr.fset.abs_a) { 3024 std::string op_b;
2650 op_a = "abs(" + op_a + ')';
2651 }
2652
2653 std::string op_b = instr.fset.neg_b ? "-" : "";
2654 3025
2655 if (instr.is_b_imm) { 3026 if (instr.is_b_imm) {
2656 const std::string imm = GetImmediate19(instr); 3027 const std::string imm = GetImmediate19(instr);
2657 if (instr.fset.neg_imm) 3028 op_b = imm;
2658 op_b += "(-" + imm + ')';
2659 else
2660 op_b += imm;
2661 } else { 3029 } else {
2662 if (instr.is_b_gpr) { 3030 if (instr.is_b_gpr) {
2663 op_b += regs.GetRegisterAsFloat(instr.gpr20); 3031 op_b = regs.GetRegisterAsFloat(instr.gpr20);
2664 } else { 3032 } else {
2665 op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, 3033 op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
2666 GLSLRegister::Type::Float); 3034 GLSLRegister::Type::Float);
2667 } 3035 }
2668 } 3036 }
2669 3037
2670 if (instr.fset.abs_b) { 3038 op_b = GetOperandAbsNeg(op_b, instr.fset.abs_b != 0, instr.fset.neg_b != 0);
2671 op_b = "abs(" + op_b + ')';
2672 }
2673 3039
2674 // The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the 3040 // The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the
2675 // condition is true, and to 0 otherwise. 3041 // condition is true, and to 0 otherwise.
@@ -2725,6 +3091,50 @@ private:
2725 } 3091 }
2726 break; 3092 break;
2727 } 3093 }
3094 case OpCode::Type::HalfSet: {
3095 ASSERT_MSG(instr.hset2.ftz == 0, "Unimplemented");
3096
3097 const std::string op_a =
3098 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hset2.type_a,
3099 instr.hset2.abs_a != 0, instr.hset2.negate_a != 0);
3100
3101 const std::string op_b = [&]() {
3102 switch (opcode->GetId()) {
3103 case OpCode::Id::HSET2_R:
3104 return GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
3105 instr.hset2.type_b, instr.hset2.abs_b != 0,
3106 instr.hset2.negate_b != 0);
3107 default:
3108 UNREACHABLE();
3109 return std::string("vec2(0)");
3110 }
3111 }();
3112
3113 const std::string second_pred =
3114 GetPredicateCondition(instr.hset2.pred39, instr.hset2.neg_pred != 0);
3115
3116 const std::string combiner = GetPredicateCombiner(instr.hset2.op);
3117
3118 // HSET2 operates on each half float in the pack.
3119 std::string result;
3120 for (int i = 0; i < 2; ++i) {
3121 const std::string float_value = i == 0 ? "0x00003c00" : "0x3c000000";
3122 const std::string integer_value = i == 0 ? "0x0000ffff" : "0xffff0000";
3123 const std::string value = instr.hset2.bf == 1 ? float_value : integer_value;
3124
3125 const std::string comp = std::string(".") + "xy"[i];
3126 const std::string predicate =
3127 "((" + GetPredicateComparison(instr.hset2.cond, op_a + comp, op_b + comp) +
3128 ") " + combiner + " (" + second_pred + "))";
3129
3130 result += '(' + predicate + " ? " + value + " : 0)";
3131 if (i == 0) {
3132 result += " | ";
3133 }
3134 }
3135 regs.SetRegisterToInteger(instr.gpr0, false, 0, '(' + result + ')', 1, 1);
3136 break;
3137 }
2728 case OpCode::Type::Xmad: { 3138 case OpCode::Type::Xmad: {
2729 ASSERT_MSG(!instr.xmad.sign_a, "Unimplemented"); 3139 ASSERT_MSG(!instr.xmad.sign_a, "Unimplemented");
2730 ASSERT_MSG(!instr.xmad.sign_b, "Unimplemented"); 3140 ASSERT_MSG(!instr.xmad.sign_b, "Unimplemented");
@@ -2933,16 +3343,32 @@ private:
2933 // The SSY opcode tells the GPU where to re-converge divergent execution paths, it 3343 // The SSY opcode tells the GPU where to re-converge divergent execution paths, it
2934 // sets the target of the jump that the SYNC instruction will make. The SSY opcode 3344 // sets the target of the jump that the SYNC instruction will make. The SSY opcode
2935 // has a similar structure to the BRA opcode. 3345 // has a similar structure to the BRA opcode.
2936 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer SSY is not supported"); 3346 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer flow is not supported");
3347
3348 const u32 target = offset + instr.bra.GetBranchTarget();
3349 EmitPushToFlowStack(target);
3350 break;
3351 }
3352 case OpCode::Id::PBK: {
3353 // PBK pushes to a stack the address where BRK will jump to. This shares stack with
3354 // SSY but using SYNC on a PBK address will kill the shader execution. We don't
3355 // emulate this because it's very unlikely a driver will emit such invalid shader.
3356 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer PBK is not supported");
2937 3357
2938 const u32 target = offset + instr.bra.GetBranchTarget(); 3358 const u32 target = offset + instr.bra.GetBranchTarget();
2939 EmitPushToSSYStack(target); 3359 EmitPushToFlowStack(target);
2940 break; 3360 break;
2941 } 3361 }
2942 case OpCode::Id::SYNC: { 3362 case OpCode::Id::SYNC: {
2943 // The SYNC opcode jumps to the address previously set by the SSY opcode 3363 // The SYNC opcode jumps to the address previously set by the SSY opcode
2944 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); 3364 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
2945 EmitPopFromSSYStack(); 3365 EmitPopFromFlowStack();
3366 break;
3367 }
3368 case OpCode::Id::BRK: {
3369 // The BRK opcode jumps to the address previously set by the PBK opcode
3370 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
3371 EmitPopFromFlowStack();
2946 break; 3372 break;
2947 } 3373 }
2948 case OpCode::Id::DEPBAR: { 3374 case OpCode::Id::DEPBAR: {
@@ -2952,87 +3378,51 @@ private:
2952 break; 3378 break;
2953 } 3379 }
2954 case OpCode::Id::VMAD: { 3380 case OpCode::Id::VMAD: {
2955 const bool signed_a = instr.vmad.signed_a == 1; 3381 const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1;
2956 const bool signed_b = instr.vmad.signed_b == 1; 3382 const std::string op_a = GetVideoOperandA(instr);
2957 const bool result_signed = signed_a || signed_b; 3383 const std::string op_b = GetVideoOperandB(instr);
2958 boost::optional<std::string> forced_result;
2959
2960 auto Unpack = [&](const std::string& op, bool is_chunk, bool is_signed,
2961 Tegra::Shader::VmadType type, u64 byte_height) {
2962 const std::string value = [&]() {
2963 if (!is_chunk) {
2964 const auto offset = static_cast<u32>(byte_height * 8);
2965 return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)";
2966 }
2967 const std::string zero = "0";
2968
2969 switch (type) {
2970 case Tegra::Shader::VmadType::Size16_Low:
2971 return '(' + op + " & 0xffff)";
2972 case Tegra::Shader::VmadType::Size16_High:
2973 return '(' + op + " >> 16)";
2974 case Tegra::Shader::VmadType::Size32:
2975 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
2976 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
2977 // explanation is found: assert.
2978 UNREACHABLE_MSG("Unimplemented");
2979 return zero;
2980 case Tegra::Shader::VmadType::Invalid:
2981 // Note(Rodrigo): This flag is invalid according to nvdisasm. From my
2982 // testing (even though it's invalid) this makes the whole instruction
2983 // assign zero to target register.
2984 forced_result = boost::make_optional(zero);
2985 return zero;
2986 default:
2987 UNREACHABLE();
2988 return zero;
2989 }
2990 }();
2991
2992 if (is_signed) {
2993 return "int(" + value + ')';
2994 }
2995 return value;
2996 };
2997
2998 const std::string op_a = Unpack(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
2999 instr.vmad.is_byte_chunk_a != 0, signed_a,
3000 instr.vmad.type_a, instr.vmad.byte_height_a);
3001
3002 std::string op_b;
3003 if (instr.vmad.use_register_b) {
3004 op_b = Unpack(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
3005 instr.vmad.is_byte_chunk_b != 0, signed_b, instr.vmad.type_b,
3006 instr.vmad.byte_height_b);
3007 } else {
3008 op_b = '(' +
3009 std::to_string(signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
3010 : instr.alu.GetImm20_16()) +
3011 ')';
3012 }
3013
3014 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed); 3384 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed);
3015 3385
3016 std::string result; 3386 std::string result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
3017 if (forced_result) {
3018 result = *forced_result;
3019 } else {
3020 result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
3021 3387
3022 switch (instr.vmad.shr) { 3388 switch (instr.vmad.shr) {
3023 case Tegra::Shader::VmadShr::Shr7: 3389 case Tegra::Shader::VmadShr::Shr7:
3024 result = '(' + result + " >> 7)"; 3390 result = '(' + result + " >> 7)";
3025 break; 3391 break;
3026 case Tegra::Shader::VmadShr::Shr15: 3392 case Tegra::Shader::VmadShr::Shr15:
3027 result = '(' + result + " >> 15)"; 3393 result = '(' + result + " >> 15)";
3028 break; 3394 break;
3029 }
3030 } 3395 }
3396
3031 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, 3397 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
3032 instr.vmad.saturate == 1, 0, Register::Size::Word, 3398 instr.vmad.saturate == 1, 0, Register::Size::Word,
3033 instr.vmad.cc); 3399 instr.vmad.cc);
3034 break; 3400 break;
3035 } 3401 }
3402 case OpCode::Id::VSETP: {
3403 const std::string op_a = GetVideoOperandA(instr);
3404 const std::string op_b = GetVideoOperandB(instr);
3405
3406 // We can't use the constant predicate as destination.
3407 ASSERT(instr.vsetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
3408
3409 const std::string second_pred = GetPredicateCondition(instr.vsetp.pred39, false);
3410
3411 const std::string combiner = GetPredicateCombiner(instr.vsetp.op);
3412
3413 const std::string predicate = GetPredicateComparison(instr.vsetp.cond, op_a, op_b);
3414 // Set the primary predicate to the result of Predicate OP SecondPredicate
3415 SetPredicate(instr.vsetp.pred3,
3416 '(' + predicate + ") " + combiner + " (" + second_pred + ')');
3417
3418 if (instr.vsetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
3419 // Set the secondary predicate to the result of !Predicate OP SecondPredicate,
3420 // if enabled
3421 SetPredicate(instr.vsetp.pred0,
3422 "!(" + predicate + ") " + combiner + " (" + second_pred + ')');
3423 }
3424 break;
3425 }
3036 default: { 3426 default: {
3037 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); 3427 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName());
3038 UNREACHABLE(); 3428 UNREACHABLE();
@@ -3096,11 +3486,11 @@ private:
3096 labels.insert(subroutine.begin); 3486 labels.insert(subroutine.begin);
3097 shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;"); 3487 shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;");
3098 3488
3099 // TODO(Subv): Figure out the actual depth of the SSY stack, for now it seems 3489 // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
3100 // unlikely that shaders will use 20 nested SSYs. 3490 // unlikely that shaders will use 20 nested SSYs and PBKs.
3101 constexpr u32 SSY_STACK_SIZE = 20; 3491 constexpr u32 FLOW_STACK_SIZE = 20;
3102 shader.AddLine("uint ssy_stack[" + std::to_string(SSY_STACK_SIZE) + "];"); 3492 shader.AddLine("uint flow_stack[" + std::to_string(FLOW_STACK_SIZE) + "];");
3103 shader.AddLine("uint ssy_stack_top = 0u;"); 3493 shader.AddLine("uint flow_stack_top = 0u;");
3104 3494
3105 shader.AddLine("while (true) {"); 3495 shader.AddLine("while (true) {");
3106 ++shader.scope; 3496 ++shader.scope;
@@ -3167,7 +3557,7 @@ private:
3167 3557
3168 // Declarations 3558 // Declarations
3169 std::set<std::string> declr_predicates; 3559 std::set<std::string> declr_predicates;
3170}; // namespace Decompiler 3560}; // namespace OpenGL::GLShader::Decompiler
3171 3561
3172std::string GetCommonDeclarations() { 3562std::string GetCommonDeclarations() {
3173 return fmt::format("#define MAX_CONSTBUFFER_ELEMENTS {}\n", 3563 return fmt::format("#define MAX_CONSTBUFFER_ELEMENTS {}\n",
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 1e5eb32df..e883ffb1d 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -23,10 +23,13 @@ out gl_PerVertex {
23 vec4 gl_Position; 23 vec4 gl_Position;
24}; 24};
25 25
26layout (location = 0) out vec4 position;
27
26layout(std140) uniform vs_config { 28layout(std140) uniform vs_config {
27 vec4 viewport_flip; 29 vec4 viewport_flip;
28 uvec4 instance_id; 30 uvec4 instance_id;
29 uvec4 flip_stage; 31 uvec4 flip_stage;
32 uvec4 alpha_test;
30}; 33};
31)"; 34)";
32 35
@@ -96,10 +99,14 @@ out gl_PerVertex {
96 vec4 gl_Position; 99 vec4 gl_Position;
97}; 100};
98 101
102layout (location = 0) in vec4 gs_position[];
103layout (location = 0) out vec4 position;
104
99layout (std140) uniform gs_config { 105layout (std140) uniform gs_config {
100 vec4 viewport_flip; 106 vec4 viewport_flip;
101 uvec4 instance_id; 107 uvec4 instance_id;
102 uvec4 flip_stage; 108 uvec4 flip_stage;
109 uvec4 alpha_test;
103}; 110};
104 111
105void main() { 112void main() {
@@ -131,12 +138,39 @@ layout(location = 5) out vec4 FragColor5;
131layout(location = 6) out vec4 FragColor6; 138layout(location = 6) out vec4 FragColor6;
132layout(location = 7) out vec4 FragColor7; 139layout(location = 7) out vec4 FragColor7;
133 140
141layout (location = 0) in vec4 position;
142
134layout (std140) uniform fs_config { 143layout (std140) uniform fs_config {
135 vec4 viewport_flip; 144 vec4 viewport_flip;
136 uvec4 instance_id; 145 uvec4 instance_id;
137 uvec4 flip_stage; 146 uvec4 flip_stage;
147 uvec4 alpha_test;
138}; 148};
139 149
150bool AlphaFunc(in float value) {
151 float ref = uintBitsToFloat(alpha_test[2]);
152 switch (alpha_test[1]) {
153 case 1:
154 return false;
155 case 2:
156 return value < ref;
157 case 3:
158 return value == ref;
159 case 4:
160 return value <= ref;
161 case 5:
162 return value > ref;
163 case 6:
164 return value != ref;
165 case 7:
166 return value >= ref;
167 case 8:
168 return true;
169 default:
170 return false;
171 }
172}
173
140void main() { 174void main() {
141 exec_fragment(); 175 exec_fragment();
142} 176}
@@ -145,4 +179,4 @@ void main() {
145 out += program.first; 179 out += program.first;
146 return {out, program.second}; 180 return {out, program.second};
147} 181}
148} // namespace OpenGL::GLShader \ No newline at end of file 182} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index 79596087a..520b9d4e3 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -16,6 +16,8 @@ namespace OpenGL::GLShader {
16constexpr std::size_t MAX_PROGRAM_CODE_LENGTH{0x1000}; 16constexpr std::size_t MAX_PROGRAM_CODE_LENGTH{0x1000};
17using ProgramCode = std::vector<u64>; 17using ProgramCode = std::vector<u64>;
18 18
19enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
20
19class ConstBufferEntry { 21class ConstBufferEntry {
20 using Maxwell = Tegra::Engines::Maxwell3D::Regs; 22 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
21 23
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 010857ec6..8b8869ecb 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -16,6 +16,17 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
16 viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; 16 viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
17 viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f; 17 viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f;
18 18
19 u32 func = static_cast<u32>(regs.alpha_test_func);
20 // Normalize the gl variants of opCompare to be the same as the normal variants
21 u32 op_gl_variant_base = static_cast<u32>(Tegra::Engines::Maxwell3D::Regs::ComparisonOp::Never);
22 if (func >= op_gl_variant_base) {
23 func = func - op_gl_variant_base + 1U;
24 }
25
26 alpha_test.enabled = regs.alpha_test_enabled;
27 alpha_test.func = func;
28 alpha_test.ref = regs.alpha_test_ref;
29
19 // We only assign the instance to the first component of the vector, the rest is just padding. 30 // We only assign the instance to the first component of the vector, the rest is just padding.
20 instance_id[0] = state.current_instance; 31 instance_id[0] = state.current_instance;
21 32
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index b3a191cf2..36fe1f04c 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -22,8 +22,14 @@ struct MaxwellUniformData {
22 alignas(16) GLvec4 viewport_flip; 22 alignas(16) GLvec4 viewport_flip;
23 alignas(16) GLuvec4 instance_id; 23 alignas(16) GLuvec4 instance_id;
24 alignas(16) GLuvec4 flip_stage; 24 alignas(16) GLuvec4 flip_stage;
25 struct alignas(16) {
26 GLuint enabled;
27 GLuint func;
28 GLfloat ref;
29 GLuint padding;
30 } alpha_test;
25}; 31};
26static_assert(sizeof(MaxwellUniformData) == 48, "MaxwellUniformData structure size is incorrect"); 32static_assert(sizeof(MaxwellUniformData) == 64, "MaxwellUniformData structure size is incorrect");
27static_assert(sizeof(MaxwellUniformData) < 16384, 33static_assert(sizeof(MaxwellUniformData) < 16384,
28 "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec"); 34 "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
29 35
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 3c3bcaae4..0f6dcab2b 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -82,8 +82,20 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
82 return {}; 82 return {};
83 } 83 }
84 84
85 case Maxwell::VertexAttribute::Type::Float: 85 case Maxwell::VertexAttribute::Type::Float: {
86 return GL_FLOAT; 86 switch (attrib.size) {
87 case Maxwell::VertexAttribute::Size::Size_16:
88 case Maxwell::VertexAttribute::Size::Size_16_16:
89 case Maxwell::VertexAttribute::Size::Size_16_16_16:
90 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
91 return GL_HALF_FLOAT;
92 case Maxwell::VertexAttribute::Size::Size_32:
93 case Maxwell::VertexAttribute::Size::Size_32_32:
94 case Maxwell::VertexAttribute::Size::Size_32_32_32:
95 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
96 return GL_FLOAT;
97 }
98 }
87 } 99 }
88 100
89 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); 101 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString());
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 18ab723f7..f1b40e7f5 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -237,6 +237,46 @@ std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pix
237 return unswizzled_data; 237 return unswizzled_data;
238} 238}
239 239
240void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
241 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
242 u32 block_height) {
243 const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + 63) / 64};
244 for (u32 line = 0; line < subrect_height; ++line) {
245 const u32 gob_address_y =
246 (line / (8 * block_height)) * 512 * block_height * image_width_in_gobs +
247 (line % (8 * block_height) / 8) * 512;
248 const auto& table = legacy_swizzle_table[line % 8];
249 for (u32 x = 0; x < subrect_width; ++x) {
250 const u32 gob_address = gob_address_y + (x * bytes_per_pixel / 64) * 512 * block_height;
251 const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % 64];
252 const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
253 const VAddr dest_addr = swizzled_data + swizzled_offset;
254
255 Memory::CopyBlock(dest_addr, source_line, bytes_per_pixel);
256 }
257 }
258}
259
260void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
261 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
262 u32 block_height, u32 offset_x, u32 offset_y) {
263 for (u32 line = 0; line < subrect_height; ++line) {
264 const u32 y2 = line + offset_y;
265 const u32 gob_address_y =
266 (y2 / (8 * block_height)) * 512 * block_height + (y2 % (8 * block_height) / 8) * 512;
267 const auto& table = legacy_swizzle_table[y2 % 8];
268 for (u32 x = 0; x < subrect_width; ++x) {
269 const u32 x2 = (x + offset_x) * bytes_per_pixel;
270 const u32 gob_address = gob_address_y + (x2 / 64) * 512 * block_height;
271 const u32 swizzled_offset = gob_address + table[x2 % 64];
272 const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel;
273 const VAddr source_addr = swizzled_data + swizzled_offset;
274
275 Memory::CopyBlock(dest_line, source_addr, bytes_per_pixel);
276 }
277 }
278}
279
240std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, 280std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
241 u32 height) { 281 u32 height) {
242 std::vector<u8> rgba_data; 282 std::vector<u8> rgba_data;
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index aaf316947..4726f54a5 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -35,4 +35,13 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
35std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 35std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
36 u32 block_height, u32 block_depth); 36 u32 block_height, u32 block_depth);
37 37
38/// Copies an untiled subrectangle into a tiled surface.
39void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
40 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
41 u32 block_height);
42/// Copies a tiled subrectangle into a linear surface.
43void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
44 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
45 u32 block_height, u32 offset_x, u32 offset_y);
46
38} // namespace Tegra::Texture 47} // namespace Tegra::Texture
diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt
index 1c83e9c34..01f2d129d 100644
--- a/src/web_service/CMakeLists.txt
+++ b/src/web_service/CMakeLists.txt
@@ -10,7 +10,7 @@ add_library(web_service STATIC
10create_target_directory_groups(web_service) 10create_target_directory_groups(web_service)
11 11
12get_directory_property(OPENSSL_LIBS 12get_directory_property(OPENSSL_LIBS
13 DIRECTORY ${CMAKE_SOURCE_DIR}/externals/libressl 13 DIRECTORY ${PROJECT_SOURCE_DIR}/externals/libressl
14 DEFINITION OPENSSL_LIBS) 14 DEFINITION OPENSSL_LIBS)
15target_compile_definitions(web_service PUBLIC -DCPPHTTPLIB_OPENSSL_SUPPORT) 15target_compile_definitions(web_service PRIVATE -DCPPHTTPLIB_OPENSSL_SUPPORT)
16target_link_libraries(web_service PRIVATE common json-headers ${OPENSSL_LIBS} httplib lurlparser) 16target_link_libraries(web_service PRIVATE common json-headers ${OPENSSL_LIBS} httplib lurlparser)
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 04464ad5e..9379d9110 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -82,10 +82,10 @@ set(UIS
82) 82)
83 83
84file(GLOB COMPAT_LIST 84file(GLOB COMPAT_LIST
85 ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc 85 ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
86 ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) 86 ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
87file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*) 87file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*)
88file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*) 88file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*)
89 89
90qt5_wrap_ui(UI_HDRS ${UIS}) 90qt5_wrap_ui(UI_HDRS ${UIS})
91 91
@@ -121,7 +121,7 @@ target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets)
121target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) 121target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
122 122
123if (YUZU_ENABLE_COMPATIBILITY_REPORTING) 123if (YUZU_ENABLE_COMPATIBILITY_REPORTING)
124 add_definitions(-DYUZU_ENABLE_COMPATIBILITY_REPORTING) 124 target_compile_definitions(yuzu PRIVATE -DYUZU_ENABLE_COMPATIBILITY_REPORTING)
125endif() 125endif()
126 126
127if (USE_DISCORD_PRESENCE) 127if (USE_DISCORD_PRESENCE)
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 7fec15991..71c6ebb41 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -85,8 +85,8 @@ void Config::ReadValues() {
85 Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat(); 85 Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat();
86 Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool(); 86 Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool();
87 Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt(); 87 Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt();
88 Settings::values.use_accurate_framebuffers = 88 Settings::values.use_accurate_gpu_emulation =
89 qt_config->value("use_accurate_framebuffers", false).toBool(); 89 qt_config->value("use_accurate_gpu_emulation", false).toBool();
90 90
91 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat(); 91 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat();
92 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat(); 92 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat();
@@ -233,7 +233,7 @@ void Config::SaveValues() {
233 qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor); 233 qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor);
234 qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit); 234 qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit);
235 qt_config->setValue("frame_limit", Settings::values.frame_limit); 235 qt_config->setValue("frame_limit", Settings::values.frame_limit);
236 qt_config->setValue("use_accurate_framebuffers", Settings::values.use_accurate_framebuffers); 236 qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation);
237 237
238 // Cast to double because Qt's written float values are not human-readable 238 // Cast to double because Qt's written float values are not human-readable
239 qt_config->setValue("bg_red", (double)Settings::values.bg_red); 239 qt_config->setValue("bg_red", (double)Settings::values.bg_red);
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index cd1549462..8290b4384 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -75,7 +75,7 @@ void ConfigureGraphics::setConfiguration() {
75 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); 75 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
76 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); 76 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);
77 ui->frame_limit->setValue(Settings::values.frame_limit); 77 ui->frame_limit->setValue(Settings::values.frame_limit);
78 ui->use_accurate_framebuffers->setChecked(Settings::values.use_accurate_framebuffers); 78 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
79 bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, 79 bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
80 Settings::values.bg_blue); 80 Settings::values.bg_blue);
81 ui->bg_button->setStyleSheet( 81 ui->bg_button->setStyleSheet(
@@ -87,7 +87,7 @@ void ConfigureGraphics::applyConfiguration() {
87 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); 87 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
88 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); 88 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();
89 Settings::values.frame_limit = ui->frame_limit->value(); 89 Settings::values.frame_limit = ui->frame_limit->value();
90 Settings::values.use_accurate_framebuffers = ui->use_accurate_framebuffers->isChecked(); 90 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
91 Settings::values.bg_red = static_cast<float>(bg_color.redF()); 91 Settings::values.bg_red = static_cast<float>(bg_color.redF());
92 Settings::values.bg_green = static_cast<float>(bg_color.greenF()); 92 Settings::values.bg_green = static_cast<float>(bg_color.greenF());
93 Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); 93 Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 8fc00af1b..91fcad994 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -50,9 +50,9 @@
50 </layout> 50 </layout>
51 </item> 51 </item>
52 <item> 52 <item>
53 <widget class="QCheckBox" name="use_accurate_framebuffers"> 53 <widget class="QCheckBox" name="use_accurate_gpu_emulation">
54 <property name="text"> 54 <property name="text">
55 <string>Use accurate framebuffers (slow)</string> 55 <string>Use accurate GPU emulation (slow)</string>
56 </property> 56 </property>
57 </widget> 57 </widget>
58 </item> 58 </item>
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 7403e9ccd..0c831c9f4 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -9,8 +9,8 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
11#include "core/hle/kernel/handle_table.h" 11#include "core/hle/kernel/handle_table.h"
12#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/mutex.h" 12#include "core/hle/kernel/mutex.h"
13#include "core/hle/kernel/process.h"
14#include "core/hle/kernel/scheduler.h" 14#include "core/hle/kernel/scheduler.h"
15#include "core/hle/kernel/thread.h" 15#include "core/hle/kernel/thread.h"
16#include "core/hle/kernel/timer.h" 16#include "core/hle/kernel/timer.h"
@@ -83,7 +83,7 @@ QString WaitTreeText::GetText() const {
83} 83}
84 84
85WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address) : mutex_address(mutex_address) { 85WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address) : mutex_address(mutex_address) {
86 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); 86 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
87 87
88 mutex_value = Memory::Read32(mutex_address); 88 mutex_value = Memory::Read32(mutex_address);
89 owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask); 89 owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask);
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 9851f507d..dffd9c788 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -97,18 +97,24 @@
97 <addaction name="action_Show_Status_Bar"/> 97 <addaction name="action_Show_Status_Bar"/>
98 <addaction name="menu_View_Debugging"/> 98 <addaction name="menu_View_Debugging"/>
99 </widget> 99 </widget>
100 <widget class ="QMenu" name="menu_Tools">
101 <property name="title">
102 <string>Tools</string>
103 </property>
104 <addaction name="action_Rederive" />
105 </widget>
100 <widget class="QMenu" name="menu_Help"> 106 <widget class="QMenu" name="menu_Help">
101 <property name="title"> 107 <property name="title">
102 <string>&amp;Help</string> 108 <string>&amp;Help</string>
103 </property> 109 </property>
104 <addaction name="action_Report_Compatibility"/> 110 <addaction name="action_Report_Compatibility"/>
105 <addaction name="separator"/> 111 <addaction name="separator"/>
106 <addaction name="action_Rederive"/>
107 <addaction name="action_About"/> 112 <addaction name="action_About"/>
108 </widget> 113 </widget>
109 <addaction name="menu_File"/> 114 <addaction name="menu_File"/>
110 <addaction name="menu_Emulation"/> 115 <addaction name="menu_Emulation"/>
111 <addaction name="menu_View"/> 116 <addaction name="menu_View"/>
117 <addaction name="menu_Tools" />
112 <addaction name="menu_Help"/> 118 <addaction name="menu_Help"/>
113 </widget> 119 </widget>
114 <action name="action_Install_File_NAND"> 120 <action name="action_Install_File_NAND">
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 2470f4640..5e42e48b2 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -99,8 +99,8 @@ void Config::ReadValues() {
99 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); 99 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
100 Settings::values.frame_limit = 100 Settings::values.frame_limit =
101 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); 101 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
102 Settings::values.use_accurate_framebuffers = 102 Settings::values.use_accurate_gpu_emulation =
103 sdl2_config->GetBoolean("Renderer", "use_accurate_framebuffers", false); 103 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
104 104
105 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0); 105 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
106 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); 106 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 762396e3b..a97b75f7b 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -110,9 +110,9 @@ use_frame_limit =
110# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default) 110# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
111frame_limit = 111frame_limit =
112 112
113# Whether to use accurate framebuffers 113# Whether to use accurate GPU emulation
114# 0 (default): Off (fast), 1 : On (slow) 114# 0 (default): Off (fast), 1 : On (slow)
115use_accurate_framebuffers = 115use_accurate_gpu_emulation =
116 116
117# The clear color for the renderer. What shows up on the sides of the bottom screen. 117# The clear color for the renderer. What shows up on the sides of the bottom screen.
118# Must be in range of 0.0-1.0. Defaults to 1.0 for all. 118# Must be in range of 0.0-1.0. Defaults to 1.0 for all.