diff options
Diffstat (limited to '')
49 files changed, 647 insertions, 350 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/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index 76a2b7e86..e29f70b3a 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp @@ -8,8 +8,9 @@ namespace FileSys { -BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_) +BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_) : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)), + dump_root(std::move(dump_root_)), sysnand_cache(std::make_unique<RegisteredCache>( GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), usrnand_cache(std::make_unique<RegisteredCache>( @@ -32,4 +33,10 @@ VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const { return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id)); } +VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const { + if (title_id == 0) + return nullptr; + return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id)); +} + } // namespace FileSys diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h index 364d309bd..453c11ad2 100644 --- a/src/core/file_sys/bis_factory.h +++ b/src/core/file_sys/bis_factory.h @@ -17,17 +17,19 @@ class RegisteredCache; /// registered caches. class BISFactory { public: - explicit BISFactory(VirtualDir nand_root, VirtualDir load_root); + explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root); ~BISFactory(); RegisteredCache* GetSystemNANDContents() const; RegisteredCache* GetUserNANDContents() const; VirtualDir GetModificationLoadRoot(u64 title_id) const; + VirtualDir GetModificationDumpRoot(u64 title_id) const; private: VirtualDir nand_root; VirtualDir load_root; + VirtualDir dump_root; std::unique_ptr<RegisteredCache> sysnand_cache; std::unique_ptr<RegisteredCache> usrnand_cache; 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/errors.h b/src/core/file_sys/errors.h index fea0593c7..e4a4ee4ab 100644 --- a/src/core/file_sys/errors.h +++ b/src/core/file_sys/errors.h @@ -8,25 +8,10 @@ namespace FileSys { -namespace ErrCodes { -enum { - NotFound = 1, - TitleNotFound = 1002, - SdCardNotFound = 2001, - RomFSNotFound = 2520, -}; -} - -constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound); - -// TODO(bunnei): Replace these with correct errors for Switch OS -constexpr ResultCode ERROR_INVALID_PATH(-1); -constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(-1); -constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(-1); -constexpr ResultCode ERROR_FILE_NOT_FOUND(-1); -constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(-1); -constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(-1); -constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(-1); -constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(-1); +constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1}; +constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002}; +constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001}; +constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061}; +constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062}; } // namespace FileSys diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 0c1156989..8d062eb3e 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -19,6 +19,7 @@ #include "core/file_sys/vfs_vector.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" +#include "core/settings.h" namespace FileSys { @@ -119,6 +120,18 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { const auto build_id_raw = Common::HexArrayToString(header.build_id); const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); + if (Settings::values.dump_nso) { + LOG_INFO(Loader, "Dumping NSO for build_id={}, title_id={:016X}", build_id, title_id); + const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); + if (dump_dir != nullptr) { + const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso"); + const auto file = nso_dir->CreateFile(fmt::format("{}.nso", build_id)); + + file->Resize(nso.size()); + file->WriteBytes(nso); + } + } + LOG_INFO(Loader, "Patching NSO for build_id={}", build_id); const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 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 0bfe1e3be..b0b6508d9 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -559,7 +559,16 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) *result = 0; break; case GetInfoType::RandomEntropy: - *result = Settings::values.rng_seed.value_or(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/thread.cpp b/src/core/hle/kernel/thread.cpp index dd5cd9ced..4ffb76818 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -142,36 +142,7 @@ void Thread::ResumeFromWait() { status = ThreadStatus::Ready; - std::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask); - if (!new_processor_id) { - new_processor_id = processor_id; - } - if (ideal_core != -1 && - Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) { - new_processor_id = ideal_core; - } - - ASSERT(*new_processor_id < 4); - - // Add thread to new core's scheduler - auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id); - - if (*new_processor_id != processor_id) { - // Remove thread from previous core's scheduler - scheduler->RemoveThread(this); - next_scheduler->AddThread(this, current_priority); - } - - processor_id = *new_processor_id; - - // If the thread was ready, unschedule from the previous core and schedule on the new core - scheduler->UnscheduleThread(this, current_priority); - next_scheduler->ScheduleThread(this, current_priority); - - // Change thread's scheduler - scheduler = next_scheduler; - - Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); + ChangeScheduler(); } /** @@ -364,42 +335,45 @@ void Thread::UpdatePriority() { void Thread::ChangeCore(u32 core, u64 mask) { ideal_core = core; affinity_mask = mask; + ChangeScheduler(); +} +void Thread::ChangeScheduler() { if (status != ThreadStatus::Ready) { return; } + auto& system = Core::System::GetInstance(); std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)}; if (!new_processor_id) { new_processor_id = processor_id; } - if (ideal_core != -1 && - Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) { + if (ideal_core != -1 && system.Scheduler(ideal_core).GetCurrentThread() == nullptr) { new_processor_id = ideal_core; } ASSERT(*new_processor_id < 4); // Add thread to new core's scheduler - auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id); + auto& next_scheduler = system.Scheduler(*new_processor_id); if (*new_processor_id != processor_id) { // Remove thread from previous core's scheduler scheduler->RemoveThread(this); - next_scheduler->AddThread(this, current_priority); + next_scheduler.AddThread(this, current_priority); } processor_id = *new_processor_id; // If the thread was ready, unschedule from the previous core and schedule on the new core scheduler->UnscheduleThread(this, current_priority); - next_scheduler->ScheduleThread(this, current_priority); + next_scheduler.ScheduleThread(this, current_priority); // Change thread's scheduler - scheduler = next_scheduler; + scheduler = &next_scheduler; - Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); + system.CpuCore(processor_id).PrepareReschedule(); } bool Thread::AllWaitObjectsReady() { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 4a6e11239..d384d50db 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -374,6 +374,8 @@ private: explicit Thread(KernelCore& kernel); ~Thread() override; + void ChangeScheduler(); + Core::ARM_Interface::ThreadContext context{}; u32 thread_id = 0; 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/result.h b/src/core/hle/result.h index c6b18cfba..bfb77cc31 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -19,8 +19,6 @@ enum class ErrorDescription : u32 { Success = 0, RemoteProcessDead = 301, - InvalidOffset = 6061, - InvalidLength = 6062, }; /** 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..d3ea57ea7 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -28,13 +28,13 @@ public: {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"}, {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"}, {3, &IAudioRenderer::GetState, "GetState"}, - {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"}, + {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"}, {5, &IAudioRenderer::Start, "Start"}, {6, &IAudioRenderer::Stop, "Stop"}, {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, - {8, nullptr, "SetRenderingTimeLimit"}, - {9, nullptr, "GetRenderingTimeLimit"}, - {10, nullptr, "RequestUpdateAuto"}, + {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"}, + {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, + {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"}, {11, nullptr, "ExecuteAudioRendererRendering"}, }; // clang-format on @@ -79,7 +79,7 @@ private: LOG_DEBUG(Service_Audio, "called"); } - void RequestUpdate(Kernel::HLERequestContext& ctx) { + void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -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..554474cf6 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -303,17 +303,27 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, static_cast<u8>(space), save_struct.DebugInfo()); if (save_data_factory == nullptr) { - return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound); + return FileSys::ERROR_ENTITY_NOT_FOUND; } 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 FileSys::ERROR_ENTITY_NOT_FOUND; + } + + return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space)); +} + ResultVal<FileSys::VirtualDir> OpenSDMC() { LOG_TRACE(Service_FS, "Opening SDMC"); if (sdmc_factory == nullptr) { - return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SdCardNotFound); + return FileSys::ERROR_SD_CARD_NOT_FOUND; } return sdmc_factory->Open(); @@ -360,6 +370,15 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) { return bis_factory->GetModificationLoadRoot(title_id); } +FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) { + LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id); + + if (bis_factory == nullptr) + return nullptr; + + return bis_factory->GetModificationDumpRoot(title_id); +} + void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { if (overwrite) { bis_factory = nullptr; @@ -373,13 +392,21 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { FileSys::Mode::ReadWrite); auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), FileSys::Mode::ReadWrite); + auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), + FileSys::Mode::ReadWrite); - if (bis_factory == nullptr) - bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory); - if (save_data_factory == nullptr) + if (bis_factory == nullptr) { + bis_factory = + std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory); + } + + if (save_data_factory == nullptr) { save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); - if (sdmc_factory == nullptr) + } + + if (sdmc_factory == nullptr) { sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); + } } void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) { diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 6ca5c5636..e38f02869 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(); @@ -54,6 +55,7 @@ FileSys::RegisteredCache* GetUserNANDContents(); FileSys::RegisteredCache* GetSDMCContents(); FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); +FileSys::VirtualDir GetModificationDumpRoot(u64 title_id); // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function // above is called. diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index c1c83a11d..038dc80b1 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" @@ -62,12 +63,12 @@ private: // Error checking if (length < 0) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + rb.Push(FileSys::ERROR_INVALID_SIZE); return; } if (offset < 0) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + rb.Push(FileSys::ERROR_INVALID_OFFSET); return; } @@ -107,12 +108,12 @@ private: // Error checking if (length < 0) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + rb.Push(FileSys::ERROR_INVALID_SIZE); return; } if (offset < 0) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + rb.Push(FileSys::ERROR_INVALID_OFFSET); return; } @@ -138,12 +139,12 @@ private: // Error checking if (length < 0) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + rb.Push(FileSys::ERROR_INVALID_SIZE); return; } if (offset < 0) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + rb.Push(FileSys::ERROR_INVALID_OFFSET); return; } @@ -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); } @@ -602,7 +744,7 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { if (dir.Failed()) { IPC::ResponseBuilder rb{ctx, 2, 0, 0}; - rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound)); + rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); return; } @@ -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"); @@ -685,7 +836,7 @@ void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { static_cast<u8>(storage_id), title_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound)); + rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); } } // namespace Service::FileSystem 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..205e4fd14 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -40,6 +40,29 @@ enum class JoystickId : std::size_t { Joystick_Right, }; +static std::size_t NPadIdToIndex(u32 npad_id) { + switch (npad_id) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + return npad_id; + case 8: + case NPAD_HANDHELD: + return 8; + case 9: + case NPAD_UNKNOWN: + return 9; + default: + UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id); + return 0; + } +} + Controller_NPad::Controller_NPad() = default; Controller_NPad::~Controller_NPad() = default; @@ -288,10 +311,11 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) { switch (controller_type) { case NPadControllerType::Handheld: handheld_entry.connection_status.raw = 0; - handheld_entry.connection_status.IsConnected.Assign(1); - if (!Settings::values.use_docked_mode) { - handheld_entry.connection_status.IsWired.Assign(1); - } + handheld_entry.connection_status.IsWired.Assign(1); + handheld_entry.connection_status.IsLeftJoyConnected.Assign(1); + handheld_entry.connection_status.IsRightJoyConnected.Assign(1); + handheld_entry.connection_status.IsLeftJoyWired.Assign(1); + handheld_entry.connection_status.IsRightJoyWired.Assign(1); handheld_entry.pad_states.raw = pad_state.raw; handheld_entry.l_stick = lstick_entry; handheld_entry.r_stick = rstick_entry; @@ -310,6 +334,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; @@ -370,16 +395,30 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { supported_npad_id_types.clear(); supported_npad_id_types.resize(length / sizeof(u32)); std::memcpy(supported_npad_id_types.data(), data, length); + bool had_controller_update = false; for (std::size_t i = 0; i < connected_controllers.size(); i++) { auto& controller = connected_controllers[i]; if (!controller.is_connected) { continue; } if (!IsControllerSupported(PREFERRED_CONTROLLER)) { - controller.type = DecideBestController(PREFERRED_CONTROLLER); - InitNewlyAddedControler(i); + const auto best_type = DecideBestController(PREFERRED_CONTROLLER); + const bool is_handheld = (best_type == NPadControllerType::Handheld || + PREFERRED_CONTROLLER == NPadControllerType::Handheld); + if (is_handheld) { + controller.type = NPadControllerType::None; + controller.is_connected = false; + AddNewController(best_type); + } else { + controller.type = best_type; + InitNewlyAddedControler(i); + } + had_controller_update = true; } } + if (had_controller_update) { + styleset_changed_event->Signal(); + } } void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { @@ -457,15 +496,11 @@ void Controller_NPad::AddNewController(NPadControllerType controller) { } void Controller_NPad::ConnectNPad(u32 npad_id) { - if (npad_id >= connected_controllers.size()) - return; - connected_controllers[npad_id].is_connected = true; + connected_controllers[NPadIdToIndex(npad_id)].is_connected = true; } void Controller_NPad::DisconnectNPad(u32 npad_id) { - if (npad_id >= connected_controllers.size()) - return; - connected_controllers[npad_id].is_connected = false; + connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; } Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index a45fd4954..39631b14f 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -286,10 +286,10 @@ public: {519, nullptr, "GetPalmaOperationResult"}, {520, nullptr, "ReadPalmaPlayLog"}, {521, nullptr, "ResetPalmaPlayLog"}, - {522, nullptr, "SetIsPalmaAllConnectable"}, + {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"}, {523, nullptr, "SetIsPalmaPairedConnectable"}, {524, nullptr, "PairPalma"}, - {525, nullptr, "SetPalmaBoostMode"}, + {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, {1000, nullptr, "SetNpadCommunicationMode"}, {1001, nullptr, "GetNpadCommunicationMode"}, }; @@ -596,6 +596,18 @@ private: rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_HID, "(STUBBED) called"); } + + void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } }; class HidDbg final : public ServiceFramework<HidDbg> { 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 69c260408..b2de2a818 100644 --- a/src/core/hle/service/spl/module.cpp +++ b/src/core/hle/service/spl/module.cpp @@ -28,8 +28,9 @@ void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { std::size_t size = ctx.GetWriteBufferSize(); + std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max()); std::vector<u8> data(size); - std::generate(data.begin(), data.end(), rng); + std::generate(data.begin(), data.end(), [&] { return static_cast<u8>(distribution(rng)); }); ctx.WriteBuffer(data); 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/loader/nso.cpp b/src/core/loader/nso.cpp index 68efca5c0..aaf006309 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -154,7 +154,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAd program_image.resize(image_size); // Apply patches if necessary - if (pm && pm->HasNSOPatch(nso_header.build_id)) { + if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { std::vector<u8> pi_header(program_image.size() + 0x100); std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); diff --git a/src/core/settings.h b/src/core/settings.h index 83a1a7069..e424479f2 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -115,7 +115,7 @@ struct Values { // System bool use_docked_mode; bool enable_nfc; - std::optional<u64> rng_seed; + std::optional<u32> rng_seed; s32 current_user; s32 language_index; @@ -159,6 +159,7 @@ struct Values { bool use_gdbstub; u16 gdbstub_port; std::string program_args; + bool dump_nso; // WebService bool enable_telemetry; diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index d1777b25b..6de07ea56 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -121,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/rasterizer_cache.h b/src/video_core/rasterizer_cache.h index 6d41321fa..bcf0c15a4 100644 --- a/src/video_core/rasterizer_cache.h +++ b/src/video_core/rasterizer_cache.h @@ -5,6 +5,7 @@ #pragma once #include <set> +#include <unordered_map> #include <boost/icl/interval_map.hpp> #include <boost/range/iterator_range_core.hpp> @@ -88,29 +89,25 @@ public: /// Invalidates everything in the cache void InvalidateAll() { - while (object_cache.begin() != object_cache.end()) { - Unregister(*object_cache.begin()->second.begin()); + while (interval_cache.begin() != interval_cache.end()) { + Unregister(*interval_cache.begin()->second.begin()); } } protected: /// Tries to get an object from the cache with the specified address T TryGet(VAddr addr) const { - const ObjectInterval interval{addr}; - for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) { - for (auto& cached_object : pair.second) { - if (cached_object->GetAddr() == addr) { - return cached_object; - } - } - } + const auto iter = map_cache.find(addr); + if (iter != map_cache.end()) + return iter->second; return nullptr; } /// Register an object into the cache void Register(const T& object) { object->SetIsRegistered(true); - object_cache.add({GetInterval(object), ObjectSet{object}}); + interval_cache.add({GetInterval(object), ObjectSet{object}}); + map_cache.insert({object->GetAddr(), object}); rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1); } @@ -118,13 +115,13 @@ protected: void Unregister(const T& object) { object->SetIsRegistered(false); rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1); - // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit if (Settings::values.use_accurate_gpu_emulation) { FlushObject(object); } - object_cache.subtract({GetInterval(object), ObjectSet{object}}); + interval_cache.subtract({GetInterval(object), ObjectSet{object}}); + map_cache.erase(object->GetAddr()); } /// Returns a ticks counter used for tracking when cached objects were last modified @@ -141,7 +138,7 @@ private: std::vector<T> objects; const ObjectInterval interval{addr, addr + size}; - for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) { + for (auto& pair : boost::make_iterator_range(interval_cache.equal_range(interval))) { for (auto& cached_object : pair.second) { if (!cached_object) { continue; @@ -167,15 +164,17 @@ private: } using ObjectSet = std::set<T>; - using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>; - using ObjectInterval = typename ObjectCache::interval_type; + using ObjectCache = std::unordered_map<VAddr, T>; + using IntervalCache = boost::icl::interval_map<VAddr, ObjectSet>; + using ObjectInterval = typename IntervalCache::interval_type; static auto GetInterval(const T& object) { return ObjectInterval::right_open(object->GetAddr(), object->GetAddr() + object->GetSizeInBytes()); } - ObjectCache object_cache; ///< Cache of objects - u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing + ObjectCache map_cache; + IntervalCache interval_cache; ///< Cache of objects + u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing VideoCore::RasterizerInterface& rasterizer; }; 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_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 864f426f7..26711e6f7 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -715,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); @@ -750,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); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index d3b7fa59d..60606bb33 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -153,6 +153,7 @@ void Config::ReadValues() { Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString(); + Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool(); qt_config->endGroup(); qt_config->beginGroup("WebService"); @@ -295,6 +296,7 @@ void Config::SaveValues() { qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args)); + qt_config->setValue("dump_nso", Settings::values.dump_nso); qt_config->endGroup(); qt_config->beginGroup("WebService"); diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 9e765fc93..fd5876b41 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -34,6 +34,7 @@ void ConfigureDebug::setConfiguration() { ui->toggle_console->setChecked(UISettings::values.show_console); ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); + ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso); } void ConfigureDebug::applyConfiguration() { @@ -42,6 +43,7 @@ void ConfigureDebug::applyConfiguration() { UISettings::values.show_console = ui->toggle_console->isChecked(); Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); + Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked(); Debugger::ToggleConsole(); Log::Filter filter; filter.ParseFilterString(Settings::values.log_filter); diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index ff4987604..9c5b702f8 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>400</width> - <height>300</height> + <height>357</height> </rect> </property> <property name="windowTitle"> @@ -130,6 +130,25 @@ </widget> </item> <item> + <widget class="QGroupBox" name="groupBox_4"> + <property name="title"> + <string>Dump</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QCheckBox" name="dump_decompressed_nso"> + <property name="whatsThis"> + <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string> + </property> + <property name="text"> + <string>Dump Decompressed NSOs</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index b322258a0..314f51203 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -47,6 +47,9 @@ void ConfigureGeneral::OnDockedModeChanged(bool last_state, bool new_state) { } Core::System& system{Core::System::GetInstance()}; + if (!system.IsPoweredOn()) { + return; + } Service::SM::ServiceManager& sm = system.ServiceManager(); // Message queue is shared between these services, we just need to signal an operation diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 67f07ecb1..ab5d46492 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -140,7 +140,7 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) 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("0000000000000000")); + ui->rng_seed_edit->setText(QStringLiteral("00000000")); }); scene = new QGraphicsScene; @@ -165,9 +165,8 @@ void ConfigureSystem::setConfiguration() { 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), 16, 16, QLatin1Char{'0'}) - .toUpper(); + 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); } diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index d0fcd0163..a91580893 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui @@ -269,10 +269,10 @@ </font> </property> <property name="inputMask"> - <string>HHHHHHHHHHHHHHHH</string> + <string notr="true">HHHHHHHH</string> </property> <property name="maxLength"> - <number>16</number> + <number>8</number> </property> </widget> </item> 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 f3134d4cb..9cc409fd5 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -148,6 +148,7 @@ void Config::ReadValues() { Settings::values.gdbstub_port = static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); + Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); // Web Service Settings::values.enable_telemetry = diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index dd6644d79..ecf625e7b 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -206,6 +206,8 @@ log_filter = *:Trace # Port for listening to GDB connections. use_gdbstub=false gdbstub_port=24689 +# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them +dump_nso=false [WebService] # Whether or not to enable telemetry |