diff options
Diffstat (limited to 'src')
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. | ||
| 37 | std::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 | |||
| 54 | std::string StringFromBuffer(const std::vector<u8>& data) { | 35 | std::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 | ||
| 78 | bool 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 | |||
| 101 | bool 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 | |||
| 112 | std::string StringFromBool(bool value) { | 59 | std::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 |
| 20 | std::string ToUpper(std::string str); | 18 | std::string ToUpper(std::string str); |
| 21 | 19 | ||
| 22 | std::string ArrayToString(const u8* data, std::size_t size, int line_len = 20, bool spaces = true); | ||
| 23 | |||
| 24 | std::string StringFromBuffer(const std::vector<u8>& data); | 20 | std::string StringFromBuffer(const std::vector<u8>& data); |
| 25 | 21 | ||
| 26 | std::string StripSpaces(const std::string& s); | 22 | std::string StripSpaces(const std::string& s); |
| 27 | std::string StripQuotes(const std::string& s); | 23 | std::string StripQuotes(const std::string& s); |
| 28 | 24 | ||
| 29 | // Thousand separator. Turns 12345678 into 12,345,678 | ||
| 30 | template <typename I> | ||
| 31 | std::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 | |||
| 43 | std::string StringFromBool(bool value); | 25 | std::string StringFromBool(bool value); |
| 44 | 26 | ||
| 45 | bool TryParse(const std::string& str, bool* output); | ||
| 46 | bool TryParse(const std::string& str, u32* output); | ||
| 47 | |||
| 48 | template <typename N> | ||
| 49 | static 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 | |||
| 60 | std::string TabsToSpaces(int tab_size, std::string in); | 27 | std::string TabsToSpaces(int tab_size, std::string in); |
| 61 | 28 | ||
| 62 | void SplitString(const std::string& str, char delim, std::vector<std::string>& output); | 29 | void 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 | |||
| 70 | std::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 | ||
| 85 | private: | 86 | private: |
| 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 | ||
| 86 | std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, | 86 | VirtualDir 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 | ||
| 90 | std::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 | |||
| 104 | std::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 | ||
| 21 | namespace Kernel { | 20 | namespace 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 | ||
| 243 | ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { | 247 | ResultVal<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 | ||
| 279 | ResultCode Process::HeapFree(VAddr target, u32 size) { | 251 | ResultCode 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 | ||
| 298 | ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | 255 | ResultCode 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 | ||
| 323 | ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) { | 259 | ResultCode 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 | ||
| 120 | class Process final : public Object { | 121 | class Process final : public Object { |
| 121 | public: | 122 | public: |
| 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 | ||
| 38 | namespace Kernel { | 39 | namespace Kernel { |
| 39 | namespace { | 40 | namespace { |
| @@ -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 | ||
| 246 | ResultVal<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 | |||
| 282 | ResultCode 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 | |||
| 301 | ResultCode 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 | |||
| 246 | void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) { | 325 | void 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 | ||
| 497 | u64 VMManager::GetTotalHeapUsage() const { | 576 | u64 VMManager::GetTotalHeapUsage() const { |
| 498 | LOG_WARNING(Kernel, "(STUBBED) called"); | 577 | return heap_used; |
| 499 | return 0x0; | ||
| 500 | } | 578 | } |
| 501 | 579 | ||
| 502 | VAddr VMManager::GetAddressSpaceBaseAddress() const { | 580 | VAddr 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 | ||
| 45 | std::string UUID::Format() const { | ||
| 46 | return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); | ||
| 47 | } | ||
| 48 | |||
| 49 | std::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 | |||
| 42 | ProfileManager::ProfileManager() { | 58 | ProfileManager::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 | }; |
| 58 | static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); | 49 | static_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 | ||
| 117 | class IAudioDevice final : public ServiceFramework<IAudioDevice> { | 138 | class 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 | ||
| 312 | ResultVal<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 | |||
| 312 | ResultVal<FileSys::VirtualDir> OpenSDMC() { | 322 | ResultVal<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); |
| 46 | ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, | 46 | ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, |
| 47 | FileSys::SaveDataDescriptor save_struct); | 47 | FileSys::SaveDataDescriptor save_struct); |
| 48 | ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); | ||
| 48 | ResultVal<FileSys::VirtualDir> OpenSDMC(); | 49 | ResultVal<FileSys::VirtualDir> OpenSDMC(); |
| 49 | 50 | ||
| 50 | std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); | 51 | std::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 | ||
| 455 | class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { | ||
| 456 | public: | ||
| 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 | |||
| 489 | private: | ||
| 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 | |||
| 454 | FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | 594 | FSP_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 | ||
| 763 | void 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 | |||
| 621 | void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | 772 | void 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 | ||
| 248 | class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> { | 310 | class 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 | */ |
| 83 | static 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 | ||
| 14 | namespace Service::SPL { | 18 | namespace Service::SPL { |
| 15 | 19 | ||
| 16 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | 20 | Module::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 | ||
| 19 | Module::Interface::~Interface() = default; | 24 | Module::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 | ||
| 9 | namespace Service::SPL { | 10 | namespace 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 | ||
| 16 | namespace Service::Time { | 16 | namespace Service::Time { |
| 17 | 17 | ||
| 18 | static 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 | |||
| 41 | static 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 | |||
| 18 | class ISystemClock final : public ServiceFramework<ISystemClock> { | 56 | class ISystemClock final : public ServiceFramework<ISystemClock> { |
| 19 | public: | 57 | public: |
| 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 | ||
| 253 | void 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 | |||
| 210 | Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) | 302 | Module::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 | ||
| 10 | namespace Service::Time { | 11 | namespace Service::Time { |
| @@ -53,6 +54,23 @@ struct SystemClockContext { | |||
| 53 | static_assert(sizeof(SystemClockContext) == 0x20, | 54 | static_assert(sizeof(SystemClockContext) == 0x20, |
| 54 | "SystemClockContext structure has incorrect size"); | 55 | "SystemClockContext structure has incorrect size"); |
| 55 | 56 | ||
| 57 | struct 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 | }; | ||
| 72 | static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size"); | ||
| 73 | |||
| 56 | class Module final { | 74 | class Module final { |
| 57 | public: | 75 | public: |
| 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 | ||
| 58 | void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { | 71 | void 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 | ||
| 10 | namespace VideoCore { | 9 | namespace 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 | ||
| 188 | void RasterizerOpenGL::SetupVertexBuffer() { | 188 | void 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 | ||
| 349 | std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { | 347 | std::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 | ||
| 415 | void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb, | 413 | void 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 | ||
| 518 | void RasterizerOpenGL::Clear() { | 513 | void 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 | ||
| 743 | void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) { | 736 | void 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 | ||
| 798 | u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader, | 807 | u32 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 | ||
| 915 | void RasterizerOpenGL::SyncViewport() { | 924 | void 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 | ||
| 1008 | void RasterizerOpenGL::SyncColorMask() { | 1023 | void 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 | ||
| 710 | MICROPROFILE_DEFINE(OpenGL_CopySurface, "OpenGL", "CopySurface", MP_RGB(128, 192, 64)); | 716 | MICROPROFILE_DEFINE(OpenGL_CopySurface, "OpenGL", "CopySurface", MP_RGB(128, 192, 64)); |
| 711 | static void CopySurface(const Surface& src_surface, const Surface& dst_surface, | 717 | static 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 | ||
| 430 | void OpenGLState::Apply() const { | 430 | void 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 | ||
| 440 | void 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 | ||
| 452 | void 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 | ||
| 168 | inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { | 166 | inline 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 | ||
| 191 | inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { | 188 | inline 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 | ||
| 216 | inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { | 212 | inline 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 | ||
| 234 | inline GLenum BlendFunc(Maxwell::Blend::Factor factor) { | 229 | inline 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 | ||
| 299 | inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) { | 293 | inline 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 | ||
| 320 | inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) { | 313 | inline 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 | ||
| 352 | inline GLenum StencilOp(Maxwell::StencilOp stencil) { | 344 | inline 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 | ||
| 376 | inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) { | 375 | inline 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 | ||
| 388 | inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) { | 386 | inline 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 | ||
| 402 | inline GLenum LogicOp(Maxwell::LogicOperation operation) { | 399 | inline 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 | ||
| 160 | void ConfigureSystem::PopulateUserList() { | 173 | void 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 | ||
| 241 | void ConfigureSystem::SelectUser(const QModelIndex& index) { | 260 | void 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>&Help</string> | 112 | <string>&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 |
| 179 | enable_nfc = | 179 | enable_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 | ||
| 183 | rng_seed_enabled = | ||
| 184 | rng_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) |
| 183 | username = yuzu | 188 | username = yuzu |