summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/audio_renderer.h2
-rw-r--r--src/common/string_util.cpp57
-rw-r--r--src/common/string_util.h33
-rw-r--r--src/core/file_sys/control_metadata.cpp6
-rw-r--r--src/core/file_sys/control_metadata.h1
-rw-r--r--src/core/file_sys/savedata_factory.cpp32
-rw-r--r--src/core/file_sys/savedata_factory.h3
-rw-r--r--src/core/hle/kernel/process.cpp82
-rw-r--r--src/core/hle/kernel/process.h22
-rw-r--r--src/core/hle/kernel/svc.cpp12
-rw-r--r--src/core/hle/kernel/vm_manager.cpp82
-rw-r--r--src/core/hle/kernel/vm_manager.h15
-rw-r--r--src/core/hle/service/acc/acc.cpp6
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp25
-rw-r--r--src/core/hle/service/acc/profile_manager.h13
-rw-r--r--src/core/hle/service/audio/audren_u.cpp25
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp10
-rw-r--r--src/core/hle/service/filesystem/filesystem.h1
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp153
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h1
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp1
-rw-r--r--src/core/hle/service/nfp/nfp.cpp2
-rw-r--r--src/core/hle/service/ns/ns.cpp64
-rw-r--r--src/core/hle/service/service.cpp4
-rw-r--r--src/core/hle/service/spl/module.cpp9
-rw-r--r--src/core/hle/service/spl/module.h4
-rw-r--r--src/core/hle/service/time/interface.cpp2
-rw-r--r--src/core/hle/service/time/time.cpp132
-rw-r--r--src/core/hle/service/time/time.h19
-rw-r--r--src/core/loader/nro.cpp5
-rw-r--r--src/core/settings.h6
-rw-r--r--src/video_core/engines/maxwell_3d.cpp19
-rw-r--r--src/video_core/engines/maxwell_3d.h8
-rw-r--r--src/video_core/renderer_base.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp108
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h8
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp36
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_state.h4
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h66
-rw-r--r--src/video_core/surface.cpp4
-rw-r--r--src/video_core/surface.h148
-rw-r--r--src/video_core/textures/decoders.cpp4
-rw-r--r--src/video_core/textures/texture.h13
-rw-r--r--src/yuzu/configuration/config.cpp12
-rw-r--r--src/yuzu/configuration/configure_system.cpp21
-rw-r--r--src/yuzu/configuration/configure_system.ui228
-rw-r--r--src/yuzu/main.cpp5
-rw-r--r--src/yuzu/main.ui3
-rw-r--r--src/yuzu_cmd/config.cpp7
-rw-r--r--src/yuzu_cmd/default_ini.h5
51 files changed, 1018 insertions, 519 deletions
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index 046417da3..71ba4be40 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -143,7 +143,7 @@ struct AuxInfo {
143 std::array<u8, 24> output_mix_buffers; 143 std::array<u8, 24> output_mix_buffers;
144 u32_le mix_buffer_count; 144 u32_le mix_buffer_count;
145 u32_le sample_rate; // Stored in the aux buffer currently 145 u32_le sample_rate; // Stored in the aux buffer currently
146 u32_le sampe_count; 146 u32_le sample_count;
147 u64_le send_buffer_info; 147 u64_le send_buffer_info;
148 u64_le send_buffer_base; 148 u64_le send_buffer_base;
149 149
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 731d1db34..14f7037d8 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -4,11 +4,10 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cctype> 6#include <cctype>
7#include <cerrno>
8#include <codecvt> 7#include <codecvt>
9#include <cstdio>
10#include <cstdlib> 8#include <cstdlib>
11#include <cstring> 9#include <locale>
10#include <sstream>
12#include "common/common_paths.h" 11#include "common/common_paths.h"
13#include "common/logging/log.h" 12#include "common/logging/log.h"
14#include "common/string_util.h" 13#include "common/string_util.h"
@@ -33,24 +32,6 @@ std::string ToUpper(std::string str) {
33 return str; 32 return str;
34} 33}
35 34
36// For Debugging. Read out an u8 array.
37std::string ArrayToString(const u8* data, std::size_t size, int line_len, bool spaces) {
38 std::ostringstream oss;
39 oss << std::setfill('0') << std::hex;
40
41 for (int line = 0; size; ++data, --size) {
42 oss << std::setw(2) << (int)*data;
43
44 if (line_len == ++line) {
45 oss << '\n';
46 line = 0;
47 } else if (spaces)
48 oss << ' ';
49 }
50
51 return oss.str();
52}
53
54std::string StringFromBuffer(const std::vector<u8>& data) { 35std::string StringFromBuffer(const std::vector<u8>& data) {
55 return std::string(data.begin(), std::find(data.begin(), data.end(), '\0')); 36 return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
56} 37}
@@ -75,40 +56,6 @@ std::string StripQuotes(const std::string& s) {
75 return s; 56 return s;
76} 57}
77 58
78bool TryParse(const std::string& str, u32* const output) {
79 char* endptr = nullptr;
80
81 // Reset errno to a value other than ERANGE
82 errno = 0;
83
84 unsigned long value = strtoul(str.c_str(), &endptr, 0);
85
86 if (!endptr || *endptr)
87 return false;
88
89 if (errno == ERANGE)
90 return false;
91
92#if ULONG_MAX > UINT_MAX
93 if (value >= 0x100000000ull && value <= 0xFFFFFFFF00000000ull)
94 return false;
95#endif
96
97 *output = static_cast<u32>(value);
98 return true;
99}
100
101bool TryParse(const std::string& str, bool* const output) {
102 if ("1" == str || "true" == ToLower(str))
103 *output = true;
104 else if ("0" == str || "false" == ToLower(str))
105 *output = false;
106 else
107 return false;
108
109 return true;
110}
111
112std::string StringFromBool(bool value) { 59std::string StringFromBool(bool value) {
113 return value ? "True" : "False"; 60 return value ? "True" : "False";
114} 61}
diff --git a/src/common/string_util.h b/src/common/string_util.h
index 32bf6a19c..08f96533b 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -5,8 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include <iomanip>
9#include <sstream>
10#include <string> 8#include <string>
11#include <vector> 9#include <vector>
12#include "common/common_types.h" 10#include "common/common_types.h"
@@ -19,44 +17,13 @@ std::string ToLower(std::string str);
19/// Make a string uppercase 17/// Make a string uppercase
20std::string ToUpper(std::string str); 18std::string ToUpper(std::string str);
21 19
22std::string ArrayToString(const u8* data, std::size_t size, int line_len = 20, bool spaces = true);
23
24std::string StringFromBuffer(const std::vector<u8>& data); 20std::string StringFromBuffer(const std::vector<u8>& data);
25 21
26std::string StripSpaces(const std::string& s); 22std::string StripSpaces(const std::string& s);
27std::string StripQuotes(const std::string& s); 23std::string StripQuotes(const std::string& s);
28 24
29// Thousand separator. Turns 12345678 into 12,345,678
30template <typename I>
31std::string ThousandSeparate(I value, int spaces = 0) {
32 std::ostringstream oss;
33
34// std::locale("") seems to be broken on many platforms
35#if defined _WIN32 || (defined __linux__ && !defined __clang__)
36 oss.imbue(std::locale(""));
37#endif
38 oss << std::setw(spaces) << value;
39
40 return oss.str();
41}
42
43std::string StringFromBool(bool value); 25std::string StringFromBool(bool value);
44 26
45bool TryParse(const std::string& str, bool* output);
46bool TryParse(const std::string& str, u32* output);
47
48template <typename N>
49static bool TryParse(const std::string& str, N* const output) {
50 std::istringstream iss(str);
51
52 N tmp = 0;
53 if (iss >> tmp) {
54 *output = tmp;
55 return true;
56 } else
57 return false;
58}
59
60std::string TabsToSpaces(int tab_size, std::string in); 27std::string TabsToSpaces(int tab_size, std::string in);
61 28
62void SplitString(const std::string& str, char delim, std::vector<std::string>& output); 29void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index a012c2be9..c8fa912bf 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -66,4 +66,10 @@ std::string NACP::GetVersionString() const {
66 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 66 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
67 raw->version_string.size()); 67 raw->version_string.size());
68} 68}
69
70std::vector<u8> NACP::GetRawBytes() const {
71 std::vector<u8> out(sizeof(RawNACP));
72 std::memcpy(out.data(), raw.get(), sizeof(RawNACP));
73 return out;
74}
69} // namespace FileSys 75} // namespace FileSys
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 141f7e056..bfaad46b4 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -81,6 +81,7 @@ public:
81 u64 GetTitleId() const; 81 u64 GetTitleId() const;
82 u64 GetDLCBaseTitleId() const; 82 u64 GetDLCBaseTitleId() const;
83 std::string GetVersionString() const; 83 std::string GetVersionString() const;
84 std::vector<u8> GetRawBytes() const;
84 85
85private: 86private:
86 std::unique_ptr<RawNACP> raw; 87 std::unique_ptr<RawNACP> raw;
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index ef1aaebbb..5434f2149 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -83,28 +83,32 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
83 return MakeResult<VirtualDir>(std::move(out)); 83 return MakeResult<VirtualDir>(std::move(out));
84} 84}
85 85
86std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, 86VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) const {
87 u128 user_id, u64 save_id) { 87 return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space));
88 // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should 88}
89 // be interpreted as the title id of the current process.
90 if (type == SaveDataType::SaveData && title_id == 0)
91 title_id = Core::CurrentProcess()->GetTitleID();
92
93 std::string out;
94 89
90std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
95 switch (space) { 91 switch (space) {
96 case SaveDataSpaceId::NandSystem: 92 case SaveDataSpaceId::NandSystem:
97 out = "/system/"; 93 return "/system/";
98 break;
99 case SaveDataSpaceId::NandUser: 94 case SaveDataSpaceId::NandUser:
100 out = "/user/"; 95 return "/user/";
101 break;
102 case SaveDataSpaceId::TemporaryStorage: 96 case SaveDataSpaceId::TemporaryStorage:
103 out = "/temp/"; 97 return "/temp/";
104 break;
105 default: 98 default:
106 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); 99 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
100 return "/unrecognized/"; ///< To prevent corruption when ignoring asserts.
107 } 101 }
102}
103
104std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
105 u128 user_id, u64 save_id) {
106 // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
107 // be interpreted as the title id of the current process.
108 if (type == SaveDataType::SaveData && title_id == 0)
109 title_id = Core::CurrentProcess()->GetTitleID();
110
111 std::string out = GetSaveDataSpaceIdPath(space);
108 112
109 switch (type) { 113 switch (type) {
110 case SaveDataType::SystemSaveData: 114 case SaveDataType::SystemSaveData:
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index d69ef6741..2a0088040 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -52,6 +52,9 @@ public:
52 52
53 ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta); 53 ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
54 54
55 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
56
57 static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
55 static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, 58 static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id,
56 u128 user_id, u64 save_id); 59 u128 user_id, u64 save_id);
57 60
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 420218d59..f06b6bb55 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -5,11 +5,9 @@
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/common_funcs.h"
9#include "common/logging/log.h" 8#include "common/logging/log.h"
10#include "core/core.h" 9#include "core/core.h"
11#include "core/file_sys/program_metadata.h" 10#include "core/file_sys/program_metadata.h"
12#include "core/hle/kernel/errors.h"
13#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/resource_limit.h" 13#include "core/hle/kernel/resource_limit.h"
@@ -17,6 +15,7 @@
17#include "core/hle/kernel/thread.h" 15#include "core/hle/kernel/thread.h"
18#include "core/hle/kernel/vm_manager.h" 16#include "core/hle/kernel/vm_manager.h"
19#include "core/memory.h" 17#include "core/memory.h"
18#include "core/settings.h"
20 19
21namespace Kernel { 20namespace Kernel {
22 21
@@ -35,6 +34,11 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
35 process->process_id = kernel.CreateNewProcessID(); 34 process->process_id = kernel.CreateNewProcessID();
36 process->svc_access_mask.set(); 35 process->svc_access_mask.set();
37 36
37 std::mt19937 rng(Settings::values.rng_seed.value_or(0));
38 std::uniform_int_distribution<u64> distribution;
39 std::generate(process->random_entropy.begin(), process->random_entropy.end(),
40 [&] { return distribution(rng); });
41
38 kernel.AppendNewProcess(process); 42 kernel.AppendNewProcess(process);
39 return process; 43 return process;
40} 44}
@@ -241,83 +245,15 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
241} 245}
242 246
243ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { 247ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
244 if (target < vm_manager.GetHeapRegionBaseAddress() || 248 return vm_manager.HeapAllocate(target, size, perms);
245 target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
246 return ERR_INVALID_ADDRESS;
247 }
248
249 if (heap_memory == nullptr) {
250 // Initialize heap
251 heap_memory = std::make_shared<std::vector<u8>>();
252 heap_start = heap_end = target;
253 } else {
254 vm_manager.UnmapRange(heap_start, heap_end - heap_start);
255 }
256
257 // If necessary, expand backing vector to cover new heap extents.
258 if (target < heap_start) {
259 heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
260 heap_start = target;
261 vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
262 }
263 if (target + size > heap_end) {
264 heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
265 heap_end = target + size;
266 vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
267 }
268 ASSERT(heap_end - heap_start == heap_memory->size());
269
270 CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, heap_memory, target - heap_start,
271 size, MemoryState::Heap));
272 vm_manager.Reprotect(vma, perms);
273
274 heap_used = size;
275
276 return MakeResult<VAddr>(heap_end - size);
277} 249}
278 250
279ResultCode Process::HeapFree(VAddr target, u32 size) { 251ResultCode Process::HeapFree(VAddr target, u32 size) {
280 if (target < vm_manager.GetHeapRegionBaseAddress() || 252 return vm_manager.HeapFree(target, size);
281 target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
282 return ERR_INVALID_ADDRESS;
283 }
284
285 if (size == 0) {
286 return RESULT_SUCCESS;
287 }
288
289 ResultCode result = vm_manager.UnmapRange(target, size);
290 if (result.IsError())
291 return result;
292
293 heap_used -= size;
294
295 return RESULT_SUCCESS;
296} 253}
297 254
298ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 255ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
299 auto vma = vm_manager.FindVMA(src_addr); 256 return vm_manager.MirrorMemory(dst_addr, src_addr, size);
300
301 ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
302 ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
303
304 // The returned VMA might be a bigger one encompassing the desired address.
305 auto vma_offset = src_addr - vma->first;
306 ASSERT_MSG(vma_offset + size <= vma->second.size,
307 "Shared memory exceeds bounds of mapped block");
308
309 const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
310 std::size_t backing_block_offset = vma->second.offset + vma_offset;
311
312 CASCADE_RESULT(auto new_vma,
313 vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
314 MemoryState::Mapped));
315 // Protect mirror with permissions from old region
316 vm_manager.Reprotect(new_vma, vma->second.permissions);
317 // Remove permissions from old region
318 vm_manager.Reprotect(vma, VMAPermission::None);
319
320 return RESULT_SUCCESS;
321} 257}
322 258
323ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) { 259ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 8d2616c79..cf48787ce 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -8,6 +8,7 @@
8#include <bitset> 8#include <bitset>
9#include <cstddef> 9#include <cstddef>
10#include <memory> 10#include <memory>
11#include <random>
11#include <string> 12#include <string>
12#include <vector> 13#include <vector>
13#include <boost/container/static_vector.hpp> 14#include <boost/container/static_vector.hpp>
@@ -119,6 +120,8 @@ struct CodeSet final {
119 120
120class Process final : public Object { 121class Process final : public Object {
121public: 122public:
123 static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
124
122 static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name); 125 static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
123 126
124 std::string GetTypeName() const override { 127 std::string GetTypeName() const override {
@@ -212,6 +215,11 @@ public:
212 total_process_running_time_ticks += ticks; 215 total_process_running_time_ticks += ticks;
213 } 216 }
214 217
218 /// Gets 8 bytes of random data for svcGetInfo RandomEntropy
219 u64 GetRandomEntropy(std::size_t index) const {
220 return random_entropy.at(index);
221 }
222
215 /** 223 /**
216 * Loads process-specifics configuration info with metadata provided 224 * Loads process-specifics configuration info with metadata provided
217 * by an executable. 225 * by an executable.
@@ -292,17 +300,6 @@ private:
292 u32 allowed_thread_priority_mask = 0xFFFFFFFF; 300 u32 allowed_thread_priority_mask = 0xFFFFFFFF;
293 u32 is_virtual_address_memory_enabled = 0; 301 u32 is_virtual_address_memory_enabled = 0;
294 302
295 // Memory used to back the allocations in the regular heap. A single vector is used to cover
296 // the entire virtual address space extents that bound the allocations, including any holes.
297 // This makes deallocation and reallocation of holes fast and keeps process memory contiguous
298 // in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
299 std::shared_ptr<std::vector<u8>> heap_memory;
300
301 // The left/right bounds of the address space covered by heap_memory.
302 VAddr heap_start = 0;
303 VAddr heap_end = 0;
304 u64 heap_used = 0;
305
306 /// The Thread Local Storage area is allocated as processes create threads, 303 /// The Thread Local Storage area is allocated as processes create threads,
307 /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part 304 /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
308 /// holds the TLS for a specific thread. This vector contains which parts are in use for each 305 /// holds the TLS for a specific thread. This vector contains which parts are in use for each
@@ -321,6 +318,9 @@ private:
321 /// Per-process handle table for storing created object handles in. 318 /// Per-process handle table for storing created object handles in.
322 HandleTable handle_table; 319 HandleTable handle_table;
323 320
321 /// Random values for svcGetInfo RandomEntropy
322 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
323
324 std::string name; 324 std::string name;
325}; 325};
326 326
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 7e8e87c33..b0b6508d9 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -34,6 +34,7 @@
34#include "core/hle/lock.h" 34#include "core/hle/lock.h"
35#include "core/hle/result.h" 35#include "core/hle/result.h"
36#include "core/hle/service/service.h" 36#include "core/hle/service/service.h"
37#include "core/settings.h"
37 38
38namespace Kernel { 39namespace Kernel {
39namespace { 40namespace {
@@ -558,7 +559,16 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
558 *result = 0; 559 *result = 0;
559 break; 560 break;
560 case GetInfoType::RandomEntropy: 561 case GetInfoType::RandomEntropy:
561 *result = 0; 562 if (handle != 0) {
563 return ERR_INVALID_HANDLE;
564 }
565
566 if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) {
567 return ERR_INVALID_COMBINATION_KERNEL;
568 }
569
570 *result = current_process->GetRandomEntropy(info_sub_id);
571 return RESULT_SUCCESS;
562 break; 572 break;
563 case GetInfoType::ASLRRegionBaseAddr: 573 case GetInfoType::ASLRRegionBaseAddr:
564 *result = vm_manager.GetASLRRegionBaseAddress(); 574 *result = vm_manager.GetASLRRegionBaseAddress();
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 1a92c8f70..ec7fd6150 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -243,6 +243,85 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
243 return RESULT_SUCCESS; 243 return RESULT_SUCCESS;
244} 244}
245 245
246ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
247 if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
248 target + size < target) {
249 return ERR_INVALID_ADDRESS;
250 }
251
252 if (heap_memory == nullptr) {
253 // Initialize heap
254 heap_memory = std::make_shared<std::vector<u8>>();
255 heap_start = heap_end = target;
256 } else {
257 UnmapRange(heap_start, heap_end - heap_start);
258 }
259
260 // If necessary, expand backing vector to cover new heap extents.
261 if (target < heap_start) {
262 heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
263 heap_start = target;
264 RefreshMemoryBlockMappings(heap_memory.get());
265 }
266 if (target + size > heap_end) {
267 heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
268 heap_end = target + size;
269 RefreshMemoryBlockMappings(heap_memory.get());
270 }
271 ASSERT(heap_end - heap_start == heap_memory->size());
272
273 CASCADE_RESULT(auto vma, MapMemoryBlock(target, heap_memory, target - heap_start, size,
274 MemoryState::Heap));
275 Reprotect(vma, perms);
276
277 heap_used = size;
278
279 return MakeResult<VAddr>(heap_end - size);
280}
281
282ResultCode VMManager::HeapFree(VAddr target, u64 size) {
283 if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
284 target + size < target) {
285 return ERR_INVALID_ADDRESS;
286 }
287
288 if (size == 0) {
289 return RESULT_SUCCESS;
290 }
291
292 const ResultCode result = UnmapRange(target, size);
293 if (result.IsError()) {
294 return result;
295 }
296
297 heap_used -= size;
298 return RESULT_SUCCESS;
299}
300
301ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
302 const auto vma = FindVMA(src_addr);
303
304 ASSERT_MSG(vma != vma_map.end(), "Invalid memory address");
305 ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address");
306
307 // The returned VMA might be a bigger one encompassing the desired address.
308 const auto vma_offset = src_addr - vma->first;
309 ASSERT_MSG(vma_offset + size <= vma->second.size,
310 "Shared memory exceeds bounds of mapped block");
311
312 const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
313 const std::size_t backing_block_offset = vma->second.offset + vma_offset;
314
315 CASCADE_RESULT(auto new_vma, MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
316 MemoryState::Mapped));
317 // Protect mirror with permissions from old region
318 Reprotect(new_vma, vma->second.permissions);
319 // Remove permissions from old region
320 Reprotect(vma, VMAPermission::None);
321
322 return RESULT_SUCCESS;
323}
324
246void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) { 325void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
247 // If this ever proves to have a noticeable performance impact, allow users of the function to 326 // If this ever proves to have a noticeable performance impact, allow users of the function to
248 // specify a specific range of addresses to limit the scan to. 327 // specify a specific range of addresses to limit the scan to.
@@ -495,8 +574,7 @@ u64 VMManager::GetTotalMemoryUsage() const {
495} 574}
496 575
497u64 VMManager::GetTotalHeapUsage() const { 576u64 VMManager::GetTotalHeapUsage() const {
498 LOG_WARNING(Kernel, "(STUBBED) called"); 577 return heap_used;
499 return 0x0;
500} 578}
501 579
502VAddr VMManager::GetAddressSpaceBaseAddress() const { 580VAddr VMManager::GetAddressSpaceBaseAddress() const {
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 2447cbb8f..248cc46dc 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -186,6 +186,11 @@ public:
186 /// Changes the permissions of a range of addresses, splitting VMAs as necessary. 186 /// Changes the permissions of a range of addresses, splitting VMAs as necessary.
187 ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms); 187 ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms);
188 188
189 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
190 ResultCode HeapFree(VAddr target, u64 size);
191
192 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
193
189 /** 194 /**
190 * Scans all VMAs and updates the page table range of any that use the given vector as backing 195 * Scans all VMAs and updates the page table range of any that use the given vector as backing
191 * memory. This should be called after any operation that causes reallocation of the vector. 196 * memory. This should be called after any operation that causes reallocation of the vector.
@@ -343,5 +348,15 @@ private:
343 348
344 VAddr tls_io_region_base = 0; 349 VAddr tls_io_region_base = 0;
345 VAddr tls_io_region_end = 0; 350 VAddr tls_io_region_end = 0;
351
352 // Memory used to back the allocations in the regular heap. A single vector is used to cover
353 // the entire virtual address space extents that bound the allocations, including any holes.
354 // This makes deallocation and reallocation of holes fast and keeps process memory contiguous
355 // in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
356 std::shared_ptr<std::vector<u8>> heap_memory;
357 // The left/right bounds of the address space covered by heap_memory.
358 VAddr heap_start = 0;
359 VAddr heap_end = 0;
360 u64 heap_used = 0;
346}; 361};
347} // namespace Kernel 362} // namespace Kernel
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 8318eff5f..c629f9357 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -252,8 +252,10 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
252 rb.PushRaw<u128>(INVALID_UUID); 252 rb.PushRaw<u128>(INVALID_UUID);
253 return; 253 return;
254 } 254 }
255 auto user_list = profile_manager->GetAllUsers(); 255
256 if (user_list.empty()) { 256 const auto user_list = profile_manager->GetAllUsers();
257 if (std::all_of(user_list.begin(), user_list.end(),
258 [](const auto& user) { return user.uuid == INVALID_UUID; })) {
257 rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code 259 rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code
258 rb.PushRaw<u128>(INVALID_UUID); 260 rb.PushRaw<u128>(INVALID_UUID);
259 return; 261 return;
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index c08394e4c..968263846 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -2,8 +2,11 @@
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 <cstring>
5#include <random> 6#include <random>
6 7
8#include <fmt/format.h>
9
7#include "common/file_util.h" 10#include "common/file_util.h"
8#include "core/hle/service/acc/profile_manager.h" 11#include "core/hle/service/acc/profile_manager.h"
9#include "core/settings.h" 12#include "core/settings.h"
@@ -39,6 +42,19 @@ UUID UUID::Generate() {
39 return UUID{distribution(gen), distribution(gen)}; 42 return UUID{distribution(gen), distribution(gen)};
40} 43}
41 44
45std::string UUID::Format() const {
46 return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
47}
48
49std::string UUID::FormatSwitch() const {
50 std::array<u8, 16> s{};
51 std::memcpy(s.data(), uuid.data(), sizeof(u128));
52 return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
53 ":02x}{:02x}{:02x}{:02x}{:02x}",
54 s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
55 s[12], s[13], s[14], s[15]);
56}
57
42ProfileManager::ProfileManager() { 58ProfileManager::ProfileManager() {
43 ParseUserSaveFile(); 59 ParseUserSaveFile();
44 60
@@ -325,11 +341,12 @@ void ProfileManager::ParseUserSaveFile() {
325 return; 341 return;
326 } 342 }
327 343
328 for (std::size_t i = 0; i < MAX_USERS; ++i) { 344 for (const auto& user : data.users) {
329 const auto& user = data.users[i]; 345 if (user.uuid == UUID(INVALID_UUID)) {
346 continue;
347 }
330 348
331 if (user.uuid != UUID(INVALID_UUID)) 349 AddUser({user.uuid, user.username, user.timestamp, {}, false});
332 AddUser({user.uuid, user.username, user.timestamp, {}, false});
333 } 350 }
334 351
335 std::stable_partition(profiles.begin(), profiles.end(), 352 std::stable_partition(profiles.begin(), profiles.end(),
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 747c46c20..d2d8e6c6b 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -42,18 +42,9 @@ struct UUID {
42 void Invalidate() { 42 void Invalidate() {
43 uuid = INVALID_UUID; 43 uuid = INVALID_UUID;
44 } 44 }
45 std::string Format() const {
46 return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
47 }
48 45
49 std::string FormatSwitch() const { 46 std::string Format() const;
50 std::array<u8, 16> s{}; 47 std::string FormatSwitch() const;
51 std::memcpy(s.data(), uuid.data(), sizeof(u128));
52 return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
53 ":02x}{:02x}{:02x}{:02x}{:02x}",
54 s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
55 s[12], s[13], s[14], s[15]);
56 }
57}; 48};
58static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); 49static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
59 50
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index fac6785a5..35a8bef6c 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -32,8 +32,8 @@ public:
32 {5, &IAudioRenderer::Start, "Start"}, 32 {5, &IAudioRenderer::Start, "Start"},
33 {6, &IAudioRenderer::Stop, "Stop"}, 33 {6, &IAudioRenderer::Stop, "Stop"},
34 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, 34 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
35 {8, nullptr, "SetRenderingTimeLimit"}, 35 {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
36 {9, nullptr, "GetRenderingTimeLimit"}, 36 {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
37 {10, nullptr, "RequestUpdateAuto"}, 37 {10, nullptr, "RequestUpdateAuto"},
38 {11, nullptr, "ExecuteAudioRendererRendering"}, 38 {11, nullptr, "ExecuteAudioRendererRendering"},
39 }; 39 };
@@ -110,8 +110,29 @@ private:
110 LOG_WARNING(Service_Audio, "(STUBBED) called"); 110 LOG_WARNING(Service_Audio, "(STUBBED) called");
111 } 111 }
112 112
113 void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
114 IPC::RequestParser rp{ctx};
115 rendering_time_limit_percent = rp.Pop<u32>();
116 ASSERT(rendering_time_limit_percent >= 0 && rendering_time_limit_percent <= 100);
117
118 IPC::ResponseBuilder rb{ctx, 2};
119 rb.Push(RESULT_SUCCESS);
120
121 LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}",
122 rendering_time_limit_percent);
123 }
124
125 void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
126 LOG_DEBUG(Service_Audio, "called");
127
128 IPC::ResponseBuilder rb{ctx, 3};
129 rb.Push(RESULT_SUCCESS);
130 rb.Push(rendering_time_limit_percent);
131 }
132
113 Kernel::SharedPtr<Kernel::Event> system_event; 133 Kernel::SharedPtr<Kernel::Event> system_event;
114 std::unique_ptr<AudioCore::AudioRenderer> renderer; 134 std::unique_ptr<AudioCore::AudioRenderer> renderer;
135 u32 rendering_time_limit_percent = 100;
115}; 136};
116 137
117class IAudioDevice final : public ServiceFramework<IAudioDevice> { 138class IAudioDevice final : public ServiceFramework<IAudioDevice> {
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index e32a7c48e..ea8fd965a 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -309,6 +309,16 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
309 return save_data_factory->Open(space, save_struct); 309 return save_data_factory->Open(space, save_struct);
310} 310}
311 311
312ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) {
313 LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space));
314
315 if (save_data_factory == nullptr) {
316 return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound);
317 }
318
319 return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
320}
321
312ResultVal<FileSys::VirtualDir> OpenSDMC() { 322ResultVal<FileSys::VirtualDir> OpenSDMC() {
313 LOG_TRACE(Service_FS, "Opening SDMC"); 323 LOG_TRACE(Service_FS, "Opening SDMC");
314 324
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 6ca5c5636..2cbb70c87 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -45,6 +45,7 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora
45 FileSys::ContentRecordType type); 45 FileSys::ContentRecordType type);
46ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, 46ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
47 FileSys::SaveDataDescriptor save_struct); 47 FileSys::SaveDataDescriptor save_struct);
48ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
48ResultVal<FileSys::VirtualDir> OpenSDMC(); 49ResultVal<FileSys::VirtualDir> OpenSDMC();
49 50
50std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); 51std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index c1c83a11d..b9a1d5105 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -11,6 +11,7 @@
11 11
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/hex_util.h"
14#include "common/logging/log.h" 15#include "common/logging/log.h"
15#include "common/string_util.h" 16#include "common/string_util.h"
16#include "core/file_sys/directory.h" 17#include "core/file_sys/directory.h"
@@ -451,7 +452,147 @@ private:
451 VfsDirectoryServiceWrapper backend; 452 VfsDirectoryServiceWrapper backend;
452}; 453};
453 454
455class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
456public:
457 explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space)
458 : ServiceFramework("ISaveDataInfoReader") {
459 static const FunctionInfo functions[] = {
460 {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
461 };
462 RegisterHandlers(functions);
463
464 FindAllSaves(space);
465 }
466
467 void ReadSaveDataInfo(Kernel::HLERequestContext& ctx) {
468 // Calculate how many entries we can fit in the output buffer
469 const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo);
470
471 // Cap at total number of entries.
472 const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index);
473
474 // Determine data start and end
475 const auto* begin = reinterpret_cast<u8*>(info.data() + next_entry_index);
476 const auto* end = reinterpret_cast<u8*>(info.data() + next_entry_index + actual_entries);
477 const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
478
479 next_entry_index += actual_entries;
480
481 // Write the data to memory
482 ctx.WriteBuffer(begin, range_size);
483
484 IPC::ResponseBuilder rb{ctx, 3};
485 rb.Push(RESULT_SUCCESS);
486 rb.Push<u32>(static_cast<u32>(actual_entries));
487 }
488
489private:
490 static u64 stoull_be(std::string_view str) {
491 if (str.size() != 16)
492 return 0;
493
494 const auto bytes = Common::HexStringToArray<0x8>(str);
495 u64 out{};
496 std::memcpy(&out, bytes.data(), sizeof(u64));
497
498 return Common::swap64(out);
499 }
500
501 void FindAllSaves(FileSys::SaveDataSpaceId space) {
502 const auto save_root = OpenSaveDataSpace(space);
503 ASSERT(save_root.Succeeded());
504
505 for (const auto& type : (*save_root)->GetSubdirectories()) {
506 if (type->GetName() == "save") {
507 for (const auto& save_id : type->GetSubdirectories()) {
508 for (const auto& user_id : save_id->GetSubdirectories()) {
509 const auto save_id_numeric = stoull_be(save_id->GetName());
510 auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName());
511 std::reverse(user_id_numeric.begin(), user_id_numeric.end());
512
513 if (save_id_numeric != 0) {
514 // System Save Data
515 info.emplace_back(SaveDataInfo{
516 0,
517 space,
518 FileSys::SaveDataType::SystemSaveData,
519 {},
520 user_id_numeric,
521 save_id_numeric,
522 0,
523 user_id->GetSize(),
524 {},
525 });
526
527 continue;
528 }
529
530 for (const auto& title_id : user_id->GetSubdirectories()) {
531 const auto device =
532 std::all_of(user_id_numeric.begin(), user_id_numeric.end(),
533 [](u8 val) { return val == 0; });
534 info.emplace_back(SaveDataInfo{
535 0,
536 space,
537 device ? FileSys::SaveDataType::DeviceSaveData
538 : FileSys::SaveDataType::SaveData,
539 {},
540 user_id_numeric,
541 save_id_numeric,
542 stoull_be(title_id->GetName()),
543 title_id->GetSize(),
544 {},
545 });
546 }
547 }
548 }
549 } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) {
550 // Temporary Storage
551 for (const auto& user_id : type->GetSubdirectories()) {
552 for (const auto& title_id : user_id->GetSubdirectories()) {
553 if (!title_id->GetFiles().empty() ||
554 !title_id->GetSubdirectories().empty()) {
555 auto user_id_numeric =
556 Common::HexStringToArray<0x10>(user_id->GetName());
557 std::reverse(user_id_numeric.begin(), user_id_numeric.end());
558
559 info.emplace_back(SaveDataInfo{
560 0,
561 space,
562 FileSys::SaveDataType::TemporaryStorage,
563 {},
564 user_id_numeric,
565 stoull_be(type->GetName()),
566 stoull_be(title_id->GetName()),
567 title_id->GetSize(),
568 {},
569 });
570 }
571 }
572 }
573 }
574 }
575 }
576
577 struct SaveDataInfo {
578 u64_le save_id_unknown;
579 FileSys::SaveDataSpaceId space;
580 FileSys::SaveDataType type;
581 INSERT_PADDING_BYTES(0x6);
582 std::array<u8, 0x10> user_id;
583 u64_le save_id;
584 u64_le title_id;
585 u64_le save_image_size;
586 INSERT_PADDING_BYTES(0x28);
587 };
588 static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
589
590 std::vector<SaveDataInfo> info;
591 u64 next_entry_index = 0;
592};
593
454FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { 594FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
595 // clang-format off
455 static const FunctionInfo functions[] = { 596 static const FunctionInfo functions[] = {
456 {0, nullptr, "MountContent"}, 597 {0, nullptr, "MountContent"},
457 {1, &FSP_SRV::Initialize, "Initialize"}, 598 {1, &FSP_SRV::Initialize, "Initialize"},
@@ -485,7 +626,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
485 {58, nullptr, "ReadSaveDataFileSystemExtraData"}, 626 {58, nullptr, "ReadSaveDataFileSystemExtraData"},
486 {59, nullptr, "WriteSaveDataFileSystemExtraData"}, 627 {59, nullptr, "WriteSaveDataFileSystemExtraData"},
487 {60, nullptr, "OpenSaveDataInfoReader"}, 628 {60, nullptr, "OpenSaveDataInfoReader"},
488 {61, nullptr, "OpenSaveDataInfoReaderBySaveDataSpaceId"}, 629 {61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
489 {62, nullptr, "OpenCacheStorageList"}, 630 {62, nullptr, "OpenCacheStorageList"},
490 {64, nullptr, "OpenSaveDataInternalStorageFileSystem"}, 631 {64, nullptr, "OpenSaveDataInternalStorageFileSystem"},
491 {65, nullptr, "UpdateSaveDataMacForDebug"}, 632 {65, nullptr, "UpdateSaveDataMacForDebug"},
@@ -544,6 +685,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
544 {1009, nullptr, "GetAndClearMemoryReportInfo"}, 685 {1009, nullptr, "GetAndClearMemoryReportInfo"},
545 {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, 686 {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
546 }; 687 };
688 // clang-format on
547 RegisterHandlers(functions); 689 RegisterHandlers(functions);
548} 690}
549 691
@@ -618,6 +760,15 @@ void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
618 MountSaveData(ctx); 760 MountSaveData(ctx);
619} 761}
620 762
763void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) {
764 IPC::RequestParser rp{ctx};
765 const auto space = rp.PopRaw<FileSys::SaveDataSpaceId>();
766
767 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
768 rb.Push(RESULT_SUCCESS);
769 rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space));
770}
771
621void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { 772void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
622 LOG_WARNING(Service_FS, "(STUBBED) called"); 773 LOG_WARNING(Service_FS, "(STUBBED) called");
623 774
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 4aa0358cb..e7abec0a3 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -25,6 +25,7 @@ private:
25 void CreateSaveData(Kernel::HLERequestContext& ctx); 25 void CreateSaveData(Kernel::HLERequestContext& ctx);
26 void MountSaveData(Kernel::HLERequestContext& ctx); 26 void MountSaveData(Kernel::HLERequestContext& ctx);
27 void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); 27 void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
28 void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx);
28 void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); 29 void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
29 void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); 30 void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
30 void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); 31 void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index ff9b64be4..56c415e4e 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -310,6 +310,7 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
310 dual_entry.pad_states.raw = pad_state.raw; 310 dual_entry.pad_states.raw = pad_state.raw;
311 dual_entry.l_stick = lstick_entry; 311 dual_entry.l_stick = lstick_entry;
312 dual_entry.r_stick = rstick_entry; 312 dual_entry.r_stick = rstick_entry;
313 break;
313 case NPadControllerType::JoyLeft: 314 case NPadControllerType::JoyLeft:
314 left_entry.connection_status.raw = 0; 315 left_entry.connection_status.raw = 0;
315 316
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index c1af878fe..1d6e7756f 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -212,7 +212,7 @@ private:
212 IPC::ResponseBuilder rb{ctx, 2}; 212 IPC::ResponseBuilder rb{ctx, 2};
213 auto amiibo = nfp_interface.GetAmiiboBuffer(); 213 auto amiibo = nfp_interface.GetAmiiboBuffer();
214 TagInfo tag_info{}; 214 TagInfo tag_info{};
215 std::memcpy(tag_info.uuid.data(), amiibo.uuid.data(), sizeof(tag_info.uuid.size())); 215 tag_info.uuid = amiibo.uuid;
216 tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size()); 216 tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size());
217 217
218 tag_info.protocol = 1; // TODO(ogniK): Figure out actual values 218 tag_info.protocol = 1; // TODO(ogniK): Figure out actual values
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 07c1381fe..1d2978f24 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -2,6 +2,9 @@
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 "common/logging/log.h"
6#include "core/file_sys/control_metadata.h"
7#include "core/file_sys/patch_manager.h"
5#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
6#include "core/hle/kernel/hle_ipc.h" 9#include "core/hle/kernel/hle_ipc.h"
7#include "core/hle/service/ns/ns.h" 10#include "core/hle/service/ns/ns.h"
@@ -118,7 +121,7 @@ public:
118 {305, nullptr, "TerminateSystemApplet"}, 121 {305, nullptr, "TerminateSystemApplet"},
119 {306, nullptr, "LaunchOverlayApplet"}, 122 {306, nullptr, "LaunchOverlayApplet"},
120 {307, nullptr, "TerminateOverlayApplet"}, 123 {307, nullptr, "TerminateOverlayApplet"},
121 {400, nullptr, "GetApplicationControlData"}, 124 {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
122 {401, nullptr, "InvalidateAllApplicationControlCache"}, 125 {401, nullptr, "InvalidateAllApplicationControlCache"},
123 {402, nullptr, "RequestDownloadApplicationControlData"}, 126 {402, nullptr, "RequestDownloadApplicationControlData"},
124 {403, nullptr, "GetMaxApplicationControlCacheCount"}, 127 {403, nullptr, "GetMaxApplicationControlCacheCount"},
@@ -243,6 +246,65 @@ public:
243 246
244 RegisterHandlers(functions); 247 RegisterHandlers(functions);
245 } 248 }
249
250 void GetApplicationControlData(Kernel::HLERequestContext& ctx) {
251 IPC::RequestParser rp{ctx};
252 const auto flag = rp.PopRaw<u64>();
253 LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
254
255 const auto title_id = rp.PopRaw<u64>();
256
257 const auto size = ctx.GetWriteBufferSize();
258
259 const FileSys::PatchManager pm{title_id};
260 const auto control = pm.GetControlMetadata();
261
262 std::vector<u8> out;
263
264 if (control.first != nullptr) {
265 if (size < 0x4000) {
266 LOG_ERROR(Service_NS,
267 "output buffer is too small! (actual={:016X}, expected_min=0x4000)",
268 size);
269 IPC::ResponseBuilder rb{ctx, 2};
270 // TODO(DarkLordZach): Find a better error code for this.
271 rb.Push(ResultCode(-1));
272 return;
273 }
274
275 out.resize(0x4000);
276 const auto bytes = control.first->GetRawBytes();
277 std::memcpy(out.data(), bytes.data(), bytes.size());
278 } else {
279 LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.",
280 title_id);
281 out.resize(std::min<u64>(0x4000, size));
282 }
283
284 if (control.second != nullptr) {
285 if (size < 0x4000 + control.second->GetSize()) {
286 LOG_ERROR(Service_NS,
287 "output buffer is too small! (actual={:016X}, expected_min={:016X})",
288 size, 0x4000 + control.second->GetSize());
289 IPC::ResponseBuilder rb{ctx, 2};
290 // TODO(DarkLordZach): Find a better error code for this.
291 rb.Push(ResultCode(-1));
292 return;
293 }
294
295 out.resize(0x4000 + control.second->GetSize());
296 control.second->Read(out.data() + 0x4000, control.second->GetSize());
297 } else {
298 LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.",
299 title_id);
300 }
301
302 ctx.WriteBuffer(out);
303
304 IPC::ResponseBuilder rb{ctx, 3};
305 rb.Push(RESULT_SUCCESS);
306 rb.Push<u32>(static_cast<u32>(out.size()));
307 }
246}; 308};
247 309
248class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> { 310class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index a4cf45267..1ec340466 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -80,8 +80,8 @@ namespace Service {
80 * Creates a function string for logging, complete with the name (or header code, depending 80 * Creates a function string for logging, complete with the name (or header code, depending
81 * on what's passed in) the port name, and all the cmd_buff arguments. 81 * on what's passed in) the port name, and all the cmd_buff arguments.
82 */ 82 */
83static std::string MakeFunctionString(const char* name, const char* port_name, 83[[maybe_unused]] static std::string MakeFunctionString(const char* name, const char* port_name,
84 const u32* cmd_buff) { 84 const u32* cmd_buff) {
85 // Number of params == bits 0-5 + bits 6-11 85 // Number of params == bits 0-5 + bits 6-11
86 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); 86 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
87 87
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp
index 44a6717d0..69c260408 100644
--- a/src/core/hle/service/spl/module.cpp
+++ b/src/core/hle/service/spl/module.cpp
@@ -3,18 +3,23 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <chrono>
6#include <cstdlib> 7#include <cstdlib>
8#include <ctime>
9#include <functional>
7#include <vector> 10#include <vector>
8#include "common/logging/log.h" 11#include "common/logging/log.h"
9#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
10#include "core/hle/service/spl/csrng.h" 13#include "core/hle/service/spl/csrng.h"
11#include "core/hle/service/spl/module.h" 14#include "core/hle/service/spl/module.h"
12#include "core/hle/service/spl/spl.h" 15#include "core/hle/service/spl/spl.h"
16#include "core/settings.h"
13 17
14namespace Service::SPL { 18namespace Service::SPL {
15 19
16Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 20Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
17 : ServiceFramework(name), module(std::move(module)) {} 21 : ServiceFramework(name), module(std::move(module)),
22 rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {}
18 23
19Module::Interface::~Interface() = default; 24Module::Interface::~Interface() = default;
20 25
@@ -24,7 +29,7 @@ void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
24 std::size_t size = ctx.GetWriteBufferSize(); 29 std::size_t size = ctx.GetWriteBufferSize();
25 30
26 std::vector<u8> data(size); 31 std::vector<u8> data(size);
27 std::generate(data.begin(), data.end(), std::rand); 32 std::generate(data.begin(), data.end(), rng);
28 33
29 ctx.WriteBuffer(data); 34 ctx.WriteBuffer(data);
30 35
diff --git a/src/core/hle/service/spl/module.h b/src/core/hle/service/spl/module.h
index 48fda6099..afa1f0295 100644
--- a/src/core/hle/service/spl/module.h
+++ b/src/core/hle/service/spl/module.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <random>
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9namespace Service::SPL { 10namespace Service::SPL {
@@ -19,6 +20,9 @@ public:
19 20
20 protected: 21 protected:
21 std::shared_ptr<Module> module; 22 std::shared_ptr<Module> module;
23
24 private:
25 std::mt19937 rng;
22 }; 26 };
23}; 27};
24 28
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index 18a5d71d5..e3cbd7004 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -21,7 +21,7 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
21 {102, nullptr, "GetStandardUserSystemClockInitialYear"}, 21 {102, nullptr, "GetStandardUserSystemClockInitialYear"},
22 {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, 22 {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"},
23 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, 23 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
24 {400, nullptr, "GetClockSnapshot"}, 24 {400, &Time::GetClockSnapshot, "GetClockSnapshot"},
25 {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, 25 {401, nullptr, "GetClockSnapshotFromSystemClockContext"},
26 {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"}, 26 {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"},
27 {501, nullptr, "CalculateSpanBetween"}, 27 {501, nullptr, "CalculateSpanBetween"},
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 28fd8debc..85e7b1195 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -15,6 +15,44 @@
15 15
16namespace Service::Time { 16namespace Service::Time {
17 17
18static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
19 CalendarAdditionalInfo& additional_info,
20 [[maybe_unused]] const TimeZoneRule& /*rule*/) {
21 const std::time_t time(posix_time);
22 const std::tm* tm = std::localtime(&time);
23 if (tm == nullptr) {
24 calendar_time = {};
25 additional_info = {};
26 return;
27 }
28 calendar_time.year = tm->tm_year + 1900;
29 calendar_time.month = tm->tm_mon + 1;
30 calendar_time.day = tm->tm_mday;
31 calendar_time.hour = tm->tm_hour;
32 calendar_time.minute = tm->tm_min;
33 calendar_time.second = tm->tm_sec;
34
35 additional_info.day_of_week = tm->tm_wday;
36 additional_info.day_of_year = tm->tm_yday;
37 std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC"));
38 additional_info.utc_offset = 0;
39}
40
41static u64 CalendarToPosix(const CalendarTime& calendar_time,
42 [[maybe_unused]] const TimeZoneRule& /*rule*/) {
43 std::tm time{};
44 time.tm_year = calendar_time.year - 1900;
45 time.tm_mon = calendar_time.month - 1;
46 time.tm_mday = calendar_time.day;
47
48 time.tm_hour = calendar_time.hour;
49 time.tm_min = calendar_time.minute;
50 time.tm_sec = calendar_time.second;
51
52 std::time_t epoch_time = std::mktime(&time);
53 return static_cast<u64>(epoch_time);
54}
55
18class ISystemClock final : public ServiceFramework<ISystemClock> { 56class ISystemClock final : public ServiceFramework<ISystemClock> {
19public: 57public:
20 ISystemClock() : ServiceFramework("ISystemClock") { 58 ISystemClock() : ServiceFramework("ISystemClock") {
@@ -80,8 +118,8 @@ public:
80 {5, nullptr, "GetTimeZoneRuleVersion"}, 118 {5, nullptr, "GetTimeZoneRuleVersion"},
81 {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"}, 119 {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
82 {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, 120 {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
83 {201, nullptr, "ToPosixTime"}, 121 {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"},
84 {202, nullptr, "ToPosixTimeWithMyRule"}, 122 {202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
85 }; 123 };
86 RegisterHandlers(functions); 124 RegisterHandlers(functions);
87 } 125 }
@@ -151,24 +189,29 @@ private:
151 rb.PushRaw(additional_info); 189 rb.PushRaw(additional_info);
152 } 190 }
153 191
154 void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time, 192 void ToPosixTime(Kernel::HLERequestContext& ctx) {
155 CalendarAdditionalInfo& additional_info, const TimeZoneRule& /*rule*/) { 193 // TODO(ogniK): Figure out how to handle multiple times
156 std::time_t t(posix_time); 194 LOG_WARNING(Service_Time, "(STUBBED) called");
157 std::tm* tm = std::localtime(&t); 195 IPC::RequestParser rp{ctx};
158 if (!tm) { 196 auto calendar_time = rp.PopRaw<CalendarTime>();
159 return; 197 auto posix_time = CalendarToPosix(calendar_time, {});
160 } 198
161 calendar_time.year = tm->tm_year + 1900; 199 IPC::ResponseBuilder rb{ctx, 3};
162 calendar_time.month = tm->tm_mon + 1; 200 rb.Push(RESULT_SUCCESS);
163 calendar_time.day = tm->tm_mday; 201 rb.PushRaw<u32>(1); // Amount of times we're returning
164 calendar_time.hour = tm->tm_hour; 202 ctx.WriteBuffer(&posix_time, sizeof(u64));
165 calendar_time.minute = tm->tm_min; 203 }
166 calendar_time.second = tm->tm_sec; 204
167 205 void ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
168 additional_info.day_of_week = tm->tm_wday; 206 LOG_WARNING(Service_Time, "(STUBBED) called");
169 additional_info.day_of_year = tm->tm_yday; 207 IPC::RequestParser rp{ctx};
170 std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC")); 208 auto calendar_time = rp.PopRaw<CalendarTime>();
171 additional_info.utc_offset = 0; 209 auto posix_time = CalendarToPosix(calendar_time, {});
210
211 IPC::ResponseBuilder rb{ctx, 3};
212 rb.Push(RESULT_SUCCESS);
213 rb.PushRaw<u32>(1); // Amount of times we're returning
214 ctx.WriteBuffer(&posix_time, sizeof(u64));
172 } 215 }
173}; 216};
174 217
@@ -207,6 +250,55 @@ void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& c
207 LOG_DEBUG(Service_Time, "called"); 250 LOG_DEBUG(Service_Time, "called");
208} 251}
209 252
253void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
254 LOG_DEBUG(Service_Time, "called");
255
256 IPC::RequestParser rp{ctx};
257 auto unknown_u8 = rp.PopRaw<u8>();
258
259 ClockSnapshot clock_snapshot{};
260
261 const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>(
262 std::chrono::system_clock::now().time_since_epoch())
263 .count()};
264 CalendarTime calendar_time{};
265 const std::time_t time(time_since_epoch);
266 const std::tm* tm = std::localtime(&time);
267 if (tm == nullptr) {
268 IPC::ResponseBuilder rb{ctx, 2};
269 rb.Push(ResultCode(-1)); // TODO(ogniK): Find appropriate error code
270 return;
271 }
272 SteadyClockTimePoint steady_clock_time_point{CoreTiming::cyclesToMs(CoreTiming::GetTicks()) /
273 1000};
274
275 LocationName location_name{"UTC"};
276 calendar_time.year = tm->tm_year + 1900;
277 calendar_time.month = tm->tm_mon + 1;
278 calendar_time.day = tm->tm_mday;
279 calendar_time.hour = tm->tm_hour;
280 calendar_time.minute = tm->tm_min;
281 calendar_time.second = tm->tm_sec;
282 clock_snapshot.system_posix_time = time_since_epoch;
283 clock_snapshot.network_posix_time = time_since_epoch;
284 clock_snapshot.system_calendar_time = calendar_time;
285 clock_snapshot.network_calendar_time = calendar_time;
286
287 CalendarAdditionalInfo additional_info{};
288 PosixToCalendar(time_since_epoch, calendar_time, additional_info, {});
289
290 clock_snapshot.system_calendar_info = additional_info;
291 clock_snapshot.network_calendar_info = additional_info;
292
293 clock_snapshot.steady_clock_timepoint = steady_clock_time_point;
294 clock_snapshot.location_name = location_name;
295 clock_snapshot.clock_auto_adjustment_enabled = 1;
296 clock_snapshot.ipc_u8 = unknown_u8;
297 IPC::ResponseBuilder rb{ctx, 2};
298 rb.Push(RESULT_SUCCESS);
299 ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
300}
301
210Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) 302Module::Interface::Interface(std::shared_ptr<Module> time, const char* name)
211 : ServiceFramework(name), time(std::move(time)) {} 303 : ServiceFramework(name), time(std::move(time)) {}
212 304
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index 5659ecad3..77871ae07 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include "common/common_funcs.h"
8#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
9 10
10namespace Service::Time { 11namespace Service::Time {
@@ -53,6 +54,23 @@ struct SystemClockContext {
53static_assert(sizeof(SystemClockContext) == 0x20, 54static_assert(sizeof(SystemClockContext) == 0x20,
54 "SystemClockContext structure has incorrect size"); 55 "SystemClockContext structure has incorrect size");
55 56
57struct ClockSnapshot {
58 SystemClockContext user_clock_context;
59 SystemClockContext network_clock_context;
60 s64_le system_posix_time;
61 s64_le network_posix_time;
62 CalendarTime system_calendar_time;
63 CalendarTime network_calendar_time;
64 CalendarAdditionalInfo system_calendar_info;
65 CalendarAdditionalInfo network_calendar_info;
66 SteadyClockTimePoint steady_clock_timepoint;
67 LocationName location_name;
68 u8 clock_auto_adjustment_enabled;
69 u8 ipc_u8;
70 INSERT_PADDING_BYTES(2);
71};
72static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size");
73
56class Module final { 74class Module final {
57public: 75public:
58 class Interface : public ServiceFramework<Interface> { 76 class Interface : public ServiceFramework<Interface> {
@@ -65,6 +83,7 @@ public:
65 void GetStandardSteadyClock(Kernel::HLERequestContext& ctx); 83 void GetStandardSteadyClock(Kernel::HLERequestContext& ctx);
66 void GetTimeZoneService(Kernel::HLERequestContext& ctx); 84 void GetTimeZoneService(Kernel::HLERequestContext& ctx);
67 void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); 85 void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
86 void GetClockSnapshot(Kernel::HLERequestContext& ctx);
68 87
69 protected: 88 protected:
70 std::shared_ptr<Module> time; 89 std::shared_ptr<Module> time;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index bc8e402a8..c8e491fec 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -12,10 +12,12 @@
12#include "common/swap.h" 12#include "common/swap.h"
13#include "core/core.h" 13#include "core/core.h"
14#include "core/file_sys/control_metadata.h" 14#include "core/file_sys/control_metadata.h"
15#include "core/file_sys/romfs_factory.h"
15#include "core/file_sys/vfs_offset.h" 16#include "core/file_sys/vfs_offset.h"
16#include "core/gdbstub/gdbstub.h" 17#include "core/gdbstub/gdbstub.h"
17#include "core/hle/kernel/process.h" 18#include "core/hle/kernel/process.h"
18#include "core/hle/kernel/vm_manager.h" 19#include "core/hle/kernel/vm_manager.h"
20#include "core/hle/service/filesystem/filesystem.h"
19#include "core/loader/nro.h" 21#include "core/loader/nro.h"
20#include "core/loader/nso.h" 22#include "core/loader/nso.h"
21#include "core/memory.h" 23#include "core/memory.h"
@@ -208,6 +210,9 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
208 return ResultStatus::ErrorLoadingNRO; 210 return ResultStatus::ErrorLoadingNRO;
209 } 211 }
210 212
213 if (romfs != nullptr)
214 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
215
211 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); 216 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
212 217
213 is_loaded = true; 218 is_loaded = true;
diff --git a/src/core/settings.h b/src/core/settings.h
index b5aeff29b..84dc5050b 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -6,6 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <atomic> 8#include <atomic>
9#include <optional>
9#include <string> 10#include <string>
10#include "common/common_types.h" 11#include "common/common_types.h"
11 12
@@ -114,8 +115,9 @@ struct Values {
114 // System 115 // System
115 bool use_docked_mode; 116 bool use_docked_mode;
116 bool enable_nfc; 117 bool enable_nfc;
117 int current_user; 118 std::optional<u32> rng_seed;
118 int language_index; 119 s32 current_user;
120 s32 language_index;
119 121
120 // Controls 122 // Controls
121 std::array<std::string, NativeButton::NumButtons> buttons; 123 std::array<std::string, NativeButton::NumButtons> buttons;
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 5ae836aca..6de07ea56 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -53,6 +53,19 @@ void Maxwell3D::InitializeRegisterDefaults() {
53 regs.independent_blend[blend_index].factor_source_a = Regs::Blend::Factor::One; 53 regs.independent_blend[blend_index].factor_source_a = Regs::Blend::Factor::One;
54 regs.independent_blend[blend_index].factor_dest_a = Regs::Blend::Factor::Zero; 54 regs.independent_blend[blend_index].factor_dest_a = Regs::Blend::Factor::Zero;
55 } 55 }
56 regs.stencil_front_op_fail = Regs::StencilOp::Keep;
57 regs.stencil_front_op_zfail = Regs::StencilOp::Keep;
58 regs.stencil_front_op_zpass = Regs::StencilOp::Keep;
59 regs.stencil_front_func_func = Regs::ComparisonOp::Always;
60 regs.stencil_front_func_mask = 0xFFFFFFFF;
61 regs.stencil_front_mask = 0xFFFFFFFF;
62 regs.stencil_two_side_enable = 1;
63 regs.stencil_back_op_fail = Regs::StencilOp::Keep;
64 regs.stencil_back_op_zfail = Regs::StencilOp::Keep;
65 regs.stencil_back_op_zpass = Regs::StencilOp::Keep;
66 regs.stencil_back_func_func = Regs::ComparisonOp::Always;
67 regs.stencil_back_func_mask = 0xFFFFFFFF;
68 regs.stencil_back_mask = 0xFFFFFFFF;
56} 69}
57 70
58void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { 71void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
@@ -108,10 +121,8 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
108 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr); 121 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr);
109 } 122 }
110 123
111 u32 old = regs.reg_array[method]; 124 if (regs.reg_array[method] != value) {
112 regs.reg_array[method] = value; 125 regs.reg_array[method] = value;
113
114 if (value != old) {
115 if (method >= MAXWELL3D_REG_INDEX(vertex_attrib_format) && 126 if (method >= MAXWELL3D_REG_INDEX(vertex_attrib_format) &&
116 method < MAXWELL3D_REG_INDEX(vertex_attrib_format) + regs.vertex_attrib_format.size()) { 127 method < MAXWELL3D_REG_INDEX(vertex_attrib_format) + regs.vertex_attrib_format.size()) {
117 dirty_flags.vertex_attrib_format = true; 128 dirty_flags.vertex_attrib_format = true;
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 557795d0f..91ca57883 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -345,6 +345,14 @@ public:
345 Invert = 6, 345 Invert = 6,
346 IncrWrap = 7, 346 IncrWrap = 7,
347 DecrWrap = 8, 347 DecrWrap = 8,
348 KeepOGL = 0x1E00,
349 ZeroOGL = 0,
350 ReplaceOGL = 0x1E01,
351 IncrOGL = 0x1E02,
352 DecrOGL = 0x1E03,
353 InvertOGL = 0x150A,
354 IncrWrapOGL = 0x8507,
355 DecrWrapOGL = 0x8508,
348 }; 356 };
349 357
350 enum class MemoryLayout : u32 { 358 enum class MemoryLayout : u32 {
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index 0df3725c2..1482cdb40 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -5,7 +5,6 @@
5#include "core/frontend/emu_window.h" 5#include "core/frontend/emu_window.h"
6#include "core/settings.h" 6#include "core/settings.h"
7#include "video_core/renderer_base.h" 7#include "video_core/renderer_base.h"
8#include "video_core/renderer_opengl/gl_rasterizer.h"
9 8
10namespace VideoCore { 9namespace VideoCore {
11 10
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index a404764f5..54cc47a9b 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -140,7 +140,7 @@ void RasterizerOpenGL::SetupVertexFormat() {
140 if (is_cache_miss) { 140 if (is_cache_miss) {
141 VAO.Create(); 141 VAO.Create();
142 state.draw.vertex_array = VAO.handle; 142 state.draw.vertex_array = VAO.handle;
143 state.Apply(); 143 state.ApplyVertexBufferState();
144 144
145 // The index buffer binding is stored within the VAO. Stupid OpenGL, but easy to work 145 // The index buffer binding is stored within the VAO. Stupid OpenGL, but easy to work
146 // around. 146 // around.
@@ -182,7 +182,7 @@ void RasterizerOpenGL::SetupVertexFormat() {
182 } 182 }
183 } 183 }
184 state.draw.vertex_array = VAO.handle; 184 state.draw.vertex_array = VAO.handle;
185 state.Apply(); 185 state.ApplyVertexBufferState();
186} 186}
187 187
188void RasterizerOpenGL::SetupVertexBuffer() { 188void RasterizerOpenGL::SetupVertexBuffer() {
@@ -342,8 +342,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
342 index++; 342 index++;
343 } 343 }
344 } 344 }
345
346 state.Apply();
347} 345}
348 346
349std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { 347std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
@@ -412,8 +410,8 @@ void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
412 cached_pages.add({pages_interval, delta}); 410 cached_pages.add({pages_interval, delta});
413} 411}
414 412
415void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb, 413void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool using_color_fb,
416 bool preserve_contents, 414 bool using_depth_fb, bool preserve_contents,
417 std::optional<std::size_t> single_color_target) { 415 std::optional<std::size_t> single_color_target) {
418 MICROPROFILE_SCOPE(OpenGL_Framebuffer); 416 MICROPROFILE_SCOPE(OpenGL_Framebuffer);
419 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 417 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
@@ -429,9 +427,9 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
429 ASSERT_MSG(regs.rt_separate_frag_data == 0, "Unimplemented"); 427 ASSERT_MSG(regs.rt_separate_frag_data == 0, "Unimplemented");
430 428
431 // Bind the framebuffer surfaces 429 // Bind the framebuffer surfaces
432 state.draw.draw_framebuffer = framebuffer.handle; 430 current_state.draw.draw_framebuffer = framebuffer.handle;
433 state.Apply(); 431 current_state.ApplyFramebufferState();
434 state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0; 432 current_state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
435 433
436 if (using_color_fb) { 434 if (using_color_fb) {
437 if (single_color_target) { 435 if (single_color_target) {
@@ -509,10 +507,7 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
509 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 507 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
510 0); 508 0);
511 } 509 }
512 510 SyncViewport(current_state);
513 SyncViewport();
514
515 state.Apply();
516} 511}
517 512
518void RasterizerOpenGL::Clear() { 513void RasterizerOpenGL::Clear() {
@@ -525,22 +520,23 @@ void RasterizerOpenGL::Clear() {
525 bool use_stencil{}; 520 bool use_stencil{};
526 521
527 OpenGLState clear_state; 522 OpenGLState clear_state;
528 clear_state.draw.draw_framebuffer = framebuffer.handle;
529 clear_state.color_mask[0].red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
530 clear_state.color_mask[0].green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
531 clear_state.color_mask[0].blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
532 clear_state.color_mask[0].alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE;
533
534 if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || 523 if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B ||
535 regs.clear_buffers.A) { 524 regs.clear_buffers.A) {
536 use_color = true; 525 use_color = true;
537 } 526 }
527 if (use_color) {
528 clear_state.color_mask[0].red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
529 clear_state.color_mask[0].green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
530 clear_state.color_mask[0].blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
531 clear_state.color_mask[0].alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE;
532 }
538 if (regs.clear_buffers.Z) { 533 if (regs.clear_buffers.Z) {
539 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!"); 534 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!");
540 use_depth = true; 535 use_depth = true;
541 536
542 // Always enable the depth write when clearing the depth buffer. The depth write mask is 537 // Always enable the depth write when clearing the depth buffer. The depth write mask is
543 // ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to true. 538 // ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to
539 // true.
544 clear_state.depth.test_enabled = true; 540 clear_state.depth.test_enabled = true;
545 clear_state.depth.test_func = GL_ALWAYS; 541 clear_state.depth.test_func = GL_ALWAYS;
546 } 542 }
@@ -557,11 +553,8 @@ void RasterizerOpenGL::Clear() {
557 553
558 ScopeAcquireGLContext acquire_context{emu_window}; 554 ScopeAcquireGLContext acquire_context{emu_window};
559 555
560 ConfigureFramebuffers(use_color, use_depth || use_stencil, false, 556 ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false,
561 regs.clear_buffers.RT.Value()); 557 regs.clear_buffers.RT.Value());
562 // Copy the sRGB setting to the clear state to avoid problem with
563 // specific driver implementations
564 clear_state.framebuffer_srgb.enabled = state.framebuffer_srgb.enabled;
565 clear_state.Apply(); 558 clear_state.Apply();
566 559
567 if (use_color) { 560 if (use_color) {
@@ -587,7 +580,7 @@ void RasterizerOpenGL::DrawArrays() {
587 580
588 ScopeAcquireGLContext acquire_context{emu_window}; 581 ScopeAcquireGLContext acquire_context{emu_window};
589 582
590 ConfigureFramebuffers(); 583 ConfigureFramebuffers(state);
591 SyncColorMask(); 584 SyncColorMask();
592 SyncDepthTestState(); 585 SyncDepthTestState();
593 SyncStencilTestState(); 586 SyncStencilTestState();
@@ -608,7 +601,7 @@ void RasterizerOpenGL::DrawArrays() {
608 const bool is_indexed = accelerate_draw == AccelDraw::Indexed; 601 const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
609 602
610 state.draw.vertex_buffer = buffer_cache.GetHandle(); 603 state.draw.vertex_buffer = buffer_cache.GetHandle();
611 state.Apply(); 604 state.ApplyVertexBufferState();
612 605
613 std::size_t buffer_size = CalculateVertexArraysSize(); 606 std::size_t buffer_size = CalculateVertexArraysSize();
614 607
@@ -740,9 +733,9 @@ void RasterizerOpenGL::SamplerInfo::Create() {
740 glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER); 733 glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER);
741} 734}
742 735
743void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) { 736void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTextureInfo& info) {
744 const GLuint s = sampler.handle; 737 const GLuint s = sampler.handle;
745 738 const Tegra::Texture::TSCEntry& config = info.tsc;
746 if (mag_filter != config.mag_filter) { 739 if (mag_filter != config.mag_filter) {
747 mag_filter = config.mag_filter; 740 mag_filter = config.mag_filter;
748 glSamplerParameteri( 741 glSamplerParameteri(
@@ -793,6 +786,22 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
793 glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data()); 786 glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data());
794 } 787 }
795 } 788 }
789 if (info.tic.use_header_opt_control == 0) {
790 if (GLAD_GL_ARB_texture_filter_anisotropic) {
791 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY,
792 static_cast<float>(1 << info.tic.max_anisotropy.Value()));
793 } else if (GLAD_GL_EXT_texture_filter_anisotropic) {
794 glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT,
795 static_cast<float>(1 << info.tic.max_anisotropy.Value()));
796 }
797 glSamplerParameterf(s, GL_TEXTURE_MIN_LOD,
798 static_cast<float>(info.tic.res_min_mip_level.Value()));
799 glSamplerParameterf(s, GL_TEXTURE_MAX_LOD,
800 static_cast<float>(info.tic.res_max_mip_level.Value() == 0
801 ? 16
802 : info.tic.res_max_mip_level.Value()));
803 glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, info.tic.mip_lod_bias.Value() / 256.f);
804 }
796} 805}
797 806
798u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader, 807u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
@@ -890,7 +899,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
890 continue; 899 continue;
891 } 900 }
892 901
893 texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); 902 texture_samplers[current_bindpoint].SyncWithConfig(texture);
894 Surface surface = res_cache.GetTextureSurface(texture, entry); 903 Surface surface = res_cache.GetTextureSurface(texture, entry);
895 if (surface != nullptr) { 904 if (surface != nullptr) {
896 state.texture_units[current_bindpoint].texture = surface->Texture().handle; 905 state.texture_units[current_bindpoint].texture = surface->Texture().handle;
@@ -912,15 +921,15 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
912 return current_unit + static_cast<u32>(entries.size()); 921 return current_unit + static_cast<u32>(entries.size());
913} 922}
914 923
915void RasterizerOpenGL::SyncViewport() { 924void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
916 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 925 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
917 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { 926 for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
918 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()}; 927 const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()};
919 auto& viewport = state.viewports[i]; 928 auto& viewport = current_state.viewports[i];
920 viewport.x = viewport_rect.left; 929 viewport.x = viewport_rect.left;
921 viewport.y = viewport_rect.bottom; 930 viewport.y = viewport_rect.bottom;
922 viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth()); 931 viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth());
923 viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight()); 932 viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight());
924 viewport.depth_range_far = regs.viewport[i].depth_range_far; 933 viewport.depth_range_far = regs.viewport[i].depth_range_far;
925 viewport.depth_range_near = regs.viewport[i].depth_range_near; 934 viewport.depth_range_near = regs.viewport[i].depth_range_near;
926 } 935 }
@@ -985,9 +994,6 @@ void RasterizerOpenGL::SyncStencilTestState() {
985 return; 994 return;
986 } 995 }
987 996
988 // TODO(bunnei): Verify behavior when this is not set
989 ASSERT(regs.stencil_two_side_enable);
990
991 state.stencil.front.test_func = MaxwellToGL::ComparisonOp(regs.stencil_front_func_func); 997 state.stencil.front.test_func = MaxwellToGL::ComparisonOp(regs.stencil_front_func_func);
992 state.stencil.front.test_ref = regs.stencil_front_func_ref; 998 state.stencil.front.test_ref = regs.stencil_front_func_ref;
993 state.stencil.front.test_mask = regs.stencil_front_func_mask; 999 state.stencil.front.test_mask = regs.stencil_front_func_mask;
@@ -995,14 +1001,23 @@ void RasterizerOpenGL::SyncStencilTestState() {
995 state.stencil.front.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_front_op_zfail); 1001 state.stencil.front.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_front_op_zfail);
996 state.stencil.front.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_front_op_zpass); 1002 state.stencil.front.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_front_op_zpass);
997 state.stencil.front.write_mask = regs.stencil_front_mask; 1003 state.stencil.front.write_mask = regs.stencil_front_mask;
998 1004 if (regs.stencil_two_side_enable) {
999 state.stencil.back.test_func = MaxwellToGL::ComparisonOp(regs.stencil_back_func_func); 1005 state.stencil.back.test_func = MaxwellToGL::ComparisonOp(regs.stencil_back_func_func);
1000 state.stencil.back.test_ref = regs.stencil_back_func_ref; 1006 state.stencil.back.test_ref = regs.stencil_back_func_ref;
1001 state.stencil.back.test_mask = regs.stencil_back_func_mask; 1007 state.stencil.back.test_mask = regs.stencil_back_func_mask;
1002 state.stencil.back.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_fail); 1008 state.stencil.back.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_fail);
1003 state.stencil.back.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_zfail); 1009 state.stencil.back.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_zfail);
1004 state.stencil.back.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_back_op_zpass); 1010 state.stencil.back.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_back_op_zpass);
1005 state.stencil.back.write_mask = regs.stencil_back_mask; 1011 state.stencil.back.write_mask = regs.stencil_back_mask;
1012 } else {
1013 state.stencil.back.test_func = GL_ALWAYS;
1014 state.stencil.back.test_ref = 0;
1015 state.stencil.back.test_mask = 0xFFFFFFFF;
1016 state.stencil.back.write_mask = 0xFFFFFFFF;
1017 state.stencil.back.action_stencil_fail = GL_KEEP;
1018 state.stencil.back.action_depth_fail = GL_KEEP;
1019 state.stencil.back.action_depth_pass = GL_KEEP;
1020 }
1006} 1021}
1007 1022
1008void RasterizerOpenGL::SyncColorMask() { 1023void RasterizerOpenGL::SyncColorMask() {
@@ -1114,9 +1129,8 @@ void RasterizerOpenGL::CheckAlphaTests() {
1114 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1129 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1115 1130
1116 if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) { 1131 if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) {
1117 LOG_CRITICAL( 1132 LOG_CRITICAL(Render_OpenGL, "Alpha Testing is enabled with Multiple Render Targets, "
1118 Render_OpenGL, 1133 "this behavior is undefined.");
1119 "Alpha Testing is enabled with Multiple Render Targets, this behavior is undefined.");
1120 UNREACHABLE(); 1134 UNREACHABLE();
1121 } 1135 }
1122} 1136}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 5eee5f088..8ef0f6c12 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -88,7 +88,7 @@ private:
88 /// SamplerInfo struct. 88 /// SamplerInfo struct.
89 void Create(); 89 void Create();
90 /// Syncs the sampler object with the config, updating any necessary state. 90 /// Syncs the sampler object with the config, updating any necessary state.
91 void SyncWithConfig(const Tegra::Texture::TSCEntry& config); 91 void SyncWithConfig(const Tegra::Texture::FullTextureInfo& info);
92 92
93 private: 93 private:
94 Tegra::Texture::TextureFilter mag_filter; 94 Tegra::Texture::TextureFilter mag_filter;
@@ -109,8 +109,8 @@ private:
109 * @param preserve_contents If true, tries to preserve data from a previously used framebuffer. 109 * @param preserve_contents If true, tries to preserve data from a previously used framebuffer.
110 * @param single_color_target Specifies if a single color buffer target should be used. 110 * @param single_color_target Specifies if a single color buffer target should be used.
111 */ 111 */
112 void ConfigureFramebuffers(bool use_color_fb = true, bool using_depth_fb = true, 112 void ConfigureFramebuffers(OpenGLState& current_state, bool use_color_fb = true,
113 bool preserve_contents = true, 113 bool using_depth_fb = true, bool preserve_contents = true,
114 std::optional<std::size_t> single_color_target = {}); 114 std::optional<std::size_t> single_color_target = {});
115 115
116 /* 116 /*
@@ -134,7 +134,7 @@ private:
134 GLenum primitive_mode, u32 current_unit); 134 GLenum primitive_mode, u32 current_unit);
135 135
136 /// Syncs the viewport and depth range to match the guest state 136 /// Syncs the viewport and depth range to match the guest state
137 void SyncViewport(); 137 void SyncViewport(OpenGLState& current_state);
138 138
139 /// Syncs the clip enabled status to match the guest state 139 /// Syncs the clip enabled status to match the guest state
140 void SyncClipEnabled(); 140 void SyncClipEnabled();
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index c8864cce8..26711e6f7 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -314,6 +314,8 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
314 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB 314 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB
315 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5 315 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5
316 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5_SRGB 316 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5_SRGB
317 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_10X8
318 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_10X8_SRGB
317 319
318 // Depth formats 320 // Depth formats
319 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F 321 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
@@ -456,6 +458,8 @@ static constexpr GLConversionArray morton_to_gl_fns = {
456 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>, 458 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>,
457 MortonCopy<true, PixelFormat::ASTC_2D_5X5>, 459 MortonCopy<true, PixelFormat::ASTC_2D_5X5>,
458 MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>, 460 MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>,
461 MortonCopy<true, PixelFormat::ASTC_2D_10X8>,
462 MortonCopy<true, PixelFormat::ASTC_2D_10X8_SRGB>,
459 MortonCopy<true, PixelFormat::Z32F>, 463 MortonCopy<true, PixelFormat::Z32F>,
460 MortonCopy<true, PixelFormat::Z16>, 464 MortonCopy<true, PixelFormat::Z16>,
461 MortonCopy<true, PixelFormat::Z24S8>, 465 MortonCopy<true, PixelFormat::Z24S8>,
@@ -526,6 +530,8 @@ static constexpr GLConversionArray gl_to_morton_fns = {
526 nullptr, 530 nullptr,
527 nullptr, 531 nullptr,
528 nullptr, 532 nullptr,
533 nullptr,
534 nullptr,
529 MortonCopy<false, PixelFormat::Z32F>, 535 MortonCopy<false, PixelFormat::Z32F>,
530 MortonCopy<false, PixelFormat::Z16>, 536 MortonCopy<false, PixelFormat::Z16>,
531 MortonCopy<false, PixelFormat::Z24S8>, 537 MortonCopy<false, PixelFormat::Z24S8>,
@@ -580,7 +586,7 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
580 state.draw.draw_framebuffer = draw_fb_handle; 586 state.draw.draw_framebuffer = draw_fb_handle;
581 // Set sRGB enabled if the destination surfaces need it 587 // Set sRGB enabled if the destination surfaces need it
582 state.framebuffer_srgb.enabled = dst_params.srgb_conversion; 588 state.framebuffer_srgb.enabled = dst_params.srgb_conversion;
583 state.Apply(); 589 state.ApplyFramebufferState();
584 590
585 u32 buffers{}; 591 u32 buffers{};
586 592
@@ -709,18 +715,18 @@ static void FastCopySurface(const Surface& src_surface, const Surface& dst_surfa
709 715
710MICROPROFILE_DEFINE(OpenGL_CopySurface, "OpenGL", "CopySurface", MP_RGB(128, 192, 64)); 716MICROPROFILE_DEFINE(OpenGL_CopySurface, "OpenGL", "CopySurface", MP_RGB(128, 192, 64));
711static void CopySurface(const Surface& src_surface, const Surface& dst_surface, 717static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
712 GLuint copy_pbo_handle, GLenum src_attachment = 0, 718 const GLuint copy_pbo_handle, const GLenum src_attachment = 0,
713 GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { 719 const GLenum dst_attachment = 0, const std::size_t cubemap_face = 0) {
714 MICROPROFILE_SCOPE(OpenGL_CopySurface); 720 MICROPROFILE_SCOPE(OpenGL_CopySurface);
715 ASSERT_MSG(dst_attachment == 0, "Unimplemented"); 721 ASSERT_MSG(dst_attachment == 0, "Unimplemented");
716 722
717 const auto& src_params{src_surface->GetSurfaceParams()}; 723 const auto& src_params{src_surface->GetSurfaceParams()};
718 const auto& dst_params{dst_surface->GetSurfaceParams()}; 724 const auto& dst_params{dst_surface->GetSurfaceParams()};
719 725
720 auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type); 726 const auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type);
721 auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type); 727 const auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type);
722 728
723 std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes); 729 const std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);
724 730
725 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle); 731 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);
726 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); 732 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
@@ -744,13 +750,10 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
744 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during " 750 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
745 "reinterpretation but the texture is tiled."); 751 "reinterpretation but the texture is tiled.");
746 } 752 }
747 std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes; 753 const std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
748 std::vector<u8> data(remaining_size);
749 std::memcpy(data.data(), Memory::GetPointer(dst_params.addr + src_params.size_in_bytes),
750 data.size());
751 754
752 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size, 755 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,
753 data.data()); 756 Memory::GetPointer(dst_params.addr + src_params.size_in_bytes));
754 } 757 }
755 758
756 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 759 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
@@ -932,7 +935,9 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
932 case PixelFormat::ASTC_2D_8X8_SRGB: 935 case PixelFormat::ASTC_2D_8X8_SRGB:
933 case PixelFormat::ASTC_2D_8X5_SRGB: 936 case PixelFormat::ASTC_2D_8X5_SRGB:
934 case PixelFormat::ASTC_2D_5X4_SRGB: 937 case PixelFormat::ASTC_2D_5X4_SRGB:
935 case PixelFormat::ASTC_2D_5X5_SRGB: { 938 case PixelFormat::ASTC_2D_5X5_SRGB:
939 case PixelFormat::ASTC_2D_10X8:
940 case PixelFormat::ASTC_2D_10X8_SRGB: {
936 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. 941 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
937 u32 block_width{}; 942 u32 block_width{};
938 u32 block_height{}; 943 u32 block_height{};
@@ -967,7 +972,11 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelForm
967 case PixelFormat::ASTC_2D_4X4: 972 case PixelFormat::ASTC_2D_4X4:
968 case PixelFormat::ASTC_2D_8X8: 973 case PixelFormat::ASTC_2D_8X8:
969 case PixelFormat::ASTC_2D_4X4_SRGB: 974 case PixelFormat::ASTC_2D_4X4_SRGB:
970 case PixelFormat::ASTC_2D_8X8_SRGB: { 975 case PixelFormat::ASTC_2D_8X8_SRGB:
976 case PixelFormat::ASTC_2D_5X5:
977 case PixelFormat::ASTC_2D_5X5_SRGB:
978 case PixelFormat::ASTC_2D_10X8:
979 case PixelFormat::ASTC_2D_10X8_SRGB: {
971 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented", 980 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
972 static_cast<u32>(pixel_format)); 981 static_cast<u32>(pixel_format));
973 UNREACHABLE(); 982 UNREACHABLE();
@@ -1336,6 +1345,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1336 break; 1345 break;
1337 case SurfaceTarget::TextureCubemap: 1346 case SurfaceTarget::TextureCubemap:
1338 case SurfaceTarget::Texture3D: 1347 case SurfaceTarget::Texture3D:
1348 case SurfaceTarget::Texture2DArray:
1339 case SurfaceTarget::TextureCubeArray: 1349 case SurfaceTarget::TextureCubeArray:
1340 AccurateCopySurface(old_surface, new_surface); 1350 AccurateCopySurface(old_surface, new_surface);
1341 break; 1351 break;
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 9517285e5..2635f2b0c 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -427,7 +427,7 @@ void OpenGLState::ApplySamplers() const {
427 } 427 }
428} 428}
429 429
430void OpenGLState::Apply() const { 430void OpenGLState::ApplyFramebufferState() const {
431 // Framebuffer 431 // Framebuffer
432 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { 432 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
433 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); 433 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
@@ -435,7 +435,9 @@ void OpenGLState::Apply() const {
435 if (draw.draw_framebuffer != cur_state.draw.draw_framebuffer) { 435 if (draw.draw_framebuffer != cur_state.draw.draw_framebuffer) {
436 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer); 436 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer);
437 } 437 }
438}
438 439
440void OpenGLState::ApplyVertexBufferState() const {
439 // Vertex array 441 // Vertex array
440 if (draw.vertex_array != cur_state.draw.vertex_array) { 442 if (draw.vertex_array != cur_state.draw.vertex_array) {
441 glBindVertexArray(draw.vertex_array); 443 glBindVertexArray(draw.vertex_array);
@@ -445,7 +447,11 @@ void OpenGLState::Apply() const {
445 if (draw.vertex_buffer != cur_state.draw.vertex_buffer) { 447 if (draw.vertex_buffer != cur_state.draw.vertex_buffer) {
446 glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer); 448 glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer);
447 } 449 }
450}
448 451
452void OpenGLState::Apply() const {
453 ApplyFramebufferState();
454 ApplyVertexBufferState();
449 // Uniform buffer 455 // Uniform buffer
450 if (draw.uniform_buffer != cur_state.draw.uniform_buffer) { 456 if (draw.uniform_buffer != cur_state.draw.uniform_buffer) {
451 glBindBuffer(GL_UNIFORM_BUFFER, draw.uniform_buffer); 457 glBindBuffer(GL_UNIFORM_BUFFER, draw.uniform_buffer);
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index b8cf1f637..eacca0b9c 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -181,6 +181,10 @@ public:
181 } 181 }
182 /// Apply this state as the current OpenGL state 182 /// Apply this state as the current OpenGL state
183 void Apply() const; 183 void Apply() const;
184 /// Apply only the state afecting the framebuffer
185 void ApplyFramebufferState() const;
186 /// Apply only the state afecting the vertex buffer
187 void ApplyVertexBufferState() const;
184 /// Set the initial OpenGL state 188 /// Set the initial OpenGL state
185 static void ApplyDefaultState(); 189 static void ApplyDefaultState();
186 /// Resets any references to the given resource 190 /// Resets any references to the given resource
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 87d511c38..3ce2cc6d2 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -159,10 +159,8 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
159 } 159 }
160 } 160 }
161 } 161 }
162 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}", 162 LOG_ERROR(Render_OpenGL, "Unimplemented texture filter mode={}", static_cast<u32>(filter_mode));
163 static_cast<u32>(filter_mode)); 163 return GL_LINEAR;
164 UNREACHABLE();
165 return {};
166} 164}
167 165
168inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { 166inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
@@ -183,9 +181,8 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
183 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge: 181 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
184 return GL_MIRROR_CLAMP_TO_EDGE; 182 return GL_MIRROR_CLAMP_TO_EDGE;
185 } 183 }
186 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); 184 LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode));
187 UNREACHABLE(); 185 return GL_REPEAT;
188 return {};
189} 186}
190 187
191inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { 188inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
@@ -207,10 +204,9 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
207 case Tegra::Texture::DepthCompareFunc::Always: 204 case Tegra::Texture::DepthCompareFunc::Always:
208 return GL_ALWAYS; 205 return GL_ALWAYS;
209 } 206 }
210 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture depth compare function ={}", 207 LOG_ERROR(Render_OpenGL, "Unimplemented texture depth compare function ={}",
211 static_cast<u32>(func)); 208 static_cast<u32>(func));
212 UNREACHABLE(); 209 return GL_GREATER;
213 return {};
214} 210}
215 211
216inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { 212inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
@@ -226,9 +222,8 @@ inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
226 case Maxwell::Blend::Equation::Max: 222 case Maxwell::Blend::Equation::Max:
227 return GL_MAX; 223 return GL_MAX;
228 } 224 }
229 LOG_CRITICAL(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); 225 LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation));
230 UNREACHABLE(); 226 return GL_FUNC_ADD;
231 return {};
232} 227}
233 228
234inline GLenum BlendFunc(Maxwell::Blend::Factor factor) { 229inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
@@ -291,9 +286,8 @@ inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
291 case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: 286 case Maxwell::Blend::Factor::OneMinusConstantAlphaGL:
292 return GL_ONE_MINUS_CONSTANT_ALPHA; 287 return GL_ONE_MINUS_CONSTANT_ALPHA;
293 } 288 }
294 LOG_CRITICAL(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor)); 289 LOG_ERROR(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor));
295 UNREACHABLE(); 290 return GL_ZERO;
296 return {};
297} 291}
298 292
299inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) { 293inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) {
@@ -312,9 +306,8 @@ inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) {
312 case Tegra::Texture::SwizzleSource::OneFloat: 306 case Tegra::Texture::SwizzleSource::OneFloat:
313 return GL_ONE; 307 return GL_ONE;
314 } 308 }
315 LOG_CRITICAL(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source)); 309 LOG_ERROR(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source));
316 UNREACHABLE(); 310 return GL_ZERO;
317 return {};
318} 311}
319 312
320inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) { 313inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
@@ -344,33 +337,39 @@ inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) {
344 case Maxwell::ComparisonOp::AlwaysOld: 337 case Maxwell::ComparisonOp::AlwaysOld:
345 return GL_ALWAYS; 338 return GL_ALWAYS;
346 } 339 }
347 LOG_CRITICAL(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison)); 340 LOG_ERROR(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison));
348 UNREACHABLE(); 341 return GL_ALWAYS;
349 return {};
350} 342}
351 343
352inline GLenum StencilOp(Maxwell::StencilOp stencil) { 344inline GLenum StencilOp(Maxwell::StencilOp stencil) {
353 switch (stencil) { 345 switch (stencil) {
354 case Maxwell::StencilOp::Keep: 346 case Maxwell::StencilOp::Keep:
347 case Maxwell::StencilOp::KeepOGL:
355 return GL_KEEP; 348 return GL_KEEP;
356 case Maxwell::StencilOp::Zero: 349 case Maxwell::StencilOp::Zero:
350 case Maxwell::StencilOp::ZeroOGL:
357 return GL_ZERO; 351 return GL_ZERO;
358 case Maxwell::StencilOp::Replace: 352 case Maxwell::StencilOp::Replace:
353 case Maxwell::StencilOp::ReplaceOGL:
359 return GL_REPLACE; 354 return GL_REPLACE;
360 case Maxwell::StencilOp::Incr: 355 case Maxwell::StencilOp::Incr:
356 case Maxwell::StencilOp::IncrOGL:
361 return GL_INCR; 357 return GL_INCR;
362 case Maxwell::StencilOp::Decr: 358 case Maxwell::StencilOp::Decr:
359 case Maxwell::StencilOp::DecrOGL:
363 return GL_DECR; 360 return GL_DECR;
364 case Maxwell::StencilOp::Invert: 361 case Maxwell::StencilOp::Invert:
362 case Maxwell::StencilOp::InvertOGL:
365 return GL_INVERT; 363 return GL_INVERT;
366 case Maxwell::StencilOp::IncrWrap: 364 case Maxwell::StencilOp::IncrWrap:
365 case Maxwell::StencilOp::IncrWrapOGL:
367 return GL_INCR_WRAP; 366 return GL_INCR_WRAP;
368 case Maxwell::StencilOp::DecrWrap: 367 case Maxwell::StencilOp::DecrWrap:
368 case Maxwell::StencilOp::DecrWrapOGL:
369 return GL_DECR_WRAP; 369 return GL_DECR_WRAP;
370 } 370 }
371 LOG_CRITICAL(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil)); 371 LOG_ERROR(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil));
372 UNREACHABLE(); 372 return GL_KEEP;
373 return {};
374} 373}
375 374
376inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) { 375inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) {
@@ -380,9 +379,8 @@ inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) {
380 case Maxwell::Cull::FrontFace::CounterClockWise: 379 case Maxwell::Cull::FrontFace::CounterClockWise:
381 return GL_CCW; 380 return GL_CCW;
382 } 381 }
383 LOG_CRITICAL(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face)); 382 LOG_ERROR(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face));
384 UNREACHABLE(); 383 return GL_CCW;
385 return {};
386} 384}
387 385
388inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) { 386inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) {
@@ -394,9 +392,8 @@ inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) {
394 case Maxwell::Cull::CullFace::FrontAndBack: 392 case Maxwell::Cull::CullFace::FrontAndBack:
395 return GL_FRONT_AND_BACK; 393 return GL_FRONT_AND_BACK;
396 } 394 }
397 LOG_CRITICAL(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face)); 395 LOG_ERROR(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face));
398 UNREACHABLE(); 396 return GL_BACK;
399 return {};
400} 397}
401 398
402inline GLenum LogicOp(Maxwell::LogicOperation operation) { 399inline GLenum LogicOp(Maxwell::LogicOperation operation) {
@@ -434,9 +431,8 @@ inline GLenum LogicOp(Maxwell::LogicOperation operation) {
434 case Maxwell::LogicOperation::Set: 431 case Maxwell::LogicOperation::Set:
435 return GL_SET; 432 return GL_SET;
436 } 433 }
437 LOG_CRITICAL(Render_OpenGL, "Unimplemented logic operation={}", static_cast<u32>(operation)); 434 LOG_ERROR(Render_OpenGL, "Unimplemented logic operation={}", static_cast<u32>(operation));
438 UNREACHABLE(); 435 return GL_COPY;
439 return {};
440} 436}
441 437
442} // namespace MaxwellToGL 438} // namespace MaxwellToGL
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 051ad3964..9582dd2ca 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -306,6 +306,8 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
306 return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8; 306 return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8;
307 case Tegra::Texture::TextureFormat::ASTC_2D_8X5: 307 case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
308 return is_srgb ? PixelFormat::ASTC_2D_8X5_SRGB : PixelFormat::ASTC_2D_8X5; 308 return is_srgb ? PixelFormat::ASTC_2D_8X5_SRGB : PixelFormat::ASTC_2D_8X5;
309 case Tegra::Texture::TextureFormat::ASTC_2D_10X8:
310 return is_srgb ? PixelFormat::ASTC_2D_10X8_SRGB : PixelFormat::ASTC_2D_10X8;
309 case Tegra::Texture::TextureFormat::R16_G16: 311 case Tegra::Texture::TextureFormat::R16_G16:
310 switch (component_type) { 312 switch (component_type) {
311 case Tegra::Texture::ComponentType::FLOAT: 313 case Tegra::Texture::ComponentType::FLOAT:
@@ -453,6 +455,8 @@ bool IsPixelFormatASTC(PixelFormat format) {
453 case PixelFormat::ASTC_2D_5X5_SRGB: 455 case PixelFormat::ASTC_2D_5X5_SRGB:
454 case PixelFormat::ASTC_2D_8X8_SRGB: 456 case PixelFormat::ASTC_2D_8X8_SRGB:
455 case PixelFormat::ASTC_2D_8X5_SRGB: 457 case PixelFormat::ASTC_2D_8X5_SRGB:
458 case PixelFormat::ASTC_2D_10X8:
459 case PixelFormat::ASTC_2D_10X8_SRGB:
456 return true; 460 return true;
457 default: 461 default:
458 return false; 462 return false;
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index dfdb8d122..0dd3eb2e4 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -74,19 +74,21 @@ enum class PixelFormat {
74 ASTC_2D_5X4_SRGB = 56, 74 ASTC_2D_5X4_SRGB = 56,
75 ASTC_2D_5X5 = 57, 75 ASTC_2D_5X5 = 57,
76 ASTC_2D_5X5_SRGB = 58, 76 ASTC_2D_5X5_SRGB = 58,
77 ASTC_2D_10X8 = 59,
78 ASTC_2D_10X8_SRGB = 60,
77 79
78 MaxColorFormat, 80 MaxColorFormat,
79 81
80 // Depth formats 82 // Depth formats
81 Z32F = 59, 83 Z32F = 61,
82 Z16 = 60, 84 Z16 = 62,
83 85
84 MaxDepthFormat, 86 MaxDepthFormat,
85 87
86 // DepthStencil formats 88 // DepthStencil formats
87 Z24S8 = 61, 89 Z24S8 = 63,
88 S8Z24 = 62, 90 S8Z24 = 64,
89 Z32FS8 = 63, 91 Z32FS8 = 65,
90 92
91 MaxDepthStencilFormat, 93 MaxDepthStencilFormat,
92 94
@@ -193,6 +195,8 @@ static constexpr u32 GetCompressionFactor(PixelFormat format) {
193 4, // ASTC_2D_5X4_SRGB 195 4, // ASTC_2D_5X4_SRGB
194 4, // ASTC_2D_5X5 196 4, // ASTC_2D_5X5
195 4, // ASTC_2D_5X5_SRGB 197 4, // ASTC_2D_5X5_SRGB
198 4, // ASTC_2D_10X8
199 4, // ASTC_2D_10X8_SRGB
196 1, // Z32F 200 1, // Z32F
197 1, // Z16 201 1, // Z16
198 1, // Z24S8 202 1, // Z24S8
@@ -208,70 +212,72 @@ static constexpr u32 GetDefaultBlockWidth(PixelFormat format) {
208 if (format == PixelFormat::Invalid) 212 if (format == PixelFormat::Invalid)
209 return 0; 213 return 0;
210 constexpr std::array<u32, MaxPixelFormat> block_width_table = {{ 214 constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
211 1, // ABGR8U 215 1, // ABGR8U
212 1, // ABGR8S 216 1, // ABGR8S
213 1, // ABGR8UI 217 1, // ABGR8UI
214 1, // B5G6R5U 218 1, // B5G6R5U
215 1, // A2B10G10R10U 219 1, // A2B10G10R10U
216 1, // A1B5G5R5U 220 1, // A1B5G5R5U
217 1, // R8U 221 1, // R8U
218 1, // R8UI 222 1, // R8UI
219 1, // RGBA16F 223 1, // RGBA16F
220 1, // RGBA16U 224 1, // RGBA16U
221 1, // RGBA16UI 225 1, // RGBA16UI
222 1, // R11FG11FB10F 226 1, // R11FG11FB10F
223 1, // RGBA32UI 227 1, // RGBA32UI
224 4, // DXT1 228 4, // DXT1
225 4, // DXT23 229 4, // DXT23
226 4, // DXT45 230 4, // DXT45
227 4, // DXN1 231 4, // DXN1
228 4, // DXN2UNORM 232 4, // DXN2UNORM
229 4, // DXN2SNORM 233 4, // DXN2SNORM
230 4, // BC7U 234 4, // BC7U
231 4, // BC6H_UF16 235 4, // BC6H_UF16
232 4, // BC6H_SF16 236 4, // BC6H_SF16
233 4, // ASTC_2D_4X4 237 4, // ASTC_2D_4X4
234 1, // G8R8U 238 1, // G8R8U
235 1, // G8R8S 239 1, // G8R8S
236 1, // BGRA8 240 1, // BGRA8
237 1, // RGBA32F 241 1, // RGBA32F
238 1, // RG32F 242 1, // RG32F
239 1, // R32F 243 1, // R32F
240 1, // R16F 244 1, // R16F
241 1, // R16U 245 1, // R16U
242 1, // R16S 246 1, // R16S
243 1, // R16UI 247 1, // R16UI
244 1, // R16I 248 1, // R16I
245 1, // RG16 249 1, // RG16
246 1, // RG16F 250 1, // RG16F
247 1, // RG16UI 251 1, // RG16UI
248 1, // RG16I 252 1, // RG16I
249 1, // RG16S 253 1, // RG16S
250 1, // RGB32F 254 1, // RGB32F
251 1, // RGBA8_SRGB 255 1, // RGBA8_SRGB
252 1, // RG8U 256 1, // RG8U
253 1, // RG8S 257 1, // RG8S
254 1, // RG32UI 258 1, // RG32UI
255 1, // R32UI 259 1, // R32UI
256 8, // ASTC_2D_8X8 260 8, // ASTC_2D_8X8
257 8, // ASTC_2D_8X5 261 8, // ASTC_2D_8X5
258 5, // ASTC_2D_5X4 262 5, // ASTC_2D_5X4
259 1, // BGRA8_SRGB 263 1, // BGRA8_SRGB
260 4, // DXT1_SRGB 264 4, // DXT1_SRGB
261 4, // DXT23_SRGB 265 4, // DXT23_SRGB
262 4, // DXT45_SRGB 266 4, // DXT45_SRGB
263 4, // BC7U_SRGB 267 4, // BC7U_SRGB
264 4, // ASTC_2D_4X4_SRGB 268 4, // ASTC_2D_4X4_SRGB
265 8, // ASTC_2D_8X8_SRGB 269 8, // ASTC_2D_8X8_SRGB
266 8, // ASTC_2D_8X5_SRGB 270 8, // ASTC_2D_8X5_SRGB
267 5, // ASTC_2D_5X4_SRGB 271 5, // ASTC_2D_5X4_SRGB
268 5, // ASTC_2D_5X5 272 5, // ASTC_2D_5X5
269 5, // ASTC_2D_5X5_SRGB 273 5, // ASTC_2D_5X5_SRGB
270 1, // Z32F 274 10, // ASTC_2D_10X8
271 1, // Z16 275 10, // ASTC_2D_10X8_SRGB
272 1, // Z24S8 276 1, // Z32F
273 1, // S8Z24 277 1, // Z16
274 1, // Z32FS8 278 1, // Z24S8
279 1, // S8Z24
280 1, // Z32FS8
275 }}; 281 }};
276 ASSERT(static_cast<std::size_t>(format) < block_width_table.size()); 282 ASSERT(static_cast<std::size_t>(format) < block_width_table.size());
277 return block_width_table[static_cast<std::size_t>(format)]; 283 return block_width_table[static_cast<std::size_t>(format)];
@@ -341,6 +347,8 @@ static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
341 4, // ASTC_2D_5X4_SRGB 347 4, // ASTC_2D_5X4_SRGB
342 5, // ASTC_2D_5X5 348 5, // ASTC_2D_5X5
343 5, // ASTC_2D_5X5_SRGB 349 5, // ASTC_2D_5X5_SRGB
350 8, // ASTC_2D_10X8
351 8, // ASTC_2D_10X8_SRGB
344 1, // Z32F 352 1, // Z32F
345 1, // Z16 353 1, // Z16
346 1, // Z24S8 354 1, // Z24S8
@@ -416,6 +424,8 @@ static constexpr u32 GetFormatBpp(PixelFormat format) {
416 128, // ASTC_2D_5X4_SRGB 424 128, // ASTC_2D_5X4_SRGB
417 128, // ASTC_2D_5X5 425 128, // ASTC_2D_5X5
418 128, // ASTC_2D_5X5_SRGB 426 128, // ASTC_2D_5X5_SRGB
427 128, // ASTC_2D_10X8
428 128, // ASTC_2D_10X8_SRGB
419 32, // Z32F 429 32, // Z32F
420 16, // Z16 430 16, // Z16
421 32, // Z24S8 431 32, // Z24S8
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 3066abf61..a9d134d14 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -202,6 +202,8 @@ u32 BytesPerPixel(TextureFormat format) {
202 case TextureFormat::ASTC_2D_5X4: 202 case TextureFormat::ASTC_2D_5X4:
203 case TextureFormat::ASTC_2D_8X8: 203 case TextureFormat::ASTC_2D_8X8:
204 case TextureFormat::ASTC_2D_8X5: 204 case TextureFormat::ASTC_2D_8X5:
205 case TextureFormat::ASTC_2D_10X8:
206 case TextureFormat::ASTC_2D_5X5:
205 case TextureFormat::A8R8G8B8: 207 case TextureFormat::A8R8G8B8:
206 case TextureFormat::A2B10G10R10: 208 case TextureFormat::A2B10G10R10:
207 case TextureFormat::BF10GF11RF11: 209 case TextureFormat::BF10GF11RF11:
@@ -294,6 +296,8 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
294 case TextureFormat::BC6H_SF16: 296 case TextureFormat::BC6H_SF16:
295 case TextureFormat::ASTC_2D_4X4: 297 case TextureFormat::ASTC_2D_4X4:
296 case TextureFormat::ASTC_2D_8X8: 298 case TextureFormat::ASTC_2D_8X8:
299 case TextureFormat::ASTC_2D_5X5:
300 case TextureFormat::ASTC_2D_10X8:
297 case TextureFormat::A8R8G8B8: 301 case TextureFormat::A8R8G8B8:
298 case TextureFormat::A2B10G10R10: 302 case TextureFormat::A2B10G10R10:
299 case TextureFormat::A1B5G5R5: 303 case TextureFormat::A1B5G5R5:
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index d12d2ecb8..e199d019a 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -168,20 +168,29 @@ struct TICEntry {
168 168
169 // High 16 bits of the pitch value 169 // High 16 bits of the pitch value
170 BitField<0, 16, u32> pitch_high; 170 BitField<0, 16, u32> pitch_high;
171 171 BitField<26, 1, u32> use_header_opt_control;
172 BitField<27, 1, u32> depth_texture;
172 BitField<28, 4, u32> max_mip_level; 173 BitField<28, 4, u32> max_mip_level;
173 }; 174 };
174 union { 175 union {
175 BitField<0, 16, u32> width_minus_1; 176 BitField<0, 16, u32> width_minus_1;
176 BitField<22, 1, u32> srgb_conversion; 177 BitField<22, 1, u32> srgb_conversion;
177 BitField<23, 4, TextureType> texture_type; 178 BitField<23, 4, TextureType> texture_type;
179 BitField<29, 3, u32> border_size;
178 }; 180 };
179 union { 181 union {
180 BitField<0, 16, u32> height_minus_1; 182 BitField<0, 16, u32> height_minus_1;
181 BitField<16, 15, u32> depth_minus_1; 183 BitField<16, 15, u32> depth_minus_1;
182 }; 184 };
185 union {
186 BitField<6, 13, u32> mip_lod_bias;
187 BitField<27, 3, u32> max_anisotropy;
188 };
183 189
184 INSERT_PADDING_BYTES(8); 190 union {
191 BitField<0, 4, u32> res_min_mip_level;
192 BitField<4, 4, u32> res_max_mip_level;
193 };
185 194
186 GPUVAddr Address() const { 195 GPUVAddr Address() const {
187 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low); 196 return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low);
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index d4fd60a73..d3b7fa59d 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -134,6 +134,14 @@ void Config::ReadValues() {
134 Service::Account::MAX_USERS - 1); 134 Service::Account::MAX_USERS - 1);
135 135
136 Settings::values.language_index = qt_config->value("language_index", 1).toInt(); 136 Settings::values.language_index = qt_config->value("language_index", 1).toInt();
137
138 const auto enabled = qt_config->value("rng_seed_enabled", false).toBool();
139 if (enabled) {
140 Settings::values.rng_seed = qt_config->value("rng_seed", 0).toULongLong();
141 } else {
142 Settings::values.rng_seed = std::nullopt;
143 }
144
137 qt_config->endGroup(); 145 qt_config->endGroup();
138 146
139 qt_config->beginGroup("Miscellaneous"); 147 qt_config->beginGroup("Miscellaneous");
@@ -272,6 +280,10 @@ void Config::SaveValues() {
272 qt_config->setValue("current_user", Settings::values.current_user); 280 qt_config->setValue("current_user", Settings::values.current_user);
273 281
274 qt_config->setValue("language_index", Settings::values.language_index); 282 qt_config->setValue("language_index", Settings::values.language_index);
283
284 qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value());
285 qt_config->setValue("rng_seed", Settings::values.rng_seed.value_or(0));
286
275 qt_config->endGroup(); 287 qt_config->endGroup();
276 288
277 qt_config->beginGroup("Miscellaneous"); 289 qt_config->beginGroup("Miscellaneous");
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index b4b4a4a56..ab5d46492 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -137,6 +137,12 @@ ConfigureSystem::ConfigureSystem(QWidget* parent)
137 connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureSystem::DeleteUser); 137 connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureSystem::DeleteUser);
138 connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureSystem::SetUserImage); 138 connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureSystem::SetUserImage);
139 139
140 connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) {
141 ui->rng_seed_edit->setEnabled(checked);
142 if (!checked)
143 ui->rng_seed_edit->setText(QStringLiteral("00000000"));
144 });
145
140 scene = new QGraphicsScene; 146 scene = new QGraphicsScene;
141 ui->current_user_icon->setScene(scene); 147 ui->current_user_icon->setScene(scene);
142 148
@@ -155,6 +161,13 @@ void ConfigureSystem::setConfiguration() {
155 161
156 PopulateUserList(); 162 PopulateUserList();
157 UpdateCurrentUser(); 163 UpdateCurrentUser();
164
165 ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value());
166 ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.has_value());
167
168 const auto rng_seed =
169 QString("%1").arg(Settings::values.rng_seed.value_or(0), 8, 16, QLatin1Char{'0'}).toUpper();
170 ui->rng_seed_edit->setText(rng_seed);
158} 171}
159 172
160void ConfigureSystem::PopulateUserList() { 173void ConfigureSystem::PopulateUserList() {
@@ -195,6 +208,12 @@ void ConfigureSystem::applyConfiguration() {
195 return; 208 return;
196 209
197 Settings::values.language_index = ui->combo_language->currentIndex(); 210 Settings::values.language_index = ui->combo_language->currentIndex();
211
212 if (ui->rng_seed_checkbox->isChecked())
213 Settings::values.rng_seed = ui->rng_seed_edit->text().toULongLong(nullptr, 16);
214 else
215 Settings::values.rng_seed = std::nullopt;
216
198 Settings::Apply(); 217 Settings::Apply();
199} 218}
200 219
@@ -240,7 +259,7 @@ void ConfigureSystem::RefreshConsoleID() {
240 259
241void ConfigureSystem::SelectUser(const QModelIndex& index) { 260void ConfigureSystem::SelectUser(const QModelIndex& index) {
242 Settings::values.current_user = 261 Settings::values.current_user =
243 std::clamp<std::size_t>(index.row(), 0, profile_manager->GetUserCount() - 1); 262 std::clamp<s32>(index.row(), 0, static_cast<s32>(profile_manager->GetUserCount() - 1));
244 263
245 UpdateCurrentUser(); 264 UpdateCurrentUser();
246 265
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index 020b32a37..a91580893 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.ui
@@ -6,7 +6,7 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>360</width> 9 <width>366</width>
10 <height>483</height> 10 <height>483</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
@@ -22,98 +22,6 @@
22 <string>System Settings</string> 22 <string>System Settings</string>
23 </property> 23 </property>
24 <layout class="QGridLayout" name="gridLayout"> 24 <layout class="QGridLayout" name="gridLayout">
25 <item row="1" column="0">
26 <widget class="QLabel" name="label_language">
27 <property name="text">
28 <string>Language</string>
29 </property>
30 </widget>
31 </item>
32 <item row="0" column="0">
33 <widget class="QLabel" name="label_birthday">
34 <property name="text">
35 <string>Birthday</string>
36 </property>
37 </widget>
38 </item>
39 <item row="3" column="0">
40 <widget class="QLabel" name="label_console_id">
41 <property name="text">
42 <string>Console ID:</string>
43 </property>
44 </widget>
45 </item>
46 <item row="0" column="1">
47 <layout class="QHBoxLayout" name="horizontalLayout_birthday2">
48 <item>
49 <widget class="QComboBox" name="combo_birthmonth">
50 <item>
51 <property name="text">
52 <string>January</string>
53 </property>
54 </item>
55 <item>
56 <property name="text">
57 <string>February</string>
58 </property>
59 </item>
60 <item>
61 <property name="text">
62 <string>March</string>
63 </property>
64 </item>
65 <item>
66 <property name="text">
67 <string>April</string>
68 </property>
69 </item>
70 <item>
71 <property name="text">
72 <string>May</string>
73 </property>
74 </item>
75 <item>
76 <property name="text">
77 <string>June</string>
78 </property>
79 </item>
80 <item>
81 <property name="text">
82 <string>July</string>
83 </property>
84 </item>
85 <item>
86 <property name="text">
87 <string>August</string>
88 </property>
89 </item>
90 <item>
91 <property name="text">
92 <string>September</string>
93 </property>
94 </item>
95 <item>
96 <property name="text">
97 <string>October</string>
98 </property>
99 </item>
100 <item>
101 <property name="text">
102 <string>November</string>
103 </property>
104 </item>
105 <item>
106 <property name="text">
107 <string>December</string>
108 </property>
109 </item>
110 </widget>
111 </item>
112 <item>
113 <widget class="QComboBox" name="combo_birthday"/>
114 </item>
115 </layout>
116 </item>
117 <item row="1" column="1"> 25 <item row="1" column="1">
118 <widget class="QComboBox" name="combo_language"> 26 <widget class="QComboBox" name="combo_language">
119 <property name="toolTip"> 27 <property name="toolTip">
@@ -206,6 +114,13 @@
206 </item> 114 </item>
207 </widget> 115 </widget>
208 </item> 116 </item>
117 <item row="3" column="0">
118 <widget class="QLabel" name="label_console_id">
119 <property name="text">
120 <string>Console ID:</string>
121 </property>
122 </widget>
123 </item>
209 <item row="2" column="0"> 124 <item row="2" column="0">
210 <widget class="QLabel" name="label_sound"> 125 <widget class="QLabel" name="label_sound">
211 <property name="text"> 126 <property name="text">
@@ -213,6 +128,100 @@
213 </property> 128 </property>
214 </widget> 129 </widget>
215 </item> 130 </item>
131 <item row="0" column="0">
132 <widget class="QLabel" name="label_birthday">
133 <property name="text">
134 <string>Birthday</string>
135 </property>
136 </widget>
137 </item>
138 <item row="0" column="1">
139 <layout class="QHBoxLayout" name="horizontalLayout_birthday2">
140 <item>
141 <widget class="QComboBox" name="combo_birthmonth">
142 <item>
143 <property name="text">
144 <string>January</string>
145 </property>
146 </item>
147 <item>
148 <property name="text">
149 <string>February</string>
150 </property>
151 </item>
152 <item>
153 <property name="text">
154 <string>March</string>
155 </property>
156 </item>
157 <item>
158 <property name="text">
159 <string>April</string>
160 </property>
161 </item>
162 <item>
163 <property name="text">
164 <string>May</string>
165 </property>
166 </item>
167 <item>
168 <property name="text">
169 <string>June</string>
170 </property>
171 </item>
172 <item>
173 <property name="text">
174 <string>July</string>
175 </property>
176 </item>
177 <item>
178 <property name="text">
179 <string>August</string>
180 </property>
181 </item>
182 <item>
183 <property name="text">
184 <string>September</string>
185 </property>
186 </item>
187 <item>
188 <property name="text">
189 <string>October</string>
190 </property>
191 </item>
192 <item>
193 <property name="text">
194 <string>November</string>
195 </property>
196 </item>
197 <item>
198 <property name="text">
199 <string>December</string>
200 </property>
201 </item>
202 </widget>
203 </item>
204 <item>
205 <widget class="QComboBox" name="combo_birthday"/>
206 </item>
207 </layout>
208 </item>
209 <item row="3" column="1">
210 <widget class="QPushButton" name="button_regenerate_console_id">
211 <property name="sizePolicy">
212 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
213 <horstretch>0</horstretch>
214 <verstretch>0</verstretch>
215 </sizepolicy>
216 </property>
217 <property name="layoutDirection">
218 <enum>Qt::RightToLeft</enum>
219 </property>
220 <property name="text">
221 <string>Regenerate</string>
222 </property>
223 </widget>
224 </item>
216 <item row="2" column="1"> 225 <item row="2" column="1">
217 <widget class="QComboBox" name="combo_sound"> 226 <widget class="QComboBox" name="combo_sound">
218 <item> 227 <item>
@@ -232,19 +241,38 @@
232 </item> 241 </item>
233 </widget> 242 </widget>
234 </item> 243 </item>
235 <item row="3" column="1"> 244 <item row="1" column="0">
236 <widget class="QPushButton" name="button_regenerate_console_id"> 245 <widget class="QLabel" name="label_language">
246 <property name="text">
247 <string>Language</string>
248 </property>
249 </widget>
250 </item>
251 <item row="4" column="0">
252 <widget class="QCheckBox" name="rng_seed_checkbox">
253 <property name="text">
254 <string>RNG Seed</string>
255 </property>
256 </widget>
257 </item>
258 <item row="4" column="1">
259 <widget class="QLineEdit" name="rng_seed_edit">
237 <property name="sizePolicy"> 260 <property name="sizePolicy">
238 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 261 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
239 <horstretch>0</horstretch> 262 <horstretch>0</horstretch>
240 <verstretch>0</verstretch> 263 <verstretch>0</verstretch>
241 </sizepolicy> 264 </sizepolicy>
242 </property> 265 </property>
243 <property name="layoutDirection"> 266 <property name="font">
244 <enum>Qt::RightToLeft</enum> 267 <font>
268 <family>Lucida Console</family>
269 </font>
245 </property> 270 </property>
246 <property name="text"> 271 <property name="inputMask">
247 <string>Regenerate</string> 272 <string notr="true">HHHHHHHH</string>
273 </property>
274 <property name="maxLength">
275 <number>8</number>
248 </property> 276 </property>
249 </widget> 277 </widget>
250 </item> 278 </item>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 74a44be37..131ad19de 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -933,7 +933,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
933 const auto full = res == "Full"; 933 const auto full = res == "Full";
934 const auto entry_size = CalculateRomFSEntrySize(extracted, full); 934 const auto entry_size = CalculateRomFSEntrySize(extracted, full);
935 935
936 QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, entry_size, this); 936 QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0,
937 static_cast<s32>(entry_size), this);
937 progress.setWindowModality(Qt::WindowModal); 938 progress.setWindowModality(Qt::WindowModal);
938 progress.setMinimumDuration(100); 939 progress.setMinimumDuration(100);
939 940
@@ -1621,7 +1622,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
1621 return; 1622 return;
1622 } 1623 }
1623 1624
1624 if (ui.action_Fullscreen->isChecked()) { 1625 if (!ui.action_Fullscreen->isChecked()) {
1625 UISettings::values.geometry = saveGeometry(); 1626 UISettings::values.geometry = saveGeometry();
1626 UISettings::values.renderwindow_geometry = render_window->saveGeometry(); 1627 UISettings::values.renderwindow_geometry = render_window->saveGeometry();
1627 } 1628 }
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 28cf269e7..75e96387f 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -70,6 +70,8 @@
70 <addaction name="separator"/> 70 <addaction name="separator"/>
71 <addaction name="action_Load_Amiibo"/> 71 <addaction name="action_Load_Amiibo"/>
72 <addaction name="separator"/> 72 <addaction name="separator"/>
73 <addaction name="action_Open_yuzu_Folder"/>
74 <addaction name="separator"/>
73 <addaction name="action_Exit"/> 75 <addaction name="action_Exit"/>
74 </widget> 76 </widget>
75 <widget class="QMenu" name="menu_Emulation"> 77 <widget class="QMenu" name="menu_Emulation">
@@ -110,7 +112,6 @@
110 <string>&amp;Help</string> 112 <string>&amp;Help</string>
111 </property> 113 </property>
112 <addaction name="action_Report_Compatibility"/> 114 <addaction name="action_Report_Compatibility"/>
113 <addaction name="action_Open_yuzu_Folder" />
114 <addaction name="separator"/> 115 <addaction name="separator"/>
115 <addaction name="action_About"/> 116 <addaction name="action_About"/>
116 </widget> 117 </widget>
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index b456266a6..f3134d4cb 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -132,6 +132,13 @@ void Config::ReadValues() {
132 Settings::values.current_user = std::clamp<int>( 132 Settings::values.current_user = std::clamp<int>(
133 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); 133 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);
134 134
135 const auto enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false);
136 if (enabled) {
137 Settings::values.rng_seed = sdl2_config->GetInteger("System", "rng_seed", 0);
138 } else {
139 Settings::values.rng_seed = std::nullopt;
140 }
141
135 // Miscellaneous 142 // Miscellaneous
136 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 143 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
137 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); 144 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index e0b223cd6..dd6644d79 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -178,6 +178,11 @@ use_docked_mode =
178# 1 (default): Yes, 0 : No 178# 1 (default): Yes, 0 : No
179enable_nfc = 179enable_nfc =
180 180
181# Sets the seed for the RNG generator built into the switch
182# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
183rng_seed_enabled =
184rng_seed =
185
181# Sets the account username, max length is 32 characters 186# Sets the account username, max length is 32 characters
182# yuzu (default) 187# yuzu (default)
183username = yuzu 188username = yuzu