summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt44
-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/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/kernel/hle_ipc.cpp25
-rw-r--r--src/core/hle/kernel/hle_ipc.h13
-rw-r--r--src/core/hle/kernel/kernel.cpp10
-rw-r--r--src/core/hle/kernel/kernel.h6
-rw-r--r--src/core/hle/kernel/process.h14
-rw-r--r--src/core/hle/kernel/server_session.cpp3
-rw-r--r--src/core/hle/kernel/svc.cpp178
-rw-r--r--src/core/hle/kernel/thread.cpp2
-rw-r--r--src/core/hle/service/acc/acc.cpp54
-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/tcap.cpp23
-rw-r--r--src/core/hle/service/am/tcap.h17
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp6
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp6
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp5
-rw-r--r--src/core/hle/service/hid/hid.cpp1
-rw-r--r--src/core/hle/service/lbl/lbl.cpp56
-rw-r--r--src/core/hle/service/nfc/nfc.cpp53
-rw-r--r--src/core/hle/service/nfp/nfp.cpp268
-rw-r--r--src/core/hle/service/nfp/nfp.h23
-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/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/perf_stats.cpp4
-rw-r--r--src/core/settings.h3
-rw-r--r--src/video_core/engines/maxwell_3d.cpp7
-rw-r--r--src/video_core/engines/maxwell_3d.h4
-rw-r--r--src/video_core/engines/maxwell_compute.cpp6
-rw-r--r--src/video_core/engines/maxwell_dma.cpp17
-rw-r--r--src/video_core/engines/shader_bytecode.h35
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp25
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp329
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp32
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp11
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h8
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h16
-rw-r--r--src/video_core/textures/decoders.cpp1
-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.cpp260
-rw-r--r--src/yuzu/configuration/configure_system.h33
-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.cpp168
-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
81 files changed, 2186 insertions, 885 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6021a8054..918cf5372 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -23,21 +23,21 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
23 23
24option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) 24option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
25 25
26if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit) 26if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
27 message(STATUS "Copying pre-commit hook") 27 message(STATUS "Copying pre-commit hook")
28 file(COPY hooks/pre-commit 28 file(COPY hooks/pre-commit
29 DESTINATION ${CMAKE_SOURCE_DIR}/.git/hooks) 29 DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks)
30endif() 30endif()
31 31
32# Sanity check : Check that all submodules are present 32# Sanity check : Check that all submodules are present
33# ======================================================================= 33# =======================================================================
34 34
35function(check_submodules_present) 35function(check_submodules_present)
36 file(READ "${CMAKE_SOURCE_DIR}/.gitmodules" gitmodules) 36 file(READ "${PROJECT_SOURCE_DIR}/.gitmodules" gitmodules)
37 string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules}) 37 string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules})
38 foreach(module ${gitmodules}) 38 foreach(module ${gitmodules})
39 string(REGEX REPLACE "path *= *" "" module ${module}) 39 string(REGEX REPLACE "path *= *" "" module ${module})
40 if (NOT EXISTS "${CMAKE_SOURCE_DIR}/${module}/.git") 40 if (NOT EXISTS "${PROJECT_SOURCE_DIR}/${module}/.git")
41 message(FATAL_ERROR "Git submodule ${module} not found. " 41 message(FATAL_ERROR "Git submodule ${module} not found. "
42 "Please run: git submodule update --init --recursive") 42 "Please run: git submodule update --init --recursive")
43 endif() 43 endif()
@@ -45,17 +45,17 @@ function(check_submodules_present)
45endfunction() 45endfunction()
46check_submodules_present() 46check_submodules_present()
47 47
48configure_file(${CMAKE_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc 48configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
49 ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc 49 ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
50 COPYONLY) 50 COPYONLY)
51if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) 51if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
52 message(STATUS "Downloading compatibility list for yuzu...") 52 message(STATUS "Downloading compatibility list for yuzu...")
53 file(DOWNLOAD 53 file(DOWNLOAD
54 https://api.yuzu-emu.org/gamedb/ 54 https://api.yuzu-emu.org/gamedb/
55 "${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS) 55 "${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
56endif() 56endif()
57if (NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) 57if (NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
58 file(WRITE ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "") 58 file(WRITE ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
59endif() 59endif()
60 60
61# Detect current compilation architecture and create standard definitions 61# Detect current compilation architecture and create standard definitions
@@ -170,7 +170,7 @@ endif()
170# On modern Unixes, this is typically already the case. The lone exception is 170# On modern Unixes, this is typically already the case. The lone exception is
171# glibc, which may default to 32 bits. glibc allows this to be configured 171# glibc, which may default to 32 bits. glibc allows this to be configured
172# by setting _FILE_OFFSET_BITS. 172# by setting _FILE_OFFSET_BITS.
173if(CMAKE_SYSTEM_NAME STREQUAL "Linux") 173if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
174 add_definitions(-D_FILE_OFFSET_BITS=64) 174 add_definitions(-D_FILE_OFFSET_BITS=64)
175endif() 175endif()
176 176
@@ -178,10 +178,6 @@ endif()
178set_property(DIRECTORY APPEND PROPERTY 178set_property(DIRECTORY APPEND PROPERTY
179 COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>) 179 COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
180 180
181
182math(EXPR EMU_ARCH_BITS ${CMAKE_SIZEOF_VOID_P}*8)
183add_definitions(-DEMU_ARCH_BITS=${EMU_ARCH_BITS})
184
185# System imported libraries 181# System imported libraries
186# ====================== 182# ======================
187 183
@@ -189,13 +185,13 @@ find_package(Boost 1.63.0 QUIET)
189if (NOT Boost_FOUND) 185if (NOT Boost_FOUND)
190 message(STATUS "Boost 1.63.0 or newer not found, falling back to externals") 186 message(STATUS "Boost 1.63.0 or newer not found, falling back to externals")
191 187
192 set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost") 188 set(BOOST_ROOT "${PROJECT_SOURCE_DIR}/externals/boost")
193 set(Boost_NO_SYSTEM_PATHS OFF) 189 set(Boost_NO_SYSTEM_PATHS OFF)
194 find_package(Boost QUIET REQUIRED) 190 find_package(Boost QUIET REQUIRED)
195endif() 191endif()
196 192
197# Output binaries to bin/ 193# Output binaries to bin/
198set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 194set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
199 195
200# Prefer the -pthread flag on Linux. 196# Prefer the -pthread flag on Linux.
201set(THREADS_PREFER_PTHREAD_FLAG ON) 197set(THREADS_PREFER_PTHREAD_FLAG ON)
@@ -264,7 +260,7 @@ if (YUZU_USE_BUNDLED_UNICORN)
264 endif() 260 endif()
265 261
266 set(UNICORN_FOUND YES) 262 set(UNICORN_FOUND YES)
267 set(UNICORN_PREFIX ${CMAKE_SOURCE_DIR}/externals/unicorn) 263 set(UNICORN_PREFIX ${PROJECT_SOURCE_DIR}/externals/unicorn)
268 set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/${UNICORN_LIB_NAME}" CACHE PATH "Path to Unicorn library" FORCE) 264 set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/${UNICORN_LIB_NAME}" CACHE PATH "Path to Unicorn library" FORCE)
269 set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE) 265 set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE)
270 set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/" CACHE PATH "Path to unicorn dynamic library" FORCE) 266 set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/" CACHE PATH "Path to unicorn dynamic library" FORCE)
@@ -356,12 +352,12 @@ set(CLANG_FORMAT_POSTFIX "-6.0")
356find_program(CLANG_FORMAT 352find_program(CLANG_FORMAT
357 NAMES clang-format${CLANG_FORMAT_POSTFIX} 353 NAMES clang-format${CLANG_FORMAT_POSTFIX}
358 clang-format 354 clang-format
359 PATHS ${CMAKE_BINARY_DIR}/externals) 355 PATHS ${PROJECT_BINARY_DIR}/externals)
360# if find_program doesn't find it, try to download from externals 356# if find_program doesn't find it, try to download from externals
361if (NOT CLANG_FORMAT) 357if (NOT CLANG_FORMAT)
362 if (WIN32) 358 if (WIN32)
363 message(STATUS "Clang format not found! Downloading...") 359 message(STATUS "Clang format not found! Downloading...")
364 set(CLANG_FORMAT "${CMAKE_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe") 360 set(CLANG_FORMAT "${PROJECT_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe")
365 file(DOWNLOAD 361 file(DOWNLOAD
366 https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe 362 https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe
367 "${CLANG_FORMAT}" SHOW_PROGRESS 363 "${CLANG_FORMAT}" SHOW_PROGRESS
@@ -377,7 +373,7 @@ if (NOT CLANG_FORMAT)
377endif() 373endif()
378 374
379if (CLANG_FORMAT) 375if (CLANG_FORMAT)
380 set(SRCS ${CMAKE_SOURCE_DIR}/src) 376 set(SRCS ${PROJECT_SOURCE_DIR}/src)
381 set(CCOMMENT "Running clang format against all the .h and .cpp files in src/") 377 set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
382 if (WIN32) 378 if (WIN32)
383 add_custom_target(clang-format 379 add_custom_target(clang-format
@@ -450,10 +446,10 @@ endif()
450# http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html 446# http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
451# http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html 447# http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
452if(ENABLE_QT AND UNIX AND NOT APPLE) 448if(ENABLE_QT AND UNIX AND NOT APPLE)
453 install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.desktop" 449 install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.desktop"
454 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications") 450 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications")
455 install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.svg" 451 install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.svg"
456 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps") 452 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps")
457 install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.xml" 453 install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.xml"
458 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages") 454 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages")
459endif() 455endif()
diff --git a/src/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/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/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index edad5f1b1..68d5376cb 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -77,7 +77,8 @@ HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_ses
77 77
78HLERequestContext::~HLERequestContext() = default; 78HLERequestContext::~HLERequestContext() = default;
79 79
80void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { 80void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf,
81 bool incoming) {
81 IPC::RequestParser rp(src_cmdbuf); 82 IPC::RequestParser rp(src_cmdbuf);
82 command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>()); 83 command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
83 84
@@ -94,8 +95,6 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
94 rp.Skip(2, false); 95 rp.Skip(2, false);
95 } 96 }
96 if (incoming) { 97 if (incoming) {
97 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
98
99 // Populate the object lists with the data in the IPC request. 98 // Populate the object lists with the data in the IPC request.
100 for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { 99 for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
101 copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>())); 100 copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>()));
@@ -189,10 +188,9 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
189 rp.Skip(1, false); // The command is actually an u64, but we don't use the high part. 188 rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
190} 189}
191 190
192ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, 191ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
193 Process& src_process, 192 u32_le* src_cmdbuf) {
194 HandleTable& src_table) { 193 ParseCommandBuffer(handle_table, src_cmdbuf, true);
195 ParseCommandBuffer(src_cmdbuf, true);
196 if (command_header->type == IPC::CommandType::Close) { 194 if (command_header->type == IPC::CommandType::Close) {
197 // Close does not populate the rest of the IPC header 195 // Close does not populate the rest of the IPC header
198 return RESULT_SUCCESS; 196 return RESULT_SUCCESS;
@@ -207,14 +205,17 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
207 return RESULT_SUCCESS; 205 return RESULT_SUCCESS;
208} 206}
209 207
210ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) { 208ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
209 auto& owner_process = *thread.GetOwnerProcess();
210 auto& handle_table = owner_process.GetHandleTable();
211
211 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; 212 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
212 Memory::ReadBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(), 213 Memory::ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
213 dst_cmdbuf.size() * sizeof(u32)); 214 dst_cmdbuf.size() * sizeof(u32));
214 215
215 // The header was already built in the internal command buffer. Attempt to parse it to verify 216 // The header was already built in the internal command buffer. Attempt to parse it to verify
216 // the integrity and then copy it over to the target command buffer. 217 // the integrity and then copy it over to the target command buffer.
217 ParseCommandBuffer(cmd_buf.data(), false); 218 ParseCommandBuffer(handle_table, cmd_buf.data(), false);
218 219
219 // The data_size already includes the payload header, the padding and the domain header. 220 // The data_size already includes the payload header, the padding and the domain header.
220 std::size_t size = data_payload_offset + command_header->data_size - 221 std::size_t size = data_payload_offset + command_header->data_size -
@@ -236,8 +237,6 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
236 ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy); 237 ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
237 ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move); 238 ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move);
238 239
239 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
240
241 // We don't make a distinction between copy and move handles when translating since HLE 240 // We don't make a distinction between copy and move handles when translating since HLE
242 // services don't deal with handles directly. However, the guest applications might check 241 // services don't deal with handles directly. However, the guest applications might check
243 // for specific values in each of these descriptors. 242 // for specific values in each of these descriptors.
@@ -268,7 +267,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
268 } 267 }
269 268
270 // Copy the translated command buffer back into the thread's command buffer area. 269 // Copy the translated command buffer back into the thread's command buffer area.
271 Memory::WriteBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(), 270 Memory::WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
272 dst_cmdbuf.size() * sizeof(u32)); 271 dst_cmdbuf.size() * sizeof(u32));
273 272
274 return RESULT_SUCCESS; 273 return RESULT_SUCCESS;
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 894479ee0..f01491daa 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -24,10 +24,10 @@ class ServiceFrameworkBase;
24namespace Kernel { 24namespace Kernel {
25 25
26class Domain; 26class Domain;
27class Event;
27class HandleTable; 28class HandleTable;
28class HLERequestContext; 29class HLERequestContext;
29class Process; 30class Process;
30class Event;
31 31
32/** 32/**
33 * Interface implemented by HLE Session handlers. 33 * Interface implemented by HLE Session handlers.
@@ -126,13 +126,12 @@ public:
126 u64 timeout, WakeupCallback&& callback, 126 u64 timeout, WakeupCallback&& callback,
127 Kernel::SharedPtr<Kernel::Event> event = nullptr); 127 Kernel::SharedPtr<Kernel::Event> event = nullptr);
128 128
129 void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
130
131 /// Populates this context with data from the requesting process/thread. 129 /// Populates this context with data from the requesting process/thread.
132 ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process, 130 ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
133 HandleTable& src_table); 131 u32_le* src_cmdbuf);
132
134 /// Writes data from this context back to the requesting process/thread. 133 /// Writes data from this context back to the requesting process/thread.
135 ResultCode WriteToOutgoingCommandBuffer(const Thread& thread); 134 ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
136 135
137 u32_le GetCommand() const { 136 u32_le GetCommand() const {
138 return command; 137 return command;
@@ -255,6 +254,8 @@ public:
255 std::string Description() const; 254 std::string Description() const;
256 255
257private: 256private:
257 void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
258
258 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; 259 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
259 SharedPtr<Kernel::ServerSession> server_session; 260 SharedPtr<Kernel::ServerSession> server_session;
260 // TODO(yuriks): Check common usage of this and optimize size accordingly 261 // TODO(yuriks): Check common usage of this and optimize size accordingly
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index bd680adfe..4b6b32dd5 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -118,7 +118,6 @@ struct KernelCore::Impl {
118 process_list.clear(); 118 process_list.clear();
119 current_process = nullptr; 119 current_process = nullptr;
120 120
121 handle_table.Clear();
122 resource_limits.fill(nullptr); 121 resource_limits.fill(nullptr);
123 122
124 thread_wakeup_callback_handle_table.Clear(); 123 thread_wakeup_callback_handle_table.Clear();
@@ -209,7 +208,6 @@ struct KernelCore::Impl {
209 std::vector<SharedPtr<Process>> process_list; 208 std::vector<SharedPtr<Process>> process_list;
210 Process* current_process = nullptr; 209 Process* current_process = nullptr;
211 210
212 Kernel::HandleTable handle_table;
213 std::array<SharedPtr<ResourceLimit>, 4> resource_limits; 211 std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
214 212
215 /// The event type of the generic timer callback event 213 /// The event type of the generic timer callback event
@@ -241,14 +239,6 @@ void KernelCore::Shutdown() {
241 impl->Shutdown(); 239 impl->Shutdown();
242} 240}
243 241
244Kernel::HandleTable& KernelCore::HandleTable() {
245 return impl->handle_table;
246}
247
248const Kernel::HandleTable& KernelCore::HandleTable() const {
249 return impl->handle_table;
250}
251
252SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory( 242SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory(
253 ResourceLimitCategory category) const { 243 ResourceLimitCategory category) const {
254 return impl->resource_limits.at(static_cast<std::size_t>(category)); 244 return impl->resource_limits.at(static_cast<std::size_t>(category));
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 41554821f..7f822d524 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -47,12 +47,6 @@ public:
47 /// Clears all resources in use by the kernel instance. 47 /// Clears all resources in use by the kernel instance.
48 void Shutdown(); 48 void Shutdown();
49 49
50 /// Provides a reference to the handle table.
51 Kernel::HandleTable& HandleTable();
52
53 /// Provides a const reference to the handle table.
54 const Kernel::HandleTable& HandleTable() const;
55
56 /// Retrieves a shared pointer to a ResourceLimit identified by the given category. 50 /// Retrieves a shared pointer to a ResourceLimit identified by the given category.
57 SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const; 51 SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const;
58 52
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index f2816943a..148478488 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -13,6 +13,7 @@
13#include <boost/container/static_vector.hpp> 13#include <boost/container/static_vector.hpp>
14#include "common/bit_field.h" 14#include "common/bit_field.h"
15#include "common/common_types.h" 15#include "common/common_types.h"
16#include "core/hle/kernel/handle_table.h"
16#include "core/hle/kernel/object.h" 17#include "core/hle/kernel/object.h"
17#include "core/hle/kernel/thread.h" 18#include "core/hle/kernel/thread.h"
18#include "core/hle/kernel/vm_manager.h" 19#include "core/hle/kernel/vm_manager.h"
@@ -142,6 +143,16 @@ public:
142 return vm_manager; 143 return vm_manager;
143 } 144 }
144 145
146 /// Gets a reference to the process' handle table.
147 HandleTable& GetHandleTable() {
148 return handle_table;
149 }
150
151 /// Gets a const reference to the process' handle table.
152 const HandleTable& GetHandleTable() const {
153 return handle_table;
154 }
155
145 /// Gets the current status of the process 156 /// Gets the current status of the process
146 ProcessStatus GetStatus() const { 157 ProcessStatus GetStatus() const {
147 return status; 158 return status;
@@ -294,6 +305,9 @@ private:
294 /// specified by metadata provided to the process during loading. 305 /// specified by metadata provided to the process during loading.
295 bool is_64bit_process = true; 306 bool is_64bit_process = true;
296 307
308 /// Per-process handle table for storing created object handles in.
309 HandleTable handle_table;
310
297 std::string name; 311 std::string name;
298}; 312};
299 313
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 1ece691c7..5fc320403 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -107,8 +107,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
107 // similar. 107 // similar.
108 Kernel::HLERequestContext context(this); 108 Kernel::HLERequestContext context(this);
109 u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); 109 u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
110 context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(), 110 context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
111 kernel.HandleTable());
112 111
113 ResultCode result = RESULT_SUCCESS; 112 ResultCode result = RESULT_SUCCESS;
114 // If the session has been converted to a domain, handle the domain request 113 // If the session has been converted to a domain, handle the domain request
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 3b8a2e230..9a783d524 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,14 +593,15 @@ 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); 597 SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
551 if (!thread) 598 if (!thread) {
552 return ERR_INVALID_HANDLE; 599 return ERR_INVALID_HANDLE;
600 }
553 601
554 // Note: The kernel uses the current process's resource limit instead of 602 // Note: The kernel uses the current process's resource limit instead of
555 // the one from the thread owner's resource limit. 603 // the one from the thread owner's resource limit.
556 const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); 604 const ResourceLimit& resource_limit = current_process->GetResourceLimit();
557 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { 605 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
558 return ERR_NOT_AUTHORIZED; 606 return ERR_NOT_AUTHORIZED;
559 } 607 }
@@ -595,15 +643,13 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
595 return ERR_INVALID_MEMORY_PERMISSIONS; 643 return ERR_INVALID_MEMORY_PERMISSIONS;
596 } 644 }
597 645
598 auto& kernel = Core::System::GetInstance().Kernel(); 646 auto* const current_process = Core::CurrentProcess();
599 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); 647 auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
600 if (!shared_memory) { 648 if (!shared_memory) {
601 return ERR_INVALID_HANDLE; 649 return ERR_INVALID_HANDLE;
602 } 650 }
603 651
604 auto* const current_process = Core::CurrentProcess();
605 const auto& vm_manager = current_process->VMManager(); 652 const auto& vm_manager = current_process->VMManager();
606
607 if (!vm_manager.IsWithinASLRRegion(addr, size)) { 653 if (!vm_manager.IsWithinASLRRegion(addr, size)) {
608 return ERR_INVALID_MEMORY_RANGE; 654 return ERR_INVALID_MEMORY_RANGE;
609 } 655 }
@@ -627,15 +673,13 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
627 return ERR_INVALID_ADDRESS_STATE; 673 return ERR_INVALID_ADDRESS_STATE;
628 } 674 }
629 675
630 auto& kernel = Core::System::GetInstance().Kernel(); 676 auto* const current_process = Core::CurrentProcess();
631 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); 677 auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
632 if (!shared_memory) { 678 if (!shared_memory) {
633 return ERR_INVALID_HANDLE; 679 return ERR_INVALID_HANDLE;
634 } 680 }
635 681
636 auto* const current_process = Core::CurrentProcess();
637 const auto& vm_manager = current_process->VMManager(); 682 const auto& vm_manager = current_process->VMManager();
638
639 if (!vm_manager.IsWithinASLRRegion(addr, size)) { 683 if (!vm_manager.IsWithinASLRRegion(addr, size)) {
640 return ERR_INVALID_MEMORY_RANGE; 684 return ERR_INVALID_MEMORY_RANGE;
641 } 685 }
@@ -646,15 +690,14 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
646/// Query process memory 690/// Query process memory
647static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, 691static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/,
648 Handle process_handle, u64 addr) { 692 Handle process_handle, u64 addr) {
649 693 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
650 auto& kernel = Core::System::GetInstance().Kernel(); 694 SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
651 SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle);
652 if (!process) { 695 if (!process) {
653 return ERR_INVALID_HANDLE; 696 return ERR_INVALID_HANDLE;
654 } 697 }
655 auto vma = process->VMManager().FindVMA(addr); 698 auto vma = process->VMManager().FindVMA(addr);
656 memory_info->attributes = 0; 699 memory_info->attributes = 0;
657 if (vma == Core::CurrentProcess()->VMManager().vma_map.end()) { 700 if (vma == process->VMManager().vma_map.end()) {
658 memory_info->base_address = 0; 701 memory_info->base_address = 0;
659 memory_info->permission = static_cast<u32>(VMAPermission::None); 702 memory_info->permission = static_cast<u32>(VMAPermission::None);
660 memory_info->size = 0; 703 memory_info->size = 0;
@@ -695,20 +738,19 @@ static void ExitProcess() {
695/// Creates a new thread 738/// Creates a new thread
696static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, 739static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top,
697 u32 priority, s32 processor_id) { 740 u32 priority, s32 processor_id) {
698 std::string name = fmt::format("thread-{:X}", entry_point);
699
700 if (priority > THREADPRIO_LOWEST) { 741 if (priority > THREADPRIO_LOWEST) {
701 return ERR_INVALID_THREAD_PRIORITY; 742 return ERR_INVALID_THREAD_PRIORITY;
702 } 743 }
703 744
704 const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); 745 auto* const current_process = Core::CurrentProcess();
746 const ResourceLimit& resource_limit = current_process->GetResourceLimit();
705 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { 747 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
706 return ERR_NOT_AUTHORIZED; 748 return ERR_NOT_AUTHORIZED;
707 } 749 }
708 750
709 if (processor_id == THREADPROCESSORID_DEFAULT) { 751 if (processor_id == THREADPROCESSORID_DEFAULT) {
710 // Set the target CPU to the one specified in the process' exheader. 752 // Set the target CPU to the one specified in the process' exheader.
711 processor_id = Core::CurrentProcess()->GetDefaultProcessorID(); 753 processor_id = current_process->GetDefaultProcessorID();
712 ASSERT(processor_id != THREADPROCESSORID_DEFAULT); 754 ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
713 } 755 }
714 756
@@ -723,11 +765,13 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
723 return ERR_INVALID_PROCESSOR_ID; 765 return ERR_INVALID_PROCESSOR_ID;
724 } 766 }
725 767
768 const std::string name = fmt::format("thread-{:X}", entry_point);
726 auto& kernel = Core::System::GetInstance().Kernel(); 769 auto& kernel = Core::System::GetInstance().Kernel();
727 CASCADE_RESULT(SharedPtr<Thread> thread, 770 CASCADE_RESULT(SharedPtr<Thread> thread,
728 Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, 771 Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top,
729 *Core::CurrentProcess())); 772 *current_process));
730 const auto new_guest_handle = kernel.HandleTable().Create(thread); 773
774 const auto new_guest_handle = current_process->GetHandleTable().Create(thread);
731 if (new_guest_handle.Failed()) { 775 if (new_guest_handle.Failed()) {
732 return new_guest_handle.Code(); 776 return new_guest_handle.Code();
733 } 777 }
@@ -748,8 +792,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
748static ResultCode StartThread(Handle thread_handle) { 792static ResultCode StartThread(Handle thread_handle) {
749 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); 793 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
750 794
751 auto& kernel = Core::System::GetInstance().Kernel(); 795 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
752 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 796 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
753 if (!thread) { 797 if (!thread) {
754 return ERR_INVALID_HANDLE; 798 return ERR_INVALID_HANDLE;
755 } 799 }
@@ -796,8 +840,8 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
796 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", 840 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
797 mutex_addr, condition_variable_addr, thread_handle, nano_seconds); 841 mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
798 842
799 auto& kernel = Core::System::GetInstance().Kernel(); 843 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
800 SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 844 SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
801 ASSERT(thread); 845 ASSERT(thread);
802 846
803 CASCADE_CODE(Mutex::Release(mutex_addr)); 847 CASCADE_CODE(Mutex::Release(mutex_addr));
@@ -908,9 +952,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
908 mutex_val | Mutex::MutexHasWaitersFlag)); 952 mutex_val | Mutex::MutexHasWaitersFlag));
909 953
910 // The mutex is already owned by some other thread, make this thread wait on it. 954 // The mutex is already owned by some other thread, make this thread wait on it.
911 auto& kernel = Core::System::GetInstance().Kernel(); 955 const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
912 Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); 956 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
913 auto owner = kernel.HandleTable().Get<Thread>(owner_handle); 957 auto owner = handle_table.Get<Thread>(owner_handle);
914 ASSERT(owner); 958 ASSERT(owner);
915 ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); 959 ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
916 thread->InvalidateWakeupCallback(); 960 thread->InvalidateWakeupCallback();
@@ -989,16 +1033,16 @@ static u64 GetSystemTick() {
989static ResultCode CloseHandle(Handle handle) { 1033static ResultCode CloseHandle(Handle handle) {
990 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); 1034 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
991 1035
992 auto& kernel = Core::System::GetInstance().Kernel(); 1036 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
993 return kernel.HandleTable().Close(handle); 1037 return handle_table.Close(handle);
994} 1038}
995 1039
996/// Reset an event 1040/// Reset an event
997static ResultCode ResetSignal(Handle handle) { 1041static ResultCode ResetSignal(Handle handle) {
998 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); 1042 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle);
999 1043
1000 auto& kernel = Core::System::GetInstance().Kernel(); 1044 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1001 auto event = kernel.HandleTable().Get<Event>(handle); 1045 auto event = handle_table.Get<Event>(handle);
1002 1046
1003 ASSERT(event != nullptr); 1047 ASSERT(event != nullptr);
1004 1048
@@ -1017,8 +1061,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
1017static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { 1061static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) {
1018 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); 1062 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
1019 1063
1020 auto& kernel = Core::System::GetInstance().Kernel(); 1064 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1021 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 1065 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1022 if (!thread) { 1066 if (!thread) {
1023 return ERR_INVALID_HANDLE; 1067 return ERR_INVALID_HANDLE;
1024 } 1068 }
@@ -1033,8 +1077,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
1033 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle, 1077 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle,
1034 mask, core); 1078 mask, core);
1035 1079
1036 auto& kernel = Core::System::GetInstance().Kernel(); 1080 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1037 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 1081 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1038 if (!thread) { 1082 if (!thread) {
1039 return ERR_INVALID_HANDLE; 1083 return ERR_INVALID_HANDLE;
1040 } 1084 }
@@ -1095,7 +1139,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
1095 } 1139 }
1096 1140
1097 auto& kernel = Core::System::GetInstance().Kernel(); 1141 auto& kernel = Core::System::GetInstance().Kernel();
1098 auto& handle_table = kernel.HandleTable(); 1142 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1099 auto shared_mem_handle = 1143 auto shared_mem_handle =
1100 SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, 1144 SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size,
1101 local_perms, remote_perms); 1145 local_perms, remote_perms);
@@ -1107,10 +1151,12 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
1107static ResultCode ClearEvent(Handle handle) { 1151static ResultCode ClearEvent(Handle handle) {
1108 LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); 1152 LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
1109 1153
1110 auto& kernel = Core::System::GetInstance().Kernel(); 1154 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1111 SharedPtr<Event> evt = kernel.HandleTable().Get<Event>(handle); 1155 SharedPtr<Event> evt = handle_table.Get<Event>(handle);
1112 if (evt == nullptr) 1156 if (evt == nullptr) {
1113 return ERR_INVALID_HANDLE; 1157 return ERR_INVALID_HANDLE;
1158 }
1159
1114 evt->Clear(); 1160 evt->Clear();
1115 return RESULT_SUCCESS; 1161 return RESULT_SUCCESS;
1116} 1162}
@@ -1123,8 +1169,8 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
1123 Status, 1169 Status,
1124 }; 1170 };
1125 1171
1126 const auto& kernel = Core::System::GetInstance().Kernel(); 1172 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1127 const auto process = kernel.HandleTable().Get<Process>(process_handle); 1173 const auto process = handle_table.Get<Process>(process_handle);
1128 if (!process) { 1174 if (!process) {
1129 return ERR_INVALID_HANDLE; 1175 return ERR_INVALID_HANDLE;
1130 } 1176 }
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/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index e61748ca3..cf065c2e0 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,9 @@
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
24constexpr u32 MAX_JPEG_IMAGE_SIZE = 0x20000;
25
19// TODO: RE this structure 26// TODO: RE this structure
20struct UserData { 27struct UserData {
21 INSERT_PADDING_WORDS(1); 28 INSERT_PADDING_WORDS(1);
@@ -27,6 +34,11 @@ struct UserData {
27}; 34};
28static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); 35static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
29 36
37static std::string GetImagePath(UUID uuid) {
38 return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
39 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
40}
41
30class IProfile final : public ServiceFramework<IProfile> { 42class IProfile final : public ServiceFramework<IProfile> {
31public: 43public:
32 explicit IProfile(UUID user_id, ProfileManager& profile_manager) 44 explicit IProfile(UUID user_id, ProfileManager& profile_manager)
@@ -73,11 +85,11 @@ private:
73 } 85 }
74 86
75 void LoadImage(Kernel::HLERequestContext& ctx) { 87 void LoadImage(Kernel::HLERequestContext& ctx) {
76 LOG_WARNING(Service_ACC, "(STUBBED) called"); 88 LOG_DEBUG(Service_ACC, "called");
77 // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg 89 // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
78 // TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000 90 // used as a backup should the one on disk not exist
79 constexpr u32 jpeg_size = 107; 91 constexpr u32 backup_jpeg_size = 107;
80 static constexpr std::array<u8, jpeg_size> jpeg{ 92 static constexpr std::array<u8, backup_jpeg_size> backup_jpeg{
81 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 93 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, 94 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, 95 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a,
@@ -87,18 +99,42 @@ private:
87 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, 99 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01,
88 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, 100 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
89 }; 101 };
90 ctx.WriteBuffer(jpeg); 102
91 IPC::ResponseBuilder rb{ctx, 3}; 103 IPC::ResponseBuilder rb{ctx, 3};
92 rb.Push(RESULT_SUCCESS); 104 rb.Push(RESULT_SUCCESS);
93 rb.Push<u32>(jpeg_size); 105
106 const FileUtil::IOFile image(GetImagePath(user_id), "rb");
107
108 if (!image.IsOpen()) {
109 LOG_WARNING(Service_ACC,
110 "Failed to load user provided image! Falling back to built-in backup...");
111 ctx.WriteBuffer(backup_jpeg);
112 rb.Push<u32>(backup_jpeg_size);
113 } else {
114 const auto size = std::min<u32>(image.GetSize(), MAX_JPEG_IMAGE_SIZE);
115 std::vector<u8> buffer(size);
116 image.ReadBytes(buffer.data(), buffer.size());
117
118 ctx.WriteBuffer(buffer.data(), buffer.size());
119 rb.Push<u32>(buffer.size());
120 }
94 } 121 }
95 122
96 void GetImageSize(Kernel::HLERequestContext& ctx) { 123 void GetImageSize(Kernel::HLERequestContext& ctx) {
97 LOG_WARNING(Service_ACC, "(STUBBED) called"); 124 LOG_DEBUG(Service_ACC, "called");
98 constexpr u32 jpeg_size = 107; 125 constexpr u32 backup_jpeg_size = 107;
99 IPC::ResponseBuilder rb{ctx, 3}; 126 IPC::ResponseBuilder rb{ctx, 3};
100 rb.Push(RESULT_SUCCESS); 127 rb.Push(RESULT_SUCCESS);
101 rb.Push<u32>(jpeg_size); 128
129 const FileUtil::IOFile image(GetImagePath(user_id), "rb");
130
131 if (!image.IsOpen()) {
132 LOG_WARNING(Service_ACC,
133 "Failed to load user provided image! Falling back to built-in backup...");
134 rb.Push<u32>(backup_jpeg_size);
135 } else {
136 rb.Push<u32>(std::min<u32>(image.GetSize(), MAX_JPEG_IMAGE_SIZE));
137 }
102 } 138 }
103 139
104 const ProfileManager& profile_manager; 140 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/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 428069df2..54305cf05 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -24,8 +24,8 @@ namespace Service::AOC {
24constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; 24constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
25constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000; 25constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000;
26 26
27static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) { 27static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
28 return (aoc & DLC_BASE_TITLE_ID_MASK) == base; 28 return (title_id & DLC_BASE_TITLE_ID_MASK) == base;
29} 29}
30 30
31static std::vector<u64> AccumulateAOCTitleIDs() { 31static std::vector<u64> AccumulateAOCTitleIDs() {
@@ -74,7 +74,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
74 const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 74 const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
75 rb.Push<u32>(static_cast<u32>( 75 rb.Push<u32>(static_cast<u32>(
76 std::count_if(add_on_content.begin(), add_on_content.end(), 76 std::count_if(add_on_content.begin(), add_on_content.end(),
77 [&current](u64 tid) { return (tid & DLC_BASE_TITLE_ID_MASK) == current; }))); 77 [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); })));
78} 78}
79 79
80void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { 80void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
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/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index b06e65a77..4b4d1324f 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -108,9 +108,10 @@ void Controller_NPad::OnInit() {
108 styleset_changed_event = 108 styleset_changed_event =
109 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); 109 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
110 110
111 if (!IsControllerActivated()) 111 if (!IsControllerActivated()) {
112 return; 112 return;
113 std::size_t controller{}; 113 }
114
114 if (style.raw == 0) { 115 if (style.raw == 0) {
115 // We want to support all controllers 116 // We want to support all controllers
116 style.handheld.Assign(1); 117 style.handheld.Assign(1);
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index beb89218a..a9aa9ec78 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -827,6 +827,7 @@ public:
827 {11, nullptr, "EnableJoyPollingReceiveMode"}, 827 {11, nullptr, "EnableJoyPollingReceiveMode"},
828 {12, nullptr, "DisableJoyPollingReceiveMode"}, 828 {12, nullptr, "DisableJoyPollingReceiveMode"},
829 {13, nullptr, "GetPollingData"}, 829 {13, nullptr, "GetPollingData"},
830 {14, nullptr, "SetStatusManagerType"},
830 }; 831 };
831 // clang-format on 832 // clang-format on
832 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/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..9a4eb9301 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
69 }; 97 };
70 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);
107 };
108 static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
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,217 @@ 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
331void 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; // Failed to load file
335 }
336 std::memcpy(&amiibo, buffer.data(), sizeof(amiibo));
337 nfc_tag_load->Signal();
338}
339const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const {
340 return nfc_tag_load;
341}
342const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const {
343 return amiibo;
160} 344}
161 345
162void InstallInterfaces(SM::ServiceManager& service_manager) { 346void 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..46370dedd 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 void 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/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/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/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/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 8afd26fe9..78ba29fc1 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -13,8 +13,7 @@
13#include "video_core/renderer_base.h" 13#include "video_core/renderer_base.h"
14#include "video_core/textures/texture.h" 14#include "video_core/textures/texture.h"
15 15
16namespace Tegra { 16namespace Tegra::Engines {
17namespace Engines {
18 17
19/// First register id that is actually a Macro call. 18/// First register id that is actually a Macro call.
20constexpr u32 MacroRegistersStart = 0xE00; 19constexpr u32 MacroRegistersStart = 0xE00;
@@ -156,7 +155,6 @@ void Maxwell3D::ProcessQueryGet() {
156 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, 155 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
157 "Units other than CROP are unimplemented"); 156 "Units other than CROP are unimplemented");
158 157
159 u32 value = Memory::Read32(*address);
160 u64 result = 0; 158 u64 result = 0;
161 159
162 // TODO(Subv): Support the other query variables 160 // TODO(Subv): Support the other query variables
@@ -408,5 +406,4 @@ void Maxwell3D::ProcessClearBuffers() {
408 rasterizer.Clear(); 406 rasterizer.Clear();
409} 407}
410 408
411} // namespace Engines 409} // 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..0e09a7ee5 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;
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 103cd110e..b8a78cf82 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -7,8 +7,7 @@
7#include "video_core/rasterizer_interface.h" 7#include "video_core/rasterizer_interface.h"
8#include "video_core/textures/decoders.h" 8#include "video_core/textures/decoders.h"
9 9
10namespace Tegra { 10namespace Tegra::Engines {
11namespace Engines {
12 11
13MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) 12MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
14 : memory_manager(memory_manager), rasterizer{rasterizer} {} 13 : memory_manager(memory_manager), rasterizer{rasterizer} {}
@@ -78,9 +77,9 @@ void MaxwellDMA::HandleCopy() {
78 77
79 ASSERT(regs.exec.enable_2d == 1); 78 ASSERT(regs.exec.enable_2d == 1);
80 79
81 std::size_t copy_size = regs.x_count * regs.y_count; 80 const std::size_t copy_size = regs.x_count * regs.y_count;
82 81
83 const auto FlushAndInvalidate = [&](u32 src_size, u32 dst_size) { 82 const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) {
84 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated 83 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
85 // copying. 84 // copying.
86 rasterizer.FlushRegion(source_cpu, src_size); 85 rasterizer.FlushRegion(source_cpu, src_size);
@@ -91,14 +90,11 @@ void MaxwellDMA::HandleCopy() {
91 rasterizer.InvalidateRegion(dest_cpu, dst_size); 90 rasterizer.InvalidateRegion(dest_cpu, dst_size);
92 }; 91 };
93 92
94 u8* src_buffer = Memory::GetPointer(source_cpu);
95 u8* dst_buffer = Memory::GetPointer(dest_cpu);
96
97 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { 93 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
98 ASSERT(regs.src_params.size_z == 1); 94 ASSERT(regs.src_params.size_z == 1);
99 // 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.
100 96
101 u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x; 97 const u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x;
102 98
103 FlushAndInvalidate(regs.src_pitch * regs.src_params.size_y, 99 FlushAndInvalidate(regs.src_pitch * regs.src_params.size_y,
104 copy_size * src_bytes_per_pixel); 100 copy_size * src_bytes_per_pixel);
@@ -111,7 +107,7 @@ void MaxwellDMA::HandleCopy() {
111 ASSERT(regs.dst_params.size_z == 1); 107 ASSERT(regs.dst_params.size_z == 1);
112 ASSERT(regs.src_pitch == regs.x_count); 108 ASSERT(regs.src_pitch == regs.x_count);
113 109
114 u32 src_bpp = regs.src_pitch / regs.x_count; 110 const u32 src_bpp = regs.src_pitch / regs.x_count;
115 111
116 FlushAndInvalidate(regs.src_pitch * regs.y_count, 112 FlushAndInvalidate(regs.src_pitch * regs.y_count,
117 regs.dst_params.size_x * regs.dst_params.size_y * src_bpp); 113 regs.dst_params.size_x * regs.dst_params.size_y * src_bpp);
@@ -122,5 +118,4 @@ void MaxwellDMA::HandleCopy() {
122 } 118 }
123} 119}
124 120
125} // namespace Engines 121} // namespace Tegra::Engines
126} // namespace Tegra
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index e3d67ff87..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,
@@ -564,6 +565,10 @@ union Instruction {
564 } fmul; 565 } fmul;
565 566
566 union { 567 union {
568 BitField<55, 1, u64> saturate;
569 } fmul32;
570
571 union {
567 BitField<48, 1, u64> is_signed; 572 BitField<48, 1, u64> is_signed;
568 } shift; 573 } shift;
569 574
@@ -753,7 +758,6 @@ union Instruction {
753 BitField<45, 2, PredOperation> op; 758 BitField<45, 2, PredOperation> op;
754 BitField<47, 1, u64> ftz; 759 BitField<47, 1, u64> ftz;
755 BitField<48, 4, PredCondition> cond; 760 BitField<48, 4, PredCondition> cond;
756 BitField<56, 1, u64> neg_b;
757 } fsetp; 761 } fsetp;
758 762
759 union { 763 union {
@@ -780,6 +784,14 @@ union Instruction {
780 } psetp; 784 } psetp;
781 785
782 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 {
783 BitField<12, 3, u64> pred12; 795 BitField<12, 3, u64> pred12;
784 BitField<15, 1, u64> neg_pred12; 796 BitField<15, 1, u64> neg_pred12;
785 BitField<24, 2, PredOperation> cond; 797 BitField<24, 2, PredOperation> cond;
@@ -828,7 +840,6 @@ union Instruction {
828 BitField<53, 1, u64> neg_b; 840 BitField<53, 1, u64> neg_b;
829 BitField<54, 1, u64> abs_a; 841 BitField<54, 1, u64> abs_a;
830 BitField<55, 1, u64> ftz; 842 BitField<55, 1, u64> ftz;
831 BitField<56, 1, u64> neg_imm;
832 } fset; 843 } fset;
833 844
834 union { 845 union {
@@ -1152,15 +1163,17 @@ union Instruction {
1152 union { 1163 union {
1153 BitField<48, 1, u64> signed_a; 1164 BitField<48, 1, u64> signed_a;
1154 BitField<38, 1, u64> is_byte_chunk_a; 1165 BitField<38, 1, u64> is_byte_chunk_a;
1155 BitField<36, 2, VmadType> type_a; 1166 BitField<36, 2, VideoType> type_a;
1156 BitField<36, 2, u64> byte_height_a; 1167 BitField<36, 2, u64> byte_height_a;
1157 1168
1158 BitField<49, 1, u64> signed_b; 1169 BitField<49, 1, u64> signed_b;
1159 BitField<50, 1, u64> use_register_b; 1170 BitField<50, 1, u64> use_register_b;
1160 BitField<30, 1, u64> is_byte_chunk_b; 1171 BitField<30, 1, u64> is_byte_chunk_b;
1161 BitField<28, 2, VmadType> type_b; 1172 BitField<28, 2, VideoType> type_b;
1162 BitField<28, 2, u64> byte_height_b; 1173 BitField<28, 2, u64> byte_height_b;
1174 } video;
1163 1175
1176 union {
1164 BitField<51, 2, VmadShr> shr; 1177 BitField<51, 2, VmadShr> shr;
1165 BitField<55, 1, u64> saturate; // Saturates the result (a * b + c) 1178 BitField<55, 1, u64> saturate; // Saturates the result (a * b + c)
1166 BitField<47, 1, u64> cc; 1179 BitField<47, 1, u64> cc;
@@ -1211,11 +1224,13 @@ public:
1211 KIL, 1224 KIL,
1212 SSY, 1225 SSY,
1213 SYNC, 1226 SYNC,
1227 BRK,
1214 DEPBAR, 1228 DEPBAR,
1215 BFE_C, 1229 BFE_C,
1216 BFE_R, 1230 BFE_R,
1217 BFE_IMM, 1231 BFE_IMM,
1218 BRA, 1232 BRA,
1233 PBK,
1219 LD_A, 1234 LD_A,
1220 LD_C, 1235 LD_C,
1221 ST_A, 1236 ST_A,
@@ -1234,6 +1249,7 @@ public:
1234 OUT_R, // Emit vertex/primitive 1249 OUT_R, // Emit vertex/primitive
1235 ISBERD, 1250 ISBERD,
1236 VMAD, 1251 VMAD,
1252 VSETP,
1237 FFMA_IMM, // Fused Multiply and Add 1253 FFMA_IMM, // Fused Multiply and Add
1238 FFMA_CR, 1254 FFMA_CR,
1239 FFMA_RC, 1255 FFMA_RC,
@@ -1372,7 +1388,7 @@ public:
1372 /// conditionally executed). 1388 /// conditionally executed).
1373 static bool IsPredicatedInstruction(Id opcode) { 1389 static bool IsPredicatedInstruction(Id opcode) {
1374 // TODO(Subv): Add the rest of unpredicated instructions. 1390 // TODO(Subv): Add the rest of unpredicated instructions.
1375 return opcode != Id::SSY; 1391 return opcode != Id::SSY && opcode != Id::PBK;
1376 } 1392 }
1377 1393
1378 class Matcher { 1394 class Matcher {
@@ -1468,9 +1484,11 @@ private:
1468#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)
1469 INST("111000110011----", Id::KIL, Type::Flow, "KIL"), 1485 INST("111000110011----", Id::KIL, Type::Flow, "KIL"),
1470 INST("111000101001----", Id::SSY, Type::Flow, "SSY"), 1486 INST("111000101001----", Id::SSY, Type::Flow, "SSY"),
1487 INST("111000101010----", Id::PBK, Type::Flow, "PBK"),
1471 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"),
1472 INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"), 1491 INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
1473 INST("1111000011111---", Id::SYNC, Type::Synch, "SYNC"),
1474 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), 1492 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
1475 INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"), 1493 INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"),
1476 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), 1494 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
@@ -1489,6 +1507,7 @@ private:
1489 INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"), 1507 INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
1490 INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"), 1508 INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
1491 INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"), 1509 INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"),
1510 INST("0101000011110---", Id::VSETP, Type::Trivial, "VSETP"),
1492 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), 1511 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
1493 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), 1512 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
1494 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), 1513 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
@@ -1608,4 +1627,4 @@ private:
1608 } 1627 }
1609}; 1628};
1610 1629
1611} // namespace Tegra::Shader 1630} // namespace Tegra::Shader \ No newline at end of file
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 3daccf82f..be51c5215 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -570,10 +570,11 @@ void RasterizerOpenGL::DrawArrays() {
570 SyncBlendState(); 570 SyncBlendState();
571 SyncLogicOpState(); 571 SyncLogicOpState();
572 SyncCullMode(); 572 SyncCullMode();
573 SyncAlphaTest();
574 SyncScissorTest(); 573 SyncScissorTest();
574 // Alpha Testing is synced on shaders.
575 SyncTransformFeedback(); 575 SyncTransformFeedback();
576 SyncPointState(); 576 SyncPointState();
577 CheckAlphaTests();
577 578
578 // TODO(bunnei): Sync framebuffer_scale uniform here 579 // TODO(bunnei): Sync framebuffer_scale uniform here
579 // TODO(bunnei): Sync scissorbox uniform(s) here 580 // TODO(bunnei): Sync scissorbox uniform(s) here
@@ -1007,17 +1008,6 @@ void RasterizerOpenGL::SyncLogicOpState() {
1007 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); 1008 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation);
1008} 1009}
1009 1010
1010void RasterizerOpenGL::SyncAlphaTest() {
1011 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1012
1013 // TODO(Rodrigo): Alpha testing is a legacy OpenGL feature, but it can be
1014 // implemented with a test+discard in fragment shaders.
1015 if (regs.alpha_test_enabled != 0) {
1016 LOG_CRITICAL(Render_OpenGL, "Alpha testing is not implemented");
1017 UNREACHABLE();
1018 }
1019}
1020
1021void RasterizerOpenGL::SyncScissorTest() { 1011void RasterizerOpenGL::SyncScissorTest() {
1022 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1012 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1023 1013
@@ -1052,4 +1042,15 @@ void RasterizerOpenGL::SyncPointState() {
1052 state.point.size = regs.point_size == 0 ? 1 : regs.point_size; 1042 state.point.size = regs.point_size == 0 ? 1 : regs.point_size;
1053} 1043}
1054 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
1055} // namespace OpenGL 1056} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index b1f7ccc7e..0e90a31f5 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -162,9 +162,6 @@ private:
162 /// Syncs the LogicOp state to match the guest state 162 /// Syncs the LogicOp state to match the guest state
163 void SyncLogicOpState(); 163 void SyncLogicOpState();
164 164
165 /// Syncs the alpha test state to match the guest state
166 void SyncAlphaTest();
167
168 /// Syncs the scissor test state to match the guest state 165 /// Syncs the scissor test state to match the guest state
169 void SyncScissorTest(); 166 void SyncScissorTest();
170 167
@@ -174,6 +171,9 @@ private:
174 /// Syncs the point state to match the guest state 171 /// Syncs the point state to match the guest state
175 void SyncPointState(); 172 void SyncPointState();
176 173
174 /// Check asserts for alpha testing.
175 void CheckAlphaTests();
176
177 bool has_ARB_direct_state_access = false; 177 bool has_ARB_direct_state_access = false;
178 bool has_ARB_multi_bind = false; 178 bool has_ARB_multi_bind = false;
179 bool has_ARB_separate_shader_objects = false; 179 bool has_ARB_separate_shader_objects = false;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index a427353e9..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>
@@ -163,10 +164,11 @@ private:
163 const ExitMethod jmp = Scan(target, end, labels); 164 const ExitMethod jmp = Scan(target, end, labels);
164 return exit_method = ParallelExit(no_jmp, jmp); 165 return exit_method = ParallelExit(no_jmp, jmp);
165 } 166 }
166 case OpCode::Id::SSY: { 167 case OpCode::Id::SSY:
167 // 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.
168 ASSERT_MSG(instr.bra.constant_buffer == 0, 170 ASSERT_MSG(instr.bra.constant_buffer == 0,
169 "Constant buffer SSY is not supported"); 171 "Constant buffer branching is not supported");
170 const u32 target = offset + instr.bra.GetBranchTarget(); 172 const u32 target = offset + instr.bra.GetBranchTarget();
171 labels.insert(target); 173 labels.insert(target);
172 // Continue scanning for an exit method. 174 // Continue scanning for an exit method.
@@ -275,7 +277,8 @@ public:
275 GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, 277 GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations,
276 const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix, 278 const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix,
277 const Tegra::Shader::Header& header) 279 const Tegra::Shader::Header& header)
278 : 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{} {
279 BuildRegisterList(); 282 BuildRegisterList();
280 BuildInputList(); 283 BuildInputList();
281 } 284 }
@@ -378,8 +381,8 @@ public:
378 * @param reg The destination register to use. 381 * @param reg The destination register to use.
379 * @param elem The element to use for the operation. 382 * @param elem The element to use for the operation.
380 * @param value The code representing the value to assign. Type has to be half float. 383 * @param value The code representing the value to assign. Type has to be half float.
381 * @param type Half float kind of assignment. 384 * @param merge Half float kind of assignment.
382 * @param dest_num_components Number of components in the destionation. 385 * @param dest_num_components Number of components in the destination.
383 * @param value_num_components Number of components in the value. 386 * @param value_num_components Number of components in the value.
384 * @param is_saturated Optional, when True, saturates the provided value. 387 * @param is_saturated Optional, when True, saturates the provided value.
385 * @param dest_elem Optional, the destination element to use for the operation. 388 * @param dest_elem Optional, the destination element to use for the operation.
@@ -422,6 +425,7 @@ public:
422 * @param reg The destination register to use. 425 * @param reg The destination register to use.
423 * @param elem The element to use for the operation. 426 * @param elem The element to use for the operation.
424 * @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.
425 * @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).
426 */ 430 */
427 void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute, 431 void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute,
@@ -478,7 +482,12 @@ public:
478 std::to_string(static_cast<u32>(attribute)) + ']' + 482 std::to_string(static_cast<u32>(attribute)) + ']' +
479 GetSwizzle(elem) + " = " + src + ';'); 483 GetSwizzle(elem) + " = " + src + ';');
480 } else { 484 } else {
481 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 }
482 } 491 }
483 } 492 }
484 } 493 }
@@ -522,6 +531,7 @@ public:
522 531
523 /// Add declarations. 532 /// Add declarations.
524 void GenerateDeclarations(const std::string& suffix) { 533 void GenerateDeclarations(const std::string& suffix) {
534 GenerateVertex();
525 GenerateRegisters(suffix); 535 GenerateRegisters(suffix);
526 GenerateInternalFlags(); 536 GenerateInternalFlags();
527 GenerateInputAttrs(); 537 GenerateInputAttrs();
@@ -681,6 +691,20 @@ private:
681 declarations.AddNewLine(); 691 declarations.AddNewLine();
682 } 692 }
683 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
684 /// Generates code representing a temporary (GPR) register. 708 /// Generates code representing a temporary (GPR) register.
685 std::string GetRegister(const Register& reg, unsigned elem) { 709 std::string GetRegister(const Register& reg, unsigned elem) {
686 if (reg == Register::ZeroIndex) { 710 if (reg == Register::ZeroIndex) {
@@ -834,6 +858,8 @@ private:
834 /// Generates code representing the declaration name of an output attribute register. 858 /// Generates code representing the declaration name of an output attribute register.
835 std::string GetOutputAttribute(Attribute::Index attribute) { 859 std::string GetOutputAttribute(Attribute::Index attribute) {
836 switch (attribute) { 860 switch (attribute) {
861 case Attribute::Index::PointSize:
862 return "gl_PointSize";
837 case Attribute::Index::Position: 863 case Attribute::Index::Position:
838 return "position"; 864 return "position";
839 default: 865 default:
@@ -868,6 +894,7 @@ private:
868 const Maxwell3D::Regs::ShaderStage& stage; 894 const Maxwell3D::Regs::ShaderStage& stage;
869 const std::string& suffix; 895 const std::string& suffix;
870 const Tegra::Shader::Header& header; 896 const Tegra::Shader::Header& header;
897 std::unordered_set<Attribute::Index> fixed_pipeline_output_attributes_used;
871}; 898};
872 899
873class GLSLGenerator { 900class GLSLGenerator {
@@ -951,7 +978,7 @@ private:
951 // Can't assign to the constant predicate. 978 // Can't assign to the constant predicate.
952 ASSERT(pred != static_cast<u64>(Pred::UnusedIndex)); 979 ASSERT(pred != static_cast<u64>(Pred::UnusedIndex));
953 980
954 const std::string variable = 'p' + std::to_string(pred) + '_' + suffix; 981 std::string variable = 'p' + std::to_string(pred) + '_' + suffix;
955 shader.AddLine(variable + " = " + value + ';'); 982 shader.AddLine(variable + " = " + value + ';');
956 declr_predicates.insert(std::move(variable)); 983 declr_predicates.insert(std::move(variable));
957 } 984 }
@@ -1058,7 +1085,7 @@ private:
1058 /* 1085 /*
1059 * Transforms the input string GLSL operand into an unpacked half float pair. 1086 * Transforms the input string GLSL operand into an unpacked half float pair.
1060 * @note This function returns a float type pair instead of a half float pair. This is because 1087 * @note This function returns a float type pair instead of a half float pair. This is because
1061 * real half floats are not standarized in GLSL but unpackHalf2x16 (which returns a vec2) is. 1088 * real half floats are not standardized in GLSL but unpackHalf2x16 (which returns a vec2) is.
1062 * @param operand Input operand. It has to be an unsigned integer. 1089 * @param operand Input operand. It has to be an unsigned integer.
1063 * @param type How to unpack the unsigned integer to a half float pair. 1090 * @param type How to unpack the unsigned integer to a half float pair.
1064 * @param abs Get the absolute value of unpacked half floats. 1091 * @param abs Get the absolute value of unpacked half floats.
@@ -1232,27 +1259,27 @@ private:
1232 } 1259 }
1233 1260
1234 /* 1261 /*
1235 * 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
1236 * top. 1263 * top.
1237 */ 1264 */
1238 void EmitPushToSSYStack(u32 target) { 1265 void EmitPushToFlowStack(u32 target) {
1239 shader.AddLine('{'); 1266 shader.AddLine('{');
1240 ++shader.scope; 1267 ++shader.scope;
1241 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;");
1242 shader.AddLine("ssy_stack_top++;"); 1269 shader.AddLine("flow_stack_top++;");
1243 --shader.scope; 1270 --shader.scope;
1244 shader.AddLine('}'); 1271 shader.AddLine('}');
1245 } 1272 }
1246 1273
1247 /* 1274 /*
1248 * 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
1249 * popped address and decrementing the stack top. 1276 * popped address and decrementing the stack top.
1250 */ 1277 */
1251 void EmitPopFromSSYStack() { 1278 void EmitPopFromFlowStack() {
1252 shader.AddLine('{'); 1279 shader.AddLine('{');
1253 ++shader.scope; 1280 ++shader.scope;
1254 shader.AddLine("ssy_stack_top--;"); 1281 shader.AddLine("flow_stack_top--;");
1255 shader.AddLine("jmp_to = ssy_stack[ssy_stack_top];"); 1282 shader.AddLine("jmp_to = flow_stack[flow_stack_top];");
1256 shader.AddLine("break;"); 1283 shader.AddLine("break;");
1257 --shader.scope; 1284 --shader.scope;
1258 shader.AddLine('}'); 1285 shader.AddLine('}');
@@ -1264,9 +1291,29 @@ private:
1264 1291
1265 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");
1266 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
1267 // 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
1268 // rendertargets/components are skipped in the register assignment. 1315 // rendertargets/components are skipped in the register assignment.
1269 u32 current_reg = 0; 1316 current_reg = 0;
1270 for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets; 1317 for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets;
1271 ++render_target) { 1318 ++render_target) {
1272 // 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.
@@ -1290,6 +1337,63 @@ private:
1290 } 1337 }
1291 } 1338 }
1292 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
1293 /** 1397 /**
1294 * Compiles a single instruction from Tegra to GLSL. 1398 * Compiles a single instruction from Tegra to GLSL.
1295 * @param offset the offset of the Tegra shader instruction. 1399 * @param offset the offset of the Tegra shader instruction.
@@ -1459,9 +1563,10 @@ private:
1459 break; 1563 break;
1460 } 1564 }
1461 case OpCode::Id::FMUL32_IMM: { 1565 case OpCode::Id::FMUL32_IMM: {
1462 regs.SetRegisterToFloat( 1566 regs.SetRegisterToFloat(instr.gpr0, 0,
1463 instr.gpr0, 0, 1567 regs.GetRegisterAsFloat(instr.gpr8) + " * " +
1464 regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1); 1568 GetImmediate32(instr),
1569 1, 1, instr.fmul32.saturate);
1465 break; 1570 break;
1466 } 1571 }
1467 case OpCode::Id::FADD32I: { 1572 case OpCode::Id::FADD32I: {
@@ -2736,20 +2841,13 @@ private:
2736 break; 2841 break;
2737 } 2842 }
2738 case OpCode::Type::FloatSetPredicate: { 2843 case OpCode::Type::FloatSetPredicate: {
2739 std::string op_a = instr.fsetp.neg_a ? "-" : ""; 2844 const std::string op_a =
2740 op_a += regs.GetRegisterAsFloat(instr.gpr8); 2845 GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8), instr.fsetp.abs_a != 0,
2741 2846 instr.fsetp.neg_a != 0);
2742 if (instr.fsetp.abs_a) {
2743 op_a = "abs(" + op_a + ')';
2744 }
2745 2847
2746 std::string op_b{}; 2848 std::string op_b;
2747 2849
2748 if (instr.is_b_imm) { 2850 if (instr.is_b_imm) {
2749 if (instr.fsetp.neg_b) {
2750 // Only the immediate version of fsetp has a neg_b bit.
2751 op_b += '-';
2752 }
2753 op_b += '(' + GetImmediate19(instr) + ')'; 2851 op_b += '(' + GetImmediate19(instr) + ')';
2754 } else { 2852 } else {
2755 if (instr.is_b_gpr) { 2853 if (instr.is_b_gpr) {
@@ -2945,33 +3043,24 @@ private:
2945 break; 3043 break;
2946 } 3044 }
2947 case OpCode::Type::FloatSet: { 3045 case OpCode::Type::FloatSet: {
2948 std::string op_a = instr.fset.neg_a ? "-" : ""; 3046 const std::string op_a = GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8),
2949 op_a += regs.GetRegisterAsFloat(instr.gpr8); 3047 instr.fset.abs_a != 0, instr.fset.neg_a != 0);
2950 3048
2951 if (instr.fset.abs_a) { 3049 std::string op_b;
2952 op_a = "abs(" + op_a + ')';
2953 }
2954
2955 std::string op_b = instr.fset.neg_b ? "-" : "";
2956 3050
2957 if (instr.is_b_imm) { 3051 if (instr.is_b_imm) {
2958 const std::string imm = GetImmediate19(instr); 3052 const std::string imm = GetImmediate19(instr);
2959 if (instr.fset.neg_imm) 3053 op_b = imm;
2960 op_b += "(-" + imm + ')';
2961 else
2962 op_b += imm;
2963 } else { 3054 } else {
2964 if (instr.is_b_gpr) { 3055 if (instr.is_b_gpr) {
2965 op_b += regs.GetRegisterAsFloat(instr.gpr20); 3056 op_b = regs.GetRegisterAsFloat(instr.gpr20);
2966 } else { 3057 } else {
2967 op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, 3058 op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
2968 GLSLRegister::Type::Float); 3059 GLSLRegister::Type::Float);
2969 } 3060 }
2970 } 3061 }
2971 3062
2972 if (instr.fset.abs_b) { 3063 op_b = GetOperandAbsNeg(op_b, instr.fset.abs_b != 0, instr.fset.neg_b != 0);
2973 op_b = "abs(" + op_b + ')';
2974 }
2975 3064
2976 // 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
2977 // condition is true, and to 0 otherwise. 3066 // condition is true, and to 0 otherwise.
@@ -3279,16 +3368,32 @@ private:
3279 // 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
3280 // 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
3281 // has a similar structure to the BRA opcode. 3370 // has a similar structure to the BRA opcode.
3282 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");
3283 3372
3284 const u32 target = offset + instr.bra.GetBranchTarget(); 3373 const u32 target = offset + instr.bra.GetBranchTarget();
3285 EmitPushToSSYStack(target); 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");
3382
3383 const u32 target = offset + instr.bra.GetBranchTarget();
3384 EmitPushToFlowStack(target);
3286 break; 3385 break;
3287 } 3386 }
3288 case OpCode::Id::SYNC: { 3387 case OpCode::Id::SYNC: {
3289 // 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
3290 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); 3389 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
3291 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();
3292 break; 3397 break;
3293 } 3398 }
3294 case OpCode::Id::DEPBAR: { 3399 case OpCode::Id::DEPBAR: {
@@ -3298,87 +3403,51 @@ private:
3298 break; 3403 break;
3299 } 3404 }
3300 case OpCode::Id::VMAD: { 3405 case OpCode::Id::VMAD: {
3301 const bool signed_a = instr.vmad.signed_a == 1; 3406 const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1;
3302 const bool signed_b = instr.vmad.signed_b == 1; 3407 const std::string op_a = GetVideoOperandA(instr);
3303 const bool result_signed = signed_a || signed_b; 3408 const std::string op_b = GetVideoOperandB(instr);
3304 boost::optional<std::string> forced_result;
3305
3306 auto Unpack = [&](const std::string& op, bool is_chunk, bool is_signed,
3307 Tegra::Shader::VmadType type, u64 byte_height) {
3308 const std::string value = [&]() {
3309 if (!is_chunk) {
3310 const auto offset = static_cast<u32>(byte_height * 8);
3311 return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)";
3312 }
3313 const std::string zero = "0";
3314
3315 switch (type) {
3316 case Tegra::Shader::VmadType::Size16_Low:
3317 return '(' + op + " & 0xffff)";
3318 case Tegra::Shader::VmadType::Size16_High:
3319 return '(' + op + " >> 16)";
3320 case Tegra::Shader::VmadType::Size32:
3321 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
3322 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
3323 // explanation is found: assert.
3324 UNREACHABLE_MSG("Unimplemented");
3325 return zero;
3326 case Tegra::Shader::VmadType::Invalid:
3327 // Note(Rodrigo): This flag is invalid according to nvdisasm. From my
3328 // testing (even though it's invalid) this makes the whole instruction
3329 // assign zero to target register.
3330 forced_result = boost::make_optional(zero);
3331 return zero;
3332 default:
3333 UNREACHABLE();
3334 return zero;
3335 }
3336 }();
3337
3338 if (is_signed) {
3339 return "int(" + value + ')';
3340 }
3341 return value;
3342 };
3343
3344 const std::string op_a = Unpack(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
3345 instr.vmad.is_byte_chunk_a != 0, signed_a,
3346 instr.vmad.type_a, instr.vmad.byte_height_a);
3347
3348 std::string op_b;
3349 if (instr.vmad.use_register_b) {
3350 op_b = Unpack(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
3351 instr.vmad.is_byte_chunk_b != 0, signed_b, instr.vmad.type_b,
3352 instr.vmad.byte_height_b);
3353 } else {
3354 op_b = '(' +
3355 std::to_string(signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
3356 : instr.alu.GetImm20_16()) +
3357 ')';
3358 }
3359
3360 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);
3361 3410
3362 std::string result; 3411 std::string result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
3363 if (forced_result) {
3364 result = *forced_result;
3365 } else {
3366 result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
3367 3412
3368 switch (instr.vmad.shr) { 3413 switch (instr.vmad.shr) {
3369 case Tegra::Shader::VmadShr::Shr7: 3414 case Tegra::Shader::VmadShr::Shr7:
3370 result = '(' + result + " >> 7)"; 3415 result = '(' + result + " >> 7)";
3371 break; 3416 break;
3372 case Tegra::Shader::VmadShr::Shr15: 3417 case Tegra::Shader::VmadShr::Shr15:
3373 result = '(' + result + " >> 15)"; 3418 result = '(' + result + " >> 15)";
3374 break; 3419 break;
3375 }
3376 } 3420 }
3421
3377 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, 3422 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
3378 instr.vmad.saturate == 1, 0, Register::Size::Word, 3423 instr.vmad.saturate == 1, 0, Register::Size::Word,
3379 instr.vmad.cc); 3424 instr.vmad.cc);
3380 break; 3425 break;
3381 } 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 }
3382 default: { 3451 default: {
3383 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); 3452 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName());
3384 UNREACHABLE(); 3453 UNREACHABLE();
@@ -3442,11 +3511,11 @@ private:
3442 labels.insert(subroutine.begin); 3511 labels.insert(subroutine.begin);
3443 shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;"); 3512 shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;");
3444 3513
3445 // 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
3446 // unlikely that shaders will use 20 nested SSYs. 3515 // unlikely that shaders will use 20 nested SSYs and PBKs.
3447 constexpr u32 SSY_STACK_SIZE = 20; 3516 constexpr u32 FLOW_STACK_SIZE = 20;
3448 shader.AddLine("uint ssy_stack[" + std::to_string(SSY_STACK_SIZE) + "];"); 3517 shader.AddLine("uint flow_stack[" + std::to_string(FLOW_STACK_SIZE) + "];");
3449 shader.AddLine("uint ssy_stack_top = 0u;"); 3518 shader.AddLine("uint flow_stack_top = 0u;");
3450 3519
3451 shader.AddLine("while (true) {"); 3520 shader.AddLine("while (true) {");
3452 ++shader.scope; 3521 ++shader.scope;
@@ -3513,7 +3582,7 @@ private:
3513 3582
3514 // Declarations 3583 // Declarations
3515 std::set<std::string> declr_predicates; 3584 std::set<std::string> declr_predicates;
3516}; // namespace Decompiler 3585}; // namespace OpenGL::GLShader::Decompiler
3517 3586
3518std::string GetCommonDeclarations() { 3587std::string GetCommonDeclarations() {
3519 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 ecbc9d8ed..dfb562706 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -19,9 +19,6 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
19 out += Decompiler::GetCommonDeclarations(); 19 out += Decompiler::GetCommonDeclarations();
20 20
21 out += R"( 21 out += R"(
22out gl_PerVertex {
23 vec4 gl_Position;
24};
25 22
26layout (location = 0) out vec4 position; 23layout (location = 0) out vec4 position;
27 24
@@ -29,6 +26,7 @@ layout(std140) uniform vs_config {
29 vec4 viewport_flip; 26 vec4 viewport_flip;
30 uvec4 instance_id; 27 uvec4 instance_id;
31 uvec4 flip_stage; 28 uvec4 flip_stage;
29 uvec4 alpha_test;
32}; 30};
33)"; 31)";
34 32
@@ -105,6 +103,7 @@ layout (std140) uniform gs_config {
105 vec4 viewport_flip; 103 vec4 viewport_flip;
106 uvec4 instance_id; 104 uvec4 instance_id;
107 uvec4 flip_stage; 105 uvec4 flip_stage;
106 uvec4 alpha_test;
108}; 107};
109 108
110void main() { 109void main() {
@@ -142,8 +141,33 @@ layout (std140) uniform fs_config {
142 vec4 viewport_flip; 141 vec4 viewport_flip;
143 uvec4 instance_id; 142 uvec4 instance_id;
144 uvec4 flip_stage; 143 uvec4 flip_stage;
144 uvec4 alpha_test;
145}; 145};
146 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
147void main() { 171void main() {
148 exec_fragment(); 172 exec_fragment();
149} 173}
@@ -152,4 +176,4 @@ void main() {
152 out += program.first; 176 out += program.first;
153 return {out, program.second}; 177 return {out, program.second};
154} 178}
155} // namespace OpenGL::GLShader \ No newline at end of file 179} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 010857ec6..8b8869ecb 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -16,6 +16,17 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
16 viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; 16 viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
17 viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f; 17 viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f;
18 18
19 u32 func = static_cast<u32>(regs.alpha_test_func);
20 // Normalize the gl variants of opCompare to be the same as the normal variants
21 u32 op_gl_variant_base = static_cast<u32>(Tegra::Engines::Maxwell3D::Regs::ComparisonOp::Never);
22 if (func >= op_gl_variant_base) {
23 func = func - op_gl_variant_base + 1U;
24 }
25
26 alpha_test.enabled = regs.alpha_test_enabled;
27 alpha_test.func = func;
28 alpha_test.ref = regs.alpha_test_ref;
29
19 // We only assign the instance to the first component of the vector, the rest is just padding. 30 // We only assign the instance to the first component of the vector, the rest is just padding.
20 instance_id[0] = state.current_instance; 31 instance_id[0] = state.current_instance;
21 32
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index b3a191cf2..36fe1f04c 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -22,8 +22,14 @@ struct MaxwellUniformData {
22 alignas(16) GLvec4 viewport_flip; 22 alignas(16) GLvec4 viewport_flip;
23 alignas(16) GLuvec4 instance_id; 23 alignas(16) GLuvec4 instance_id;
24 alignas(16) GLuvec4 flip_stage; 24 alignas(16) GLuvec4 flip_stage;
25 struct alignas(16) {
26 GLuint enabled;
27 GLuint func;
28 GLfloat ref;
29 GLuint padding;
30 } alpha_test;
25}; 31};
26static_assert(sizeof(MaxwellUniformData) == 48, "MaxwellUniformData structure size is incorrect"); 32static_assert(sizeof(MaxwellUniformData) == 64, "MaxwellUniformData structure size is incorrect");
27static_assert(sizeof(MaxwellUniformData) < 16384, 33static_assert(sizeof(MaxwellUniformData) < 16384,
28 "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec"); 34 "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
29 35
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 3c3bcaae4..0f6dcab2b 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -82,8 +82,20 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
82 return {}; 82 return {};
83 } 83 }
84 84
85 case Maxwell::VertexAttribute::Type::Float: 85 case Maxwell::VertexAttribute::Type::Float: {
86 return GL_FLOAT; 86 switch (attrib.size) {
87 case Maxwell::VertexAttribute::Size::Size_16:
88 case Maxwell::VertexAttribute::Size::Size_16_16:
89 case Maxwell::VertexAttribute::Size::Size_16_16_16:
90 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
91 return GL_HALF_FLOAT;
92 case Maxwell::VertexAttribute::Size::Size_32:
93 case Maxwell::VertexAttribute::Size::Size_32_32:
94 case Maxwell::VertexAttribute::Size::Size_32_32_32:
95 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
96 return GL_FLOAT;
97 }
98 }
87 } 99 }
88 100
89 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); 101 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString());
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 56c61b60c..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;
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..0bc307e99 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -2,13 +2,30 @@
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
24static std::string GetImagePath(Service::Account::UUID uuid) {
25 return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
26 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
27}
28
12static const std::array<int, 12> days_in_month = {{ 29static const std::array<int, 12> days_in_month = {{
13 31, 30 31,
14 29, 31 29,
@@ -24,7 +41,20 @@ static const std::array<int, 12> days_in_month = {{
24 31, 41 31,
25}}; 42}};
26 43
27ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) { 44// Same backup JPEG used by acc IProfile::GetImage if no jpeg found
45static constexpr std::array<u8, 107> backup_jpeg{
46 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
47 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
48 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
49 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
50 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
51 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
52 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
53};
54
55ConfigureSystem::ConfigureSystem(QWidget* parent)
56 : QWidget(parent), ui(new Ui::ConfigureSystem),
57 profile_manager(std::make_unique<Service::Account::ProfileManager>()) {
28 ui->setupUi(this); 58 ui->setupUi(this);
29 connect(ui->combo_birthmonth, 59 connect(ui->combo_birthmonth,
30 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 60 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
@@ -32,6 +62,45 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
32 connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, 62 connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
33 &ConfigureSystem::refreshConsoleID); 63 &ConfigureSystem::refreshConsoleID);
34 64
65 layout = new QVBoxLayout;
66 tree_view = new QTreeView;
67 item_model = new QStandardItemModel(tree_view);
68 tree_view->setModel(item_model);
69
70 tree_view->setAlternatingRowColors(true);
71 tree_view->setSelectionMode(QHeaderView::SingleSelection);
72 tree_view->setSelectionBehavior(QHeaderView::SelectRows);
73 tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel);
74 tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel);
75 tree_view->setSortingEnabled(true);
76 tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
77 tree_view->setUniformRowHeights(true);
78 tree_view->setIconSize({64, 64});
79 tree_view->setContextMenuPolicy(Qt::NoContextMenu);
80
81 item_model->insertColumns(0, 1);
82 item_model->setHeaderData(0, Qt::Horizontal, "Users");
83
84 // We must register all custom types with the Qt Automoc system so that we are able to use it
85 // with signals/slots. In this case, QList falls under the umbrells of custom types.
86 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
87
88 layout->setContentsMargins(0, 0, 0, 0);
89 layout->setSpacing(0);
90 layout->addWidget(tree_view);
91
92 ui->scrollArea->setLayout(layout);
93
94 connect(tree_view, &QTreeView::clicked, this, &ConfigureSystem::SelectUser);
95
96 connect(ui->pm_add, &QPushButton::pressed, this, &ConfigureSystem::AddUser);
97 connect(ui->pm_rename, &QPushButton::pressed, this, &ConfigureSystem::RenameUser);
98 connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureSystem::DeleteUser);
99 connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureSystem::SetUserImage);
100
101 scene = new QGraphicsScene;
102 ui->current_user_icon->setScene(scene);
103
35 this->setConfiguration(); 104 this->setConfiguration();
36} 105}
37 106
@@ -39,16 +108,74 @@ ConfigureSystem::~ConfigureSystem() = default;
39 108
40void ConfigureSystem::setConfiguration() { 109void ConfigureSystem::setConfiguration() {
41 enabled = !Core::System::GetInstance().IsPoweredOn(); 110 enabled = !Core::System::GetInstance().IsPoweredOn();
42 ui->edit_username->setText(QString::fromStdString(Settings::values.username)); 111
43 ui->combo_language->setCurrentIndex(Settings::values.language_index); 112 ui->combo_language->setCurrentIndex(Settings::values.language_index);
113
114 item_model->removeRows(0, item_model->rowCount());
115 list_items.clear();
116
117 PopulateUserList();
118 UpdateCurrentUser();
119}
120
121static QPixmap GetIcon(Service::Account::UUID uuid) {
122 const auto icon_url = QString::fromStdString(GetImagePath(uuid));
123 QPixmap icon{icon_url};
124
125 if (!icon) {
126 icon.fill(Qt::black);
127 icon.loadFromData(backup_jpeg.data(), backup_jpeg.size());
128 }
129
130 return icon;
131}
132
133void ConfigureSystem::PopulateUserList() {
134 const auto& profiles = profile_manager->GetAllUsers();
135 for (const auto& user : profiles) {
136 Service::Account::ProfileBase profile;
137 if (!profile_manager->GetProfileBase(user, profile))
138 continue;
139
140 const auto username = Common::StringFromFixedZeroTerminatedBuffer(
141 reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
142
143 list_items.push_back(QList<QStandardItem*>{new QStandardItem{
144 GetIcon(user).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
145 QString::fromStdString(username + '\n' + user.FormatSwitch())}});
146 }
147
148 for (const auto& item : list_items)
149 item_model->appendRow(item);
150}
151
152void ConfigureSystem::UpdateCurrentUser() {
153 ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS);
154
155 const auto& current_user = profile_manager->GetUser(Settings::values.current_user);
156 ASSERT(current_user != std::nullopt);
157 const auto username = GetAccountUsername(*current_user);
158
159 scene->clear();
160 scene->addPixmap(
161 GetIcon(*current_user).scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
162 ui->current_user_username->setText(QString::fromStdString(username));
44} 163}
45 164
46void ConfigureSystem::ReadSystemSettings() {} 165void ConfigureSystem::ReadSystemSettings() {}
47 166
167std::string ConfigureSystem::GetAccountUsername(Service::Account::UUID uuid) const {
168 Service::Account::ProfileBase profile;
169 if (!profile_manager->GetProfileBase(uuid, profile))
170 return "";
171 return Common::StringFromFixedZeroTerminatedBuffer(
172 reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
173}
174
48void ConfigureSystem::applyConfiguration() { 175void ConfigureSystem::applyConfiguration() {
49 if (!enabled) 176 if (!enabled)
50 return; 177 return;
51 Settings::values.username = ui->edit_username->text().toStdString(); 178
52 Settings::values.language_index = ui->combo_language->currentIndex(); 179 Settings::values.language_index = ui->combo_language->currentIndex();
53 Settings::Apply(); 180 Settings::Apply();
54} 181}
@@ -92,3 +219,130 @@ void ConfigureSystem::refreshConsoleID() {
92 ui->label_console_id->setText( 219 ui->label_console_id->setText(
93 tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); 220 tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper()));
94} 221}
222
223void ConfigureSystem::SelectUser(const QModelIndex& index) {
224 Settings::values.current_user =
225 std::clamp<std::size_t>(index.row(), 0, profile_manager->GetUserCount() - 1);
226
227 UpdateCurrentUser();
228
229 ui->pm_remove->setEnabled(profile_manager->GetUserCount() >= 2);
230 ui->pm_rename->setEnabled(true);
231 ui->pm_set_image->setEnabled(true);
232}
233
234void ConfigureSystem::AddUser() {
235 Service::Account::UUID uuid;
236 uuid.Generate();
237
238 bool ok = false;
239 const auto username =
240 QInputDialog::getText(this, tr("Enter Username"), tr("Enter a username for the new user:"),
241 QLineEdit::Normal, QString(), &ok);
242 if (!ok)
243 return;
244
245 profile_manager->CreateNewUser(uuid, username.toStdString());
246
247 item_model->appendRow(new QStandardItem{
248 GetIcon(uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
249 QString::fromStdString(username.toStdString() + '\n' + uuid.FormatSwitch())});
250}
251
252void ConfigureSystem::RenameUser() {
253 const auto user = tree_view->currentIndex().row();
254 const auto uuid = profile_manager->GetUser(user);
255 ASSERT(uuid != std::nullopt);
256 const auto username = GetAccountUsername(*uuid);
257
258 Service::Account::ProfileBase profile;
259 if (!profile_manager->GetProfileBase(*uuid, profile))
260 return;
261
262 bool ok = false;
263 const auto new_username =
264 QInputDialog::getText(this, tr("Enter Username"), tr("Enter a new username:"),
265 QLineEdit::Normal, QString::fromStdString(username), &ok);
266
267 if (!ok)
268 return;
269
270 std::fill(profile.username.begin(), profile.username.end(), '\0');
271 const auto username_std = new_username.toStdString();
272 if (username_std.size() > profile.username.size()) {
273 std::copy_n(username_std.begin(), std::min(profile.username.size(), username_std.size()),
274 profile.username.begin());
275 } else {
276 std::copy(username_std.begin(), username_std.end(), profile.username.begin());
277 }
278
279 profile_manager->SetProfileBase(*uuid, profile);
280
281 item_model->setItem(
282 user, 0,
283 new QStandardItem{
284 GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
285 tr("%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. "
286 "00112233-4455-6677-8899-AABBCCDDEEFF))")
287 .arg(QString::fromStdString(username_std),
288 QString::fromStdString(uuid->FormatSwitch()))});
289 UpdateCurrentUser();
290}
291
292void ConfigureSystem::DeleteUser() {
293 const auto index = tree_view->currentIndex().row();
294 const auto uuid = profile_manager->GetUser(index);
295 ASSERT(uuid != std::nullopt);
296 const auto username = GetAccountUsername(*uuid);
297
298 const auto confirm =
299 QMessageBox::question(this, tr("Confirm Delete"),
300 tr("You are about to delete user with name %1. Are you sure?")
301 .arg(QString::fromStdString(username)));
302
303 if (confirm == QMessageBox::No)
304 return;
305
306 if (Settings::values.current_user == tree_view->currentIndex().row())
307 Settings::values.current_user = 0;
308 UpdateCurrentUser();
309
310 if (!profile_manager->RemoveUser(*uuid))
311 return;
312
313 item_model->removeRows(tree_view->currentIndex().row(), 1);
314 tree_view->clearSelection();
315
316 ui->pm_remove->setEnabled(false);
317 ui->pm_rename->setEnabled(false);
318}
319
320void ConfigureSystem::SetUserImage() {
321 const auto index = tree_view->currentIndex().row();
322 const auto uuid = profile_manager->GetUser(index);
323 ASSERT(uuid != std::nullopt);
324 const auto username = GetAccountUsername(*uuid);
325
326 const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(),
327 "JPEG Images (*.jpg *.jpeg)");
328
329 if (file.isEmpty())
330 return;
331
332 FileUtil::Delete(GetImagePath(*uuid));
333
334 const auto raw_path =
335 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010";
336 if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
337 FileUtil::Delete(raw_path);
338
339 FileUtil::CreateFullPath(GetImagePath(*uuid));
340 FileUtil::Copy(file.toStdString(), GetImagePath(*uuid));
341
342 item_model->setItem(
343 index, 0,
344 new QStandardItem{
345 GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
346 QString::fromStdString(username + '\n' + uuid->FormatSwitch())});
347 UpdateCurrentUser();
348}
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index f13de17d4..b73e0719c 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -5,8 +5,21 @@
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
12namespace Service::Account {
13class ProfileManager;
14struct UUID;
15} // namespace Service::Account
16
17class QGraphicsScene;
18class QStandardItem;
19class QStandardItemModel;
20class QTreeView;
21class QVBoxLayout;
22
10namespace Ui { 23namespace Ui {
11class ConfigureSystem; 24class ConfigureSystem;
12} 25}
@@ -21,18 +34,36 @@ public:
21 void applyConfiguration(); 34 void applyConfiguration();
22 void setConfiguration(); 35 void setConfiguration();
23 36
37 void PopulateUserList();
38 void UpdateCurrentUser();
39
24public slots: 40public slots:
25 void updateBirthdayComboBox(int birthmonth_index); 41 void updateBirthdayComboBox(int birthmonth_index);
26 void refreshConsoleID(); 42 void refreshConsoleID();
27 43
44 void SelectUser(const QModelIndex& index);
45 void AddUser();
46 void RenameUser();
47 void DeleteUser();
48 void SetUserImage();
49
28private: 50private:
29 void ReadSystemSettings(); 51 void ReadSystemSettings();
52 std::string GetAccountUsername(Service::Account::UUID uuid) const;
53
54 QVBoxLayout* layout;
55 QTreeView* tree_view;
56 QStandardItemModel* item_model;
57 QGraphicsScene* scene;
58
59 std::vector<QList<QStandardItem*>> list_items;
30 60
31 std::unique_ptr<Ui::ConfigureSystem> ui; 61 std::unique_ptr<Ui::ConfigureSystem> ui;
32 bool enabled; 62 bool enabled;
33 63
34 std::u16string username;
35 int birthmonth, birthday; 64 int birthmonth, birthday;
36 int language_index; 65 int language_index;
37 int sound_index; 66 int sound_index;
67
68 std::unique_ptr<Service::Account::ProfileManager> profile_manager;
38}; 69};
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..55508b1e1 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.
@@ -60,6 +61,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
60#include "core/hle/kernel/process.h" 61#include "core/hle/kernel/process.h"
61#include "core/hle/service/filesystem/filesystem.h" 62#include "core/hle/service/filesystem/filesystem.h"
62#include "core/hle/service/filesystem/fsp_ldr.h" 63#include "core/hle/service/filesystem/fsp_ldr.h"
64#include "core/hle/service/nfp/nfp.h"
65#include "core/hle/service/sm/sm.h"
63#include "core/loader/loader.h" 66#include "core/loader/loader.h"
64#include "core/perf_stats.h" 67#include "core/perf_stats.h"
65#include "core/settings.h" 68#include "core/settings.h"
@@ -100,6 +103,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
100} 103}
101#endif 104#endif
102 105
106constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
107
103/** 108/**
104 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there 109 * "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 110 * is a bitfield "callout_flags" options, used to track if a message has already been shown to the
@@ -422,6 +427,7 @@ void GMainWindow::ConnectMenuEvents() {
422 connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this, 427 connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this,
423 [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); }); 428 [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); });
424 connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); 429 connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
430 connect(ui.action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo);
425 431
426 // Emulation 432 // Emulation
427 connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); 433 connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame);
@@ -690,6 +696,7 @@ void GMainWindow::ShutdownGame() {
690 ui.action_Stop->setEnabled(false); 696 ui.action_Stop->setEnabled(false);
691 ui.action_Restart->setEnabled(false); 697 ui.action_Restart->setEnabled(false);
692 ui.action_Report_Compatibility->setEnabled(false); 698 ui.action_Report_Compatibility->setEnabled(false);
699 ui.action_Load_Amiibo->setEnabled(false);
693 render_window->hide(); 700 render_window->hide();
694 game_list->show(); 701 game_list->show();
695 game_list->setFilterFocus(); 702 game_list->setFilterFocus();
@@ -751,12 +758,43 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
751 open_target = "Save Data"; 758 open_target = "Save Data";
752 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); 759 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
753 ASSERT(program_id != 0); 760 ASSERT(program_id != 0);
754 // TODO(tech4me): Update this to work with arbitrary user profile 761
755 // Refer to core/hle/service/acc/profile_manager.cpp ProfileManager constructor 762 Service::Account::ProfileManager manager{};
756 constexpr u128 user_id = {1, 0}; 763 const auto user_ids = manager.GetAllUsers();
764 QStringList list;
765 for (const auto& user_id : user_ids) {
766 if (user_id == Service::Account::UUID{})
767 continue;
768 Service::Account::ProfileBase base;
769 if (!manager.GetProfileBase(user_id, base))
770 continue;
771
772 list.push_back(QString::fromStdString(Common::StringFromFixedZeroTerminatedBuffer(
773 reinterpret_cast<const char*>(base.username.data()), base.username.size())));
774 }
775
776 bool ok = false;
777 const auto index_string =
778 QInputDialog::getItem(this, tr("Select User"),
779 tr("Please select the user's save data you would like to open."),
780 list, Settings::values.current_user, false, &ok);
781 if (!ok)
782 return;
783
784 const auto index = list.indexOf(index_string);
785 ASSERT(index != -1 && index < 8);
786
787 const auto user_id = manager.GetUser(index);
788 ASSERT(user_id != std::nullopt);
757 path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, 789 path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser,
758 FileSys::SaveDataType::SaveData, 790 FileSys::SaveDataType::SaveData,
759 program_id, user_id, 0); 791 program_id, user_id->uuid, 0);
792
793 if (!FileUtil::Exists(path)) {
794 FileUtil::CreateFullPath(path);
795 FileUtil::CreateDir(path);
796 }
797
760 break; 798 break;
761 } 799 }
762 case GameListOpenTarget::ModData: { 800 case GameListOpenTarget::ModData: {
@@ -823,14 +861,10 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src
823} 861}
824 862
825void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { 863void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
826 const auto path = fmt::format("{}{:016X}/romfs", 864 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!"), 865 QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
831 tr("There was an error copying the RomFS files or the user " 866 tr("There was an error copying the RomFS files or the user "
832 "cancelled the operation.")); 867 "cancelled the operation."));
833 vfs->DeleteDirectory(path);
834 }; 868 };
835 869
836 const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read)); 870 const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read));
@@ -845,10 +879,24 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
845 return; 879 return;
846 } 880 }
847 881
848 const auto romfs = 882 const auto installed = Service::FileSystem::GetUnionContents();
849 loader->IsRomFSUpdatable() 883 auto romfs_title_id = SelectRomFSDumpTarget(*installed, program_id);
850 ? FileSys::PatchManager(program_id).PatchRomFS(file, loader->ReadRomFSIVFCOffset()) 884
851 : file; 885 if (!romfs_title_id) {
886 failed();
887 return;
888 }
889
890 const auto path = fmt::format(
891 "{}{:016X}/romfs", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), *romfs_title_id);
892
893 FileSys::VirtualFile romfs;
894
895 if (*romfs_title_id == program_id) {
896 romfs = file;
897 } else {
898 romfs = installed->GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS();
899 }
852 900
853 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); 901 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
854 if (extracted == nullptr) { 902 if (extracted == nullptr) {
@@ -860,6 +908,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
860 908
861 if (out == nullptr) { 909 if (out == nullptr) {
862 failed(); 910 failed();
911 vfs->DeleteDirectory(path);
863 return; 912 return;
864 } 913 }
865 914
@@ -870,8 +919,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 " 919 "files into the new directory while <br>skeleton will only create the directory "
871 "structure."), 920 "structure."),
872 {"Full", "Skeleton"}, 0, false, &ok); 921 {"Full", "Skeleton"}, 0, false, &ok);
873 if (!ok) 922 if (!ok) {
874 failed(); 923 failed();
924 vfs->DeleteDirectory(path);
925 return;
926 }
875 927
876 const auto full = res == "Full"; 928 const auto full = res == "Full";
877 const auto entry_size = CalculateRomFSEntrySize(extracted, full); 929 const auto entry_size = CalculateRomFSEntrySize(extracted, full);
@@ -888,6 +940,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
888 } else { 940 } else {
889 progress.close(); 941 progress.close();
890 failed(); 942 failed();
943 vfs->DeleteDirectory(path);
891 } 944 }
892} 945}
893 946
@@ -1174,6 +1227,7 @@ void GMainWindow::OnStartGame() {
1174 ui.action_Report_Compatibility->setEnabled(true); 1227 ui.action_Report_Compatibility->setEnabled(true);
1175 1228
1176 discord_rpc->Update(); 1229 discord_rpc->Update();
1230 ui.action_Load_Amiibo->setEnabled(true);
1177} 1231}
1178 1232
1179void GMainWindow::OnPauseGame() { 1233void GMainWindow::OnPauseGame() {
@@ -1278,6 +1332,27 @@ void GMainWindow::OnConfigure() {
1278 } 1332 }
1279} 1333}
1280 1334
1335void GMainWindow::OnLoadAmiibo() {
1336 const QString extensions{"*.bin"};
1337 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
1338 const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter);
1339 if (!filename.isEmpty()) {
1340 Core::System& system{Core::System::GetInstance()};
1341 Service::SM::ServiceManager& sm = system.ServiceManager();
1342 auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user");
1343 if (nfc != nullptr) {
1344 auto nfc_file = FileUtil::IOFile(filename.toStdString(), "rb");
1345 if (!nfc_file.IsOpen()) {
1346 return;
1347 }
1348 std::vector<u8> amiibo_buffer(nfc_file.GetSize());
1349 nfc_file.ReadBytes(amiibo_buffer.data(), amiibo_buffer.size());
1350 nfc_file.Close();
1351 nfc->LoadAmiibo(amiibo_buffer);
1352 }
1353 }
1354}
1355
1281void GMainWindow::OnAbout() { 1356void GMainWindow::OnAbout() {
1282 AboutDialog aboutDialog(this); 1357 AboutDialog aboutDialog(this);
1283 aboutDialog.exec(); 1358 aboutDialog.exec();
@@ -1318,15 +1393,17 @@ void GMainWindow::UpdateStatusBar() {
1318void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) { 1393void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) {
1319 QMessageBox::StandardButton answer; 1394 QMessageBox::StandardButton answer;
1320 QString status_message; 1395 QString status_message;
1321 const QString common_message = tr( 1396 const QString common_message =
1322 "The game you are trying to load requires additional files from your Switch to be dumped " 1397 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 " 1398 "dumped "
1324 "following wiki page: <a " 1399 "before playing.<br/><br/>For more information on dumping these files, please see the "
1325 "href='https://yuzu-emu.org/wiki/" 1400 "following wiki page: <a "
1326 "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System " 1401 "href='https://yuzu-emu.org/wiki/"
1327 "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit " 1402 "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 " 1403 "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to "
1329 "data, or other bugs."); 1404 "quit "
1405 "back to the game list? Continuing emulation may result in crashes, corrupted save "
1406 "data, or other bugs.");
1330 switch (result) { 1407 switch (result) {
1331 case Core::System::ResultStatus::ErrorSystemFiles: { 1408 case Core::System::ResultStatus::ErrorSystemFiles: {
1332 QString message = "yuzu was unable to locate a Switch system archive"; 1409 QString message = "yuzu was unable to locate a Switch system archive";
@@ -1357,9 +1434,12 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
1357 this, tr("Fatal Error"), 1434 this, tr("Fatal Error"),
1358 tr("yuzu has encountered a fatal error, please see the log for more details. " 1435 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: " 1436 "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 " 1437 "<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? " 1438 "to "
1362 "Continuing emulation may result in crashes, corrupted save data, or other bugs."), 1439 "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game "
1440 "list? "
1441 "Continuing emulation may result in crashes, corrupted save data, or other "
1442 "bugs."),
1363 QMessageBox::Yes | QMessageBox::No, QMessageBox::No); 1443 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
1364 status_message = "Fatal Error encountered"; 1444 status_message = "Fatal Error encountered";
1365 break; 1445 break;
@@ -1459,6 +1539,42 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1459 } 1539 }
1460} 1540}
1461 1541
1542boost::optional<u64> GMainWindow::SelectRomFSDumpTarget(
1543 const FileSys::RegisteredCacheUnion& installed, u64 program_id) {
1544 const auto dlc_entries =
1545 installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
1546 std::vector<FileSys::RegisteredCacheEntry> dlc_match;
1547 dlc_match.reserve(dlc_entries.size());
1548 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
1549 [&program_id, &installed](const FileSys::RegisteredCacheEntry& entry) {
1550 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id &&
1551 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
1552 });
1553
1554 std::vector<u64> romfs_tids;
1555 romfs_tids.push_back(program_id);
1556 for (const auto& entry : dlc_match)
1557 romfs_tids.push_back(entry.title_id);
1558
1559 if (romfs_tids.size() > 1) {
1560 QStringList list{"Base"};
1561 for (std::size_t i = 1; i < romfs_tids.size(); ++i)
1562 list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF));
1563
1564 bool ok;
1565 const auto res = QInputDialog::getItem(
1566 this, tr("Select RomFS Dump Target"),
1567 tr("Please select which RomFS you would like to dump."), list, 0, false, &ok);
1568 if (!ok) {
1569 return boost::none;
1570 }
1571
1572 return romfs_tids[list.indexOf(res)];
1573 }
1574
1575 return program_id;
1576}
1577
1462bool GMainWindow::ConfirmClose() { 1578bool GMainWindow::ConfirmClose() {
1463 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) 1579 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
1464 return true; 1580 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