summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt42
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/logging/backend.cpp3
-rw-r--r--src/common/logging/log.h2
-rw-r--r--src/common/memory_util.cpp177
-rw-r--r--src/common/memory_util.h21
-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.cpp176
-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.cpp147
-rw-r--r--src/core/hle/service/acc/profile_manager.h21
-rw-r--r--src/core/hle/service/am/am.cpp33
-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/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/perf_stats.cpp4
-rw-r--r--src/core/settings.h3
-rw-r--r--src/video_core/engines/maxwell_3d.cpp1
-rw-r--r--src/video_core/textures/decoders.cpp1
-rw-r--r--src/web_service/CMakeLists.txt2
-rw-r--r--src/yuzu/CMakeLists.txt8
-rw-r--r--src/yuzu/bootmanager.cpp6
-rw-r--r--src/yuzu/configuration/config.cpp11
-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/wait_tree.cpp4
-rw-r--r--src/yuzu/game_list.cpp10
-rw-r--r--src/yuzu/main.cpp168
-rw-r--r--src/yuzu/main.h7
-rw-r--r--src/yuzu/main.ui27
-rw-r--r--src/yuzu_cmd/config.cpp10
-rw-r--r--src/yuzu_cmd/default_ini.h4
48 files changed, 1418 insertions, 568 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6e98b557e..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
@@ -178,10 +178,6 @@ endif()
178set_property(DIRECTORY APPEND PROPERTY 178set_property(DIRECTORY APPEND PROPERTY
179 COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>) 179 COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
180 180
181
182math(EXPR EMU_ARCH_BITS ${CMAKE_SIZEOF_VOID_P}*8)
183add_definitions(-DEMU_ARCH_BITS=${EMU_ARCH_BITS})
184
185# System imported libraries 181# System imported libraries
186# ====================== 182# ======================
187 183
@@ -189,13 +185,13 @@ find_package(Boost 1.63.0 QUIET)
189if (NOT Boost_FOUND) 185if (NOT Boost_FOUND)
190 message(STATUS "Boost 1.63.0 or newer not found, falling back to externals") 186 message(STATUS "Boost 1.63.0 or newer not found, falling back to externals")
191 187
192 set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost") 188 set(BOOST_ROOT "${PROJECT_SOURCE_DIR}/externals/boost")
193 set(Boost_NO_SYSTEM_PATHS OFF) 189 set(Boost_NO_SYSTEM_PATHS OFF)
194 find_package(Boost QUIET REQUIRED) 190 find_package(Boost QUIET REQUIRED)
195endif() 191endif()
196 192
197# Output binaries to bin/ 193# Output binaries to bin/
198set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 194set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
199 195
200# Prefer the -pthread flag on Linux. 196# Prefer the -pthread flag on Linux.
201set(THREADS_PREFER_PTHREAD_FLAG ON) 197set(THREADS_PREFER_PTHREAD_FLAG ON)
@@ -264,7 +260,7 @@ if (YUZU_USE_BUNDLED_UNICORN)
264 endif() 260 endif()
265 261
266 set(UNICORN_FOUND YES) 262 set(UNICORN_FOUND YES)
267 set(UNICORN_PREFIX ${CMAKE_SOURCE_DIR}/externals/unicorn) 263 set(UNICORN_PREFIX ${PROJECT_SOURCE_DIR}/externals/unicorn)
268 set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/${UNICORN_LIB_NAME}" CACHE PATH "Path to Unicorn library" FORCE) 264 set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/${UNICORN_LIB_NAME}" CACHE PATH "Path to Unicorn library" FORCE)
269 set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE) 265 set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE)
270 set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/" CACHE PATH "Path to unicorn dynamic library" FORCE) 266 set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/" CACHE PATH "Path to unicorn dynamic library" FORCE)
@@ -356,12 +352,12 @@ set(CLANG_FORMAT_POSTFIX "-6.0")
356find_program(CLANG_FORMAT 352find_program(CLANG_FORMAT
357 NAMES clang-format${CLANG_FORMAT_POSTFIX} 353 NAMES clang-format${CLANG_FORMAT_POSTFIX}
358 clang-format 354 clang-format
359 PATHS ${CMAKE_BINARY_DIR}/externals) 355 PATHS ${PROJECT_BINARY_DIR}/externals)
360# if find_program doesn't find it, try to download from externals 356# if find_program doesn't find it, try to download from externals
361if (NOT CLANG_FORMAT) 357if (NOT CLANG_FORMAT)
362 if (WIN32) 358 if (WIN32)
363 message(STATUS "Clang format not found! Downloading...") 359 message(STATUS "Clang format not found! Downloading...")
364 set(CLANG_FORMAT "${CMAKE_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe") 360 set(CLANG_FORMAT "${PROJECT_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe")
365 file(DOWNLOAD 361 file(DOWNLOAD
366 https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe 362 https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe
367 "${CLANG_FORMAT}" SHOW_PROGRESS 363 "${CLANG_FORMAT}" SHOW_PROGRESS
@@ -377,7 +373,7 @@ if (NOT CLANG_FORMAT)
377endif() 373endif()
378 374
379if (CLANG_FORMAT) 375if (CLANG_FORMAT)
380 set(SRCS ${CMAKE_SOURCE_DIR}/src) 376 set(SRCS ${PROJECT_SOURCE_DIR}/src)
381 set(CCOMMENT "Running clang format against all the .h and .cpp files in src/") 377 set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
382 if (WIN32) 378 if (WIN32)
383 add_custom_target(clang-format 379 add_custom_target(clang-format
@@ -450,10 +446,10 @@ endif()
450# http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html 446# http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
451# http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html 447# http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
452if(ENABLE_QT AND UNIX AND NOT APPLE) 448if(ENABLE_QT AND UNIX AND NOT APPLE)
453 install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.desktop" 449 install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.desktop"
454 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications") 450 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications")
455 install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.svg" 451 install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.svg"
456 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps") 452 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps")
457 install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.xml" 453 install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.xml"
458 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages") 454 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages")
459endif() 455endif()
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index d0e506689..eccd8f64a 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -64,8 +64,6 @@ add_library(common STATIC
64 logging/text_formatter.cpp 64 logging/text_formatter.cpp
65 logging/text_formatter.h 65 logging/text_formatter.h
66 math_util.h 66 math_util.h
67 memory_util.cpp
68 memory_util.h
69 microprofile.cpp 67 microprofile.cpp
70 microprofile.h 68 microprofile.h
71 microprofileui.h 69 microprofileui.h
diff --git a/src/common/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 c9161155a..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
@@ -96,6 +97,7 @@ enum class Class : ClassType {
96 Service_SM, ///< The SM (Service manager) service 97 Service_SM, ///< The SM (Service manager) service
97 Service_SPL, ///< The SPL service 98 Service_SPL, ///< The SPL service
98 Service_SSL, ///< The SSL service 99 Service_SSL, ///< The SSL service
100 Service_TCAP, ///< The TCAP service.
99 Service_Time, ///< The time service 101 Service_Time, ///< The time service
100 Service_USB, ///< The USB (Universal Serial Bus) service 102 Service_USB, ///< The USB (Universal Serial Bus) service
101 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/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 690b84930..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,9 +690,8 @@ 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 }
@@ -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..06f7d1b15 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -4,32 +4,57 @@
4 4
5#include <random> 5#include <random>
6#include <boost/optional.hpp> 6#include <boost/optional.hpp>
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
@@ -101,6 +126,12 @@ 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
129boost::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
130 if (index >= MAX_USERS)
131 return boost::none;
132 return profiles[index].user_uuid;
133}
134
104/// Returns a users profile index based on their user id. 135/// Returns a users profile index based on their user id.
105boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { 136boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
106 if (!uuid) { 137 if (!uuid) {
@@ -164,6 +195,12 @@ bool ProfileManager::UserExists(UUID uuid) const {
164 return (GetUserIndex(uuid) != boost::none); 195 return (GetUserIndex(uuid) != boost::none);
165} 196}
166 197
198bool ProfileManager::UserExistsIndex(std::size_t index) const {
199 if (index >= MAX_USERS)
200 return false;
201 return profiles[index].user_uuid.uuid != INVALID_UUID;
202}
203
167/// Opens a specific user 204/// Opens a specific user
168void ProfileManager::OpenUser(UUID uuid) { 205void ProfileManager::OpenUser(UUID uuid) {
169 auto idx = GetUserIndex(uuid); 206 auto idx = GetUserIndex(uuid);
@@ -239,4 +276,96 @@ bool ProfileManager::CanSystemRegisterUser() const {
239 // emulate qlaunch. Update this to dynamically change. 276 // emulate qlaunch. Update this to dynamically change.
240} 277}
241 278
279bool ProfileManager::RemoveUser(UUID uuid) {
280 auto index = GetUserIndex(uuid);
281 if (index == boost::none) {
282 return false;
283 }
284
285 profiles[*index] = ProfileInfo{};
286 std::stable_partition(profiles.begin(), profiles.end(),
287 [](const ProfileInfo& profile) { return profile.user_uuid; });
288 return true;
289}
290
291bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
292 auto index = GetUserIndex(uuid);
293 if (profile_new.user_uuid == UUID(INVALID_UUID) || index == boost::none) {
294 return false;
295 }
296
297 auto& profile = profiles[*index];
298 profile.user_uuid = profile_new.user_uuid;
299 profile.username = profile_new.username;
300 profile.creation_time = profile_new.timestamp;
301
302 return true;
303}
304
305void ProfileManager::ParseUserSaveFile() {
306 FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
307 ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat",
308 "rb");
309
310 if (!save.IsOpen()) {
311 LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
312 "user 'yuzu' with random UUID.");
313 return;
314 }
315
316 ProfileDataRaw data;
317 if (save.ReadBytes(&data, sizeof(ProfileDataRaw)) != sizeof(ProfileDataRaw)) {
318 LOG_WARNING(Service_ACC, "profiles.dat is smaller than expected... Generating new user "
319 "'yuzu' with random UUID.");
320 return;
321 }
322
323 for (std::size_t i = 0; i < MAX_USERS; ++i) {
324 const auto& user = data.users[i];
325
326 if (user.uuid != UUID(INVALID_UUID))
327 AddUser({user.uuid, user.username, user.timestamp, {}, false});
328 }
329
330 std::stable_partition(profiles.begin(), profiles.end(),
331 [](const ProfileInfo& profile) { return profile.user_uuid; });
332}
333
334void ProfileManager::WriteUserSaveFile() {
335 ProfileDataRaw raw{};
336
337 for (std::size_t i = 0; i < MAX_USERS; ++i) {
338 raw.users[i].username = profiles[i].username;
339 raw.users[i].uuid2 = profiles[i].user_uuid;
340 raw.users[i].uuid = profiles[i].user_uuid;
341 raw.users[i].timestamp = profiles[i].creation_time;
342 }
343
344 const auto raw_path =
345 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010";
346 if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
347 FileUtil::Delete(raw_path);
348
349 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
350 ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
351
352 if (!FileUtil::CreateFullPath(path)) {
353 LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory "
354 "nand/system/save/8000000000000010/su/avators to mitigate this "
355 "issue.");
356 return;
357 }
358
359 FileUtil::IOFile save(path, "wb");
360
361 if (!save.IsOpen()) {
362 LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
363 "made in current session will be saved.");
364 return;
365 }
366
367 save.Resize(sizeof(ProfileDataRaw));
368 save.WriteBytes(&raw, sizeof(ProfileDataRaw));
369}
370
242}; // namespace Service::Account 371}; // 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..235208d56 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.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,12 +90,13 @@ 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);
99 boost::optional<UUID> GetUser(std::size_t index) const;
90 boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const; 100 boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
91 boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; 101 boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
92 bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const; 102 bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const;
@@ -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,7 +119,13 @@ 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);
124
111private: 125private:
126 void ParseUserSaveFile();
127 void WriteUserSaveFile();
128
112 std::array<ProfileInfo, MAX_USERS> profiles{}; 129 std::array<ProfileInfo, MAX_USERS> profiles{};
113 std::size_t user_count = 0; 130 std::size_t user_count = 0;
114 boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); 131 boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index ecf72ae24..4ed66d817 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -4,11 +4,13 @@
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"
@@ -26,6 +28,16 @@
26 28
27namespace Service::AM { 29namespace Service::AM {
28 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
29IWindowController::IWindowController() : ServiceFramework("IWindowController") { 41IWindowController::IWindowController() : ServiceFramework("IWindowController") {
30 // clang-format off 42 // clang-format off
31 static const FunctionInfo functions[] = { 43 static const FunctionInfo functions[] = {
@@ -724,20 +736,23 @@ void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx
724} 736}
725 737
726void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { 738void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
727 constexpr std::array<u8, 0x88> data{{ 739 LaunchParameters params{};
728 0xca, 0x97, 0x94, 0xc7, // Magic
729 1, 0, 0, 0, // IsAccountSelected (bool)
730 1, 0, 0, 0, // User Id (word 0)
731 0, 0, 0, 0, // User Id (word 1)
732 0, 0, 0, 0, // User Id (word 2)
733 0, 0, 0, 0 // User Id (word 3)
734 }};
735 740
736 std::vector<u8> buffer(data.begin(), data.end()); 741 params.magic = POP_LAUNCH_PARAMETER_MAGIC;
742 params.is_account_selected = 1;
743
744 Account::ProfileManager profile_manager{};
745 const auto uuid = profile_manager.GetUser(Settings::values.current_user);
746 ASSERT(uuid != boost::none);
747 params.current_user = uuid->uuid;
737 748
738 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 749 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
739 750
740 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
741 rb.PushIpcInterface<AM::IStorage>(buffer); 756 rb.PushIpcInterface<AM::IStorage>(buffer);
742 757
743 LOG_DEBUG(Service_AM, "called"); 758 LOG_DEBUG(Service_AM, "called");
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/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/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 bca014a4a..78ba29fc1 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -155,7 +155,6 @@ void Maxwell3D::ProcessQueryGet() {
155 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, 155 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
156 "Units other than CROP are unimplemented"); 156 "Units other than CROP are unimplemented");
157 157
158 u32 value = Memory::Read32(*address);
159 u64 result = 0; 158 u64 result = 0;
160 159
161 // TODO(Subv): Support the other query variables 160 // TODO(Subv): Support the other query variables
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index f1b40e7f5..da7989db9 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 9ad75e74a..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 PRIVATE -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 b901c29d2..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
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..1fe9a7edd 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"
@@ -122,7 +123,11 @@ void Config::ReadValues() {
122 123
123 qt_config->beginGroup("System"); 124 qt_config->beginGroup("System");
124 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); 125 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
125 Settings::values.username = qt_config->value("username", "yuzu").toString().toStdString(); 126 Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool();
127
128 Settings::values.current_user = std::clamp<int>(qt_config->value("current_user", 0).toInt(), 0,
129 Service::Account::MAX_USERS - 1);
130
126 Settings::values.language_index = qt_config->value("language_index", 1).toInt(); 131 Settings::values.language_index = qt_config->value("language_index", 1).toInt();
127 qt_config->endGroup(); 132 qt_config->endGroup();
128 133
@@ -258,7 +263,9 @@ void Config::SaveValues() {
258 263
259 qt_config->beginGroup("System"); 264 qt_config->beginGroup("System");
260 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); 265 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode);
261 qt_config->setValue("username", QString::fromStdString(Settings::values.username)); 266 qt_config->setValue("enable_nfc", Settings::values.enable_nfc);
267 qt_config->setValue("current_user", Settings::values.current_user);
268
262 qt_config->setValue("language_index", Settings::values.language_index); 269 qt_config->setValue("language_index", Settings::values.language_index);
263 qt_config->endGroup(); 270 qt_config->endGroup();
264 271
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..83cc49dfc 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 != boost::none);
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 != boost::none);
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 != boost::none);
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 != boost::none);
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/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 3a2685e24..a5a4aa432 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -216,11 +216,11 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
216 tree_view->setContextMenuPolicy(Qt::CustomContextMenu); 216 tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
217 217
218 item_model->insertColumns(0, COLUMN_COUNT); 218 item_model->insertColumns(0, COLUMN_COUNT);
219 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); 219 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
220 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility"); 220 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
221 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, "Add-ons"); 221 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
222 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); 222 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
223 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); 223 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
224 224
225 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); 225 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
226 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); 226 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index bef9df00d..47f494841 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 != boost::none);
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 dffd9c788..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">
@@ -117,11 +119,14 @@
117 <addaction name="menu_Tools" /> 119 <addaction name="menu_Tools" />
118 <addaction name="menu_Help"/> 120 <addaction name="menu_Help"/>
119 </widget> 121 </widget>
120 <action name="action_Install_File_NAND"> 122 <action name="action_Install_File_NAND">
121 <property name="text"> 123 <property name="enabled">
122 <string>Install File to NAND...</string> 124 <bool>true</bool>
123 </property> 125 </property>
124 </action> 126 <property name="text">
127 <string>Install File to NAND...</string>
128 </property>
129 </action>
125 <action name="action_Load_File"> 130 <action name="action_Load_File">
126 <property name="text"> 131 <property name="text">
127 <string>Load File...</string> 132 <string>Load File...</string>
@@ -253,6 +258,14 @@
253 <string>Restart</string> 258 <string>Restart</string>
254 </property> 259 </property>
255 </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>
256 <action name="action_Report_Compatibility"> 269 <action name="action_Report_Compatibility">
257 <property name="enabled"> 270 <property name="enabled">
258 <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