diff options
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/file_sys/control_metadata.cpp | 6 | ||||
| -rw-r--r-- | src/core/file_sys/control_metadata.h | 1 | ||||
| -rw-r--r-- | src/core/file_sys/savedata_factory.cpp | 32 | ||||
| -rw-r--r-- | src/core/file_sys/savedata_factory.h | 3 | ||||
| -rw-r--r-- | src/core/hle/kernel/process.cpp | 82 | ||||
| -rw-r--r-- | src/core/hle/kernel/process.h | 22 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 11 | ||||
| -rw-r--r-- | src/core/hle/kernel/vm_manager.cpp | 82 | ||||
| -rw-r--r-- | src/core/hle/kernel/vm_manager.h | 15 | ||||
| -rw-r--r-- | src/core/hle/service/audio/audren_u.cpp | 25 | ||||
| -rw-r--r-- | src/core/hle/service/filesystem/filesystem.cpp | 10 | ||||
| -rw-r--r-- | src/core/hle/service/filesystem/filesystem.h | 1 | ||||
| -rw-r--r-- | src/core/hle/service/filesystem/fsp_srv.cpp | 153 | ||||
| -rw-r--r-- | src/core/hle/service/filesystem/fsp_srv.h | 1 | ||||
| -rw-r--r-- | src/core/hle/service/hid/controllers/npad.cpp | 1 | ||||
| -rw-r--r-- | src/core/hle/service/ns/ns.cpp | 64 | ||||
| -rw-r--r-- | src/core/hle/service/service.cpp | 4 | ||||
| -rw-r--r-- | src/core/loader/nro.cpp | 5 | ||||
| -rw-r--r-- | src/core/settings.h | 2 | 
19 files changed, 412 insertions, 108 deletions
| 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 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/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/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/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/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 83a1a7069..84dc5050b 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; | 
