summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/file_util.cpp13
-rw-r--r--src/common/memory_util.cpp177
-rw-r--r--src/common/memory_util.h21
-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/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.cpp120
-rw-r--r--src/core/hle/kernel/thread.cpp2
-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/settings.h1
-rw-r--r--src/video_core/engines/maxwell_3d.cpp6
-rw-r--r--src/video_core/engines/maxwell_3d.h4
-rw-r--r--src/video_core/engines/maxwell_compute.cpp6
-rw-r--r--src/video_core/engines/maxwell_dma.cpp17
-rw-r--r--src/video_core/engines/shader_bytecode.h32
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp25
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h6
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp251
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp29
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp11
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h8
-rw-r--r--src/web_service/CMakeLists.txt2
-rw-r--r--src/yuzu/CMakeLists.txt8
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_general.cpp2
-rw-r--r--src/yuzu/configuration/configure_general.ui29
-rw-r--r--src/yuzu/debugger/wait_tree.cpp4
-rw-r--r--src/yuzu/main.cpp128
-rw-r--r--src/yuzu/main.h7
-rw-r--r--src/yuzu/main.ui27
-rw-r--r--src/yuzu_cmd/config.cpp1
-rw-r--r--src/yuzu_cmd/default_ini.h4
42 files changed, 849 insertions, 545 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index d0e506689..eccd8f64a 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -64,8 +64,6 @@ add_library(common STATIC
64 logging/text_formatter.cpp 64 logging/text_formatter.cpp
65 logging/text_formatter.h 65 logging/text_formatter.h
66 math_util.h 66 math_util.h
67 memory_util.cpp
68 memory_util.h
69 microprofile.cpp 67 microprofile.cpp
70 microprofile.h 68 microprofile.h
71 microprofileui.h 69 microprofileui.h
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 548463787..b52492da6 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -15,21 +15,24 @@
15#ifdef _WIN32 15#ifdef _WIN32
16#include <windows.h> 16#include <windows.h>
17// windows.h needs to be included before other windows headers 17// windows.h needs to be included before other windows headers
18#include <commdlg.h> // for GetSaveFileName 18#include <direct.h> // getcwd
19#include <direct.h> // getcwd
20#include <io.h> 19#include <io.h>
21#include <shellapi.h> 20#include <shellapi.h>
22#include <shlobj.h> // for SHGetFolderPath 21#include <shlobj.h> // for SHGetFolderPath
23#include <tchar.h> 22#include <tchar.h>
24#include "common/string_util.h" 23#include "common/string_util.h"
25 24
26// 64 bit offsets for windows 25#ifdef _MSC_VER
26// 64 bit offsets for MSVC
27#define fseeko _fseeki64 27#define fseeko _fseeki64
28#define ftello _ftelli64 28#define ftello _ftelli64
29#define atoll _atoi64 29#define fileno _fileno
30#endif
31
32// 64 bit offsets for MSVC and MinGW. MinGW also needs this for using _wstat64
30#define stat _stat64 33#define stat _stat64
31#define fstat _fstat64 34#define fstat _fstat64
32#define fileno _fileno 35
33#else 36#else
34#ifdef __APPLE__ 37#ifdef __APPLE__
35#include <sys/param.h> 38#include <sys/param.h>
diff --git a/src/common/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/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/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 61b9cfdc1..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}
@@ -545,13 +546,12 @@ static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
545static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { 546static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
546 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);
547 548
548 auto& kernel = Core::System::GetInstance().Kernel(); 549 const auto* current_process = Core::CurrentProcess();
549 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); 550 const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
550 if (!thread) { 551 if (!thread) {
551 return ERR_INVALID_HANDLE; 552 return ERR_INVALID_HANDLE;
552 } 553 }
553 554
554 const auto* current_process = Core::CurrentProcess();
555 if (thread->GetOwnerProcess() != current_process) { 555 if (thread->GetOwnerProcess() != current_process) {
556 return ERR_INVALID_HANDLE; 556 return ERR_INVALID_HANDLE;
557 } 557 }
@@ -577,10 +577,11 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
577 577
578/// Gets the priority for the specified thread 578/// Gets the priority for the specified thread
579static ResultCode GetThreadPriority(u32* priority, Handle handle) { 579static ResultCode GetThreadPriority(u32* priority, Handle handle) {
580 auto& kernel = Core::System::GetInstance().Kernel(); 580 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
581 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); 581 const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle);
582 if (!thread) 582 if (!thread) {
583 return ERR_INVALID_HANDLE; 583 return ERR_INVALID_HANDLE;
584 }
584 585
585 *priority = thread->GetPriority(); 586 *priority = thread->GetPriority();
586 return RESULT_SUCCESS; 587 return RESULT_SUCCESS;
@@ -592,14 +593,15 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
592 return ERR_INVALID_THREAD_PRIORITY; 593 return ERR_INVALID_THREAD_PRIORITY;
593 } 594 }
594 595
595 auto& kernel = Core::System::GetInstance().Kernel(); 596 const auto* const current_process = Core::CurrentProcess();
596 SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); 597 SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
597 if (!thread) 598 if (!thread) {
598 return ERR_INVALID_HANDLE; 599 return ERR_INVALID_HANDLE;
600 }
599 601
600 // 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
601 // the one from the thread owner's resource limit. 603 // the one from the thread owner's resource limit.
602 const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); 604 const ResourceLimit& resource_limit = current_process->GetResourceLimit();
603 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { 605 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
604 return ERR_NOT_AUTHORIZED; 606 return ERR_NOT_AUTHORIZED;
605 } 607 }
@@ -641,15 +643,13 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
641 return ERR_INVALID_MEMORY_PERMISSIONS; 643 return ERR_INVALID_MEMORY_PERMISSIONS;
642 } 644 }
643 645
644 auto& kernel = Core::System::GetInstance().Kernel(); 646 auto* const current_process = Core::CurrentProcess();
645 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); 647 auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
646 if (!shared_memory) { 648 if (!shared_memory) {
647 return ERR_INVALID_HANDLE; 649 return ERR_INVALID_HANDLE;
648 } 650 }
649 651
650 auto* const current_process = Core::CurrentProcess();
651 const auto& vm_manager = current_process->VMManager(); 652 const auto& vm_manager = current_process->VMManager();
652
653 if (!vm_manager.IsWithinASLRRegion(addr, size)) { 653 if (!vm_manager.IsWithinASLRRegion(addr, size)) {
654 return ERR_INVALID_MEMORY_RANGE; 654 return ERR_INVALID_MEMORY_RANGE;
655 } 655 }
@@ -673,15 +673,13 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
673 return ERR_INVALID_ADDRESS_STATE; 673 return ERR_INVALID_ADDRESS_STATE;
674 } 674 }
675 675
676 auto& kernel = Core::System::GetInstance().Kernel(); 676 auto* const current_process = Core::CurrentProcess();
677 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); 677 auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
678 if (!shared_memory) { 678 if (!shared_memory) {
679 return ERR_INVALID_HANDLE; 679 return ERR_INVALID_HANDLE;
680 } 680 }
681 681
682 auto* const current_process = Core::CurrentProcess();
683 const auto& vm_manager = current_process->VMManager(); 682 const auto& vm_manager = current_process->VMManager();
684
685 if (!vm_manager.IsWithinASLRRegion(addr, size)) { 683 if (!vm_manager.IsWithinASLRRegion(addr, size)) {
686 return ERR_INVALID_MEMORY_RANGE; 684 return ERR_INVALID_MEMORY_RANGE;
687 } 685 }
@@ -692,9 +690,8 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
692/// Query process memory 690/// Query process memory
693static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, 691static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/,
694 Handle process_handle, u64 addr) { 692 Handle process_handle, u64 addr) {
695 693 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
696 auto& kernel = Core::System::GetInstance().Kernel(); 694 SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
697 SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle);
698 if (!process) { 695 if (!process) {
699 return ERR_INVALID_HANDLE; 696 return ERR_INVALID_HANDLE;
700 } 697 }
@@ -741,20 +738,19 @@ static void ExitProcess() {
741/// Creates a new thread 738/// Creates a new thread
742static 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,
743 u32 priority, s32 processor_id) { 740 u32 priority, s32 processor_id) {
744 std::string name = fmt::format("thread-{:X}", entry_point);
745
746 if (priority > THREADPRIO_LOWEST) { 741 if (priority > THREADPRIO_LOWEST) {
747 return ERR_INVALID_THREAD_PRIORITY; 742 return ERR_INVALID_THREAD_PRIORITY;
748 } 743 }
749 744
750 const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); 745 auto* const current_process = Core::CurrentProcess();
746 const ResourceLimit& resource_limit = current_process->GetResourceLimit();
751 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { 747 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
752 return ERR_NOT_AUTHORIZED; 748 return ERR_NOT_AUTHORIZED;
753 } 749 }
754 750
755 if (processor_id == THREADPROCESSORID_DEFAULT) { 751 if (processor_id == THREADPROCESSORID_DEFAULT) {
756 // 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.
757 processor_id = Core::CurrentProcess()->GetDefaultProcessorID(); 753 processor_id = current_process->GetDefaultProcessorID();
758 ASSERT(processor_id != THREADPROCESSORID_DEFAULT); 754 ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
759 } 755 }
760 756
@@ -769,11 +765,13 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
769 return ERR_INVALID_PROCESSOR_ID; 765 return ERR_INVALID_PROCESSOR_ID;
770 } 766 }
771 767
768 const std::string name = fmt::format("thread-{:X}", entry_point);
772 auto& kernel = Core::System::GetInstance().Kernel(); 769 auto& kernel = Core::System::GetInstance().Kernel();
773 CASCADE_RESULT(SharedPtr<Thread> thread, 770 CASCADE_RESULT(SharedPtr<Thread> thread,
774 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,
775 *Core::CurrentProcess())); 772 *current_process));
776 const auto new_guest_handle = kernel.HandleTable().Create(thread); 773
774 const auto new_guest_handle = current_process->GetHandleTable().Create(thread);
777 if (new_guest_handle.Failed()) { 775 if (new_guest_handle.Failed()) {
778 return new_guest_handle.Code(); 776 return new_guest_handle.Code();
779 } 777 }
@@ -794,8 +792,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
794static ResultCode StartThread(Handle thread_handle) { 792static ResultCode StartThread(Handle thread_handle) {
795 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); 793 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
796 794
797 auto& kernel = Core::System::GetInstance().Kernel(); 795 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
798 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 796 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
799 if (!thread) { 797 if (!thread) {
800 return ERR_INVALID_HANDLE; 798 return ERR_INVALID_HANDLE;
801 } 799 }
@@ -842,8 +840,8 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
842 "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={}",
843 mutex_addr, condition_variable_addr, thread_handle, nano_seconds); 841 mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
844 842
845 auto& kernel = Core::System::GetInstance().Kernel(); 843 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
846 SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 844 SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
847 ASSERT(thread); 845 ASSERT(thread);
848 846
849 CASCADE_CODE(Mutex::Release(mutex_addr)); 847 CASCADE_CODE(Mutex::Release(mutex_addr));
@@ -954,9 +952,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
954 mutex_val | Mutex::MutexHasWaitersFlag)); 952 mutex_val | Mutex::MutexHasWaitersFlag));
955 953
956 // 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.
957 auto& kernel = Core::System::GetInstance().Kernel(); 955 const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
958 Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); 956 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
959 auto owner = kernel.HandleTable().Get<Thread>(owner_handle); 957 auto owner = handle_table.Get<Thread>(owner_handle);
960 ASSERT(owner); 958 ASSERT(owner);
961 ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); 959 ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
962 thread->InvalidateWakeupCallback(); 960 thread->InvalidateWakeupCallback();
@@ -1035,16 +1033,16 @@ static u64 GetSystemTick() {
1035static ResultCode CloseHandle(Handle handle) { 1033static ResultCode CloseHandle(Handle handle) {
1036 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); 1034 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
1037 1035
1038 auto& kernel = Core::System::GetInstance().Kernel(); 1036 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1039 return kernel.HandleTable().Close(handle); 1037 return handle_table.Close(handle);
1040} 1038}
1041 1039
1042/// Reset an event 1040/// Reset an event
1043static ResultCode ResetSignal(Handle handle) { 1041static ResultCode ResetSignal(Handle handle) {
1044 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); 1042 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle);
1045 1043
1046 auto& kernel = Core::System::GetInstance().Kernel(); 1044 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1047 auto event = kernel.HandleTable().Get<Event>(handle); 1045 auto event = handle_table.Get<Event>(handle);
1048 1046
1049 ASSERT(event != nullptr); 1047 ASSERT(event != nullptr);
1050 1048
@@ -1063,8 +1061,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
1063static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { 1061static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) {
1064 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); 1062 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
1065 1063
1066 auto& kernel = Core::System::GetInstance().Kernel(); 1064 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1067 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 1065 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1068 if (!thread) { 1066 if (!thread) {
1069 return ERR_INVALID_HANDLE; 1067 return ERR_INVALID_HANDLE;
1070 } 1068 }
@@ -1079,8 +1077,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
1079 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,
1080 mask, core); 1078 mask, core);
1081 1079
1082 auto& kernel = Core::System::GetInstance().Kernel(); 1080 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1083 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 1081 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1084 if (!thread) { 1082 if (!thread) {
1085 return ERR_INVALID_HANDLE; 1083 return ERR_INVALID_HANDLE;
1086 } 1084 }
@@ -1141,7 +1139,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
1141 } 1139 }
1142 1140
1143 auto& kernel = Core::System::GetInstance().Kernel(); 1141 auto& kernel = Core::System::GetInstance().Kernel();
1144 auto& handle_table = kernel.HandleTable(); 1142 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1145 auto shared_mem_handle = 1143 auto shared_mem_handle =
1146 SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, 1144 SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size,
1147 local_perms, remote_perms); 1145 local_perms, remote_perms);
@@ -1153,10 +1151,12 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
1153static ResultCode ClearEvent(Handle handle) { 1151static ResultCode ClearEvent(Handle handle) {
1154 LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); 1152 LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
1155 1153
1156 auto& kernel = Core::System::GetInstance().Kernel(); 1154 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1157 SharedPtr<Event> evt = kernel.HandleTable().Get<Event>(handle); 1155 SharedPtr<Event> evt = handle_table.Get<Event>(handle);
1158 if (evt == nullptr) 1156 if (evt == nullptr) {
1159 return ERR_INVALID_HANDLE; 1157 return ERR_INVALID_HANDLE;
1158 }
1159
1160 evt->Clear(); 1160 evt->Clear();
1161 return RESULT_SUCCESS; 1161 return RESULT_SUCCESS;
1162} 1162}
@@ -1169,8 +1169,8 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
1169 Status, 1169 Status,
1170 }; 1170 };
1171 1171
1172 const auto& kernel = Core::System::GetInstance().Kernel(); 1172 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1173 const auto process = kernel.HandleTable().Get<Process>(process_handle); 1173 const auto process = handle_table.Get<Process>(process_handle);
1174 if (!process) { 1174 if (!process) {
1175 return ERR_INVALID_HANDLE; 1175 return ERR_INVALID_HANDLE;
1176 } 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/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/settings.h b/src/core/settings.h
index 8f2da01c8..ca80718e2 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -113,6 +113,7 @@ 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 bool enable_nfc;
116 std::string username; 117 std::string username;
117 int language_index; 118 int language_index;
118 119
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 8afd26fe9..bca014a4a 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -13,8 +13,7 @@
13#include "video_core/renderer_base.h" 13#include "video_core/renderer_base.h"
14#include "video_core/textures/texture.h" 14#include "video_core/textures/texture.h"
15 15
16namespace Tegra { 16namespace Tegra::Engines {
17namespace Engines {
18 17
19/// First register id that is actually a Macro call. 18/// First register id that is actually a Macro call.
20constexpr u32 MacroRegistersStart = 0xE00; 19constexpr u32 MacroRegistersStart = 0xE00;
@@ -408,5 +407,4 @@ void Maxwell3D::ProcessClearBuffers() {
408 rasterizer.Clear(); 407 rasterizer.Clear();
409} 408}
410 409
411} // namespace Engines 410} // namespace Tegra::Engines
412} // namespace Tegra
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index c8af1c6b6..0e09a7ee5 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -643,8 +643,10 @@ public:
643 u32 d3d_cull_mode; 643 u32 d3d_cull_mode;
644 644
645 ComparisonOp depth_test_func; 645 ComparisonOp depth_test_func;
646 float alpha_test_ref;
647 ComparisonOp alpha_test_func;
646 648
647 INSERT_PADDING_WORDS(0xB); 649 INSERT_PADDING_WORDS(0x9);
648 650
649 struct { 651 struct {
650 u32 separate_alpha; 652 u32 separate_alpha;
diff --git a/src/video_core/engines/maxwell_compute.cpp b/src/video_core/engines/maxwell_compute.cpp
index 59e28b22d..8b5f08351 100644
--- a/src/video_core/engines/maxwell_compute.cpp
+++ b/src/video_core/engines/maxwell_compute.cpp
@@ -6,8 +6,7 @@
6#include "core/core.h" 6#include "core/core.h"
7#include "video_core/engines/maxwell_compute.h" 7#include "video_core/engines/maxwell_compute.h"
8 8
9namespace Tegra { 9namespace Tegra::Engines {
10namespace Engines {
11 10
12void MaxwellCompute::WriteReg(u32 method, u32 value) { 11void MaxwellCompute::WriteReg(u32 method, u32 value) {
13 ASSERT_MSG(method < Regs::NUM_REGS, 12 ASSERT_MSG(method < Regs::NUM_REGS,
@@ -26,5 +25,4 @@ void MaxwellCompute::WriteReg(u32 method, u32 value) {
26 } 25 }
27} 26}
28 27
29} // namespace Engines 28} // namespace Tegra::Engines
30} // namespace Tegra
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 103cd110e..b8a78cf82 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -7,8 +7,7 @@
7#include "video_core/rasterizer_interface.h" 7#include "video_core/rasterizer_interface.h"
8#include "video_core/textures/decoders.h" 8#include "video_core/textures/decoders.h"
9 9
10namespace Tegra { 10namespace Tegra::Engines {
11namespace Engines {
12 11
13MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) 12MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
14 : memory_manager(memory_manager), rasterizer{rasterizer} {} 13 : memory_manager(memory_manager), rasterizer{rasterizer} {}
@@ -78,9 +77,9 @@ void MaxwellDMA::HandleCopy() {
78 77
79 ASSERT(regs.exec.enable_2d == 1); 78 ASSERT(regs.exec.enable_2d == 1);
80 79
81 std::size_t copy_size = regs.x_count * regs.y_count; 80 const std::size_t copy_size = regs.x_count * regs.y_count;
82 81
83 const auto FlushAndInvalidate = [&](u32 src_size, u32 dst_size) { 82 const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) {
84 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated 83 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
85 // copying. 84 // copying.
86 rasterizer.FlushRegion(source_cpu, src_size); 85 rasterizer.FlushRegion(source_cpu, src_size);
@@ -91,14 +90,11 @@ void MaxwellDMA::HandleCopy() {
91 rasterizer.InvalidateRegion(dest_cpu, dst_size); 90 rasterizer.InvalidateRegion(dest_cpu, dst_size);
92 }; 91 };
93 92
94 u8* src_buffer = Memory::GetPointer(source_cpu);
95 u8* dst_buffer = Memory::GetPointer(dest_cpu);
96
97 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { 93 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
98 ASSERT(regs.src_params.size_z == 1); 94 ASSERT(regs.src_params.size_z == 1);
99 // If the input is tiled and the output is linear, deswizzle the input and copy it over. 95 // If the input is tiled and the output is linear, deswizzle the input and copy it over.
100 96
101 u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x; 97 const u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x;
102 98
103 FlushAndInvalidate(regs.src_pitch * regs.src_params.size_y, 99 FlushAndInvalidate(regs.src_pitch * regs.src_params.size_y,
104 copy_size * src_bytes_per_pixel); 100 copy_size * src_bytes_per_pixel);
@@ -111,7 +107,7 @@ void MaxwellDMA::HandleCopy() {
111 ASSERT(regs.dst_params.size_z == 1); 107 ASSERT(regs.dst_params.size_z == 1);
112 ASSERT(regs.src_pitch == regs.x_count); 108 ASSERT(regs.src_pitch == regs.x_count);
113 109
114 u32 src_bpp = regs.src_pitch / regs.x_count; 110 const u32 src_bpp = regs.src_pitch / regs.x_count;
115 111
116 FlushAndInvalidate(regs.src_pitch * regs.y_count, 112 FlushAndInvalidate(regs.src_pitch * regs.y_count,
117 regs.dst_params.size_x * regs.dst_params.size_y * src_bpp); 113 regs.dst_params.size_x * regs.dst_params.size_y * src_bpp);
@@ -122,5 +118,4 @@ void MaxwellDMA::HandleCopy() {
122 } 118 }
123} 119}
124 120
125} // namespace Engines 121} // namespace Tegra::Engines
126} // namespace Tegra
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 67501cf0a..6cd08d28b 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -214,7 +214,7 @@ enum class IMinMaxExchange : u64 {
214 XHi = 3, 214 XHi = 3,
215}; 215};
216 216
217enum class VmadType : u64 { 217enum class VideoType : u64 {
218 Size16_Low = 0, 218 Size16_Low = 0,
219 Size16_High = 1, 219 Size16_High = 1,
220 Size32 = 2, 220 Size32 = 2,
@@ -564,6 +564,10 @@ union Instruction {
564 } fmul; 564 } fmul;
565 565
566 union { 566 union {
567 BitField<55, 1, u64> saturate;
568 } fmul32;
569
570 union {
567 BitField<48, 1, u64> is_signed; 571 BitField<48, 1, u64> is_signed;
568 } shift; 572 } shift;
569 573
@@ -779,6 +783,14 @@ union Instruction {
779 } psetp; 783 } psetp;
780 784
781 union { 785 union {
786 BitField<43, 4, PredCondition> cond;
787 BitField<45, 2, PredOperation> op;
788 BitField<3, 3, u64> pred3;
789 BitField<0, 3, u64> pred0;
790 BitField<39, 3, u64> pred39;
791 } vsetp;
792
793 union {
782 BitField<12, 3, u64> pred12; 794 BitField<12, 3, u64> pred12;
783 BitField<15, 1, u64> neg_pred12; 795 BitField<15, 1, u64> neg_pred12;
784 BitField<24, 2, PredOperation> cond; 796 BitField<24, 2, PredOperation> cond;
@@ -1150,15 +1162,17 @@ union Instruction {
1150 union { 1162 union {
1151 BitField<48, 1, u64> signed_a; 1163 BitField<48, 1, u64> signed_a;
1152 BitField<38, 1, u64> is_byte_chunk_a; 1164 BitField<38, 1, u64> is_byte_chunk_a;
1153 BitField<36, 2, VmadType> type_a; 1165 BitField<36, 2, VideoType> type_a;
1154 BitField<36, 2, u64> byte_height_a; 1166 BitField<36, 2, u64> byte_height_a;
1155 1167
1156 BitField<49, 1, u64> signed_b; 1168 BitField<49, 1, u64> signed_b;
1157 BitField<50, 1, u64> use_register_b; 1169 BitField<50, 1, u64> use_register_b;
1158 BitField<30, 1, u64> is_byte_chunk_b; 1170 BitField<30, 1, u64> is_byte_chunk_b;
1159 BitField<28, 2, VmadType> type_b; 1171 BitField<28, 2, VideoType> type_b;
1160 BitField<28, 2, u64> byte_height_b; 1172 BitField<28, 2, u64> byte_height_b;
1173 } video;
1161 1174
1175 union {
1162 BitField<51, 2, VmadShr> shr; 1176 BitField<51, 2, VmadShr> shr;
1163 BitField<55, 1, u64> saturate; // Saturates the result (a * b + c) 1177 BitField<55, 1, u64> saturate; // Saturates the result (a * b + c)
1164 BitField<47, 1, u64> cc; 1178 BitField<47, 1, u64> cc;
@@ -1209,11 +1223,13 @@ public:
1209 KIL, 1223 KIL,
1210 SSY, 1224 SSY,
1211 SYNC, 1225 SYNC,
1226 BRK,
1212 DEPBAR, 1227 DEPBAR,
1213 BFE_C, 1228 BFE_C,
1214 BFE_R, 1229 BFE_R,
1215 BFE_IMM, 1230 BFE_IMM,
1216 BRA, 1231 BRA,
1232 PBK,
1217 LD_A, 1233 LD_A,
1218 LD_C, 1234 LD_C,
1219 ST_A, 1235 ST_A,
@@ -1232,6 +1248,7 @@ public:
1232 OUT_R, // Emit vertex/primitive 1248 OUT_R, // Emit vertex/primitive
1233 ISBERD, 1249 ISBERD,
1234 VMAD, 1250 VMAD,
1251 VSETP,
1235 FFMA_IMM, // Fused Multiply and Add 1252 FFMA_IMM, // Fused Multiply and Add
1236 FFMA_CR, 1253 FFMA_CR,
1237 FFMA_RC, 1254 FFMA_RC,
@@ -1370,7 +1387,7 @@ public:
1370 /// conditionally executed). 1387 /// conditionally executed).
1371 static bool IsPredicatedInstruction(Id opcode) { 1388 static bool IsPredicatedInstruction(Id opcode) {
1372 // TODO(Subv): Add the rest of unpredicated instructions. 1389 // TODO(Subv): Add the rest of unpredicated instructions.
1373 return opcode != Id::SSY; 1390 return opcode != Id::SSY && opcode != Id::PBK;
1374 } 1391 }
1375 1392
1376 class Matcher { 1393 class Matcher {
@@ -1466,9 +1483,11 @@ private:
1466#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name) 1483#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name)
1467 INST("111000110011----", Id::KIL, Type::Flow, "KIL"), 1484 INST("111000110011----", Id::KIL, Type::Flow, "KIL"),
1468 INST("111000101001----", Id::SSY, Type::Flow, "SSY"), 1485 INST("111000101001----", Id::SSY, Type::Flow, "SSY"),
1486 INST("111000101010----", Id::PBK, Type::Flow, "PBK"),
1469 INST("111000100100----", Id::BRA, Type::Flow, "BRA"), 1487 INST("111000100100----", Id::BRA, Type::Flow, "BRA"),
1488 INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"),
1489 INST("111000110100---", Id::BRK, Type::Flow, "BRK"),
1470 INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"), 1490 INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
1471 INST("1111000011111---", Id::SYNC, Type::Synch, "SYNC"),
1472 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), 1491 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
1473 INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"), 1492 INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"),
1474 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), 1493 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
@@ -1487,6 +1506,7 @@ private:
1487 INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"), 1506 INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
1488 INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"), 1507 INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
1489 INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"), 1508 INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"),
1509 INST("0101000011110---", Id::VSETP, Type::Trivial, "VSETP"),
1490 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), 1510 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
1491 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), 1511 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
1492 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), 1512 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
@@ -1606,4 +1626,4 @@ private:
1606 } 1626 }
1607}; 1627};
1608 1628
1609} // namespace Tegra::Shader 1629} // namespace Tegra::Shader \ No newline at end of file
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 3daccf82f..be51c5215 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -570,10 +570,11 @@ void RasterizerOpenGL::DrawArrays() {
570 SyncBlendState(); 570 SyncBlendState();
571 SyncLogicOpState(); 571 SyncLogicOpState();
572 SyncCullMode(); 572 SyncCullMode();
573 SyncAlphaTest();
574 SyncScissorTest(); 573 SyncScissorTest();
574 // Alpha Testing is synced on shaders.
575 SyncTransformFeedback(); 575 SyncTransformFeedback();
576 SyncPointState(); 576 SyncPointState();
577 CheckAlphaTests();
577 578
578 // TODO(bunnei): Sync framebuffer_scale uniform here 579 // TODO(bunnei): Sync framebuffer_scale uniform here
579 // TODO(bunnei): Sync scissorbox uniform(s) here 580 // TODO(bunnei): Sync scissorbox uniform(s) here
@@ -1007,17 +1008,6 @@ void RasterizerOpenGL::SyncLogicOpState() {
1007 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); 1008 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation);
1008} 1009}
1009 1010
1010void RasterizerOpenGL::SyncAlphaTest() {
1011 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1012
1013 // TODO(Rodrigo): Alpha testing is a legacy OpenGL feature, but it can be
1014 // implemented with a test+discard in fragment shaders.
1015 if (regs.alpha_test_enabled != 0) {
1016 LOG_CRITICAL(Render_OpenGL, "Alpha testing is not implemented");
1017 UNREACHABLE();
1018 }
1019}
1020
1021void RasterizerOpenGL::SyncScissorTest() { 1011void RasterizerOpenGL::SyncScissorTest() {
1022 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1012 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1023 1013
@@ -1052,4 +1042,15 @@ void RasterizerOpenGL::SyncPointState() {
1052 state.point.size = regs.point_size == 0 ? 1 : regs.point_size; 1042 state.point.size = regs.point_size == 0 ? 1 : regs.point_size;
1053} 1043}
1054 1044
1045void RasterizerOpenGL::CheckAlphaTests() {
1046 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1047
1048 if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) {
1049 LOG_CRITICAL(
1050 Render_OpenGL,
1051 "Alpha Testing is enabled with Multiple Render Targets, this behavior is undefined.");
1052 UNREACHABLE();
1053 }
1054}
1055
1055} // namespace OpenGL 1056} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index b1f7ccc7e..0e90a31f5 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -162,9 +162,6 @@ private:
162 /// Syncs the LogicOp state to match the guest state 162 /// Syncs the LogicOp state to match the guest state
163 void SyncLogicOpState(); 163 void SyncLogicOpState();
164 164
165 /// Syncs the alpha test state to match the guest state
166 void SyncAlphaTest();
167
168 /// Syncs the scissor test state to match the guest state 165 /// Syncs the scissor test state to match the guest state
169 void SyncScissorTest(); 166 void SyncScissorTest();
170 167
@@ -174,6 +171,9 @@ private:
174 /// Syncs the point state to match the guest state 171 /// Syncs the point state to match the guest state
175 void SyncPointState(); 172 void SyncPointState();
176 173
174 /// Check asserts for alpha testing.
175 void CheckAlphaTests();
176
177 bool has_ARB_direct_state_access = false; 177 bool has_ARB_direct_state_access = false;
178 bool has_ARB_multi_bind = false; 178 bool has_ARB_multi_bind = false;
179 bool has_ARB_separate_shader_objects = false; 179 bool has_ARB_separate_shader_objects = false;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 09db58ab6..fe4d1bd83 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -163,10 +163,11 @@ private:
163 const ExitMethod jmp = Scan(target, end, labels); 163 const ExitMethod jmp = Scan(target, end, labels);
164 return exit_method = ParallelExit(no_jmp, jmp); 164 return exit_method = ParallelExit(no_jmp, jmp);
165 } 165 }
166 case OpCode::Id::SSY: { 166 case OpCode::Id::SSY:
167 // The SSY instruction uses a similar encoding as the BRA instruction. 167 case OpCode::Id::PBK: {
168 // The SSY and PBK use a similar encoding as the BRA instruction.
168 ASSERT_MSG(instr.bra.constant_buffer == 0, 169 ASSERT_MSG(instr.bra.constant_buffer == 0,
169 "Constant buffer SSY is not supported"); 170 "Constant buffer branching is not supported");
170 const u32 target = offset + instr.bra.GetBranchTarget(); 171 const u32 target = offset + instr.bra.GetBranchTarget();
171 labels.insert(target); 172 labels.insert(target);
172 // Continue scanning for an exit method. 173 // Continue scanning for an exit method.
@@ -1233,27 +1234,27 @@ private:
1233 } 1234 }
1234 1235
1235 /* 1236 /*
1236 * Emits code to push the input target address to the SSY address stack, incrementing the stack 1237 * Emits code to push the input target address to the flow address stack, incrementing the stack
1237 * top. 1238 * top.
1238 */ 1239 */
1239 void EmitPushToSSYStack(u32 target) { 1240 void EmitPushToFlowStack(u32 target) {
1240 shader.AddLine('{'); 1241 shader.AddLine('{');
1241 ++shader.scope; 1242 ++shader.scope;
1242 shader.AddLine("ssy_stack[ssy_stack_top] = " + std::to_string(target) + "u;"); 1243 shader.AddLine("flow_stack[flow_stack_top] = " + std::to_string(target) + "u;");
1243 shader.AddLine("ssy_stack_top++;"); 1244 shader.AddLine("flow_stack_top++;");
1244 --shader.scope; 1245 --shader.scope;
1245 shader.AddLine('}'); 1246 shader.AddLine('}');
1246 } 1247 }
1247 1248
1248 /* 1249 /*
1249 * Emits code to pop an address from the SSY address stack, setting the jump address to the 1250 * Emits code to pop an address from the flow address stack, setting the jump address to the
1250 * popped address and decrementing the stack top. 1251 * popped address and decrementing the stack top.
1251 */ 1252 */
1252 void EmitPopFromSSYStack() { 1253 void EmitPopFromFlowStack() {
1253 shader.AddLine('{'); 1254 shader.AddLine('{');
1254 ++shader.scope; 1255 ++shader.scope;
1255 shader.AddLine("ssy_stack_top--;"); 1256 shader.AddLine("flow_stack_top--;");
1256 shader.AddLine("jmp_to = ssy_stack[ssy_stack_top];"); 1257 shader.AddLine("jmp_to = flow_stack[flow_stack_top];");
1257 shader.AddLine("break;"); 1258 shader.AddLine("break;");
1258 --shader.scope; 1259 --shader.scope;
1259 shader.AddLine('}'); 1260 shader.AddLine('}');
@@ -1265,9 +1266,29 @@ private:
1265 1266
1266 ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented"); 1267 ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented");
1267 1268
1269 shader.AddLine("if (alpha_test[0] != 0) {");
1270 ++shader.scope;
1271 // We start on the register containing the alpha value in the first RT.
1272 u32 current_reg = 3;
1273 for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets;
1274 ++render_target) {
1275 // TODO(Blinkhawk): verify the behavior of alpha testing on hardware when
1276 // multiple render targets are used.
1277 if (header.ps.IsColorComponentOutputEnabled(render_target, 0) ||
1278 header.ps.IsColorComponentOutputEnabled(render_target, 1) ||
1279 header.ps.IsColorComponentOutputEnabled(render_target, 2) ||
1280 header.ps.IsColorComponentOutputEnabled(render_target, 3)) {
1281 shader.AddLine(fmt::format("if (!AlphaFunc({})) discard;",
1282 regs.GetRegisterAsFloat(current_reg)));
1283 current_reg += 4;
1284 }
1285 }
1286 --shader.scope;
1287 shader.AddLine('}');
1288
1268 // Write the color outputs using the data in the shader registers, disabled 1289 // Write the color outputs using the data in the shader registers, disabled
1269 // rendertargets/components are skipped in the register assignment. 1290 // rendertargets/components are skipped in the register assignment.
1270 u32 current_reg = 0; 1291 current_reg = 0;
1271 for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets; 1292 for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets;
1272 ++render_target) { 1293 ++render_target) {
1273 // TODO(Subv): Figure out how dual-source blending is configured in the Switch. 1294 // TODO(Subv): Figure out how dual-source blending is configured in the Switch.
@@ -1291,6 +1312,63 @@ private:
1291 } 1312 }
1292 } 1313 }
1293 1314
1315 /// Unpacks a video instruction operand (e.g. VMAD).
1316 std::string GetVideoOperand(const std::string& op, bool is_chunk, bool is_signed,
1317 Tegra::Shader::VideoType type, u64 byte_height) {
1318 const std::string value = [&]() {
1319 if (!is_chunk) {
1320 const auto offset = static_cast<u32>(byte_height * 8);
1321 return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)";
1322 }
1323 const std::string zero = "0";
1324
1325 switch (type) {
1326 case Tegra::Shader::VideoType::Size16_Low:
1327 return '(' + op + " & 0xffff)";
1328 case Tegra::Shader::VideoType::Size16_High:
1329 return '(' + op + " >> 16)";
1330 case Tegra::Shader::VideoType::Size32:
1331 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
1332 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
1333 // explanation is found: assert.
1334 UNIMPLEMENTED();
1335 return zero;
1336 case Tegra::Shader::VideoType::Invalid:
1337 UNREACHABLE_MSG("Invalid instruction encoding");
1338 return zero;
1339 default:
1340 UNREACHABLE();
1341 return zero;
1342 }
1343 }();
1344
1345 if (is_signed) {
1346 return "int(" + value + ')';
1347 }
1348 return value;
1349 };
1350
1351 /// Gets the A operand for a video instruction.
1352 std::string GetVideoOperandA(Instruction instr) {
1353 return GetVideoOperand(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
1354 instr.video.is_byte_chunk_a != 0, instr.video.signed_a,
1355 instr.video.type_a, instr.video.byte_height_a);
1356 }
1357
1358 /// Gets the B operand for a video instruction.
1359 std::string GetVideoOperandB(Instruction instr) {
1360 if (instr.video.use_register_b) {
1361 return GetVideoOperand(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
1362 instr.video.is_byte_chunk_b != 0, instr.video.signed_b,
1363 instr.video.type_b, instr.video.byte_height_b);
1364 } else {
1365 return '(' +
1366 std::to_string(instr.video.signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
1367 : instr.alu.GetImm20_16()) +
1368 ')';
1369 }
1370 }
1371
1294 /** 1372 /**
1295 * Compiles a single instruction from Tegra to GLSL. 1373 * Compiles a single instruction from Tegra to GLSL.
1296 * @param offset the offset of the Tegra shader instruction. 1374 * @param offset the offset of the Tegra shader instruction.
@@ -1460,9 +1538,10 @@ private:
1460 break; 1538 break;
1461 } 1539 }
1462 case OpCode::Id::FMUL32_IMM: { 1540 case OpCode::Id::FMUL32_IMM: {
1463 regs.SetRegisterToFloat( 1541 regs.SetRegisterToFloat(instr.gpr0, 0,
1464 instr.gpr0, 0, 1542 regs.GetRegisterAsFloat(instr.gpr8) + " * " +
1465 regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1); 1543 GetImmediate32(instr),
1544 1, 1, instr.fmul32.saturate);
1466 break; 1545 break;
1467 } 1546 }
1468 case OpCode::Id::FADD32I: { 1547 case OpCode::Id::FADD32I: {
@@ -3264,16 +3343,32 @@ private:
3264 // The SSY opcode tells the GPU where to re-converge divergent execution paths, it 3343 // The SSY opcode tells the GPU where to re-converge divergent execution paths, it
3265 // sets the target of the jump that the SYNC instruction will make. The SSY opcode 3344 // sets the target of the jump that the SYNC instruction will make. The SSY opcode
3266 // has a similar structure to the BRA opcode. 3345 // has a similar structure to the BRA opcode.
3267 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer SSY is not supported"); 3346 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer flow is not supported");
3347
3348 const u32 target = offset + instr.bra.GetBranchTarget();
3349 EmitPushToFlowStack(target);
3350 break;
3351 }
3352 case OpCode::Id::PBK: {
3353 // PBK pushes to a stack the address where BRK will jump to. This shares stack with
3354 // SSY but using SYNC on a PBK address will kill the shader execution. We don't
3355 // emulate this because it's very unlikely a driver will emit such invalid shader.
3356 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer PBK is not supported");
3268 3357
3269 const u32 target = offset + instr.bra.GetBranchTarget(); 3358 const u32 target = offset + instr.bra.GetBranchTarget();
3270 EmitPushToSSYStack(target); 3359 EmitPushToFlowStack(target);
3271 break; 3360 break;
3272 } 3361 }
3273 case OpCode::Id::SYNC: { 3362 case OpCode::Id::SYNC: {
3274 // The SYNC opcode jumps to the address previously set by the SSY opcode 3363 // The SYNC opcode jumps to the address previously set by the SSY opcode
3275 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); 3364 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
3276 EmitPopFromSSYStack(); 3365 EmitPopFromFlowStack();
3366 break;
3367 }
3368 case OpCode::Id::BRK: {
3369 // The BRK opcode jumps to the address previously set by the PBK opcode
3370 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
3371 EmitPopFromFlowStack();
3277 break; 3372 break;
3278 } 3373 }
3279 case OpCode::Id::DEPBAR: { 3374 case OpCode::Id::DEPBAR: {
@@ -3283,87 +3378,51 @@ private:
3283 break; 3378 break;
3284 } 3379 }
3285 case OpCode::Id::VMAD: { 3380 case OpCode::Id::VMAD: {
3286 const bool signed_a = instr.vmad.signed_a == 1; 3381 const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1;
3287 const bool signed_b = instr.vmad.signed_b == 1; 3382 const std::string op_a = GetVideoOperandA(instr);
3288 const bool result_signed = signed_a || signed_b; 3383 const std::string op_b = GetVideoOperandB(instr);
3289 boost::optional<std::string> forced_result;
3290
3291 auto Unpack = [&](const std::string& op, bool is_chunk, bool is_signed,
3292 Tegra::Shader::VmadType type, u64 byte_height) {
3293 const std::string value = [&]() {
3294 if (!is_chunk) {
3295 const auto shift = static_cast<u32>(byte_height * 8);
3296 return "((" + op + " >> " + std::to_string(shift) + ") & 0xff)";
3297 }
3298 const std::string zero = "0";
3299
3300 switch (type) {
3301 case Tegra::Shader::VmadType::Size16_Low:
3302 return '(' + op + " & 0xffff)";
3303 case Tegra::Shader::VmadType::Size16_High:
3304 return '(' + op + " >> 16)";
3305 case Tegra::Shader::VmadType::Size32:
3306 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
3307 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
3308 // explanation is found: assert.
3309 UNREACHABLE_MSG("Unimplemented");
3310 return zero;
3311 case Tegra::Shader::VmadType::Invalid:
3312 // Note(Rodrigo): This flag is invalid according to nvdisasm. From my
3313 // testing (even though it's invalid) this makes the whole instruction
3314 // assign zero to target register.
3315 forced_result = boost::make_optional(zero);
3316 return zero;
3317 default:
3318 UNREACHABLE();
3319 return zero;
3320 }
3321 }();
3322
3323 if (is_signed) {
3324 return "int(" + value + ')';
3325 }
3326 return value;
3327 };
3328
3329 const std::string op_a = Unpack(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
3330 instr.vmad.is_byte_chunk_a != 0, signed_a,
3331 instr.vmad.type_a, instr.vmad.byte_height_a);
3332
3333 std::string op_b;
3334 if (instr.vmad.use_register_b) {
3335 op_b = Unpack(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
3336 instr.vmad.is_byte_chunk_b != 0, signed_b, instr.vmad.type_b,
3337 instr.vmad.byte_height_b);
3338 } else {
3339 op_b = '(' +
3340 std::to_string(signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
3341 : instr.alu.GetImm20_16()) +
3342 ')';
3343 }
3344
3345 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed); 3384 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed);
3346 3385
3347 std::string result; 3386 std::string result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
3348 if (forced_result) {
3349 result = *forced_result;
3350 } else {
3351 result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
3352 3387
3353 switch (instr.vmad.shr) { 3388 switch (instr.vmad.shr) {
3354 case Tegra::Shader::VmadShr::Shr7: 3389 case Tegra::Shader::VmadShr::Shr7:
3355 result = '(' + result + " >> 7)"; 3390 result = '(' + result + " >> 7)";
3356 break; 3391 break;
3357 case Tegra::Shader::VmadShr::Shr15: 3392 case Tegra::Shader::VmadShr::Shr15:
3358 result = '(' + result + " >> 15)"; 3393 result = '(' + result + " >> 15)";
3359 break; 3394 break;
3360 }
3361 } 3395 }
3396
3362 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, 3397 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
3363 instr.vmad.saturate == 1, 0, Register::Size::Word, 3398 instr.vmad.saturate == 1, 0, Register::Size::Word,
3364 instr.vmad.cc); 3399 instr.vmad.cc);
3365 break; 3400 break;
3366 } 3401 }
3402 case OpCode::Id::VSETP: {
3403 const std::string op_a = GetVideoOperandA(instr);
3404 const std::string op_b = GetVideoOperandB(instr);
3405
3406 // We can't use the constant predicate as destination.
3407 ASSERT(instr.vsetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
3408
3409 const std::string second_pred = GetPredicateCondition(instr.vsetp.pred39, false);
3410
3411 const std::string combiner = GetPredicateCombiner(instr.vsetp.op);
3412
3413 const std::string predicate = GetPredicateComparison(instr.vsetp.cond, op_a, op_b);
3414 // Set the primary predicate to the result of Predicate OP SecondPredicate
3415 SetPredicate(instr.vsetp.pred3,
3416 '(' + predicate + ") " + combiner + " (" + second_pred + ')');
3417
3418 if (instr.vsetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
3419 // Set the secondary predicate to the result of !Predicate OP SecondPredicate,
3420 // if enabled
3421 SetPredicate(instr.vsetp.pred0,
3422 "!(" + predicate + ") " + combiner + " (" + second_pred + ')');
3423 }
3424 break;
3425 }
3367 default: { 3426 default: {
3368 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); 3427 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName());
3369 UNREACHABLE(); 3428 UNREACHABLE();
@@ -3427,11 +3486,11 @@ private:
3427 labels.insert(subroutine.begin); 3486 labels.insert(subroutine.begin);
3428 shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;"); 3487 shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;");
3429 3488
3430 // TODO(Subv): Figure out the actual depth of the SSY stack, for now it seems 3489 // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
3431 // unlikely that shaders will use 20 nested SSYs. 3490 // unlikely that shaders will use 20 nested SSYs and PBKs.
3432 constexpr u32 SSY_STACK_SIZE = 20; 3491 constexpr u32 FLOW_STACK_SIZE = 20;
3433 shader.AddLine("uint ssy_stack[" + std::to_string(SSY_STACK_SIZE) + "];"); 3492 shader.AddLine("uint flow_stack[" + std::to_string(FLOW_STACK_SIZE) + "];");
3434 shader.AddLine("uint ssy_stack_top = 0u;"); 3493 shader.AddLine("uint flow_stack_top = 0u;");
3435 3494
3436 shader.AddLine("while (true) {"); 3495 shader.AddLine("while (true) {");
3437 ++shader.scope; 3496 ++shader.scope;
@@ -3498,7 +3557,7 @@ private:
3498 3557
3499 // Declarations 3558 // Declarations
3500 std::set<std::string> declr_predicates; 3559 std::set<std::string> declr_predicates;
3501}; // namespace Decompiler 3560}; // namespace OpenGL::GLShader::Decompiler
3502 3561
3503std::string GetCommonDeclarations() { 3562std::string GetCommonDeclarations() {
3504 return fmt::format("#define MAX_CONSTBUFFER_ELEMENTS {}\n", 3563 return fmt::format("#define MAX_CONSTBUFFER_ELEMENTS {}\n",
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index ecbc9d8ed..e883ffb1d 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -29,6 +29,7 @@ layout(std140) uniform vs_config {
29 vec4 viewport_flip; 29 vec4 viewport_flip;
30 uvec4 instance_id; 30 uvec4 instance_id;
31 uvec4 flip_stage; 31 uvec4 flip_stage;
32 uvec4 alpha_test;
32}; 33};
33)"; 34)";
34 35
@@ -105,6 +106,7 @@ layout (std140) uniform gs_config {
105 vec4 viewport_flip; 106 vec4 viewport_flip;
106 uvec4 instance_id; 107 uvec4 instance_id;
107 uvec4 flip_stage; 108 uvec4 flip_stage;
109 uvec4 alpha_test;
108}; 110};
109 111
110void main() { 112void main() {
@@ -142,8 +144,33 @@ layout (std140) uniform fs_config {
142 vec4 viewport_flip; 144 vec4 viewport_flip;
143 uvec4 instance_id; 145 uvec4 instance_id;
144 uvec4 flip_stage; 146 uvec4 flip_stage;
147 uvec4 alpha_test;
145}; 148};
146 149
150bool AlphaFunc(in float value) {
151 float ref = uintBitsToFloat(alpha_test[2]);
152 switch (alpha_test[1]) {
153 case 1:
154 return false;
155 case 2:
156 return value < ref;
157 case 3:
158 return value == ref;
159 case 4:
160 return value <= ref;
161 case 5:
162 return value > ref;
163 case 6:
164 return value != ref;
165 case 7:
166 return value >= ref;
167 case 8:
168 return true;
169 default:
170 return false;
171 }
172}
173
147void main() { 174void main() {
148 exec_fragment(); 175 exec_fragment();
149} 176}
@@ -152,4 +179,4 @@ void main() {
152 out += program.first; 179 out += program.first;
153 return {out, program.second}; 180 return {out, program.second};
154} 181}
155} // namespace OpenGL::GLShader \ No newline at end of file 182} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 010857ec6..8b8869ecb 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -16,6 +16,17 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
16 viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; 16 viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
17 viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f; 17 viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f;
18 18
19 u32 func = static_cast<u32>(regs.alpha_test_func);
20 // Normalize the gl variants of opCompare to be the same as the normal variants
21 u32 op_gl_variant_base = static_cast<u32>(Tegra::Engines::Maxwell3D::Regs::ComparisonOp::Never);
22 if (func >= op_gl_variant_base) {
23 func = func - op_gl_variant_base + 1U;
24 }
25
26 alpha_test.enabled = regs.alpha_test_enabled;
27 alpha_test.func = func;
28 alpha_test.ref = regs.alpha_test_ref;
29
19 // We only assign the instance to the first component of the vector, the rest is just padding. 30 // We only assign the instance to the first component of the vector, the rest is just padding.
20 instance_id[0] = state.current_instance; 31 instance_id[0] = state.current_instance;
21 32
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index b3a191cf2..36fe1f04c 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -22,8 +22,14 @@ struct MaxwellUniformData {
22 alignas(16) GLvec4 viewport_flip; 22 alignas(16) GLvec4 viewport_flip;
23 alignas(16) GLuvec4 instance_id; 23 alignas(16) GLuvec4 instance_id;
24 alignas(16) GLuvec4 flip_stage; 24 alignas(16) GLuvec4 flip_stage;
25 struct alignas(16) {
26 GLuint enabled;
27 GLuint func;
28 GLfloat ref;
29 GLuint padding;
30 } alpha_test;
25}; 31};
26static_assert(sizeof(MaxwellUniformData) == 48, "MaxwellUniformData structure size is incorrect"); 32static_assert(sizeof(MaxwellUniformData) == 64, "MaxwellUniformData structure size is incorrect");
27static_assert(sizeof(MaxwellUniformData) < 16384, 33static_assert(sizeof(MaxwellUniformData) < 16384,
28 "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec"); 34 "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
29 35
diff --git a/src/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/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 71c6ebb41..d029590ff 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -122,6 +122,7 @@ void Config::ReadValues() {
122 122
123 qt_config->beginGroup("System"); 123 qt_config->beginGroup("System");
124 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); 124 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
125 Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool();
125 Settings::values.username = qt_config->value("username", "yuzu").toString().toStdString(); 126 Settings::values.username = qt_config->value("username", "yuzu").toString().toStdString();
126 Settings::values.language_index = qt_config->value("language_index", 1).toInt(); 127 Settings::values.language_index = qt_config->value("language_index", 1).toInt();
127 qt_config->endGroup(); 128 qt_config->endGroup();
@@ -258,6 +259,7 @@ void Config::SaveValues() {
258 259
259 qt_config->beginGroup("System"); 260 qt_config->beginGroup("System");
260 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); 261 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode);
262 qt_config->setValue("enable_nfc", Settings::values.enable_nfc);
261 qt_config->setValue("username", QString::fromStdString(Settings::values.username)); 263 qt_config->setValue("username", QString::fromStdString(Settings::values.username));
262 qt_config->setValue("language_index", Settings::values.language_index); 264 qt_config->setValue("language_index", Settings::values.language_index);
263 qt_config->endGroup(); 265 qt_config->endGroup();
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/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 7403e9ccd..0c831c9f4 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -9,8 +9,8 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
11#include "core/hle/kernel/handle_table.h" 11#include "core/hle/kernel/handle_table.h"
12#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/mutex.h" 12#include "core/hle/kernel/mutex.h"
13#include "core/hle/kernel/process.h"
14#include "core/hle/kernel/scheduler.h" 14#include "core/hle/kernel/scheduler.h"
15#include "core/hle/kernel/thread.h" 15#include "core/hle/kernel/thread.h"
16#include "core/hle/kernel/timer.h" 16#include "core/hle/kernel/timer.h"
@@ -83,7 +83,7 @@ QString WaitTreeText::GetText() const {
83} 83}
84 84
85WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address) : mutex_address(mutex_address) { 85WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address) : mutex_address(mutex_address) {
86 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); 86 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
87 87
88 mutex_value = Memory::Read32(mutex_address); 88 mutex_value = Memory::Read32(mutex_address);
89 owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask); 89 owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index bef9df00d..be9896614 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -60,6 +60,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
60#include "core/hle/kernel/process.h" 60#include "core/hle/kernel/process.h"
61#include "core/hle/service/filesystem/filesystem.h" 61#include "core/hle/service/filesystem/filesystem.h"
62#include "core/hle/service/filesystem/fsp_ldr.h" 62#include "core/hle/service/filesystem/fsp_ldr.h"
63#include "core/hle/service/nfp/nfp.h"
64#include "core/hle/service/sm/sm.h"
63#include "core/loader/loader.h" 65#include "core/loader/loader.h"
64#include "core/perf_stats.h" 66#include "core/perf_stats.h"
65#include "core/settings.h" 67#include "core/settings.h"
@@ -100,6 +102,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
100} 102}
101#endif 103#endif
102 104
105constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
106
103/** 107/**
104 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there 108 * "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 109 * is a bitfield "callout_flags" options, used to track if a message has already been shown to the
@@ -422,6 +426,7 @@ void GMainWindow::ConnectMenuEvents() {
422 connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this, 426 connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this,
423 [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); }); 427 [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); });
424 connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); 428 connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
429 connect(ui.action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo);
425 430
426 // Emulation 431 // Emulation
427 connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); 432 connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame);
@@ -690,6 +695,7 @@ void GMainWindow::ShutdownGame() {
690 ui.action_Stop->setEnabled(false); 695 ui.action_Stop->setEnabled(false);
691 ui.action_Restart->setEnabled(false); 696 ui.action_Restart->setEnabled(false);
692 ui.action_Report_Compatibility->setEnabled(false); 697 ui.action_Report_Compatibility->setEnabled(false);
698 ui.action_Load_Amiibo->setEnabled(false);
693 render_window->hide(); 699 render_window->hide();
694 game_list->show(); 700 game_list->show();
695 game_list->setFilterFocus(); 701 game_list->setFilterFocus();
@@ -823,14 +829,10 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src
823} 829}
824 830
825void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { 831void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
826 const auto path = fmt::format("{}{:016X}/romfs", 832 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!"), 833 QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
831 tr("There was an error copying the RomFS files or the user " 834 tr("There was an error copying the RomFS files or the user "
832 "cancelled the operation.")); 835 "cancelled the operation."));
833 vfs->DeleteDirectory(path);
834 }; 836 };
835 837
836 const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read)); 838 const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read));
@@ -845,10 +847,24 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
845 return; 847 return;
846 } 848 }
847 849
848 const auto romfs = 850 const auto installed = Service::FileSystem::GetUnionContents();
849 loader->IsRomFSUpdatable() 851 auto romfs_title_id = SelectRomFSDumpTarget(*installed, program_id);
850 ? FileSys::PatchManager(program_id).PatchRomFS(file, loader->ReadRomFSIVFCOffset()) 852
851 : file; 853 if (!romfs_title_id) {
854 failed();
855 return;
856 }
857
858 const auto path = fmt::format(
859 "{}{:016X}/romfs", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), *romfs_title_id);
860
861 FileSys::VirtualFile romfs;
862
863 if (*romfs_title_id == program_id) {
864 romfs = file;
865 } else {
866 romfs = installed->GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS();
867 }
852 868
853 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); 869 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
854 if (extracted == nullptr) { 870 if (extracted == nullptr) {
@@ -860,6 +876,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
860 876
861 if (out == nullptr) { 877 if (out == nullptr) {
862 failed(); 878 failed();
879 vfs->DeleteDirectory(path);
863 return; 880 return;
864 } 881 }
865 882
@@ -870,8 +887,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 " 887 "files into the new directory while <br>skeleton will only create the directory "
871 "structure."), 888 "structure."),
872 {"Full", "Skeleton"}, 0, false, &ok); 889 {"Full", "Skeleton"}, 0, false, &ok);
873 if (!ok) 890 if (!ok) {
874 failed(); 891 failed();
892 vfs->DeleteDirectory(path);
893 return;
894 }
875 895
876 const auto full = res == "Full"; 896 const auto full = res == "Full";
877 const auto entry_size = CalculateRomFSEntrySize(extracted, full); 897 const auto entry_size = CalculateRomFSEntrySize(extracted, full);
@@ -888,6 +908,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
888 } else { 908 } else {
889 progress.close(); 909 progress.close();
890 failed(); 910 failed();
911 vfs->DeleteDirectory(path);
891 } 912 }
892} 913}
893 914
@@ -1174,6 +1195,7 @@ void GMainWindow::OnStartGame() {
1174 ui.action_Report_Compatibility->setEnabled(true); 1195 ui.action_Report_Compatibility->setEnabled(true);
1175 1196
1176 discord_rpc->Update(); 1197 discord_rpc->Update();
1198 ui.action_Load_Amiibo->setEnabled(true);
1177} 1199}
1178 1200
1179void GMainWindow::OnPauseGame() { 1201void GMainWindow::OnPauseGame() {
@@ -1278,6 +1300,27 @@ void GMainWindow::OnConfigure() {
1278 } 1300 }
1279} 1301}
1280 1302
1303void GMainWindow::OnLoadAmiibo() {
1304 const QString extensions{"*.bin"};
1305 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
1306 const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter);
1307 if (!filename.isEmpty()) {
1308 Core::System& system{Core::System::GetInstance()};
1309 Service::SM::ServiceManager& sm = system.ServiceManager();
1310 auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user");
1311 if (nfc != nullptr) {
1312 auto nfc_file = FileUtil::IOFile(filename.toStdString(), "rb");
1313 if (!nfc_file.IsOpen()) {
1314 return;
1315 }
1316 std::vector<u8> amiibo_buffer(nfc_file.GetSize());
1317 nfc_file.ReadBytes(amiibo_buffer.data(), amiibo_buffer.size());
1318 nfc_file.Close();
1319 nfc->LoadAmiibo(amiibo_buffer);
1320 }
1321 }
1322}
1323
1281void GMainWindow::OnAbout() { 1324void GMainWindow::OnAbout() {
1282 AboutDialog aboutDialog(this); 1325 AboutDialog aboutDialog(this);
1283 aboutDialog.exec(); 1326 aboutDialog.exec();
@@ -1318,15 +1361,17 @@ void GMainWindow::UpdateStatusBar() {
1318void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) { 1361void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) {
1319 QMessageBox::StandardButton answer; 1362 QMessageBox::StandardButton answer;
1320 QString status_message; 1363 QString status_message;
1321 const QString common_message = tr( 1364 const QString common_message =
1322 "The game you are trying to load requires additional files from your Switch to be dumped " 1365 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 " 1366 "dumped "
1324 "following wiki page: <a " 1367 "before playing.<br/><br/>For more information on dumping these files, please see the "
1325 "href='https://yuzu-emu.org/wiki/" 1368 "following wiki page: <a "
1326 "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System " 1369 "href='https://yuzu-emu.org/wiki/"
1327 "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit " 1370 "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 " 1371 "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to "
1329 "data, or other bugs."); 1372 "quit "
1373 "back to the game list? Continuing emulation may result in crashes, corrupted save "
1374 "data, or other bugs.");
1330 switch (result) { 1375 switch (result) {
1331 case Core::System::ResultStatus::ErrorSystemFiles: { 1376 case Core::System::ResultStatus::ErrorSystemFiles: {
1332 QString message = "yuzu was unable to locate a Switch system archive"; 1377 QString message = "yuzu was unable to locate a Switch system archive";
@@ -1357,9 +1402,12 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
1357 this, tr("Fatal Error"), 1402 this, tr("Fatal Error"),
1358 tr("yuzu has encountered a fatal error, please see the log for more details. " 1403 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: " 1404 "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 " 1405 "<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? " 1406 "to "
1362 "Continuing emulation may result in crashes, corrupted save data, or other bugs."), 1407 "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game "
1408 "list? "
1409 "Continuing emulation may result in crashes, corrupted save data, or other "
1410 "bugs."),
1363 QMessageBox::Yes | QMessageBox::No, QMessageBox::No); 1411 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
1364 status_message = "Fatal Error encountered"; 1412 status_message = "Fatal Error encountered";
1365 break; 1413 break;
@@ -1459,6 +1507,42 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1459 } 1507 }
1460} 1508}
1461 1509
1510boost::optional<u64> GMainWindow::SelectRomFSDumpTarget(
1511 const FileSys::RegisteredCacheUnion& installed, u64 program_id) {
1512 const auto dlc_entries =
1513 installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
1514 std::vector<FileSys::RegisteredCacheEntry> dlc_match;
1515 dlc_match.reserve(dlc_entries.size());
1516 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
1517 [&program_id, &installed](const FileSys::RegisteredCacheEntry& entry) {
1518 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id &&
1519 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
1520 });
1521
1522 std::vector<u64> romfs_tids;
1523 romfs_tids.push_back(program_id);
1524 for (const auto& entry : dlc_match)
1525 romfs_tids.push_back(entry.title_id);
1526
1527 if (romfs_tids.size() > 1) {
1528 QStringList list{"Base"};
1529 for (std::size_t i = 1; i < romfs_tids.size(); ++i)
1530 list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF));
1531
1532 bool ok;
1533 const auto res = QInputDialog::getItem(
1534 this, tr("Select RomFS Dump Target"),
1535 tr("Please select which RomFS you would like to dump."), list, 0, false, &ok);
1536 if (!ok) {
1537 return boost::none;
1538 }
1539
1540 return romfs_tids[list.indexOf(res)];
1541 }
1542
1543 return program_id;
1544}
1545
1462bool GMainWindow::ConfirmClose() { 1546bool GMainWindow::ConfirmClose() {
1463 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) 1547 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
1464 return true; 1548 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..654a15a5c 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -125,6 +125,7 @@ void Config::ReadValues() {
125 125
126 // System 126 // System
127 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); 127 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
128 Settings::values.enable_nfc = sdl2_config->GetBoolean("System", "enable_nfc", true);
128 Settings::values.username = sdl2_config->Get("System", "username", "yuzu"); 129 Settings::values.username = sdl2_config->Get("System", "username", "yuzu");
129 if (Settings::values.username.empty()) { 130 if (Settings::values.username.empty()) {
130 Settings::values.username = "yuzu"; 131 Settings::values.username = "yuzu";
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