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 { std::array<u8, 24> output_mix_buffers; u32_le mix_buffer_count; u32_le sample_rate; // Stored in the aux buffer currently - u32_le sampe_count; + u32_le sample_count; u64_le send_buffer_info; u64_le send_buffer_base; 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 @@ #include <algorithm> #include <cctype> -#include <cerrno> #include <codecvt> -#include <cstdio> #include <cstdlib> -#include <cstring> +#include <locale> +#include <sstream> #include "common/common_paths.h" #include "common/logging/log.h" #include "common/string_util.h" @@ -33,24 +32,6 @@ std::string ToUpper(std::string str) { return str; } -// For Debugging. Read out an u8 array. -std::string ArrayToString(const u8* data, std::size_t size, int line_len, bool spaces) { - std::ostringstream oss; - oss << std::setfill('0') << std::hex; - - for (int line = 0; size; ++data, --size) { - oss << std::setw(2) << (int)*data; - - if (line_len == ++line) { - oss << '\n'; - line = 0; - } else if (spaces) - oss << ' '; - } - - return oss.str(); -} - std::string StringFromBuffer(const std::vector<u8>& data) { return std::string(data.begin(), std::find(data.begin(), data.end(), '\0')); } @@ -75,40 +56,6 @@ std::string StripQuotes(const std::string& s) { return s; } -bool TryParse(const std::string& str, u32* const output) { - char* endptr = nullptr; - - // Reset errno to a value other than ERANGE - errno = 0; - - unsigned long value = strtoul(str.c_str(), &endptr, 0); - - if (!endptr || *endptr) - return false; - - if (errno == ERANGE) - return false; - -#if ULONG_MAX > UINT_MAX - if (value >= 0x100000000ull && value <= 0xFFFFFFFF00000000ull) - return false; -#endif - - *output = static_cast<u32>(value); - return true; -} - -bool TryParse(const std::string& str, bool* const output) { - if ("1" == str || "true" == ToLower(str)) - *output = true; - else if ("0" == str || "false" == ToLower(str)) - *output = false; - else - return false; - - return true; -} - std::string StringFromBool(bool value) { return value ? "True" : "False"; } 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 @@ #pragma once #include <cstddef> -#include <iomanip> -#include <sstream> #include <string> #include <vector> #include "common/common_types.h" @@ -19,44 +17,13 @@ std::string ToLower(std::string str); /// Make a string uppercase std::string ToUpper(std::string str); -std::string ArrayToString(const u8* data, std::size_t size, int line_len = 20, bool spaces = true); - std::string StringFromBuffer(const std::vector<u8>& data); std::string StripSpaces(const std::string& s); std::string StripQuotes(const std::string& s); -// Thousand separator. Turns 12345678 into 12,345,678 -template <typename I> -std::string ThousandSeparate(I value, int spaces = 0) { - std::ostringstream oss; - -// std::locale("") seems to be broken on many platforms -#if defined _WIN32 || (defined __linux__ && !defined __clang__) - oss.imbue(std::locale("")); -#endif - oss << std::setw(spaces) << value; - - return oss.str(); -} - std::string StringFromBool(bool value); -bool TryParse(const std::string& str, bool* output); -bool TryParse(const std::string& str, u32* output); - -template <typename N> -static bool TryParse(const std::string& str, N* const output) { - std::istringstream iss(str); - - N tmp = 0; - if (iss >> tmp) { - *output = tmp; - return true; - } else - return false; -} - std::string TabsToSpaces(int tab_size, std::string in); 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 { return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), raw->version_string.size()); } + +std::vector<u8> NACP::GetRawBytes() const { + std::vector<u8> out(sizeof(RawNACP)); + std::memcpy(out.data(), raw.get(), sizeof(RawNACP)); + return out; +} } // 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: u64 GetTitleId() const; u64 GetDLCBaseTitleId() const; std::string GetVersionString() const; + std::vector<u8> GetRawBytes() const; private: 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 return MakeResult<VirtualDir>(std::move(out)); } -std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, - u128 user_id, u64 save_id) { - // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should - // be interpreted as the title id of the current process. - if (type == SaveDataType::SaveData && title_id == 0) - title_id = Core::CurrentProcess()->GetTitleID(); - - std::string out; +VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) const { + return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space)); +} +std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) { switch (space) { case SaveDataSpaceId::NandSystem: - out = "/system/"; - break; + return "/system/"; case SaveDataSpaceId::NandUser: - out = "/user/"; - break; + return "/user/"; case SaveDataSpaceId::TemporaryStorage: - out = "/temp/"; - break; + return "/temp/"; default: ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); + return "/unrecognized/"; ///< To prevent corruption when ignoring asserts. } +} + +std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, + u128 user_id, u64 save_id) { + // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should + // be interpreted as the title id of the current process. + if (type == SaveDataType::SaveData && title_id == 0) + title_id = Core::CurrentProcess()->GetTitleID(); + + std::string out = GetSaveDataSpaceIdPath(space); switch (type) { 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: ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta); + VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; + + static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, u64 save_id); 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 @@ #include <algorithm> #include <memory> #include "common/assert.h" -#include "common/common_funcs.h" #include "common/logging/log.h" #include "core/core.h" #include "core/file_sys/program_metadata.h" -#include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" @@ -17,6 +15,7 @@ #include "core/hle/kernel/thread.h" #include "core/hle/kernel/vm_manager.h" #include "core/memory.h" +#include "core/settings.h" namespace Kernel { @@ -35,6 +34,11 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { process->process_id = kernel.CreateNewProcessID(); process->svc_access_mask.set(); + std::mt19937 rng(Settings::values.rng_seed.value_or(0)); + std::uniform_int_distribution<u64> distribution; + std::generate(process->random_entropy.begin(), process->random_entropy.end(), + [&] { return distribution(rng); }); + kernel.AppendNewProcess(process); return process; } @@ -241,83 +245,15 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) { } ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { - if (target < vm_manager.GetHeapRegionBaseAddress() || - target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) { - return ERR_INVALID_ADDRESS; - } - - if (heap_memory == nullptr) { - // Initialize heap - heap_memory = std::make_shared<std::vector<u8>>(); - heap_start = heap_end = target; - } else { - vm_manager.UnmapRange(heap_start, heap_end - heap_start); - } - - // If necessary, expand backing vector to cover new heap extents. - if (target < heap_start) { - heap_memory->insert(begin(*heap_memory), heap_start - target, 0); - heap_start = target; - vm_manager.RefreshMemoryBlockMappings(heap_memory.get()); - } - if (target + size > heap_end) { - heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0); - heap_end = target + size; - vm_manager.RefreshMemoryBlockMappings(heap_memory.get()); - } - ASSERT(heap_end - heap_start == heap_memory->size()); - - CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, heap_memory, target - heap_start, - size, MemoryState::Heap)); - vm_manager.Reprotect(vma, perms); - - heap_used = size; - - return MakeResult<VAddr>(heap_end - size); + return vm_manager.HeapAllocate(target, size, perms); } ResultCode Process::HeapFree(VAddr target, u32 size) { - if (target < vm_manager.GetHeapRegionBaseAddress() || - target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) { - return ERR_INVALID_ADDRESS; - } - - if (size == 0) { - return RESULT_SUCCESS; - } - - ResultCode result = vm_manager.UnmapRange(target, size); - if (result.IsError()) - return result; - - heap_used -= size; - - return RESULT_SUCCESS; + return vm_manager.HeapFree(target, size); } ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { - auto vma = vm_manager.FindVMA(src_addr); - - ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address"); - ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address"); - - // The returned VMA might be a bigger one encompassing the desired address. - auto vma_offset = src_addr - vma->first; - ASSERT_MSG(vma_offset + size <= vma->second.size, - "Shared memory exceeds bounds of mapped block"); - - const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block; - std::size_t backing_block_offset = vma->second.offset + vma_offset; - - CASCADE_RESULT(auto new_vma, - vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, - MemoryState::Mapped)); - // Protect mirror with permissions from old region - vm_manager.Reprotect(new_vma, vma->second.permissions); - // Remove permissions from old region - vm_manager.Reprotect(vma, VMAPermission::None); - - return RESULT_SUCCESS; + return vm_manager.MirrorMemory(dst_addr, src_addr, size); } 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 @@ #include <bitset> #include <cstddef> #include <memory> +#include <random> #include <string> #include <vector> #include <boost/container/static_vector.hpp> @@ -119,6 +120,8 @@ struct CodeSet final { class Process final : public Object { public: + static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; + static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name); std::string GetTypeName() const override { @@ -212,6 +215,11 @@ public: total_process_running_time_ticks += ticks; } + /// Gets 8 bytes of random data for svcGetInfo RandomEntropy + u64 GetRandomEntropy(std::size_t index) const { + return random_entropy.at(index); + } + /** * Loads process-specifics configuration info with metadata provided * by an executable. @@ -292,17 +300,6 @@ private: u32 allowed_thread_priority_mask = 0xFFFFFFFF; u32 is_virtual_address_memory_enabled = 0; - // Memory used to back the allocations in the regular heap. A single vector is used to cover - // the entire virtual address space extents that bound the allocations, including any holes. - // This makes deallocation and reallocation of holes fast and keeps process memory contiguous - // in the emulator address space, allowing Memory::GetPointer to be reasonably safe. - std::shared_ptr<std::vector<u8>> heap_memory; - - // The left/right bounds of the address space covered by heap_memory. - VAddr heap_start = 0; - VAddr heap_end = 0; - u64 heap_used = 0; - /// The Thread Local Storage area is allocated as processes create threads, /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part /// holds the TLS for a specific thread. This vector contains which parts are in use for each @@ -321,6 +318,9 @@ private: /// Per-process handle table for storing created object handles in. HandleTable handle_table; + /// Random values for svcGetInfo RandomEntropy + std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy; + std::string name; }; 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 @@ #include "core/hle/lock.h" #include "core/hle/result.h" #include "core/hle/service/service.h" +#include "core/settings.h" namespace Kernel { namespace { @@ -558,7 +559,16 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) *result = 0; break; case GetInfoType::RandomEntropy: - *result = 0; + if (handle != 0) { + return ERR_INVALID_HANDLE; + } + + if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) { + return ERR_INVALID_COMBINATION_KERNEL; + } + + *result = current_process->GetRandomEntropy(info_sub_id); + return RESULT_SUCCESS; break; case GetInfoType::ASLRRegionBaseAddr: *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 return RESULT_SUCCESS; } +ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { + if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() || + target + size < target) { + return ERR_INVALID_ADDRESS; + } + + if (heap_memory == nullptr) { + // Initialize heap + heap_memory = std::make_shared<std::vector<u8>>(); + heap_start = heap_end = target; + } else { + UnmapRange(heap_start, heap_end - heap_start); + } + + // If necessary, expand backing vector to cover new heap extents. + if (target < heap_start) { + heap_memory->insert(begin(*heap_memory), heap_start - target, 0); + heap_start = target; + RefreshMemoryBlockMappings(heap_memory.get()); + } + if (target + size > heap_end) { + heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0); + heap_end = target + size; + RefreshMemoryBlockMappings(heap_memory.get()); + } + ASSERT(heap_end - heap_start == heap_memory->size()); + + CASCADE_RESULT(auto vma, MapMemoryBlock(target, heap_memory, target - heap_start, size, + MemoryState::Heap)); + Reprotect(vma, perms); + + heap_used = size; + + return MakeResult<VAddr>(heap_end - size); +} + +ResultCode VMManager::HeapFree(VAddr target, u64 size) { + if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() || + target + size < target) { + return ERR_INVALID_ADDRESS; + } + + if (size == 0) { + return RESULT_SUCCESS; + } + + const ResultCode result = UnmapRange(target, size); + if (result.IsError()) { + return result; + } + + heap_used -= size; + return RESULT_SUCCESS; +} + +ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { + const auto vma = FindVMA(src_addr); + + ASSERT_MSG(vma != vma_map.end(), "Invalid memory address"); + ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address"); + + // The returned VMA might be a bigger one encompassing the desired address. + const auto vma_offset = src_addr - vma->first; + ASSERT_MSG(vma_offset + size <= vma->second.size, + "Shared memory exceeds bounds of mapped block"); + + const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block; + const std::size_t backing_block_offset = vma->second.offset + vma_offset; + + CASCADE_RESULT(auto new_vma, MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, + MemoryState::Mapped)); + // Protect mirror with permissions from old region + Reprotect(new_vma, vma->second.permissions); + // Remove permissions from old region + Reprotect(vma, VMAPermission::None); + + return RESULT_SUCCESS; +} + void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) { // If this ever proves to have a noticeable performance impact, allow users of the function to // specify a specific range of addresses to limit the scan to. @@ -495,8 +574,7 @@ u64 VMManager::GetTotalMemoryUsage() const { } u64 VMManager::GetTotalHeapUsage() const { - LOG_WARNING(Kernel, "(STUBBED) called"); - return 0x0; + return heap_used; } 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: /// Changes the permissions of a range of addresses, splitting VMAs as necessary. ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms); + ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); + ResultCode HeapFree(VAddr target, u64 size); + + ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size); + /** * Scans all VMAs and updates the page table range of any that use the given vector as backing * memory. This should be called after any operation that causes reallocation of the vector. @@ -343,5 +348,15 @@ private: VAddr tls_io_region_base = 0; VAddr tls_io_region_end = 0; + + // Memory used to back the allocations in the regular heap. A single vector is used to cover + // the entire virtual address space extents that bound the allocations, including any holes. + // This makes deallocation and reallocation of holes fast and keeps process memory contiguous + // in the emulator address space, allowing Memory::GetPointer to be reasonably safe. + std::shared_ptr<std::vector<u8>> heap_memory; + // The left/right bounds of the address space covered by heap_memory. + VAddr heap_start = 0; + VAddr heap_end = 0; + u64 heap_used = 0; }; } // 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 rb.PushRaw<u128>(INVALID_UUID); return; } - auto user_list = profile_manager->GetAllUsers(); - if (user_list.empty()) { + + const auto user_list = profile_manager->GetAllUsers(); + if (std::all_of(user_list.begin(), user_list.end(), + [](const auto& user) { return user.uuid == INVALID_UUID; })) { rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code rb.PushRaw<u128>(INVALID_UUID); 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 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> #include <random> +#include <fmt/format.h> + #include "common/file_util.h" #include "core/hle/service/acc/profile_manager.h" #include "core/settings.h" @@ -39,6 +42,19 @@ UUID UUID::Generate() { return UUID{distribution(gen), distribution(gen)}; } +std::string UUID::Format() const { + return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); +} + +std::string UUID::FormatSwitch() const { + std::array<u8, 16> s{}; + std::memcpy(s.data(), uuid.data(), sizeof(u128)); + return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{" + ":02x}{:02x}{:02x}{:02x}{:02x}", + s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], + s[12], s[13], s[14], s[15]); +} + ProfileManager::ProfileManager() { ParseUserSaveFile(); @@ -325,11 +341,12 @@ void ProfileManager::ParseUserSaveFile() { return; } - for (std::size_t i = 0; i < MAX_USERS; ++i) { - const auto& user = data.users[i]; + for (const auto& user : data.users) { + if (user.uuid == UUID(INVALID_UUID)) { + continue; + } - if (user.uuid != UUID(INVALID_UUID)) - AddUser({user.uuid, user.username, user.timestamp, {}, false}); + AddUser({user.uuid, user.username, user.timestamp, {}, false}); } 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 { void Invalidate() { uuid = INVALID_UUID; } - std::string Format() const { - return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); - } - std::string FormatSwitch() const { - std::array<u8, 16> s{}; - std::memcpy(s.data(), uuid.data(), sizeof(u128)); - return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{" - ":02x}{:02x}{:02x}{:02x}{:02x}", - s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], - s[12], s[13], s[14], s[15]); - } + std::string Format() const; + std::string FormatSwitch() const; }; static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); 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: {5, &IAudioRenderer::Start, "Start"}, {6, &IAudioRenderer::Stop, "Stop"}, {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, - {8, nullptr, "SetRenderingTimeLimit"}, - {9, nullptr, "GetRenderingTimeLimit"}, + {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"}, + {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, {10, nullptr, "RequestUpdateAuto"}, {11, nullptr, "ExecuteAudioRendererRendering"}, }; @@ -110,8 +110,29 @@ private: LOG_WARNING(Service_Audio, "(STUBBED) called"); } + void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + rendering_time_limit_percent = rp.Pop<u32>(); + ASSERT(rendering_time_limit_percent >= 0 && rendering_time_limit_percent <= 100); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}", + rendering_time_limit_percent); + } + + void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(rendering_time_limit_percent); + } + Kernel::SharedPtr<Kernel::Event> system_event; std::unique_ptr<AudioCore::AudioRenderer> renderer; + u32 rendering_time_limit_percent = 100; }; 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, return save_data_factory->Open(space, save_struct); } +ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) { + LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space)); + + if (save_data_factory == nullptr) { + return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound); + } + + return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space)); +} + ResultVal<FileSys::VirtualDir> OpenSDMC() { LOG_TRACE(Service_FS, "Opening SDMC"); 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 FileSys::ContentRecordType type); ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, FileSys::SaveDataDescriptor save_struct); +ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); ResultVal<FileSys::VirtualDir> OpenSDMC(); 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 @@ #include "common/assert.h" #include "common/common_types.h" +#include "common/hex_util.h" #include "common/logging/log.h" #include "common/string_util.h" #include "core/file_sys/directory.h" @@ -451,7 +452,147 @@ private: VfsDirectoryServiceWrapper backend; }; +class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { +public: + explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space) + : ServiceFramework("ISaveDataInfoReader") { + static const FunctionInfo functions[] = { + {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, + }; + RegisterHandlers(functions); + + FindAllSaves(space); + } + + void ReadSaveDataInfo(Kernel::HLERequestContext& ctx) { + // Calculate how many entries we can fit in the output buffer + const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo); + + // Cap at total number of entries. + const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index); + + // Determine data start and end + const auto* begin = reinterpret_cast<u8*>(info.data() + next_entry_index); + const auto* end = reinterpret_cast<u8*>(info.data() + next_entry_index + actual_entries); + const auto range_size = static_cast<std::size_t>(std::distance(begin, end)); + + next_entry_index += actual_entries; + + // Write the data to memory + ctx.WriteBuffer(begin, range_size); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(static_cast<u32>(actual_entries)); + } + +private: + static u64 stoull_be(std::string_view str) { + if (str.size() != 16) + return 0; + + const auto bytes = Common::HexStringToArray<0x8>(str); + u64 out{}; + std::memcpy(&out, bytes.data(), sizeof(u64)); + + return Common::swap64(out); + } + + void FindAllSaves(FileSys::SaveDataSpaceId space) { + const auto save_root = OpenSaveDataSpace(space); + ASSERT(save_root.Succeeded()); + + for (const auto& type : (*save_root)->GetSubdirectories()) { + if (type->GetName() == "save") { + for (const auto& save_id : type->GetSubdirectories()) { + for (const auto& user_id : save_id->GetSubdirectories()) { + const auto save_id_numeric = stoull_be(save_id->GetName()); + auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName()); + std::reverse(user_id_numeric.begin(), user_id_numeric.end()); + + if (save_id_numeric != 0) { + // System Save Data + info.emplace_back(SaveDataInfo{ + 0, + space, + FileSys::SaveDataType::SystemSaveData, + {}, + user_id_numeric, + save_id_numeric, + 0, + user_id->GetSize(), + {}, + }); + + continue; + } + + for (const auto& title_id : user_id->GetSubdirectories()) { + const auto device = + std::all_of(user_id_numeric.begin(), user_id_numeric.end(), + [](u8 val) { return val == 0; }); + info.emplace_back(SaveDataInfo{ + 0, + space, + device ? FileSys::SaveDataType::DeviceSaveData + : FileSys::SaveDataType::SaveData, + {}, + user_id_numeric, + save_id_numeric, + stoull_be(title_id->GetName()), + title_id->GetSize(), + {}, + }); + } + } + } + } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) { + // Temporary Storage + for (const auto& user_id : type->GetSubdirectories()) { + for (const auto& title_id : user_id->GetSubdirectories()) { + if (!title_id->GetFiles().empty() || + !title_id->GetSubdirectories().empty()) { + auto user_id_numeric = + Common::HexStringToArray<0x10>(user_id->GetName()); + std::reverse(user_id_numeric.begin(), user_id_numeric.end()); + + info.emplace_back(SaveDataInfo{ + 0, + space, + FileSys::SaveDataType::TemporaryStorage, + {}, + user_id_numeric, + stoull_be(type->GetName()), + stoull_be(title_id->GetName()), + title_id->GetSize(), + {}, + }); + } + } + } + } + } + } + + struct SaveDataInfo { + u64_le save_id_unknown; + FileSys::SaveDataSpaceId space; + FileSys::SaveDataType type; + INSERT_PADDING_BYTES(0x6); + std::array<u8, 0x10> user_id; + u64_le save_id; + u64_le title_id; + u64_le save_image_size; + INSERT_PADDING_BYTES(0x28); + }; + static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); + + std::vector<SaveDataInfo> info; + u64 next_entry_index = 0; +}; + FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "MountContent"}, {1, &FSP_SRV::Initialize, "Initialize"}, @@ -485,7 +626,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { {58, nullptr, "ReadSaveDataFileSystemExtraData"}, {59, nullptr, "WriteSaveDataFileSystemExtraData"}, {60, nullptr, "OpenSaveDataInfoReader"}, - {61, nullptr, "OpenSaveDataInfoReaderBySaveDataSpaceId"}, + {61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"}, {62, nullptr, "OpenCacheStorageList"}, {64, nullptr, "OpenSaveDataInternalStorageFileSystem"}, {65, nullptr, "UpdateSaveDataMacForDebug"}, @@ -544,6 +685,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { {1009, nullptr, "GetAndClearMemoryReportInfo"}, {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, }; + // clang-format on RegisterHandlers(functions); } @@ -618,6 +760,15 @@ void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) { MountSaveData(ctx); } +void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto space = rp.PopRaw<FileSys::SaveDataSpaceId>(); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space)); +} + void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_FS, "(STUBBED) called"); 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: void CreateSaveData(Kernel::HLERequestContext& ctx); void MountSaveData(Kernel::HLERequestContext& ctx); void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); + void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx); void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); 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) { dual_entry.pad_states.raw = pad_state.raw; dual_entry.l_stick = lstick_entry; dual_entry.r_stick = rstick_entry; + break; case NPadControllerType::JoyLeft: left_entry.connection_status.raw = 0; 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: IPC::ResponseBuilder rb{ctx, 2}; auto amiibo = nfp_interface.GetAmiiboBuffer(); TagInfo tag_info{}; - std::memcpy(tag_info.uuid.data(), amiibo.uuid.data(), sizeof(tag_info.uuid.size())); + tag_info.uuid = amiibo.uuid; tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size()); 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 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/ns/ns.h" @@ -118,7 +121,7 @@ public: {305, nullptr, "TerminateSystemApplet"}, {306, nullptr, "LaunchOverlayApplet"}, {307, nullptr, "TerminateOverlayApplet"}, - {400, nullptr, "GetApplicationControlData"}, + {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"}, {401, nullptr, "InvalidateAllApplicationControlCache"}, {402, nullptr, "RequestDownloadApplicationControlData"}, {403, nullptr, "GetMaxApplicationControlCacheCount"}, @@ -243,6 +246,65 @@ public: RegisterHandlers(functions); } + + void GetApplicationControlData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto flag = rp.PopRaw<u64>(); + LOG_DEBUG(Service_NS, "called with flag={:016X}", flag); + + const auto title_id = rp.PopRaw<u64>(); + + const auto size = ctx.GetWriteBufferSize(); + + const FileSys::PatchManager pm{title_id}; + const auto control = pm.GetControlMetadata(); + + std::vector<u8> out; + + if (control.first != nullptr) { + if (size < 0x4000) { + LOG_ERROR(Service_NS, + "output buffer is too small! (actual={:016X}, expected_min=0x4000)", + size); + IPC::ResponseBuilder rb{ctx, 2}; + // TODO(DarkLordZach): Find a better error code for this. + rb.Push(ResultCode(-1)); + return; + } + + out.resize(0x4000); + const auto bytes = control.first->GetRawBytes(); + std::memcpy(out.data(), bytes.data(), bytes.size()); + } else { + LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.", + title_id); + out.resize(std::min<u64>(0x4000, size)); + } + + if (control.second != nullptr) { + if (size < 0x4000 + control.second->GetSize()) { + LOG_ERROR(Service_NS, + "output buffer is too small! (actual={:016X}, expected_min={:016X})", + size, 0x4000 + control.second->GetSize()); + IPC::ResponseBuilder rb{ctx, 2}; + // TODO(DarkLordZach): Find a better error code for this. + rb.Push(ResultCode(-1)); + return; + } + + out.resize(0x4000 + control.second->GetSize()); + control.second->Read(out.data() + 0x4000, control.second->GetSize()); + } else { + LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.", + title_id); + } + + ctx.WriteBuffer(out); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(static_cast<u32>(out.size())); + } }; 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 { * Creates a function string for logging, complete with the name (or header code, depending * on what's passed in) the port name, and all the cmd_buff arguments. */ -static std::string MakeFunctionString(const char* name, const char* port_name, - const u32* cmd_buff) { +[[maybe_unused]] static std::string MakeFunctionString(const char* name, const char* port_name, + const u32* cmd_buff) { // Number of params == bits 0-5 + bits 6-11 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); 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 @@ // Refer to the license.txt file included. #include <algorithm> +#include <chrono> #include <cstdlib> +#include <ctime> +#include <functional> #include <vector> #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/spl/csrng.h" #include "core/hle/service/spl/module.h" #include "core/hle/service/spl/spl.h" +#include "core/settings.h" namespace Service::SPL { Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) - : ServiceFramework(name), module(std::move(module)) {} + : ServiceFramework(name), module(std::move(module)), + rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {} Module::Interface::~Interface() = default; @@ -24,7 +29,7 @@ void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { std::size_t size = ctx.GetWriteBufferSize(); std::vector<u8> data(size); - std::generate(data.begin(), data.end(), std::rand); + std::generate(data.begin(), data.end(), rng); ctx.WriteBuffer(data); 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 @@ #pragma once +#include <random> #include "core/hle/service/service.h" namespace Service::SPL { @@ -19,6 +20,9 @@ public: protected: std::shared_ptr<Module> module; + + private: + std::mt19937 rng; }; }; 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) {102, nullptr, "GetStandardUserSystemClockInitialYear"}, {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, - {400, nullptr, "GetClockSnapshot"}, + {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"}, {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 @@ namespace Service::Time { +static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time, + CalendarAdditionalInfo& additional_info, + [[maybe_unused]] const TimeZoneRule& /*rule*/) { + const std::time_t time(posix_time); + const std::tm* tm = std::localtime(&time); + if (tm == nullptr) { + calendar_time = {}; + additional_info = {}; + return; + } + calendar_time.year = tm->tm_year + 1900; + calendar_time.month = tm->tm_mon + 1; + calendar_time.day = tm->tm_mday; + calendar_time.hour = tm->tm_hour; + calendar_time.minute = tm->tm_min; + calendar_time.second = tm->tm_sec; + + additional_info.day_of_week = tm->tm_wday; + additional_info.day_of_year = tm->tm_yday; + std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC")); + additional_info.utc_offset = 0; +} + +static u64 CalendarToPosix(const CalendarTime& calendar_time, + [[maybe_unused]] const TimeZoneRule& /*rule*/) { + std::tm time{}; + time.tm_year = calendar_time.year - 1900; + time.tm_mon = calendar_time.month - 1; + time.tm_mday = calendar_time.day; + + time.tm_hour = calendar_time.hour; + time.tm_min = calendar_time.minute; + time.tm_sec = calendar_time.second; + + std::time_t epoch_time = std::mktime(&time); + return static_cast<u64>(epoch_time); +} + class ISystemClock final : public ServiceFramework<ISystemClock> { public: ISystemClock() : ServiceFramework("ISystemClock") { @@ -80,8 +118,8 @@ public: {5, nullptr, "GetTimeZoneRuleVersion"}, {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"}, {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, - {201, nullptr, "ToPosixTime"}, - {202, nullptr, "ToPosixTimeWithMyRule"}, + {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"}, + {202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"}, }; RegisterHandlers(functions); } @@ -151,24 +189,29 @@ private: rb.PushRaw(additional_info); } - void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time, - CalendarAdditionalInfo& additional_info, const TimeZoneRule& /*rule*/) { - std::time_t t(posix_time); - std::tm* tm = std::localtime(&t); - if (!tm) { - return; - } - calendar_time.year = tm->tm_year + 1900; - calendar_time.month = tm->tm_mon + 1; - calendar_time.day = tm->tm_mday; - calendar_time.hour = tm->tm_hour; - calendar_time.minute = tm->tm_min; - calendar_time.second = tm->tm_sec; - - additional_info.day_of_week = tm->tm_wday; - additional_info.day_of_year = tm->tm_yday; - std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC")); - additional_info.utc_offset = 0; + void ToPosixTime(Kernel::HLERequestContext& ctx) { + // TODO(ogniK): Figure out how to handle multiple times + LOG_WARNING(Service_Time, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + auto calendar_time = rp.PopRaw<CalendarTime>(); + auto posix_time = CalendarToPosix(calendar_time, {}); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<u32>(1); // Amount of times we're returning + ctx.WriteBuffer(&posix_time, sizeof(u64)); + } + + void ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Time, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + auto calendar_time = rp.PopRaw<CalendarTime>(); + auto posix_time = CalendarToPosix(calendar_time, {}); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<u32>(1); // Amount of times we're returning + ctx.WriteBuffer(&posix_time, sizeof(u64)); } }; @@ -207,6 +250,55 @@ void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& c LOG_DEBUG(Service_Time, "called"); } +void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + + IPC::RequestParser rp{ctx}; + auto unknown_u8 = rp.PopRaw<u8>(); + + ClockSnapshot clock_snapshot{}; + + const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>( + std::chrono::system_clock::now().time_since_epoch()) + .count()}; + CalendarTime calendar_time{}; + const std::time_t time(time_since_epoch); + const std::tm* tm = std::localtime(&time); + if (tm == nullptr) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(-1)); // TODO(ogniK): Find appropriate error code + return; + } + SteadyClockTimePoint steady_clock_time_point{CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / + 1000}; + + LocationName location_name{"UTC"}; + calendar_time.year = tm->tm_year + 1900; + calendar_time.month = tm->tm_mon + 1; + calendar_time.day = tm->tm_mday; + calendar_time.hour = tm->tm_hour; + calendar_time.minute = tm->tm_min; + calendar_time.second = tm->tm_sec; + clock_snapshot.system_posix_time = time_since_epoch; + clock_snapshot.network_posix_time = time_since_epoch; + clock_snapshot.system_calendar_time = calendar_time; + clock_snapshot.network_calendar_time = calendar_time; + + CalendarAdditionalInfo additional_info{}; + PosixToCalendar(time_since_epoch, calendar_time, additional_info, {}); + + clock_snapshot.system_calendar_info = additional_info; + clock_snapshot.network_calendar_info = additional_info; + + clock_snapshot.steady_clock_timepoint = steady_clock_time_point; + clock_snapshot.location_name = location_name; + clock_snapshot.clock_auto_adjustment_enabled = 1; + clock_snapshot.ipc_u8 = unknown_u8; + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot)); +} + Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) : ServiceFramework(name), time(std::move(time)) {} 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 @@ #pragma once #include <array> +#include "common/common_funcs.h" #include "core/hle/service/service.h" namespace Service::Time { @@ -53,6 +54,23 @@ struct SystemClockContext { static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext structure has incorrect size"); +struct ClockSnapshot { + SystemClockContext user_clock_context; + SystemClockContext network_clock_context; + s64_le system_posix_time; + s64_le network_posix_time; + CalendarTime system_calendar_time; + CalendarTime network_calendar_time; + CalendarAdditionalInfo system_calendar_info; + CalendarAdditionalInfo network_calendar_info; + SteadyClockTimePoint steady_clock_timepoint; + LocationName location_name; + u8 clock_auto_adjustment_enabled; + u8 ipc_u8; + INSERT_PADDING_BYTES(2); +}; +static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size"); + class Module final { public: class Interface : public ServiceFramework<Interface> { @@ -65,6 +83,7 @@ public: void GetStandardSteadyClock(Kernel::HLERequestContext& ctx); void GetTimeZoneService(Kernel::HLERequestContext& ctx); void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); + void GetClockSnapshot(Kernel::HLERequestContext& ctx); protected: 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 @@ #include "common/swap.h" #include "core/core.h" #include "core/file_sys/control_metadata.h" +#include "core/file_sys/romfs_factory.h" #include "core/file_sys/vfs_offset.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/vm_manager.h" +#include "core/hle/service/filesystem/filesystem.h" #include "core/loader/nro.h" #include "core/loader/nso.h" #include "core/memory.h" @@ -208,6 +210,9 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { return ResultStatus::ErrorLoadingNRO; } + if (romfs != nullptr) + Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); + process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); 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 @@ #include <array> #include <atomic> +#include <optional> #include <string> #include "common/common_types.h" @@ -114,8 +115,9 @@ struct Values { // System bool use_docked_mode; bool enable_nfc; - int current_user; - int language_index; + std::optional<u32> rng_seed; + s32 current_user; + s32 language_index; // Controls 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() { regs.independent_blend[blend_index].factor_source_a = Regs::Blend::Factor::One; regs.independent_blend[blend_index].factor_dest_a = Regs::Blend::Factor::Zero; } + regs.stencil_front_op_fail = Regs::StencilOp::Keep; + regs.stencil_front_op_zfail = Regs::StencilOp::Keep; + regs.stencil_front_op_zpass = Regs::StencilOp::Keep; + regs.stencil_front_func_func = Regs::ComparisonOp::Always; + regs.stencil_front_func_mask = 0xFFFFFFFF; + regs.stencil_front_mask = 0xFFFFFFFF; + regs.stencil_two_side_enable = 1; + regs.stencil_back_op_fail = Regs::StencilOp::Keep; + regs.stencil_back_op_zfail = Regs::StencilOp::Keep; + regs.stencil_back_op_zpass = Regs::StencilOp::Keep; + regs.stencil_back_func_func = Regs::ComparisonOp::Always; + regs.stencil_back_func_mask = 0xFFFFFFFF; + regs.stencil_back_mask = 0xFFFFFFFF; } void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { @@ -108,10 +121,8 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandLoaded, nullptr); } - u32 old = regs.reg_array[method]; - regs.reg_array[method] = value; - - if (value != old) { + if (regs.reg_array[method] != value) { + regs.reg_array[method] = value; if (method >= MAXWELL3D_REG_INDEX(vertex_attrib_format) && method < MAXWELL3D_REG_INDEX(vertex_attrib_format) + regs.vertex_attrib_format.size()) { 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: Invert = 6, IncrWrap = 7, DecrWrap = 8, + KeepOGL = 0x1E00, + ZeroOGL = 0, + ReplaceOGL = 0x1E01, + IncrOGL = 0x1E02, + DecrOGL = 0x1E03, + InvertOGL = 0x150A, + IncrWrapOGL = 0x8507, + DecrWrapOGL = 0x8508, }; 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 @@ #include "core/frontend/emu_window.h" #include "core/settings.h" #include "video_core/renderer_base.h" -#include "video_core/renderer_opengl/gl_rasterizer.h" namespace VideoCore { 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() { if (is_cache_miss) { VAO.Create(); state.draw.vertex_array = VAO.handle; - state.Apply(); + state.ApplyVertexBufferState(); // The index buffer binding is stored within the VAO. Stupid OpenGL, but easy to work // around. @@ -182,7 +182,7 @@ void RasterizerOpenGL::SetupVertexFormat() { } } state.draw.vertex_array = VAO.handle; - state.Apply(); + state.ApplyVertexBufferState(); } void RasterizerOpenGL::SetupVertexBuffer() { @@ -342,8 +342,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { index++; } } - - state.Apply(); } std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { @@ -412,8 +410,8 @@ void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { cached_pages.add({pages_interval, delta}); } -void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb, - bool preserve_contents, +void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool using_color_fb, + bool using_depth_fb, bool preserve_contents, std::optional<std::size_t> single_color_target) { MICROPROFILE_SCOPE(OpenGL_Framebuffer); const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; @@ -429,9 +427,9 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep ASSERT_MSG(regs.rt_separate_frag_data == 0, "Unimplemented"); // Bind the framebuffer surfaces - state.draw.draw_framebuffer = framebuffer.handle; - state.Apply(); - state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0; + current_state.draw.draw_framebuffer = framebuffer.handle; + current_state.ApplyFramebufferState(); + current_state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0; if (using_color_fb) { if (single_color_target) { @@ -509,10 +507,7 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); } - - SyncViewport(); - - state.Apply(); + SyncViewport(current_state); } void RasterizerOpenGL::Clear() { @@ -525,22 +520,23 @@ void RasterizerOpenGL::Clear() { bool use_stencil{}; OpenGLState clear_state; - clear_state.draw.draw_framebuffer = framebuffer.handle; - clear_state.color_mask[0].red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE; - clear_state.color_mask[0].green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE; - clear_state.color_mask[0].blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE; - clear_state.color_mask[0].alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE; - if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B || regs.clear_buffers.A) { use_color = true; } + if (use_color) { + clear_state.color_mask[0].red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE; + clear_state.color_mask[0].green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE; + clear_state.color_mask[0].blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE; + clear_state.color_mask[0].alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE; + } if (regs.clear_buffers.Z) { ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!"); use_depth = true; // Always enable the depth write when clearing the depth buffer. The depth write mask is - // ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to true. + // ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to + // true. clear_state.depth.test_enabled = true; clear_state.depth.test_func = GL_ALWAYS; } @@ -557,11 +553,8 @@ void RasterizerOpenGL::Clear() { ScopeAcquireGLContext acquire_context{emu_window}; - ConfigureFramebuffers(use_color, use_depth || use_stencil, false, + ConfigureFramebuffers(clear_state, use_color, use_depth || use_stencil, false, regs.clear_buffers.RT.Value()); - // Copy the sRGB setting to the clear state to avoid problem with - // specific driver implementations - clear_state.framebuffer_srgb.enabled = state.framebuffer_srgb.enabled; clear_state.Apply(); if (use_color) { @@ -587,7 +580,7 @@ void RasterizerOpenGL::DrawArrays() { ScopeAcquireGLContext acquire_context{emu_window}; - ConfigureFramebuffers(); + ConfigureFramebuffers(state); SyncColorMask(); SyncDepthTestState(); SyncStencilTestState(); @@ -608,7 +601,7 @@ void RasterizerOpenGL::DrawArrays() { const bool is_indexed = accelerate_draw == AccelDraw::Indexed; state.draw.vertex_buffer = buffer_cache.GetHandle(); - state.Apply(); + state.ApplyVertexBufferState(); std::size_t buffer_size = CalculateVertexArraysSize(); @@ -740,9 +733,9 @@ void RasterizerOpenGL::SamplerInfo::Create() { glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER); } -void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) { +void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::FullTextureInfo& info) { const GLuint s = sampler.handle; - + const Tegra::Texture::TSCEntry& config = info.tsc; if (mag_filter != config.mag_filter) { mag_filter = config.mag_filter; glSamplerParameteri( @@ -793,6 +786,22 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, border_color.data()); } } + if (info.tic.use_header_opt_control == 0) { + if (GLAD_GL_ARB_texture_filter_anisotropic) { + glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY, + static_cast<float>(1 << info.tic.max_anisotropy.Value())); + } else if (GLAD_GL_EXT_texture_filter_anisotropic) { + glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT, + static_cast<float>(1 << info.tic.max_anisotropy.Value())); + } + glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, + static_cast<float>(info.tic.res_min_mip_level.Value())); + glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, + static_cast<float>(info.tic.res_max_mip_level.Value() == 0 + ? 16 + : info.tic.res_max_mip_level.Value())); + glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, info.tic.mip_lod_bias.Value() / 256.f); + } } u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader, @@ -890,7 +899,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, continue; } - texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); + texture_samplers[current_bindpoint].SyncWithConfig(texture); Surface surface = res_cache.GetTextureSurface(texture, entry); if (surface != nullptr) { state.texture_units[current_bindpoint].texture = surface->Texture().handle; @@ -912,15 +921,15 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, return current_unit + static_cast<u32>(entries.size()); } -void RasterizerOpenGL::SyncViewport() { +void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; for (size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()}; - auto& viewport = state.viewports[i]; + auto& viewport = current_state.viewports[i]; viewport.x = viewport_rect.left; viewport.y = viewport_rect.bottom; - viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth()); - viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight()); + viewport.width = static_cast<GLfloat>(viewport_rect.GetWidth()); + viewport.height = static_cast<GLfloat>(viewport_rect.GetHeight()); viewport.depth_range_far = regs.viewport[i].depth_range_far; viewport.depth_range_near = regs.viewport[i].depth_range_near; } @@ -985,9 +994,6 @@ void RasterizerOpenGL::SyncStencilTestState() { return; } - // TODO(bunnei): Verify behavior when this is not set - ASSERT(regs.stencil_two_side_enable); - state.stencil.front.test_func = MaxwellToGL::ComparisonOp(regs.stencil_front_func_func); state.stencil.front.test_ref = regs.stencil_front_func_ref; state.stencil.front.test_mask = regs.stencil_front_func_mask; @@ -995,14 +1001,23 @@ void RasterizerOpenGL::SyncStencilTestState() { state.stencil.front.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_front_op_zfail); state.stencil.front.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_front_op_zpass); state.stencil.front.write_mask = regs.stencil_front_mask; - - state.stencil.back.test_func = MaxwellToGL::ComparisonOp(regs.stencil_back_func_func); - state.stencil.back.test_ref = regs.stencil_back_func_ref; - state.stencil.back.test_mask = regs.stencil_back_func_mask; - state.stencil.back.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_fail); - state.stencil.back.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_zfail); - state.stencil.back.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_back_op_zpass); - state.stencil.back.write_mask = regs.stencil_back_mask; + if (regs.stencil_two_side_enable) { + state.stencil.back.test_func = MaxwellToGL::ComparisonOp(regs.stencil_back_func_func); + state.stencil.back.test_ref = regs.stencil_back_func_ref; + state.stencil.back.test_mask = regs.stencil_back_func_mask; + state.stencil.back.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_fail); + state.stencil.back.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_zfail); + state.stencil.back.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_back_op_zpass); + state.stencil.back.write_mask = regs.stencil_back_mask; + } else { + state.stencil.back.test_func = GL_ALWAYS; + state.stencil.back.test_ref = 0; + state.stencil.back.test_mask = 0xFFFFFFFF; + state.stencil.back.write_mask = 0xFFFFFFFF; + state.stencil.back.action_stencil_fail = GL_KEEP; + state.stencil.back.action_depth_fail = GL_KEEP; + state.stencil.back.action_depth_pass = GL_KEEP; + } } void RasterizerOpenGL::SyncColorMask() { @@ -1114,9 +1129,8 @@ void RasterizerOpenGL::CheckAlphaTests() { const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) { - LOG_CRITICAL( - Render_OpenGL, - "Alpha Testing is enabled with Multiple Render Targets, this behavior is undefined."); + LOG_CRITICAL(Render_OpenGL, "Alpha Testing is enabled with Multiple Render Targets, " + "this behavior is undefined."); UNREACHABLE(); } } 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: /// SamplerInfo struct. void Create(); /// Syncs the sampler object with the config, updating any necessary state. - void SyncWithConfig(const Tegra::Texture::TSCEntry& config); + void SyncWithConfig(const Tegra::Texture::FullTextureInfo& info); private: Tegra::Texture::TextureFilter mag_filter; @@ -109,8 +109,8 @@ private: * @param preserve_contents If true, tries to preserve data from a previously used framebuffer. * @param single_color_target Specifies if a single color buffer target should be used. */ - void ConfigureFramebuffers(bool use_color_fb = true, bool using_depth_fb = true, - bool preserve_contents = true, + void ConfigureFramebuffers(OpenGLState& current_state, bool use_color_fb = true, + bool using_depth_fb = true, bool preserve_contents = true, std::optional<std::size_t> single_color_target = {}); /* @@ -134,7 +134,7 @@ private: GLenum primitive_mode, u32 current_unit); /// Syncs the viewport and depth range to match the guest state - void SyncViewport(); + void SyncViewport(OpenGLState& current_state); /// Syncs the clip enabled status to match the guest state 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 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5_SRGB + {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_10X8 + {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_10X8_SRGB // Depth formats {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F @@ -456,6 +458,8 @@ static constexpr GLConversionArray morton_to_gl_fns = { MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>, MortonCopy<true, PixelFormat::ASTC_2D_5X5>, MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>, + MortonCopy<true, PixelFormat::ASTC_2D_10X8>, + MortonCopy<true, PixelFormat::ASTC_2D_10X8_SRGB>, MortonCopy<true, PixelFormat::Z32F>, MortonCopy<true, PixelFormat::Z16>, MortonCopy<true, PixelFormat::Z24S8>, @@ -526,6 +530,8 @@ static constexpr GLConversionArray gl_to_morton_fns = { nullptr, nullptr, nullptr, + nullptr, + nullptr, MortonCopy<false, PixelFormat::Z32F>, MortonCopy<false, PixelFormat::Z16>, MortonCopy<false, PixelFormat::Z24S8>, @@ -580,7 +586,7 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, state.draw.draw_framebuffer = draw_fb_handle; // Set sRGB enabled if the destination surfaces need it state.framebuffer_srgb.enabled = dst_params.srgb_conversion; - state.Apply(); + state.ApplyFramebufferState(); u32 buffers{}; @@ -709,18 +715,18 @@ static void FastCopySurface(const Surface& src_surface, const Surface& dst_surfa MICROPROFILE_DEFINE(OpenGL_CopySurface, "OpenGL", "CopySurface", MP_RGB(128, 192, 64)); static void CopySurface(const Surface& src_surface, const Surface& dst_surface, - GLuint copy_pbo_handle, GLenum src_attachment = 0, - GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { + const GLuint copy_pbo_handle, const GLenum src_attachment = 0, + const GLenum dst_attachment = 0, const std::size_t cubemap_face = 0) { MICROPROFILE_SCOPE(OpenGL_CopySurface); ASSERT_MSG(dst_attachment == 0, "Unimplemented"); const auto& src_params{src_surface->GetSurfaceParams()}; const auto& dst_params{dst_surface->GetSurfaceParams()}; - auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type); - auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type); + const auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type); + const auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type); - std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes); + const std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes); glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle); 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, LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during " "reinterpretation but the texture is tiled."); } - std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes; - std::vector<u8> data(remaining_size); - std::memcpy(data.data(), Memory::GetPointer(dst_params.addr + src_params.size_in_bytes), - data.size()); + const std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes; glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size, - data.data()); + Memory::GetPointer(dst_params.addr + src_params.size_in_bytes)); } glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); @@ -932,7 +935,9 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma case PixelFormat::ASTC_2D_8X8_SRGB: case PixelFormat::ASTC_2D_8X5_SRGB: case PixelFormat::ASTC_2D_5X4_SRGB: - case PixelFormat::ASTC_2D_5X5_SRGB: { + case PixelFormat::ASTC_2D_5X5_SRGB: + case PixelFormat::ASTC_2D_10X8: + case PixelFormat::ASTC_2D_10X8_SRGB: { // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. u32 block_width{}; u32 block_height{}; @@ -967,7 +972,11 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelForm case PixelFormat::ASTC_2D_4X4: case PixelFormat::ASTC_2D_8X8: case PixelFormat::ASTC_2D_4X4_SRGB: - case PixelFormat::ASTC_2D_8X8_SRGB: { + case PixelFormat::ASTC_2D_8X8_SRGB: + case PixelFormat::ASTC_2D_5X5: + case PixelFormat::ASTC_2D_5X5_SRGB: + case PixelFormat::ASTC_2D_10X8: + case PixelFormat::ASTC_2D_10X8_SRGB: { LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented", static_cast<u32>(pixel_format)); UNREACHABLE(); @@ -1336,6 +1345,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, break; case SurfaceTarget::TextureCubemap: case SurfaceTarget::Texture3D: + case SurfaceTarget::Texture2DArray: case SurfaceTarget::TextureCubeArray: AccurateCopySurface(old_surface, new_surface); 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 { } } -void OpenGLState::Apply() const { +void OpenGLState::ApplyFramebufferState() const { // Framebuffer if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); @@ -435,7 +435,9 @@ void OpenGLState::Apply() const { if (draw.draw_framebuffer != cur_state.draw.draw_framebuffer) { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer); } +} +void OpenGLState::ApplyVertexBufferState() const { // Vertex array if (draw.vertex_array != cur_state.draw.vertex_array) { glBindVertexArray(draw.vertex_array); @@ -445,7 +447,11 @@ void OpenGLState::Apply() const { if (draw.vertex_buffer != cur_state.draw.vertex_buffer) { glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer); } +} +void OpenGLState::Apply() const { + ApplyFramebufferState(); + ApplyVertexBufferState(); // Uniform buffer if (draw.uniform_buffer != cur_state.draw.uniform_buffer) { 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: } /// Apply this state as the current OpenGL state void Apply() const; + /// Apply only the state afecting the framebuffer + void ApplyFramebufferState() const; + /// Apply only the state afecting the vertex buffer + void ApplyVertexBufferState() const; /// Set the initial OpenGL state static void ApplyDefaultState(); /// 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, } } } - LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}", - static_cast<u32>(filter_mode)); - UNREACHABLE(); - return {}; + LOG_ERROR(Render_OpenGL, "Unimplemented texture filter mode={}", static_cast<u32>(filter_mode)); + return GL_LINEAR; } inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { @@ -183,9 +181,8 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { case Tegra::Texture::WrapMode::MirrorOnceClampToEdge: return GL_MIRROR_CLAMP_TO_EDGE; } - LOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); - UNREACHABLE(); - return {}; + LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); + return GL_REPEAT; } inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { @@ -207,10 +204,9 @@ inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { case Tegra::Texture::DepthCompareFunc::Always: return GL_ALWAYS; } - LOG_CRITICAL(Render_OpenGL, "Unimplemented texture depth compare function ={}", - static_cast<u32>(func)); - UNREACHABLE(); - return {}; + LOG_ERROR(Render_OpenGL, "Unimplemented texture depth compare function ={}", + static_cast<u32>(func)); + return GL_GREATER; } inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { @@ -226,9 +222,8 @@ inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { case Maxwell::Blend::Equation::Max: return GL_MAX; } - LOG_CRITICAL(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); - UNREACHABLE(); - return {}; + LOG_ERROR(Render_OpenGL, "Unimplemented blend equation={}", static_cast<u32>(equation)); + return GL_FUNC_ADD; } inline GLenum BlendFunc(Maxwell::Blend::Factor factor) { @@ -291,9 +286,8 @@ inline GLenum BlendFunc(Maxwell::Blend::Factor factor) { case Maxwell::Blend::Factor::OneMinusConstantAlphaGL: return GL_ONE_MINUS_CONSTANT_ALPHA; } - LOG_CRITICAL(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor)); - UNREACHABLE(); - return {}; + LOG_ERROR(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor)); + return GL_ZERO; } inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) { @@ -312,9 +306,8 @@ inline GLenum SwizzleSource(Tegra::Texture::SwizzleSource source) { case Tegra::Texture::SwizzleSource::OneFloat: return GL_ONE; } - LOG_CRITICAL(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source)); - UNREACHABLE(); - return {}; + LOG_ERROR(Render_OpenGL, "Unimplemented swizzle source={}", static_cast<u32>(source)); + return GL_ZERO; } inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) { @@ -344,33 +337,39 @@ inline GLenum ComparisonOp(Maxwell::ComparisonOp comparison) { case Maxwell::ComparisonOp::AlwaysOld: return GL_ALWAYS; } - LOG_CRITICAL(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison)); - UNREACHABLE(); - return {}; + LOG_ERROR(Render_OpenGL, "Unimplemented comparison op={}", static_cast<u32>(comparison)); + return GL_ALWAYS; } inline GLenum StencilOp(Maxwell::StencilOp stencil) { switch (stencil) { case Maxwell::StencilOp::Keep: + case Maxwell::StencilOp::KeepOGL: return GL_KEEP; case Maxwell::StencilOp::Zero: + case Maxwell::StencilOp::ZeroOGL: return GL_ZERO; case Maxwell::StencilOp::Replace: + case Maxwell::StencilOp::ReplaceOGL: return GL_REPLACE; case Maxwell::StencilOp::Incr: + case Maxwell::StencilOp::IncrOGL: return GL_INCR; case Maxwell::StencilOp::Decr: + case Maxwell::StencilOp::DecrOGL: return GL_DECR; case Maxwell::StencilOp::Invert: + case Maxwell::StencilOp::InvertOGL: return GL_INVERT; case Maxwell::StencilOp::IncrWrap: + case Maxwell::StencilOp::IncrWrapOGL: return GL_INCR_WRAP; case Maxwell::StencilOp::DecrWrap: + case Maxwell::StencilOp::DecrWrapOGL: return GL_DECR_WRAP; } - LOG_CRITICAL(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil)); - UNREACHABLE(); - return {}; + LOG_ERROR(Render_OpenGL, "Unimplemented stencil op={}", static_cast<u32>(stencil)); + return GL_KEEP; } inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) { @@ -380,9 +379,8 @@ inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) { case Maxwell::Cull::FrontFace::CounterClockWise: return GL_CCW; } - LOG_CRITICAL(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face)); - UNREACHABLE(); - return {}; + LOG_ERROR(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face)); + return GL_CCW; } inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) { @@ -394,9 +392,8 @@ inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) { case Maxwell::Cull::CullFace::FrontAndBack: return GL_FRONT_AND_BACK; } - LOG_CRITICAL(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face)); - UNREACHABLE(); - return {}; + LOG_ERROR(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face)); + return GL_BACK; } inline GLenum LogicOp(Maxwell::LogicOperation operation) { @@ -434,9 +431,8 @@ inline GLenum LogicOp(Maxwell::LogicOperation operation) { case Maxwell::LogicOperation::Set: return GL_SET; } - LOG_CRITICAL(Render_OpenGL, "Unimplemented logic operation={}", static_cast<u32>(operation)); - UNREACHABLE(); - return {}; + LOG_ERROR(Render_OpenGL, "Unimplemented logic operation={}", static_cast<u32>(operation)); + return GL_COPY; } } // 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, return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8; case Tegra::Texture::TextureFormat::ASTC_2D_8X5: return is_srgb ? PixelFormat::ASTC_2D_8X5_SRGB : PixelFormat::ASTC_2D_8X5; + case Tegra::Texture::TextureFormat::ASTC_2D_10X8: + return is_srgb ? PixelFormat::ASTC_2D_10X8_SRGB : PixelFormat::ASTC_2D_10X8; case Tegra::Texture::TextureFormat::R16_G16: switch (component_type) { case Tegra::Texture::ComponentType::FLOAT: @@ -453,6 +455,8 @@ bool IsPixelFormatASTC(PixelFormat format) { case PixelFormat::ASTC_2D_5X5_SRGB: case PixelFormat::ASTC_2D_8X8_SRGB: case PixelFormat::ASTC_2D_8X5_SRGB: + case PixelFormat::ASTC_2D_10X8: + case PixelFormat::ASTC_2D_10X8_SRGB: return true; default: 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 { ASTC_2D_5X4_SRGB = 56, ASTC_2D_5X5 = 57, ASTC_2D_5X5_SRGB = 58, + ASTC_2D_10X8 = 59, + ASTC_2D_10X8_SRGB = 60, MaxColorFormat, // Depth formats - Z32F = 59, - Z16 = 60, + Z32F = 61, + Z16 = 62, MaxDepthFormat, // DepthStencil formats - Z24S8 = 61, - S8Z24 = 62, - Z32FS8 = 63, + Z24S8 = 63, + S8Z24 = 64, + Z32FS8 = 65, MaxDepthStencilFormat, @@ -193,6 +195,8 @@ static constexpr u32 GetCompressionFactor(PixelFormat format) { 4, // ASTC_2D_5X4_SRGB 4, // ASTC_2D_5X5 4, // ASTC_2D_5X5_SRGB + 4, // ASTC_2D_10X8 + 4, // ASTC_2D_10X8_SRGB 1, // Z32F 1, // Z16 1, // Z24S8 @@ -208,70 +212,72 @@ static constexpr u32 GetDefaultBlockWidth(PixelFormat format) { if (format == PixelFormat::Invalid) return 0; constexpr std::array<u32, MaxPixelFormat> block_width_table = {{ - 1, // ABGR8U - 1, // ABGR8S - 1, // ABGR8UI - 1, // B5G6R5U - 1, // A2B10G10R10U - 1, // A1B5G5R5U - 1, // R8U - 1, // R8UI - 1, // RGBA16F - 1, // RGBA16U - 1, // RGBA16UI - 1, // R11FG11FB10F - 1, // RGBA32UI - 4, // DXT1 - 4, // DXT23 - 4, // DXT45 - 4, // DXN1 - 4, // DXN2UNORM - 4, // DXN2SNORM - 4, // BC7U - 4, // BC6H_UF16 - 4, // BC6H_SF16 - 4, // ASTC_2D_4X4 - 1, // G8R8U - 1, // G8R8S - 1, // BGRA8 - 1, // RGBA32F - 1, // RG32F - 1, // R32F - 1, // R16F - 1, // R16U - 1, // R16S - 1, // R16UI - 1, // R16I - 1, // RG16 - 1, // RG16F - 1, // RG16UI - 1, // RG16I - 1, // RG16S - 1, // RGB32F - 1, // RGBA8_SRGB - 1, // RG8U - 1, // RG8S - 1, // RG32UI - 1, // R32UI - 8, // ASTC_2D_8X8 - 8, // ASTC_2D_8X5 - 5, // ASTC_2D_5X4 - 1, // BGRA8_SRGB - 4, // DXT1_SRGB - 4, // DXT23_SRGB - 4, // DXT45_SRGB - 4, // BC7U_SRGB - 4, // ASTC_2D_4X4_SRGB - 8, // ASTC_2D_8X8_SRGB - 8, // ASTC_2D_8X5_SRGB - 5, // ASTC_2D_5X4_SRGB - 5, // ASTC_2D_5X5 - 5, // ASTC_2D_5X5_SRGB - 1, // Z32F - 1, // Z16 - 1, // Z24S8 - 1, // S8Z24 - 1, // Z32FS8 + 1, // ABGR8U + 1, // ABGR8S + 1, // ABGR8UI + 1, // B5G6R5U + 1, // A2B10G10R10U + 1, // A1B5G5R5U + 1, // R8U + 1, // R8UI + 1, // RGBA16F + 1, // RGBA16U + 1, // RGBA16UI + 1, // R11FG11FB10F + 1, // RGBA32UI + 4, // DXT1 + 4, // DXT23 + 4, // DXT45 + 4, // DXN1 + 4, // DXN2UNORM + 4, // DXN2SNORM + 4, // BC7U + 4, // BC6H_UF16 + 4, // BC6H_SF16 + 4, // ASTC_2D_4X4 + 1, // G8R8U + 1, // G8R8S + 1, // BGRA8 + 1, // RGBA32F + 1, // RG32F + 1, // R32F + 1, // R16F + 1, // R16U + 1, // R16S + 1, // R16UI + 1, // R16I + 1, // RG16 + 1, // RG16F + 1, // RG16UI + 1, // RG16I + 1, // RG16S + 1, // RGB32F + 1, // RGBA8_SRGB + 1, // RG8U + 1, // RG8S + 1, // RG32UI + 1, // R32UI + 8, // ASTC_2D_8X8 + 8, // ASTC_2D_8X5 + 5, // ASTC_2D_5X4 + 1, // BGRA8_SRGB + 4, // DXT1_SRGB + 4, // DXT23_SRGB + 4, // DXT45_SRGB + 4, // BC7U_SRGB + 4, // ASTC_2D_4X4_SRGB + 8, // ASTC_2D_8X8_SRGB + 8, // ASTC_2D_8X5_SRGB + 5, // ASTC_2D_5X4_SRGB + 5, // ASTC_2D_5X5 + 5, // ASTC_2D_5X5_SRGB + 10, // ASTC_2D_10X8 + 10, // ASTC_2D_10X8_SRGB + 1, // Z32F + 1, // Z16 + 1, // Z24S8 + 1, // S8Z24 + 1, // Z32FS8 }}; ASSERT(static_cast<std::size_t>(format) < block_width_table.size()); return block_width_table[static_cast<std::size_t>(format)]; @@ -341,6 +347,8 @@ static constexpr u32 GetDefaultBlockHeight(PixelFormat format) { 4, // ASTC_2D_5X4_SRGB 5, // ASTC_2D_5X5 5, // ASTC_2D_5X5_SRGB + 8, // ASTC_2D_10X8 + 8, // ASTC_2D_10X8_SRGB 1, // Z32F 1, // Z16 1, // Z24S8 @@ -416,6 +424,8 @@ static constexpr u32 GetFormatBpp(PixelFormat format) { 128, // ASTC_2D_5X4_SRGB 128, // ASTC_2D_5X5 128, // ASTC_2D_5X5_SRGB + 128, // ASTC_2D_10X8 + 128, // ASTC_2D_10X8_SRGB 32, // Z32F 16, // Z16 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) { case TextureFormat::ASTC_2D_5X4: case TextureFormat::ASTC_2D_8X8: case TextureFormat::ASTC_2D_8X5: + case TextureFormat::ASTC_2D_10X8: + case TextureFormat::ASTC_2D_5X5: case TextureFormat::A8R8G8B8: case TextureFormat::A2B10G10R10: case TextureFormat::BF10GF11RF11: @@ -294,6 +296,8 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat case TextureFormat::BC6H_SF16: case TextureFormat::ASTC_2D_4X4: case TextureFormat::ASTC_2D_8X8: + case TextureFormat::ASTC_2D_5X5: + case TextureFormat::ASTC_2D_10X8: case TextureFormat::A8R8G8B8: case TextureFormat::A2B10G10R10: 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 { // High 16 bits of the pitch value BitField<0, 16, u32> pitch_high; - + BitField<26, 1, u32> use_header_opt_control; + BitField<27, 1, u32> depth_texture; BitField<28, 4, u32> max_mip_level; }; union { BitField<0, 16, u32> width_minus_1; BitField<22, 1, u32> srgb_conversion; BitField<23, 4, TextureType> texture_type; + BitField<29, 3, u32> border_size; }; union { BitField<0, 16, u32> height_minus_1; BitField<16, 15, u32> depth_minus_1; }; + union { + BitField<6, 13, u32> mip_lod_bias; + BitField<27, 3, u32> max_anisotropy; + }; - INSERT_PADDING_BYTES(8); + union { + BitField<0, 4, u32> res_min_mip_level; + BitField<4, 4, u32> res_max_mip_level; + }; GPUVAddr Address() const { 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() { Service::Account::MAX_USERS - 1); Settings::values.language_index = qt_config->value("language_index", 1).toInt(); + + const auto enabled = qt_config->value("rng_seed_enabled", false).toBool(); + if (enabled) { + Settings::values.rng_seed = qt_config->value("rng_seed", 0).toULongLong(); + } else { + Settings::values.rng_seed = std::nullopt; + } + qt_config->endGroup(); qt_config->beginGroup("Miscellaneous"); @@ -272,6 +280,10 @@ void Config::SaveValues() { qt_config->setValue("current_user", Settings::values.current_user); qt_config->setValue("language_index", Settings::values.language_index); + + qt_config->setValue("rng_seed_enabled", Settings::values.rng_seed.has_value()); + qt_config->setValue("rng_seed", Settings::values.rng_seed.value_or(0)); + qt_config->endGroup(); 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) connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureSystem::DeleteUser); connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureSystem::SetUserImage); + connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) { + ui->rng_seed_edit->setEnabled(checked); + if (!checked) + ui->rng_seed_edit->setText(QStringLiteral("00000000")); + }); + scene = new QGraphicsScene; ui->current_user_icon->setScene(scene); @@ -155,6 +161,13 @@ void ConfigureSystem::setConfiguration() { PopulateUserList(); UpdateCurrentUser(); + + ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value()); + ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.has_value()); + + const auto rng_seed = + QString("%1").arg(Settings::values.rng_seed.value_or(0), 8, 16, QLatin1Char{'0'}).toUpper(); + ui->rng_seed_edit->setText(rng_seed); } void ConfigureSystem::PopulateUserList() { @@ -195,6 +208,12 @@ void ConfigureSystem::applyConfiguration() { return; Settings::values.language_index = ui->combo_language->currentIndex(); + + if (ui->rng_seed_checkbox->isChecked()) + Settings::values.rng_seed = ui->rng_seed_edit->text().toULongLong(nullptr, 16); + else + Settings::values.rng_seed = std::nullopt; + Settings::Apply(); } @@ -240,7 +259,7 @@ void ConfigureSystem::RefreshConsoleID() { void ConfigureSystem::SelectUser(const QModelIndex& index) { Settings::values.current_user = - std::clamp<std::size_t>(index.row(), 0, profile_manager->GetUserCount() - 1); + std::clamp<s32>(index.row(), 0, static_cast<s32>(profile_manager->GetUserCount() - 1)); UpdateCurrentUser(); 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 @@ <rect> <x>0</x> <y>0</y> - <width>360</width> + <width>366</width> <height>483</height> </rect> </property> @@ -22,98 +22,6 @@ <string>System Settings</string> </property> <layout class="QGridLayout" name="gridLayout"> - <item row="1" column="0"> - <widget class="QLabel" name="label_language"> - <property name="text"> - <string>Language</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="label_birthday"> - <property name="text"> - <string>Birthday</string> - </property> - </widget> - </item> - <item row="3" column="0"> - <widget class="QLabel" name="label_console_id"> - <property name="text"> - <string>Console ID:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <layout class="QHBoxLayout" name="horizontalLayout_birthday2"> - <item> - <widget class="QComboBox" name="combo_birthmonth"> - <item> - <property name="text"> - <string>January</string> - </property> - </item> - <item> - <property name="text"> - <string>February</string> - </property> - </item> - <item> - <property name="text"> - <string>March</string> - </property> - </item> - <item> - <property name="text"> - <string>April</string> - </property> - </item> - <item> - <property name="text"> - <string>May</string> - </property> - </item> - <item> - <property name="text"> - <string>June</string> - </property> - </item> - <item> - <property name="text"> - <string>July</string> - </property> - </item> - <item> - <property name="text"> - <string>August</string> - </property> - </item> - <item> - <property name="text"> - <string>September</string> - </property> - </item> - <item> - <property name="text"> - <string>October</string> - </property> - </item> - <item> - <property name="text"> - <string>November</string> - </property> - </item> - <item> - <property name="text"> - <string>December</string> - </property> - </item> - </widget> - </item> - <item> - <widget class="QComboBox" name="combo_birthday"/> - </item> - </layout> - </item> <item row="1" column="1"> <widget class="QComboBox" name="combo_language"> <property name="toolTip"> @@ -206,6 +114,13 @@ </item> </widget> </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_console_id"> + <property name="text"> + <string>Console ID:</string> + </property> + </widget> + </item> <item row="2" column="0"> <widget class="QLabel" name="label_sound"> <property name="text"> @@ -213,6 +128,100 @@ </property> </widget> </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_birthday"> + <property name="text"> + <string>Birthday</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_birthday2"> + <item> + <widget class="QComboBox" name="combo_birthmonth"> + <item> + <property name="text"> + <string>January</string> + </property> + </item> + <item> + <property name="text"> + <string>February</string> + </property> + </item> + <item> + <property name="text"> + <string>March</string> + </property> + </item> + <item> + <property name="text"> + <string>April</string> + </property> + </item> + <item> + <property name="text"> + <string>May</string> + </property> + </item> + <item> + <property name="text"> + <string>June</string> + </property> + </item> + <item> + <property name="text"> + <string>July</string> + </property> + </item> + <item> + <property name="text"> + <string>August</string> + </property> + </item> + <item> + <property name="text"> + <string>September</string> + </property> + </item> + <item> + <property name="text"> + <string>October</string> + </property> + </item> + <item> + <property name="text"> + <string>November</string> + </property> + </item> + <item> + <property name="text"> + <string>December</string> + </property> + </item> + </widget> + </item> + <item> + <widget class="QComboBox" name="combo_birthday"/> + </item> + </layout> + </item> + <item row="3" column="1"> + <widget class="QPushButton" name="button_regenerate_console_id"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="layoutDirection"> + <enum>Qt::RightToLeft</enum> + </property> + <property name="text"> + <string>Regenerate</string> + </property> + </widget> + </item> <item row="2" column="1"> <widget class="QComboBox" name="combo_sound"> <item> @@ -232,19 +241,38 @@ </item> </widget> </item> - <item row="3" column="1"> - <widget class="QPushButton" name="button_regenerate_console_id"> + <item row="1" column="0"> + <widget class="QLabel" name="label_language"> + <property name="text"> + <string>Language</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QCheckBox" name="rng_seed_checkbox"> + <property name="text"> + <string>RNG Seed</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLineEdit" name="rng_seed_edit"> <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="layoutDirection"> - <enum>Qt::RightToLeft</enum> + <property name="font"> + <font> + <family>Lucida Console</family> + </font> </property> - <property name="text"> - <string>Regenerate</string> + <property name="inputMask"> + <string notr="true">HHHHHHHH</string> + </property> + <property name="maxLength"> + <number>8</number> </property> </widget> </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 const auto full = res == "Full"; const auto entry_size = CalculateRomFSEntrySize(extracted, full); - QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, entry_size, this); + QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, + static_cast<s32>(entry_size), this); progress.setWindowModality(Qt::WindowModal); progress.setMinimumDuration(100); @@ -1621,7 +1622,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) { return; } - if (ui.action_Fullscreen->isChecked()) { + if (!ui.action_Fullscreen->isChecked()) { UISettings::values.geometry = saveGeometry(); UISettings::values.renderwindow_geometry = render_window->saveGeometry(); } 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 @@ <addaction name="separator"/> <addaction name="action_Load_Amiibo"/> <addaction name="separator"/> + <addaction name="action_Open_yuzu_Folder"/> + <addaction name="separator"/> <addaction name="action_Exit"/> </widget> <widget class="QMenu" name="menu_Emulation"> @@ -110,7 +112,6 @@ <string>&Help</string> </property> <addaction name="action_Report_Compatibility"/> - <addaction name="action_Open_yuzu_Folder" /> <addaction name="separator"/> <addaction name="action_About"/> </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() { Settings::values.current_user = std::clamp<int>( sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); + const auto enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false); + if (enabled) { + Settings::values.rng_seed = sdl2_config->GetInteger("System", "rng_seed", 0); + } else { + Settings::values.rng_seed = std::nullopt; + } + // Miscellaneous Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 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 = # 1 (default): Yes, 0 : No enable_nfc = +# Sets the seed for the RNG generator built into the switch +# rng_seed will be ignored and randomly generated if rng_seed_enabled is false +rng_seed_enabled = +rng_seed = + # Sets the account username, max length is 32 characters # yuzu (default) username = yuzu |