summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt44
-rw-r--r--git0
-rw-r--r--src/audio_core/time_stretch.cpp3
-rw-r--r--src/audio_core/time_stretch.h1
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/file_util.cpp13
-rw-r--r--src/common/logging/backend.cpp3
-rw-r--r--src/common/logging/log.h3
-rw-r--r--src/common/memory_util.cpp177
-rw-r--r--src/common/memory_util.h21
-rw-r--r--src/core/CMakeLists.txt6
-rw-r--r--src/core/crypto/key_manager.cpp1
-rw-r--r--src/core/crypto/partition_data_manager.cpp3
-rw-r--r--src/core/file_sys/ips_layer.cpp2
-rw-r--r--src/core/file_sys/patch_manager.cpp5
-rw-r--r--src/core/file_sys/registered_cache.cpp15
-rw-r--r--src/core/file_sys/registered_cache.h8
-rw-r--r--src/core/file_sys/vfs.cpp8
-rw-r--r--src/core/hle/ipc.h5
-rw-r--r--src/core/hle/kernel/errors.h21
-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.cpp6
-rw-r--r--src/core/hle/kernel/process.h14
-rw-r--r--src/core/hle/kernel/server_port.cpp4
-rw-r--r--src/core/hle/kernel/server_session.cpp3
-rw-r--r--src/core/hle/kernel/shared_memory.cpp7
-rw-r--r--src/core/hle/kernel/svc.cpp207
-rw-r--r--src/core/hle/kernel/thread.cpp2
-rw-r--r--src/core/hle/kernel/vm_manager.cpp40
-rw-r--r--src/core/hle/kernel/vm_manager.h11
-rw-r--r--src/core/hle/service/acc/acc.cpp76
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp204
-rw-r--r--src/core/hle/service/acc/profile_manager.h35
-rw-r--r--src/core/hle/service/am/am.cpp97
-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.cpp21
-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/filesystem/fsp_srv.cpp6
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h4
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h1
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h1
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h1
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h1
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp211
-rw-r--r--src/core/hle/service/hid/controllers/npad.h9
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h1
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h1
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h2
-rw-r--r--src/core/hle/service/hid/hid.cpp52
-rw-r--r--src/core/hle/service/lbl/lbl.cpp56
-rw-r--r--src/core/hle/service/ldr/ldr.cpp52
-rw-r--r--src/core/hle/service/nfc/nfc.cpp53
-rw-r--r--src/core/hle/service/nfp/nfp.cpp270
-rw-r--r--src/core/hle/service/nfp/nfp.h23
-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/hle/service/usb/usb.cpp43
-rw-r--r--src/core/loader/nro.cpp21
-rw-r--r--src/core/loader/nro.h3
-rw-r--r--src/core/perf_stats.cpp4
-rw-r--r--src/core/settings.h3
-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.cpp27
-rw-r--r--src/video_core/engines/maxwell_3d.h6
-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.h180
-rw-r--r--src/video_core/gpu.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp41
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h13
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp135
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h40
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp649
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp39
-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/gl_state.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_state.h8
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h16
-rw-r--r--src/video_core/textures/decoders.cpp47
-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/bootmanager.cpp6
-rw-r--r--src/yuzu/configuration/config.cpp24
-rw-r--r--src/yuzu/configuration/config.h14
-rw-r--r--src/yuzu/configuration/configure_general.cpp2
-rw-r--r--src/yuzu/configuration/configure_general.ui29
-rw-r--r--src/yuzu/configuration/configure_system.cpp274
-rw-r--r--src/yuzu/configuration/configure_system.h48
-rw-r--r--src/yuzu/configuration/configure_system.ui252
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints.cpp33
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints_p.h2
-rw-r--r--src/yuzu/debugger/wait_tree.cpp4
-rw-r--r--src/yuzu/game_list.cpp17
-rw-r--r--src/yuzu/game_list_worker.cpp13
-rw-r--r--src/yuzu/main.cpp189
-rw-r--r--src/yuzu/main.h7
-rw-r--r--src/yuzu/main.ui35
-rw-r--r--src/yuzu_cmd/config.cpp10
-rw-r--r--src/yuzu_cmd/default_ini.h4
131 files changed, 3574 insertions, 1161 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/git b/git
deleted file mode 100644
index e69de29bb..000000000
--- a/git
+++ /dev/null
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp
index d72d67994..cee8b12dd 100644
--- a/src/audio_core/time_stretch.cpp
+++ b/src/audio_core/time_stretch.cpp
@@ -10,8 +10,7 @@
10 10
11namespace AudioCore { 11namespace AudioCore {
12 12
13TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) 13TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} {
14 : m_sample_rate(sample_rate), m_channel_count(channel_count) {
15 m_sound_touch.setChannels(channel_count); 14 m_sound_touch.setChannels(channel_count);
16 m_sound_touch.setSampleRate(sample_rate); 15 m_sound_touch.setSampleRate(sample_rate);
17 m_sound_touch.setPitch(1.0); 16 m_sound_touch.setPitch(1.0);
diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h
index decd760f1..bb2270b96 100644
--- a/src/audio_core/time_stretch.h
+++ b/src/audio_core/time_stretch.h
@@ -27,7 +27,6 @@ public:
27 27
28private: 28private:
29 u32 m_sample_rate; 29 u32 m_sample_rate;
30 u32 m_channel_count;
31 soundtouch::SoundTouch m_sound_touch; 30 soundtouch::SoundTouch m_sound_touch;
32 double m_stretch_ratio = 1.0; 31 double m_stretch_ratio = 1.0;
33}; 32};
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/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/backend.cpp b/src/common/logging/backend.cpp
index 9f5918851..6d5218465 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -196,6 +196,7 @@ void FileBackend::Write(const Entry& entry) {
196 SUB(Service, NFP) \ 196 SUB(Service, NFP) \
197 SUB(Service, NIFM) \ 197 SUB(Service, NIFM) \
198 SUB(Service, NIM) \ 198 SUB(Service, NIM) \
199 SUB(Service, NPNS) \
199 SUB(Service, NS) \ 200 SUB(Service, NS) \
200 SUB(Service, NVDRV) \ 201 SUB(Service, NVDRV) \
201 SUB(Service, PCIE) \ 202 SUB(Service, PCIE) \
@@ -204,10 +205,12 @@ void FileBackend::Write(const Entry& entry) {
204 SUB(Service, PM) \ 205 SUB(Service, PM) \
205 SUB(Service, PREPO) \ 206 SUB(Service, PREPO) \
206 SUB(Service, PSC) \ 207 SUB(Service, PSC) \
208 SUB(Service, PSM) \
207 SUB(Service, SET) \ 209 SUB(Service, SET) \
208 SUB(Service, SM) \ 210 SUB(Service, SM) \
209 SUB(Service, SPL) \ 211 SUB(Service, SPL) \
210 SUB(Service, SSL) \ 212 SUB(Service, SSL) \
213 SUB(Service, TCAP) \
211 SUB(Service, Time) \ 214 SUB(Service, Time) \
212 SUB(Service, USB) \ 215 SUB(Service, USB) \
213 SUB(Service, VI) \ 216 SUB(Service, VI) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index abbd056ee..d4ec31ec3 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -83,6 +83,7 @@ enum class Class : ClassType {
83 Service_NFP, ///< The NFP service 83 Service_NFP, ///< The NFP service
84 Service_NIFM, ///< The NIFM (Network interface) service 84 Service_NIFM, ///< The NIFM (Network interface) service
85 Service_NIM, ///< The NIM service 85 Service_NIM, ///< The NIM service
86 Service_NPNS, ///< The NPNS service
86 Service_NS, ///< The NS services 87 Service_NS, ///< The NS services
87 Service_NVDRV, ///< The NVDRV (Nvidia driver) service 88 Service_NVDRV, ///< The NVDRV (Nvidia driver) service
88 Service_PCIE, ///< The PCIe service 89 Service_PCIE, ///< The PCIe service
@@ -91,10 +92,12 @@ enum class Class : ClassType {
91 Service_PM, ///< The PM service 92 Service_PM, ///< The PM service
92 Service_PREPO, ///< The PREPO (Play report) service 93 Service_PREPO, ///< The PREPO (Play report) service
93 Service_PSC, ///< The PSC service 94 Service_PSC, ///< The PSC service
95 Service_PSM, ///< The PSM service
94 Service_SET, ///< The SET (Settings) service 96 Service_SET, ///< The SET (Settings) service
95 Service_SM, ///< The SM (Service manager) service 97 Service_SM, ///< The SM (Service manager) service
96 Service_SPL, ///< The SPL service 98 Service_SPL, ///< The SPL service
97 Service_SSL, ///< The SSL service 99 Service_SSL, ///< The SSL service
100 Service_TCAP, ///< The TCAP service.
98 Service_Time, ///< The time service 101 Service_Time, ///< The time service
99 Service_USB, ///< The USB (Universal Serial Bus) service 102 Service_USB, ///< The USB (Universal Serial Bus) service
100 Service_VI, ///< The VI (Video interface) service 103 Service_VI, ///< The VI (Video interface) 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 4755ec822..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
@@ -280,6 +282,8 @@ add_library(core STATIC
280 hle/service/nifm/nifm.h 282 hle/service/nifm/nifm.h
281 hle/service/nim/nim.cpp 283 hle/service/nim/nim.cpp
282 hle/service/nim/nim.h 284 hle/service/nim/nim.h
285 hle/service/npns/npns.cpp
286 hle/service/npns/npns.h
283 hle/service/ns/ns.cpp 287 hle/service/ns/ns.cpp
284 hle/service/ns/ns.h 288 hle/service/ns/ns.h
285 hle/service/ns/pl_u.cpp 289 hle/service/ns/pl_u.cpp
@@ -327,6 +331,8 @@ add_library(core STATIC
327 hle/service/prepo/prepo.h 331 hle/service/prepo/prepo.h
328 hle/service/psc/psc.cpp 332 hle/service/psc/psc.cpp
329 hle/service/psc/psc.h 333 hle/service/psc/psc.h
334 hle/service/ptm/psm.cpp
335 hle/service/ptm/psm.h
330 hle/service/service.cpp 336 hle/service/service.cpp
331 hle/service/service.h 337 hle/service/service.h
332 hle/service/set/set.cpp 338 hle/service/set/set.cpp
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index fd0786068..fefc3c747 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -713,7 +713,6 @@ void KeyManager::DeriveBase() {
713 713
714 const auto sbk = GetKey(S128KeyType::SecureBoot); 714 const auto sbk = GetKey(S128KeyType::SecureBoot);
715 const auto tsec = GetKey(S128KeyType::TSEC); 715 const auto tsec = GetKey(S128KeyType::TSEC);
716 const auto master_source = GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master));
717 716
718 for (size_t i = 0; i < revisions.size(); ++i) { 717 for (size_t i = 0; i < revisions.size(); ++i) {
719 if (!revisions[i]) 718 if (!revisions[i])
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/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index 554eae9bc..999939d5a 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -99,7 +99,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
99 u16 rle_size{}; 99 u16 rle_size{};
100 if (ips->ReadObject(&rle_size, offset) != sizeof(u16)) 100 if (ips->ReadObject(&rle_size, offset) != sizeof(u16))
101 return nullptr; 101 return nullptr;
102 rle_size = Common::swap16(data_size); 102 rle_size = Common::swap16(rle_size);
103 offset += sizeof(u16); 103 offset += sizeof(u16);
104 104
105 const auto data = ips->ReadByte(offset++); 105 const auto data = ips->ReadByte(offset++);
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 0117cb0bf..1f4928562 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -168,7 +168,8 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
168 168
169static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { 169static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
170 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 170 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
171 if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) { 171 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
172 load_dir == nullptr || load_dir->GetSize() <= 0) {
172 return; 173 return;
173 } 174 }
174 175
@@ -218,7 +219,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
218 title_id, static_cast<u8>(type)) 219 title_id, static_cast<u8>(type))
219 .c_str(); 220 .c_str();
220 221
221 if (type == ContentRecordType::Program) 222 if (type == ContentRecordType::Program || type == ContentRecordType::Data)
222 LOG_INFO(Loader, log_string); 223 LOG_INFO(Loader, log_string);
223 else 224 else
224 LOG_DEBUG(Loader, log_string); 225 LOG_DEBUG(Loader, log_string);
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 1febb398e..29b100414 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -2,6 +2,7 @@
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 <algorithm>
5#include <regex> 6#include <regex>
6#include <mbedtls/sha256.h> 7#include <mbedtls/sha256.h>
7#include "common/assert.h" 8#include "common/assert.h"
@@ -30,6 +31,14 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
30 return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type); 31 return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type);
31} 32}
32 33
34bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
35 return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type);
36}
37
38bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
39 return !operator==(lhs, rhs);
40}
41
33static bool FollowsTwoDigitDirFormat(std::string_view name) { 42static bool FollowsTwoDigitDirFormat(std::string_view name) {
34 static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript | 43 static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript |
35 std::regex_constants::icase); 44 std::regex_constants::icase);
@@ -593,6 +602,9 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
593 }, 602 },
594 [](const CNMT& c, const ContentRecord& r) { return true; }); 603 [](const CNMT& c, const ContentRecord& r) { return true; });
595 } 604 }
605
606 std::sort(out.begin(), out.end());
607 out.erase(std::unique(out.begin(), out.end()), out.end());
596 return out; 608 return out;
597} 609}
598 610
@@ -616,6 +628,9 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
616 return true; 628 return true;
617 }); 629 });
618 } 630 }
631
632 std::sort(out.begin(), out.end());
633 out.erase(std::unique(out.begin(), out.end()), out.end());
619 return out; 634 return out;
620} 635}
621} // namespace FileSys 636} // namespace FileSys
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 5ddacba47..5beceffb3 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -50,6 +50,10 @@ constexpr u64 GetUpdateTitleID(u64 base_title_id) {
50// boost flat_map requires operator< for O(log(n)) lookups. 50// boost flat_map requires operator< for O(log(n)) lookups.
51bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); 51bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
52 52
53// std unique requires operator== to identify duplicates.
54bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
55bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
56
53/* 57/*
54 * A class that catalogues NCAs in the registered directory structure. 58 * A class that catalogues NCAs in the registered directory structure.
55 * Nintendo's registered format follows this structure: 59 * Nintendo's registered format follows this structure:
@@ -60,8 +64,8 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
60 * | 00 64 * | 00
61 * | 01 <- Actual content split along 4GB boundaries. (optional) 65 * | 01 <- Actual content split along 4GB boundaries. (optional)
62 * 66 *
63 * (This impl also supports substituting the nca dir for an nca file, as that's more convenient when 67 * (This impl also supports substituting the nca dir for an nca file, as that's more convenient
64 * 4GB splitting can be ignored.) 68 * when 4GB splitting can be ignored.)
65 */ 69 */
66class RegisteredCache { 70class RegisteredCache {
67 friend class RegisteredCacheUnion; 71 friend class RegisteredCacheUnion;
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index bfe50da73..3824c74e0 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -472,10 +472,14 @@ bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t blo
472 std::vector<u8> temp(std::min(block_size, src->GetSize())); 472 std::vector<u8> temp(std::min(block_size, src->GetSize()));
473 for (std::size_t i = 0; i < src->GetSize(); i += block_size) { 473 for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
474 const auto read = std::min(block_size, src->GetSize() - i); 474 const auto read = std::min(block_size, src->GetSize() - i);
475 const auto block = src->Read(temp.data(), read, i);
476 475
477 if (dest->Write(temp.data(), read, i) != read) 476 if (src->Read(temp.data(), read, i) != read) {
478 return false; 477 return false;
478 }
479
480 if (dest->Write(temp.data(), read, i) != read) {
481 return false;
482 }
479 } 483 }
480 484
481 return true; 485 return true;
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 419f45896..ed84197b3 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -14,11 +14,6 @@ namespace IPC {
14/// Size of the command buffer area, in 32-bit words. 14/// Size of the command buffer area, in 32-bit words.
15constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); 15constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
16 16
17// These errors are commonly returned by invalid IPC translations, so alias them here for
18// convenience.
19// TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
20constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS;
21
22enum class ControlCommand : u32 { 17enum class ControlCommand : u32 {
23 ConvertSessionToDomain = 0, 18 ConvertSessionToDomain = 0,
24 ConvertDomainToSession = 1, 19 ConvertDomainToSession = 1,
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index 885259618..ee698c8a7 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -10,11 +10,6 @@ namespace Kernel {
10 10
11namespace ErrCodes { 11namespace ErrCodes {
12enum { 12enum {
13 // TODO(Subv): Remove these 3DS OS error codes.
14 SessionClosedByRemote = 26,
15 NoPendingSessions = 35,
16 InvalidBufferDescriptor = 48,
17
18 // Confirmed Switch OS error codes 13 // Confirmed Switch OS error codes
19 MaxConnectionsReached = 7, 14 MaxConnectionsReached = 7,
20 InvalidSize = 101, 15 InvalidSize = 101,
@@ -26,6 +21,7 @@ enum {
26 InvalidThreadPriority = 112, 21 InvalidThreadPriority = 112,
27 InvalidProcessorId = 113, 22 InvalidProcessorId = 113,
28 InvalidHandle = 114, 23 InvalidHandle = 114,
24 InvalidPointer = 115,
29 InvalidCombination = 116, 25 InvalidCombination = 116,
30 Timeout = 117, 26 Timeout = 117,
31 SynchronizationCanceled = 118, 27 SynchronizationCanceled = 118,
@@ -33,6 +29,7 @@ enum {
33 InvalidEnumValue = 120, 29 InvalidEnumValue = 120,
34 NoSuchEntry = 121, 30 NoSuchEntry = 121,
35 AlreadyRegistered = 122, 31 AlreadyRegistered = 122,
32 SessionClosed = 123,
36 InvalidState = 125, 33 InvalidState = 125,
37 ResourceLimitExceeded = 132, 34 ResourceLimitExceeded = 132,
38}; 35};
@@ -41,18 +38,14 @@ enum {
41// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always 38// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always
42// double check that the code matches before re-using the constant. 39// double check that the code matches before re-using the constant.
43 40
44// TODO(bunnei): Replace -1 with correct errors for Switch OS
45constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); 41constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull);
46constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1); 42constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed);
47constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); 43constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge);
48constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel, 44constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel,
49 ErrCodes::MaxConnectionsReached); 45 ErrCodes::MaxConnectionsReached);
50constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue); 46constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
51constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1);
52constexpr ResultCode ERR_INVALID_COMBINATION(-1);
53constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel, 47constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel,
54 ErrCodes::InvalidCombination); 48 ErrCodes::InvalidCombination);
55constexpr ResultCode ERR_OUT_OF_MEMORY(-1);
56constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress); 49constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
57constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); 50constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
58constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, 51constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
@@ -65,14 +58,8 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::Alrea
65constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); 58constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
66constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel, 59constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
67 ErrCodes::InvalidThreadPriority); 60 ErrCodes::InvalidThreadPriority);
68constexpr ResultCode ERR_INVALID_POINTER(-1); 61constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer);
69constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1);
70constexpr ResultCode ERR_NOT_AUTHORIZED(-1);
71/// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths.
72constexpr ResultCode ERR_INVALID_HANDLE_OS(-1);
73constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry); 62constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
74constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); 63constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout);
75/// Returned when Accept() is called on a port with no sessions to be accepted.
76constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1);
77 64
78} // namespace Kernel 65} // namespace Kernel
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.cpp b/src/core/hle/kernel/process.cpp
index 073dd5a7d..420218d59 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -232,6 +232,12 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
232 MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); 232 MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
233 MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); 233 MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
234 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); 234 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
235
236 // Clear instruction cache in CPU JIT
237 Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
238 Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
239 Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
240 Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
235} 241}
236 242
237ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { 243ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
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_port.cpp b/src/core/hle/kernel/server_port.cpp
index 3792e3e18..d6ceeb2da 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -18,7 +18,7 @@ ServerPort::~ServerPort() = default;
18 18
19ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { 19ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
20 if (pending_sessions.empty()) { 20 if (pending_sessions.empty()) {
21 return ERR_NO_PENDING_SESSIONS; 21 return ERR_NOT_FOUND;
22 } 22 }
23 23
24 auto session = std::move(pending_sessions.back()); 24 auto session = std::move(pending_sessions.back());
@@ -28,7 +28,7 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
28 28
29bool ServerPort::ShouldWait(Thread* thread) const { 29bool ServerPort::ShouldWait(Thread* thread) const {
30 // If there are no pending sessions, we wait until a new one is added. 30 // If there are no pending sessions, we wait until a new one is added.
31 return pending_sessions.size() == 0; 31 return pending_sessions.empty();
32} 32}
33 33
34void ServerPort::Acquire(Thread* thread) { 34void ServerPort::Acquire(Thread* thread) {
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/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index d061e6155..a016a86b6 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -80,20 +80,19 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
80 80
81ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, 81ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions,
82 MemoryPermission other_permissions) { 82 MemoryPermission other_permissions) {
83 83 const MemoryPermission own_other_permissions =
84 MemoryPermission own_other_permissions =
85 target_process == owner_process ? this->permissions : this->other_permissions; 84 target_process == owner_process ? this->permissions : this->other_permissions;
86 85
87 // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare 86 // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
88 if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { 87 if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
89 return ERR_INVALID_COMBINATION; 88 return ERR_INVALID_MEMORY_PERMISSIONS;
90 } 89 }
91 90
92 // Error out if the requested permissions don't match what the creator process allows. 91 // Error out if the requested permissions don't match what the creator process allows.
93 if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { 92 if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
94 LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", 93 LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match",
95 GetObjectId(), address, name); 94 GetObjectId(), address, name);
96 return ERR_INVALID_COMBINATION; 95 return ERR_INVALID_MEMORY_PERMISSIONS;
97 } 96 }
98 97
99 // Error out if the provided permissions are not compatible with what the creator process needs. 98 // Error out if the provided permissions are not compatible with what the creator process needs.
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index d3c9d50b5..a5302d924 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -189,14 +189,15 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
189 CASCADE_RESULT(client_session, client_port->Connect()); 189 CASCADE_RESULT(client_session, client_port->Connect());
190 190
191 // Return the client session 191 // Return the client session
192 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));
193 return RESULT_SUCCESS; 194 return RESULT_SUCCESS;
194} 195}
195 196
196/// Makes a blocking IPC call to an OS service. 197/// Makes a blocking IPC call to an OS service.
197static ResultCode SendSyncRequest(Handle handle) { 198static ResultCode SendSyncRequest(Handle handle) {
198 auto& kernel = Core::System::GetInstance().Kernel(); 199 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
199 SharedPtr<ClientSession> session = kernel.HandleTable().Get<ClientSession>(handle); 200 SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle);
200 if (!session) { 201 if (!session) {
201 LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); 202 LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle);
202 return ERR_INVALID_HANDLE; 203 return ERR_INVALID_HANDLE;
@@ -215,8 +216,8 @@ static ResultCode SendSyncRequest(Handle handle) {
215static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { 216static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
216 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); 217 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
217 218
218 auto& kernel = Core::System::GetInstance().Kernel(); 219 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
219 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 220 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
220 if (!thread) { 221 if (!thread) {
221 return ERR_INVALID_HANDLE; 222 return ERR_INVALID_HANDLE;
222 } 223 }
@@ -229,8 +230,8 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
229static ResultCode GetProcessId(u32* process_id, Handle process_handle) { 230static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
230 LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); 231 LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle);
231 232
232 auto& kernel = Core::System::GetInstance().Kernel(); 233 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
233 const SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle); 234 const SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
234 if (!process) { 235 if (!process) {
235 return ERR_INVALID_HANDLE; 236 return ERR_INVALID_HANDLE;
236 } 237 }
@@ -273,11 +274,11 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
273 274
274 using ObjectPtr = Thread::ThreadWaitObjects::value_type; 275 using ObjectPtr = Thread::ThreadWaitObjects::value_type;
275 Thread::ThreadWaitObjects objects(handle_count); 276 Thread::ThreadWaitObjects objects(handle_count);
276 auto& kernel = Core::System::GetInstance().Kernel(); 277 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
277 278
278 for (u64 i = 0; i < handle_count; ++i) { 279 for (u64 i = 0; i < handle_count; ++i) {
279 const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); 280 const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle));
280 const auto object = kernel.HandleTable().Get<WaitObject>(handle); 281 const auto object = handle_table.Get<WaitObject>(handle);
281 282
282 if (object == nullptr) { 283 if (object == nullptr) {
283 return ERR_INVALID_HANDLE; 284 return ERR_INVALID_HANDLE;
@@ -325,8 +326,8 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
325static ResultCode CancelSynchronization(Handle thread_handle) { 326static ResultCode CancelSynchronization(Handle thread_handle) {
326 LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); 327 LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
327 328
328 auto& kernel = Core::System::GetInstance().Kernel(); 329 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
329 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 330 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
330 if (!thread) { 331 if (!thread) {
331 return ERR_INVALID_HANDLE; 332 return ERR_INVALID_HANDLE;
332 } 333 }
@@ -354,7 +355,7 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
354 return ERR_INVALID_ADDRESS; 355 return ERR_INVALID_ADDRESS;
355 } 356 }
356 357
357 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); 358 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
358 return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, 359 return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle,
359 requesting_thread_handle); 360 requesting_thread_handle);
360} 361}
@@ -374,9 +375,19 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
374 return Mutex::Release(mutex_addr); 375 return Mutex::Release(mutex_addr);
375} 376}
376 377
378enum class BreakType : u32 {
379 Panic = 0,
380 AssertionFailed = 1,
381 PreNROLoad = 3,
382 PostNROLoad = 4,
383 PreNROUnload = 5,
384 PostNROUnload = 6,
385};
386
377struct BreakReason { 387struct BreakReason {
378 union { 388 union {
379 u32 raw; 389 u32 raw;
390 BitField<0, 30, BreakType> break_type;
380 BitField<31, 1, u32> signal_debugger; 391 BitField<31, 1, u32> signal_debugger;
381 }; 392 };
382}; 393};
@@ -384,12 +395,48 @@ struct BreakReason {
384/// Break program execution 395/// Break program execution
385static void Break(u32 reason, u64 info1, u64 info2) { 396static void Break(u32 reason, u64 info1, u64 info2) {
386 BreakReason break_reason{reason}; 397 BreakReason break_reason{reason};
387 if (break_reason.signal_debugger) { 398
388 LOG_ERROR( 399 switch (break_reason.break_type) {
400 case BreakType::Panic:
401 LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}",
402 info1, info2);
403 break;
404 case BreakType::AssertionFailed:
405 LOG_CRITICAL(Debug_Emulated,
406 "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
407 info1, info2);
408 break;
409 case BreakType::PreNROLoad:
410 LOG_WARNING(
389 Debug_Emulated, 411 Debug_Emulated,
390 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 412 "Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}",
391 reason, info1, info2); 413 info1, info2);
392 } else { 414 break;
415 case BreakType::PostNROLoad:
416 LOG_WARNING(Debug_Emulated,
417 "Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
418 info2);
419 break;
420 case BreakType::PreNROUnload:
421 LOG_WARNING(
422 Debug_Emulated,
423 "Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}",
424 info1, info2);
425 break;
426 case BreakType::PostNROUnload:
427 LOG_WARNING(Debug_Emulated,
428 "Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
429 info2);
430 break;
431 default:
432 LOG_WARNING(
433 Debug_Emulated,
434 "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}",
435 static_cast<u32>(break_reason.break_type.Value()), info1, info2);
436 break;
437 }
438
439 if (!break_reason.signal_debugger) {
393 LOG_CRITICAL( 440 LOG_CRITICAL(
394 Debug_Emulated, 441 Debug_Emulated,
395 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 442 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
@@ -499,13 +546,12 @@ static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
499static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { 546static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
500 LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); 547 LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle);
501 548
502 auto& kernel = Core::System::GetInstance().Kernel(); 549 const auto* current_process = Core::CurrentProcess();
503 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); 550 const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
504 if (!thread) { 551 if (!thread) {
505 return ERR_INVALID_HANDLE; 552 return ERR_INVALID_HANDLE;
506 } 553 }
507 554
508 const auto* current_process = Core::CurrentProcess();
509 if (thread->GetOwnerProcess() != current_process) { 555 if (thread->GetOwnerProcess() != current_process) {
510 return ERR_INVALID_HANDLE; 556 return ERR_INVALID_HANDLE;
511 } 557 }
@@ -531,10 +577,11 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
531 577
532/// Gets the priority for the specified thread 578/// Gets the priority for the specified thread
533static ResultCode GetThreadPriority(u32* priority, Handle handle) { 579static ResultCode GetThreadPriority(u32* priority, Handle handle) {
534 auto& kernel = Core::System::GetInstance().Kernel(); 580 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
535 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); 581 const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle);
536 if (!thread) 582 if (!thread) {
537 return ERR_INVALID_HANDLE; 583 return ERR_INVALID_HANDLE;
584 }
538 585
539 *priority = thread->GetPriority(); 586 *priority = thread->GetPriority();
540 return RESULT_SUCCESS; 587 return RESULT_SUCCESS;
@@ -546,16 +593,18 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
546 return ERR_INVALID_THREAD_PRIORITY; 593 return ERR_INVALID_THREAD_PRIORITY;
547 } 594 }
548 595
549 auto& kernel = Core::System::GetInstance().Kernel(); 596 const auto* const current_process = Core::CurrentProcess();
550 SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle);
551 if (!thread)
552 return ERR_INVALID_HANDLE;
553 597
554 // Note: The kernel uses the current process's resource limit instead of 598 // Note: The kernel uses the current process's resource limit instead of
555 // the one from the thread owner's resource limit. 599 // the one from the thread owner's resource limit.
556 const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); 600 const ResourceLimit& resource_limit = current_process->GetResourceLimit();
557 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { 601 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
558 return ERR_NOT_AUTHORIZED; 602 return ERR_INVALID_THREAD_PRIORITY;
603 }
604
605 SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
606 if (!thread) {
607 return ERR_INVALID_HANDLE;
559 } 608 }
560 609
561 thread->SetPriority(priority); 610 thread->SetPriority(priority);
@@ -584,6 +633,10 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
584 return ERR_INVALID_SIZE; 633 return ERR_INVALID_SIZE;
585 } 634 }
586 635
636 if (!IsValidAddressRange(addr, size)) {
637 return ERR_INVALID_ADDRESS_STATE;
638 }
639
587 const auto permissions_type = static_cast<MemoryPermission>(permissions); 640 const auto permissions_type = static_cast<MemoryPermission>(permissions);
588 if (permissions_type != MemoryPermission::Read && 641 if (permissions_type != MemoryPermission::Read &&
589 permissions_type != MemoryPermission::ReadWrite) { 642 permissions_type != MemoryPermission::ReadWrite) {
@@ -591,14 +644,18 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
591 return ERR_INVALID_MEMORY_PERMISSIONS; 644 return ERR_INVALID_MEMORY_PERMISSIONS;
592 } 645 }
593 646
594 auto& kernel = Core::System::GetInstance().Kernel(); 647 auto* const current_process = Core::CurrentProcess();
595 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); 648 auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
596 if (!shared_memory) { 649 if (!shared_memory) {
597 return ERR_INVALID_HANDLE; 650 return ERR_INVALID_HANDLE;
598 } 651 }
599 652
600 return shared_memory->Map(Core::CurrentProcess(), addr, permissions_type, 653 const auto& vm_manager = current_process->VMManager();
601 MemoryPermission::DontCare); 654 if (!vm_manager.IsWithinASLRRegion(addr, size)) {
655 return ERR_INVALID_MEMORY_RANGE;
656 }
657
658 return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare);
602} 659}
603 660
604static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { 661static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
@@ -613,24 +670,35 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
613 return ERR_INVALID_SIZE; 670 return ERR_INVALID_SIZE;
614 } 671 }
615 672
616 auto& kernel = Core::System::GetInstance().Kernel(); 673 if (!IsValidAddressRange(addr, size)) {
617 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); 674 return ERR_INVALID_ADDRESS_STATE;
675 }
676
677 auto* const current_process = Core::CurrentProcess();
678 auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
679 if (!shared_memory) {
680 return ERR_INVALID_HANDLE;
681 }
682
683 const auto& vm_manager = current_process->VMManager();
684 if (!vm_manager.IsWithinASLRRegion(addr, size)) {
685 return ERR_INVALID_MEMORY_RANGE;
686 }
618 687
619 return shared_memory->Unmap(Core::CurrentProcess(), addr); 688 return shared_memory->Unmap(current_process, addr);
620} 689}
621 690
622/// Query process memory 691/// Query process memory
623static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, 692static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/,
624 Handle process_handle, u64 addr) { 693 Handle process_handle, u64 addr) {
625 694 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
626 auto& kernel = Core::System::GetInstance().Kernel(); 695 SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
627 SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle);
628 if (!process) { 696 if (!process) {
629 return ERR_INVALID_HANDLE; 697 return ERR_INVALID_HANDLE;
630 } 698 }
631 auto vma = process->VMManager().FindVMA(addr); 699 auto vma = process->VMManager().FindVMA(addr);
632 memory_info->attributes = 0; 700 memory_info->attributes = 0;
633 if (vma == Core::CurrentProcess()->VMManager().vma_map.end()) { 701 if (vma == process->VMManager().vma_map.end()) {
634 memory_info->base_address = 0; 702 memory_info->base_address = 0;
635 memory_info->permission = static_cast<u32>(VMAPermission::None); 703 memory_info->permission = static_cast<u32>(VMAPermission::None);
636 memory_info->size = 0; 704 memory_info->size = 0;
@@ -671,20 +739,19 @@ static void ExitProcess() {
671/// Creates a new thread 739/// Creates a new thread
672static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, 740static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top,
673 u32 priority, s32 processor_id) { 741 u32 priority, s32 processor_id) {
674 std::string name = fmt::format("thread-{:X}", entry_point);
675
676 if (priority > THREADPRIO_LOWEST) { 742 if (priority > THREADPRIO_LOWEST) {
677 return ERR_INVALID_THREAD_PRIORITY; 743 return ERR_INVALID_THREAD_PRIORITY;
678 } 744 }
679 745
680 const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); 746 auto* const current_process = Core::CurrentProcess();
747 const ResourceLimit& resource_limit = current_process->GetResourceLimit();
681 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { 748 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
682 return ERR_NOT_AUTHORIZED; 749 return ERR_INVALID_THREAD_PRIORITY;
683 } 750 }
684 751
685 if (processor_id == THREADPROCESSORID_DEFAULT) { 752 if (processor_id == THREADPROCESSORID_DEFAULT) {
686 // Set the target CPU to the one specified in the process' exheader. 753 // Set the target CPU to the one specified in the process' exheader.
687 processor_id = Core::CurrentProcess()->GetDefaultProcessorID(); 754 processor_id = current_process->GetDefaultProcessorID();
688 ASSERT(processor_id != THREADPROCESSORID_DEFAULT); 755 ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
689 } 756 }
690 757
@@ -699,11 +766,13 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
699 return ERR_INVALID_PROCESSOR_ID; 766 return ERR_INVALID_PROCESSOR_ID;
700 } 767 }
701 768
769 const std::string name = fmt::format("thread-{:X}", entry_point);
702 auto& kernel = Core::System::GetInstance().Kernel(); 770 auto& kernel = Core::System::GetInstance().Kernel();
703 CASCADE_RESULT(SharedPtr<Thread> thread, 771 CASCADE_RESULT(SharedPtr<Thread> thread,
704 Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, 772 Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top,
705 *Core::CurrentProcess())); 773 *current_process));
706 const auto new_guest_handle = kernel.HandleTable().Create(thread); 774
775 const auto new_guest_handle = current_process->GetHandleTable().Create(thread);
707 if (new_guest_handle.Failed()) { 776 if (new_guest_handle.Failed()) {
708 return new_guest_handle.Code(); 777 return new_guest_handle.Code();
709 } 778 }
@@ -724,8 +793,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
724static ResultCode StartThread(Handle thread_handle) { 793static ResultCode StartThread(Handle thread_handle) {
725 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); 794 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
726 795
727 auto& kernel = Core::System::GetInstance().Kernel(); 796 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
728 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 797 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
729 if (!thread) { 798 if (!thread) {
730 return ERR_INVALID_HANDLE; 799 return ERR_INVALID_HANDLE;
731 } 800 }
@@ -772,8 +841,8 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
772 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", 841 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
773 mutex_addr, condition_variable_addr, thread_handle, nano_seconds); 842 mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
774 843
775 auto& kernel = Core::System::GetInstance().Kernel(); 844 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
776 SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 845 SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
777 ASSERT(thread); 846 ASSERT(thread);
778 847
779 CASCADE_CODE(Mutex::Release(mutex_addr)); 848 CASCADE_CODE(Mutex::Release(mutex_addr));
@@ -884,9 +953,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
884 mutex_val | Mutex::MutexHasWaitersFlag)); 953 mutex_val | Mutex::MutexHasWaitersFlag));
885 954
886 // The mutex is already owned by some other thread, make this thread wait on it. 955 // The mutex is already owned by some other thread, make this thread wait on it.
887 auto& kernel = Core::System::GetInstance().Kernel(); 956 const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
888 Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); 957 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
889 auto owner = kernel.HandleTable().Get<Thread>(owner_handle); 958 auto owner = handle_table.Get<Thread>(owner_handle);
890 ASSERT(owner); 959 ASSERT(owner);
891 ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); 960 ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
892 thread->InvalidateWakeupCallback(); 961 thread->InvalidateWakeupCallback();
@@ -965,16 +1034,16 @@ static u64 GetSystemTick() {
965static ResultCode CloseHandle(Handle handle) { 1034static ResultCode CloseHandle(Handle handle) {
966 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); 1035 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
967 1036
968 auto& kernel = Core::System::GetInstance().Kernel(); 1037 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
969 return kernel.HandleTable().Close(handle); 1038 return handle_table.Close(handle);
970} 1039}
971 1040
972/// Reset an event 1041/// Reset an event
973static ResultCode ResetSignal(Handle handle) { 1042static ResultCode ResetSignal(Handle handle) {
974 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); 1043 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle);
975 1044
976 auto& kernel = Core::System::GetInstance().Kernel(); 1045 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
977 auto event = kernel.HandleTable().Get<Event>(handle); 1046 auto event = handle_table.Get<Event>(handle);
978 1047
979 ASSERT(event != nullptr); 1048 ASSERT(event != nullptr);
980 1049
@@ -993,8 +1062,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
993static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { 1062static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) {
994 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); 1063 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
995 1064
996 auto& kernel = Core::System::GetInstance().Kernel(); 1065 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
997 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 1066 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
998 if (!thread) { 1067 if (!thread) {
999 return ERR_INVALID_HANDLE; 1068 return ERR_INVALID_HANDLE;
1000 } 1069 }
@@ -1009,8 +1078,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
1009 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle, 1078 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle,
1010 mask, core); 1079 mask, core);
1011 1080
1012 auto& kernel = Core::System::GetInstance().Kernel(); 1081 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1013 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 1082 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1014 if (!thread) { 1083 if (!thread) {
1015 return ERR_INVALID_HANDLE; 1084 return ERR_INVALID_HANDLE;
1016 } 1085 }
@@ -1071,7 +1140,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
1071 } 1140 }
1072 1141
1073 auto& kernel = Core::System::GetInstance().Kernel(); 1142 auto& kernel = Core::System::GetInstance().Kernel();
1074 auto& handle_table = kernel.HandleTable(); 1143 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1075 auto shared_mem_handle = 1144 auto shared_mem_handle =
1076 SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, 1145 SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size,
1077 local_perms, remote_perms); 1146 local_perms, remote_perms);
@@ -1083,10 +1152,12 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
1083static ResultCode ClearEvent(Handle handle) { 1152static ResultCode ClearEvent(Handle handle) {
1084 LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); 1153 LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
1085 1154
1086 auto& kernel = Core::System::GetInstance().Kernel(); 1155 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1087 SharedPtr<Event> evt = kernel.HandleTable().Get<Event>(handle); 1156 SharedPtr<Event> evt = handle_table.Get<Event>(handle);
1088 if (evt == nullptr) 1157 if (evt == nullptr) {
1089 return ERR_INVALID_HANDLE; 1158 return ERR_INVALID_HANDLE;
1159 }
1160
1090 evt->Clear(); 1161 evt->Clear();
1091 return RESULT_SUCCESS; 1162 return RESULT_SUCCESS;
1092} 1163}
@@ -1099,8 +1170,8 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
1099 Status, 1170 Status,
1100 }; 1171 };
1101 1172
1102 const auto& kernel = Core::System::GetInstance().Kernel(); 1173 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1103 const auto process = kernel.HandleTable().Get<Process>(process_handle); 1174 const auto process = handle_table.Get<Process>(process_handle);
1104 if (!process) { 1175 if (!process) {
1105 return ERR_INVALID_HANDLE; 1176 return ERR_INVALID_HANDLE;
1106 } 1177 }
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 1e28ccbda..1a92c8f70 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -143,6 +143,26 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
143 return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); 143 return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
144} 144}
145 145
146ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
147 // Find the first Free VMA.
148 const VAddr base = GetASLRRegionBaseAddress();
149 const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) {
150 if (vma.second.type != VMAType::Free)
151 return false;
152
153 const VAddr vma_end = vma.second.base + vma.second.size;
154 return vma_end > base && vma_end >= base + size;
155 });
156
157 if (vma_handle == vma_map.end()) {
158 // TODO(Subv): Find the correct error code here.
159 return ResultCode(-1);
160 }
161
162 const VAddr target = std::max(base, vma_handle->second.base);
163 return MakeResult<VAddr>(target);
164}
165
146ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, 166ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
147 MemoryState state, 167 MemoryState state,
148 Memory::MemoryHookPointer mmio_handler) { 168 Memory::MemoryHookPointer mmio_handler) {
@@ -507,6 +527,26 @@ u64 VMManager::GetASLRRegionSize() const {
507 return aslr_region_end - aslr_region_base; 527 return aslr_region_end - aslr_region_base;
508} 528}
509 529
530bool VMManager::IsWithinASLRRegion(VAddr begin, u64 size) const {
531 const VAddr range_end = begin + size;
532 const VAddr aslr_start = GetASLRRegionBaseAddress();
533 const VAddr aslr_end = GetASLRRegionEndAddress();
534
535 if (aslr_start > begin || begin > range_end || range_end - 1 > aslr_end - 1) {
536 return false;
537 }
538
539 if (range_end > heap_region_base && heap_region_end > begin) {
540 return false;
541 }
542
543 if (range_end > map_region_base && map_region_end > begin) {
544 return false;
545 }
546
547 return true;
548}
549
510VAddr VMManager::GetCodeRegionBaseAddress() const { 550VAddr VMManager::GetCodeRegionBaseAddress() const {
511 return code_region_base; 551 return code_region_base;
512} 552}
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 4accde6b3..2447cbb8f 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -158,6 +158,14 @@ public:
158 ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); 158 ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state);
159 159
160 /** 160 /**
161 * Finds the first free address that can hold a region of the desired size.
162 *
163 * @param size Size of the desired region.
164 * @return The found free address.
165 */
166 ResultVal<VAddr> FindFreeRegion(u64 size) const;
167
168 /**
161 * Maps a memory-mapped IO region at a given address. 169 * Maps a memory-mapped IO region at a given address.
162 * 170 *
163 * @param target The guest address to start the mapping at. 171 * @param target The guest address to start the mapping at.
@@ -211,6 +219,9 @@ public:
211 /// Gets the end address of the ASLR region. 219 /// Gets the end address of the ASLR region.
212 VAddr GetASLRRegionEndAddress() const; 220 VAddr GetASLRRegionEndAddress() const;
213 221
222 /// Determines whether or not the specified address range is within the ASLR region.
223 bool IsWithinASLRRegion(VAddr address, u64 size) const;
224
214 /// Gets the size of the ASLR region 225 /// Gets the size of the ASLR region
215 u64 GetASLRRegionSize() const; 226 u64 GetASLRRegionSize() const;
216 227
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index e61748ca3..c6437a671 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -2,9 +2,13 @@
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 <algorithm>
5#include <array> 6#include <array>
7#include "common/common_paths.h"
6#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/file_util.h"
7#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/string_util.h"
8#include "common/swap.h" 12#include "common/swap.h"
9#include "core/core_timing.h" 13#include "core/core_timing.h"
10#include "core/hle/ipc_helpers.h" 14#include "core/hle/ipc_helpers.h"
@@ -16,6 +20,7 @@
16#include "core/hle/service/acc/profile_manager.h" 20#include "core/hle/service/acc/profile_manager.h"
17 21
18namespace Service::Account { 22namespace Service::Account {
23
19// TODO: RE this structure 24// TODO: RE this structure
20struct UserData { 25struct UserData {
21 INSERT_PADDING_WORDS(1); 26 INSERT_PADDING_WORDS(1);
@@ -27,6 +32,29 @@ struct UserData {
27}; 32};
28static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); 33static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
29 34
35// Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
36// used as a backup should the one on disk not exist
37constexpr u32 backup_jpeg_size = 107;
38constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{
39 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
40 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
41 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
42 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
43 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
44 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
45 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
46}};
47
48static std::string GetImagePath(UUID uuid) {
49 return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
50 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
51}
52
53static constexpr u32 SanitizeJPEGSize(std::size_t size) {
54 constexpr std::size_t max_jpeg_image_size = 0x20000;
55 return static_cast<u32>(std::min(size, max_jpeg_image_size));
56}
57
30class IProfile final : public ServiceFramework<IProfile> { 58class IProfile final : public ServiceFramework<IProfile> {
31public: 59public:
32 explicit IProfile(UUID user_id, ProfileManager& profile_manager) 60 explicit IProfile(UUID user_id, ProfileManager& profile_manager)
@@ -73,32 +101,42 @@ private:
73 } 101 }
74 102
75 void LoadImage(Kernel::HLERequestContext& ctx) { 103 void LoadImage(Kernel::HLERequestContext& ctx) {
76 LOG_WARNING(Service_ACC, "(STUBBED) called"); 104 LOG_DEBUG(Service_ACC, "called");
77 // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg 105
78 // TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000
79 constexpr u32 jpeg_size = 107;
80 static constexpr std::array<u8, jpeg_size> jpeg{
81 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03,
82 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04,
83 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a,
84 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f,
85 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10,
86 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00,
87 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01,
88 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
89 };
90 ctx.WriteBuffer(jpeg);
91 IPC::ResponseBuilder rb{ctx, 3}; 106 IPC::ResponseBuilder rb{ctx, 3};
92 rb.Push(RESULT_SUCCESS); 107 rb.Push(RESULT_SUCCESS);
93 rb.Push<u32>(jpeg_size); 108
109 const FileUtil::IOFile image(GetImagePath(user_id), "rb");
110 if (!image.IsOpen()) {
111 LOG_WARNING(Service_ACC,
112 "Failed to load user provided image! Falling back to built-in backup...");
113 ctx.WriteBuffer(backup_jpeg);
114 rb.Push<u32>(backup_jpeg_size);
115 return;
116 }
117
118 const u32 size = SanitizeJPEGSize(image.GetSize());
119 std::vector<u8> buffer(size);
120 image.ReadBytes(buffer.data(), buffer.size());
121
122 ctx.WriteBuffer(buffer.data(), buffer.size());
123 rb.Push<u32>(size);
94 } 124 }
95 125
96 void GetImageSize(Kernel::HLERequestContext& ctx) { 126 void GetImageSize(Kernel::HLERequestContext& ctx) {
97 LOG_WARNING(Service_ACC, "(STUBBED) called"); 127 LOG_DEBUG(Service_ACC, "called");
98 constexpr u32 jpeg_size = 107;
99 IPC::ResponseBuilder rb{ctx, 3}; 128 IPC::ResponseBuilder rb{ctx, 3};
100 rb.Push(RESULT_SUCCESS); 129 rb.Push(RESULT_SUCCESS);
101 rb.Push<u32>(jpeg_size); 130
131 const FileUtil::IOFile image(GetImagePath(user_id), "rb");
132
133 if (!image.IsOpen()) {
134 LOG_WARNING(Service_ACC,
135 "Failed to load user provided image! Falling back to built-in backup...");
136 rb.Push<u32>(backup_jpeg_size);
137 } else {
138 rb.Push<u32>(SanitizeJPEGSize(image.GetSize()));
139 }
102 } 140 }
103 141
104 const ProfileManager& profile_manager; 142 const ProfileManager& profile_manager;
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index bcb3475db..3cac1b4ff 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -3,41 +3,66 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <random> 5#include <random>
6#include <boost/optional.hpp> 6
7#include "common/file_util.h"
7#include "core/hle/service/acc/profile_manager.h" 8#include "core/hle/service/acc/profile_manager.h"
8#include "core/settings.h" 9#include "core/settings.h"
9 10
10namespace Service::Account { 11namespace Service::Account {
12
13struct UserRaw {
14 UUID uuid;
15 UUID uuid2;
16 u64 timestamp;
17 ProfileUsername username;
18 INSERT_PADDING_BYTES(0x80);
19};
20static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
21
22struct ProfileDataRaw {
23 INSERT_PADDING_BYTES(0x10);
24 std::array<UserRaw, MAX_USERS> users;
25};
26static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size.");
27
11// TODO(ogniK): Get actual error codes 28// TODO(ogniK): Get actual error codes
12constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1); 29constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1);
13constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2); 30constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2);
14constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); 31constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
15 32
16const UUID& UUID::Generate() { 33constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/";
34
35UUID UUID::Generate() {
17 std::random_device device; 36 std::random_device device;
18 std::mt19937 gen(device()); 37 std::mt19937 gen(device());
19 std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); 38 std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
20 uuid[0] = distribution(gen); 39 return UUID{distribution(gen), distribution(gen)};
21 uuid[1] = distribution(gen);
22 return *this;
23} 40}
24 41
25ProfileManager::ProfileManager() { 42ProfileManager::ProfileManager() {
26 // TODO(ogniK): Create the default user we have for now until loading/saving users is added 43 ParseUserSaveFile();
27 auto user_uuid = UUID{1, 0}; 44
28 ASSERT(CreateNewUser(user_uuid, Settings::values.username).IsSuccess()); 45 if (user_count == 0)
29 OpenUser(user_uuid); 46 CreateNewUser(UUID::Generate(), "yuzu");
47
48 auto current = std::clamp<int>(Settings::values.current_user, 0, MAX_USERS - 1);
49 if (UserExistsIndex(current))
50 current = 0;
51
52 OpenUser(*GetUser(current));
30} 53}
31 54
32ProfileManager::~ProfileManager() = default; 55ProfileManager::~ProfileManager() {
56 WriteUserSaveFile();
57}
33 58
34/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the 59/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
35/// internal management of the users profiles 60/// internal management of the users profiles
36boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { 61std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) {
37 if (user_count >= MAX_USERS) { 62 if (user_count >= MAX_USERS) {
38 return boost::none; 63 return {};
39 } 64 }
40 profiles[user_count] = user; 65 profiles[user_count] = profile;
41 return user_count++; 66 return user_count++;
42} 67}
43 68
@@ -56,7 +81,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) {
56 81
57/// Helper function to register a user to the system 82/// Helper function to register a user to the system
58ResultCode ProfileManager::AddUser(const ProfileInfo& user) { 83ResultCode ProfileManager::AddUser(const ProfileInfo& user) {
59 if (AddToProfiles(user) == boost::none) { 84 if (!AddToProfiles(user)) {
60 return ERROR_TOO_MANY_USERS; 85 return ERROR_TOO_MANY_USERS;
61 } 86 }
62 return RESULT_SUCCESS; 87 return RESULT_SUCCESS;
@@ -101,31 +126,40 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username)
101 return CreateNewUser(uuid, username_output); 126 return CreateNewUser(uuid, username_output);
102} 127}
103 128
129std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
130 if (index >= MAX_USERS) {
131 return {};
132 }
133
134 return profiles[index].user_uuid;
135}
136
104/// Returns a users profile index based on their user id. 137/// Returns a users profile index based on their user id.
105boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { 138std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
106 if (!uuid) { 139 if (!uuid) {
107 return boost::none; 140 return {};
108 } 141 }
109 auto iter = std::find_if(profiles.begin(), profiles.end(), 142
110 [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); 143 const auto iter = std::find_if(profiles.begin(), profiles.end(),
144 [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
111 if (iter == profiles.end()) { 145 if (iter == profiles.end()) {
112 return boost::none; 146 return {};
113 } 147 }
148
114 return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); 149 return static_cast<std::size_t>(std::distance(profiles.begin(), iter));
115} 150}
116 151
117/// Returns a users profile index based on their profile 152/// Returns a users profile index based on their profile
118boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { 153std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const {
119 return GetUserIndex(user.user_uuid); 154 return GetUserIndex(user.user_uuid);
120} 155}
121 156
122/// Returns the data structure used by the switch when GetProfileBase is called on acc:* 157/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
123bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, 158bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const {
124 ProfileBase& profile) const { 159 if (!index || index >= MAX_USERS) {
125 if (index == boost::none || index >= MAX_USERS) {
126 return false; 160 return false;
127 } 161 }
128 const auto& prof_info = profiles[index.get()]; 162 const auto& prof_info = profiles[*index];
129 profile.user_uuid = prof_info.user_uuid; 163 profile.user_uuid = prof_info.user_uuid;
130 profile.username = prof_info.username; 164 profile.username = prof_info.username;
131 profile.timestamp = prof_info.creation_time; 165 profile.timestamp = prof_info.creation_time;
@@ -134,7 +168,7 @@ bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index,
134 168
135/// Returns the data structure used by the switch when GetProfileBase is called on acc:* 169/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
136bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const { 170bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const {
137 auto idx = GetUserIndex(uuid); 171 const auto idx = GetUserIndex(uuid);
138 return GetProfileBase(idx, profile); 172 return GetProfileBase(idx, profile);
139} 173}
140 174
@@ -161,26 +195,34 @@ std::size_t ProfileManager::GetOpenUserCount() const {
161 195
162/// Checks if a user id exists in our profile manager 196/// Checks if a user id exists in our profile manager
163bool ProfileManager::UserExists(UUID uuid) const { 197bool ProfileManager::UserExists(UUID uuid) const {
164 return (GetUserIndex(uuid) != boost::none); 198 return GetUserIndex(uuid) != std::nullopt;
199}
200
201bool ProfileManager::UserExistsIndex(std::size_t index) const {
202 if (index >= MAX_USERS)
203 return false;
204 return profiles[index].user_uuid.uuid != INVALID_UUID;
165} 205}
166 206
167/// Opens a specific user 207/// Opens a specific user
168void ProfileManager::OpenUser(UUID uuid) { 208void ProfileManager::OpenUser(UUID uuid) {
169 auto idx = GetUserIndex(uuid); 209 const auto idx = GetUserIndex(uuid);
170 if (idx == boost::none) { 210 if (!idx) {
171 return; 211 return;
172 } 212 }
173 profiles[idx.get()].is_open = true; 213
214 profiles[*idx].is_open = true;
174 last_opened_user = uuid; 215 last_opened_user = uuid;
175} 216}
176 217
177/// Closes a specific user 218/// Closes a specific user
178void ProfileManager::CloseUser(UUID uuid) { 219void ProfileManager::CloseUser(UUID uuid) {
179 auto idx = GetUserIndex(uuid); 220 const auto idx = GetUserIndex(uuid);
180 if (idx == boost::none) { 221 if (!idx) {
181 return; 222 return;
182 } 223 }
183 profiles[idx.get()].is_open = false; 224
225 profiles[*idx].is_open = false;
184} 226}
185 227
186/// Gets all valid user ids on the system 228/// Gets all valid user ids on the system
@@ -210,10 +252,10 @@ UUID ProfileManager::GetLastOpenedUser() const {
210} 252}
211 253
212/// Return the users profile base and the unknown arbitary data. 254/// Return the users profile base and the unknown arbitary data.
213bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, 255bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
214 ProfileData& data) const { 256 ProfileData& data) const {
215 if (GetProfileBase(index, profile)) { 257 if (GetProfileBase(index, profile)) {
216 data = profiles[index.get()].data; 258 data = profiles[*index].data;
217 return true; 259 return true;
218 } 260 }
219 return false; 261 return false;
@@ -222,7 +264,7 @@ bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, P
222/// Return the users profile base and the unknown arbitary data. 264/// Return the users profile base and the unknown arbitary data.
223bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, 265bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
224 ProfileData& data) const { 266 ProfileData& data) const {
225 auto idx = GetUserIndex(uuid); 267 const auto idx = GetUserIndex(uuid);
226 return GetProfileBaseAndData(idx, profile, data); 268 return GetProfileBaseAndData(idx, profile, data);
227} 269}
228 270
@@ -239,4 +281,96 @@ bool ProfileManager::CanSystemRegisterUser() const {
239 // emulate qlaunch. Update this to dynamically change. 281 // emulate qlaunch. Update this to dynamically change.
240} 282}
241 283
284bool ProfileManager::RemoveUser(UUID uuid) {
285 const auto index = GetUserIndex(uuid);
286 if (!index) {
287 return false;
288 }
289
290 profiles[*index] = ProfileInfo{};
291 std::stable_partition(profiles.begin(), profiles.end(),
292 [](const ProfileInfo& profile) { return profile.user_uuid; });
293 return true;
294}
295
296bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
297 const auto index = GetUserIndex(uuid);
298 if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) {
299 return false;
300 }
301
302 auto& profile = profiles[*index];
303 profile.user_uuid = profile_new.user_uuid;
304 profile.username = profile_new.username;
305 profile.creation_time = profile_new.timestamp;
306
307 return true;
308}
309
310void ProfileManager::ParseUserSaveFile() {
311 FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
312 ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat",
313 "rb");
314
315 if (!save.IsOpen()) {
316 LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
317 "user 'yuzu' with random UUID.");
318 return;
319 }
320
321 ProfileDataRaw data;
322 if (save.ReadBytes(&data, sizeof(ProfileDataRaw)) != sizeof(ProfileDataRaw)) {
323 LOG_WARNING(Service_ACC, "profiles.dat is smaller than expected... Generating new user "
324 "'yuzu' with random UUID.");
325 return;
326 }
327
328 for (std::size_t i = 0; i < MAX_USERS; ++i) {
329 const auto& user = data.users[i];
330
331 if (user.uuid != UUID(INVALID_UUID))
332 AddUser({user.uuid, user.username, user.timestamp, {}, false});
333 }
334
335 std::stable_partition(profiles.begin(), profiles.end(),
336 [](const ProfileInfo& profile) { return profile.user_uuid; });
337}
338
339void ProfileManager::WriteUserSaveFile() {
340 ProfileDataRaw raw{};
341
342 for (std::size_t i = 0; i < MAX_USERS; ++i) {
343 raw.users[i].username = profiles[i].username;
344 raw.users[i].uuid2 = profiles[i].user_uuid;
345 raw.users[i].uuid = profiles[i].user_uuid;
346 raw.users[i].timestamp = profiles[i].creation_time;
347 }
348
349 const auto raw_path =
350 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010";
351 if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
352 FileUtil::Delete(raw_path);
353
354 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
355 ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
356
357 if (!FileUtil::CreateFullPath(path)) {
358 LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory "
359 "nand/system/save/8000000000000010/su/avators to mitigate this "
360 "issue.");
361 return;
362 }
363
364 FileUtil::IOFile save(path, "wb");
365
366 if (!save.IsOpen()) {
367 LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
368 "made in current session will be saved.");
369 return;
370 }
371
372 save.Resize(sizeof(ProfileDataRaw));
373 save.WriteBytes(&raw, sizeof(ProfileDataRaw));
374}
375
242}; // namespace Service::Account 376}; // namespace Service::Account
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index bffd4cf4d..1cd2e51b2 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -5,8 +5,8 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <optional>
8 9
9#include "boost/optional.hpp"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/hle/result.h" 12#include "core/hle/result.h"
@@ -36,7 +36,7 @@ struct UUID {
36 } 36 }
37 37
38 // TODO(ogniK): Properly generate uuids based on RFC-4122 38 // TODO(ogniK): Properly generate uuids based on RFC-4122
39 const UUID& Generate(); 39 static UUID Generate();
40 40
41 // Set the UUID to {0,0} to be considered an invalid user 41 // Set the UUID to {0,0} to be considered an invalid user
42 void Invalidate() { 42 void Invalidate() {
@@ -45,6 +45,15 @@ struct UUID {
45 std::string Format() const { 45 std::string Format() const {
46 return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); 46 return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
47 } 47 }
48
49 std::string FormatSwitch() const {
50 std::array<u8, 16> s{};
51 std::memcpy(s.data(), uuid.data(), sizeof(u128));
52 return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
53 ":02x}{:02x}{:02x}{:02x}{:02x}",
54 s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
55 s[12], s[13], s[14], s[15]);
56 }
48}; 57};
49static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); 58static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
50 59
@@ -81,18 +90,19 @@ static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size");
81/// objects 90/// objects
82class ProfileManager { 91class ProfileManager {
83public: 92public:
84 ProfileManager(); // TODO(ogniK): Load from system save 93 ProfileManager();
85 ~ProfileManager(); 94 ~ProfileManager();
86 95
87 ResultCode AddUser(const ProfileInfo& user); 96 ResultCode AddUser(const ProfileInfo& user);
88 ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); 97 ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username);
89 ResultCode CreateNewUser(UUID uuid, const std::string& username); 98 ResultCode CreateNewUser(UUID uuid, const std::string& username);
90 boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const; 99 std::optional<UUID> GetUser(std::size_t index) const;
91 boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; 100 std::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
92 bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const; 101 std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
102 bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const;
93 bool GetProfileBase(UUID uuid, ProfileBase& profile) const; 103 bool GetProfileBase(UUID uuid, ProfileBase& profile) const;
94 bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; 104 bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
95 bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, 105 bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
96 ProfileData& data) const; 106 ProfileData& data) const;
97 bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; 107 bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const;
98 bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, 108 bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
@@ -100,6 +110,7 @@ public:
100 std::size_t GetUserCount() const; 110 std::size_t GetUserCount() const;
101 std::size_t GetOpenUserCount() const; 111 std::size_t GetOpenUserCount() const;
102 bool UserExists(UUID uuid) const; 112 bool UserExists(UUID uuid) const;
113 bool UserExistsIndex(std::size_t index) const;
103 void OpenUser(UUID uuid); 114 void OpenUser(UUID uuid);
104 void CloseUser(UUID uuid); 115 void CloseUser(UUID uuid);
105 UserIDArray GetOpenUsers() const; 116 UserIDArray GetOpenUsers() const;
@@ -108,11 +119,17 @@ public:
108 119
109 bool CanSystemRegisterUser() const; 120 bool CanSystemRegisterUser() const;
110 121
122 bool RemoveUser(UUID uuid);
123 bool SetProfileBase(UUID uuid, const ProfileBase& profile_new);
124
111private: 125private:
126 void ParseUserSaveFile();
127 void WriteUserSaveFile();
128 std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
129 bool RemoveProfileAtIndex(std::size_t index);
130
112 std::array<ProfileInfo, MAX_USERS> profiles{}; 131 std::array<ProfileInfo, MAX_USERS> profiles{};
113 std::size_t user_count = 0; 132 std::size_t user_count = 0;
114 boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
115 bool RemoveProfileAtIndex(std::size_t index);
116 UUID last_opened_user{INVALID_UUID}; 133 UUID last_opened_user{INVALID_UUID};
117}; 134};
118 135
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 4d1f83170..59aafd616 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -4,17 +4,20 @@
4 4
5#include <array> 5#include <array>
6#include <cinttypes> 6#include <cinttypes>
7#include <cstring>
7#include <stack> 8#include <stack>
8#include "core/core.h" 9#include "core/core.h"
9#include "core/hle/ipc_helpers.h" 10#include "core/hle/ipc_helpers.h"
10#include "core/hle/kernel/event.h" 11#include "core/hle/kernel/event.h"
11#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
13#include "core/hle/service/acc/profile_manager.h"
12#include "core/hle/service/am/am.h" 14#include "core/hle/service/am/am.h"
13#include "core/hle/service/am/applet_ae.h" 15#include "core/hle/service/am/applet_ae.h"
14#include "core/hle/service/am/applet_oe.h" 16#include "core/hle/service/am/applet_oe.h"
15#include "core/hle/service/am/idle.h" 17#include "core/hle/service/am/idle.h"
16#include "core/hle/service/am/omm.h" 18#include "core/hle/service/am/omm.h"
17#include "core/hle/service/am/spsm.h" 19#include "core/hle/service/am/spsm.h"
20#include "core/hle/service/am/tcap.h"
18#include "core/hle/service/apm/apm.h" 21#include "core/hle/service/apm/apm.h"
19#include "core/hle/service/filesystem/filesystem.h" 22#include "core/hle/service/filesystem/filesystem.h"
20#include "core/hle/service/nvflinger/nvflinger.h" 23#include "core/hle/service/nvflinger/nvflinger.h"
@@ -25,14 +28,29 @@
25 28
26namespace Service::AM { 29namespace Service::AM {
27 30
31constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
32
33struct LaunchParameters {
34 u32_le magic;
35 u32_le is_account_selected;
36 u128 current_user;
37 INSERT_PADDING_BYTES(0x70);
38};
39static_assert(sizeof(LaunchParameters) == 0x88);
40
28IWindowController::IWindowController() : ServiceFramework("IWindowController") { 41IWindowController::IWindowController() : ServiceFramework("IWindowController") {
42 // clang-format off
29 static const FunctionInfo functions[] = { 43 static const FunctionInfo functions[] = {
30 {0, nullptr, "CreateWindow"}, 44 {0, nullptr, "CreateWindow"},
31 {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, 45 {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
32 {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, 46 {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
33 {11, nullptr, "ReleaseForegroundRights"}, 47 {11, nullptr, "ReleaseForegroundRights"},
34 {12, nullptr, "RejectToChangeIntoBackground"}, 48 {12, nullptr, "RejectToChangeIntoBackground"},
49 {20, nullptr, "SetAppletWindowVisibility"},
50 {21, nullptr, "SetAppletGpuTimeSlice"},
35 }; 51 };
52 // clang-format on
53
36 RegisterHandlers(functions); 54 RegisterHandlers(functions);
37} 55}
38 56
@@ -87,6 +105,7 @@ void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestCo
87} 105}
88 106
89IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") { 107IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {
108 // clang-format off
90 static const FunctionInfo functions[] = { 109 static const FunctionInfo functions[] = {
91 {0, nullptr, "GetLastForegroundCaptureImage"}, 110 {0, nullptr, "GetLastForegroundCaptureImage"},
92 {1, nullptr, "UpdateLastForegroundCaptureImage"}, 111 {1, nullptr, "UpdateLastForegroundCaptureImage"},
@@ -117,7 +136,11 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
117 {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, 136 {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"},
118 {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"}, 137 {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"},
119 {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"}, 138 {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"},
139 // 6.0.0+
140 {28, nullptr, "TakeScreenShotOfOwnLayerEx"},
120 }; 141 };
142 // clang-format on
143
121 RegisterHandlers(functions); 144 RegisterHandlers(functions);
122} 145}
123 146
@@ -128,6 +151,7 @@ IDebugFunctions::~IDebugFunctions() = default;
128 151
129ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 152ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
130 : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) { 153 : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) {
154 // clang-format off
131 static const FunctionInfo functions[] = { 155 static const FunctionInfo functions[] = {
132 {0, nullptr, "Exit"}, 156 {0, nullptr, "Exit"},
133 {1, &ISelfController::LockExit, "LockExit"}, 157 {1, &ISelfController::LockExit, "LockExit"},
@@ -136,10 +160,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
136 {4, nullptr, "LeaveFatalSection"}, 160 {4, nullptr, "LeaveFatalSection"},
137 {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"}, 161 {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
138 {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"}, 162 {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
139 {11, &ISelfController::SetOperationModeChangedNotification, 163 {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"},
140 "SetOperationModeChangedNotification"}, 164 {12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"},
141 {12, &ISelfController::SetPerformanceModeChangedNotification,
142 "SetPerformanceModeChangedNotification"},
143 {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"}, 165 {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"},
144 {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"}, 166 {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"},
145 {15, nullptr, "SetScreenShotAppletIdentityInfo"}, 167 {15, nullptr, "SetScreenShotAppletIdentityInfo"},
@@ -165,7 +187,12 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
165 {69, nullptr, "IsAutoSleepDisabled"}, 187 {69, nullptr, "IsAutoSleepDisabled"},
166 {70, nullptr, "ReportMultimediaError"}, 188 {70, nullptr, "ReportMultimediaError"},
167 {80, nullptr, "SetWirelessPriorityMode"}, 189 {80, nullptr, "SetWirelessPriorityMode"},
190 {90, nullptr, "GetAccumulatedSuspendedTickValue"},
191 {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"},
192 {1000, nullptr, "GetDebugStorageChannel"},
168 }; 193 };
194 // clang-format on
195
169 RegisterHandlers(functions); 196 RegisterHandlers(functions);
170 197
171 auto& kernel = Core::System::GetInstance().Kernel(); 198 auto& kernel = Core::System::GetInstance().Kernel();
@@ -312,6 +339,7 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
312} 339}
313 340
314ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { 341ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") {
342 // clang-format off
315 static const FunctionInfo functions[] = { 343 static const FunctionInfo functions[] = {
316 {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, 344 {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
317 {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"}, 345 {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"},
@@ -336,11 +364,12 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter"
336 {52, nullptr, "SwitchLcdBacklight"}, 364 {52, nullptr, "SwitchLcdBacklight"},
337 {55, nullptr, "IsInControllerFirmwareUpdateSection"}, 365 {55, nullptr, "IsInControllerFirmwareUpdateSection"},
338 {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, 366 {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
339 {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, 367 {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
340 "GetDefaultDisplayResolutionChangeEvent"},
341 {62, nullptr, "GetHdcpAuthenticationState"}, 368 {62, nullptr, "GetHdcpAuthenticationState"},
342 {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, 369 {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"},
343 }; 370 };
371 // clang-format on
372
344 RegisterHandlers(functions); 373 RegisterHandlers(functions);
345 374
346 auto& kernel = Core::System::GetInstance().Kernel(); 375 auto& kernel = Core::System::GetInstance().Kernel();
@@ -432,11 +461,14 @@ class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
432public: 461public:
433 explicit IStorageAccessor(std::vector<u8> buffer) 462 explicit IStorageAccessor(std::vector<u8> buffer)
434 : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) { 463 : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
464 // clang-format off
435 static const FunctionInfo functions[] = { 465 static const FunctionInfo functions[] = {
436 {0, &IStorageAccessor::GetSize, "GetSize"}, 466 {0, &IStorageAccessor::GetSize, "GetSize"},
437 {10, &IStorageAccessor::Write, "Write"}, 467 {10, &IStorageAccessor::Write, "Write"},
438 {11, &IStorageAccessor::Read, "Read"}, 468 {11, &IStorageAccessor::Read, "Read"},
439 }; 469 };
470 // clang-format on
471
440 RegisterHandlers(functions); 472 RegisterHandlers(functions);
441 } 473 }
442 474
@@ -489,10 +521,13 @@ class IStorage final : public ServiceFramework<IStorage> {
489public: 521public:
490 explicit IStorage(std::vector<u8> buffer) 522 explicit IStorage(std::vector<u8> buffer)
491 : ServiceFramework("IStorage"), buffer(std::move(buffer)) { 523 : ServiceFramework("IStorage"), buffer(std::move(buffer)) {
524 // clang-format off
492 static const FunctionInfo functions[] = { 525 static const FunctionInfo functions[] = {
493 {0, &IStorage::Open, "Open"}, 526 {0, &IStorage::Open, "Open"},
494 {1, nullptr, "OpenTransferStorage"}, 527 {1, nullptr, "OpenTransferStorage"},
495 }; 528 };
529 // clang-format on
530
496 RegisterHandlers(functions); 531 RegisterHandlers(functions);
497 } 532 }
498 533
@@ -512,6 +547,7 @@ private:
512class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { 547class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
513public: 548public:
514 explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") { 549 explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
550 // clang-format off
515 static const FunctionInfo functions[] = { 551 static const FunctionInfo functions[] = {
516 {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, 552 {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
517 {1, nullptr, "IsCompleted"}, 553 {1, nullptr, "IsCompleted"},
@@ -532,6 +568,8 @@ public:
532 {150, nullptr, "RequestForAppletToGetForeground"}, 568 {150, nullptr, "RequestForAppletToGetForeground"},
533 {160, nullptr, "GetIndirectLayerConsumerHandle"}, 569 {160, nullptr, "GetIndirectLayerConsumerHandle"},
534 }; 570 };
571 // clang-format on
572
535 RegisterHandlers(functions); 573 RegisterHandlers(functions);
536 574
537 auto& kernel = Core::System::GetInstance().Kernel(); 575 auto& kernel = Core::System::GetInstance().Kernel();
@@ -624,13 +662,13 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
624} 662}
625 663
626IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { 664IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
665 // clang-format off
627 static const FunctionInfo functions[] = { 666 static const FunctionInfo functions[] = {
628 {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, 667 {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
629 {10, nullptr, "CreateApplicationAndPushAndRequestToStart"}, 668 {10, nullptr, "CreateApplicationAndPushAndRequestToStart"},
630 {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"}, 669 {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
631 {12, nullptr, "CreateApplicationAndRequestToStart"}, 670 {12, nullptr, "CreateApplicationAndRequestToStart"},
632 {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, 671 {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"},
633 "CreateApplicationAndRequestToStartForQuest"},
634 {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, 672 {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
635 {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, 673 {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
636 {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, 674 {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
@@ -638,10 +676,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
638 {24, nullptr, "GetLaunchStorageInfoForDebug"}, 676 {24, nullptr, "GetLaunchStorageInfoForDebug"},
639 {25, nullptr, "ExtendSaveData"}, 677 {25, nullptr, "ExtendSaveData"},
640 {26, nullptr, "GetSaveDataSize"}, 678 {26, nullptr, "GetSaveDataSize"},
641 {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, 679 {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
642 "BeginBlockingHomeButtonShortAndLongPressed"}, 680 {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
643 {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed,
644 "EndBlockingHomeButtonShortAndLongPressed"},
645 {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, 681 {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
646 {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"}, 682 {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
647 {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, 683 {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
@@ -666,6 +702,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
666 {1000, nullptr, "CreateMovieMaker"}, 702 {1000, nullptr, "CreateMovieMaker"},
667 {1001, nullptr, "PrepareForJit"}, 703 {1001, nullptr, "PrepareForJit"},
668 }; 704 };
705 // clang-format on
706
669 RegisterHandlers(functions); 707 RegisterHandlers(functions);
670} 708}
671 709
@@ -698,20 +736,23 @@ void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx
698} 736}
699 737
700void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { 738void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
701 constexpr std::array<u8, 0x88> data{{ 739 LaunchParameters params{};
702 0xca, 0x97, 0x94, 0xc7, // Magic 740
703 1, 0, 0, 0, // IsAccountSelected (bool) 741 params.magic = POP_LAUNCH_PARAMETER_MAGIC;
704 1, 0, 0, 0, // User Id (word 0) 742 params.is_account_selected = 1;
705 0, 0, 0, 0, // User Id (word 1)
706 0, 0, 0, 0, // User Id (word 2)
707 0, 0, 0, 0 // User Id (word 3)
708 }};
709 743
710 std::vector<u8> buffer(data.begin(), data.end()); 744 Account::ProfileManager profile_manager{};
745 const auto uuid = profile_manager.GetUser(Settings::values.current_user);
746 ASSERT(uuid != std::nullopt);
747 params.current_user = uuid->uuid;
711 748
712 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 749 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
713 750
714 rb.Push(RESULT_SUCCESS); 751 rb.Push(RESULT_SUCCESS);
752
753 std::vector<u8> buffer(sizeof(LaunchParameters));
754 std::memcpy(buffer.data(), &params, buffer.size());
755
715 rb.PushIpcInterface<AM::IStorage>(buffer); 756 rb.PushIpcInterface<AM::IStorage>(buffer);
716 757
717 LOG_DEBUG(Service_AM, "called"); 758 LOG_DEBUG(Service_AM, "called");
@@ -804,9 +845,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager,
804 std::make_shared<IdleSys>()->InstallAsService(service_manager); 845 std::make_shared<IdleSys>()->InstallAsService(service_manager);
805 std::make_shared<OMM>()->InstallAsService(service_manager); 846 std::make_shared<OMM>()->InstallAsService(service_manager);
806 std::make_shared<SPSM>()->InstallAsService(service_manager); 847 std::make_shared<SPSM>()->InstallAsService(service_manager);
848 std::make_shared<TCAP>()->InstallAsService(service_manager);
807} 849}
808 850
809IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") { 851IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") {
852 // clang-format off
810 static const FunctionInfo functions[] = { 853 static const FunctionInfo functions[] = {
811 {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"}, 854 {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"},
812 {11, nullptr, "LockForeground"}, 855 {11, nullptr, "LockForeground"},
@@ -815,7 +858,10 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions"
815 {21, nullptr, "GetPopFromGeneralChannelEvent"}, 858 {21, nullptr, "GetPopFromGeneralChannelEvent"},
816 {30, nullptr, "GetHomeButtonWriterLockAccessor"}, 859 {30, nullptr, "GetHomeButtonWriterLockAccessor"},
817 {31, nullptr, "GetWriterLockAccessorEx"}, 860 {31, nullptr, "GetWriterLockAccessorEx"},
861 {100, nullptr, "PopRequestLaunchApplicationForDebug"},
818 }; 862 };
863 // clang-format on
864
819 RegisterHandlers(functions); 865 RegisterHandlers(functions);
820} 866}
821 867
@@ -828,6 +874,7 @@ void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx)
828} 874}
829 875
830IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") { 876IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") {
877 // clang-format off
831 static const FunctionInfo functions[] = { 878 static const FunctionInfo functions[] = {
832 {0, nullptr, "RequestToEnterSleep"}, 879 {0, nullptr, "RequestToEnterSleep"},
833 {1, nullptr, "EnterSleep"}, 880 {1, nullptr, "EnterSleep"},
@@ -841,18 +888,23 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat
841 {14, nullptr, "ShouldSleepOnBoot"}, 888 {14, nullptr, "ShouldSleepOnBoot"},
842 {15, nullptr, "GetHdcpAuthenticationFailedEvent"}, 889 {15, nullptr, "GetHdcpAuthenticationFailedEvent"},
843 }; 890 };
891 // clang-format on
892
844 RegisterHandlers(functions); 893 RegisterHandlers(functions);
845} 894}
846 895
847IGlobalStateController::~IGlobalStateController() = default; 896IGlobalStateController::~IGlobalStateController() = default;
848 897
849IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") { 898IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") {
899 // clang-format off
850 static const FunctionInfo functions[] = { 900 static const FunctionInfo functions[] = {
851 {0, nullptr, "CreateApplication"}, 901 {0, nullptr, "CreateApplication"},
852 {1, nullptr, "PopLaunchRequestedApplication"}, 902 {1, nullptr, "PopLaunchRequestedApplication"},
853 {10, nullptr, "CreateSystemApplication"}, 903 {10, nullptr, "CreateSystemApplication"},
854 {100, nullptr, "PopFloatingApplicationForDevelopment"}, 904 {100, nullptr, "PopFloatingApplicationForDevelopment"},
855 }; 905 };
906 // clang-format on
907
856 RegisterHandlers(functions); 908 RegisterHandlers(functions);
857} 909}
858 910
@@ -860,6 +912,7 @@ IApplicationCreator::~IApplicationCreator() = default;
860 912
861IProcessWindingController::IProcessWindingController() 913IProcessWindingController::IProcessWindingController()
862 : ServiceFramework("IProcessWindingController") { 914 : ServiceFramework("IProcessWindingController") {
915 // clang-format off
863 static const FunctionInfo functions[] = { 916 static const FunctionInfo functions[] = {
864 {0, nullptr, "GetLaunchReason"}, 917 {0, nullptr, "GetLaunchReason"},
865 {11, nullptr, "OpenCallingLibraryApplet"}, 918 {11, nullptr, "OpenCallingLibraryApplet"},
@@ -870,6 +923,8 @@ IProcessWindingController::IProcessWindingController()
870 {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"}, 923 {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"},
871 {41, nullptr, "ReserveToStartAndWait"}, 924 {41, nullptr, "ReserveToStartAndWait"},
872 }; 925 };
926 // clang-format on
927
873 RegisterHandlers(functions); 928 RegisterHandlers(functions);
874} 929}
875 930
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..54305cf05 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"
@@ -23,8 +24,8 @@ namespace Service::AOC {
23constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; 24constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
24constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000; 25constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000;
25 26
26static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) { 27static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
27 return (aoc & DLC_BASE_TITLE_ID_MASK) == base; 28 return (title_id & DLC_BASE_TITLE_ID_MASK) == base;
28} 29}
29 30
30static std::vector<u64> AccumulateAOCTitleIDs() { 31static std::vector<u64> AccumulateAOCTitleIDs() {
@@ -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;
@@ -69,7 +74,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
69 const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 74 const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
70 rb.Push<u32>(static_cast<u32>( 75 rb.Push<u32>(static_cast<u32>(
71 std::count_if(add_on_content.begin(), add_on_content.end(), 76 std::count_if(add_on_content.begin(), add_on_content.end(),
72 [&current](u64 tid) { return (tid & DLC_BASE_TITLE_ID_MASK) == current; }))); 77 [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); })));
73} 78}
74 79
75void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { 80void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
@@ -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/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index d5dced429..c87721c39 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -17,6 +17,7 @@
17#include "core/file_sys/errors.h" 17#include "core/file_sys/errors.h"
18#include "core/file_sys/mode.h" 18#include "core/file_sys/mode.h"
19#include "core/file_sys/nca_metadata.h" 19#include "core/file_sys/nca_metadata.h"
20#include "core/file_sys/patch_manager.h"
20#include "core/file_sys/savedata_factory.h" 21#include "core/file_sys/savedata_factory.h"
21#include "core/file_sys/vfs.h" 22#include "core/file_sys/vfs.h"
22#include "core/hle/ipc_helpers.h" 23#include "core/hle/ipc_helpers.h"
@@ -630,6 +631,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
630 static_cast<u8>(storage_id), unknown, title_id); 631 static_cast<u8>(storage_id), unknown, title_id);
631 632
632 auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); 633 auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
634
633 if (data.Failed()) { 635 if (data.Failed()) {
634 // TODO(DarkLordZach): Find the right error code to use here 636 // TODO(DarkLordZach): Find the right error code to use here
635 LOG_ERROR(Service_FS, 637 LOG_ERROR(Service_FS,
@@ -640,7 +642,9 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
640 return; 642 return;
641 } 643 }
642 644
643 IStorage storage(std::move(data.Unwrap())); 645 FileSys::PatchManager pm{title_id};
646
647 IStorage storage(pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data));
644 648
645 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 649 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
646 rb.Push(RESULT_SUCCESS); 650 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
index 1625e9c3d..0993a7815 100644
--- a/src/core/hle/service/hid/controllers/controller_base.cpp
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -5,6 +5,8 @@
5#include "core/hle/service/hid/controllers/controller_base.h" 5#include "core/hle/service/hid/controllers/controller_base.h"
6 6
7namespace Service::HID { 7namespace Service::HID {
8
9ControllerBase::ControllerBase() = default;
8ControllerBase::~ControllerBase() = default; 10ControllerBase::~ControllerBase() = default;
9 11
10void ControllerBase::ActivateController() { 12void ControllerBase::ActivateController() {
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index fa98e2354..f0e092b1b 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -10,8 +10,8 @@
10namespace Service::HID { 10namespace Service::HID {
11class ControllerBase { 11class ControllerBase {
12public: 12public:
13 ControllerBase() = default; 13 ControllerBase();
14 virtual ~ControllerBase() = 0; 14 virtual ~ControllerBase();
15 15
16 // Called when the controller is initialized 16 // Called when the controller is initialized
17 virtual void OnInit() = 0; 17 virtual void OnInit() = 0;
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 6f8ef6e3f..3d100763f 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -4,13 +4,13 @@
4 4
5#include <cstring> 5#include <cstring>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/swap.h"
8#include "core/core_timing.h" 7#include "core/core_timing.h"
9#include "core/hle/service/hid/controllers/debug_pad.h" 8#include "core/hle/service/hid/controllers/debug_pad.h"
10 9
11namespace Service::HID { 10namespace Service::HID {
12 11
13Controller_DebugPad::Controller_DebugPad() = default; 12Controller_DebugPad::Controller_DebugPad() = default;
13Controller_DebugPad::~Controller_DebugPad() = default;
14 14
15void Controller_DebugPad::OnInit() {} 15void Controller_DebugPad::OnInit() {}
16 16
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index e35675fa1..62b4f2682 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -14,6 +14,7 @@ namespace Service::HID {
14class Controller_DebugPad final : public ControllerBase { 14class Controller_DebugPad final : public ControllerBase {
15public: 15public:
16 Controller_DebugPad(); 16 Controller_DebugPad();
17 ~Controller_DebugPad() override;
17 18
18 // Called when the controller is initialized 19 // Called when the controller is initialized
19 void OnInit() override; 20 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index b473b9e2b..898572277 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -4,7 +4,6 @@
4 4
5#include <cstring> 5#include <cstring>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/swap.h"
8#include "core/core_timing.h" 7#include "core/core_timing.h"
9#include "core/hle/service/hid/controllers/gesture.h" 8#include "core/hle/service/hid/controllers/gesture.h"
10 9
@@ -12,6 +11,7 @@ namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00; 11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
13 12
14Controller_Gesture::Controller_Gesture() = default; 13Controller_Gesture::Controller_Gesture() = default;
14Controller_Gesture::~Controller_Gesture() = default;
15 15
16void Controller_Gesture::OnInit() {} 16void Controller_Gesture::OnInit() {}
17 17
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 0ced50dfd..1056ffbcd 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -13,6 +13,7 @@ namespace Service::HID {
13class Controller_Gesture final : public ControllerBase { 13class Controller_Gesture final : public ControllerBase {
14public: 14public:
15 Controller_Gesture(); 15 Controller_Gesture();
16 ~Controller_Gesture() override;
16 17
17 // Called when the controller is initialized 18 // Called when the controller is initialized
18 void OnInit() override; 19 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 089c02ac4..ccfbce9ac 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -4,7 +4,6 @@
4 4
5#include <cstring> 5#include <cstring>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/swap.h"
8#include "core/core_timing.h" 7#include "core/core_timing.h"
9#include "core/hle/service/hid/controllers/keyboard.h" 8#include "core/hle/service/hid/controllers/keyboard.h"
10 9
@@ -12,6 +11,7 @@ namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; 11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
13 12
14Controller_Keyboard::Controller_Keyboard() = default; 13Controller_Keyboard::Controller_Keyboard() = default;
14Controller_Keyboard::~Controller_Keyboard() = default;
15 15
16void Controller_Keyboard::OnInit() {} 16void Controller_Keyboard::OnInit() {}
17 17
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index 778e14f7e..493e68fce 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -14,6 +14,7 @@ namespace Service::HID {
14class Controller_Keyboard final : public ControllerBase { 14class Controller_Keyboard final : public ControllerBase {
15public: 15public:
16 Controller_Keyboard(); 16 Controller_Keyboard();
17 ~Controller_Keyboard() override;
17 18
18 // Called when the controller is initialized 19 // Called when the controller is initialized
19 void OnInit() override; 20 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 78e9b5e9e..4e246a57d 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -4,7 +4,6 @@
4 4
5#include <cstring> 5#include <cstring>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/swap.h"
8#include "core/core_timing.h" 7#include "core/core_timing.h"
9#include "core/hle/service/hid/controllers/mouse.h" 8#include "core/hle/service/hid/controllers/mouse.h"
10 9
@@ -12,6 +11,7 @@ namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; 11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
13 12
14Controller_Mouse::Controller_Mouse() = default; 13Controller_Mouse::Controller_Mouse() = default;
14Controller_Mouse::~Controller_Mouse() = default;
15 15
16void Controller_Mouse::OnInit() {} 16void Controller_Mouse::OnInit() {}
17 17
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 05358a4f5..543b0b71f 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -13,6 +13,7 @@ namespace Service::HID {
13class Controller_Mouse final : public ControllerBase { 13class Controller_Mouse final : public ControllerBase {
14public: 14public:
15 Controller_Mouse(); 15 Controller_Mouse();
16 ~Controller_Mouse() override;
16 17
17 // Called when the controller is initialized 18 // Called when the controller is initialized
18 void OnInit() override; 19 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index d17e64b2a..4b4d1324f 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -2,8 +2,6 @@
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#pragma once
6
7#include <algorithm> 5#include <algorithm>
8#include <array> 6#include <array>
9#include <cstring> 7#include <cstring>
@@ -11,7 +9,6 @@
11#include "common/bit_field.h" 9#include "common/bit_field.h"
12#include "common/common_types.h" 10#include "common/common_types.h"
13#include "common/logging/log.h" 11#include "common/logging/log.h"
14#include "common/swap.h"
15#include "core/core.h" 12#include "core/core.h"
16#include "core/core_timing.h" 13#include "core/core_timing.h"
17#include "core/frontend/input.h" 14#include "core/frontend/input.h"
@@ -20,6 +17,7 @@
20#include "core/settings.h" 17#include "core/settings.h"
21 18
22namespace Service::HID { 19namespace Service::HID {
20
23constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; 21constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
24constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; 22constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
25constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6; 23constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
@@ -28,9 +26,22 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
28constexpr s32 HID_JOYSTICK_MIN = -0x7fff; 26constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
29constexpr std::size_t NPAD_OFFSET = 0x9A00; 27constexpr std::size_t NPAD_OFFSET = 0x9A00;
30constexpr u32 BATTERY_FULL = 2; 28constexpr u32 BATTERY_FULL = 2;
31enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; 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};
32 42
33Controller_NPad::Controller_NPad() = default; 43Controller_NPad::Controller_NPad() = default;
44Controller_NPad::~Controller_NPad() = default;
34 45
35void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { 46void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
36 const auto controller_type = connected_controllers[controller_idx].type; 47 const auto controller_type = connected_controllers[controller_idx].type;
@@ -97,9 +108,10 @@ void Controller_NPad::OnInit() {
97 styleset_changed_event = 108 styleset_changed_event =
98 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); 109 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
99 110
100 if (!IsControllerActivated()) 111 if (!IsControllerActivated()) {
101 return; 112 return;
102 std::size_t controller{}; 113 }
114
103 if (style.raw == 0) { 115 if (style.raw == 0) {
104 // We want to support all controllers 116 // We want to support all controllers
105 style.handheld.Assign(1); 117 style.handheld.Assign(1);
@@ -114,7 +126,7 @@ void Controller_NPad::OnInit() {
114 supported_npad_id_types.resize(npad_id_list.size()); 126 supported_npad_id_types.resize(npad_id_list.size());
115 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 127 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
116 npad_id_list.size() * sizeof(u32)); 128 npad_id_list.size() * sizeof(u32));
117 AddNewController(NPadControllerType::JoyDual); 129 AddNewController(PREFERRED_CONTROLLER);
118 } 130 }
119} 131}
120 132
@@ -211,6 +223,51 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
211 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); 223 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
212 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); 224 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
213 225
226 if (controller_type == NPadControllerType::JoyLeft ||
227 controller_type == NPadControllerType::JoyRight) {
228 if (npad.properties.is_horizontal) {
229 ControllerPadState state{};
230 AnalogPosition temp_lstick_entry{};
231 AnalogPosition temp_rstick_entry{};
232 if (controller_type == NPadControllerType::JoyLeft) {
233 state.d_down.Assign(pad_state.d_left.Value());
234 state.d_left.Assign(pad_state.d_up.Value());
235 state.d_right.Assign(pad_state.d_down.Value());
236 state.d_up.Assign(pad_state.d_right.Value());
237 state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
238 state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
239
240 state.zl.Assign(pad_state.zl.Value());
241 state.plus.Assign(pad_state.minus.Value());
242
243 temp_lstick_entry = lstick_entry;
244 temp_rstick_entry = rstick_entry;
245 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
246 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
247 temp_lstick_entry.y *= -1;
248 } else if (controller_type == NPadControllerType::JoyRight) {
249 state.x.Assign(pad_state.a.Value());
250 state.a.Assign(pad_state.b.Value());
251 state.b.Assign(pad_state.y.Value());
252 state.y.Assign(pad_state.b.Value());
253
254 state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
255 state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
256 state.zr.Assign(pad_state.zr.Value());
257 state.plus.Assign(pad_state.plus.Value());
258
259 temp_lstick_entry = lstick_entry;
260 temp_rstick_entry = rstick_entry;
261 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
262 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
263 temp_rstick_entry.x *= -1;
264 }
265 pad_state.raw = state.raw;
266 lstick_entry = temp_lstick_entry;
267 rstick_entry = temp_rstick_entry;
268 }
269 }
270
214 auto& main_controller = 271 auto& main_controller =
215 npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index]; 272 npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
216 auto& handheld_entry = 273 auto& handheld_entry =
@@ -313,9 +370,19 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
313 supported_npad_id_types.clear(); 370 supported_npad_id_types.clear();
314 supported_npad_id_types.resize(length / sizeof(u32)); 371 supported_npad_id_types.resize(length / sizeof(u32));
315 std::memcpy(supported_npad_id_types.data(), data, length); 372 std::memcpy(supported_npad_id_types.data(), data, length);
373 for (std::size_t i = 0; i < connected_controllers.size(); i++) {
374 auto& controller = connected_controllers[i];
375 if (!controller.is_connected) {
376 continue;
377 }
378 if (!IsControllerSupported(PREFERRED_CONTROLLER)) {
379 controller.type = DecideBestController(PREFERRED_CONTROLLER);
380 InitNewlyAddedControler(i);
381 }
382 }
316} 383}
317 384
318const void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 385void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
319 ASSERT(max_length < supported_npad_id_types.size()); 386 ASSERT(max_length < supported_npad_id_types.size());
320 std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size()); 387 std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size());
321} 388}
@@ -344,11 +411,11 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
344 for (std::size_t i = 0; i < controller_ids.size(); i++) { 411 for (std::size_t i = 0; i < controller_ids.size(); i++) {
345 std::size_t controller_pos = i; 412 std::size_t controller_pos = i;
346 // Handheld controller conversion 413 // Handheld controller conversion
347 if (controller_pos == 32) { 414 if (controller_pos == NPAD_HANDHELD) {
348 controller_pos = 8; 415 controller_pos = 8;
349 } 416 }
350 // Unknown controller conversion 417 // Unknown controller conversion
351 if (controller_pos == 16) { 418 if (controller_pos == NPAD_UNKNOWN) {
352 controller_pos = 9; 419 controller_pos = 9;
353 } 420 }
354 if (connected_controllers[controller_pos].is_connected) { 421 if (connected_controllers[controller_pos].is_connected) {
@@ -426,4 +493,128 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
426void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { 493void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
427 can_controllers_vibrate = can_vibrate; 494 can_controllers_vibrate = can_vibrate;
428} 495}
496
497bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
498 const bool support_handheld =
499 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) !=
500 supported_npad_id_types.end();
501 if (controller == NPadControllerType::Handheld) {
502 // Handheld is not even a supported type, lets stop here
503 if (!support_handheld) {
504 return false;
505 }
506 // Handheld should not be supported in docked mode
507 if (Settings::values.use_docked_mode) {
508 return false;
509 }
510
511 return true;
512 }
513 if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(),
514 [](u32 npad_id) { return npad_id <= MAX_NPAD_ID; })) {
515 switch (controller) {
516 case NPadControllerType::ProController:
517 return style.pro_controller;
518 case NPadControllerType::JoyDual:
519 return style.joycon_dual;
520 case NPadControllerType::JoyLeft:
521 return style.joycon_left;
522 case NPadControllerType::JoyRight:
523 return style.joycon_right;
524 case NPadControllerType::Pokeball:
525 return style.pokeball;
526 default:
527 return false;
528 }
529 }
530 return false;
531}
532
533Controller_NPad::NPadControllerType Controller_NPad::DecideBestController(
534 NPadControllerType priority) const {
535 if (IsControllerSupported(priority)) {
536 return priority;
537 }
538 const auto is_docked = Settings::values.use_docked_mode;
539 if (is_docked && priority == NPadControllerType::Handheld) {
540 priority = NPadControllerType::JoyDual;
541 if (IsControllerSupported(priority)) {
542 return priority;
543 }
544 }
545 std::vector<NPadControllerType> priority_list;
546 switch (priority) {
547 case NPadControllerType::ProController:
548 priority_list.push_back(NPadControllerType::JoyDual);
549 if (!is_docked) {
550 priority_list.push_back(NPadControllerType::Handheld);
551 }
552 priority_list.push_back(NPadControllerType::JoyLeft);
553 priority_list.push_back(NPadControllerType::JoyRight);
554 priority_list.push_back(NPadControllerType::Pokeball);
555 break;
556 case NPadControllerType::Handheld:
557 priority_list.push_back(NPadControllerType::JoyDual);
558 priority_list.push_back(NPadControllerType::ProController);
559 priority_list.push_back(NPadControllerType::JoyLeft);
560 priority_list.push_back(NPadControllerType::JoyRight);
561 priority_list.push_back(NPadControllerType::Pokeball);
562 break;
563 case NPadControllerType::JoyDual:
564 if (!is_docked) {
565 priority_list.push_back(NPadControllerType::Handheld);
566 }
567 priority_list.push_back(NPadControllerType::ProController);
568 priority_list.push_back(NPadControllerType::JoyLeft);
569 priority_list.push_back(NPadControllerType::JoyRight);
570 priority_list.push_back(NPadControllerType::Pokeball);
571 break;
572 case NPadControllerType::JoyLeft:
573 priority_list.push_back(NPadControllerType::JoyRight);
574 priority_list.push_back(NPadControllerType::JoyDual);
575 if (!is_docked) {
576 priority_list.push_back(NPadControllerType::Handheld);
577 }
578 priority_list.push_back(NPadControllerType::ProController);
579 priority_list.push_back(NPadControllerType::Pokeball);
580 break;
581 case NPadControllerType::JoyRight:
582 priority_list.push_back(NPadControllerType::JoyLeft);
583 priority_list.push_back(NPadControllerType::JoyDual);
584 if (!is_docked) {
585 priority_list.push_back(NPadControllerType::Handheld);
586 }
587 priority_list.push_back(NPadControllerType::ProController);
588 priority_list.push_back(NPadControllerType::Pokeball);
589 break;
590 case NPadControllerType::Pokeball:
591 priority_list.push_back(NPadControllerType::JoyLeft);
592 priority_list.push_back(NPadControllerType::JoyRight);
593 priority_list.push_back(NPadControllerType::JoyDual);
594 if (!is_docked) {
595 priority_list.push_back(NPadControllerType::Handheld);
596 }
597 priority_list.push_back(NPadControllerType::ProController);
598 break;
599 default:
600 priority_list.push_back(NPadControllerType::JoyDual);
601 if (!is_docked) {
602 priority_list.push_back(NPadControllerType::Handheld);
603 }
604 priority_list.push_back(NPadControllerType::ProController);
605 priority_list.push_back(NPadControllerType::JoyLeft);
606 priority_list.push_back(NPadControllerType::JoyRight);
607 priority_list.push_back(NPadControllerType::JoyDual);
608 }
609
610 const auto iter = std::find_if(priority_list.begin(), priority_list.end(),
611 [this](auto type) { return IsControllerSupported(type); });
612 if (iter == priority_list.end()) {
613 UNIMPLEMENTED_MSG("Could not find supported controller!");
614 return priority;
615 }
616
617 return *iter;
618}
619
429} // namespace Service::HID 620} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 9d07d258d..ac86985ff 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -15,6 +15,7 @@ namespace Service::HID {
15class Controller_NPad final : public ControllerBase { 15class Controller_NPad final : public ControllerBase {
16public: 16public:
17 Controller_NPad(); 17 Controller_NPad();
18 ~Controller_NPad() override;
18 19
19 // Called when the controller is initialized 20 // Called when the controller is initialized
20 void OnInit() override; 21 void OnInit() override;
@@ -77,7 +78,7 @@ public:
77 position1.Assign(light2); 78 position1.Assign(light2);
78 position1.Assign(light3); 79 position1.Assign(light3);
79 position1.Assign(light4); 80 position1.Assign(light4);
80 }; 81 }
81 union { 82 union {
82 u64 raw{}; 83 u64 raw{};
83 BitField<0, 1, u64> position1; 84 BitField<0, 1, u64> position1;
@@ -91,7 +92,7 @@ public:
91 NPadType GetSupportedStyleSet() const; 92 NPadType GetSupportedStyleSet() const;
92 93
93 void SetSupportedNPadIdTypes(u8* data, std::size_t length); 94 void SetSupportedNPadIdTypes(u8* data, std::size_t length);
94 const void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); 95 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
95 std::size_t GetSupportedNPadIdTypesSize() const; 96 std::size_t GetSupportedNPadIdTypesSize() const;
96 97
97 void SetHoldType(NpadHoldType joy_hold_type); 98 void SetHoldType(NpadHoldType joy_hold_type);
@@ -277,12 +278,12 @@ private:
277 std::vector<u32> supported_npad_id_types{}; 278 std::vector<u32> supported_npad_id_types{};
278 NpadHoldType hold_type{NpadHoldType::Vertical}; 279 NpadHoldType hold_type{NpadHoldType::Vertical};
279 Kernel::SharedPtr<Kernel::Event> styleset_changed_event; 280 Kernel::SharedPtr<Kernel::Event> styleset_changed_event;
280 std::size_t dump_idx{};
281 Vibration last_processed_vibration{}; 281 Vibration last_processed_vibration{};
282 static constexpr std::array<u32, 10> npad_id_list{0, 1, 2, 3, 4, 5, 6, 7, 32, 16};
283 std::array<ControllerHolder, 10> connected_controllers{}; 282 std::array<ControllerHolder, 10> connected_controllers{};
284 bool can_controllers_vibrate{true}; 283 bool can_controllers_vibrate{true};
285 284
286 void InitNewlyAddedControler(std::size_t controller_idx); 285 void InitNewlyAddedControler(std::size_t controller_idx);
286 bool IsControllerSupported(NPadControllerType controller) const;
287 NPadControllerType DecideBestController(NPadControllerType priority) const;
287}; 288};
288} // namespace Service::HID 289} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index 3a13d5991..02fcfadd9 100644
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -4,13 +4,13 @@
4 4
5#include <cstring> 5#include <cstring>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/swap.h"
8#include "core/core_timing.h" 7#include "core/core_timing.h"
9#include "core/hle/service/hid/controllers/stubbed.h" 8#include "core/hle/service/hid/controllers/stubbed.h"
10 9
11namespace Service::HID { 10namespace Service::HID {
12 11
13Controller_Stubbed::Controller_Stubbed() = default; 12Controller_Stubbed::Controller_Stubbed() = default;
13Controller_Stubbed::~Controller_Stubbed() = default;
14 14
15void Controller_Stubbed::OnInit() {} 15void Controller_Stubbed::OnInit() {}
16 16
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
index 9c1b57f83..4a21c643e 100644
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -11,6 +11,7 @@ namespace Service::HID {
11class Controller_Stubbed final : public ControllerBase { 11class Controller_Stubbed final : public ControllerBase {
12public: 12public:
13 Controller_Stubbed(); 13 Controller_Stubbed();
14 ~Controller_Stubbed() override;
14 15
15 // Called when the controller is initialized 16 // Called when the controller is initialized
16 void OnInit() override; 17 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index e97f84ea1..43efef803 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -4,7 +4,6 @@
4 4
5#include <cstring> 5#include <cstring>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/swap.h"
8#include "core/core_timing.h" 7#include "core/core_timing.h"
9#include "core/frontend/emu_window.h" 8#include "core/frontend/emu_window.h"
10#include "core/frontend/input.h" 9#include "core/frontend/input.h"
@@ -15,6 +14,7 @@ namespace Service::HID {
15constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; 14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
16 15
17Controller_Touchscreen::Controller_Touchscreen() = default; 16Controller_Touchscreen::Controller_Touchscreen() = default;
17Controller_Touchscreen::~Controller_Touchscreen() = default;
18 18
19void Controller_Touchscreen::OnInit() {} 19void Controller_Touchscreen::OnInit() {}
20 20
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 1d97b6c2a..e5db6e6ba 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -14,6 +14,7 @@ namespace Service::HID {
14class Controller_Touchscreen final : public ControllerBase { 14class Controller_Touchscreen final : public ControllerBase {
15public: 15public:
16 Controller_Touchscreen(); 16 Controller_Touchscreen();
17 ~Controller_Touchscreen() override;
17 18
18 // Called when the controller is initialized 19 // Called when the controller is initialized
19 void OnInit() override; 20 void OnInit() override;
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index df0b48451..cd397c70b 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -4,7 +4,6 @@
4 4
5#include <cstring> 5#include <cstring>
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "common/swap.h"
8#include "core/core_timing.h" 7#include "core/core_timing.h"
9#include "core/hle/service/hid/controllers/xpad.h" 8#include "core/hle/service/hid/controllers/xpad.h"
10 9
@@ -12,6 +11,7 @@ namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; 11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
13 12
14Controller_XPad::Controller_XPad() = default; 13Controller_XPad::Controller_XPad() = default;
14Controller_XPad::~Controller_XPad() = default;
15 15
16void Controller_XPad::OnInit() {} 16void Controller_XPad::OnInit() {}
17 17
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index e2007183d..ff836989f 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -7,13 +7,13 @@
7#include "common/common_funcs.h" 7#include "common/common_funcs.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/swap.h" 9#include "common/swap.h"
10#include "core/frontend/input.h"
11#include "core/hle/service/hid/controllers/controller_base.h" 10#include "core/hle/service/hid/controllers/controller_base.h"
12 11
13namespace Service::HID { 12namespace Service::HID {
14class Controller_XPad final : public ControllerBase { 13class Controller_XPad final : public ControllerBase {
15public: 14public:
16 Controller_XPad(); 15 Controller_XPad();
16 ~Controller_XPad() override;
17 17
18 // Called when the controller is initialized 18 // Called when the controller is initialized
19 void OnInit() override; 19 void OnInit() override;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 8aca0f197..a9aa9ec78 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -177,6 +177,7 @@ public:
177 {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"}, 177 {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"},
178 {21, &Hid::ActivateMouse, "ActivateMouse"}, 178 {21, &Hid::ActivateMouse, "ActivateMouse"},
179 {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, 179 {31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
180 {32, nullptr, "SendKeyboardLockKeyEvent"},
180 {40, nullptr, "AcquireXpadIdEventHandle"}, 181 {40, nullptr, "AcquireXpadIdEventHandle"},
181 {41, nullptr, "ReleaseXpadIdEventHandle"}, 182 {41, nullptr, "ReleaseXpadIdEventHandle"},
182 {51, &Hid::ActivateXpad, "ActivateXpad"}, 183 {51, &Hid::ActivateXpad, "ActivateXpad"},
@@ -207,6 +208,7 @@ public:
207 {80, nullptr, "GetGyroscopeZeroDriftMode"}, 208 {80, nullptr, "GetGyroscopeZeroDriftMode"},
208 {81, nullptr, "ResetGyroscopeZeroDriftMode"}, 209 {81, nullptr, "ResetGyroscopeZeroDriftMode"},
209 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, 210 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
211 {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"},
210 {91, &Hid::ActivateGesture, "ActivateGesture"}, 212 {91, &Hid::ActivateGesture, "ActivateGesture"},
211 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, 213 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
212 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, 214 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
@@ -252,6 +254,7 @@ public:
252 {307, nullptr, "FinalizeSevenSixAxisSensor"}, 254 {307, nullptr, "FinalizeSevenSixAxisSensor"},
253 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, 255 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
254 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, 256 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
257 {310, nullptr, "ResetSevenSixAxisSensorTimestamp"},
255 {400, nullptr, "IsUsbFullKeyControllerEnabled"}, 258 {400, nullptr, "IsUsbFullKeyControllerEnabled"},
256 {401, nullptr, "EnableUsbFullKeyController"}, 259 {401, nullptr, "EnableUsbFullKeyController"},
257 {402, nullptr, "IsUsbFullKeyControllerConnected"}, 260 {402, nullptr, "IsUsbFullKeyControllerConnected"},
@@ -267,12 +270,24 @@ public:
267 {505, nullptr, "SetPalmaFrModeType"}, 270 {505, nullptr, "SetPalmaFrModeType"},
268 {506, nullptr, "ReadPalmaStep"}, 271 {506, nullptr, "ReadPalmaStep"},
269 {507, nullptr, "EnablePalmaStep"}, 272 {507, nullptr, "EnablePalmaStep"},
270 {508, nullptr, "SuspendPalmaStep"}, 273 {508, nullptr, "ResetPalmaStep"},
271 {509, nullptr, "ResetPalmaStep"}, 274 {509, nullptr, "ReadPalmaApplicationSection"},
272 {510, nullptr, "ReadPalmaApplicationSection"}, 275 {510, nullptr, "WritePalmaApplicationSection"},
273 {511, nullptr, "WritePalmaApplicationSection"}, 276 {511, nullptr, "ReadPalmaUniqueCode"},
274 {512, nullptr, "ReadPalmaUniqueCode"}, 277 {512, nullptr, "SetPalmaUniqueCodeInvalid"},
275 {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"},
276 {1000, nullptr, "SetNpadCommunicationMode"}, 291 {1000, nullptr, "SetNpadCommunicationMode"},
277 {1001, nullptr, "GetNpadCommunicationMode"}, 292 {1001, nullptr, "GetNpadCommunicationMode"},
278 }; 293 };
@@ -620,6 +635,7 @@ public:
620 {140, nullptr, "DeactivateConsoleSixAxisSensor"}, 635 {140, nullptr, "DeactivateConsoleSixAxisSensor"},
621 {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"}, 636 {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
622 {142, nullptr, "DeactivateSevenSixAxisSensor"}, 637 {142, nullptr, "DeactivateSevenSixAxisSensor"},
638 {143, nullptr, "GetConsoleSixAxisSensorCountStates"},
623 {201, nullptr, "ActivateFirmwareUpdate"}, 639 {201, nullptr, "ActivateFirmwareUpdate"},
624 {202, nullptr, "DeactivateFirmwareUpdate"}, 640 {202, nullptr, "DeactivateFirmwareUpdate"},
625 {203, nullptr, "StartFirmwareUpdate"}, 641 {203, nullptr, "StartFirmwareUpdate"},
@@ -630,12 +646,23 @@ public:
630 {208, nullptr, "StartFirmwareUpdateForRevert"}, 646 {208, nullptr, "StartFirmwareUpdateForRevert"},
631 {209, nullptr, "GetAvailableFirmwareVersionForRevert"}, 647 {209, nullptr, "GetAvailableFirmwareVersionForRevert"},
632 {210, nullptr, "IsFirmwareUpdatingDevice"}, 648 {210, nullptr, "IsFirmwareUpdatingDevice"},
649 {211, nullptr, "StartFirmwareUpdateIndividual"},
650 {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
651 {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
633 {221, nullptr, "UpdateControllerColor"}, 652 {221, nullptr, "UpdateControllerColor"},
634 {222, nullptr, "ConnectUsbPadsAsync"}, 653 {222, nullptr, "ConnectUsbPadsAsync"},
635 {223, nullptr, "DisconnectUsbPadsAsync"}, 654 {223, nullptr, "DisconnectUsbPadsAsync"},
636 {224, nullptr, "UpdateDesignInfo"}, 655 {224, nullptr, "UpdateDesignInfo"},
637 {225, nullptr, "GetUniquePadDriverState"}, 656 {225, nullptr, "GetUniquePadDriverState"},
638 {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"},
639 {301, nullptr, "GetAbstractedPadHandles"}, 666 {301, nullptr, "GetAbstractedPadHandles"},
640 {302, nullptr, "GetAbstractedPadState"}, 667 {302, nullptr, "GetAbstractedPadState"},
641 {303, nullptr, "GetAbstractedPadsState"}, 668 {303, nullptr, "GetAbstractedPadsState"},
@@ -643,6 +670,8 @@ public:
643 {322, nullptr, "UnsetAutoPilotVirtualPadState"}, 670 {322, nullptr, "UnsetAutoPilotVirtualPadState"},
644 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, 671 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
645 {350, nullptr, "AddRegisteredDevice"}, 672 {350, nullptr, "AddRegisteredDevice"},
673 {400, nullptr, "DisableExternalMcuOnNxDevice"},
674 {401, nullptr, "DisableRailDeviceFiltering"},
646 }; 675 };
647 // clang-format on 676 // clang-format on
648 677
@@ -678,7 +707,9 @@ public:
678 {307, nullptr, "GetNpadSystemExtStyle"}, 707 {307, nullptr, "GetNpadSystemExtStyle"},
679 {308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, 708 {308, nullptr, "ApplyNpadSystemCommonPolicyFull"},
680 {309, nullptr, "GetNpadFullKeyGripColor"}, 709 {309, nullptr, "GetNpadFullKeyGripColor"},
710 {310, nullptr, "GetMaskedSupportedNpadStyleSet"},
681 {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, 711 {311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
712 {312, nullptr, "SetSupportedNpadStyleSetAll"},
682 {321, nullptr, "GetUniquePadsFromNpad"}, 713 {321, nullptr, "GetUniquePadsFromNpad"},
683 {322, nullptr, "GetIrSensorState"}, 714 {322, nullptr, "GetIrSensorState"},
684 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, 715 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
@@ -703,6 +734,7 @@ public:
703 {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"}, 734 {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"},
704 {547, nullptr, "GetAllowedBluetoothLinksCount"}, 735 {547, nullptr, "GetAllowedBluetoothLinksCount"},
705 {548, nullptr, "GetRegisteredDevices"}, 736 {548, nullptr, "GetRegisteredDevices"},
737 {549, nullptr, "GetConnectableRegisteredDevices"},
706 {700, nullptr, "ActivateUniquePad"}, 738 {700, nullptr, "ActivateUniquePad"},
707 {702, nullptr, "AcquireUniquePadConnectionEventHandle"}, 739 {702, nullptr, "AcquireUniquePadConnectionEventHandle"},
708 {703, nullptr, "GetUniquePadIds"}, 740 {703, nullptr, "GetUniquePadIds"},
@@ -731,6 +763,7 @@ public:
731 {850, nullptr, "IsUsbFullKeyControllerEnabled"}, 763 {850, nullptr, "IsUsbFullKeyControllerEnabled"},
732 {851, nullptr, "EnableUsbFullKeyController"}, 764 {851, nullptr, "EnableUsbFullKeyController"},
733 {852, nullptr, "IsUsbConnected"}, 765 {852, nullptr, "IsUsbConnected"},
766 {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
734 {900, nullptr, "ActivateInputDetector"}, 767 {900, nullptr, "ActivateInputDetector"},
735 {901, nullptr, "NotifyInputDetector"}, 768 {901, nullptr, "NotifyInputDetector"},
736 {1000, nullptr, "InitializeFirmwareUpdate"}, 769 {1000, nullptr, "InitializeFirmwareUpdate"},
@@ -750,6 +783,12 @@ public:
750 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, 783 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
751 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, 784 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
752 {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"},
753 }; 792 };
754 // clang-format on 793 // clang-format on
755 794
@@ -788,6 +827,7 @@ public:
788 {11, nullptr, "EnableJoyPollingReceiveMode"}, 827 {11, nullptr, "EnableJoyPollingReceiveMode"},
789 {12, nullptr, "DisableJoyPollingReceiveMode"}, 828 {12, nullptr, "DisableJoyPollingReceiveMode"},
790 {13, nullptr, "GetPollingData"}, 829 {13, nullptr, "GetPollingData"},
830 {14, nullptr, "SetStatusManagerType"},
791 }; 831 };
792 // clang-format on 832 // clang-format on
793 833
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/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index ec32faf15..d607d985e 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -3,9 +3,13 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include <fmt/format.h>
6 7
8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/process.h"
7#include "core/hle/service/ldr/ldr.h" 10#include "core/hle/service/ldr/ldr.h"
8#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
12#include "core/loader/nro.h"
9 13
10namespace Service::LDR { 14namespace Service::LDR {
11 15
@@ -59,16 +63,58 @@ public:
59 explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { 63 explicit RelocatableObject() : ServiceFramework{"ldr:ro"} {
60 // clang-format off 64 // clang-format off
61 static const FunctionInfo functions[] = { 65 static const FunctionInfo functions[] = {
62 {0, nullptr, "LoadNro"}, 66 {0, &RelocatableObject::LoadNro, "LoadNro"},
63 {1, nullptr, "UnloadNro"}, 67 {1, nullptr, "UnloadNro"},
64 {2, nullptr, "LoadNrr"}, 68 {2, &RelocatableObject::LoadNrr, "LoadNrr"},
65 {3, nullptr, "UnloadNrr"}, 69 {3, nullptr, "UnloadNrr"},
66 {4, nullptr, "Initialize"}, 70 {4, &RelocatableObject::Initialize, "Initialize"},
67 }; 71 };
68 // clang-format on 72 // clang-format on
69 73
70 RegisterHandlers(functions); 74 RegisterHandlers(functions);
71 } 75 }
76
77 void LoadNrr(Kernel::HLERequestContext& ctx) {
78 IPC::ResponseBuilder rb{ctx, 2};
79 rb.Push(RESULT_SUCCESS);
80 LOG_WARNING(Service_LDR, "(STUBBED) called");
81 }
82
83 void LoadNro(Kernel::HLERequestContext& ctx) {
84 IPC::RequestParser rp{ctx};
85 rp.Skip(2, false);
86 const VAddr nro_addr{rp.Pop<VAddr>()};
87 const u64 nro_size{rp.Pop<u64>()};
88 const VAddr bss_addr{rp.Pop<VAddr>()};
89 const u64 bss_size{rp.Pop<u64>()};
90
91 // Read NRO data from memory
92 std::vector<u8> nro_data(nro_size);
93 Memory::ReadBlock(nro_addr, nro_data.data(), nro_size);
94
95 // Load NRO as new executable module
96 const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)};
97 Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr);
98
99 // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party.
100 // It is currently missing:
101 // - Signature checks with LoadNRR
102 // - Checking if a module has already been loaded
103 // - Using/validating BSS, etc. params (these are used from NRO header instead)
104 // - Error checking
105 // - ...Probably other things
106
107 IPC::ResponseBuilder rb{ctx, 4};
108 rb.Push(RESULT_SUCCESS);
109 rb.Push(addr);
110 LOG_WARNING(Service_LDR, "(STUBBED) called");
111 }
112
113 void Initialize(Kernel::HLERequestContext& ctx) {
114 IPC::ResponseBuilder rb{ctx, 2};
115 rb.Push(RESULT_SUCCESS);
116 LOG_WARNING(Service_LDR, "(STUBBED) called");
117 }
72}; 118};
73 119
74void InstallInterfaces(SM::ServiceManager& sm) { 120void InstallInterfaces(SM::ServiceManager& sm) {
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index 8fec97db8..30e542542 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -10,12 +10,13 @@
10#include "core/hle/service/nfc/nfc.h" 10#include "core/hle/service/nfc/nfc.h"
11#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h" 12#include "core/hle/service/sm/sm.h"
13#include "core/settings.h"
13 14
14namespace Service::NFC { 15namespace Service::NFC {
15 16
16class IAm final : public ServiceFramework<IAm> { 17class IAm final : public ServiceFramework<IAm> {
17public: 18public:
18 explicit IAm() : ServiceFramework{"IAm"} { 19 explicit IAm() : ServiceFramework{"NFC::IAm"} {
19 // clang-format off 20 // clang-format off
20 static const FunctionInfo functions[] = { 21 static const FunctionInfo functions[] = {
21 {0, nullptr, "Initialize"}, 22 {0, nullptr, "Initialize"},
@@ -52,7 +53,7 @@ private:
52 53
53class MFIUser final : public ServiceFramework<MFIUser> { 54class MFIUser final : public ServiceFramework<MFIUser> {
54public: 55public:
55 explicit MFIUser() : ServiceFramework{"IUser"} { 56 explicit MFIUser() : ServiceFramework{"NFC::MFIUser"} {
56 // clang-format off 57 // clang-format off
57 static const FunctionInfo functions[] = { 58 static const FunctionInfo functions[] = {
58 {0, nullptr, "Initialize"}, 59 {0, nullptr, "Initialize"},
@@ -100,13 +101,13 @@ private:
100 101
101class IUser final : public ServiceFramework<IUser> { 102class IUser final : public ServiceFramework<IUser> {
102public: 103public:
103 explicit IUser() : ServiceFramework{"IUser"} { 104 explicit IUser() : ServiceFramework{"NFC::IUser"} {
104 // clang-format off 105 // clang-format off
105 static const FunctionInfo functions[] = { 106 static const FunctionInfo functions[] = {
106 {0, nullptr, "Initialize"}, 107 {0, &IUser::InitializeOld, "InitializeOld"},
107 {1, nullptr, "Finalize"}, 108 {1, &IUser::FinalizeOld, "FinalizeOld"},
108 {2, nullptr, "GetState"}, 109 {2, &IUser::GetStateOld, "GetStateOld"},
109 {3, nullptr, "IsNfcEnabled"}, 110 {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"},
110 {400, nullptr, "Initialize"}, 111 {400, nullptr, "Initialize"},
111 {401, nullptr, "Finalize"}, 112 {401, nullptr, "Finalize"},
112 {402, nullptr, "GetState"}, 113 {402, nullptr, "GetState"},
@@ -130,11 +131,47 @@ public:
130 131
131 RegisterHandlers(functions); 132 RegisterHandlers(functions);
132 } 133 }
134
135private:
136 enum class NfcStates : u32 {
137 Finalized = 6,
138 };
139
140 void InitializeOld(Kernel::HLERequestContext& ctx) {
141 IPC::ResponseBuilder rb{ctx, 2, 0};
142 rb.Push(RESULT_SUCCESS);
143
144 // We don't deal with hardware initialization so we can just stub this.
145 LOG_DEBUG(Service_NFC, "called");
146 }
147
148 void IsNfcEnabledOld(Kernel::HLERequestContext& ctx) {
149 IPC::ResponseBuilder rb{ctx, 3};
150 rb.Push(RESULT_SUCCESS);
151 rb.PushRaw<u8>(Settings::values.enable_nfc);
152
153 LOG_DEBUG(Service_NFC, "IsNfcEnabledOld");
154 }
155
156 void GetStateOld(Kernel::HLERequestContext& ctx) {
157 LOG_WARNING(Service_NFC, "(STUBBED) called");
158
159 IPC::ResponseBuilder rb{ctx, 3};
160 rb.Push(RESULT_SUCCESS);
161 rb.PushEnum(NfcStates::Finalized); // TODO(ogniK): Figure out if this matches nfp
162 }
163
164 void FinalizeOld(Kernel::HLERequestContext& ctx) {
165 LOG_WARNING(Service_NFC, "(STUBBED) called");
166
167 IPC::ResponseBuilder rb{ctx, 2};
168 rb.Push(RESULT_SUCCESS);
169 }
133}; 170};
134 171
135class NFC_U final : public ServiceFramework<NFC_U> { 172class NFC_U final : public ServiceFramework<NFC_U> {
136public: 173public:
137 explicit NFC_U() : ServiceFramework{"nfc:u"} { 174 explicit NFC_U() : ServiceFramework{"nfc:user"} {
138 // clang-format off 175 // clang-format off
139 static const FunctionInfo functions[] = { 176 static const FunctionInfo functions[] = {
140 {0, &NFC_U::CreateUserInterface, "CreateUserInterface"}, 177 {0, &NFC_U::CreateUserInterface, "CreateUserInterface"},
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 39c0c1e63..c1af878fe 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -2,56 +2,67 @@
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 <atomic>
6
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/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
8#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
11#include "core/hle/lock.h"
9#include "core/hle/service/hid/hid.h" 12#include "core/hle/service/hid/hid.h"
10#include "core/hle/service/nfp/nfp.h" 13#include "core/hle/service/nfp/nfp.h"
11#include "core/hle/service/nfp/nfp_user.h" 14#include "core/hle/service/nfp/nfp_user.h"
12 15
13namespace Service::NFP { 16namespace Service::NFP {
14 17
18namespace ErrCodes {
19constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
20 -1); // TODO(ogniK): Find the actual error code
21}
22
15Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 23Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
16 : ServiceFramework(name), module(std::move(module)) {} 24 : ServiceFramework(name), module(std::move(module)) {
25 auto& kernel = Core::System::GetInstance().Kernel();
26 nfc_tag_load =
27 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:NFCTagDetected");
28}
17 29
18Module::Interface::~Interface() = default; 30Module::Interface::~Interface() = default;
19 31
20class IUser final : public ServiceFramework<IUser> { 32class IUser final : public ServiceFramework<IUser> {
21public: 33public:
22 IUser() : ServiceFramework("IUser") { 34 IUser(Module::Interface& nfp_interface)
35 : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) {
23 static const FunctionInfo functions[] = { 36 static const FunctionInfo functions[] = {
24 {0, &IUser::Initialize, "Initialize"}, 37 {0, &IUser::Initialize, "Initialize"},
25 {1, nullptr, "Finalize"}, 38 {1, &IUser::Finalize, "Finalize"},
26 {2, &IUser::ListDevices, "ListDevices"}, 39 {2, &IUser::ListDevices, "ListDevices"},
27 {3, nullptr, "StartDetection"}, 40 {3, &IUser::StartDetection, "StartDetection"},
28 {4, nullptr, "StopDetection"}, 41 {4, &IUser::StopDetection, "StopDetection"},
29 {5, nullptr, "Mount"}, 42 {5, &IUser::Mount, "Mount"},
30 {6, nullptr, "Unmount"}, 43 {6, &IUser::Unmount, "Unmount"},
31 {7, nullptr, "OpenApplicationArea"}, 44 {7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
32 {8, nullptr, "GetApplicationArea"}, 45 {8, &IUser::GetApplicationArea, "GetApplicationArea"},
33 {9, nullptr, "SetApplicationArea"}, 46 {9, nullptr, "SetApplicationArea"},
34 {10, nullptr, "Flush"}, 47 {10, nullptr, "Flush"},
35 {11, nullptr, "Restore"}, 48 {11, nullptr, "Restore"},
36 {12, nullptr, "CreateApplicationArea"}, 49 {12, nullptr, "CreateApplicationArea"},
37 {13, nullptr, "GetTagInfo"}, 50 {13, &IUser::GetTagInfo, "GetTagInfo"},
38 {14, nullptr, "GetRegisterInfo"}, 51 {14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
39 {15, nullptr, "GetCommonInfo"}, 52 {15, &IUser::GetCommonInfo, "GetCommonInfo"},
40 {16, nullptr, "GetModelInfo"}, 53 {16, &IUser::GetModelInfo, "GetModelInfo"},
41 {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, 54 {17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
42 {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, 55 {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
43 {19, &IUser::GetState, "GetState"}, 56 {19, &IUser::GetState, "GetState"},
44 {20, &IUser::GetDeviceState, "GetDeviceState"}, 57 {20, &IUser::GetDeviceState, "GetDeviceState"},
45 {21, &IUser::GetNpadId, "GetNpadId"}, 58 {21, &IUser::GetNpadId, "GetNpadId"},
46 {22, nullptr, "GetApplicationArea2"}, 59 {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
47 {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, 60 {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
48 {24, nullptr, "RecreateApplicationArea"}, 61 {24, nullptr, "RecreateApplicationArea"},
49 }; 62 };
50 RegisterHandlers(functions); 63 RegisterHandlers(functions);
51 64
52 auto& kernel = Core::System::GetInstance().Kernel(); 65 auto& kernel = Core::System::GetInstance().Kernel();
53 activate_event =
54 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:ActivateEvent");
55 deactivate_event = 66 deactivate_event =
56 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent"); 67 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
57 availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, 68 availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
@@ -59,6 +70,17 @@ public:
59 } 70 }
60 71
61private: 72private:
73 struct TagInfo {
74 std::array<u8, 10> uuid;
75 u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it
76 // mean something else
77 INSERT_PADDING_BYTES(0x15);
78 u32_le protocol;
79 u32_le tag_type;
80 INSERT_PADDING_BYTES(0x2c);
81 };
82 static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size");
83
62 enum class State : u32 { 84 enum class State : u32 {
63 NonInitialized = 0, 85 NonInitialized = 0,
64 Initialized = 1, 86 Initialized = 1,
@@ -66,15 +88,40 @@ private:
66 88
67 enum class DeviceState : u32 { 89 enum class DeviceState : u32 {
68 Initialized = 0, 90 Initialized = 0,
91 SearchingForTag = 1,
92 TagFound = 2,
93 TagRemoved = 3,
94 TagNearby = 4,
95 Unknown5 = 5,
96 Finalized = 6
97 };
98
99 struct CommonInfo {
100 u16_be last_write_year;
101 u8 last_write_month;
102 u8 last_write_day;
103 u16_be write_counter;
104 u16_be version;
105 u32_be application_area_size;
106 INSERT_PADDING_BYTES(0x34);
69 }; 107 };
108 static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
70 109
71 void Initialize(Kernel::HLERequestContext& ctx) { 110 void Initialize(Kernel::HLERequestContext& ctx) {
72 LOG_WARNING(Service_NFP, "(STUBBED) called"); 111 IPC::ResponseBuilder rb{ctx, 2, 0};
112 rb.Push(RESULT_SUCCESS);
73 113
74 state = State::Initialized; 114 state = State::Initialized;
75 115
76 IPC::ResponseBuilder rb{ctx, 2}; 116 LOG_DEBUG(Service_NFC, "called");
117 }
118
119 void GetState(Kernel::HLERequestContext& ctx) {
120 IPC::ResponseBuilder rb{ctx, 3, 0};
77 rb.Push(RESULT_SUCCESS); 121 rb.Push(RESULT_SUCCESS);
122 rb.PushRaw<u32>(static_cast<u32>(state));
123
124 LOG_DEBUG(Service_NFC, "called");
78 } 125 }
79 126
80 void ListDevices(Kernel::HLERequestContext& ctx) { 127 void ListDevices(Kernel::HLERequestContext& ctx) {
@@ -83,80 +130,219 @@ private:
83 130
84 ctx.WriteBuffer(&device_handle, sizeof(device_handle)); 131 ctx.WriteBuffer(&device_handle, sizeof(device_handle));
85 132
86 LOG_WARNING(Service_NFP, "(STUBBED) called, array_size={}", array_size); 133 LOG_DEBUG(Service_NFP, "called, array_size={}", array_size);
87 134
88 IPC::ResponseBuilder rb{ctx, 3}; 135 IPC::ResponseBuilder rb{ctx, 3};
89 rb.Push(RESULT_SUCCESS); 136 rb.Push(RESULT_SUCCESS);
90 rb.Push<u32>(0); 137 rb.Push<u32>(1);
91 } 138 }
92 139
93 void AttachActivateEvent(Kernel::HLERequestContext& ctx) { 140 void GetNpadId(Kernel::HLERequestContext& ctx) {
94 IPC::RequestParser rp{ctx}; 141 IPC::RequestParser rp{ctx};
95 const u64 dev_handle = rp.Pop<u64>(); 142 const u64 dev_handle = rp.Pop<u64>();
96 LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); 143 LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
144 IPC::ResponseBuilder rb{ctx, 3};
145 rb.Push(RESULT_SUCCESS);
146 rb.Push<u32>(npad_id);
147 }
97 148
149 void AttachActivateEvent(Kernel::HLERequestContext& ctx) {
150 IPC::RequestParser rp{ctx};
151 const u64 dev_handle = rp.Pop<u64>();
152 LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
98 IPC::ResponseBuilder rb{ctx, 2, 1}; 153 IPC::ResponseBuilder rb{ctx, 2, 1};
99 rb.Push(RESULT_SUCCESS); 154 rb.Push(RESULT_SUCCESS);
100 rb.PushCopyObjects(activate_event); 155 rb.PushCopyObjects(nfp_interface.GetNFCEvent());
156 has_attached_handle = true;
101 } 157 }
102 158
103 void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { 159 void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
104 IPC::RequestParser rp{ctx}; 160 IPC::RequestParser rp{ctx};
105 const u64 dev_handle = rp.Pop<u64>(); 161 const u64 dev_handle = rp.Pop<u64>();
106 LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); 162 LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
107 163
108 IPC::ResponseBuilder rb{ctx, 2, 1}; 164 IPC::ResponseBuilder rb{ctx, 2, 1};
109 rb.Push(RESULT_SUCCESS); 165 rb.Push(RESULT_SUCCESS);
110 rb.PushCopyObjects(deactivate_event); 166 rb.PushCopyObjects(deactivate_event);
111 } 167 }
112 168
113 void GetState(Kernel::HLERequestContext& ctx) { 169 void StopDetection(Kernel::HLERequestContext& ctx) {
114 LOG_WARNING(Service_NFP, "(STUBBED) called"); 170 LOG_DEBUG(Service_NFP, "called");
115 IPC::ResponseBuilder rb{ctx, 3}; 171 switch (device_state) {
172 case DeviceState::TagFound:
173 case DeviceState::TagNearby:
174 deactivate_event->Signal();
175 device_state = DeviceState::Initialized;
176 break;
177 case DeviceState::SearchingForTag:
178 case DeviceState::TagRemoved:
179 device_state = DeviceState::Initialized;
180 break;
181 }
182 IPC::ResponseBuilder rb{ctx, 2};
116 rb.Push(RESULT_SUCCESS); 183 rb.Push(RESULT_SUCCESS);
117 rb.Push<u32>(static_cast<u32>(state));
118 } 184 }
119 185
120 void GetDeviceState(Kernel::HLERequestContext& ctx) { 186 void GetDeviceState(Kernel::HLERequestContext& ctx) {
121 LOG_WARNING(Service_NFP, "(STUBBED) called"); 187 LOG_DEBUG(Service_NFP, "called");
188 auto nfc_event = nfp_interface.GetNFCEvent();
189 if (!nfc_event->ShouldWait(Kernel::GetCurrentThread()) && !has_attached_handle) {
190 device_state = DeviceState::TagFound;
191 nfc_event->Clear();
192 }
193
122 IPC::ResponseBuilder rb{ctx, 3}; 194 IPC::ResponseBuilder rb{ctx, 3};
123 rb.Push(RESULT_SUCCESS); 195 rb.Push(RESULT_SUCCESS);
124 rb.Push<u32>(static_cast<u32>(device_state)); 196 rb.Push<u32>(static_cast<u32>(device_state));
125 } 197 }
126 198
127 void GetNpadId(Kernel::HLERequestContext& ctx) { 199 void StartDetection(Kernel::HLERequestContext& ctx) {
128 IPC::RequestParser rp{ctx}; 200 LOG_DEBUG(Service_NFP, "called");
129 const u64 dev_handle = rp.Pop<u64>(); 201
130 LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); 202 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
131 IPC::ResponseBuilder rb{ctx, 3}; 203 device_state = DeviceState::SearchingForTag;
204 }
205 IPC::ResponseBuilder rb{ctx, 2};
206 rb.Push(RESULT_SUCCESS);
207 }
208
209 void GetTagInfo(Kernel::HLERequestContext& ctx) {
210 LOG_DEBUG(Service_NFP, "called");
211
212 IPC::ResponseBuilder rb{ctx, 2};
213 auto amiibo = nfp_interface.GetAmiiboBuffer();
214 TagInfo tag_info{};
215 std::memcpy(tag_info.uuid.data(), amiibo.uuid.data(), sizeof(tag_info.uuid.size()));
216 tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size());
217
218 tag_info.protocol = 1; // TODO(ogniK): Figure out actual values
219 tag_info.tag_type = 2;
220 ctx.WriteBuffer(&tag_info, sizeof(TagInfo));
221 rb.Push(RESULT_SUCCESS);
222 }
223
224 void Mount(Kernel::HLERequestContext& ctx) {
225 LOG_DEBUG(Service_NFP, "called");
226
227 device_state = DeviceState::TagNearby;
228 IPC::ResponseBuilder rb{ctx, 2};
229 rb.Push(RESULT_SUCCESS);
230 }
231
232 void GetModelInfo(Kernel::HLERequestContext& ctx) {
233 LOG_DEBUG(Service_NFP, "called");
234
235 IPC::ResponseBuilder rb{ctx, 2};
236 auto amiibo = nfp_interface.GetAmiiboBuffer();
237 ctx.WriteBuffer(&amiibo.model_info, sizeof(amiibo.model_info));
238 rb.Push(RESULT_SUCCESS);
239 }
240
241 void Unmount(Kernel::HLERequestContext& ctx) {
242 LOG_DEBUG(Service_NFP, "called");
243
244 device_state = DeviceState::TagFound;
245
246 IPC::ResponseBuilder rb{ctx, 2};
247 rb.Push(RESULT_SUCCESS);
248 }
249
250 void Finalize(Kernel::HLERequestContext& ctx) {
251 LOG_DEBUG(Service_NFP, "called");
252
253 device_state = DeviceState::Finalized;
254
255 IPC::ResponseBuilder rb{ctx, 2};
132 rb.Push(RESULT_SUCCESS); 256 rb.Push(RESULT_SUCCESS);
133 rb.Push<u32>(npad_id);
134 } 257 }
135 258
136 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { 259 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
137 IPC::RequestParser rp{ctx}; 260 LOG_WARNING(Service_NFP, "(STUBBED) called");
138 const u64 dev_handle = rp.Pop<u64>();
139 LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
140 261
141 IPC::ResponseBuilder rb{ctx, 2, 1}; 262 IPC::ResponseBuilder rb{ctx, 2, 1};
142 rb.Push(RESULT_SUCCESS); 263 rb.Push(RESULT_SUCCESS);
143 rb.PushCopyObjects(availability_change_event); 264 rb.PushCopyObjects(availability_change_event);
144 } 265 }
145 266
146 const u64 device_handle{0xDEAD}; 267 void GetRegisterInfo(Kernel::HLERequestContext& ctx) {
147 const u32 npad_id{0}; // This is the first player controller id 268 LOG_WARNING(Service_NFP, "(STUBBED) called");
269
270 // TODO(ogniK): Pull Mii and owner data from amiibo
271
272 IPC::ResponseBuilder rb{ctx, 2};
273 rb.Push(RESULT_SUCCESS);
274 }
275
276 void GetCommonInfo(Kernel::HLERequestContext& ctx) {
277 LOG_WARNING(Service_NFP, "(STUBBED) called");
278
279 // TODO(ogniK): Pull common information from amiibo
280
281 CommonInfo common_info{};
282 common_info.application_area_size = 0;
283 ctx.WriteBuffer(&common_info, sizeof(CommonInfo));
284
285 IPC::ResponseBuilder rb{ctx, 2};
286 rb.Push(RESULT_SUCCESS);
287 }
288
289 void OpenApplicationArea(Kernel::HLERequestContext& ctx) {
290 LOG_DEBUG(Service_NFP, "called");
291 // We don't need to worry about this since we can just open the file
292 IPC::ResponseBuilder rb{ctx, 2};
293 rb.Push(RESULT_SUCCESS);
294 }
295
296 void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
297 LOG_WARNING(Service_NFP, "(STUBBED) called");
298 // We don't need to worry about this since we can just open the file
299 IPC::ResponseBuilder rb{ctx, 3};
300 rb.Push(RESULT_SUCCESS);
301 rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub
302 }
303
304 void GetApplicationArea(Kernel::HLERequestContext& ctx) {
305 LOG_WARNING(Service_NFP, "(STUBBED) called");
306
307 // TODO(ogniK): Pull application area from amiibo
308
309 IPC::ResponseBuilder rb{ctx, 3};
310 rb.Push(RESULT_SUCCESS);
311 rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub
312 }
313
314 bool has_attached_handle{};
315 const u64 device_handle{Common::MakeMagic('Y', 'U', 'Z', 'U')};
316 const u32 npad_id{0}; // Player 1 controller
148 State state{State::NonInitialized}; 317 State state{State::NonInitialized};
149 DeviceState device_state{DeviceState::Initialized}; 318 DeviceState device_state{DeviceState::Initialized};
150 Kernel::SharedPtr<Kernel::Event> activate_event;
151 Kernel::SharedPtr<Kernel::Event> deactivate_event; 319 Kernel::SharedPtr<Kernel::Event> deactivate_event;
152 Kernel::SharedPtr<Kernel::Event> availability_change_event; 320 Kernel::SharedPtr<Kernel::Event> availability_change_event;
321 const Module::Interface& nfp_interface;
153}; 322};
154 323
155void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { 324void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
156 LOG_DEBUG(Service_NFP, "called"); 325 LOG_DEBUG(Service_NFP, "called");
157 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 326 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
158 rb.Push(RESULT_SUCCESS); 327 rb.Push(RESULT_SUCCESS);
159 rb.PushIpcInterface<IUser>(); 328 rb.PushIpcInterface<IUser>(*this);
329}
330
331bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
332 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
333 if (buffer.size() < sizeof(AmiiboFile)) {
334 return false;
335 }
336
337 std::memcpy(&amiibo, buffer.data(), sizeof(amiibo));
338 nfc_tag_load->Signal();
339 return true;
340}
341const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const {
342 return nfc_tag_load;
343}
344const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const {
345 return amiibo;
160} 346}
161 347
162void InstallInterfaces(SM::ServiceManager& service_manager) { 348void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 77df343c4..5c0ae8a54 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -4,6 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <vector>
9#include "core/hle/kernel/event.h"
7#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
8 11
9namespace Service::NFP { 12namespace Service::NFP {
@@ -15,7 +18,27 @@ public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 18 explicit Interface(std::shared_ptr<Module> module, const char* name);
16 ~Interface() override; 19 ~Interface() override;
17 20
21 struct ModelInfo {
22 std::array<u8, 0x8> amiibo_identification_block;
23 INSERT_PADDING_BYTES(0x38);
24 };
25 static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
26
27 struct AmiiboFile {
28 std::array<u8, 10> uuid;
29 INSERT_PADDING_BYTES(0x4a);
30 ModelInfo model_info;
31 };
32 static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size");
33
18 void CreateUserInterface(Kernel::HLERequestContext& ctx); 34 void CreateUserInterface(Kernel::HLERequestContext& ctx);
35 bool LoadAmiibo(const std::vector<u8>& buffer);
36 const Kernel::SharedPtr<Kernel::Event>& GetNFCEvent() const;
37 const AmiiboFile& GetAmiiboBuffer() const;
38
39 private:
40 Kernel::SharedPtr<Kernel::Event> nfc_tag_load{};
41 AmiiboFile amiibo{};
19 42
20 protected: 43 protected:
21 std::shared_ptr<Module> module; 44 std::shared_ptr<Module> module;
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/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp
index e7fb5a419..c489da071 100644
--- a/src/core/hle/service/usb/usb.cpp
+++ b/src/core/hle/service/usb/usb.cpp
@@ -67,15 +67,15 @@ public:
67 explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} { 67 explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} {
68 // clang-format off 68 // clang-format off
69 static const FunctionInfo functions[] = { 69 static const FunctionInfo functions[] = {
70 {0, nullptr, "Unknown1"}, 70 {0, nullptr, "Open"},
71 {1, nullptr, "Unknown2"}, 71 {1, nullptr, "Close"},
72 {2, nullptr, "Unknown3"}, 72 {2, nullptr, "Unknown1"},
73 {3, nullptr, "Unknown4"}, 73 {3, nullptr, "Populate"},
74 {4, nullptr, "PostBufferAsync"}, 74 {4, nullptr, "PostBufferAsync"},
75 {5, nullptr, "Unknown5"}, 75 {5, nullptr, "GetXferReport"},
76 {6, nullptr, "Unknown6"}, 76 {6, nullptr, "Unknown2"},
77 {7, nullptr, "Unknown7"}, 77 {7, nullptr, "Unknown3"},
78 {8, nullptr, "Unknown8"}, 78 {8, nullptr, "Unknown4"},
79 }; 79 };
80 // clang-format on 80 // clang-format on
81 81
@@ -89,15 +89,15 @@ public:
89 // clang-format off 89 // clang-format off
90 static const FunctionInfo functions[] = { 90 static const FunctionInfo functions[] = {
91 {0, nullptr, "Unknown1"}, 91 {0, nullptr, "Unknown1"},
92 {1, nullptr, "Unknown2"}, 92 {1, nullptr, "SetInterface"},
93 {2, nullptr, "Unknown3"}, 93 {2, nullptr, "GetInterface"},
94 {3, nullptr, "Unknown4"}, 94 {3, nullptr, "GetAlternateInterface"},
95 {4, nullptr, "Unknown5"}, 95 {4, nullptr, "GetCurrentFrame"},
96 {5, nullptr, "CtrlXferAsync"}, 96 {5, nullptr, "CtrlXferAsync"},
97 {6, nullptr, "Unknown6"}, 97 {6, nullptr, "Unknown2"},
98 {7, nullptr, "GetCtrlXferReport"}, 98 {7, nullptr, "GetCtrlXferReport"},
99 {8, nullptr, "Unknown7"}, 99 {8, nullptr, "ResetDevice"},
100 {9, nullptr, "GetClientEpSession"}, 100 {9, nullptr, "OpenUsbEp"},
101 }; 101 };
102 // clang-format on 102 // clang-format on
103 103
@@ -111,13 +111,14 @@ public:
111 // clang-format off 111 // clang-format off
112 static const FunctionInfo functions[] = { 112 static const FunctionInfo functions[] = {
113 {0, nullptr, "BindClientProcess"}, 113 {0, nullptr, "BindClientProcess"},
114 {1, nullptr, "Unknown1"}, 114 {1, nullptr, "QueryAllInterfaces"},
115 {2, nullptr, "Unknown2"}, 115 {2, nullptr, "QueryAvailableInterfaces"},
116 {3, nullptr, "Unknown3"}, 116 {3, nullptr, "QueryAcquiredInterfaces"},
117 {4, nullptr, "Unknown4"}, 117 {4, nullptr, "CreateInterfaceAvailableEvent"},
118 {5, nullptr, "Unknown5"}, 118 {5, nullptr, "DestroyInterfaceAvailableEvent"},
119 {6, nullptr, "GetInterfaceStateChangeEvent"}, 119 {6, nullptr, "GetInterfaceStateChangeEvent"},
120 {7, nullptr, "GetClientIfSession"}, 120 {7, nullptr, "AcquireUsbIf"},
121 {8, nullptr, "Unknown1"},
121 }; 122 };
122 // clang-format on 123 // clang-format on
123 124
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 243b499f2..bc8e402a8 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -127,18 +127,23 @@ static constexpr u32 PageAlignSize(u32 size) {
127 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 127 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
128} 128}
129 129
130bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { 130/*static*/ bool AppLoader_NRO::LoadNro(const std::vector<u8>& data, const std::string& name,
131 // Read NSO header 131 VAddr load_base) {
132 NroHeader nro_header{}; 132
133 if (sizeof(NroHeader) != file.ReadObject(&nro_header)) { 133 if (data.size() < sizeof(NroHeader)) {
134 return {}; 134 return {};
135 } 135 }
136
137 // Read NSO header
138 NroHeader nro_header{};
139 std::memcpy(&nro_header, data.data(), sizeof(NroHeader));
136 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { 140 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
137 return {}; 141 return {};
138 } 142 }
139 143
140 // Build program image 144 // Build program image
141 std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size)); 145 std::vector<u8> program_image(PageAlignSize(nro_header.file_size));
146 std::memcpy(program_image.data(), data.data(), program_image.size());
142 if (program_image.size() != PageAlignSize(nro_header.file_size)) { 147 if (program_image.size() != PageAlignSize(nro_header.file_size)) {
143 return {}; 148 return {};
144 } 149 }
@@ -182,11 +187,15 @@ bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
182 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); 187 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
183 188
184 // Register module with GDBStub 189 // Register module with GDBStub
185 GDBStub::RegisterModule(file.GetName(), load_base, load_base); 190 GDBStub::RegisterModule(name, load_base, load_base);
186 191
187 return true; 192 return true;
188} 193}
189 194
195bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
196 return AppLoader_NRO::LoadNro(file.ReadAllBytes(), file.GetName(), load_base);
197}
198
190ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { 199ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
191 if (is_loaded) { 200 if (is_loaded) {
192 return ResultStatus::ErrorAlreadyLoaded; 201 return ResultStatus::ErrorAlreadyLoaded;
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 50ee5a78a..3e6959302 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <string> 7#include <string>
8#include <vector>
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "core/loader/linker.h" 10#include "core/loader/linker.h"
10#include "core/loader/loader.h" 11#include "core/loader/loader.h"
@@ -40,6 +41,8 @@ public:
40 ResultStatus ReadTitle(std::string& title) override; 41 ResultStatus ReadTitle(std::string& title) override;
41 bool IsRomFSUpdatable() const override; 42 bool IsRomFSUpdatable() const override;
42 43
44 static bool LoadNro(const std::vector<u8>& data, const std::string& name, VAddr load_base);
45
43private: 46private:
44 bool LoadNro(const FileSys::VfsFile& file, VAddr load_base); 47 bool LoadNro(const FileSys::VfsFile& file, VAddr load_base);
45 48
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 7d95816fe..c716a462b 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -74,10 +74,6 @@ double PerfStats::GetLastFrameTimeScale() {
74} 74}
75 75
76void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { 76void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
77 // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher
78 // values increase the time needed to recover and limit framerate again after spikes.
79 constexpr microseconds MAX_LAG_TIME_US = 25000us;
80
81 if (!Settings::values.use_frame_limit) { 77 if (!Settings::values.use_frame_limit) {
82 return; 78 return;
83 } 79 }
diff --git a/src/core/settings.h b/src/core/settings.h
index 8f2da01c8..b5aeff29b 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -113,7 +113,8 @@ static const std::array<const char*, NumAnalogs> mapping = {{
113struct Values { 113struct Values {
114 // System 114 // System
115 bool use_docked_mode; 115 bool use_docked_mode;
116 std::string username; 116 bool enable_nfc;
117 int current_user;
117 int language_index; 118 int language_index;
118 119
119 // Controls 120 // Controls
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..27ef865a2 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cinttypes> 5#include <cinttypes>
6#include <cstring>
6#include "common/assert.h" 7#include "common/assert.h"
7#include "core/core.h" 8#include "core/core.h"
8#include "core/core_timing.h" 9#include "core/core_timing.h"
@@ -13,14 +14,30 @@
13#include "video_core/renderer_base.h" 14#include "video_core/renderer_base.h"
14#include "video_core/textures/texture.h" 15#include "video_core/textures/texture.h"
15 16
16namespace Tegra { 17namespace Tegra::Engines {
17namespace Engines {
18 18
19/// First register id that is actually a Macro call. 19/// First register id that is actually a Macro call.
20constexpr u32 MacroRegistersStart = 0xE00; 20constexpr u32 MacroRegistersStart = 0xE00;
21 21
22Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) 22Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
23 : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {} 23 : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {
24 InitializeRegisterDefaults();
25}
26
27void Maxwell3D::InitializeRegisterDefaults() {
28 // Initializes registers to their default values - what games expect them to be at boot. This is
29 // for certain registers that may not be explicitly set by games.
30
31 // Reset all registers to zero
32 std::memset(&regs, 0, sizeof(regs));
33
34 // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is
35 // needed for ARMS.
36 for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) {
37 regs.viewport[viewport].depth_range_near = 0.0f;
38 regs.viewport[viewport].depth_range_far = 1.0f;
39 }
40}
24 41
25void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { 42void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
26 // Reset the current macro. 43 // Reset the current macro.
@@ -156,7 +173,6 @@ void Maxwell3D::ProcessQueryGet() {
156 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, 173 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
157 "Units other than CROP are unimplemented"); 174 "Units other than CROP are unimplemented");
158 175
159 u32 value = Memory::Read32(*address);
160 u64 result = 0; 176 u64 result = 0;
161 177
162 // TODO(Subv): Support the other query variables 178 // TODO(Subv): Support the other query variables
@@ -408,5 +424,4 @@ void Maxwell3D::ProcessClearBuffers() {
408 rasterizer.Clear(); 424 rasterizer.Clear();
409} 425}
410 426
411} // namespace Engines 427} // 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 c8af1c6b6..754a149fa 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -643,8 +643,10 @@ public:
643 u32 d3d_cull_mode; 643 u32 d3d_cull_mode;
644 644
645 ComparisonOp depth_test_func; 645 ComparisonOp depth_test_func;
646 float alpha_test_ref;
647 ComparisonOp alpha_test_func;
646 648
647 INSERT_PADDING_WORDS(0xB); 649 INSERT_PADDING_WORDS(0x9);
648 650
649 struct { 651 struct {
650 u32 separate_alpha; 652 u32 separate_alpha;
@@ -982,6 +984,8 @@ public:
982 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const; 984 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const;
983 985
984private: 986private:
987 void InitializeRegisterDefaults();
988
985 VideoCore::RasterizerInterface& rasterizer; 989 VideoCore::RasterizerInterface& rasterizer;
986 990
987 std::unordered_map<u32, std::vector<u32>> uploaded_macros; 991 std::unordered_map<u32, std::vector<u32>> uploaded_macros;
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..af7756266 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -79,6 +79,7 @@ union Attribute {
79 constexpr explicit Attribute(u64 value) : value(value) {} 79 constexpr explicit Attribute(u64 value) : value(value) {}
80 80
81 enum class Index : u64 { 81 enum class Index : u64 {
82 PointSize = 6,
82 Position = 7, 83 Position = 7,
83 Attribute_0 = 8, 84 Attribute_0 = 8,
84 Attribute_31 = 39, 85 Attribute_31 = 39,
@@ -214,7 +215,7 @@ enum class IMinMaxExchange : u64 {
214 XHi = 3, 215 XHi = 3,
215}; 216};
216 217
217enum class VmadType : u64 { 218enum class VideoType : u64 {
218 Size16_Low = 0, 219 Size16_Low = 0,
219 Size16_High = 1, 220 Size16_High = 1,
220 Size32 = 2, 221 Size32 = 2,
@@ -335,6 +336,26 @@ enum class IsberdMode : u64 {
335 336
336enum class IsberdShift : u64 { None = 0, U16 = 1, B32 = 2 }; 337enum class IsberdShift : u64 { None = 0, U16 = 1, B32 = 2 };
337 338
339enum class HalfType : u64 {
340 H0_H1 = 0,
341 F32 = 1,
342 H0_H0 = 2,
343 H1_H1 = 3,
344};
345
346enum class HalfMerge : u64 {
347 H0_H1 = 0,
348 F32 = 1,
349 Mrg_H0 = 2,
350 Mrg_H1 = 3,
351};
352
353enum class HalfPrecision : u64 {
354 None = 0,
355 FTZ = 1,
356 FMZ = 2,
357};
358
338enum class IpaInterpMode : u64 { 359enum class IpaInterpMode : u64 {
339 Linear = 0, 360 Linear = 0,
340 Perspective = 1, 361 Perspective = 1,
@@ -544,6 +565,10 @@ union Instruction {
544 } fmul; 565 } fmul;
545 566
546 union { 567 union {
568 BitField<55, 1, u64> saturate;
569 } fmul32;
570
571 union {
547 BitField<48, 1, u64> is_signed; 572 BitField<48, 1, u64> is_signed;
548 } shift; 573 } shift;
549 574
@@ -554,6 +579,70 @@ union Instruction {
554 } alu_integer; 579 } alu_integer;
555 580
556 union { 581 union {
582 BitField<39, 1, u64> ftz;
583 BitField<32, 1, u64> saturate;
584 BitField<49, 2, HalfMerge> merge;
585
586 BitField<43, 1, u64> negate_a;
587 BitField<44, 1, u64> abs_a;
588 BitField<47, 2, HalfType> type_a;
589
590 BitField<31, 1, u64> negate_b;
591 BitField<30, 1, u64> abs_b;
592 BitField<47, 2, HalfType> type_b;
593
594 BitField<35, 2, HalfType> type_c;
595 } alu_half;
596
597 union {
598 BitField<39, 2, HalfPrecision> precision;
599 BitField<39, 1, u64> ftz;
600 BitField<52, 1, u64> saturate;
601 BitField<49, 2, HalfMerge> merge;
602
603 BitField<43, 1, u64> negate_a;
604 BitField<44, 1, u64> abs_a;
605 BitField<47, 2, HalfType> type_a;
606 } alu_half_imm;
607
608 union {
609 BitField<29, 1, u64> first_negate;
610 BitField<20, 9, u64> first;
611
612 BitField<56, 1, u64> second_negate;
613 BitField<30, 9, u64> second;
614
615 u32 PackImmediates() const {
616 // Immediates are half floats shifted.
617 constexpr u32 imm_shift = 6;
618 return static_cast<u32>((first << imm_shift) | (second << (16 + imm_shift)));
619 }
620 } half_imm;
621
622 union {
623 union {
624 BitField<37, 2, HalfPrecision> precision;
625 BitField<32, 1, u64> saturate;
626
627 BitField<30, 1, u64> negate_c;
628 BitField<35, 2, HalfType> type_c;
629 } rr;
630
631 BitField<57, 2, HalfPrecision> precision;
632 BitField<52, 1, u64> saturate;
633
634 BitField<49, 2, HalfMerge> merge;
635
636 BitField<47, 2, HalfType> type_a;
637
638 BitField<56, 1, u64> negate_b;
639 BitField<28, 2, HalfType> type_b;
640
641 BitField<51, 1, u64> negate_c;
642 BitField<53, 2, HalfType> type_reg39;
643 } hfma2;
644
645 union {
557 BitField<40, 1, u64> invert; 646 BitField<40, 1, u64> invert;
558 } popc; 647 } popc;
559 648
@@ -669,7 +758,6 @@ union Instruction {
669 BitField<45, 2, PredOperation> op; 758 BitField<45, 2, PredOperation> op;
670 BitField<47, 1, u64> ftz; 759 BitField<47, 1, u64> ftz;
671 BitField<48, 4, PredCondition> cond; 760 BitField<48, 4, PredCondition> cond;
672 BitField<56, 1, u64> neg_b;
673 } fsetp; 761 } fsetp;
674 762
675 union { 763 union {
@@ -696,6 +784,14 @@ union Instruction {
696 } psetp; 784 } psetp;
697 785
698 union { 786 union {
787 BitField<43, 4, PredCondition> cond;
788 BitField<45, 2, PredOperation> op;
789 BitField<3, 3, u64> pred3;
790 BitField<0, 3, u64> pred0;
791 BitField<39, 3, u64> pred39;
792 } vsetp;
793
794 union {
699 BitField<12, 3, u64> pred12; 795 BitField<12, 3, u64> pred12;
700 BitField<15, 1, u64> neg_pred12; 796 BitField<15, 1, u64> neg_pred12;
701 BitField<24, 2, PredOperation> cond; 797 BitField<24, 2, PredOperation> cond;
@@ -717,6 +813,23 @@ union Instruction {
717 } csetp; 813 } csetp;
718 814
719 union { 815 union {
816 BitField<35, 4, PredCondition> cond;
817 BitField<49, 1, u64> h_and;
818 BitField<6, 1, u64> ftz;
819 BitField<45, 2, PredOperation> op;
820 BitField<3, 3, u64> pred3;
821 BitField<0, 3, u64> pred0;
822 BitField<43, 1, u64> negate_a;
823 BitField<44, 1, u64> abs_a;
824 BitField<47, 2, HalfType> type_a;
825 BitField<31, 1, u64> negate_b;
826 BitField<30, 1, u64> abs_b;
827 BitField<28, 2, HalfType> type_b;
828 BitField<42, 1, u64> neg_pred;
829 BitField<39, 3, u64> pred39;
830 } hsetp2;
831
832 union {
720 BitField<39, 3, u64> pred39; 833 BitField<39, 3, u64> pred39;
721 BitField<42, 1, u64> neg_pred; 834 BitField<42, 1, u64> neg_pred;
722 BitField<43, 1, u64> neg_a; 835 BitField<43, 1, u64> neg_a;
@@ -727,10 +840,24 @@ union Instruction {
727 BitField<53, 1, u64> neg_b; 840 BitField<53, 1, u64> neg_b;
728 BitField<54, 1, u64> abs_a; 841 BitField<54, 1, u64> abs_a;
729 BitField<55, 1, u64> ftz; 842 BitField<55, 1, u64> ftz;
730 BitField<56, 1, u64> neg_imm;
731 } fset; 843 } fset;
732 844
733 union { 845 union {
846 BitField<49, 1, u64> bf;
847 BitField<35, 3, PredCondition> cond;
848 BitField<50, 1, u64> ftz;
849 BitField<45, 2, PredOperation> op;
850 BitField<43, 1, u64> negate_a;
851 BitField<44, 1, u64> abs_a;
852 BitField<47, 2, HalfType> type_a;
853 BitField<31, 1, u64> negate_b;
854 BitField<30, 1, u64> abs_b;
855 BitField<28, 2, HalfType> type_b;
856 BitField<42, 1, u64> neg_pred;
857 BitField<39, 3, u64> pred39;
858 } hset2;
859
860 union {
734 BitField<39, 3, u64> pred39; 861 BitField<39, 3, u64> pred39;
735 BitField<42, 1, u64> neg_pred; 862 BitField<42, 1, u64> neg_pred;
736 BitField<44, 1, u64> bf; 863 BitField<44, 1, u64> bf;
@@ -1036,15 +1163,17 @@ union Instruction {
1036 union { 1163 union {
1037 BitField<48, 1, u64> signed_a; 1164 BitField<48, 1, u64> signed_a;
1038 BitField<38, 1, u64> is_byte_chunk_a; 1165 BitField<38, 1, u64> is_byte_chunk_a;
1039 BitField<36, 2, VmadType> type_a; 1166 BitField<36, 2, VideoType> type_a;
1040 BitField<36, 2, u64> byte_height_a; 1167 BitField<36, 2, u64> byte_height_a;
1041 1168
1042 BitField<49, 1, u64> signed_b; 1169 BitField<49, 1, u64> signed_b;
1043 BitField<50, 1, u64> use_register_b; 1170 BitField<50, 1, u64> use_register_b;
1044 BitField<30, 1, u64> is_byte_chunk_b; 1171 BitField<30, 1, u64> is_byte_chunk_b;
1045 BitField<28, 2, VmadType> type_b; 1172 BitField<28, 2, VideoType> type_b;
1046 BitField<28, 2, u64> byte_height_b; 1173 BitField<28, 2, u64> byte_height_b;
1174 } video;
1047 1175
1176 union {
1048 BitField<51, 2, VmadShr> shr; 1177 BitField<51, 2, VmadShr> shr;
1049 BitField<55, 1, u64> saturate; // Saturates the result (a * b + c) 1178 BitField<55, 1, u64> saturate; // Saturates the result (a * b + c)
1050 BitField<47, 1, u64> cc; 1179 BitField<47, 1, u64> cc;
@@ -1095,11 +1224,13 @@ public:
1095 KIL, 1224 KIL,
1096 SSY, 1225 SSY,
1097 SYNC, 1226 SYNC,
1227 BRK,
1098 DEPBAR, 1228 DEPBAR,
1099 BFE_C, 1229 BFE_C,
1100 BFE_R, 1230 BFE_R,
1101 BFE_IMM, 1231 BFE_IMM,
1102 BRA, 1232 BRA,
1233 PBK,
1103 LD_A, 1234 LD_A,
1104 LD_C, 1235 LD_C,
1105 ST_A, 1236 ST_A,
@@ -1118,6 +1249,7 @@ public:
1118 OUT_R, // Emit vertex/primitive 1249 OUT_R, // Emit vertex/primitive
1119 ISBERD, 1250 ISBERD,
1120 VMAD, 1251 VMAD,
1252 VSETP,
1121 FFMA_IMM, // Fused Multiply and Add 1253 FFMA_IMM, // Fused Multiply and Add
1122 FFMA_CR, 1254 FFMA_CR,
1123 FFMA_RC, 1255 FFMA_RC,
@@ -1145,6 +1277,18 @@ public:
1145 LEA_RZ, 1277 LEA_RZ,
1146 LEA_IMM, 1278 LEA_IMM,
1147 LEA_HI, 1279 LEA_HI,
1280 HADD2_C,
1281 HADD2_R,
1282 HADD2_IMM,
1283 HMUL2_C,
1284 HMUL2_R,
1285 HMUL2_IMM,
1286 HFMA2_CR,
1287 HFMA2_RC,
1288 HFMA2_RR,
1289 HFMA2_IMM_R,
1290 HSETP2_R,
1291 HSET2_R,
1148 POPC_C, 1292 POPC_C,
1149 POPC_R, 1293 POPC_R,
1150 POPC_IMM, 1294 POPC_IMM,
@@ -1218,9 +1362,12 @@ public:
1218 ArithmeticImmediate, 1362 ArithmeticImmediate,
1219 ArithmeticInteger, 1363 ArithmeticInteger,
1220 ArithmeticIntegerImmediate, 1364 ArithmeticIntegerImmediate,
1365 ArithmeticHalf,
1366 ArithmeticHalfImmediate,
1221 Bfe, 1367 Bfe,
1222 Shift, 1368 Shift,
1223 Ffma, 1369 Ffma,
1370 Hfma2,
1224 Flow, 1371 Flow,
1225 Synch, 1372 Synch,
1226 Memory, 1373 Memory,
@@ -1228,6 +1375,8 @@ public:
1228 FloatSetPredicate, 1375 FloatSetPredicate,
1229 IntegerSet, 1376 IntegerSet,
1230 IntegerSetPredicate, 1377 IntegerSetPredicate,
1378 HalfSet,
1379 HalfSetPredicate,
1231 PredicateSetPredicate, 1380 PredicateSetPredicate,
1232 PredicateSetRegister, 1381 PredicateSetRegister,
1233 Conversion, 1382 Conversion,
@@ -1239,7 +1388,7 @@ public:
1239 /// conditionally executed). 1388 /// conditionally executed).
1240 static bool IsPredicatedInstruction(Id opcode) { 1389 static bool IsPredicatedInstruction(Id opcode) {
1241 // TODO(Subv): Add the rest of unpredicated instructions. 1390 // TODO(Subv): Add the rest of unpredicated instructions.
1242 return opcode != Id::SSY; 1391 return opcode != Id::SSY && opcode != Id::PBK;
1243 } 1392 }
1244 1393
1245 class Matcher { 1394 class Matcher {
@@ -1335,9 +1484,11 @@ private:
1335#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name) 1484#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name)
1336 INST("111000110011----", Id::KIL, Type::Flow, "KIL"), 1485 INST("111000110011----", Id::KIL, Type::Flow, "KIL"),
1337 INST("111000101001----", Id::SSY, Type::Flow, "SSY"), 1486 INST("111000101001----", Id::SSY, Type::Flow, "SSY"),
1487 INST("111000101010----", Id::PBK, Type::Flow, "PBK"),
1338 INST("111000100100----", Id::BRA, Type::Flow, "BRA"), 1488 INST("111000100100----", Id::BRA, Type::Flow, "BRA"),
1489 INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"),
1490 INST("111000110100---", Id::BRK, Type::Flow, "BRK"),
1339 INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"), 1491 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"), 1492 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
1342 INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"), 1493 INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"),
1343 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), 1494 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
@@ -1356,6 +1507,7 @@ private:
1356 INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"), 1507 INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
1357 INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"), 1508 INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
1358 INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"), 1509 INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"),
1510 INST("0101000011110---", Id::VSETP, Type::Trivial, "VSETP"),
1359 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), 1511 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
1360 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), 1512 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
1361 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), 1513 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
@@ -1389,6 +1541,18 @@ private:
1389 INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"), 1541 INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"),
1390 INST("010010111101----", Id::LEA_RZ, Type::ArithmeticInteger, "LEA_RZ"), 1542 INST("010010111101----", Id::LEA_RZ, Type::ArithmeticInteger, "LEA_RZ"),
1391 INST("00011000--------", Id::LEA_HI, Type::ArithmeticInteger, "LEA_HI"), 1543 INST("00011000--------", Id::LEA_HI, Type::ArithmeticInteger, "LEA_HI"),
1544 INST("0111101-1-------", Id::HADD2_C, Type::ArithmeticHalf, "HADD2_C"),
1545 INST("0101110100010---", Id::HADD2_R, Type::ArithmeticHalf, "HADD2_R"),
1546 INST("0111101-0-------", Id::HADD2_IMM, Type::ArithmeticHalfImmediate, "HADD2_IMM"),
1547 INST("0111100-1-------", Id::HMUL2_C, Type::ArithmeticHalf, "HMUL2_C"),
1548 INST("0101110100001---", Id::HMUL2_R, Type::ArithmeticHalf, "HMUL2_R"),
1549 INST("0111100-0-------", Id::HMUL2_IMM, Type::ArithmeticHalfImmediate, "HMUL2_IMM"),
1550 INST("01110---1-------", Id::HFMA2_CR, Type::Hfma2, "HFMA2_CR"),
1551 INST("01100---1-------", Id::HFMA2_RC, Type::Hfma2, "HFMA2_RC"),
1552 INST("0101110100000---", Id::HFMA2_RR, Type::Hfma2, "HFMA2_RR"),
1553 INST("01110---0-------", Id::HFMA2_IMM_R, Type::Hfma2, "HFMA2_R_IMM"),
1554 INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP_R"),
1555 INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"),
1392 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), 1556 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
1393 INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), 1557 INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"),
1394 INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), 1558 INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"),
@@ -1463,4 +1627,4 @@ private:
1463 } 1627 }
1464}; 1628};
1465 1629
1466} // namespace Tegra::Shader 1630} // 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/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 468253033..b472f421f 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -570,10 +570,12 @@ void RasterizerOpenGL::DrawArrays() {
570 SyncBlendState(); 570 SyncBlendState();
571 SyncLogicOpState(); 571 SyncLogicOpState();
572 SyncCullMode(); 572 SyncCullMode();
573 SyncAlphaTest(); 573 SyncDepthRange();
574 SyncScissorTest(); 574 SyncScissorTest();
575 // Alpha Testing is synced on shaders.
575 SyncTransformFeedback(); 576 SyncTransformFeedback();
576 SyncPointState(); 577 SyncPointState();
578 CheckAlphaTests();
577 579
578 // TODO(bunnei): Sync framebuffer_scale uniform here 580 // TODO(bunnei): Sync framebuffer_scale uniform here
579 // TODO(bunnei): Sync scissorbox uniform(s) here 581 // TODO(bunnei): Sync scissorbox uniform(s) here
@@ -659,6 +661,12 @@ void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
659bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, 661bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
660 const Tegra::Engines::Fermi2D::Regs::Surface& dst) { 662 const Tegra::Engines::Fermi2D::Regs::Surface& dst) {
661 MICROPROFILE_SCOPE(OpenGL_Blits); 663 MICROPROFILE_SCOPE(OpenGL_Blits);
664
665 if (Settings::values.use_accurate_gpu_emulation) {
666 // Skip the accelerated copy and perform a slow but more accurate copy
667 return false;
668 }
669
662 res_cache.FermiCopySurface(src, dst); 670 res_cache.FermiCopySurface(src, dst);
663 return true; 671 return true;
664} 672}
@@ -916,12 +924,11 @@ void RasterizerOpenGL::SyncCullMode() {
916 } 924 }
917} 925}
918 926
919void RasterizerOpenGL::SyncDepthScale() { 927void RasterizerOpenGL::SyncDepthRange() {
920 UNREACHABLE(); 928 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
921}
922 929
923void RasterizerOpenGL::SyncDepthOffset() { 930 state.depth.depth_range_near = regs.viewport->depth_range_near;
924 UNREACHABLE(); 931 state.depth.depth_range_far = regs.viewport->depth_range_far;
925} 932}
926 933
927void RasterizerOpenGL::SyncDepthTestState() { 934void RasterizerOpenGL::SyncDepthTestState() {
@@ -1001,17 +1008,6 @@ void RasterizerOpenGL::SyncLogicOpState() {
1001 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); 1008 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation);
1002} 1009}
1003 1010
1004void RasterizerOpenGL::SyncAlphaTest() {
1005 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1006
1007 // TODO(Rodrigo): Alpha testing is a legacy OpenGL feature, but it can be
1008 // implemented with a test+discard in fragment shaders.
1009 if (regs.alpha_test_enabled != 0) {
1010 LOG_CRITICAL(Render_OpenGL, "Alpha testing is not implemented");
1011 UNREACHABLE();
1012 }
1013}
1014
1015void RasterizerOpenGL::SyncScissorTest() { 1011void RasterizerOpenGL::SyncScissorTest() {
1016 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1012 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1017 1013
@@ -1046,4 +1042,15 @@ void RasterizerOpenGL::SyncPointState() {
1046 state.point.size = regs.point_size == 0 ? 1 : regs.point_size; 1042 state.point.size = regs.point_size == 0 ? 1 : regs.point_size;
1047} 1043}
1048 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
1049} // 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..731a336d5 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -144,11 +144,8 @@ private:
144 /// Syncs the cull mode to match the guest state 144 /// Syncs the cull mode to match the guest state
145 void SyncCullMode(); 145 void SyncCullMode();
146 146
147 /// Syncs the depth scale to match the guest state 147 /// Syncs the depth range to match the guest state
148 void SyncDepthScale(); 148 void SyncDepthRange();
149
150 /// Syncs the depth offset to match the guest state
151 void SyncDepthOffset();
152 149
153 /// Syncs the depth test state to match the guest state 150 /// Syncs the depth test state to match the guest state
154 void SyncDepthTestState(); 151 void SyncDepthTestState();
@@ -162,9 +159,6 @@ private:
162 /// Syncs the LogicOp state to match the guest state 159 /// Syncs the LogicOp state to match the guest state
163 void SyncLogicOpState(); 160 void SyncLogicOpState();
164 161
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 162 /// Syncs the scissor test state to match the guest state
169 void SyncScissorTest(); 163 void SyncScissorTest();
170 164
@@ -174,6 +168,9 @@ private:
174 /// Syncs the point state to match the guest state 168 /// Syncs the point state to match the guest state
175 void SyncPointState(); 169 void SyncPointState();
176 170
171 /// Check asserts for alpha testing.
172 void CheckAlphaTests();
173
177 bool has_ARB_direct_state_access = false; 174 bool has_ARB_direct_state_access = false;
178 bool has_ARB_multi_bind = false; 175 bool has_ARB_multi_bind = false;
179 bool has_ARB_separate_shader_objects = false; 176 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 9c8925383..591ec7998 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -78,6 +78,29 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
78 } 78 }
79} 79}
80 80
81std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const {
82 const u32 compression_factor{GetCompressionFactor(pixel_format)};
83 const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)};
84 u32 m_depth = (layer_only ? 1U : depth);
85 u32 m_width = std::max(1U, width / compression_factor);
86 u32 m_height = std::max(1U, height / compression_factor);
87 std::size_t size = Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height,
88 m_depth, block_height, block_depth);
89 u32 m_block_height = block_height;
90 u32 m_block_depth = block_depth;
91 std::size_t block_size_bytes = 512 * block_height * block_depth; // 512 is GOB size
92 for (u32 i = 1; i < max_mip_level; i++) {
93 m_width = std::max(1U, m_width / 2);
94 m_height = std::max(1U, m_height / 2);
95 m_depth = std::max(1U, m_depth / 2);
96 m_block_height = std::max(1U, m_block_height / 2);
97 m_block_depth = std::max(1U, m_block_depth / 2);
98 size += Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height, m_depth,
99 m_block_height, m_block_depth);
100 }
101 return is_tiled ? Common::AlignUp(size, block_size_bytes) : size;
102}
103
81/*static*/ SurfaceParams SurfaceParams::CreateForTexture( 104/*static*/ SurfaceParams SurfaceParams::CreateForTexture(
82 const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) { 105 const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) {
83 SurfaceParams params{}; 106 SurfaceParams params{};
@@ -124,6 +147,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
124 break; 147 break;
125 } 148 }
126 149
150 params.is_layered = SurfaceTargetIsLayered(params.target);
127 params.max_mip_level = config.tic.max_mip_level + 1; 151 params.max_mip_level = config.tic.max_mip_level + 1;
128 params.rt = {}; 152 params.rt = {};
129 153
@@ -150,6 +174,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
150 params.target = SurfaceTarget::Texture2D; 174 params.target = SurfaceTarget::Texture2D;
151 params.depth = 1; 175 params.depth = 1;
152 params.max_mip_level = 0; 176 params.max_mip_level = 0;
177 params.is_layered = false;
153 178
154 // Render target specific parameters, not used for caching 179 // Render target specific parameters, not used for caching
155 params.rt.index = static_cast<u32>(index); 180 params.rt.index = static_cast<u32>(index);
@@ -182,6 +207,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
182 params.target = SurfaceTarget::Texture2D; 207 params.target = SurfaceTarget::Texture2D;
183 params.depth = 1; 208 params.depth = 1;
184 params.max_mip_level = 0; 209 params.max_mip_level = 0;
210 params.is_layered = false;
185 params.rt = {}; 211 params.rt = {};
186 212
187 params.InitCacheParameters(zeta_address); 213 params.InitCacheParameters(zeta_address);
@@ -361,10 +387,11 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d
361 } 387 }
362} 388}
363 389
364static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), 390using GLConversionArray = std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
365 SurfaceParams::MaxPixelFormat> 391 SurfaceParams::MaxPixelFormat>;
366 morton_to_gl_fns = { 392
367 // clang-format off 393static constexpr GLConversionArray morton_to_gl_fns = {
394 // clang-format off
368 MortonCopy<true, PixelFormat::ABGR8U>, 395 MortonCopy<true, PixelFormat::ABGR8U>,
369 MortonCopy<true, PixelFormat::ABGR8S>, 396 MortonCopy<true, PixelFormat::ABGR8S>,
370 MortonCopy<true, PixelFormat::ABGR8UI>, 397 MortonCopy<true, PixelFormat::ABGR8UI>,
@@ -418,13 +445,11 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t,
418 MortonCopy<true, PixelFormat::Z24S8>, 445 MortonCopy<true, PixelFormat::Z24S8>,
419 MortonCopy<true, PixelFormat::S8Z24>, 446 MortonCopy<true, PixelFormat::S8Z24>,
420 MortonCopy<true, PixelFormat::Z32FS8>, 447 MortonCopy<true, PixelFormat::Z32FS8>,
421 // clang-format on 448 // clang-format on
422}; 449};
423 450
424static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), 451static constexpr GLConversionArray gl_to_morton_fns = {
425 SurfaceParams::MaxPixelFormat> 452 // clang-format off
426 gl_to_morton_fns = {
427 // clang-format off
428 MortonCopy<false, PixelFormat::ABGR8U>, 453 MortonCopy<false, PixelFormat::ABGR8U>,
429 MortonCopy<false, PixelFormat::ABGR8S>, 454 MortonCopy<false, PixelFormat::ABGR8S>,
430 MortonCopy<false, PixelFormat::ABGR8UI>, 455 MortonCopy<false, PixelFormat::ABGR8UI>,
@@ -479,9 +504,35 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t,
479 MortonCopy<false, PixelFormat::Z24S8>, 504 MortonCopy<false, PixelFormat::Z24S8>,
480 MortonCopy<false, PixelFormat::S8Z24>, 505 MortonCopy<false, PixelFormat::S8Z24>,
481 MortonCopy<false, PixelFormat::Z32FS8>, 506 MortonCopy<false, PixelFormat::Z32FS8>,
482 // clang-format on 507 // clang-format on
483}; 508};
484 509
510void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params,
511 std::vector<u8>& gl_buffer) {
512 u32 depth = params.depth;
513 if (params.target == SurfaceParams::SurfaceTarget::Texture2D) {
514 // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
515 depth = 1U;
516 }
517 if (params.is_layered) {
518 u64 offset = 0;
519 u64 offset_gl = 0;
520 u64 layer_size = params.LayerMemorySize();
521 u64 gl_size = params.LayerSizeGL();
522 for (u32 i = 0; i < depth; i++) {
523 functions[static_cast<std::size_t>(params.pixel_format)](
524 params.width, params.block_height, params.height, params.block_depth, 1,
525 gl_buffer.data() + offset_gl, gl_size, params.addr + offset);
526 offset += layer_size;
527 offset_gl += gl_size;
528 }
529 } else {
530 functions[static_cast<std::size_t>(params.pixel_format)](
531 params.width, params.block_height, params.height, params.block_depth, depth,
532 gl_buffer.data(), gl_buffer.size(), params.addr);
533 }
534}
535
485static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, 536static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
486 GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0, 537 GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0,
487 GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { 538 GLenum dst_attachment = 0, std::size_t cubemap_face = 0) {
@@ -881,21 +932,10 @@ void CachedSurface::LoadGLBuffer() {
881 932
882 gl_buffer.resize(params.size_in_bytes_gl); 933 gl_buffer.resize(params.size_in_bytes_gl);
883 if (params.is_tiled) { 934 if (params.is_tiled) {
884 u32 depth = params.depth;
885 u32 block_depth = params.block_depth;
886
887 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", 935 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
888 params.block_width, static_cast<u32>(params.target)); 936 params.block_width, static_cast<u32>(params.target));
889 937
890 if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { 938 SwizzleFunc(morton_to_gl_fns, params, gl_buffer);
891 // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
892 depth = 1U;
893 block_depth = 1U;
894 }
895
896 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
897 params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(),
898 gl_buffer.size(), params.addr);
899 } else { 939 } else {
900 const auto texture_src_data{Memory::GetPointer(params.addr)}; 940 const auto texture_src_data{Memory::GetPointer(params.addr)};
901 const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl}; 941 const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl};
@@ -929,19 +969,10 @@ void CachedSurface::FlushGLBuffer() {
929 const u8* const texture_src_data = Memory::GetPointer(params.addr); 969 const u8* const texture_src_data = Memory::GetPointer(params.addr);
930 ASSERT(texture_src_data); 970 ASSERT(texture_src_data);
931 if (params.is_tiled) { 971 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 {}", 972 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
936 params.block_width, static_cast<u32>(params.target)); 973 params.block_width, static_cast<u32>(params.target));
937 974
938 if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { 975 SwizzleFunc(gl_to_morton_fns, params, gl_buffer);
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 { 976 } else {
946 std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes()); 977 std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes());
947 } 978 }
@@ -1179,7 +1210,7 @@ void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface,
1179 const Surface& dst_surface) { 1210 const Surface& dst_surface) {
1180 const auto& src_params{src_surface->GetSurfaceParams()}; 1211 const auto& src_params{src_surface->GetSurfaceParams()};
1181 const auto& dst_params{dst_surface->GetSurfaceParams()}; 1212 const auto& dst_params{dst_surface->GetSurfaceParams()};
1182 FlushRegion(src_params.addr, dst_params.size_in_bytes); 1213 FlushRegion(src_params.addr, dst_params.MemorySize());
1183 LoadSurface(dst_surface); 1214 LoadSurface(dst_surface);
1184} 1215}
1185 1216
@@ -1221,44 +1252,10 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1221 CopySurface(old_surface, new_surface, copy_pbo.handle); 1252 CopySurface(old_surface, new_surface, copy_pbo.handle);
1222 } 1253 }
1223 break; 1254 break;
1255 case SurfaceParams::SurfaceTarget::TextureCubemap:
1224 case SurfaceParams::SurfaceTarget::Texture3D: 1256 case SurfaceParams::SurfaceTarget::Texture3D:
1225 AccurateCopySurface(old_surface, new_surface); 1257 AccurateCopySurface(old_surface, new_surface);
1226 break; 1258 break;
1227 case SurfaceParams::SurfaceTarget::TextureCubemap: {
1228 if (old_params.rt.array_mode != 1) {
1229 // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this
1230 // yet (array rendering used as a cubemap texture).
1231 LOG_CRITICAL(HW_GPU, "Unhandled rendertarget array_mode {}", old_params.rt.array_mode);
1232 UNREACHABLE();
1233 return new_surface;
1234 }
1235
1236 // This seems to be used for render-to-cubemap texture
1237 ASSERT_MSG(old_params.target == SurfaceParams::SurfaceTarget::Texture2D, "Unexpected");
1238 ASSERT_MSG(old_params.pixel_format == new_params.pixel_format, "Unexpected");
1239 ASSERT_MSG(old_params.rt.base_layer == 0, "Unimplemented");
1240
1241 // TODO(bunnei): Verify the below - this stride seems to be in 32-bit words, not pixels.
1242 // Tested with Splatoon 2, Super Mario Odyssey, and Breath of the Wild.
1243 const std::size_t byte_stride{old_params.rt.layer_stride * sizeof(u32)};
1244
1245 for (std::size_t index = 0; index < new_params.depth; ++index) {
1246 Surface face_surface{TryGetReservedSurface(old_params)};
1247 ASSERT_MSG(face_surface, "Unexpected");
1248
1249 if (is_blit) {
1250 BlitSurface(face_surface, new_surface, read_framebuffer.handle,
1251 draw_framebuffer.handle, face_surface->GetSurfaceParams().rt.index,
1252 new_params.rt.index, index);
1253 } else {
1254 CopySurface(face_surface, new_surface, copy_pbo.handle,
1255 face_surface->GetSurfaceParams().rt.index, new_params.rt.index, index);
1256 }
1257
1258 old_params.addr += byte_stride;
1259 }
1260 break;
1261 }
1262 default: 1259 default:
1263 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", 1260 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
1264 static_cast<u32>(new_params.target)); 1261 static_cast<u32>(new_params.target));
@@ -1266,7 +1263,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1266 } 1263 }
1267 1264
1268 return new_surface; 1265 return new_surface;
1269} 1266} // namespace OpenGL
1270 1267
1271Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const { 1268Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const {
1272 return TryGet(addr); 1269 return TryGet(addr);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 0dd0d90a3..50a7ab47d 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -168,6 +168,23 @@ struct SurfaceParams {
168 } 168 }
169 } 169 }
170 170
171 static bool SurfaceTargetIsLayered(SurfaceTarget target) {
172 switch (target) {
173 case SurfaceTarget::Texture1D:
174 case SurfaceTarget::Texture2D:
175 case SurfaceTarget::Texture3D:
176 return false;
177 case SurfaceTarget::Texture1DArray:
178 case SurfaceTarget::Texture2DArray:
179 case SurfaceTarget::TextureCubemap:
180 return true;
181 default:
182 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
183 UNREACHABLE();
184 return false;
185 }
186 }
187
171 /** 188 /**
172 * Gets the compression factor for the specified PixelFormat. This applies to just the 189 * Gets the compression factor for the specified PixelFormat. This applies to just the
173 * "compressed width" and "compressed height", not the overall compression factor of a 190 * "compressed width" and "compressed height", not the overall compression factor of a
@@ -742,6 +759,25 @@ struct SurfaceParams {
742 return size_in_bytes_gl / 6; 759 return size_in_bytes_gl / 6;
743 } 760 }
744 761
762 /// Returns the exact size of memory occupied by the texture in VRAM, including mipmaps.
763 std::size_t MemorySize() const {
764 std::size_t size = InnerMemorySize(is_layered);
765 if (is_layered)
766 return size * depth;
767 return size;
768 }
769
770 /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including
771 /// mipmaps.
772 std::size_t LayerMemorySize() const {
773 return InnerMemorySize(true);
774 }
775
776 /// Returns the size of a layer of this surface in OpenGL.
777 std::size_t LayerSizeGL() const {
778 return SizeInBytesRaw(true) / depth;
779 }
780
745 /// Creates SurfaceParams from a texture configuration 781 /// Creates SurfaceParams from a texture configuration
746 static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config, 782 static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config,
747 const GLShader::SamplerEntry& entry); 783 const GLShader::SamplerEntry& entry);
@@ -782,6 +818,7 @@ struct SurfaceParams {
782 u32 unaligned_height; 818 u32 unaligned_height;
783 SurfaceTarget target; 819 SurfaceTarget target;
784 u32 max_mip_level; 820 u32 max_mip_level;
821 bool is_layered;
785 822
786 // Parameters used for caching 823 // Parameters used for caching
787 VAddr addr; 824 VAddr addr;
@@ -797,6 +834,9 @@ struct SurfaceParams {
797 u32 layer_stride; 834 u32 layer_stride;
798 u32 base_layer; 835 u32 base_layer;
799 } rt; 836 } rt;
837
838private:
839 std::size_t InnerMemorySize(bool layer_only = false) const;
800}; 840};
801 841
802}; // namespace OpenGL 842}; // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index f4340a017..81ffb24e4 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -6,6 +6,7 @@
6#include <set> 6#include <set>
7#include <string> 7#include <string>
8#include <string_view> 8#include <string_view>
9#include <unordered_set>
9 10
10#include <boost/optional.hpp> 11#include <boost/optional.hpp>
11#include <fmt/format.h> 12#include <fmt/format.h>
@@ -30,8 +31,6 @@ using Tegra::Shader::SubOp;
30constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; 31constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
31constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); 32constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header);
32 33
33enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
34
35constexpr u32 MAX_GEOMETRY_BUFFERS = 6; 34constexpr u32 MAX_GEOMETRY_BUFFERS = 6;
36constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested 35constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested
37 36
@@ -165,10 +164,11 @@ private:
165 const ExitMethod jmp = Scan(target, end, labels); 164 const ExitMethod jmp = Scan(target, end, labels);
166 return exit_method = ParallelExit(no_jmp, jmp); 165 return exit_method = ParallelExit(no_jmp, jmp);
167 } 166 }
168 case OpCode::Id::SSY: { 167 case OpCode::Id::SSY:
169 // The SSY instruction uses a similar encoding as the BRA instruction. 168 case OpCode::Id::PBK: {
169 // The SSY and PBK use a similar encoding as the BRA instruction.
170 ASSERT_MSG(instr.bra.constant_buffer == 0, 170 ASSERT_MSG(instr.bra.constant_buffer == 0,
171 "Constant buffer SSY is not supported"); 171 "Constant buffer branching is not supported");
172 const u32 target = offset + instr.bra.GetBranchTarget(); 172 const u32 target = offset + instr.bra.GetBranchTarget();
173 labels.insert(target); 173 labels.insert(target);
174 // Continue scanning for an exit method. 174 // Continue scanning for an exit method.
@@ -277,7 +277,8 @@ public:
277 GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, 277 GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations,
278 const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix, 278 const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix,
279 const Tegra::Shader::Header& header) 279 const Tegra::Shader::Header& header)
280 : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header} { 280 : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header},
281 fixed_pipeline_output_attributes_used{} {
281 BuildRegisterList(); 282 BuildRegisterList();
282 BuildInputList(); 283 BuildInputList();
283 } 284 }
@@ -376,11 +377,55 @@ public:
376 } 377 }
377 378
378 /** 379 /**
380 * Writes code that does a register assignment to a half float value operation.
381 * @param reg The destination register to use.
382 * @param elem The element to use for the operation.
383 * @param value The code representing the value to assign. Type has to be half float.
384 * @param merge Half float kind of assignment.
385 * @param dest_num_components Number of components in the destination.
386 * @param value_num_components Number of components in the value.
387 * @param is_saturated Optional, when True, saturates the provided value.
388 * @param dest_elem Optional, the destination element to use for the operation.
389 */
390 void SetRegisterToHalfFloat(const Register& reg, u64 elem, const std::string& value,
391 Tegra::Shader::HalfMerge merge, u64 dest_num_components,
392 u64 value_num_components, bool is_saturated = false,
393 u64 dest_elem = 0) {
394 ASSERT_MSG(!is_saturated, "Unimplemented");
395
396 const std::string result = [&]() {
397 switch (merge) {
398 case Tegra::Shader::HalfMerge::H0_H1:
399 return "uintBitsToFloat(packHalf2x16(" + value + "))";
400 case Tegra::Shader::HalfMerge::F32:
401 // Half float instructions take the first component when doing a float cast.
402 return "float(" + value + ".x)";
403 case Tegra::Shader::HalfMerge::Mrg_H0:
404 // TODO(Rodrigo): I guess Mrg_H0 and Mrg_H1 take their respective component from the
405 // pack. I couldn't test this on hardware but it shouldn't really matter since most
406 // of the time when a Mrg_* flag is used both components will be mirrored. That
407 // being said, it deserves a test.
408 return "((" + GetRegisterAsInteger(reg, 0, false) +
409 " & 0xffff0000) | (packHalf2x16(" + value + ") & 0x0000ffff))";
410 case Tegra::Shader::HalfMerge::Mrg_H1:
411 return "((" + GetRegisterAsInteger(reg, 0, false) +
412 " & 0x0000ffff) | (packHalf2x16(" + value + ") & 0xffff0000))";
413 default:
414 UNREACHABLE();
415 return std::string("0");
416 }
417 }();
418
419 SetRegister(reg, elem, result, dest_num_components, value_num_components, dest_elem);
420 }
421
422 /**
379 * Writes code that does a register assignment to input attribute operation. Input attributes 423 * Writes code that does a register assignment to input attribute operation. Input attributes
380 * are stored as floats, so this may require conversion. 424 * are stored as floats, so this may require conversion.
381 * @param reg The destination register to use. 425 * @param reg The destination register to use.
382 * @param elem The element to use for the operation. 426 * @param elem The element to use for the operation.
383 * @param attribute The input attribute to use as the source value. 427 * @param attribute The input attribute to use as the source value.
428 * @param input_mode The input mode.
384 * @param vertex The register that decides which vertex to read from (used in GS). 429 * @param vertex The register that decides which vertex to read from (used in GS).
385 */ 430 */
386 void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute, 431 void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute,
@@ -437,7 +482,12 @@ public:
437 std::to_string(static_cast<u32>(attribute)) + ']' + 482 std::to_string(static_cast<u32>(attribute)) + ']' +
438 GetSwizzle(elem) + " = " + src + ';'); 483 GetSwizzle(elem) + " = " + src + ';');
439 } else { 484 } else {
440 shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); 485 if (attribute == Attribute::Index::PointSize) {
486 fixed_pipeline_output_attributes_used.insert(attribute);
487 shader.AddLine(dest + " = " + src + ';');
488 } else {
489 shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';');
490 }
441 } 491 }
442 } 492 }
443 } 493 }
@@ -481,6 +531,7 @@ public:
481 531
482 /// Add declarations. 532 /// Add declarations.
483 void GenerateDeclarations(const std::string& suffix) { 533 void GenerateDeclarations(const std::string& suffix) {
534 GenerateVertex();
484 GenerateRegisters(suffix); 535 GenerateRegisters(suffix);
485 GenerateInternalFlags(); 536 GenerateInternalFlags();
486 GenerateInputAttrs(); 537 GenerateInputAttrs();
@@ -548,13 +599,6 @@ private:
548 599
549 /// Generates declarations for input attributes. 600 /// Generates declarations for input attributes.
550 void GenerateInputAttrs() { 601 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) { 602 for (const auto element : declr_input_attribute) {
559 // TODO(bunnei): Use proper number of elements for these 603 // TODO(bunnei): Use proper number of elements for these
560 u32 idx = 604 u32 idx =
@@ -577,10 +621,6 @@ private:
577 621
578 /// Generates declarations for output attributes. 622 /// Generates declarations for output attributes.
579 void GenerateOutputAttrs() { 623 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) { 624 for (const auto& index : declr_output_attribute) {
585 // TODO(bunnei): Use proper number of elements for these 625 // TODO(bunnei): Use proper number of elements for these
586 const u32 idx = static_cast<u32>(index) - 626 const u32 idx = static_cast<u32>(index) -
@@ -651,6 +691,20 @@ private:
651 declarations.AddNewLine(); 691 declarations.AddNewLine();
652 } 692 }
653 693
694 void GenerateVertex() {
695 if (stage != Maxwell3D::Regs::ShaderStage::Vertex)
696 return;
697 declarations.AddLine("out gl_PerVertex {");
698 ++declarations.scope;
699 declarations.AddLine("vec4 gl_Position;");
700 for (auto& o : fixed_pipeline_output_attributes_used) {
701 if (o == Attribute::Index::PointSize)
702 declarations.AddLine("float gl_PointSize;");
703 }
704 --declarations.scope;
705 declarations.AddLine("};");
706 }
707
654 /// Generates code representing a temporary (GPR) register. 708 /// Generates code representing a temporary (GPR) register.
655 std::string GetRegister(const Register& reg, unsigned elem) { 709 std::string GetRegister(const Register& reg, unsigned elem) {
656 if (reg == Register::ZeroIndex) { 710 if (reg == Register::ZeroIndex) {
@@ -804,6 +858,8 @@ private:
804 /// Generates code representing the declaration name of an output attribute register. 858 /// Generates code representing the declaration name of an output attribute register.
805 std::string GetOutputAttribute(Attribute::Index attribute) { 859 std::string GetOutputAttribute(Attribute::Index attribute) {
806 switch (attribute) { 860 switch (attribute) {
861 case Attribute::Index::PointSize:
862 return "gl_PointSize";
807 case Attribute::Index::Position: 863 case Attribute::Index::Position:
808 return "position"; 864 return "position";
809 default: 865 default:
@@ -838,6 +894,7 @@ private:
838 const Maxwell3D::Regs::ShaderStage& stage; 894 const Maxwell3D::Regs::ShaderStage& stage;
839 const std::string& suffix; 895 const std::string& suffix;
840 const Tegra::Shader::Header& header; 896 const Tegra::Shader::Header& header;
897 std::unordered_set<Attribute::Index> fixed_pipeline_output_attributes_used;
841}; 898};
842 899
843class GLSLGenerator { 900class GLSLGenerator {
@@ -877,6 +934,19 @@ private:
877 return fmt::format("uintBitsToFloat({})", instr.alu.GetImm20_32()); 934 return fmt::format("uintBitsToFloat({})", instr.alu.GetImm20_32());
878 } 935 }
879 936
937 /// Generates code representing a vec2 pair unpacked from a half float immediate
938 static std::string UnpackHalfImmediate(const Instruction& instr, bool negate) {
939 const std::string immediate = GetHalfFloat(std::to_string(instr.half_imm.PackImmediates()));
940 if (!negate) {
941 return immediate;
942 }
943 const std::string negate_first = instr.half_imm.first_negate != 0 ? "-" : "";
944 const std::string negate_second = instr.half_imm.second_negate != 0 ? "-" : "";
945 const std::string negate_vec = "vec2(" + negate_first + "1, " + negate_second + "1)";
946
947 return '(' + immediate + " * " + negate_vec + ')';
948 }
949
880 /// Generates code representing a texture sampler. 950 /// Generates code representing a texture sampler.
881 std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array, 951 std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array,
882 bool is_shadow) { 952 bool is_shadow) {
@@ -908,7 +978,7 @@ private:
908 // Can't assign to the constant predicate. 978 // Can't assign to the constant predicate.
909 ASSERT(pred != static_cast<u64>(Pred::UnusedIndex)); 979 ASSERT(pred != static_cast<u64>(Pred::UnusedIndex));
910 980
911 const std::string variable = 'p' + std::to_string(pred) + '_' + suffix; 981 std::string variable = 'p' + std::to_string(pred) + '_' + suffix;
912 shader.AddLine(variable + " = " + value + ';'); 982 shader.AddLine(variable + " = " + value + ';');
913 declr_predicates.insert(std::move(variable)); 983 declr_predicates.insert(std::move(variable));
914 } 984 }
@@ -1013,6 +1083,41 @@ private:
1013 } 1083 }
1014 1084
1015 /* 1085 /*
1086 * Transforms the input string GLSL operand into an unpacked half float pair.
1087 * @note This function returns a float type pair instead of a half float pair. This is because
1088 * real half floats are not standardized in GLSL but unpackHalf2x16 (which returns a vec2) is.
1089 * @param operand Input operand. It has to be an unsigned integer.
1090 * @param type How to unpack the unsigned integer to a half float pair.
1091 * @param abs Get the absolute value of unpacked half floats.
1092 * @param neg Get the negative value of unpacked half floats.
1093 * @returns String corresponding to a half float pair.
1094 */
1095 static std::string GetHalfFloat(const std::string& operand,
1096 Tegra::Shader::HalfType type = Tegra::Shader::HalfType::H0_H1,
1097 bool abs = false, bool neg = false) {
1098 // "vec2" calls emitted in this function are intended to alias components.
1099 const std::string value = [&]() {
1100 switch (type) {
1101 case Tegra::Shader::HalfType::H0_H1:
1102 return "unpackHalf2x16(" + operand + ')';
1103 case Tegra::Shader::HalfType::F32:
1104 return "vec2(uintBitsToFloat(" + operand + "))";
1105 case Tegra::Shader::HalfType::H0_H0:
1106 case Tegra::Shader::HalfType::H1_H1: {
1107 const bool high = type == Tegra::Shader::HalfType::H1_H1;
1108 const char unpack_index = "xy"[high ? 1 : 0];
1109 return "vec2(unpackHalf2x16(" + operand + ")." + unpack_index + ')';
1110 }
1111 default:
1112 UNREACHABLE();
1113 return std::string("vec2(0)");
1114 }
1115 }();
1116
1117 return GetOperandAbsNeg(value, abs, neg);
1118 }
1119
1120 /*
1016 * Returns whether the instruction at the specified offset is a 'sched' instruction. 1121 * Returns whether the instruction at the specified offset is a 'sched' instruction.
1017 * Sched instructions always appear before a sequence of 3 instructions. 1122 * Sched instructions always appear before a sequence of 3 instructions.
1018 */ 1123 */
@@ -1154,27 +1259,27 @@ private:
1154 } 1259 }
1155 1260
1156 /* 1261 /*
1157 * Emits code to push the input target address to the SSY address stack, incrementing the stack 1262 * Emits code to push the input target address to the flow address stack, incrementing the stack
1158 * top. 1263 * top.
1159 */ 1264 */
1160 void EmitPushToSSYStack(u32 target) { 1265 void EmitPushToFlowStack(u32 target) {
1161 shader.AddLine('{'); 1266 shader.AddLine('{');
1162 ++shader.scope; 1267 ++shader.scope;
1163 shader.AddLine("ssy_stack[ssy_stack_top] = " + std::to_string(target) + "u;"); 1268 shader.AddLine("flow_stack[flow_stack_top] = " + std::to_string(target) + "u;");
1164 shader.AddLine("ssy_stack_top++;"); 1269 shader.AddLine("flow_stack_top++;");
1165 --shader.scope; 1270 --shader.scope;
1166 shader.AddLine('}'); 1271 shader.AddLine('}');
1167 } 1272 }
1168 1273
1169 /* 1274 /*
1170 * Emits code to pop an address from the SSY address stack, setting the jump address to the 1275 * Emits code to pop an address from the flow address stack, setting the jump address to the
1171 * popped address and decrementing the stack top. 1276 * popped address and decrementing the stack top.
1172 */ 1277 */
1173 void EmitPopFromSSYStack() { 1278 void EmitPopFromFlowStack() {
1174 shader.AddLine('{'); 1279 shader.AddLine('{');
1175 ++shader.scope; 1280 ++shader.scope;
1176 shader.AddLine("ssy_stack_top--;"); 1281 shader.AddLine("flow_stack_top--;");
1177 shader.AddLine("jmp_to = ssy_stack[ssy_stack_top];"); 1282 shader.AddLine("jmp_to = flow_stack[flow_stack_top];");
1178 shader.AddLine("break;"); 1283 shader.AddLine("break;");
1179 --shader.scope; 1284 --shader.scope;
1180 shader.AddLine('}'); 1285 shader.AddLine('}');
@@ -1186,9 +1291,29 @@ private:
1186 1291
1187 ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented"); 1292 ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented");
1188 1293
1294 shader.AddLine("if (alpha_test[0] != 0) {");
1295 ++shader.scope;
1296 // We start on the register containing the alpha value in the first RT.
1297 u32 current_reg = 3;
1298 for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets;
1299 ++render_target) {
1300 // TODO(Blinkhawk): verify the behavior of alpha testing on hardware when
1301 // multiple render targets are used.
1302 if (header.ps.IsColorComponentOutputEnabled(render_target, 0) ||
1303 header.ps.IsColorComponentOutputEnabled(render_target, 1) ||
1304 header.ps.IsColorComponentOutputEnabled(render_target, 2) ||
1305 header.ps.IsColorComponentOutputEnabled(render_target, 3)) {
1306 shader.AddLine(fmt::format("if (!AlphaFunc({})) discard;",
1307 regs.GetRegisterAsFloat(current_reg)));
1308 current_reg += 4;
1309 }
1310 }
1311 --shader.scope;
1312 shader.AddLine('}');
1313
1189 // Write the color outputs using the data in the shader registers, disabled 1314 // Write the color outputs using the data in the shader registers, disabled
1190 // rendertargets/components are skipped in the register assignment. 1315 // rendertargets/components are skipped in the register assignment.
1191 u32 current_reg = 0; 1316 current_reg = 0;
1192 for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets; 1317 for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets;
1193 ++render_target) { 1318 ++render_target) {
1194 // TODO(Subv): Figure out how dual-source blending is configured in the Switch. 1319 // TODO(Subv): Figure out how dual-source blending is configured in the Switch.
@@ -1212,6 +1337,63 @@ private:
1212 } 1337 }
1213 } 1338 }
1214 1339
1340 /// Unpacks a video instruction operand (e.g. VMAD).
1341 std::string GetVideoOperand(const std::string& op, bool is_chunk, bool is_signed,
1342 Tegra::Shader::VideoType type, u64 byte_height) {
1343 const std::string value = [&]() {
1344 if (!is_chunk) {
1345 const auto offset = static_cast<u32>(byte_height * 8);
1346 return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)";
1347 }
1348 const std::string zero = "0";
1349
1350 switch (type) {
1351 case Tegra::Shader::VideoType::Size16_Low:
1352 return '(' + op + " & 0xffff)";
1353 case Tegra::Shader::VideoType::Size16_High:
1354 return '(' + op + " >> 16)";
1355 case Tegra::Shader::VideoType::Size32:
1356 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
1357 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
1358 // explanation is found: assert.
1359 UNIMPLEMENTED();
1360 return zero;
1361 case Tegra::Shader::VideoType::Invalid:
1362 UNREACHABLE_MSG("Invalid instruction encoding");
1363 return zero;
1364 default:
1365 UNREACHABLE();
1366 return zero;
1367 }
1368 }();
1369
1370 if (is_signed) {
1371 return "int(" + value + ')';
1372 }
1373 return value;
1374 };
1375
1376 /// Gets the A operand for a video instruction.
1377 std::string GetVideoOperandA(Instruction instr) {
1378 return GetVideoOperand(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
1379 instr.video.is_byte_chunk_a != 0, instr.video.signed_a,
1380 instr.video.type_a, instr.video.byte_height_a);
1381 }
1382
1383 /// Gets the B operand for a video instruction.
1384 std::string GetVideoOperandB(Instruction instr) {
1385 if (instr.video.use_register_b) {
1386 return GetVideoOperand(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
1387 instr.video.is_byte_chunk_b != 0, instr.video.signed_b,
1388 instr.video.type_b, instr.video.byte_height_b);
1389 } else {
1390 return '(' +
1391 std::to_string(instr.video.signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
1392 : instr.alu.GetImm20_16()) +
1393 ')';
1394 }
1395 }
1396
1215 /** 1397 /**
1216 * Compiles a single instruction from Tegra to GLSL. 1398 * Compiles a single instruction from Tegra to GLSL.
1217 * @param offset the offset of the Tegra shader instruction. 1399 * @param offset the offset of the Tegra shader instruction.
@@ -1381,9 +1563,10 @@ private:
1381 break; 1563 break;
1382 } 1564 }
1383 case OpCode::Id::FMUL32_IMM: { 1565 case OpCode::Id::FMUL32_IMM: {
1384 regs.SetRegisterToFloat( 1566 regs.SetRegisterToFloat(instr.gpr0, 0,
1385 instr.gpr0, 0, 1567 regs.GetRegisterAsFloat(instr.gpr8) + " * " +
1386 regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1); 1568 GetImmediate32(instr),
1569 1, 1, instr.fmul32.saturate);
1387 break; 1570 break;
1388 } 1571 }
1389 case OpCode::Id::FADD32I: { 1572 case OpCode::Id::FADD32I: {
@@ -1748,6 +1931,86 @@ private:
1748 1931
1749 break; 1932 break;
1750 } 1933 }
1934 case OpCode::Type::ArithmeticHalf: {
1935 if (opcode->GetId() == OpCode::Id::HADD2_C || opcode->GetId() == OpCode::Id::HADD2_R) {
1936 ASSERT_MSG(instr.alu_half.ftz == 0, "Unimplemented");
1937 }
1938 const bool negate_a =
1939 opcode->GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0;
1940 const bool negate_b =
1941 opcode->GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0;
1942
1943 const std::string op_a =
1944 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.alu_half.type_a,
1945 instr.alu_half.abs_a != 0, negate_a);
1946
1947 std::string op_b;
1948 switch (opcode->GetId()) {
1949 case OpCode::Id::HADD2_C:
1950 case OpCode::Id::HMUL2_C:
1951 op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
1952 GLSLRegister::Type::UnsignedInteger);
1953 break;
1954 case OpCode::Id::HADD2_R:
1955 case OpCode::Id::HMUL2_R:
1956 op_b = regs.GetRegisterAsInteger(instr.gpr20, 0, false);
1957 break;
1958 default:
1959 UNREACHABLE();
1960 op_b = "0";
1961 break;
1962 }
1963 op_b = GetHalfFloat(op_b, instr.alu_half.type_b, instr.alu_half.abs_b != 0, negate_b);
1964
1965 const std::string result = [&]() {
1966 switch (opcode->GetId()) {
1967 case OpCode::Id::HADD2_C:
1968 case OpCode::Id::HADD2_R:
1969 return '(' + op_a + " + " + op_b + ')';
1970 case OpCode::Id::HMUL2_C:
1971 case OpCode::Id::HMUL2_R:
1972 return '(' + op_a + " * " + op_b + ')';
1973 default:
1974 LOG_CRITICAL(HW_GPU, "Unhandled half float instruction: {}", opcode->GetName());
1975 UNREACHABLE();
1976 return std::string("0");
1977 }
1978 }();
1979
1980 regs.SetRegisterToHalfFloat(instr.gpr0, 0, result, instr.alu_half.merge, 1, 1,
1981 instr.alu_half.saturate != 0);
1982 break;
1983 }
1984 case OpCode::Type::ArithmeticHalfImmediate: {
1985 if (opcode->GetId() == OpCode::Id::HADD2_IMM) {
1986 ASSERT_MSG(instr.alu_half_imm.ftz == 0, "Unimplemented");
1987 } else {
1988 ASSERT_MSG(instr.alu_half_imm.precision == Tegra::Shader::HalfPrecision::None,
1989 "Unimplemented");
1990 }
1991
1992 const std::string op_a = GetHalfFloat(
1993 regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.alu_half_imm.type_a,
1994 instr.alu_half_imm.abs_a != 0, instr.alu_half_imm.negate_a != 0);
1995
1996 const std::string op_b = UnpackHalfImmediate(instr, true);
1997
1998 const std::string result = [&]() {
1999 switch (opcode->GetId()) {
2000 case OpCode::Id::HADD2_IMM:
2001 return op_a + " + " + op_b;
2002 case OpCode::Id::HMUL2_IMM:
2003 return op_a + " * " + op_b;
2004 default:
2005 UNREACHABLE();
2006 return std::string("0");
2007 }
2008 }();
2009
2010 regs.SetRegisterToHalfFloat(instr.gpr0, 0, result, instr.alu_half_imm.merge, 1, 1,
2011 instr.alu_half_imm.saturate != 0);
2012 break;
2013 }
1751 case OpCode::Type::Ffma: { 2014 case OpCode::Type::Ffma: {
1752 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 2015 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
1753 std::string op_b = instr.ffma.negate_b ? "-" : ""; 2016 std::string op_b = instr.ffma.negate_b ? "-" : "";
@@ -1792,6 +2055,59 @@ private:
1792 instr.alu.saturate_d); 2055 instr.alu.saturate_d);
1793 break; 2056 break;
1794 } 2057 }
2058 case OpCode::Type::Hfma2: {
2059 if (opcode->GetId() == OpCode::Id::HFMA2_RR) {
2060 ASSERT_MSG(instr.hfma2.rr.precision == Tegra::Shader::HalfPrecision::None,
2061 "Unimplemented");
2062 } else {
2063 ASSERT_MSG(instr.hfma2.precision == Tegra::Shader::HalfPrecision::None,
2064 "Unimplemented");
2065 }
2066 const bool saturate = opcode->GetId() == OpCode::Id::HFMA2_RR
2067 ? instr.hfma2.rr.saturate != 0
2068 : instr.hfma2.saturate != 0;
2069
2070 const std::string op_a =
2071 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hfma2.type_a);
2072 std::string op_b, op_c;
2073
2074 switch (opcode->GetId()) {
2075 case OpCode::Id::HFMA2_CR:
2076 op_b = GetHalfFloat(regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
2077 GLSLRegister::Type::UnsignedInteger),
2078 instr.hfma2.type_b, false, instr.hfma2.negate_b);
2079 op_c = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false),
2080 instr.hfma2.type_reg39, false, instr.hfma2.negate_c);
2081 break;
2082 case OpCode::Id::HFMA2_RC:
2083 op_b = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false),
2084 instr.hfma2.type_reg39, false, instr.hfma2.negate_b);
2085 op_c = GetHalfFloat(regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
2086 GLSLRegister::Type::UnsignedInteger),
2087 instr.hfma2.type_b, false, instr.hfma2.negate_c);
2088 break;
2089 case OpCode::Id::HFMA2_RR:
2090 op_b = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
2091 instr.hfma2.type_b, false, instr.hfma2.negate_b);
2092 op_c = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false),
2093 instr.hfma2.rr.type_c, false, instr.hfma2.rr.negate_c);
2094 break;
2095 case OpCode::Id::HFMA2_IMM_R:
2096 op_b = UnpackHalfImmediate(instr, true);
2097 op_c = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false),
2098 instr.hfma2.type_reg39, false, instr.hfma2.negate_c);
2099 break;
2100 default:
2101 UNREACHABLE();
2102 op_c = op_b = "vec2(0)";
2103 break;
2104 }
2105
2106 const std::string result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
2107
2108 regs.SetRegisterToHalfFloat(instr.gpr0, 0, result, instr.hfma2.merge, 1, 1, saturate);
2109 break;
2110 }
1795 case OpCode::Type::Conversion: { 2111 case OpCode::Type::Conversion: {
1796 switch (opcode->GetId()) { 2112 switch (opcode->GetId()) {
1797 case OpCode::Id::I2I_R: { 2113 case OpCode::Id::I2I_R: {
@@ -2525,20 +2841,13 @@ private:
2525 break; 2841 break;
2526 } 2842 }
2527 case OpCode::Type::FloatSetPredicate: { 2843 case OpCode::Type::FloatSetPredicate: {
2528 std::string op_a = instr.fsetp.neg_a ? "-" : ""; 2844 const std::string op_a =
2529 op_a += regs.GetRegisterAsFloat(instr.gpr8); 2845 GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8), instr.fsetp.abs_a != 0,
2530 2846 instr.fsetp.neg_a != 0);
2531 if (instr.fsetp.abs_a) {
2532 op_a = "abs(" + op_a + ')';
2533 }
2534 2847
2535 std::string op_b{}; 2848 std::string op_b;
2536 2849
2537 if (instr.is_b_imm) { 2850 if (instr.is_b_imm) {
2538 if (instr.fsetp.neg_b) {
2539 // Only the immediate version of fsetp has a neg_b bit.
2540 op_b += '-';
2541 }
2542 op_b += '(' + GetImmediate19(instr) + ')'; 2851 op_b += '(' + GetImmediate19(instr) + ')';
2543 } else { 2852 } else {
2544 if (instr.is_b_gpr) { 2853 if (instr.is_b_gpr) {
@@ -2611,6 +2920,51 @@ private:
2611 } 2920 }
2612 break; 2921 break;
2613 } 2922 }
2923 case OpCode::Type::HalfSetPredicate: {
2924 ASSERT_MSG(instr.hsetp2.ftz == 0, "Unimplemented");
2925
2926 const std::string op_a =
2927 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hsetp2.type_a,
2928 instr.hsetp2.abs_a, instr.hsetp2.negate_a);
2929
2930 const std::string op_b = [&]() {
2931 switch (opcode->GetId()) {
2932 case OpCode::Id::HSETP2_R:
2933 return GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
2934 instr.hsetp2.type_b, instr.hsetp2.abs_a,
2935 instr.hsetp2.negate_b);
2936 default:
2937 UNREACHABLE();
2938 return std::string("vec2(0)");
2939 }
2940 }();
2941
2942 // We can't use the constant predicate as destination.
2943 ASSERT(instr.hsetp2.pred3 != static_cast<u64>(Pred::UnusedIndex));
2944
2945 const std::string second_pred =
2946 GetPredicateCondition(instr.hsetp2.pred39, instr.hsetp2.neg_pred != 0);
2947
2948 const std::string combiner = GetPredicateCombiner(instr.hsetp2.op);
2949
2950 const std::string component_combiner = instr.hsetp2.h_and ? "&&" : "||";
2951 const std::string predicate =
2952 '(' + GetPredicateComparison(instr.hsetp2.cond, op_a + ".x", op_b + ".x") + ' ' +
2953 component_combiner + ' ' +
2954 GetPredicateComparison(instr.hsetp2.cond, op_a + ".y", op_b + ".y") + ')';
2955
2956 // Set the primary predicate to the result of Predicate OP SecondPredicate
2957 SetPredicate(instr.hsetp2.pred3,
2958 '(' + predicate + ") " + combiner + " (" + second_pred + ')');
2959
2960 if (instr.hsetp2.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
2961 // Set the secondary predicate to the result of !Predicate OP SecondPredicate,
2962 // if enabled
2963 SetPredicate(instr.hsetp2.pred0,
2964 "!(" + predicate + ") " + combiner + " (" + second_pred + ')');
2965 }
2966 break;
2967 }
2614 case OpCode::Type::PredicateSetRegister: { 2968 case OpCode::Type::PredicateSetRegister: {
2615 const std::string op_a = 2969 const std::string op_a =
2616 GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0); 2970 GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0);
@@ -2689,33 +3043,24 @@ private:
2689 break; 3043 break;
2690 } 3044 }
2691 case OpCode::Type::FloatSet: { 3045 case OpCode::Type::FloatSet: {
2692 std::string op_a = instr.fset.neg_a ? "-" : ""; 3046 const std::string op_a = GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8),
2693 op_a += regs.GetRegisterAsFloat(instr.gpr8); 3047 instr.fset.abs_a != 0, instr.fset.neg_a != 0);
2694
2695 if (instr.fset.abs_a) {
2696 op_a = "abs(" + op_a + ')';
2697 }
2698 3048
2699 std::string op_b = instr.fset.neg_b ? "-" : ""; 3049 std::string op_b;
2700 3050
2701 if (instr.is_b_imm) { 3051 if (instr.is_b_imm) {
2702 const std::string imm = GetImmediate19(instr); 3052 const std::string imm = GetImmediate19(instr);
2703 if (instr.fset.neg_imm) 3053 op_b = imm;
2704 op_b += "(-" + imm + ')';
2705 else
2706 op_b += imm;
2707 } else { 3054 } else {
2708 if (instr.is_b_gpr) { 3055 if (instr.is_b_gpr) {
2709 op_b += regs.GetRegisterAsFloat(instr.gpr20); 3056 op_b = regs.GetRegisterAsFloat(instr.gpr20);
2710 } else { 3057 } else {
2711 op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, 3058 op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
2712 GLSLRegister::Type::Float); 3059 GLSLRegister::Type::Float);
2713 } 3060 }
2714 } 3061 }
2715 3062
2716 if (instr.fset.abs_b) { 3063 op_b = GetOperandAbsNeg(op_b, instr.fset.abs_b != 0, instr.fset.neg_b != 0);
2717 op_b = "abs(" + op_b + ')';
2718 }
2719 3064
2720 // The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the 3065 // The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the
2721 // condition is true, and to 0 otherwise. 3066 // condition is true, and to 0 otherwise.
@@ -2771,6 +3116,50 @@ private:
2771 } 3116 }
2772 break; 3117 break;
2773 } 3118 }
3119 case OpCode::Type::HalfSet: {
3120 ASSERT_MSG(instr.hset2.ftz == 0, "Unimplemented");
3121
3122 const std::string op_a =
3123 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hset2.type_a,
3124 instr.hset2.abs_a != 0, instr.hset2.negate_a != 0);
3125
3126 const std::string op_b = [&]() {
3127 switch (opcode->GetId()) {
3128 case OpCode::Id::HSET2_R:
3129 return GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
3130 instr.hset2.type_b, instr.hset2.abs_b != 0,
3131 instr.hset2.negate_b != 0);
3132 default:
3133 UNREACHABLE();
3134 return std::string("vec2(0)");
3135 }
3136 }();
3137
3138 const std::string second_pred =
3139 GetPredicateCondition(instr.hset2.pred39, instr.hset2.neg_pred != 0);
3140
3141 const std::string combiner = GetPredicateCombiner(instr.hset2.op);
3142
3143 // HSET2 operates on each half float in the pack.
3144 std::string result;
3145 for (int i = 0; i < 2; ++i) {
3146 const std::string float_value = i == 0 ? "0x00003c00" : "0x3c000000";
3147 const std::string integer_value = i == 0 ? "0x0000ffff" : "0xffff0000";
3148 const std::string value = instr.hset2.bf == 1 ? float_value : integer_value;
3149
3150 const std::string comp = std::string(".") + "xy"[i];
3151 const std::string predicate =
3152 "((" + GetPredicateComparison(instr.hset2.cond, op_a + comp, op_b + comp) +
3153 ") " + combiner + " (" + second_pred + "))";
3154
3155 result += '(' + predicate + " ? " + value + " : 0)";
3156 if (i == 0) {
3157 result += " | ";
3158 }
3159 }
3160 regs.SetRegisterToInteger(instr.gpr0, false, 0, '(' + result + ')', 1, 1);
3161 break;
3162 }
2774 case OpCode::Type::Xmad: { 3163 case OpCode::Type::Xmad: {
2775 ASSERT_MSG(!instr.xmad.sign_a, "Unimplemented"); 3164 ASSERT_MSG(!instr.xmad.sign_a, "Unimplemented");
2776 ASSERT_MSG(!instr.xmad.sign_b, "Unimplemented"); 3165 ASSERT_MSG(!instr.xmad.sign_b, "Unimplemented");
@@ -2979,16 +3368,32 @@ private:
2979 // The SSY opcode tells the GPU where to re-converge divergent execution paths, it 3368 // The SSY opcode tells the GPU where to re-converge divergent execution paths, it
2980 // sets the target of the jump that the SYNC instruction will make. The SSY opcode 3369 // sets the target of the jump that the SYNC instruction will make. The SSY opcode
2981 // has a similar structure to the BRA opcode. 3370 // has a similar structure to the BRA opcode.
2982 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer SSY is not supported"); 3371 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer flow is not supported");
3372
3373 const u32 target = offset + instr.bra.GetBranchTarget();
3374 EmitPushToFlowStack(target);
3375 break;
3376 }
3377 case OpCode::Id::PBK: {
3378 // PBK pushes to a stack the address where BRK will jump to. This shares stack with
3379 // SSY but using SYNC on a PBK address will kill the shader execution. We don't
3380 // emulate this because it's very unlikely a driver will emit such invalid shader.
3381 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer PBK is not supported");
2983 3382
2984 const u32 target = offset + instr.bra.GetBranchTarget(); 3383 const u32 target = offset + instr.bra.GetBranchTarget();
2985 EmitPushToSSYStack(target); 3384 EmitPushToFlowStack(target);
2986 break; 3385 break;
2987 } 3386 }
2988 case OpCode::Id::SYNC: { 3387 case OpCode::Id::SYNC: {
2989 // The SYNC opcode jumps to the address previously set by the SSY opcode 3388 // The SYNC opcode jumps to the address previously set by the SSY opcode
2990 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); 3389 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
2991 EmitPopFromSSYStack(); 3390 EmitPopFromFlowStack();
3391 break;
3392 }
3393 case OpCode::Id::BRK: {
3394 // The BRK opcode jumps to the address previously set by the PBK opcode
3395 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
3396 EmitPopFromFlowStack();
2992 break; 3397 break;
2993 } 3398 }
2994 case OpCode::Id::DEPBAR: { 3399 case OpCode::Id::DEPBAR: {
@@ -2998,87 +3403,51 @@ private:
2998 break; 3403 break;
2999 } 3404 }
3000 case OpCode::Id::VMAD: { 3405 case OpCode::Id::VMAD: {
3001 const bool signed_a = instr.vmad.signed_a == 1; 3406 const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1;
3002 const bool signed_b = instr.vmad.signed_b == 1; 3407 const std::string op_a = GetVideoOperandA(instr);
3003 const bool result_signed = signed_a || signed_b; 3408 const std::string op_b = GetVideoOperandB(instr);
3004 boost::optional<std::string> forced_result;
3005
3006 auto Unpack = [&](const std::string& op, bool is_chunk, bool is_signed,
3007 Tegra::Shader::VmadType type, u64 byte_height) {
3008 const std::string value = [&]() {
3009 if (!is_chunk) {
3010 const auto offset = static_cast<u32>(byte_height * 8);
3011 return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)";
3012 }
3013 const std::string zero = "0";
3014
3015 switch (type) {
3016 case Tegra::Shader::VmadType::Size16_Low:
3017 return '(' + op + " & 0xffff)";
3018 case Tegra::Shader::VmadType::Size16_High:
3019 return '(' + op + " >> 16)";
3020 case Tegra::Shader::VmadType::Size32:
3021 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
3022 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
3023 // explanation is found: assert.
3024 UNREACHABLE_MSG("Unimplemented");
3025 return zero;
3026 case Tegra::Shader::VmadType::Invalid:
3027 // Note(Rodrigo): This flag is invalid according to nvdisasm. From my
3028 // testing (even though it's invalid) this makes the whole instruction
3029 // assign zero to target register.
3030 forced_result = boost::make_optional(zero);
3031 return zero;
3032 default:
3033 UNREACHABLE();
3034 return zero;
3035 }
3036 }();
3037
3038 if (is_signed) {
3039 return "int(" + value + ')';
3040 }
3041 return value;
3042 };
3043
3044 const std::string op_a = Unpack(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
3045 instr.vmad.is_byte_chunk_a != 0, signed_a,
3046 instr.vmad.type_a, instr.vmad.byte_height_a);
3047
3048 std::string op_b;
3049 if (instr.vmad.use_register_b) {
3050 op_b = Unpack(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
3051 instr.vmad.is_byte_chunk_b != 0, signed_b, instr.vmad.type_b,
3052 instr.vmad.byte_height_b);
3053 } else {
3054 op_b = '(' +
3055 std::to_string(signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
3056 : instr.alu.GetImm20_16()) +
3057 ')';
3058 }
3059
3060 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed); 3409 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed);
3061 3410
3062 std::string result; 3411 std::string result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
3063 if (forced_result) {
3064 result = *forced_result;
3065 } else {
3066 result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
3067 3412
3068 switch (instr.vmad.shr) { 3413 switch (instr.vmad.shr) {
3069 case Tegra::Shader::VmadShr::Shr7: 3414 case Tegra::Shader::VmadShr::Shr7:
3070 result = '(' + result + " >> 7)"; 3415 result = '(' + result + " >> 7)";
3071 break; 3416 break;
3072 case Tegra::Shader::VmadShr::Shr15: 3417 case Tegra::Shader::VmadShr::Shr15:
3073 result = '(' + result + " >> 15)"; 3418 result = '(' + result + " >> 15)";
3074 break; 3419 break;
3075 }
3076 } 3420 }
3421
3077 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, 3422 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
3078 instr.vmad.saturate == 1, 0, Register::Size::Word, 3423 instr.vmad.saturate == 1, 0, Register::Size::Word,
3079 instr.vmad.cc); 3424 instr.vmad.cc);
3080 break; 3425 break;
3081 } 3426 }
3427 case OpCode::Id::VSETP: {
3428 const std::string op_a = GetVideoOperandA(instr);
3429 const std::string op_b = GetVideoOperandB(instr);
3430
3431 // We can't use the constant predicate as destination.
3432 ASSERT(instr.vsetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
3433
3434 const std::string second_pred = GetPredicateCondition(instr.vsetp.pred39, false);
3435
3436 const std::string combiner = GetPredicateCombiner(instr.vsetp.op);
3437
3438 const std::string predicate = GetPredicateComparison(instr.vsetp.cond, op_a, op_b);
3439 // Set the primary predicate to the result of Predicate OP SecondPredicate
3440 SetPredicate(instr.vsetp.pred3,
3441 '(' + predicate + ") " + combiner + " (" + second_pred + ')');
3442
3443 if (instr.vsetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
3444 // Set the secondary predicate to the result of !Predicate OP SecondPredicate,
3445 // if enabled
3446 SetPredicate(instr.vsetp.pred0,
3447 "!(" + predicate + ") " + combiner + " (" + second_pred + ')');
3448 }
3449 break;
3450 }
3082 default: { 3451 default: {
3083 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); 3452 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName());
3084 UNREACHABLE(); 3453 UNREACHABLE();
@@ -3142,11 +3511,11 @@ private:
3142 labels.insert(subroutine.begin); 3511 labels.insert(subroutine.begin);
3143 shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;"); 3512 shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;");
3144 3513
3145 // TODO(Subv): Figure out the actual depth of the SSY stack, for now it seems 3514 // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
3146 // unlikely that shaders will use 20 nested SSYs. 3515 // unlikely that shaders will use 20 nested SSYs and PBKs.
3147 constexpr u32 SSY_STACK_SIZE = 20; 3516 constexpr u32 FLOW_STACK_SIZE = 20;
3148 shader.AddLine("uint ssy_stack[" + std::to_string(SSY_STACK_SIZE) + "];"); 3517 shader.AddLine("uint flow_stack[" + std::to_string(FLOW_STACK_SIZE) + "];");
3149 shader.AddLine("uint ssy_stack_top = 0u;"); 3518 shader.AddLine("uint flow_stack_top = 0u;");
3150 3519
3151 shader.AddLine("while (true) {"); 3520 shader.AddLine("while (true) {");
3152 ++shader.scope; 3521 ++shader.scope;
@@ -3213,7 +3582,7 @@ private:
3213 3582
3214 // Declarations 3583 // Declarations
3215 std::set<std::string> declr_predicates; 3584 std::set<std::string> declr_predicates;
3216}; // namespace Decompiler 3585}; // namespace OpenGL::GLShader::Decompiler
3217 3586
3218std::string GetCommonDeclarations() { 3587std::string GetCommonDeclarations() {
3219 return fmt::format("#define MAX_CONSTBUFFER_ELEMENTS {}\n", 3588 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..dfb562706 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -19,14 +19,14 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
19 out += Decompiler::GetCommonDeclarations(); 19 out += Decompiler::GetCommonDeclarations();
20 20
21 out += R"( 21 out += R"(
22out gl_PerVertex { 22
23 vec4 gl_Position; 23layout (location = 0) out vec4 position;
24};
25 24
26layout(std140) uniform vs_config { 25layout(std140) uniform vs_config {
27 vec4 viewport_flip; 26 vec4 viewport_flip;
28 uvec4 instance_id; 27 uvec4 instance_id;
29 uvec4 flip_stage; 28 uvec4 flip_stage;
29 uvec4 alpha_test;
30}; 30};
31)"; 31)";
32 32
@@ -96,10 +96,14 @@ out gl_PerVertex {
96 vec4 gl_Position; 96 vec4 gl_Position;
97}; 97};
98 98
99layout (location = 0) in vec4 gs_position[];
100layout (location = 0) out vec4 position;
101
99layout (std140) uniform gs_config { 102layout (std140) uniform gs_config {
100 vec4 viewport_flip; 103 vec4 viewport_flip;
101 uvec4 instance_id; 104 uvec4 instance_id;
102 uvec4 flip_stage; 105 uvec4 flip_stage;
106 uvec4 alpha_test;
103}; 107};
104 108
105void main() { 109void main() {
@@ -131,12 +135,39 @@ layout(location = 5) out vec4 FragColor5;
131layout(location = 6) out vec4 FragColor6; 135layout(location = 6) out vec4 FragColor6;
132layout(location = 7) out vec4 FragColor7; 136layout(location = 7) out vec4 FragColor7;
133 137
138layout (location = 0) in vec4 position;
139
134layout (std140) uniform fs_config { 140layout (std140) uniform fs_config {
135 vec4 viewport_flip; 141 vec4 viewport_flip;
136 uvec4 instance_id; 142 uvec4 instance_id;
137 uvec4 flip_stage; 143 uvec4 flip_stage;
144 uvec4 alpha_test;
138}; 145};
139 146
147bool AlphaFunc(in float value) {
148 float ref = uintBitsToFloat(alpha_test[2]);
149 switch (alpha_test[1]) {
150 case 1:
151 return false;
152 case 2:
153 return value < ref;
154 case 3:
155 return value == ref;
156 case 4:
157 return value <= ref;
158 case 5:
159 return value > ref;
160 case 6:
161 return value != ref;
162 case 7:
163 return value >= ref;
164 case 8:
165 return true;
166 default:
167 return false;
168 }
169}
170
140void main() { 171void main() {
141 exec_fragment(); 172 exec_fragment();
142} 173}
@@ -145,4 +176,4 @@ void main() {
145 out += program.first; 176 out += program.first;
146 return {out, program.second}; 177 return {out, program.second};
147} 178}
148} // namespace OpenGL::GLShader \ No newline at end of file 179} // 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/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 1fe26a2a9..ba6c6919a 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -21,6 +21,8 @@ OpenGLState::OpenGLState() {
21 depth.test_enabled = false; 21 depth.test_enabled = false;
22 depth.test_func = GL_LESS; 22 depth.test_func = GL_LESS;
23 depth.write_mask = GL_TRUE; 23 depth.write_mask = GL_TRUE;
24 depth.depth_range_near = 0.0f;
25 depth.depth_range_far = 1.0f;
24 26
25 color_mask.red_enabled = GL_TRUE; 27 color_mask.red_enabled = GL_TRUE;
26 color_mask.green_enabled = GL_TRUE; 28 color_mask.green_enabled = GL_TRUE;
@@ -119,6 +121,12 @@ void OpenGLState::Apply() const {
119 glDepthMask(depth.write_mask); 121 glDepthMask(depth.write_mask);
120 } 122 }
121 123
124 // Depth range
125 if (depth.depth_range_near != cur_state.depth.depth_range_near ||
126 depth.depth_range_far != cur_state.depth.depth_range_far) {
127 glDepthRange(depth.depth_range_near, depth.depth_range_far);
128 }
129
122 // Color mask 130 // Color mask
123 if (color_mask.red_enabled != cur_state.color_mask.red_enabled || 131 if (color_mask.red_enabled != cur_state.color_mask.red_enabled ||
124 color_mask.green_enabled != cur_state.color_mask.green_enabled || 132 color_mask.green_enabled != cur_state.color_mask.green_enabled ||
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index dc21a2ee3..daf7eb533 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -42,9 +42,11 @@ public:
42 } cull; 42 } cull;
43 43
44 struct { 44 struct {
45 bool test_enabled; // GL_DEPTH_TEST 45 bool test_enabled; // GL_DEPTH_TEST
46 GLenum test_func; // GL_DEPTH_FUNC 46 GLenum test_func; // GL_DEPTH_FUNC
47 GLboolean write_mask; // GL_DEPTH_WRITEMASK 47 GLboolean write_mask; // GL_DEPTH_WRITEMASK
48 GLfloat depth_range_near; // GL_DEPTH_RANGE
49 GLfloat depth_range_far; // GL_DEPTH_RANGE
48 } depth; 50 } depth;
49 51
50 struct { 52 struct {
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..550ca856c 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -142,7 +142,6 @@ void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
142 const u32 blocks_on_x = div_ceil(width, block_x_elements); 142 const u32 blocks_on_x = div_ceil(width, block_x_elements);
143 const u32 blocks_on_y = div_ceil(height, block_y_elements); 143 const u32 blocks_on_y = div_ceil(height, block_y_elements);
144 const u32 blocks_on_z = div_ceil(depth, block_z_elements); 144 const u32 blocks_on_z = div_ceil(depth, block_z_elements);
145 const u32 blocks = blocks_on_x * blocks_on_y * blocks_on_z;
146 const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z; 145 const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
147 const u32 xy_block_size = gob_size * block_height; 146 const u32 xy_block_size = gob_size * block_height;
148 const u32 block_size = xy_block_size * block_depth; 147 const u32 block_size = xy_block_size * block_depth;
@@ -237,6 +236,46 @@ std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pix
237 return unswizzled_data; 236 return unswizzled_data;
238} 237}
239 238
239void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
240 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
241 u32 block_height) {
242 const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + 63) / 64};
243 for (u32 line = 0; line < subrect_height; ++line) {
244 const u32 gob_address_y =
245 (line / (8 * block_height)) * 512 * block_height * image_width_in_gobs +
246 (line % (8 * block_height) / 8) * 512;
247 const auto& table = legacy_swizzle_table[line % 8];
248 for (u32 x = 0; x < subrect_width; ++x) {
249 const u32 gob_address = gob_address_y + (x * bytes_per_pixel / 64) * 512 * block_height;
250 const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % 64];
251 const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
252 const VAddr dest_addr = swizzled_data + swizzled_offset;
253
254 Memory::CopyBlock(dest_addr, source_line, bytes_per_pixel);
255 }
256 }
257}
258
259void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
260 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
261 u32 block_height, u32 offset_x, u32 offset_y) {
262 for (u32 line = 0; line < subrect_height; ++line) {
263 const u32 y2 = line + offset_y;
264 const u32 gob_address_y =
265 (y2 / (8 * block_height)) * 512 * block_height + (y2 % (8 * block_height) / 8) * 512;
266 const auto& table = legacy_swizzle_table[y2 % 8];
267 for (u32 x = 0; x < subrect_width; ++x) {
268 const u32 x2 = (x + offset_x) * bytes_per_pixel;
269 const u32 gob_address = gob_address_y + (x2 / 64) * 512 * block_height;
270 const u32 swizzled_offset = gob_address + table[x2 % 64];
271 const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel;
272 const VAddr source_addr = swizzled_data + swizzled_offset;
273
274 Memory::CopyBlock(dest_line, source_addr, bytes_per_pixel);
275 }
276 }
277}
278
240std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, 279std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
241 u32 height) { 280 u32 height) {
242 std::vector<u8> rgba_data; 281 std::vector<u8> rgba_data;
@@ -280,13 +319,13 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
280std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, 319std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
281 u32 block_height, u32 block_depth) { 320 u32 block_height, u32 block_depth) {
282 if (tiled) { 321 if (tiled) {
283 const u32 gobs_in_x = 64 / bytes_per_pixel; 322 const u32 gobs_in_x = 64;
284 const u32 gobs_in_y = 8; 323 const u32 gobs_in_y = 8;
285 const u32 gobs_in_z = 1; 324 const u32 gobs_in_z = 1;
286 const u32 aligned_width = Common::AlignUp(width, gobs_in_x); 325 const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x);
287 const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height); 326 const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
288 const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth); 327 const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
289 return aligned_width * aligned_height * aligned_depth * bytes_per_pixel; 328 return aligned_width * aligned_height * aligned_depth;
290 } else { 329 } else {
291 return width * height * depth * bytes_per_pixel; 330 return width * height * depth * bytes_per_pixel;
292 } 331 }
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/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index e8ab23326..39eef8858 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -8,7 +8,6 @@
8 8
9#include "common/microprofile.h" 9#include "common/microprofile.h"
10#include "common/scm_rev.h" 10#include "common/scm_rev.h"
11#include "common/string_util.h"
12#include "core/core.h" 11#include "core/core.h"
13#include "core/frontend/framebuffer_layout.h" 12#include "core/frontend/framebuffer_layout.h"
14#include "core/settings.h" 13#include "core/settings.h"
@@ -107,9 +106,8 @@ private:
107GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) 106GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
108 : QWidget(parent), child(nullptr), emu_thread(emu_thread) { 107 : QWidget(parent), child(nullptr), emu_thread(emu_thread) {
109 108
110 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, 109 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
111 Common::g_scm_branch, Common::g_scm_desc); 110 .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
112 setWindowTitle(QString::fromStdString(window_title));
113 setAttribute(Qt::WA_AcceptTouchEvents); 111 setAttribute(Qt::WA_AcceptTouchEvents);
114 112
115 InputCommon::Init(); 113 InputCommon::Init();
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 71c6ebb41..d4fd60a73 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -4,6 +4,7 @@
4 4
5#include <QSettings> 5#include <QSettings>
6#include "common/file_util.h" 6#include "common/file_util.h"
7#include "core/hle/service/acc/profile_manager.h"
7#include "input_common/main.h" 8#include "input_common/main.h"
8#include "yuzu/configuration/config.h" 9#include "yuzu/configuration/config.h"
9#include "yuzu/ui_settings.h" 10#include "yuzu/ui_settings.h"
@@ -12,11 +13,16 @@ Config::Config() {
12 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 13 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
13 qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; 14 qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
14 FileUtil::CreateFullPath(qt_config_loc); 15 FileUtil::CreateFullPath(qt_config_loc);
15 qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat); 16 qt_config =
17 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
16 18
17 Reload(); 19 Reload();
18} 20}
19 21
22Config::~Config() {
23 Save();
24}
25
20const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { 26const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
21 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, 27 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q,
22 Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, 28 Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T,
@@ -122,7 +128,11 @@ void Config::ReadValues() {
122 128
123 qt_config->beginGroup("System"); 129 qt_config->beginGroup("System");
124 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); 130 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
125 Settings::values.username = qt_config->value("username", "yuzu").toString().toStdString(); 131 Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool();
132
133 Settings::values.current_user = std::clamp<int>(qt_config->value("current_user", 0).toInt(), 0,
134 Service::Account::MAX_USERS - 1);
135
126 Settings::values.language_index = qt_config->value("language_index", 1).toInt(); 136 Settings::values.language_index = qt_config->value("language_index", 1).toInt();
127 qt_config->endGroup(); 137 qt_config->endGroup();
128 138
@@ -258,7 +268,9 @@ void Config::SaveValues() {
258 268
259 qt_config->beginGroup("System"); 269 qt_config->beginGroup("System");
260 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); 270 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode);
261 qt_config->setValue("username", QString::fromStdString(Settings::values.username)); 271 qt_config->setValue("enable_nfc", Settings::values.enable_nfc);
272 qt_config->setValue("current_user", Settings::values.current_user);
273
262 qt_config->setValue("language_index", Settings::values.language_index); 274 qt_config->setValue("language_index", Settings::values.language_index);
263 qt_config->endGroup(); 275 qt_config->endGroup();
264 276
@@ -335,9 +347,3 @@ void Config::Reload() {
335void Config::Save() { 347void Config::Save() {
336 SaveValues(); 348 SaveValues();
337} 349}
338
339Config::~Config() {
340 Save();
341
342 delete qt_config;
343}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index cbf745ea2..9c99c1b75 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <memory>
8#include <string> 9#include <string>
9#include <QVariant> 10#include <QVariant>
10#include "core/settings.h" 11#include "core/settings.h"
@@ -12,12 +13,6 @@
12class QSettings; 13class QSettings;
13 14
14class Config { 15class Config {
15 QSettings* qt_config;
16 std::string qt_config_loc;
17
18 void ReadValues();
19 void SaveValues();
20
21public: 16public:
22 Config(); 17 Config();
23 ~Config(); 18 ~Config();
@@ -27,4 +22,11 @@ public:
27 22
28 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; 23 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
29 static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; 24 static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
25
26private:
27 void ReadValues();
28 void SaveValues();
29
30 std::unique_ptr<QSettings> qt_config;
31 std::string qt_config_loc;
30}; 32};
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index f5db9e55b..537d6e576 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -31,6 +31,7 @@ void ConfigureGeneral::setConfiguration() {
31 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); 31 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
32 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); 32 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
33 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); 33 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
34 ui->enable_nfc->setChecked(Settings::values.enable_nfc);
34} 35}
35 36
36void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) { 37void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
@@ -45,4 +46,5 @@ void ConfigureGeneral::applyConfiguration() {
45 46
46 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); 47 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
47 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); 48 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
49 Settings::values.enable_nfc = ui->enable_nfc->isChecked();
48} 50}
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index 1775c4d40..b82fffde8 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -68,19 +68,26 @@
68 <property name="title"> 68 <property name="title">
69 <string>Emulation</string> 69 <string>Emulation</string>
70 </property> 70 </property>
71 <layout class="QHBoxLayout" name="EmulationHorizontalLayout"> 71 <layout class="QHBoxLayout" name="EmulationHorizontalLayout">
72 <item>
73 <layout class="QVBoxLayout" name="EmulationVerticalLayout">
74 <item>
75 <widget class="QCheckBox" name="use_docked_mode">
76 <property name="text">
77 <string>Enable docked mode</string>
78 </property>
79 </widget>
80 </item>
72 <item> 81 <item>
73 <layout class="QVBoxLayout" name="EmulationVerticalLayout"> 82 <widget class="QCheckBox" name="enable_nfc">
74 <item> 83 <property name="text">
75 <widget class="QCheckBox" name="use_docked_mode"> 84 <string>Enable NFC</string>
76 <property name="text"> 85 </property>
77 <string>Enable docked mode</string> 86 </widget>
78 </property>
79 </widget>
80 </item>
81 </layout>
82 </item> 87 </item>
83 </layout> 88 </layout>
89 </item>
90 </layout>
84 </widget> 91 </widget>
85 </item> 92 </item>
86 <item> 93 <item>
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index e9ed9c38f..20ffb0a9a 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -2,14 +2,27 @@
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 <algorithm>
6#include <QFileDialog>
7#include <QGraphicsItem>
8#include <QGraphicsScene>
9#include <QInputDialog>
5#include <QMessageBox> 10#include <QMessageBox>
11#include <QStandardItemModel>
12#include <QTreeView>
13#include <QVBoxLayout>
14#include "common/common_paths.h"
15#include "common/logging/backend.h"
16#include "common/string_util.h"
6#include "core/core.h" 17#include "core/core.h"
18#include "core/hle/service/acc/profile_manager.h"
7#include "core/settings.h" 19#include "core/settings.h"
8#include "ui_configure_system.h" 20#include "ui_configure_system.h"
9#include "yuzu/configuration/configure_system.h" 21#include "yuzu/configuration/configure_system.h"
10#include "yuzu/main.h" 22#include "yuzu/main.h"
11 23
12static const std::array<int, 12> days_in_month = {{ 24namespace {
25constexpr std::array<int, 12> days_in_month = {{
13 31, 26 31,
14 29, 27 29,
15 31, 28 31,
@@ -24,13 +37,82 @@ static const std::array<int, 12> days_in_month = {{
24 31, 37 31,
25}}; 38}};
26 39
27ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) { 40// Same backup JPEG used by acc IProfile::GetImage if no jpeg found
41constexpr std::array<u8, 107> backup_jpeg{
42 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
43 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
44 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
45 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
46 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
47 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
48 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
49};
50
51std::string GetImagePath(Service::Account::UUID uuid) {
52 return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
53 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
54}
55
56std::string GetAccountUsername(const Service::Account::ProfileManager& manager,
57 Service::Account::UUID uuid) {
58 Service::Account::ProfileBase profile;
59 if (!manager.GetProfileBase(uuid, profile)) {
60 return "";
61 }
62
63 return Common::StringFromFixedZeroTerminatedBuffer(
64 reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
65}
66} // Anonymous namespace
67
68ConfigureSystem::ConfigureSystem(QWidget* parent)
69 : QWidget(parent), ui(new Ui::ConfigureSystem),
70 profile_manager(std::make_unique<Service::Account::ProfileManager>()) {
28 ui->setupUi(this); 71 ui->setupUi(this);
29 connect(ui->combo_birthmonth, 72 connect(ui->combo_birthmonth,
30 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 73 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
31 &ConfigureSystem::updateBirthdayComboBox); 74 &ConfigureSystem::UpdateBirthdayComboBox);
32 connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, 75 connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
33 &ConfigureSystem::refreshConsoleID); 76 &ConfigureSystem::RefreshConsoleID);
77
78 layout = new QVBoxLayout;
79 tree_view = new QTreeView;
80 item_model = new QStandardItemModel(tree_view);
81 tree_view->setModel(item_model);
82
83 tree_view->setAlternatingRowColors(true);
84 tree_view->setSelectionMode(QHeaderView::SingleSelection);
85 tree_view->setSelectionBehavior(QHeaderView::SelectRows);
86 tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel);
87 tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel);
88 tree_view->setSortingEnabled(true);
89 tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
90 tree_view->setUniformRowHeights(true);
91 tree_view->setIconSize({64, 64});
92 tree_view->setContextMenuPolicy(Qt::NoContextMenu);
93
94 item_model->insertColumns(0, 1);
95 item_model->setHeaderData(0, Qt::Horizontal, "Users");
96
97 // We must register all custom types with the Qt Automoc system so that we are able to use it
98 // with signals/slots. In this case, QList falls under the umbrells of custom types.
99 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
100
101 layout->setContentsMargins(0, 0, 0, 0);
102 layout->setSpacing(0);
103 layout->addWidget(tree_view);
104
105 ui->scrollArea->setLayout(layout);
106
107 connect(tree_view, &QTreeView::clicked, this, &ConfigureSystem::SelectUser);
108
109 connect(ui->pm_add, &QPushButton::pressed, this, &ConfigureSystem::AddUser);
110 connect(ui->pm_rename, &QPushButton::pressed, this, &ConfigureSystem::RenameUser);
111 connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureSystem::DeleteUser);
112 connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureSystem::SetUserImage);
113
114 scene = new QGraphicsScene;
115 ui->current_user_icon->setScene(scene);
34 116
35 this->setConfiguration(); 117 this->setConfiguration();
36} 118}
@@ -39,8 +121,58 @@ ConfigureSystem::~ConfigureSystem() = default;
39 121
40void ConfigureSystem::setConfiguration() { 122void ConfigureSystem::setConfiguration() {
41 enabled = !Core::System::GetInstance().IsPoweredOn(); 123 enabled = !Core::System::GetInstance().IsPoweredOn();
42 ui->edit_username->setText(QString::fromStdString(Settings::values.username)); 124
43 ui->combo_language->setCurrentIndex(Settings::values.language_index); 125 ui->combo_language->setCurrentIndex(Settings::values.language_index);
126
127 item_model->removeRows(0, item_model->rowCount());
128 list_items.clear();
129
130 PopulateUserList();
131 UpdateCurrentUser();
132}
133
134static QPixmap GetIcon(Service::Account::UUID uuid) {
135 const auto icon_url = QString::fromStdString(GetImagePath(uuid));
136 QPixmap icon{icon_url};
137
138 if (!icon) {
139 icon.fill(Qt::black);
140 icon.loadFromData(backup_jpeg.data(), backup_jpeg.size());
141 }
142
143 return icon;
144}
145
146void ConfigureSystem::PopulateUserList() {
147 const auto& profiles = profile_manager->GetAllUsers();
148 for (const auto& user : profiles) {
149 Service::Account::ProfileBase profile;
150 if (!profile_manager->GetProfileBase(user, profile))
151 continue;
152
153 const auto username = Common::StringFromFixedZeroTerminatedBuffer(
154 reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
155
156 list_items.push_back(QList<QStandardItem*>{new QStandardItem{
157 GetIcon(user).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
158 QString::fromStdString(username + '\n' + user.FormatSwitch())}});
159 }
160
161 for (const auto& item : list_items)
162 item_model->appendRow(item);
163}
164
165void ConfigureSystem::UpdateCurrentUser() {
166 ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS);
167
168 const auto& current_user = profile_manager->GetUser(Settings::values.current_user);
169 ASSERT(current_user != std::nullopt);
170 const auto username = GetAccountUsername(*profile_manager, *current_user);
171
172 scene->clear();
173 scene->addPixmap(
174 GetIcon(*current_user).scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
175 ui->current_user_username->setText(QString::fromStdString(username));
44} 176}
45 177
46void ConfigureSystem::ReadSystemSettings() {} 178void ConfigureSystem::ReadSystemSettings() {}
@@ -48,12 +180,12 @@ void ConfigureSystem::ReadSystemSettings() {}
48void ConfigureSystem::applyConfiguration() { 180void ConfigureSystem::applyConfiguration() {
49 if (!enabled) 181 if (!enabled)
50 return; 182 return;
51 Settings::values.username = ui->edit_username->text().toStdString(); 183
52 Settings::values.language_index = ui->combo_language->currentIndex(); 184 Settings::values.language_index = ui->combo_language->currentIndex();
53 Settings::Apply(); 185 Settings::Apply();
54} 186}
55 187
56void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { 188void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) {
57 if (birthmonth_index < 0 || birthmonth_index >= 12) 189 if (birthmonth_index < 0 || birthmonth_index >= 12)
58 return; 190 return;
59 191
@@ -78,7 +210,7 @@ void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) {
78 ui->combo_birthday->setCurrentIndex(birthday_index); 210 ui->combo_birthday->setCurrentIndex(birthday_index);
79} 211}
80 212
81void ConfigureSystem::refreshConsoleID() { 213void ConfigureSystem::RefreshConsoleID() {
82 QMessageBox::StandardButton reply; 214 QMessageBox::StandardButton reply;
83 QString warning_text = tr("This will replace your current virtual Switch with a new one. " 215 QString warning_text = tr("This will replace your current virtual Switch with a new one. "
84 "Your current virtual Switch will not be recoverable. " 216 "Your current virtual Switch will not be recoverable. "
@@ -92,3 +224,129 @@ void ConfigureSystem::refreshConsoleID() {
92 ui->label_console_id->setText( 224 ui->label_console_id->setText(
93 tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); 225 tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper()));
94} 226}
227
228void ConfigureSystem::SelectUser(const QModelIndex& index) {
229 Settings::values.current_user =
230 std::clamp<std::size_t>(index.row(), 0, profile_manager->GetUserCount() - 1);
231
232 UpdateCurrentUser();
233
234 ui->pm_remove->setEnabled(profile_manager->GetUserCount() >= 2);
235 ui->pm_rename->setEnabled(true);
236 ui->pm_set_image->setEnabled(true);
237}
238
239void ConfigureSystem::AddUser() {
240 const auto uuid = Service::Account::UUID::Generate();
241
242 bool ok = false;
243 const auto username =
244 QInputDialog::getText(this, tr("Enter Username"), tr("Enter a username for the new user:"),
245 QLineEdit::Normal, QString(), &ok);
246 if (!ok)
247 return;
248
249 profile_manager->CreateNewUser(uuid, username.toStdString());
250
251 item_model->appendRow(new QStandardItem{
252 GetIcon(uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
253 QString::fromStdString(username.toStdString() + '\n' + uuid.FormatSwitch())});
254}
255
256void ConfigureSystem::RenameUser() {
257 const auto user = tree_view->currentIndex().row();
258 const auto uuid = profile_manager->GetUser(user);
259 ASSERT(uuid != std::nullopt);
260 const auto username = GetAccountUsername(*profile_manager, *uuid);
261
262 Service::Account::ProfileBase profile;
263 if (!profile_manager->GetProfileBase(*uuid, profile))
264 return;
265
266 bool ok = false;
267 const auto new_username =
268 QInputDialog::getText(this, tr("Enter Username"), tr("Enter a new username:"),
269 QLineEdit::Normal, QString::fromStdString(username), &ok);
270
271 if (!ok)
272 return;
273
274 std::fill(profile.username.begin(), profile.username.end(), '\0');
275 const auto username_std = new_username.toStdString();
276 if (username_std.size() > profile.username.size()) {
277 std::copy_n(username_std.begin(), std::min(profile.username.size(), username_std.size()),
278 profile.username.begin());
279 } else {
280 std::copy(username_std.begin(), username_std.end(), profile.username.begin());
281 }
282
283 profile_manager->SetProfileBase(*uuid, profile);
284
285 item_model->setItem(
286 user, 0,
287 new QStandardItem{
288 GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
289 tr("%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. "
290 "00112233-4455-6677-8899-AABBCCDDEEFF))")
291 .arg(QString::fromStdString(username_std),
292 QString::fromStdString(uuid->FormatSwitch()))});
293 UpdateCurrentUser();
294}
295
296void ConfigureSystem::DeleteUser() {
297 const auto index = tree_view->currentIndex().row();
298 const auto uuid = profile_manager->GetUser(index);
299 ASSERT(uuid != std::nullopt);
300 const auto username = GetAccountUsername(*profile_manager, *uuid);
301
302 const auto confirm =
303 QMessageBox::question(this, tr("Confirm Delete"),
304 tr("You are about to delete user with name %1. Are you sure?")
305 .arg(QString::fromStdString(username)));
306
307 if (confirm == QMessageBox::No)
308 return;
309
310 if (Settings::values.current_user == tree_view->currentIndex().row())
311 Settings::values.current_user = 0;
312 UpdateCurrentUser();
313
314 if (!profile_manager->RemoveUser(*uuid))
315 return;
316
317 item_model->removeRows(tree_view->currentIndex().row(), 1);
318 tree_view->clearSelection();
319
320 ui->pm_remove->setEnabled(false);
321 ui->pm_rename->setEnabled(false);
322}
323
324void ConfigureSystem::SetUserImage() {
325 const auto index = tree_view->currentIndex().row();
326 const auto uuid = profile_manager->GetUser(index);
327 ASSERT(uuid != std::nullopt);
328 const auto username = GetAccountUsername(*profile_manager, *uuid);
329
330 const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(),
331 tr("JPEG Images (*.jpg *.jpeg)"));
332
333 if (file.isEmpty())
334 return;
335
336 FileUtil::Delete(GetImagePath(*uuid));
337
338 const auto raw_path =
339 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010";
340 if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
341 FileUtil::Delete(raw_path);
342
343 FileUtil::CreateFullPath(GetImagePath(*uuid));
344 FileUtil::Copy(file.toStdString(), GetImagePath(*uuid));
345
346 item_model->setItem(
347 index, 0,
348 new QStandardItem{
349 GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
350 QString::fromStdString(username + '\n' + uuid->FormatSwitch())});
351 UpdateCurrentUser();
352}
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index f13de17d4..07764e1f7 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -5,8 +5,20 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8
9#include <QList>
8#include <QWidget> 10#include <QWidget>
9 11
12class QGraphicsScene;
13class QStandardItem;
14class QStandardItemModel;
15class QTreeView;
16class QVBoxLayout;
17
18namespace Service::Account {
19class ProfileManager;
20}
21
10namespace Ui { 22namespace Ui {
11class ConfigureSystem; 23class ConfigureSystem;
12} 24}
@@ -16,23 +28,39 @@ class ConfigureSystem : public QWidget {
16 28
17public: 29public:
18 explicit ConfigureSystem(QWidget* parent = nullptr); 30 explicit ConfigureSystem(QWidget* parent = nullptr);
19 ~ConfigureSystem(); 31 ~ConfigureSystem() override;
20 32
21 void applyConfiguration(); 33 void applyConfiguration();
22 void setConfiguration(); 34 void setConfiguration();
23 35
24public slots:
25 void updateBirthdayComboBox(int birthmonth_index);
26 void refreshConsoleID();
27
28private: 36private:
29 void ReadSystemSettings(); 37 void ReadSystemSettings();
30 38
39 void UpdateBirthdayComboBox(int birthmonth_index);
40 void RefreshConsoleID();
41
42 void PopulateUserList();
43 void UpdateCurrentUser();
44 void SelectUser(const QModelIndex& index);
45 void AddUser();
46 void RenameUser();
47 void DeleteUser();
48 void SetUserImage();
49
50 QVBoxLayout* layout;
51 QTreeView* tree_view;
52 QStandardItemModel* item_model;
53 QGraphicsScene* scene;
54
55 std::vector<QList<QStandardItem*>> list_items;
56
31 std::unique_ptr<Ui::ConfigureSystem> ui; 57 std::unique_ptr<Ui::ConfigureSystem> ui;
32 bool enabled; 58 bool enabled = false;
59
60 int birthmonth = 0;
61 int birthday = 0;
62 int language_index = 0;
63 int sound_index = 0;
33 64
34 std::u16string username; 65 std::unique_ptr<Service::Account::ProfileManager> profile_manager;
35 int birthmonth, birthday;
36 int language_index;
37 int sound_index;
38}; 66};
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index f3f8db038..020b32a37 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>360</width> 9 <width>360</width>
10 <height>377</height> 10 <height>483</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -22,34 +22,28 @@
22 <string>System Settings</string> 22 <string>System Settings</string>
23 </property> 23 </property>
24 <layout class="QGridLayout" name="gridLayout"> 24 <layout class="QGridLayout" name="gridLayout">
25 <item row="0" column="0"> 25 <item row="1" column="0">
26 <widget class="QLabel" name="label_username"> 26 <widget class="QLabel" name="label_language">
27 <property name="text"> 27 <property name="text">
28 <string>Username</string> 28 <string>Language</string>
29 </property> 29 </property>
30 </widget> 30 </widget>
31 </item> 31 </item>
32 <item row="0" column="1"> 32 <item row="0" column="0">
33 <widget class="QLineEdit" name="edit_username"> 33 <widget class="QLabel" name="label_birthday">
34 <property name="sizePolicy"> 34 <property name="text">
35 <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> 35 <string>Birthday</string>
36 <horstretch>0</horstretch>
37 <verstretch>0</verstretch>
38 </sizepolicy>
39 </property>
40 <property name="maxLength">
41 <number>32</number>
42 </property> 36 </property>
43 </widget> 37 </widget>
44 </item> 38 </item>
45 <item row="1" column="0"> 39 <item row="3" column="0">
46 <widget class="QLabel" name="label_birthday"> 40 <widget class="QLabel" name="label_console_id">
47 <property name="text"> 41 <property name="text">
48 <string>Birthday</string> 42 <string>Console ID:</string>
49 </property> 43 </property>
50 </widget> 44 </widget>
51 </item> 45 </item>
52 <item row="1" column="1"> 46 <item row="0" column="1">
53 <layout class="QHBoxLayout" name="horizontalLayout_birthday2"> 47 <layout class="QHBoxLayout" name="horizontalLayout_birthday2">
54 <item> 48 <item>
55 <widget class="QComboBox" name="combo_birthmonth"> 49 <widget class="QComboBox" name="combo_birthmonth">
@@ -120,14 +114,7 @@
120 </item> 114 </item>
121 </layout> 115 </layout>
122 </item> 116 </item>
123 <item row="2" column="0"> 117 <item row="1" column="1">
124 <widget class="QLabel" name="label_language">
125 <property name="text">
126 <string>Language</string>
127 </property>
128 </widget>
129 </item>
130 <item row="2" column="1">
131 <widget class="QComboBox" name="combo_language"> 118 <widget class="QComboBox" name="combo_language">
132 <property name="toolTip"> 119 <property name="toolTip">
133 <string>Note: this can be overridden when region setting is auto-select</string> 120 <string>Note: this can be overridden when region setting is auto-select</string>
@@ -187,31 +174,31 @@
187 <string>Russian (Русский)</string> 174 <string>Russian (Русский)</string>
188 </property> 175 </property>
189 </item> 176 </item>
190 <item> 177 <item>
191 <property name="text"> 178 <property name="text">
192 <string>Taiwanese</string> 179 <string>Taiwanese</string>
193 </property> 180 </property>
194 </item> 181 </item>
195 <item> 182 <item>
196 <property name="text"> 183 <property name="text">
197 <string>British English</string> 184 <string>British English</string>
198 </property> 185 </property>
199 </item> 186 </item>
200 <item> 187 <item>
201 <property name="text"> 188 <property name="text">
202 <string>Canadian French</string> 189 <string>Canadian French</string>
203 </property> 190 </property>
204 </item> 191 </item>
205 <item> 192 <item>
206 <property name="text"> 193 <property name="text">
207 <string>Latin American Spanish</string> 194 <string>Latin American Spanish</string>
208 </property> 195 </property>
209 </item> 196 </item>
210 <item> 197 <item>
211 <property name="text"> 198 <property name="text">
212 <string>Simplified Chinese</string> 199 <string>Simplified Chinese</string>
213 </property> 200 </property>
214 </item> 201 </item>
215 <item> 202 <item>
216 <property name="text"> 203 <property name="text">
217 <string>Traditional Chinese (正體中文)</string> 204 <string>Traditional Chinese (正體中文)</string>
@@ -219,14 +206,14 @@
219 </item> 206 </item>
220 </widget> 207 </widget>
221 </item> 208 </item>
222 <item row="3" column="0"> 209 <item row="2" column="0">
223 <widget class="QLabel" name="label_sound"> 210 <widget class="QLabel" name="label_sound">
224 <property name="text"> 211 <property name="text">
225 <string>Sound output mode</string> 212 <string>Sound output mode</string>
226 </property> 213 </property>
227 </widget> 214 </widget>
228 </item> 215 </item>
229 <item row="3" column="1"> 216 <item row="2" column="1">
230 <widget class="QComboBox" name="combo_sound"> 217 <widget class="QComboBox" name="combo_sound">
231 <item> 218 <item>
232 <property name="text"> 219 <property name="text">
@@ -245,14 +232,7 @@
245 </item> 232 </item>
246 </widget> 233 </widget>
247 </item> 234 </item>
248 <item row="4" column="0"> 235 <item row="3" column="1">
249 <widget class="QLabel" name="label_console_id">
250 <property name="text">
251 <string>Console ID:</string>
252 </property>
253 </widget>
254 </item>
255 <item row="4" column="1">
256 <widget class="QPushButton" name="button_regenerate_console_id"> 236 <widget class="QPushButton" name="button_regenerate_console_id">
257 <property name="sizePolicy"> 237 <property name="sizePolicy">
258 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 238 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@@ -272,6 +252,143 @@
272 </widget> 252 </widget>
273 </item> 253 </item>
274 <item> 254 <item>
255 <widget class="QGroupBox" name="gridGroupBox">
256 <property name="title">
257 <string>Profile Manager</string>
258 </property>
259 <layout class="QGridLayout" name="gridLayout_2">
260 <property name="sizeConstraint">
261 <enum>QLayout::SetNoConstraint</enum>
262 </property>
263 <item row="0" column="0">
264 <layout class="QHBoxLayout" name="horizontalLayout_2">
265 <item>
266 <widget class="QLabel" name="label">
267 <property name="sizePolicy">
268 <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
269 <horstretch>0</horstretch>
270 <verstretch>0</verstretch>
271 </sizepolicy>
272 </property>
273 <property name="text">
274 <string>Current User</string>
275 </property>
276 </widget>
277 </item>
278 <item>
279 <widget class="QGraphicsView" name="current_user_icon">
280 <property name="minimumSize">
281 <size>
282 <width>48</width>
283 <height>48</height>
284 </size>
285 </property>
286 <property name="maximumSize">
287 <size>
288 <width>48</width>
289 <height>48</height>
290 </size>
291 </property>
292 <property name="verticalScrollBarPolicy">
293 <enum>Qt::ScrollBarAlwaysOff</enum>
294 </property>
295 <property name="horizontalScrollBarPolicy">
296 <enum>Qt::ScrollBarAlwaysOff</enum>
297 </property>
298 <property name="interactive">
299 <bool>false</bool>
300 </property>
301 </widget>
302 </item>
303 <item>
304 <widget class="QLabel" name="current_user_username">
305 <property name="sizePolicy">
306 <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
307 <horstretch>0</horstretch>
308 <verstretch>0</verstretch>
309 </sizepolicy>
310 </property>
311 <property name="text">
312 <string>Username</string>
313 </property>
314 </widget>
315 </item>
316 </layout>
317 </item>
318 <item row="1" column="0">
319 <widget class="QScrollArea" name="scrollArea">
320 <property name="sizePolicy">
321 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
322 <horstretch>0</horstretch>
323 <verstretch>0</verstretch>
324 </sizepolicy>
325 </property>
326 <property name="frameShape">
327 <enum>QFrame::StyledPanel</enum>
328 </property>
329 <property name="widgetResizable">
330 <bool>false</bool>
331 </property>
332 </widget>
333 </item>
334 <item row="2" column="0">
335 <layout class="QHBoxLayout" name="horizontalLayout_3">
336 <item>
337 <widget class="QPushButton" name="pm_set_image">
338 <property name="enabled">
339 <bool>false</bool>
340 </property>
341 <property name="text">
342 <string>Set Image</string>
343 </property>
344 </widget>
345 </item>
346 <item>
347 <spacer name="horizontalSpacer">
348 <property name="orientation">
349 <enum>Qt::Horizontal</enum>
350 </property>
351 <property name="sizeHint" stdset="0">
352 <size>
353 <width>40</width>
354 <height>20</height>
355 </size>
356 </property>
357 </spacer>
358 </item>
359 <item>
360 <widget class="QPushButton" name="pm_add">
361 <property name="text">
362 <string>Add</string>
363 </property>
364 </widget>
365 </item>
366 <item>
367 <widget class="QPushButton" name="pm_rename">
368 <property name="enabled">
369 <bool>false</bool>
370 </property>
371 <property name="text">
372 <string>Rename</string>
373 </property>
374 </widget>
375 </item>
376 <item>
377 <widget class="QPushButton" name="pm_remove">
378 <property name="enabled">
379 <bool>false</bool>
380 </property>
381 <property name="text">
382 <string>Remove</string>
383 </property>
384 </widget>
385 </item>
386 </layout>
387 </item>
388 </layout>
389 </widget>
390 </item>
391 <item>
275 <widget class="QLabel" name="label_disable_info"> 392 <widget class="QLabel" name="label_disable_info">
276 <property name="text"> 393 <property name="text">
277 <string>System settings are available only when game is not running.</string> 394 <string>System settings are available only when game is not running.</string>
@@ -281,19 +398,6 @@
281 </property> 398 </property>
282 </widget> 399 </widget>
283 </item> 400 </item>
284 <item>
285 <spacer name="verticalSpacer">
286 <property name="orientation">
287 <enum>Qt::Vertical</enum>
288 </property>
289 <property name="sizeHint" stdset="0">
290 <size>
291 <width>20</width>
292 <height>40</height>
293 </size>
294 </property>
295 </spacer>
296 </item>
297 </layout> 401 </layout>
298 </item> 402 </item>
299 </layout> 403 </layout>
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
index b5c88f944..67ed0ba6d 100644
--- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
+++ b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
@@ -2,7 +2,6 @@
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 <map>
6#include <QLabel> 5#include <QLabel>
7#include <QMetaType> 6#include <QMetaType>
8#include <QPushButton> 7#include <QPushButton>
@@ -32,21 +31,8 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const {
32 switch (role) { 31 switch (role) {
33 case Qt::DisplayRole: { 32 case Qt::DisplayRole: {
34 if (index.column() == 0) { 33 if (index.column() == 0) {
35 static const std::map<Tegra::DebugContext::Event, QString> map = { 34 return DebugContextEventToString(event);
36 {Tegra::DebugContext::Event::MaxwellCommandLoaded, tr("Maxwell command loaded")},
37 {Tegra::DebugContext::Event::MaxwellCommandProcessed,
38 tr("Maxwell command processed")},
39 {Tegra::DebugContext::Event::IncomingPrimitiveBatch,
40 tr("Incoming primitive batch")},
41 {Tegra::DebugContext::Event::FinishedPrimitiveBatch,
42 tr("Finished primitive batch")},
43 };
44
45 DEBUG_ASSERT(map.size() ==
46 static_cast<std::size_t>(Tegra::DebugContext::Event::NumEvents));
47 return (map.find(event) != map.end()) ? map.at(event) : QString();
48 } 35 }
49
50 break; 36 break;
51 } 37 }
52 38
@@ -128,6 +114,23 @@ void BreakPointModel::OnResumed() {
128 active_breakpoint = context->active_breakpoint; 114 active_breakpoint = context->active_breakpoint;
129} 115}
130 116
117QString BreakPointModel::DebugContextEventToString(Tegra::DebugContext::Event event) {
118 switch (event) {
119 case Tegra::DebugContext::Event::MaxwellCommandLoaded:
120 return tr("Maxwell command loaded");
121 case Tegra::DebugContext::Event::MaxwellCommandProcessed:
122 return tr("Maxwell command processed");
123 case Tegra::DebugContext::Event::IncomingPrimitiveBatch:
124 return tr("Incoming primitive batch");
125 case Tegra::DebugContext::Event::FinishedPrimitiveBatch:
126 return tr("Finished primitive batch");
127 case Tegra::DebugContext::Event::NumEvents:
128 break;
129 }
130
131 return tr("Unknown debug context event");
132}
133
131GraphicsBreakPointsWidget::GraphicsBreakPointsWidget( 134GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
132 std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent) 135 std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent)
133 : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver( 136 : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver(
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h
index 7112b87e6..fb488e38f 100644
--- a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h
+++ b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h
@@ -29,6 +29,8 @@ public:
29 void OnResumed(); 29 void OnResumed();
30 30
31private: 31private:
32 static QString DebugContextEventToString(Tegra::DebugContext::Event event);
33
32 std::weak_ptr<Tegra::DebugContext> context_weak; 34 std::weak_ptr<Tegra::DebugContext> context_weak;
33 bool at_breakpoint; 35 bool at_breakpoint;
34 Tegra::DebugContext::Event active_breakpoint; 36 Tegra::DebugContext::Event active_breakpoint;
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/game_list.cpp b/src/yuzu/game_list.cpp
index 67890455a..a5a4aa432 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -16,7 +16,6 @@
16#include <fmt/format.h> 16#include <fmt/format.h>
17#include "common/common_paths.h" 17#include "common/common_paths.h"
18#include "common/common_types.h" 18#include "common/common_types.h"
19#include "common/file_util.h"
20#include "common/logging/log.h" 19#include "common/logging/log.h"
21#include "core/file_sys/patch_manager.h" 20#include "core/file_sys/patch_manager.h"
22#include "yuzu/compatibility_list.h" 21#include "yuzu/compatibility_list.h"
@@ -217,11 +216,11 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
217 tree_view->setContextMenuPolicy(Qt::CustomContextMenu); 216 tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
218 217
219 item_model->insertColumns(0, COLUMN_COUNT); 218 item_model->insertColumns(0, COLUMN_COUNT);
220 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); 219 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
221 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility"); 220 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
222 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, "Add-ons"); 221 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
223 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); 222 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
224 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); 223 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
225 224
226 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); 225 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
227 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); 226 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
@@ -387,9 +386,9 @@ void GameList::LoadCompatibilityList() {
387} 386}
388 387
389void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { 388void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
390 if (!FileUtil::Exists(dir_path.toStdString()) || 389 const QFileInfo dir_info{dir_path};
391 !FileUtil::IsDirectory(dir_path.toStdString())) { 390 if (!dir_info.exists() || !dir_info.isDir()) {
392 LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toLocal8Bit().data()); 391 LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toStdString());
393 search_field->setFilterResult(0, 0); 392 search_field->setFilterResult(0, 0);
394 return; 393 return;
395 } 394 }
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 3881aba5f..3d865a12d 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -62,19 +62,24 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
62 FileSys::VirtualFile update_raw; 62 FileSys::VirtualFile update_raw;
63 loader.ReadUpdateRaw(update_raw); 63 loader.ReadUpdateRaw(update_raw);
64 for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { 64 for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) {
65 if (!updatable && kv.first == "Update") 65 const bool is_update = kv.first == "Update";
66 if (!updatable && is_update) {
66 continue; 67 continue;
68 }
69
70 const QString type = QString::fromStdString(kv.first);
67 71
68 if (kv.second.empty()) { 72 if (kv.second.empty()) {
69 out.append(fmt::format("{}\n", kv.first).c_str()); 73 out.append(QStringLiteral("%1\n").arg(type));
70 } else { 74 } else {
71 auto ver = kv.second; 75 auto ver = kv.second;
72 76
73 // Display container name for packed updates 77 // Display container name for packed updates
74 if (ver == "PACKED" && kv.first == "Update") 78 if (is_update && ver == "PACKED") {
75 ver = Loader::GetFileTypeString(loader.GetFileType()); 79 ver = Loader::GetFileTypeString(loader.GetFileType());
80 }
76 81
77 out.append(fmt::format("{} ({})\n", kv.first, ver).c_str()); 82 out.append(QStringLiteral("%1 (%2)\n").arg(type, QString::fromStdString(ver)));
78 } 83 }
79 } 84 }
80 85
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index bef9df00d..b5bfa6741 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -10,6 +10,7 @@
10// VFS includes must be before glad as they will conflict with Windows file api, which uses defines. 10// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
11#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs.h"
12#include "core/file_sys/vfs_real.h" 12#include "core/file_sys/vfs_real.h"
13#include "core/hle/service/acc/profile_manager.h"
13 14
14// These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows 15// These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows
15// defines. 16// defines.
@@ -29,6 +30,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
29#define QT_NO_OPENGL 30#define QT_NO_OPENGL
30#include <QDesktopWidget> 31#include <QDesktopWidget>
31#include <QDialogButtonBox> 32#include <QDialogButtonBox>
33#include <QFile>
32#include <QFileDialog> 34#include <QFileDialog>
33#include <QMessageBox> 35#include <QMessageBox>
34#include <QtConcurrent/QtConcurrent> 36#include <QtConcurrent/QtConcurrent>
@@ -60,6 +62,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
60#include "core/hle/kernel/process.h" 62#include "core/hle/kernel/process.h"
61#include "core/hle/service/filesystem/filesystem.h" 63#include "core/hle/service/filesystem/filesystem.h"
62#include "core/hle/service/filesystem/fsp_ldr.h" 64#include "core/hle/service/filesystem/fsp_ldr.h"
65#include "core/hle/service/nfp/nfp.h"
66#include "core/hle/service/sm/sm.h"
63#include "core/loader/loader.h" 67#include "core/loader/loader.h"
64#include "core/perf_stats.h" 68#include "core/perf_stats.h"
65#include "core/settings.h" 69#include "core/settings.h"
@@ -100,6 +104,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
100} 104}
101#endif 105#endif
102 106
107constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
108
103/** 109/**
104 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there 110 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there
105 * is a bitfield "callout_flags" options, used to track if a message has already been shown to the 111 * is a bitfield "callout_flags" options, used to track if a message has already been shown to the
@@ -422,6 +428,7 @@ void GMainWindow::ConnectMenuEvents() {
422 connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this, 428 connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this,
423 [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); }); 429 [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); });
424 connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); 430 connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
431 connect(ui.action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo);
425 432
426 // Emulation 433 // Emulation
427 connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); 434 connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame);
@@ -690,6 +697,7 @@ void GMainWindow::ShutdownGame() {
690 ui.action_Stop->setEnabled(false); 697 ui.action_Stop->setEnabled(false);
691 ui.action_Restart->setEnabled(false); 698 ui.action_Restart->setEnabled(false);
692 ui.action_Report_Compatibility->setEnabled(false); 699 ui.action_Report_Compatibility->setEnabled(false);
700 ui.action_Load_Amiibo->setEnabled(false);
693 render_window->hide(); 701 render_window->hide();
694 game_list->show(); 702 game_list->show();
695 game_list->setFilterFocus(); 703 game_list->setFilterFocus();
@@ -751,12 +759,43 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
751 open_target = "Save Data"; 759 open_target = "Save Data";
752 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); 760 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
753 ASSERT(program_id != 0); 761 ASSERT(program_id != 0);
754 // TODO(tech4me): Update this to work with arbitrary user profile 762
755 // Refer to core/hle/service/acc/profile_manager.cpp ProfileManager constructor 763 Service::Account::ProfileManager manager{};
756 constexpr u128 user_id = {1, 0}; 764 const auto user_ids = manager.GetAllUsers();
765 QStringList list;
766 for (const auto& user_id : user_ids) {
767 if (user_id == Service::Account::UUID{})
768 continue;
769 Service::Account::ProfileBase base;
770 if (!manager.GetProfileBase(user_id, base))
771 continue;
772
773 list.push_back(QString::fromStdString(Common::StringFromFixedZeroTerminatedBuffer(
774 reinterpret_cast<const char*>(base.username.data()), base.username.size())));
775 }
776
777 bool ok = false;
778 const auto index_string =
779 QInputDialog::getItem(this, tr("Select User"),
780 tr("Please select the user's save data you would like to open."),
781 list, Settings::values.current_user, false, &ok);
782 if (!ok)
783 return;
784
785 const auto index = list.indexOf(index_string);
786 ASSERT(index != -1 && index < 8);
787
788 const auto user_id = manager.GetUser(index);
789 ASSERT(user_id != std::nullopt);
757 path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, 790 path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser,
758 FileSys::SaveDataType::SaveData, 791 FileSys::SaveDataType::SaveData,
759 program_id, user_id, 0); 792 program_id, user_id->uuid, 0);
793
794 if (!FileUtil::Exists(path)) {
795 FileUtil::CreateFullPath(path);
796 FileUtil::CreateDir(path);
797 }
798
760 break; 799 break;
761 } 800 }
762 case GameListOpenTarget::ModData: { 801 case GameListOpenTarget::ModData: {
@@ -823,14 +862,10 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src
823} 862}
824 863
825void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { 864void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
826 const auto path = fmt::format("{}{:016X}/romfs", 865 const auto failed = [this] {
827 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id);
828
829 const auto failed = [this, &path] {
830 QMessageBox::warning(this, tr("RomFS Extraction Failed!"), 866 QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
831 tr("There was an error copying the RomFS files or the user " 867 tr("There was an error copying the RomFS files or the user "
832 "cancelled the operation.")); 868 "cancelled the operation."));
833 vfs->DeleteDirectory(path);
834 }; 869 };
835 870
836 const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read)); 871 const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read));
@@ -845,10 +880,24 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
845 return; 880 return;
846 } 881 }
847 882
848 const auto romfs = 883 const auto installed = Service::FileSystem::GetUnionContents();
849 loader->IsRomFSUpdatable() 884 auto romfs_title_id = SelectRomFSDumpTarget(*installed, program_id);
850 ? FileSys::PatchManager(program_id).PatchRomFS(file, loader->ReadRomFSIVFCOffset()) 885
851 : file; 886 if (!romfs_title_id) {
887 failed();
888 return;
889 }
890
891 const auto path = fmt::format(
892 "{}{:016X}/romfs", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), *romfs_title_id);
893
894 FileSys::VirtualFile romfs;
895
896 if (*romfs_title_id == program_id) {
897 romfs = file;
898 } else {
899 romfs = installed->GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS();
900 }
852 901
853 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); 902 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
854 if (extracted == nullptr) { 903 if (extracted == nullptr) {
@@ -860,6 +909,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
860 909
861 if (out == nullptr) { 910 if (out == nullptr) {
862 failed(); 911 failed();
912 vfs->DeleteDirectory(path);
863 return; 913 return;
864 } 914 }
865 915
@@ -870,8 +920,11 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
870 "files into the new directory while <br>skeleton will only create the directory " 920 "files into the new directory while <br>skeleton will only create the directory "
871 "structure."), 921 "structure."),
872 {"Full", "Skeleton"}, 0, false, &ok); 922 {"Full", "Skeleton"}, 0, false, &ok);
873 if (!ok) 923 if (!ok) {
874 failed(); 924 failed();
925 vfs->DeleteDirectory(path);
926 return;
927 }
875 928
876 const auto full = res == "Full"; 929 const auto full = res == "Full";
877 const auto entry_size = CalculateRomFSEntrySize(extracted, full); 930 const auto entry_size = CalculateRomFSEntrySize(extracted, full);
@@ -888,6 +941,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
888 } else { 941 } else {
889 progress.close(); 942 progress.close();
890 failed(); 943 failed();
944 vfs->DeleteDirectory(path);
891 } 945 }
892} 946}
893 947
@@ -1174,6 +1228,7 @@ void GMainWindow::OnStartGame() {
1174 ui.action_Report_Compatibility->setEnabled(true); 1228 ui.action_Report_Compatibility->setEnabled(true);
1175 1229
1176 discord_rpc->Update(); 1230 discord_rpc->Update();
1231 ui.action_Load_Amiibo->setEnabled(true);
1177} 1232}
1178 1233
1179void GMainWindow::OnPauseGame() { 1234void GMainWindow::OnPauseGame() {
@@ -1278,6 +1333,47 @@ void GMainWindow::OnConfigure() {
1278 } 1333 }
1279} 1334}
1280 1335
1336void GMainWindow::OnLoadAmiibo() {
1337 const QString extensions{"*.bin"};
1338 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
1339 const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter);
1340
1341 if (filename.isEmpty()) {
1342 return;
1343 }
1344
1345 Core::System& system{Core::System::GetInstance()};
1346 Service::SM::ServiceManager& sm = system.ServiceManager();
1347 auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user");
1348 if (nfc == nullptr) {
1349 return;
1350 }
1351
1352 QFile nfc_file{filename};
1353 if (!nfc_file.open(QIODevice::ReadOnly)) {
1354 QMessageBox::warning(this, tr("Error opening Amiibo data file"),
1355 tr("Unable to open Amiibo file \"%1\" for reading.").arg(filename));
1356 return;
1357 }
1358
1359 const u64 nfc_file_size = nfc_file.size();
1360 std::vector<u8> buffer(nfc_file_size);
1361 const u64 read_size = nfc_file.read(reinterpret_cast<char*>(buffer.data()), nfc_file_size);
1362 if (nfc_file_size != read_size) {
1363 QMessageBox::warning(this, tr("Error reading Amiibo data file"),
1364 tr("Unable to fully read Amiibo data. Expected to read %1 bytes, but "
1365 "was only able to read %2 bytes.")
1366 .arg(nfc_file_size)
1367 .arg(read_size));
1368 return;
1369 }
1370
1371 if (!nfc->LoadAmiibo(buffer)) {
1372 QMessageBox::warning(this, tr("Error loading Amiibo data"),
1373 tr("Unable to load Amiibo data."));
1374 }
1375}
1376
1281void GMainWindow::OnAbout() { 1377void GMainWindow::OnAbout() {
1282 AboutDialog aboutDialog(this); 1378 AboutDialog aboutDialog(this);
1283 aboutDialog.exec(); 1379 aboutDialog.exec();
@@ -1318,15 +1414,17 @@ void GMainWindow::UpdateStatusBar() {
1318void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) { 1414void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) {
1319 QMessageBox::StandardButton answer; 1415 QMessageBox::StandardButton answer;
1320 QString status_message; 1416 QString status_message;
1321 const QString common_message = tr( 1417 const QString common_message =
1322 "The game you are trying to load requires additional files from your Switch to be dumped " 1418 tr("The game you are trying to load requires additional files from your Switch to be "
1323 "before playing.<br/><br/>For more information on dumping these files, please see the " 1419 "dumped "
1324 "following wiki page: <a " 1420 "before playing.<br/><br/>For more information on dumping these files, please see the "
1325 "href='https://yuzu-emu.org/wiki/" 1421 "following wiki page: <a "
1326 "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System " 1422 "href='https://yuzu-emu.org/wiki/"
1327 "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit " 1423 "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System "
1328 "back to the game list? Continuing emulation may result in crashes, corrupted save " 1424 "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to "
1329 "data, or other bugs."); 1425 "quit "
1426 "back to the game list? Continuing emulation may result in crashes, corrupted save "
1427 "data, or other bugs.");
1330 switch (result) { 1428 switch (result) {
1331 case Core::System::ResultStatus::ErrorSystemFiles: { 1429 case Core::System::ResultStatus::ErrorSystemFiles: {
1332 QString message = "yuzu was unable to locate a Switch system archive"; 1430 QString message = "yuzu was unable to locate a Switch system archive";
@@ -1357,9 +1455,12 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
1357 this, tr("Fatal Error"), 1455 this, tr("Fatal Error"),
1358 tr("yuzu has encountered a fatal error, please see the log for more details. " 1456 tr("yuzu has encountered a fatal error, please see the log for more details. "
1359 "For more information on accessing the log, please see the following page: " 1457 "For more information on accessing the log, please see the following page: "
1360 "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to " 1458 "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How "
1361 "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? " 1459 "to "
1362 "Continuing emulation may result in crashes, corrupted save data, or other bugs."), 1460 "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game "
1461 "list? "
1462 "Continuing emulation may result in crashes, corrupted save data, or other "
1463 "bugs."),
1363 QMessageBox::Yes | QMessageBox::No, QMessageBox::No); 1464 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
1364 status_message = "Fatal Error encountered"; 1465 status_message = "Fatal Error encountered";
1365 break; 1466 break;
@@ -1459,6 +1560,42 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1459 } 1560 }
1460} 1561}
1461 1562
1563boost::optional<u64> GMainWindow::SelectRomFSDumpTarget(
1564 const FileSys::RegisteredCacheUnion& installed, u64 program_id) {
1565 const auto dlc_entries =
1566 installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
1567 std::vector<FileSys::RegisteredCacheEntry> dlc_match;
1568 dlc_match.reserve(dlc_entries.size());
1569 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
1570 [&program_id, &installed](const FileSys::RegisteredCacheEntry& entry) {
1571 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id &&
1572 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
1573 });
1574
1575 std::vector<u64> romfs_tids;
1576 romfs_tids.push_back(program_id);
1577 for (const auto& entry : dlc_match)
1578 romfs_tids.push_back(entry.title_id);
1579
1580 if (romfs_tids.size() > 1) {
1581 QStringList list{"Base"};
1582 for (std::size_t i = 1; i < romfs_tids.size(); ++i)
1583 list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF));
1584
1585 bool ok;
1586 const auto res = QInputDialog::getItem(
1587 this, tr("Select RomFS Dump Target"),
1588 tr("Please select which RomFS you would like to dump."), list, 0, false, &ok);
1589 if (!ok) {
1590 return boost::none;
1591 }
1592
1593 return romfs_tids[list.indexOf(res)];
1594 }
1595
1596 return program_id;
1597}
1598
1462bool GMainWindow::ConfirmClose() { 1599bool GMainWindow::ConfirmClose() {
1463 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) 1600 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
1464 return true; 1601 return true;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 3663d6aed..7c7c223e1 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -10,6 +10,7 @@
10#include <QMainWindow> 10#include <QMainWindow>
11#include <QTimer> 11#include <QTimer>
12 12
13#include <boost/optional.hpp>
13#include "common/common_types.h" 14#include "common/common_types.h"
14#include "core/core.h" 15#include "core/core.h"
15#include "ui_main.h" 16#include "ui_main.h"
@@ -29,8 +30,9 @@ class WaitTreeWidget;
29enum class GameListOpenTarget; 30enum class GameListOpenTarget;
30 31
31namespace FileSys { 32namespace FileSys {
33class RegisteredCacheUnion;
32class VfsFilesystem; 34class VfsFilesystem;
33} 35} // namespace FileSys
34 36
35namespace Tegra { 37namespace Tegra {
36class DebugContext; 38class DebugContext;
@@ -164,6 +166,7 @@ private slots:
164 void OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target); 166 void OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target);
165 void OnMenuRecentFile(); 167 void OnMenuRecentFile();
166 void OnConfigure(); 168 void OnConfigure();
169 void OnLoadAmiibo();
167 void OnAbout(); 170 void OnAbout();
168 void OnToggleFilterBar(); 171 void OnToggleFilterBar();
169 void OnDisplayTitleBars(bool); 172 void OnDisplayTitleBars(bool);
@@ -175,6 +178,8 @@ private slots:
175 void OnReinitializeKeys(ReinitializeKeyBehavior behavior); 178 void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
176 179
177private: 180private:
181 boost::optional<u64> SelectRomFSDumpTarget(const FileSys::RegisteredCacheUnion&,
182 u64 program_id);
178 void UpdateStatusBar(); 183 void UpdateStatusBar();
179 184
180 Ui::MainWindow ui; 185 Ui::MainWindow ui;
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 9851f507d..48d099591 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -57,8 +57,8 @@
57 <string>Recent Files</string> 57 <string>Recent Files</string>
58 </property> 58 </property>
59 </widget> 59 </widget>
60 <addaction name="action_Install_File_NAND" /> 60 <addaction name="action_Install_File_NAND"/>
61 <addaction name="separator"/> 61 <addaction name="separator"/>
62 <addaction name="action_Load_File"/> 62 <addaction name="action_Load_File"/>
63 <addaction name="action_Load_Folder"/> 63 <addaction name="action_Load_Folder"/>
64 <addaction name="separator"/> 64 <addaction name="separator"/>
@@ -68,6 +68,8 @@
68 <addaction name="action_Select_NAND_Directory"/> 68 <addaction name="action_Select_NAND_Directory"/>
69 <addaction name="action_Select_SDMC_Directory"/> 69 <addaction name="action_Select_SDMC_Directory"/>
70 <addaction name="separator"/> 70 <addaction name="separator"/>
71 <addaction name="action_Load_Amiibo"/>
72 <addaction name="separator"/>
71 <addaction name="action_Exit"/> 73 <addaction name="action_Exit"/>
72 </widget> 74 </widget>
73 <widget class="QMenu" name="menu_Emulation"> 75 <widget class="QMenu" name="menu_Emulation">
@@ -97,25 +99,34 @@
97 <addaction name="action_Show_Status_Bar"/> 99 <addaction name="action_Show_Status_Bar"/>
98 <addaction name="menu_View_Debugging"/> 100 <addaction name="menu_View_Debugging"/>
99 </widget> 101 </widget>
102 <widget class ="QMenu" name="menu_Tools">
103 <property name="title">
104 <string>Tools</string>
105 </property>
106 <addaction name="action_Rederive" />
107 </widget>
100 <widget class="QMenu" name="menu_Help"> 108 <widget class="QMenu" name="menu_Help">
101 <property name="title"> 109 <property name="title">
102 <string>&amp;Help</string> 110 <string>&amp;Help</string>
103 </property> 111 </property>
104 <addaction name="action_Report_Compatibility"/> 112 <addaction name="action_Report_Compatibility"/>
105 <addaction name="separator"/> 113 <addaction name="separator"/>
106 <addaction name="action_Rederive"/>
107 <addaction name="action_About"/> 114 <addaction name="action_About"/>
108 </widget> 115 </widget>
109 <addaction name="menu_File"/> 116 <addaction name="menu_File"/>
110 <addaction name="menu_Emulation"/> 117 <addaction name="menu_Emulation"/>
111 <addaction name="menu_View"/> 118 <addaction name="menu_View"/>
119 <addaction name="menu_Tools" />
112 <addaction name="menu_Help"/> 120 <addaction name="menu_Help"/>
113 </widget> 121 </widget>
114 <action name="action_Install_File_NAND"> 122 <action name="action_Install_File_NAND">
115 <property name="text"> 123 <property name="enabled">
116 <string>Install File to NAND...</string> 124 <bool>true</bool>
117 </property> 125 </property>
118 </action> 126 <property name="text">
127 <string>Install File to NAND...</string>
128 </property>
129 </action>
119 <action name="action_Load_File"> 130 <action name="action_Load_File">
120 <property name="text"> 131 <property name="text">
121 <string>Load File...</string> 132 <string>Load File...</string>
@@ -247,6 +258,14 @@
247 <string>Restart</string> 258 <string>Restart</string>
248 </property> 259 </property>
249 </action> 260 </action>
261 <action name="action_Load_Amiibo">
262 <property name="enabled">
263 <bool>false</bool>
264 </property>
265 <property name="text">
266 <string>Load Amiibo...</string>
267 </property>
268 </action>
250 <action name="action_Report_Compatibility"> 269 <action name="action_Report_Compatibility">
251 <property name="enabled"> 270 <property name="enabled">
252 <bool>false</bool> 271 <bool>false</bool>
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 5e42e48b2..b456266a6 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -8,6 +8,7 @@
8#include "common/file_util.h" 8#include "common/file_util.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/param_package.h" 10#include "common/param_package.h"
11#include "core/hle/service/acc/profile_manager.h"
11#include "core/settings.h" 12#include "core/settings.h"
12#include "input_common/main.h" 13#include "input_common/main.h"
13#include "yuzu_cmd/config.h" 14#include "yuzu_cmd/config.h"
@@ -125,10 +126,11 @@ void Config::ReadValues() {
125 126
126 // System 127 // System
127 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); 128 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
128 Settings::values.username = sdl2_config->Get("System", "username", "yuzu"); 129 Settings::values.enable_nfc = sdl2_config->GetBoolean("System", "enable_nfc", true);
129 if (Settings::values.username.empty()) { 130 const auto size = sdl2_config->GetInteger("System", "users_size", 0);
130 Settings::values.username = "yuzu"; 131
131 } 132 Settings::values.current_user = std::clamp<int>(
133 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);
132 134
133 // Miscellaneous 135 // Miscellaneous
134 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 136 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index a97b75f7b..e0b223cd6 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -174,6 +174,10 @@ use_virtual_sd =
174# 1: Yes, 0 (default): No 174# 1: Yes, 0 (default): No
175use_docked_mode = 175use_docked_mode =
176 176
177# Allow the use of NFC in games
178# 1 (default): Yes, 0 : No
179enable_nfc =
180
177# Sets the account username, max length is 32 characters 181# Sets the account username, max length is 32 characters
178# yuzu (default) 182# yuzu (default)
179username = yuzu 183username = yuzu