diff options
Diffstat (limited to 'src/core')
56 files changed, 1736 insertions, 420 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e02ededfc..e4f499135 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -466,14 +466,18 @@ add_library(core STATIC hle/service/caps/caps_a.h hle/service/caps/caps_c.cpp hle/service/caps/caps_c.h - hle/service/caps/caps_u.cpp - hle/service/caps/caps_u.h + hle/service/caps/caps_manager.cpp + hle/service/caps/caps_manager.h + hle/service/caps/caps_result.h hle/service/caps/caps_sc.cpp hle/service/caps/caps_sc.h hle/service/caps/caps_ss.cpp hle/service/caps/caps_ss.h hle/service/caps/caps_su.cpp hle/service/caps/caps_su.h + hle/service/caps/caps_types.h + hle/service/caps/caps_u.cpp + hle/service/caps/caps_u.h hle/service/erpt/erpt.cpp hle/service/erpt/erpt.h hle/service/es/es.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index 08cbb8978..d7e2efbd7 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -116,11 +116,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, } } - if (concat.empty()) { - return nullptr; - } - - return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName()); + return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(dir->GetName(), + std::move(concat)); } if (Common::FS::IsDir(path)) { @@ -1078,6 +1075,10 @@ void System::ApplySettings() { impl->RefreshTime(); if (IsPoweredOn()) { + if (Settings::values.custom_rtc_enabled) { + const s64 posix_time{Settings::values.custom_rtc.GetValue()}; + GetTimeManager().UpdateLocalSystemClockTime(posix_time); + } Renderer().RefreshBaseSettings(); } } diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index e55831f27..82964f0a1 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include <atomic> +#include <codecvt> +#include <locale> #include <numeric> #include <optional> #include <thread> @@ -12,6 +14,7 @@ #include "common/logging/log.h" #include "common/scope_exit.h" #include "common/settings.h" +#include "common/string_util.h" #include "core/arm/arm_interface.h" #include "core/core.h" #include "core/debugger/gdbstub.h" @@ -68,10 +71,16 @@ static std::string EscapeGDB(std::string_view data) { } static std::string EscapeXML(std::string_view data) { + std::u32string converted = U"[Encoding error]"; + try { + converted = Common::UTF8ToUTF32(data); + } catch (std::range_error&) { + } + std::string escaped; escaped.reserve(data.size()); - for (char c : data) { + for (char32_t c : converted) { switch (c) { case '&': escaped += "&"; @@ -86,7 +95,11 @@ static std::string EscapeXML(std::string_view data) { escaped += ">"; break; default: - escaped += c; + if (c > 0x7f) { + escaped += fmt::format("&#{};", static_cast<u32>(c)); + } else { + escaped += static_cast<char>(c); + } break; } } diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp index e39c7b62b..f1d3e4129 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.cpp +++ b/src/core/file_sys/fsmitm_romfsbuild.cpp @@ -107,62 +107,56 @@ static u64 romfs_get_hash_table_count(u64 num_entries) { void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir, std::shared_ptr<RomFSBuildDirectoryContext> parent) { - std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs; + for (auto& child_romfs_file : romfs_dir->GetFiles()) { + const auto name = child_romfs_file->GetName(); + const auto child = std::make_shared<RomFSBuildFileContext>(); + // Set child's path. + child->cur_path_ofs = parent->path_len + 1; + child->path_len = child->cur_path_ofs + static_cast<u32>(name.size()); + child->path = parent->path + "/" + name; + + if (ext_dir != nullptr && ext_dir->GetFile(name + ".stub") != nullptr) { + continue; + } - const auto entries = romfs_dir->GetEntries(); + // Sanity check on path_len + ASSERT(child->path_len < FS_MAX_PATH); - for (const auto& kv : entries) { - if (kv.second == VfsEntryType::Directory) { - const auto child = std::make_shared<RomFSBuildDirectoryContext>(); - // Set child's path. - child->cur_path_ofs = parent->path_len + 1; - child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); - child->path = parent->path + "/" + kv.first; + child->source = std::move(child_romfs_file); - if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) { - continue; + if (ext_dir != nullptr) { + if (const auto ips = ext_dir->GetFile(name + ".ips")) { + if (auto patched = PatchIPS(child->source, ips)) { + child->source = std::move(patched); + } } + } - // Sanity check on path_len - ASSERT(child->path_len < FS_MAX_PATH); - - if (AddDirectory(parent, child)) { - child_dirs.push_back(child); - } - } else { - const auto child = std::make_shared<RomFSBuildFileContext>(); - // Set child's path. - child->cur_path_ofs = parent->path_len + 1; - child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); - child->path = parent->path + "/" + kv.first; - - if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) { - continue; - } + child->size = child->source->GetSize(); - // Sanity check on path_len - ASSERT(child->path_len < FS_MAX_PATH); + AddFile(parent, child); + } - child->source = romfs_dir->GetFile(kv.first); + for (auto& child_romfs_dir : romfs_dir->GetSubdirectories()) { + const auto name = child_romfs_dir->GetName(); + const auto child = std::make_shared<RomFSBuildDirectoryContext>(); + // Set child's path. + child->cur_path_ofs = parent->path_len + 1; + child->path_len = child->cur_path_ofs + static_cast<u32>(name.size()); + child->path = parent->path + "/" + name; - if (ext_dir != nullptr) { - if (const auto ips = ext_dir->GetFile(kv.first + ".ips")) { - if (auto patched = PatchIPS(child->source, ips)) { - child->source = std::move(patched); - } - } - } + if (ext_dir != nullptr && ext_dir->GetFile(name + ".stub") != nullptr) { + continue; + } - child->size = child->source->GetSize(); + // Sanity check on path_len + ASSERT(child->path_len < FS_MAX_PATH); - AddFile(parent, child); + if (!AddDirectory(parent, child)) { + continue; } - } - for (auto& child : child_dirs) { - auto subdir_name = std::string_view(child->path).substr(child->cur_path_ofs); - auto child_romfs_dir = romfs_dir->GetSubdirectory(subdir_name); - auto child_ext_dir = ext_dir != nullptr ? ext_dir->GetSubdirectory(subdir_name) : nullptr; + auto child_ext_dir = ext_dir != nullptr ? ext_dir->GetSubdirectory(name) : nullptr; this->VisitDirectory(child_romfs_dir, child_ext_dir, child); } } @@ -293,7 +287,7 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() { cur_entry.name_size = name_size; - out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->source); + out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, std::move(cur_file->source)); std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry)); std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0, Common::AlignUp(cur_entry.name_size, 4)); diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 8e475f25a..0bca05587 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -377,16 +377,16 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t auto romfs_dir = FindSubdirectoryCaseless(subdir, "romfs"); if (romfs_dir != nullptr) - layers.push_back(std::make_shared<CachedVfsDirectory>(romfs_dir)); + layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(romfs_dir))); auto ext_dir = FindSubdirectoryCaseless(subdir, "romfs_ext"); if (ext_dir != nullptr) - layers_ext.push_back(std::make_shared<CachedVfsDirectory>(ext_dir)); + layers_ext.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(ext_dir))); if (type == ContentRecordType::HtmlDocument) { auto manual_dir = FindSubdirectoryCaseless(subdir, "manual_html"); if (manual_dir != nullptr) - layers.push_back(std::make_shared<CachedVfsDirectory>(manual_dir)); + layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(manual_dir))); } } @@ -400,7 +400,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t return; } - layers.push_back(std::move(extracted)); + layers.emplace_back(std::move(extracted)); auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers)); if (layered == nullptr) { diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index f00479bd3..8e291ff67 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -5,6 +5,7 @@ #include <vector> #include "common/logging/log.h" +#include "common/scope_exit.h" #include "core/file_sys/program_metadata.h" #include "core/file_sys/vfs.h" #include "core/loader/loader.h" @@ -95,6 +96,13 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { return Loader::ResultStatus::Success; } +Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) { + const u64 original_program_id = aci_header.title_id; + SCOPE_EXIT({ aci_header.title_id = original_program_id; }); + + return this->Load(file); +} + /*static*/ ProgramMetadata ProgramMetadata::GetDefault() { // Allow use of cores 0~3 and thread priorities 1~63. constexpr u32 default_thread_info_capability = 0x30007F7; diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 2e8960b07..9f8e74b13 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -56,6 +56,7 @@ public: static ProgramMetadata GetDefault(); Loader::ResultStatus Load(VirtualFile file); + Loader::ResultStatus Reload(VirtualFile file); /// Load from parameters instead of NPDM file, used for KIP void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, s32 main_thread_prio, diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 04da93d5c..1cc77ad14 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -322,7 +322,8 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& open_di return nullptr; } - return ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName()); + auto name = concat.front()->GetName(); + return ConcatenatedVfsFile::MakeConcatenatedFile(std::move(name), std::move(concat)); } VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp index 614da2130..1c580de57 100644 --- a/src/core/file_sys/romfs.cpp +++ b/src/core/file_sys/romfs.cpp @@ -133,7 +133,7 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) { out = out->GetSubdirectories().front(); } - return std::make_shared<CachedVfsDirectory>(out); + return std::make_shared<CachedVfsDirectory>(std::move(out)); } VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) { @@ -141,8 +141,7 @@ VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) { return nullptr; RomFSBuildContext ctx{dir, ext}; - auto file_map = ctx.Build(); - return ConcatenatedVfsFile::MakeConcatenatedFile(0, file_map, dir->GetName()); + return ConcatenatedVfsFile::MakeConcatenatedFile(0, dir->GetName(), ctx.Build()); } } // namespace FileSys diff --git a/src/core/file_sys/vfs_cached.cpp b/src/core/file_sys/vfs_cached.cpp index c3154ee81..7ee5300e5 100644 --- a/src/core/file_sys/vfs_cached.cpp +++ b/src/core/file_sys/vfs_cached.cpp @@ -6,13 +6,13 @@ namespace FileSys { -CachedVfsDirectory::CachedVfsDirectory(VirtualDir& source_dir) +CachedVfsDirectory::CachedVfsDirectory(VirtualDir&& source_dir) : name(source_dir->GetName()), parent(source_dir->GetParentDirectory()) { for (auto& dir : source_dir->GetSubdirectories()) { - dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(dir)); + dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(std::move(dir))); } for (auto& file : source_dir->GetFiles()) { - files.emplace(file->GetName(), file); + files.emplace(file->GetName(), std::move(file)); } } diff --git a/src/core/file_sys/vfs_cached.h b/src/core/file_sys/vfs_cached.h index 113acac12..1e5300784 100644 --- a/src/core/file_sys/vfs_cached.h +++ b/src/core/file_sys/vfs_cached.h @@ -11,7 +11,7 @@ namespace FileSys { class CachedVfsDirectory : public ReadOnlyVfsDirectory { public: - CachedVfsDirectory(VirtualDir& source_directory); + CachedVfsDirectory(VirtualDir&& source_directory); ~CachedVfsDirectory() override; VirtualFile GetFile(std::string_view file_name) const override; diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp index 311a59e5f..168b9cbec 100644 --- a/src/core/file_sys/vfs_concat.cpp +++ b/src/core/file_sys/vfs_concat.cpp @@ -10,7 +10,7 @@ namespace FileSys { -ConcatenatedVfsFile::ConcatenatedVfsFile(ConcatenationMap&& concatenation_map_, std::string&& name_) +ConcatenatedVfsFile::ConcatenatedVfsFile(std::string&& name_, ConcatenationMap&& concatenation_map_) : concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) { DEBUG_ASSERT(this->VerifyContinuity()); } @@ -30,8 +30,8 @@ bool ConcatenatedVfsFile::VerifyContinuity() const { ConcatenatedVfsFile::~ConcatenatedVfsFile() = default; -VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(const std::vector<VirtualFile>& files, - std::string&& name) { +VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::string&& name, + std::vector<VirtualFile>&& files) { // Fold trivial cases. if (files.empty()) { return nullptr; @@ -46,20 +46,21 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(const std::vector<VirtualF u64 last_offset = 0; for (auto& file : files) { + const auto size = file->GetSize(); + concatenation_map.emplace_back(ConcatenationEntry{ .offset = last_offset, - .file = file, + .file = std::move(file), }); - last_offset += file->GetSize(); + last_offset += size; } - return VirtualFile(new ConcatenatedVfsFile(std::move(concatenation_map), std::move(name))); + return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map))); } -VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, - const std::multimap<u64, VirtualFile>& files, - std::string&& name) { +VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, std::string&& name, + std::multimap<u64, VirtualFile>&& files) { // Fold trivial cases. if (files.empty()) { return nullptr; @@ -76,6 +77,8 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, // Iteration of a multimap is ordered, so offset will be strictly non-decreasing. for (auto& [offset, file] : files) { + const auto size = file->GetSize(); + if (offset > last_offset) { concatenation_map.emplace_back(ConcatenationEntry{ .offset = last_offset, @@ -85,13 +88,13 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, concatenation_map.emplace_back(ConcatenationEntry{ .offset = offset, - .file = file, + .file = std::move(file), }); - last_offset = offset + file->GetSize(); + last_offset = offset + size; } - return VirtualFile(new ConcatenatedVfsFile(std::move(concatenation_map), std::move(name))); + return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map))); } std::string ConcatenatedVfsFile::GetName() const { diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h index 6b329d545..cbddd12bd 100644 --- a/src/core/file_sys/vfs_concat.h +++ b/src/core/file_sys/vfs_concat.h @@ -24,22 +24,20 @@ private: }; using ConcatenationMap = std::vector<ConcatenationEntry>; - explicit ConcatenatedVfsFile(std::vector<ConcatenationEntry>&& concatenation_map, - std::string&& name); + explicit ConcatenatedVfsFile(std::string&& name, + std::vector<ConcatenationEntry>&& concatenation_map); bool VerifyContinuity() const; public: ~ConcatenatedVfsFile() override; /// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases. - static VirtualFile MakeConcatenatedFile(const std::vector<VirtualFile>& files, - std::string&& name); + static VirtualFile MakeConcatenatedFile(std::string&& name, std::vector<VirtualFile>&& files); /// Convenience function that turns a map of offsets to files into a concatenated file, filling /// gaps with a given filler byte. - static VirtualFile MakeConcatenatedFile(u8 filler_byte, - const std::multimap<u64, VirtualFile>& files, - std::string&& name); + static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::string&& name, + std::multimap<u64, VirtualFile>&& files); std::string GetName() const override; std::size_t GetSize() const override; diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp index 3e6426afc..08daca397 100644 --- a/src/core/file_sys/vfs_layered.cpp +++ b/src/core/file_sys/vfs_layered.cpp @@ -38,7 +38,7 @@ VirtualDir LayeredVfsDirectory::GetDirectoryRelative(std::string_view path) cons for (const auto& layer : dirs) { auto dir = layer->GetDirectoryRelative(path); if (dir != nullptr) { - out.push_back(std::move(dir)); + out.emplace_back(std::move(dir)); } } @@ -62,11 +62,11 @@ std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const { std::set<std::string, std::less<>> out_names; for (const auto& layer : dirs) { - for (const auto& file : layer->GetFiles()) { + for (auto& file : layer->GetFiles()) { auto file_name = file->GetName(); if (!out_names.contains(file_name)) { out_names.emplace(std::move(file_name)); - out.push_back(file); + out.emplace_back(std::move(file)); } } } @@ -86,7 +86,7 @@ std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const { std::vector<VirtualDir> out; out.reserve(names.size()); for (const auto& subdir : names) - out.push_back(GetSubdirectory(subdir)); + out.emplace_back(GetSubdirectory(subdir)); return out; } diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 5b51edf30..0b0cef984 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -2949,6 +2949,23 @@ Result KPageTable::UnlockForIpcUserBuffer(KProcessAddress address, size_t size) KMemoryAttribute::Locked, nullptr)); } +Result KPageTable::LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size, + KMemoryPermission perm) { + R_RETURN(this->LockMemoryAndOpen(out, nullptr, address, size, KMemoryState::FlagCanTransfer, + KMemoryState::FlagCanTransfer, KMemoryPermission::All, + KMemoryPermission::UserReadWrite, KMemoryAttribute::All, + KMemoryAttribute::None, perm, KMemoryAttribute::Locked)); +} + +Result KPageTable::UnlockForTransferMemory(KProcessAddress address, size_t size, + const KPageGroup& pg) { + R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanTransfer, + KMemoryState::FlagCanTransfer, KMemoryPermission::None, + KMemoryPermission::None, KMemoryAttribute::All, + KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, + KMemoryAttribute::Locked, std::addressof(pg))); +} + Result KPageTable::LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size) { R_RETURN(this->LockMemoryAndOpen( out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, @@ -3388,6 +3405,11 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_K new_attr, KMemoryBlockDisableMergeAttribute::Locked, KMemoryBlockDisableMergeAttribute::None); + // If we have an output page group, open. + if (out_pg) { + out_pg->Open(); + } + R_SUCCEED(); } diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index b9e8c6042..7da675f27 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -104,6 +104,9 @@ public: Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state); Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state); + Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size, + KMemoryPermission perm); + Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg); Result LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size); Result UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg); Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages, diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp index 13d34125c..0e2e11743 100644 --- a/src/core/hle/kernel/k_transfer_memory.cpp +++ b/src/core/hle/kernel/k_transfer_memory.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/scope_exit.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_transfer_memory.h" @@ -9,28 +10,50 @@ namespace Kernel { KTransferMemory::KTransferMemory(KernelCore& kernel) - : KAutoObjectWithSlabHeapAndContainer{kernel} {} + : KAutoObjectWithSlabHeapAndContainer{kernel}, m_lock{kernel} {} KTransferMemory::~KTransferMemory() = default; -Result KTransferMemory::Initialize(KProcessAddress address, std::size_t size, - Svc::MemoryPermission owner_perm) { +Result KTransferMemory::Initialize(KProcessAddress addr, std::size_t size, + Svc::MemoryPermission own_perm) { // Set members. m_owner = GetCurrentProcessPointer(m_kernel); - // TODO(bunnei): Lock for transfer memory + // Get the owner page table. + auto& page_table = m_owner->GetPageTable(); + + // Construct the page group, guarding to make sure our state is valid on exit. + m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager()); + auto pg_guard = SCOPE_GUARD({ m_page_group.reset(); }); + + // Lock the memory. + R_TRY(page_table.LockForTransferMemory(std::addressof(*m_page_group), addr, size, + ConvertToKMemoryPermission(own_perm))); // Set remaining tracking members. m_owner->Open(); - m_owner_perm = owner_perm; - m_address = address; - m_size = size; + m_owner_perm = own_perm; + m_address = addr; m_is_initialized = true; + m_is_mapped = false; + // We succeeded. + pg_guard.Cancel(); R_SUCCEED(); } -void KTransferMemory::Finalize() {} +void KTransferMemory::Finalize() { + // Unlock. + if (!m_is_mapped) { + const size_t size = m_page_group->GetNumPages() * PageSize; + ASSERT(R_SUCCEEDED( + m_owner->GetPageTable().UnlockForTransferMemory(m_address, size, *m_page_group))); + } + + // Close the page group. + m_page_group->Close(); + m_page_group->Finalize(); +} void KTransferMemory::PostDestroy(uintptr_t arg) { KProcess* owner = reinterpret_cast<KProcess*>(arg); @@ -38,4 +61,54 @@ void KTransferMemory::PostDestroy(uintptr_t arg) { owner->Close(); } +Result KTransferMemory::Map(KProcessAddress address, size_t size, Svc::MemoryPermission map_perm) { + // Validate the size. + R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); + + // Validate the permission. + R_UNLESS(m_owner_perm == map_perm, ResultInvalidState); + + // Lock ourselves. + KScopedLightLock lk(m_lock); + + // Ensure we're not already mapped. + R_UNLESS(!m_is_mapped, ResultInvalidState); + + // Map the memory. + const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None) + ? KMemoryState::Transfered + : KMemoryState::SharedTransfered; + R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup( + address, *m_page_group, state, KMemoryPermission::UserReadWrite)); + + // Mark ourselves as mapped. + m_is_mapped = true; + + R_SUCCEED(); +} + +Result KTransferMemory::Unmap(KProcessAddress address, size_t size) { + // Validate the size. + R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); + + // Lock ourselves. + KScopedLightLock lk(m_lock); + + // Unmap the memory. + const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None) + ? KMemoryState::Transfered + : KMemoryState::SharedTransfered; + R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, state)); + + // Mark ourselves as unmapped. + ASSERT(m_is_mapped); + m_is_mapped = false; + + R_SUCCEED(); +} + +size_t KTransferMemory::GetSize() const { + return m_is_initialized ? m_page_group->GetNumPages() * PageSize : 0; +} + } // namespace Kernel diff --git a/src/core/hle/kernel/k_transfer_memory.h b/src/core/hle/kernel/k_transfer_memory.h index 54f97ccb4..8a0b08761 100644 --- a/src/core/hle/kernel/k_transfer_memory.h +++ b/src/core/hle/kernel/k_transfer_memory.h @@ -3,6 +3,9 @@ #pragma once +#include <optional> + +#include "core/hle/kernel/k_page_group.h" #include "core/hle/kernel/slab_helpers.h" #include "core/hle/kernel/svc_types.h" #include "core/hle/result.h" @@ -48,16 +51,19 @@ public: return m_address; } - size_t GetSize() const { - return m_is_initialized ? m_size : 0; - } + size_t GetSize() const; + + Result Map(KProcessAddress address, size_t size, Svc::MemoryPermission map_perm); + Result Unmap(KProcessAddress address, size_t size); private: + std::optional<KPageGroup> m_page_group{}; KProcess* m_owner{}; KProcessAddress m_address{}; + KLightLock m_lock; Svc::MemoryPermission m_owner_perm{}; - size_t m_size{}; bool m_is_initialized{}; + bool m_is_mapped{}; }; } // namespace Kernel diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index a1134b7e2..cb025c3d6 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -373,7 +373,7 @@ struct KernelCore::Impl { static inline thread_local u8 host_thread_id = UINT8_MAX; /// Sets the host thread ID for the caller. - u32 SetHostThreadId(std::size_t core_id) { + LTO_NOINLINE u32 SetHostThreadId(std::size_t core_id) { // This should only be called during core init. ASSERT(host_thread_id == UINT8_MAX); @@ -384,13 +384,13 @@ struct KernelCore::Impl { } /// Gets the host thread ID for the caller - u32 GetHostThreadId() const { + LTO_NOINLINE u32 GetHostThreadId() const { return host_thread_id; } // Gets the dummy KThread for the caller, allocating a new one if this is the first time - KThread* GetHostDummyThread(KThread* existing_thread) { - const auto initialize{[](KThread* thread) { + LTO_NOINLINE KThread* GetHostDummyThread(KThread* existing_thread) { + const auto initialize{[](KThread* thread) LTO_NOINLINE { ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess()); return thread; }}; @@ -424,11 +424,11 @@ struct KernelCore::Impl { static inline thread_local bool is_phantom_mode_for_singlecore{false}; - bool IsPhantomModeForSingleCore() const { + LTO_NOINLINE bool IsPhantomModeForSingleCore() const { return is_phantom_mode_for_singlecore; } - void SetIsPhantomModeForSingleCore(bool value) { + LTO_NOINLINE void SetIsPhantomModeForSingleCore(bool value) { ASSERT(!is_multicore); is_phantom_mode_for_singlecore = value; } @@ -439,14 +439,14 @@ struct KernelCore::Impl { static inline thread_local KThread* current_thread{nullptr}; - KThread* GetCurrentEmuThread() { + LTO_NOINLINE KThread* GetCurrentEmuThread() { if (!current_thread) { current_thread = GetHostDummyThread(nullptr); } return current_thread; } - void SetCurrentEmuThread(KThread* thread) { + LTO_NOINLINE void SetCurrentEmuThread(KThread* thread) { current_thread = thread; } diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp index 7d94e7f09..1f97121b3 100644 --- a/src/core/hle/kernel/svc/svc_transfer_memory.cpp +++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp @@ -71,15 +71,59 @@ Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64 } Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size, - MemoryPermission owner_perm) { - UNIMPLEMENTED(); - R_THROW(ResultNotImplemented); + MemoryPermission map_perm) { + // Validate the address/size. + R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); + R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); + R_UNLESS(size > 0, ResultInvalidSize); + R_UNLESS((address < address + size), ResultInvalidCurrentMemory); + + // Validate the permission. + R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidState); + + // Get the transfer memory. + KScopedAutoObject trmem = GetCurrentProcess(system.Kernel()) + .GetHandleTable() + .GetObject<KTransferMemory>(trmem_handle); + R_UNLESS(trmem.IsNotNull(), ResultInvalidHandle); + + // Verify that the mapping is in range. + R_UNLESS(GetCurrentProcess(system.Kernel()) + .GetPageTable() + .CanContain(address, size, KMemoryState::Transfered), + ResultInvalidMemoryRegion); + + // Map the transfer memory. + R_TRY(trmem->Map(address, size, map_perm)); + + // We succeeded. + R_SUCCEED(); } Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size) { - UNIMPLEMENTED(); - R_THROW(ResultNotImplemented); + // Validate the address/size. + R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); + R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); + R_UNLESS(size > 0, ResultInvalidSize); + R_UNLESS((address < address + size), ResultInvalidCurrentMemory); + + // Get the transfer memory. + KScopedAutoObject trmem = GetCurrentProcess(system.Kernel()) + .GetHandleTable() + .GetObject<KTransferMemory>(trmem_handle); + R_UNLESS(trmem.IsNotNull(), ResultInvalidHandle); + + // Verify that the mapping is in range. + R_UNLESS(GetCurrentProcess(system.Kernel()) + .GetPageTable() + .CanContain(address, size, KMemoryState::Transfered), + ResultInvalidMemoryRegion); + + // Unmap the transfer memory. + R_TRY(trmem->Unmap(address, size)); + + R_SUCCEED(); } Result MapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address, diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index b971401e6..1b1c8190e 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -49,7 +49,7 @@ public: : ServiceFramework{system_, "IManagerForSystemService"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "CheckAvailability"}, + {0, &IManagerForSystemService::CheckAvailability, "CheckAvailability"}, {1, nullptr, "GetAccountId"}, {2, nullptr, "EnsureIdTokenCacheAsync"}, {3, nullptr, "LoadIdTokenCache"}, @@ -78,6 +78,13 @@ public: RegisterHandlers(functions); } + +private: + void CheckAvailability(HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } }; // 3.0.0+ @@ -400,13 +407,13 @@ protected: IPC::RequestParser rp{ctx}; const auto base = rp.PopRaw<ProfileBase>(); - const auto user_data = ctx.ReadBuffer(); - const auto image_data = ctx.ReadBuffer(1); + const auto image_data = ctx.ReadBufferA(0); + const auto user_data = ctx.ReadBufferX(0); - LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}", - Common::StringFromFixedZeroTerminatedBuffer( - reinterpret_cast<const char*>(base.username.data()), base.username.size()), - base.timestamp, base.user_uuid.RawString()); + LOG_INFO(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}", + Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast<const char*>(base.username.data()), base.username.size()), + base.timestamp, base.user_uuid.RawString()); if (user_data.size() < sizeof(UserData)) { LOG_ERROR(Service_ACC, "UserData buffer too small!"); @@ -837,6 +844,29 @@ void Module::Interface::InitializeApplicationInfoV2(HLERequestContext& ctx) { rb.Push(ResultSuccess); } +void Module::Interface::BeginUserRegistration(HLERequestContext& ctx) { + const auto user_id = Common::UUID::MakeRandom(); + profile_manager->CreateNewUser(user_id, "yuzu"); + + LOG_INFO(Service_ACC, "called, uuid={}", user_id.FormattedString()); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(user_id); +} + +void Module::Interface::CompleteUserRegistration(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + Common::UUID user_id = rp.PopRaw<Common::UUID>(); + + LOG_INFO(Service_ACC, "called, uuid={}", user_id.FormattedString()); + + profile_manager->WriteUserSaveFile(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void Module::Interface::GetProfileEditor(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; Common::UUID user_id = rp.PopRaw<Common::UUID>(); @@ -880,6 +910,17 @@ void Module::Interface::StoreSaveDataThumbnailApplication(HLERequestContext& ctx StoreSaveDataThumbnail(ctx, uuid, tid); } +void Module::Interface::GetBaasAccountManagerForSystemService(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto uuid = rp.PopRaw<Common::UUID>(); + + LOG_INFO(Service_ACC, "called, uuid=0x{}", uuid.RawString()); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IManagerForSystemService>(system, uuid); +} + void Module::Interface::StoreSaveDataThumbnailSystem(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto uuid = rp.PopRaw<Common::UUID>(); diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index 6b4735c2f..0395229b4 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -33,10 +33,13 @@ public: void TrySelectUserWithoutInteraction(HLERequestContext& ctx); void IsUserAccountSwitchLocked(HLERequestContext& ctx); void InitializeApplicationInfoV2(HLERequestContext& ctx); + void BeginUserRegistration(HLERequestContext& ctx); + void CompleteUserRegistration(HLERequestContext& ctx); void GetProfileEditor(HLERequestContext& ctx); void ListQualifiedUsers(HLERequestContext& ctx); void ListOpenContextStoredUsers(HLERequestContext& ctx); void StoreSaveDataThumbnailApplication(HLERequestContext& ctx); + void GetBaasAccountManagerForSystemService(HLERequestContext& ctx); void StoreSaveDataThumbnailSystem(HLERequestContext& ctx); private: diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index d9882ecd3..770d13ec5 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp @@ -23,7 +23,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager> {99, nullptr, "DebugActivateOpenContextRetention"}, {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, - {102, nullptr, "GetBaasAccountManagerForSystemService"}, + {102, &ACC_SU::GetBaasAccountManagerForSystemService, "GetBaasAccountManagerForSystemService"}, {103, nullptr, "GetBaasUserAvailabilityChangeNotifier"}, {104, nullptr, "GetProfileUpdateNotifier"}, {105, nullptr, "CheckNetworkServiceAvailabilityAsync"}, @@ -40,8 +40,8 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager> {152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"}, {190, nullptr, "GetUserLastOpenedApplication"}, {191, nullptr, "ActivateOpenContextHolder"}, - {200, nullptr, "BeginUserRegistration"}, - {201, nullptr, "CompleteUserRegistration"}, + {200, &ACC_SU::BeginUserRegistration, "BeginUserRegistration"}, + {201, &ACC_SU::CompleteUserRegistration, "CompleteUserRegistration"}, {202, nullptr, "CancelUserRegistration"}, {203, nullptr, "DeleteUser"}, {204, nullptr, "SetUserPosition"}, diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 993a5a57a..900e32200 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -96,9 +96,10 @@ public: bool SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new, const UserData& data_new); + void WriteUserSaveFile(); + private: void ParseUserSaveFile(); - void WriteUserSaveFile(); std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); bool RemoveProfileAtIndex(std::size_t index); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 819dea6a7..98765b81a 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -31,7 +31,7 @@ #include "core/hle/service/apm/apm_controller.h" #include "core/hle/service/apm/apm_interface.h" #include "core/hle/service/bcat/backend/backend.h" -#include "core/hle/service/caps/caps.h" +#include "core/hle/service/caps/caps_types.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ns/ns.h" @@ -210,8 +210,8 @@ IDisplayController::IDisplayController(Core::System& system_) {21, nullptr, "ClearAppletTransitionBuffer"}, {22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"}, {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"}, - {24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"}, - {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, + {24, &IDisplayController::AcquireLastForegroundCaptureSharedBuffer, "AcquireLastForegroundCaptureSharedBuffer"}, + {25, &IDisplayController::ReleaseLastForegroundCaptureSharedBuffer, "ReleaseLastForegroundCaptureSharedBuffer"}, {26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"}, {27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"}, {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, @@ -239,6 +239,22 @@ void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) { rb.Push(ResultSuccess); } +void IDisplayController::AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(1U); + rb.Push(0); +} + +void IDisplayController::ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); @@ -764,6 +780,66 @@ void AppletMessageQueue::OperationModeChanged() { on_operation_mode_changed->Signal(); } +ILockAccessor::ILockAccessor(Core::System& system_) + : ServiceFramework{system_, "ILockAccessor"}, service_context{system_, "ILockAccessor"} { + // clang-format off + static const FunctionInfo functions[] = { + {1, &ILockAccessor::TryLock, "TryLock"}, + {2, &ILockAccessor::Unlock, "Unlock"}, + {3, &ILockAccessor::GetEvent, "GetEvent"}, + {4,&ILockAccessor::IsLocked, "IsLocked"}, + }; + // clang-format on + + RegisterHandlers(functions); + + lock_event = service_context.CreateEvent("ILockAccessor::LockEvent"); +} + +ILockAccessor::~ILockAccessor() = default; + +void ILockAccessor::TryLock(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto return_handle = rp.Pop<bool>(); + + LOG_WARNING(Service_AM, "(STUBBED) called, return_handle={}", return_handle); + + // TODO: When return_handle is true this function should return the lock handle + + is_locked = true; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u8>(is_locked); +} + +void ILockAccessor::Unlock(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "called"); + + is_locked = false; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ILockAccessor::GetEvent(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "called"); + + lock_event->Signal(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(lock_event->GetReadableEvent()); +} + +void ILockAccessor::IsLocked(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + rb.Push<u8>(is_locked); +} + ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<AppletMessageQueue> msg_queue_) : ServiceFramework{system_, "ICommonStateGetter"}, msg_queue{std::move(msg_queue_)}, @@ -787,7 +863,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, {14, nullptr, "GetWakeupCount"}, {20, nullptr, "PushToGeneralChannel"}, {30, nullptr, "GetHomeButtonReaderLockAccessor"}, - {31, nullptr, "GetReaderLockAccessorEx"}, + {31, &ICommonStateGetter::GetReaderLockAccessorEx, "GetReaderLockAccessorEx"}, {32, nullptr, "GetWriterLockAccessorEx"}, {40, nullptr, "GetCradleFwVersion"}, {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"}, @@ -805,7 +881,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, {65, nullptr, "GetApplicationIdByContentActionName"}, {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"}, {67, nullptr, "CancelCpuBoostMode"}, - {68, nullptr, "GetBuiltInDisplayType"}, + {68, &ICommonStateGetter::GetBuiltInDisplayType, "GetBuiltInDisplayType"}, {80, &ICommonStateGetter::PerformSystemButtonPressingIfInFocus, "PerformSystemButtonPressingIfInFocus"}, {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, {91, nullptr, "GetCurrentPerformanceConfiguration"}, @@ -886,6 +962,18 @@ void ICommonStateGetter::RequestToAcquireSleepLock(HLERequestContext& ctx) { rb.Push(ResultSuccess); } +void ICommonStateGetter::GetReaderLockAccessorEx(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto unknown = rp.Pop<u32>(); + + LOG_INFO(Service_AM, "called, unknown={}", unknown); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(ResultSuccess); + rb.PushIpcInterface<ILockAccessor>(system); +} + void ICommonStateGetter::GetAcquiredSleepLockEvent(HLERequestContext& ctx) { LOG_WARNING(Service_AM, "called"); @@ -970,6 +1058,14 @@ void ICommonStateGetter::SetCpuBoostMode(HLERequestContext& ctx) { apm_sys->SetCpuBoostMode(ctx); } +void ICommonStateGetter::GetBuiltInDisplayType(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); +} + void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto system_button{rp.PopEnum<SystemButtonType>()}; @@ -1477,7 +1573,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) {100, nullptr, "CreateGameMovieTrimmer"}, {101, nullptr, "ReserveResourceForMovieOperation"}, {102, nullptr, "UnreserveResourceForMovieOperation"}, - {110, nullptr, "GetMainAppletAvailableUsers"}, + {110, &ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers, "GetMainAppletAvailableUsers"}, {120, nullptr, "GetLaunchStorageInfoForDebug"}, {130, nullptr, "GetGpuErrorDetectedSystemEvent"}, {140, nullptr, "SetApplicationMemoryReservation"}, @@ -1493,6 +1589,9 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) case Applets::AppletId::MiiEdit: PushInShowMiiEditData(); break; + case Applets::AppletId::PhotoViewer: + PushInShowAlbum(); + break; default: break; } @@ -1569,6 +1668,42 @@ void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& rb.PushRaw(applet_info); } +void ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(HLERequestContext& ctx) { + const Service::Account::ProfileManager manager{}; + bool is_empty{true}; + s32 user_count{-1}; + + LOG_INFO(Service_AM, "called"); + + if (manager.GetUserCount() > 0) { + is_empty = false; + user_count = static_cast<s32>(manager.GetUserCount()); + ctx.WriteBuffer(manager.GetAllUsers()); + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u8>(is_empty); + rb.Push(user_count); +} + +void ILibraryAppletSelfAccessor::PushInShowAlbum() { + const Applets::CommonArguments arguments{ + .arguments_version = Applets::CommonArgumentVersion::Version3, + .size = Applets::CommonArgumentSize::Version3, + .library_version = 1, + .theme_color = Applets::ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + std::vector<u8> argument_data(sizeof(arguments)); + std::vector<u8> settings_data{2}; + std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); + queue_data.emplace_back(std::move(argument_data)); + queue_data.emplace_back(std::move(settings_data)); +} + void ILibraryAppletSelfAccessor::PushInShowCabinetData() { const Applets::CommonArguments arguments{ .arguments_version = Applets::CommonArgumentVersion::Version3, diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 349482dcc..64b3f3fe2 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -124,6 +124,8 @@ public: private: void GetCallerAppletCaptureImageEx(HLERequestContext& ctx); void TakeScreenShotOfOwnLayer(HLERequestContext& ctx); + void AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx); + void ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx); void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); }; @@ -195,6 +197,23 @@ private: ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit; }; +class ILockAccessor final : public ServiceFramework<ILockAccessor> { +public: + explicit ILockAccessor(Core::System& system_); + ~ILockAccessor() override; + +private: + void TryLock(HLERequestContext& ctx); + void Unlock(HLERequestContext& ctx); + void GetEvent(HLERequestContext& ctx); + void IsLocked(HLERequestContext& ctx); + + bool is_locked{}; + + Kernel::KEvent* lock_event; + KernelHelpers::ServiceContext service_context; +}; + class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { public: explicit ICommonStateGetter(Core::System& system_, @@ -237,6 +256,7 @@ private: void GetCurrentFocusState(HLERequestContext& ctx); void RequestToAcquireSleepLock(HLERequestContext& ctx); void GetAcquiredSleepLockEvent(HLERequestContext& ctx); + void GetReaderLockAccessorEx(HLERequestContext& ctx); void GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx); void GetOperationMode(HLERequestContext& ctx); void GetPerformanceMode(HLERequestContext& ctx); @@ -248,6 +268,7 @@ private: void EndVrModeEx(HLERequestContext& ctx); void GetDefaultDisplayResolution(HLERequestContext& ctx); void SetCpuBoostMode(HLERequestContext& ctx); + void GetBuiltInDisplayType(HLERequestContext& ctx); void PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx); void GetSettingsPlatformRegion(HLERequestContext& ctx); void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(HLERequestContext& ctx); @@ -326,7 +347,9 @@ private: void GetLibraryAppletInfo(HLERequestContext& ctx); void ExitProcessAndReturn(HLERequestContext& ctx); void GetCallerAppletIdentityInfo(HLERequestContext& ctx); + void GetMainAppletAvailableUsers(HLERequestContext& ctx); + void PushInShowAlbum(); void PushInShowCabinetData(); void PushInShowMiiEditData(); diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index eb12312cc..e30e6478a 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp @@ -28,8 +28,8 @@ public: {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, {20, &ILibraryAppletProxy::OpenLibraryAppletSelfAccessor, "OpenLibraryAppletSelfAccessor"}, {21, &ILibraryAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"}, - {22, nullptr, "GetHomeMenuFunctions"}, - {23, nullptr, "GetGlobalStateController"}, + {22, &ILibraryAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"}, + {23, &ILibraryAppletProxy::GetGlobalStateController, "GetGlobalStateController"}, {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, }; // clang-format on @@ -110,6 +110,22 @@ private: rb.PushIpcInterface<IAppletCommonFunctions>(system); } + void GetHomeMenuFunctions(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IHomeMenuFunctions>(system); + } + + void GetGlobalStateController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IGlobalStateController>(system); + } + void GetDebugFunctions(HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp index b46ea840c..5d17c353f 100644 --- a/src/core/hle/service/am/applets/applet_error.cpp +++ b/src/core/hle/service/am/applets/applet_error.cpp @@ -138,6 +138,10 @@ void Error::Initialize() { CopyArgumentData(data, args->application_error); error_code = Result(args->application_error.error_code); break; + case ErrorAppletMode::ShowErrorPctl: + CopyArgumentData(data, args->error_record); + error_code = Decode64BitError(args->error_record.error_code_64); + break; case ErrorAppletMode::ShowErrorRecord: CopyArgumentData(data, args->error_record); error_code = Decode64BitError(args->error_record.error_code_64); @@ -191,6 +195,7 @@ void Error::Execute() { frontend.ShowCustomErrorText(error_code, main_text_string, detail_text_string, callback); break; } + case ErrorAppletMode::ShowErrorPctl: case ErrorAppletMode::ShowErrorRecord: reporter.SaveErrorReport(title_id, error_code, fmt::format("{:016X}", args->error_record.posix_time)); diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp index 610fe9940..31dd98140 100644 --- a/src/core/hle/service/caps/caps.cpp +++ b/src/core/hle/service/caps/caps.cpp @@ -4,6 +4,7 @@ #include "core/hle/service/caps/caps.h" #include "core/hle/service/caps/caps_a.h" #include "core/hle/service/caps/caps_c.h" +#include "core/hle/service/caps/caps_manager.h" #include "core/hle/service/caps/caps_sc.h" #include "core/hle/service/caps/caps_ss.h" #include "core/hle/service/caps/caps_su.h" @@ -15,13 +16,21 @@ namespace Service::Capture { void LoopProcess(Core::System& system) { auto server_manager = std::make_unique<ServerManager>(system); + auto album_manager = std::make_shared<AlbumManager>(system); + + server_manager->RegisterNamedService( + "caps:a", std::make_shared<IAlbumAccessorService>(system, album_manager)); + server_manager->RegisterNamedService( + "caps:c", std::make_shared<IAlbumControlService>(system, album_manager)); + server_manager->RegisterNamedService( + "caps:u", std::make_shared<IAlbumApplicationService>(system, album_manager)); + + server_manager->RegisterNamedService("caps:ss", std::make_shared<IScreenShotService>(system)); + server_manager->RegisterNamedService("caps:sc", + std::make_shared<IScreenShotControlService>(system)); + server_manager->RegisterNamedService("caps:su", + std::make_shared<IScreenShotApplicationService>(system)); - server_manager->RegisterNamedService("caps:a", std::make_shared<CAPS_A>(system)); - server_manager->RegisterNamedService("caps:c", std::make_shared<CAPS_C>(system)); - server_manager->RegisterNamedService("caps:u", std::make_shared<CAPS_U>(system)); - server_manager->RegisterNamedService("caps:sc", std::make_shared<CAPS_SC>(system)); - server_manager->RegisterNamedService("caps:ss", std::make_shared<CAPS_SS>(system)); - server_manager->RegisterNamedService("caps:su", std::make_shared<CAPS_SU>(system)); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h index 15f0ecfaa..58e9725b8 100644 --- a/src/core/hle/service/caps/caps.h +++ b/src/core/hle/service/caps/caps.h @@ -3,93 +3,12 @@ #pragma once -#include "common/common_funcs.h" -#include "common/common_types.h" - namespace Core { class System; } -namespace Service::SM { -class ServiceManager; -} - namespace Service::Capture { -enum class AlbumImageOrientation { - Orientation0 = 0, - Orientation1 = 1, - Orientation2 = 2, - Orientation3 = 3, -}; - -enum class AlbumReportOption : s32 { - Disable = 0, - Enable = 1, -}; - -enum class ContentType : u8 { - Screenshot = 0, - Movie = 1, - ExtraMovie = 3, -}; - -enum class AlbumStorage : u8 { - NAND = 0, - SD = 1, -}; - -struct AlbumFileDateTime { - s16 year{}; - s8 month{}; - s8 day{}; - s8 hour{}; - s8 minute{}; - s8 second{}; - s8 uid{}; -}; -static_assert(sizeof(AlbumFileDateTime) == 0x8, "AlbumFileDateTime has incorrect size."); - -struct AlbumEntry { - u64 size{}; - u64 application_id{}; - AlbumFileDateTime datetime{}; - AlbumStorage storage{}; - ContentType content{}; - INSERT_PADDING_BYTES(6); -}; -static_assert(sizeof(AlbumEntry) == 0x20, "AlbumEntry has incorrect size."); - -struct AlbumFileEntry { - u64 size{}; // Size of the entry - u64 hash{}; // AES256 with hardcoded key over AlbumEntry - AlbumFileDateTime datetime{}; - AlbumStorage storage{}; - ContentType content{}; - INSERT_PADDING_BYTES(5); - u8 unknown{1}; // Set to 1 on official SW -}; -static_assert(sizeof(AlbumFileEntry) == 0x20, "AlbumFileEntry has incorrect size."); - -struct ApplicationAlbumEntry { - u64 size{}; // Size of the entry - u64 hash{}; // AES256 with hardcoded key over AlbumEntry - AlbumFileDateTime datetime{}; - AlbumStorage storage{}; - ContentType content{}; - INSERT_PADDING_BYTES(5); - u8 unknown{1}; // Set to 1 on official SW -}; -static_assert(sizeof(ApplicationAlbumEntry) == 0x20, "ApplicationAlbumEntry has incorrect size."); - -struct ApplicationAlbumFileEntry { - ApplicationAlbumEntry entry{}; - AlbumFileDateTime datetime{}; - u64 unknown{}; -}; -static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30, - "ApplicationAlbumFileEntry has incorrect size."); - void LoopProcess(Core::System& system); } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp index 44267b284..9925720a3 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp @@ -1,40 +1,26 @@ // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/logging/log.h" #include "core/hle/service/caps/caps_a.h" +#include "core/hle/service/caps/caps_manager.h" +#include "core/hle/service/caps/caps_result.h" +#include "core/hle/service/caps/caps_types.h" +#include "core/hle/service/ipc_helpers.h" namespace Service::Capture { -class IAlbumAccessorSession final : public ServiceFramework<IAlbumAccessorSession> { -public: - explicit IAlbumAccessorSession(Core::System& system_) - : ServiceFramework{system_, "IAlbumAccessorSession"} { - // clang-format off - static const FunctionInfo functions[] = { - {2001, nullptr, "OpenAlbumMovieReadStream"}, - {2002, nullptr, "CloseAlbumMovieReadStream"}, - {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"}, - {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"}, - {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"}, - {2006, nullptr, "GetAlbumMovieReadStreamImageDataSize"}, - {2007, nullptr, "ReadImageDataFromAlbumMovieReadStream"}, - {2008, nullptr, "ReadFileAttributeFromAlbumMovieReadStream"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; - -CAPS_A::CAPS_A(Core::System& system_) : ServiceFramework{system_, "caps:a"} { +IAlbumAccessorService::IAlbumAccessorService(Core::System& system_, + std::shared_ptr<AlbumManager> album_manager) + : ServiceFramework{system_, "caps:a"}, manager{album_manager} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetAlbumFileCount"}, {1, nullptr, "GetAlbumFileList"}, {2, nullptr, "LoadAlbumFile"}, - {3, nullptr, "DeleteAlbumFile"}, + {3, &IAlbumAccessorService::DeleteAlbumFile, "DeleteAlbumFile"}, {4, nullptr, "StorageCopyAlbumFile"}, - {5, nullptr, "IsAlbumMounted"}, + {5, &IAlbumAccessorService::IsAlbumMounted, "IsAlbumMounted"}, {6, nullptr, "GetAlbumUsage"}, {7, nullptr, "GetAlbumFileSize"}, {8, nullptr, "LoadAlbumFileThumbnail"}, @@ -47,18 +33,18 @@ CAPS_A::CAPS_A(Core::System& system_) : ServiceFramework{system_, "caps:a"} { {15, nullptr, "GetAlbumUsage3"}, {16, nullptr, "GetAlbumMountResult"}, {17, nullptr, "GetAlbumUsage16"}, - {18, nullptr, "Unknown18"}, + {18, &IAlbumAccessorService::Unknown18, "Unknown18"}, {19, nullptr, "Unknown19"}, {100, nullptr, "GetAlbumFileCountEx0"}, - {101, nullptr, "GetAlbumFileListEx0"}, + {101, &IAlbumAccessorService::GetAlbumFileListEx0, "GetAlbumFileListEx0"}, {202, nullptr, "SaveEditedScreenShot"}, {301, nullptr, "GetLastThumbnail"}, {302, nullptr, "GetLastOverlayMovieThumbnail"}, - {401, nullptr, "GetAutoSavingStorage"}, + {401, &IAlbumAccessorService::GetAutoSavingStorage, "GetAutoSavingStorage"}, {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"}, {1001, nullptr, "LoadAlbumScreenShotThumbnailImageEx0"}, - {1002, nullptr, "LoadAlbumScreenShotImageEx1"}, - {1003, nullptr, "LoadAlbumScreenShotThumbnailImageEx1"}, + {1002, &IAlbumAccessorService::LoadAlbumScreenShotImageEx1, "LoadAlbumScreenShotImageEx1"}, + {1003, &IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1, "LoadAlbumScreenShotThumbnailImageEx1"}, {8001, nullptr, "ForceAlbumUnmounted"}, {8002, nullptr, "ResetAlbumMountStatus"}, {8011, nullptr, "RefreshAlbumCache"}, @@ -74,6 +60,199 @@ CAPS_A::CAPS_A(Core::System& system_) : ServiceFramework{system_, "caps:a"} { RegisterHandlers(functions); } -CAPS_A::~CAPS_A() = default; +IAlbumAccessorService::~IAlbumAccessorService() = default; + +void IAlbumAccessorService::DeleteAlbumFile(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto file_id{rp.PopRaw<AlbumFileId>()}; + + LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}", + file_id.application_id, file_id.storage, file_id.type); + + Result result = manager->DeleteAlbumFile(file_id); + result = TranslateResult(result); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IAlbumAccessorService::IsAlbumMounted(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto storage{rp.PopEnum<AlbumStorage>()}; + + LOG_INFO(Service_Capture, "called, storage={}", storage); + + Result result = manager->IsAlbumMounted(storage); + const bool is_mounted = result.IsSuccess(); + result = TranslateResult(result); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push<u8>(is_mounted); +} + +void IAlbumAccessorService::Unknown18(HLERequestContext& ctx) { + struct UnknownBuffer { + INSERT_PADDING_BYTES(0x10); + }; + static_assert(sizeof(UnknownBuffer) == 0x10, "UnknownBuffer is an invalid size"); + + LOG_WARNING(Service_Capture, "(STUBBED) called"); + + std::vector<UnknownBuffer> buffer{}; + + if (!buffer.empty()) { + ctx.WriteBuffer(buffer); + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast<u32>(buffer.size())); +} + +void IAlbumAccessorService::GetAlbumFileListEx0(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto storage{rp.PopEnum<AlbumStorage>()}; + const auto flags{rp.Pop<u8>()}; + const auto album_entry_size{ctx.GetWriteBufferNumElements<AlbumEntry>()}; + + LOG_INFO(Service_Capture, "called, storage={}, flags={}", storage, flags); + + std::vector<AlbumEntry> entries; + Result result = manager->GetAlbumFileList(entries, storage, flags); + result = TranslateResult(result); + + entries.resize(std::min(album_entry_size, entries.size())); + + if (!entries.empty()) { + ctx.WriteBuffer(entries); + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push<u64>(entries.size()); +} + +void IAlbumAccessorService::GetAutoSavingStorage(HLERequestContext& ctx) { + LOG_WARNING(Service_Capture, "(STUBBED) called"); + + bool is_autosaving{}; + Result result = manager->GetAutoSavingStorage(is_autosaving); + result = TranslateResult(result); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push<u8>(is_autosaving); +} + +void IAlbumAccessorService::LoadAlbumScreenShotImageEx1(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto file_id{rp.PopRaw<AlbumFileId>()}; + const auto decoder_options{rp.PopRaw<ScreenShotDecodeOption>()}; + const auto image_buffer_size{ctx.GetWriteBufferSize(1)}; + + LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}", + file_id.application_id, file_id.storage, file_id.type, decoder_options.flags); + + std::vector<u8> image; + LoadAlbumScreenShotImageOutput image_output; + Result result = + manager->LoadAlbumScreenShotImage(image_output, image, file_id, decoder_options); + result = TranslateResult(result); + + if (image.size() > image_buffer_size) { + result = ResultWorkMemoryError; + } + + if (result.IsSuccess()) { + ctx.WriteBuffer(image_output, 0); + ctx.WriteBuffer(image, 1); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto file_id{rp.PopRaw<AlbumFileId>()}; + const auto decoder_options{rp.PopRaw<ScreenShotDecodeOption>()}; + + LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}", + file_id.application_id, file_id.storage, file_id.type, decoder_options.flags); + + std::vector<u8> image(ctx.GetWriteBufferSize(1)); + LoadAlbumScreenShotImageOutput image_output; + Result result = + manager->LoadAlbumScreenShotThumbnail(image_output, image, file_id, decoder_options); + result = TranslateResult(result); + + if (result.IsSuccess()) { + ctx.WriteBuffer(image_output, 0); + ctx.WriteBuffer(image, 1); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +Result IAlbumAccessorService::TranslateResult(Result in_result) { + if (in_result.IsSuccess()) { + return in_result; + } + + if ((in_result.raw & 0x3801ff) == ResultUnknown1024.raw) { + if (in_result.description - 0x514 < 100) { + return ResultInvalidFileData; + } + if (in_result.description - 0x5dc < 100) { + return ResultInvalidFileData; + } + + if (in_result.description - 0x578 < 100) { + if (in_result == ResultFileCountLimit) { + return ResultUnknown22; + } + return ResultUnknown25; + } + + if (in_result.raw < ResultUnknown1801.raw) { + if (in_result == ResultUnknown1202) { + return ResultUnknown810; + } + if (in_result == ResultUnknown1203) { + return ResultUnknown810; + } + if (in_result == ResultUnknown1701) { + return ResultUnknown5; + } + } else if (in_result.raw < ResultUnknown1803.raw) { + if (in_result == ResultUnknown1801) { + return ResultUnknown5; + } + if (in_result == ResultUnknown1802) { + return ResultUnknown6; + } + } else { + if (in_result == ResultUnknown1803) { + return ResultUnknown7; + } + if (in_result == ResultUnknown1804) { + return ResultOutOfRange; + } + } + return ResultUnknown1024; + } + + if (in_result.module == ErrorModule::FS) { + if ((in_result.description >> 0xc < 0x7d) || (in_result.description - 1000 < 2000) || + (((in_result.description - 3000) >> 3) < 0x271)) { + // TODO: Translate FS error + return in_result; + } + } + + return in_result; +} } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h index 98a21a5ad..c90cff71e 100644 --- a/src/core/hle/service/caps/caps_a.h +++ b/src/core/hle/service/caps/caps_a.h @@ -10,11 +10,26 @@ class System; } namespace Service::Capture { +class AlbumManager; -class CAPS_A final : public ServiceFramework<CAPS_A> { +class IAlbumAccessorService final : public ServiceFramework<IAlbumAccessorService> { public: - explicit CAPS_A(Core::System& system_); - ~CAPS_A() override; + explicit IAlbumAccessorService(Core::System& system_, + std::shared_ptr<AlbumManager> album_manager); + ~IAlbumAccessorService() override; + +private: + void DeleteAlbumFile(HLERequestContext& ctx); + void IsAlbumMounted(HLERequestContext& ctx); + void Unknown18(HLERequestContext& ctx); + void GetAlbumFileListEx0(HLERequestContext& ctx); + void GetAutoSavingStorage(HLERequestContext& ctx); + void LoadAlbumScreenShotImageEx1(HLERequestContext& ctx); + void LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx); + + Result TranslateResult(Result in_result); + + std::shared_ptr<AlbumManager> manager = nullptr; }; } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp index fc77e35cd..1e7fe6474 100644 --- a/src/core/hle/service/caps/caps_c.cpp +++ b/src/core/hle/service/caps/caps_c.cpp @@ -3,53 +3,21 @@ #include "common/logging/log.h" #include "core/hle/service/caps/caps_c.h" +#include "core/hle/service/caps/caps_manager.h" +#include "core/hle/service/caps/caps_result.h" +#include "core/hle/service/caps/caps_types.h" #include "core/hle/service/ipc_helpers.h" namespace Service::Capture { -class IAlbumControlSession final : public ServiceFramework<IAlbumControlSession> { -public: - explicit IAlbumControlSession(Core::System& system_) - : ServiceFramework{system_, "IAlbumControlSession"} { - // clang-format off - static const FunctionInfo functions[] = { - {2001, nullptr, "OpenAlbumMovieReadStream"}, - {2002, nullptr, "CloseAlbumMovieReadStream"}, - {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"}, - {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"}, - {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"}, - {2006, nullptr, "GetAlbumMovieReadStreamImageDataSize"}, - {2007, nullptr, "ReadImageDataFromAlbumMovieReadStream"}, - {2008, nullptr, "ReadFileAttributeFromAlbumMovieReadStream"}, - {2401, nullptr, "OpenAlbumMovieWriteStream"}, - {2402, nullptr, "FinishAlbumMovieWriteStream"}, - {2403, nullptr, "CommitAlbumMovieWriteStream"}, - {2404, nullptr, "DiscardAlbumMovieWriteStream"}, - {2405, nullptr, "DiscardAlbumMovieWriteStreamNoDelete"}, - {2406, nullptr, "CommitAlbumMovieWriteStreamEx"}, - {2411, nullptr, "StartAlbumMovieWriteStreamDataSection"}, - {2412, nullptr, "EndAlbumMovieWriteStreamDataSection"}, - {2413, nullptr, "StartAlbumMovieWriteStreamMetaSection"}, - {2414, nullptr, "EndAlbumMovieWriteStreamMetaSection"}, - {2421, nullptr, "ReadDataFromAlbumMovieWriteStream"}, - {2422, nullptr, "WriteDataToAlbumMovieWriteStream"}, - {2424, nullptr, "WriteMetaToAlbumMovieWriteStream"}, - {2431, nullptr, "GetAlbumMovieWriteStreamBrokenReason"}, - {2433, nullptr, "GetAlbumMovieWriteStreamDataSize"}, - {2434, nullptr, "SetAlbumMovieWriteStreamDataSize"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; - -CAPS_C::CAPS_C(Core::System& system_) : ServiceFramework{system_, "caps:c"} { +IAlbumControlService::IAlbumControlService(Core::System& system_, + std::shared_ptr<AlbumManager> album_manager) + : ServiceFramework{system_, "caps:c"}, manager{album_manager} { // clang-format off static const FunctionInfo functions[] = { {1, nullptr, "CaptureRawImage"}, {2, nullptr, "CaptureRawImageWithTimeout"}, - {33, &CAPS_C::SetShimLibraryVersion, "SetShimLibraryVersion"}, + {33, &IAlbumControlService::SetShimLibraryVersion, "SetShimLibraryVersion"}, {1001, nullptr, "RequestTakingScreenShot"}, {1002, nullptr, "RequestTakingScreenShotWithTimeout"}, {1011, nullptr, "NotifyTakingScreenShotRefused"}, @@ -72,9 +40,9 @@ CAPS_C::CAPS_C(Core::System& system_) : ServiceFramework{system_, "caps:c"} { RegisterHandlers(functions); } -CAPS_C::~CAPS_C() = default; +IAlbumControlService::~IAlbumControlService() = default; -void CAPS_C::SetShimLibraryVersion(HLERequestContext& ctx) { +void IAlbumControlService::SetShimLibraryVersion(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto library_version{rp.Pop<u64>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h index 537b3a2e3..92ba242db 100644 --- a/src/core/hle/service/caps/caps_c.h +++ b/src/core/hle/service/caps/caps_c.h @@ -10,14 +10,18 @@ class System; } namespace Service::Capture { +class AlbumManager; -class CAPS_C final : public ServiceFramework<CAPS_C> { +class IAlbumControlService final : public ServiceFramework<IAlbumControlService> { public: - explicit CAPS_C(Core::System& system_); - ~CAPS_C() override; + explicit IAlbumControlService(Core::System& system_, + std::shared_ptr<AlbumManager> album_manager); + ~IAlbumControlService() override; private: void SetShimLibraryVersion(HLERequestContext& ctx); + + std::shared_ptr<AlbumManager> manager = nullptr; }; } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp new file mode 100644 index 000000000..2b4e3f076 --- /dev/null +++ b/src/core/hle/service/caps/caps_manager.cpp @@ -0,0 +1,386 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <sstream> +#include <stb_image.h> +#include <stb_image_resize.h> + +#include "common/fs/file.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/service/caps/caps_manager.h" +#include "core/hle/service/caps/caps_result.h" +#include "core/hle/service/time/time_manager.h" +#include "core/hle/service/time/time_zone_content_manager.h" + +namespace Service::Capture { + +AlbumManager::AlbumManager(Core::System& system_) : system{system_} {} + +AlbumManager::~AlbumManager() = default; + +Result AlbumManager::DeleteAlbumFile(const AlbumFileId& file_id) { + if (file_id.storage > AlbumStorage::Sd) { + return ResultInvalidStorage; + } + + if (!is_mounted) { + return ResultIsNotMounted; + } + + std::filesystem::path path; + const auto result = GetFile(path, file_id); + + if (result.IsError()) { + return result; + } + + if (!Common::FS::RemoveFile(path)) { + return ResultFileNotFound; + } + + return ResultSuccess; +} + +Result AlbumManager::IsAlbumMounted(AlbumStorage storage) { + if (storage > AlbumStorage::Sd) { + return ResultInvalidStorage; + } + + is_mounted = true; + + if (storage == AlbumStorage::Sd) { + FindScreenshots(); + } + + return is_mounted ? ResultSuccess : ResultIsNotMounted; +} + +Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage, + u8 flags) const { + if (storage > AlbumStorage::Sd) { + return ResultInvalidStorage; + } + + if (!is_mounted) { + return ResultIsNotMounted; + } + + for (auto& [file_id, path] : album_files) { + if (file_id.storage != storage) { + continue; + } + if (out_entries.size() >= SdAlbumFileLimit) { + break; + } + + const auto entry_size = Common::FS::GetSize(path); + out_entries.push_back({ + .entry_size = entry_size, + .file_id = file_id, + }); + } + + return ResultSuccess; +} + +Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, + ContentType contex_type, s64 start_posix_time, + s64 end_posix_time, u64 aruid) const { + if (!is_mounted) { + return ResultIsNotMounted; + } + + std::vector<ApplicationAlbumEntry> album_entries; + const auto start_date = ConvertToAlbumDateTime(start_posix_time); + const auto end_date = ConvertToAlbumDateTime(end_posix_time); + const auto result = GetAlbumFileList(album_entries, contex_type, start_date, end_date, aruid); + + if (result.IsError()) { + return result; + } + + for (const auto& album_entry : album_entries) { + ApplicationAlbumFileEntry entry{ + .entry = album_entry, + .datetime = album_entry.datetime, + .unknown = {}, + }; + out_entries.push_back(entry); + } + + return ResultSuccess; +} + +Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries, + ContentType contex_type, AlbumFileDateTime start_date, + AlbumFileDateTime end_date, u64 aruid) const { + if (!is_mounted) { + return ResultIsNotMounted; + } + + for (auto& [file_id, path] : album_files) { + if (file_id.type != contex_type) { + continue; + } + if (file_id.date > start_date) { + continue; + } + if (file_id.date < end_date) { + continue; + } + if (out_entries.size() >= SdAlbumFileLimit) { + break; + } + + const auto entry_size = Common::FS::GetSize(path); + ApplicationAlbumEntry entry{ + .size = entry_size, + .hash{}, + .datetime = file_id.date, + .storage = file_id.storage, + .content = contex_type, + .unknown = 1, + }; + out_entries.push_back(entry); + } + + return ResultSuccess; +} + +Result AlbumManager::GetAutoSavingStorage(bool& out_is_autosaving) const { + out_is_autosaving = false; + return ResultSuccess; +} + +Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output, + std::vector<u8>& out_image, + const AlbumFileId& file_id, + const ScreenShotDecodeOption& decoder_options) const { + if (file_id.storage > AlbumStorage::Sd) { + return ResultInvalidStorage; + } + + if (!is_mounted) { + return ResultIsNotMounted; + } + + out_image_output = { + .width = 1280, + .height = 720, + .attribute = + { + .unknown_0{}, + .orientation = AlbumImageOrientation::None, + .unknown_1{}, + .unknown_2{}, + }, + }; + + std::filesystem::path path; + const auto result = GetFile(path, file_id); + + if (result.IsError()) { + return result; + } + + out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha); + + return LoadImage(out_image, path, static_cast<int>(out_image_output.width), + +static_cast<int>(out_image_output.height), decoder_options.flags); +} + +Result AlbumManager::LoadAlbumScreenShotThumbnail( + LoadAlbumScreenShotImageOutput& out_image_output, std::vector<u8>& out_image, + const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options) const { + if (file_id.storage > AlbumStorage::Sd) { + return ResultInvalidStorage; + } + + if (!is_mounted) { + return ResultIsNotMounted; + } + + out_image_output = { + .width = 320, + .height = 180, + .attribute = + { + .unknown_0{}, + .orientation = AlbumImageOrientation::None, + .unknown_1{}, + .unknown_2{}, + }, + }; + + std::filesystem::path path; + const auto result = GetFile(path, file_id); + + if (result.IsError()) { + return result; + } + + out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha); + + return LoadImage(out_image, path, static_cast<int>(out_image_output.width), + +static_cast<int>(out_image_output.height), decoder_options.flags); +} + +Result AlbumManager::GetFile(std::filesystem::path& out_path, const AlbumFileId& file_id) const { + const auto file = album_files.find(file_id); + + if (file == album_files.end()) { + return ResultFileNotFound; + } + + out_path = file->second; + return ResultSuccess; +} + +void AlbumManager::FindScreenshots() { + is_mounted = false; + album_files.clear(); + + // TODO: Swap this with a blocking operation. + const auto screenshots_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ScreenshotsDir); + Common::FS::IterateDirEntries( + screenshots_dir, + [this](const std::filesystem::path& full_path) { + AlbumEntry entry; + if (GetAlbumEntry(entry, full_path).IsError()) { + return true; + } + while (album_files.contains(entry.file_id)) { + if (++entry.file_id.date.unique_id == 0) { + break; + } + } + album_files[entry.file_id] = full_path; + return true; + }, + Common::FS::DirEntryFilter::File); + + is_mounted = true; +} + +Result AlbumManager::GetAlbumEntry(AlbumEntry& out_entry, const std::filesystem::path& path) const { + std::istringstream line_stream(path.filename().string()); + std::string date; + std::string application; + std::string time; + + // Parse filename to obtain entry properties + std::getline(line_stream, application, '_'); + std::getline(line_stream, date, '_'); + std::getline(line_stream, time, '_'); + + std::istringstream date_stream(date); + std::istringstream time_stream(time); + std::string year; + std::string month; + std::string day; + std::string hour; + std::string minute; + std::string second; + + std::getline(date_stream, year, '-'); + std::getline(date_stream, month, '-'); + std::getline(date_stream, day, '-'); + + std::getline(time_stream, hour, '-'); + std::getline(time_stream, minute, '-'); + std::getline(time_stream, second, '-'); + + try { + out_entry = { + .entry_size = 1, + .file_id{ + .application_id = static_cast<u64>(std::stoll(application, 0, 16)), + .date = + { + .year = static_cast<s16>(std::stoi(year)), + .month = static_cast<s8>(std::stoi(month)), + .day = static_cast<s8>(std::stoi(day)), + .hour = static_cast<s8>(std::stoi(hour)), + .minute = static_cast<s8>(std::stoi(minute)), + .second = static_cast<s8>(std::stoi(second)), + .unique_id = 0, + }, + .storage = AlbumStorage::Sd, + .type = ContentType::Screenshot, + .unknown = 1, + }, + }; + } catch (const std::invalid_argument&) { + return ResultUnknown; + } catch (const std::out_of_range&) { + return ResultUnknown; + } catch (const std::exception&) { + return ResultUnknown; + } + + return ResultSuccess; +} + +Result AlbumManager::LoadImage(std::span<u8> out_image, const std::filesystem::path& path, + int width, int height, ScreenShotDecoderFlag flag) const { + if (out_image.size() != static_cast<std::size_t>(width * height * STBI_rgb_alpha)) { + return ResultUnknown; + } + + const Common::FS::IOFile db_file{path, Common::FS::FileAccessMode::Read, + Common::FS::FileType::BinaryFile}; + + std::vector<u8> raw_file(db_file.GetSize()); + if (db_file.Read(raw_file) != raw_file.size()) { + return ResultUnknown; + } + + int filter_flag = STBIR_FILTER_DEFAULT; + int original_width, original_height, color_channels; + const auto dbi_image = + stbi_load_from_memory(raw_file.data(), static_cast<int>(raw_file.size()), &original_width, + &original_height, &color_channels, STBI_rgb_alpha); + + if (dbi_image == nullptr) { + return ResultUnknown; + } + + switch (flag) { + case ScreenShotDecoderFlag::EnableFancyUpsampling: + filter_flag = STBIR_FILTER_TRIANGLE; + break; + case ScreenShotDecoderFlag::EnableBlockSmoothing: + filter_flag = STBIR_FILTER_BOX; + break; + default: + filter_flag = STBIR_FILTER_DEFAULT; + break; + } + + stbir_resize_uint8_srgb(dbi_image, original_width, original_height, 0, out_image.data(), width, + height, 0, STBI_rgb_alpha, 3, filter_flag); + + return ResultSuccess; +} + +AlbumFileDateTime AlbumManager::ConvertToAlbumDateTime(u64 posix_time) const { + Time::TimeZone::CalendarInfo calendar_date{}; + const auto& time_zone_manager = + system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); + + time_zone_manager.ToCalendarTimeWithMyRules(posix_time, calendar_date); + + return { + .year = calendar_date.time.year, + .month = calendar_date.time.month, + .day = calendar_date.time.day, + .hour = calendar_date.time.hour, + .minute = calendar_date.time.minute, + .second = calendar_date.time.second, + .unique_id = 0, + }; +} + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_manager.h b/src/core/hle/service/caps/caps_manager.h new file mode 100644 index 000000000..f65eb12c1 --- /dev/null +++ b/src/core/hle/service/caps/caps_manager.h @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <unordered_map> + +#include "common/fs/fs.h" +#include "core/hle/result.h" +#include "core/hle/service/caps/caps_types.h" + +namespace Core { +class System; +} + +namespace std { +// Hash used to create lists from AlbumFileId data +template <> +struct hash<Service::Capture::AlbumFileId> { + size_t operator()(const Service::Capture::AlbumFileId& pad_id) const noexcept { + u64 hash_value = (static_cast<u64>(pad_id.date.year) << 8); + hash_value ^= (static_cast<u64>(pad_id.date.month) << 7); + hash_value ^= (static_cast<u64>(pad_id.date.day) << 6); + hash_value ^= (static_cast<u64>(pad_id.date.hour) << 5); + hash_value ^= (static_cast<u64>(pad_id.date.minute) << 4); + hash_value ^= (static_cast<u64>(pad_id.date.second) << 3); + hash_value ^= (static_cast<u64>(pad_id.date.unique_id) << 2); + hash_value ^= (static_cast<u64>(pad_id.storage) << 1); + hash_value ^= static_cast<u64>(pad_id.type); + return static_cast<size_t>(hash_value); + } +}; + +} // namespace std + +namespace Service::Capture { + +class AlbumManager { +public: + explicit AlbumManager(Core::System& system_); + ~AlbumManager(); + + Result DeleteAlbumFile(const AlbumFileId& file_id); + Result IsAlbumMounted(AlbumStorage storage); + Result GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage, + u8 flags) const; + Result GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, + ContentType contex_type, s64 start_posix_time, s64 end_posix_time, + u64 aruid) const; + Result GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries, + ContentType contex_type, AlbumFileDateTime start_date, + AlbumFileDateTime end_date, u64 aruid) const; + Result GetAutoSavingStorage(bool& out_is_autosaving) const; + Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output, + std::vector<u8>& out_image, const AlbumFileId& file_id, + const ScreenShotDecodeOption& decoder_options) const; + Result LoadAlbumScreenShotThumbnail(LoadAlbumScreenShotImageOutput& out_image_output, + std::vector<u8>& out_image, const AlbumFileId& file_id, + const ScreenShotDecodeOption& decoder_options) const; + +private: + static constexpr std::size_t NandAlbumFileLimit = 1000; + static constexpr std::size_t SdAlbumFileLimit = 10000; + + void FindScreenshots(); + Result GetFile(std::filesystem::path& out_path, const AlbumFileId& file_id) const; + Result GetAlbumEntry(AlbumEntry& out_entry, const std::filesystem::path& path) const; + Result LoadImage(std::span<u8> out_image, const std::filesystem::path& path, int width, + int height, ScreenShotDecoderFlag flag) const; + + AlbumFileDateTime ConvertToAlbumDateTime(u64 posix_time) const; + + bool is_mounted{}; + std::unordered_map<AlbumFileId, std::filesystem::path> album_files; + + Core::System& system; +}; + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_result.h b/src/core/hle/service/caps/caps_result.h new file mode 100644 index 000000000..c65e5fb9a --- /dev/null +++ b/src/core/hle/service/caps/caps_result.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::Capture { + +constexpr Result ResultWorkMemoryError(ErrorModule::Capture, 3); +constexpr Result ResultUnknown5(ErrorModule::Capture, 5); +constexpr Result ResultUnknown6(ErrorModule::Capture, 6); +constexpr Result ResultUnknown7(ErrorModule::Capture, 7); +constexpr Result ResultOutOfRange(ErrorModule::Capture, 8); +constexpr Result ResulInvalidTimestamp(ErrorModule::Capture, 12); +constexpr Result ResultInvalidStorage(ErrorModule::Capture, 13); +constexpr Result ResultInvalidFileContents(ErrorModule::Capture, 14); +constexpr Result ResultIsNotMounted(ErrorModule::Capture, 21); +constexpr Result ResultUnknown22(ErrorModule::Capture, 22); +constexpr Result ResultFileNotFound(ErrorModule::Capture, 23); +constexpr Result ResultInvalidFileData(ErrorModule::Capture, 24); +constexpr Result ResultUnknown25(ErrorModule::Capture, 25); +constexpr Result ResultReadBufferShortage(ErrorModule::Capture, 30); +constexpr Result ResultUnknown810(ErrorModule::Capture, 810); +constexpr Result ResultUnknown1024(ErrorModule::Capture, 1024); +constexpr Result ResultUnknown1202(ErrorModule::Capture, 1202); +constexpr Result ResultUnknown1203(ErrorModule::Capture, 1203); +constexpr Result ResultFileCountLimit(ErrorModule::Capture, 1401); +constexpr Result ResultUnknown1701(ErrorModule::Capture, 1701); +constexpr Result ResultUnknown1801(ErrorModule::Capture, 1801); +constexpr Result ResultUnknown1802(ErrorModule::Capture, 1802); +constexpr Result ResultUnknown1803(ErrorModule::Capture, 1803); +constexpr Result ResultUnknown1804(ErrorModule::Capture, 1804); + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_sc.cpp b/src/core/hle/service/caps/caps_sc.cpp index 395b13da7..6117cb7c6 100644 --- a/src/core/hle/service/caps/caps_sc.cpp +++ b/src/core/hle/service/caps/caps_sc.cpp @@ -5,7 +5,8 @@ namespace Service::Capture { -CAPS_SC::CAPS_SC(Core::System& system_) : ServiceFramework{system_, "caps:sc"} { +IScreenShotControlService::IScreenShotControlService(Core::System& system_) + : ServiceFramework{system_, "caps:sc"} { // clang-format off static const FunctionInfo functions[] = { {1, nullptr, "CaptureRawImage"}, @@ -34,6 +35,6 @@ CAPS_SC::CAPS_SC(Core::System& system_) : ServiceFramework{system_, "caps:sc"} { RegisterHandlers(functions); } -CAPS_SC::~CAPS_SC() = default; +IScreenShotControlService::~IScreenShotControlService() = default; } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_sc.h b/src/core/hle/service/caps/caps_sc.h index e5600f6d7..d555f4979 100644 --- a/src/core/hle/service/caps/caps_sc.h +++ b/src/core/hle/service/caps/caps_sc.h @@ -11,10 +11,10 @@ class System; namespace Service::Capture { -class CAPS_SC final : public ServiceFramework<CAPS_SC> { +class IScreenShotControlService final : public ServiceFramework<IScreenShotControlService> { public: - explicit CAPS_SC(Core::System& system_); - ~CAPS_SC() override; + explicit IScreenShotControlService(Core::System& system_); + ~IScreenShotControlService() override; }; } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp index 62b9edd41..d0d1b5425 100644 --- a/src/core/hle/service/caps/caps_ss.cpp +++ b/src/core/hle/service/caps/caps_ss.cpp @@ -5,7 +5,8 @@ namespace Service::Capture { -CAPS_SS::CAPS_SS(Core::System& system_) : ServiceFramework{system_, "caps:ss"} { +IScreenShotService::IScreenShotService(Core::System& system_) + : ServiceFramework{system_, "caps:ss"} { // clang-format off static const FunctionInfo functions[] = { {201, nullptr, "SaveScreenShot"}, @@ -21,6 +22,6 @@ CAPS_SS::CAPS_SS(Core::System& system_) : ServiceFramework{system_, "caps:ss"} { RegisterHandlers(functions); } -CAPS_SS::~CAPS_SS() = default; +IScreenShotService::~IScreenShotService() = default; } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_ss.h b/src/core/hle/service/caps/caps_ss.h index 718ade485..381e44fd4 100644 --- a/src/core/hle/service/caps/caps_ss.h +++ b/src/core/hle/service/caps/caps_ss.h @@ -11,10 +11,10 @@ class System; namespace Service::Capture { -class CAPS_SS final : public ServiceFramework<CAPS_SS> { +class IScreenShotService final : public ServiceFramework<IScreenShotService> { public: - explicit CAPS_SS(Core::System& system_); - ~CAPS_SS() override; + explicit IScreenShotService(Core::System& system_); + ~IScreenShotService() override; }; } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp index 3b11cc95c..cad173dc7 100644 --- a/src/core/hle/service/caps/caps_su.cpp +++ b/src/core/hle/service/caps/caps_su.cpp @@ -7,10 +7,11 @@ namespace Service::Capture { -CAPS_SU::CAPS_SU(Core::System& system_) : ServiceFramework{system_, "caps:su"} { +IScreenShotApplicationService::IScreenShotApplicationService(Core::System& system_) + : ServiceFramework{system_, "caps:su"} { // clang-format off static const FunctionInfo functions[] = { - {32, &CAPS_SU::SetShimLibraryVersion, "SetShimLibraryVersion"}, + {32, &IScreenShotApplicationService::SetShimLibraryVersion, "SetShimLibraryVersion"}, {201, nullptr, "SaveScreenShot"}, {203, nullptr, "SaveScreenShotEx0"}, {205, nullptr, "SaveScreenShotEx1"}, @@ -21,9 +22,9 @@ CAPS_SU::CAPS_SU(Core::System& system_) : ServiceFramework{system_, "caps:su"} { RegisterHandlers(functions); } -CAPS_SU::~CAPS_SU() = default; +IScreenShotApplicationService::~IScreenShotApplicationService() = default; -void CAPS_SU::SetShimLibraryVersion(HLERequestContext& ctx) { +void IScreenShotApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto library_version{rp.Pop<u64>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h index c6398858d..647e3059d 100644 --- a/src/core/hle/service/caps/caps_su.h +++ b/src/core/hle/service/caps/caps_su.h @@ -11,10 +11,10 @@ class System; namespace Service::Capture { -class CAPS_SU final : public ServiceFramework<CAPS_SU> { +class IScreenShotApplicationService final : public ServiceFramework<IScreenShotApplicationService> { public: - explicit CAPS_SU(Core::System& system_); - ~CAPS_SU() override; + explicit IScreenShotApplicationService(Core::System& system_); + ~IScreenShotApplicationService() override; private: void SetShimLibraryVersion(HLERequestContext& ctx); diff --git a/src/core/hle/service/caps/caps_types.h b/src/core/hle/service/caps/caps_types.h new file mode 100644 index 000000000..7fd357954 --- /dev/null +++ b/src/core/hle/service/caps/caps_types.h @@ -0,0 +1,184 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Service::Capture { + +// This is nn::album::ImageOrientation +enum class AlbumImageOrientation { + None, + Rotate90, + Rotate180, + Rotate270, +}; + +// This is nn::album::AlbumReportOption +enum class AlbumReportOption : s32 { + Disable, + Enable, +}; + +enum class ContentType : u8 { + Screenshot = 0, + Movie = 1, + ExtraMovie = 3, +}; + +enum class AlbumStorage : u8 { + Nand, + Sd, +}; + +enum class ScreenShotDecoderFlag : u64 { + None = 0, + EnableFancyUpsampling = 1 << 0, + EnableBlockSmoothing = 1 << 1, +}; + +// This is nn::capsrv::AlbumFileDateTime +struct AlbumFileDateTime { + s16 year{}; + s8 month{}; + s8 day{}; + s8 hour{}; + s8 minute{}; + s8 second{}; + s8 unique_id{}; + + friend constexpr bool operator==(const AlbumFileDateTime&, const AlbumFileDateTime&) = default; + friend constexpr bool operator>(const AlbumFileDateTime& a, const AlbumFileDateTime& b) { + if (a.year > b.year) { + return true; + } + if (a.month > b.month) { + return true; + } + if (a.day > b.day) { + return true; + } + if (a.hour > b.hour) { + return true; + } + if (a.minute > b.minute) { + return true; + } + return a.second > b.second; + }; + friend constexpr bool operator<(const AlbumFileDateTime& a, const AlbumFileDateTime& b) { + if (a.year < b.year) { + return true; + } + if (a.month < b.month) { + return true; + } + if (a.day < b.day) { + return true; + } + if (a.hour < b.hour) { + return true; + } + if (a.minute < b.minute) { + return true; + } + return a.second < b.second; + }; +}; +static_assert(sizeof(AlbumFileDateTime) == 0x8, "AlbumFileDateTime has incorrect size."); + +// This is nn::album::AlbumEntry +struct AlbumFileEntry { + u64 size{}; // Size of the entry + u64 hash{}; // AES256 with hardcoded key over AlbumEntry + AlbumFileDateTime datetime{}; + AlbumStorage storage{}; + ContentType content{}; + INSERT_PADDING_BYTES(5); + u8 unknown{}; // Set to 1 on official SW +}; +static_assert(sizeof(AlbumFileEntry) == 0x20, "AlbumFileEntry has incorrect size."); + +struct AlbumFileId { + u64 application_id{}; + AlbumFileDateTime date{}; + AlbumStorage storage{}; + ContentType type{}; + INSERT_PADDING_BYTES(0x5); + u8 unknown{}; + + friend constexpr bool operator==(const AlbumFileId&, const AlbumFileId&) = default; +}; +static_assert(sizeof(AlbumFileId) == 0x18, "AlbumFileId is an invalid size"); + +// This is nn::capsrv::AlbumEntry +struct AlbumEntry { + u64 entry_size{}; + AlbumFileId file_id{}; +}; +static_assert(sizeof(AlbumEntry) == 0x20, "AlbumEntry has incorrect size."); + +// This is nn::capsrv::ApplicationAlbumEntry +struct ApplicationAlbumEntry { + u64 size{}; // Size of the entry + u64 hash{}; // AES256 with hardcoded key over AlbumEntry + AlbumFileDateTime datetime{}; + AlbumStorage storage{}; + ContentType content{}; + INSERT_PADDING_BYTES(5); + u8 unknown{1}; // Set to 1 on official SW +}; +static_assert(sizeof(ApplicationAlbumEntry) == 0x20, "ApplicationAlbumEntry has incorrect size."); + +// This is nn::capsrv::ApplicationAlbumFileEntry +struct ApplicationAlbumFileEntry { + ApplicationAlbumEntry entry{}; + AlbumFileDateTime datetime{}; + u64 unknown{}; +}; +static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30, + "ApplicationAlbumFileEntry has incorrect size."); + +struct ApplicationData { + std::array<u8, 0x400> data{}; + u32 data_size{}; +}; +static_assert(sizeof(ApplicationData) == 0x404, "ApplicationData is an invalid size"); + +struct ScreenShotAttribute { + u32 unknown_0{}; + AlbumImageOrientation orientation{}; + u32 unknown_1{}; + u32 unknown_2{}; + INSERT_PADDING_BYTES(0x30); +}; +static_assert(sizeof(ScreenShotAttribute) == 0x40, "ScreenShotAttribute is an invalid size"); + +struct ScreenShotDecodeOption { + ScreenShotDecoderFlag flags{}; + INSERT_PADDING_BYTES(0x18); +}; +static_assert(sizeof(ScreenShotDecodeOption) == 0x20, "ScreenShotDecodeOption is an invalid size"); + +struct LoadAlbumScreenShotImageOutput { + s64 width{}; + s64 height{}; + ScreenShotAttribute attribute{}; + INSERT_PADDING_BYTES(0x400); +}; +static_assert(sizeof(LoadAlbumScreenShotImageOutput) == 0x450, + "LoadAlbumScreenShotImageOutput is an invalid size"); + +struct LoadAlbumScreenShotImageOutputForApplication { + s64 width{}; + s64 height{}; + ScreenShotAttribute attribute{}; + ApplicationData data{}; + INSERT_PADDING_BYTES(0xAC); +}; +static_assert(sizeof(LoadAlbumScreenShotImageOutputForApplication) == 0x500, + "LoadAlbumScreenShotImageOutput is an invalid size"); + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp index bffe0f8d0..b6b33fb2f 100644 --- a/src/core/hle/service/caps/caps_u.cpp +++ b/src/core/hle/service/caps/caps_u.cpp @@ -2,45 +2,29 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" -#include "core/hle/service/caps/caps.h" +#include "core/hle/service/caps/caps_manager.h" +#include "core/hle/service/caps/caps_types.h" #include "core/hle/service/caps/caps_u.h" #include "core/hle/service/ipc_helpers.h" namespace Service::Capture { -class IAlbumAccessorApplicationSession final - : public ServiceFramework<IAlbumAccessorApplicationSession> { -public: - explicit IAlbumAccessorApplicationSession(Core::System& system_) - : ServiceFramework{system_, "IAlbumAccessorApplicationSession"} { - // clang-format off - static const FunctionInfo functions[] = { - {2001, nullptr, "OpenAlbumMovieReadStream"}, - {2002, nullptr, "CloseAlbumMovieReadStream"}, - {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"}, - {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"}, - {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; - -CAPS_U::CAPS_U(Core::System& system_) : ServiceFramework{system_, "caps:u"} { +IAlbumApplicationService::IAlbumApplicationService(Core::System& system_, + std::shared_ptr<AlbumManager> album_manager) + : ServiceFramework{system_, "caps:u"}, manager{album_manager} { // clang-format off static const FunctionInfo functions[] = { - {32, &CAPS_U::SetShimLibraryVersion, "SetShimLibraryVersion"}, - {102, &CAPS_U::GetAlbumContentsFileListForApplication, "GetAlbumContentsFileListForApplication"}, - {103, nullptr, "DeleteAlbumContentsFileForApplication"}, - {104, nullptr, "GetAlbumContentsFileSizeForApplication"}, + {32, &IAlbumApplicationService::SetShimLibraryVersion, "SetShimLibraryVersion"}, + {102, &IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated, "GetAlbumFileList0AafeAruidDeprecated"}, + {103, nullptr, "DeleteAlbumFileByAruid"}, + {104, nullptr, "GetAlbumFileSizeByAruid"}, {105, nullptr, "DeleteAlbumFileByAruidForDebug"}, - {110, nullptr, "LoadAlbumContentsFileScreenShotImageForApplication"}, - {120, nullptr, "LoadAlbumContentsFileThumbnailImageForApplication"}, - {130, nullptr, "PrecheckToCreateContentsForApplication"}, + {110, nullptr, "LoadAlbumScreenShotImageByAruid"}, + {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"}, + {130, nullptr, "PrecheckToCreateContentsByAruid"}, {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"}, {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, - {142, &CAPS_U::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"}, + {142, &IAlbumApplicationService::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"}, {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, {144, nullptr, "GetAllAlbumFileList3AaeAruid"}, {60002, nullptr, "OpenAccessorSessionForApplication"}, @@ -50,9 +34,9 @@ CAPS_U::CAPS_U(Core::System& system_) : ServiceFramework{system_, "caps:u"} { RegisterHandlers(functions); } -CAPS_U::~CAPS_U() = default; +IAlbumApplicationService::~IAlbumApplicationService() = default; -void CAPS_U::SetShimLibraryVersion(HLERequestContext& ctx) { +void IAlbumApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto library_version{rp.Pop<u64>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; @@ -64,37 +48,89 @@ void CAPS_U::SetShimLibraryVersion(HLERequestContext& ctx) { rb.Push(ResultSuccess); } -void CAPS_U::GetAlbumContentsFileListForApplication(HLERequestContext& ctx) { - // Takes a type-0x6 output buffer containing an array of ApplicationAlbumFileEntry, a PID, an - // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total - // output entries (which is copied to a s32 by official SW). +void IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto pid{rp.Pop<s32>()}; - const auto content_type{rp.PopEnum<ContentType>()}; - const auto start_posix_time{rp.Pop<s64>()}; - const auto end_posix_time{rp.Pop<s64>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + struct Parameters { + ContentType content_type; + INSERT_PADDING_BYTES(7); + s64 start_posix_time; + s64 end_posix_time; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); - // TODO: Update this when we implement the album. - // Currently we do not have a method of accessing album entries, set this to 0 for now. - constexpr u32 total_entries_1{}; - constexpr u32 total_entries_2{}; + const auto parameters{rp.PopRaw<Parameters>()}; - LOG_WARNING( - Service_Capture, - "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, " - "end_posix_time={}, applet_resource_user_id={}, total_entries_1={}, total_entries_2={}", - pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id, - total_entries_1, total_entries_2); + LOG_WARNING(Service_Capture, + "(STUBBED) called. content_type={}, start_posix_time={}, end_posix_time={}, " + "applet_resource_user_id={}", + parameters.content_type, parameters.start_posix_time, parameters.end_posix_time, + parameters.applet_resource_user_id); + + Result result = ResultSuccess; + + if (result.IsSuccess()) { + result = manager->IsAlbumMounted(AlbumStorage::Sd); + } + + std::vector<ApplicationAlbumFileEntry> entries; + if (result.IsSuccess()) { + result = manager->GetAlbumFileList(entries, parameters.content_type, + parameters.start_posix_time, parameters.end_posix_time, + parameters.applet_resource_user_id); + } + + if (!entries.empty()) { + ctx.WriteBuffer(entries); + } IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(total_entries_1); - rb.Push(total_entries_2); + rb.Push(result); + rb.Push<u64>(entries.size()); } -void CAPS_U::GetAlbumFileList3AaeAruid(HLERequestContext& ctx) { - GetAlbumContentsFileListForApplication(ctx); +void IAlbumApplicationService::GetAlbumFileList3AaeAruid(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + ContentType content_type; + INSERT_PADDING_BYTES(1); + AlbumFileDateTime start_date_time; + AlbumFileDateTime end_date_time; + INSERT_PADDING_BYTES(6); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_Capture, + "(STUBBED) called. content_type={}, start_date={}/{}/{}, " + "end_date={}/{}/{}, applet_resource_user_id={}", + parameters.content_type, parameters.start_date_time.year, + parameters.start_date_time.month, parameters.start_date_time.day, + parameters.end_date_time.year, parameters.end_date_time.month, + parameters.end_date_time.day, parameters.applet_resource_user_id); + + Result result = ResultSuccess; + + if (result.IsSuccess()) { + result = manager->IsAlbumMounted(AlbumStorage::Sd); + } + + std::vector<ApplicationAlbumEntry> entries; + if (result.IsSuccess()) { + result = + manager->GetAlbumFileList(entries, parameters.content_type, parameters.start_date_time, + parameters.end_date_time, parameters.applet_resource_user_id); + } + + if (!entries.empty()) { + ctx.WriteBuffer(entries); + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push<u64>(entries.size()); } } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h index e8dd037d7..9458c128e 100644 --- a/src/core/hle/service/caps/caps_u.h +++ b/src/core/hle/service/caps/caps_u.h @@ -10,16 +10,20 @@ class System; } namespace Service::Capture { +class AlbumManager; -class CAPS_U final : public ServiceFramework<CAPS_U> { +class IAlbumApplicationService final : public ServiceFramework<IAlbumApplicationService> { public: - explicit CAPS_U(Core::System& system_); - ~CAPS_U() override; + explicit IAlbumApplicationService(Core::System& system_, + std::shared_ptr<AlbumManager> album_manager); + ~IAlbumApplicationService() override; private: void SetShimLibraryVersion(HLERequestContext& ctx); - void GetAlbumContentsFileListForApplication(HLERequestContext& ctx); + void GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx); void GetAlbumFileList3AaeAruid(HLERequestContext& ctx); + + std::shared_ptr<AlbumManager> manager = nullptr; }; } // namespace Service::Capture diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp index f6a1e54f2..6f3ae3cc4 100644 --- a/src/core/hle/service/hle_ipc.cpp +++ b/src/core/hle/service/hle_ipc.cpp @@ -23,6 +23,17 @@ #include "core/hle/service/ipc_helpers.h" #include "core/memory.h" +namespace { +static thread_local std::array read_buffer_data_a{ + Common::ScratchBuffer<u8>(), + Common::ScratchBuffer<u8>(), +}; +static thread_local std::array read_buffer_data_x{ + Common::ScratchBuffer<u8>(), + Common::ScratchBuffer<u8>(), +}; +} // Anonymous namespace + namespace Service { SessionRequestHandler::SessionRequestHandler(Kernel::KernelCore& kernel_, const char* service_name_) @@ -328,26 +339,57 @@ std::vector<u8> HLERequestContext::ReadBufferCopy(std::size_t buffer_index) cons } } -std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { +std::span<const u8> HLERequestContext::ReadBufferA(std::size_t buffer_index) const { static thread_local std::array read_buffer_a{ Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), }; - static thread_local std::array read_buffer_data_a{ - Common::ScratchBuffer<u8>(), - Common::ScratchBuffer<u8>(), - }; + + ASSERT_OR_EXECUTE_MSG( + BufferDescriptorA().size() > buffer_index, { return {}; }, + "BufferDescriptorA invalid buffer_index {}", buffer_index); + auto& read_buffer = read_buffer_a[buffer_index]; + return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(), + BufferDescriptorA()[buffer_index].Size(), + &read_buffer_data_a[buffer_index]); +} + +std::span<const u8> HLERequestContext::ReadBufferX(std::size_t buffer_index) const { static thread_local std::array read_buffer_x{ Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), }; - static thread_local std::array read_buffer_data_x{ - Common::ScratchBuffer<u8>(), - Common::ScratchBuffer<u8>(), + + ASSERT_OR_EXECUTE_MSG( + BufferDescriptorX().size() > buffer_index, { return {}; }, + "BufferDescriptorX invalid buffer_index {}", buffer_index); + auto& read_buffer = read_buffer_x[buffer_index]; + return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(), + BufferDescriptorX()[buffer_index].Size(), + &read_buffer_data_x[buffer_index]); +} + +std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { + static thread_local std::array read_buffer_a{ + Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), + Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), + }; + static thread_local std::array read_buffer_x{ + Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), + Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), }; const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && BufferDescriptorA()[buffer_index].Size()}; + const bool is_buffer_x{BufferDescriptorX().size() > buffer_index && + BufferDescriptorX()[buffer_index].Size()}; + + if (is_buffer_a && is_buffer_x) { + LOG_WARNING(Input, "Both buffer descriptors are available a.size={}, x.size={}", + BufferDescriptorA()[buffer_index].Size(), + BufferDescriptorX()[buffer_index].Size()); + } + if (is_buffer_a) { ASSERT_OR_EXECUTE_MSG( BufferDescriptorA().size() > buffer_index, { return {}; }, diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h index 4bd24c899..ad5259a5c 100644 --- a/src/core/hle/service/hle_ipc.h +++ b/src/core/hle/service/hle_ipc.h @@ -253,6 +253,12 @@ public: return domain_message_header.has_value(); } + /// Helper function to get a span of a buffer using the buffer descriptor A + [[nodiscard]] std::span<const u8> ReadBufferA(std::size_t buffer_index = 0) const; + + /// Helper function to get a span of a buffer using the buffer descriptor X + [[nodiscard]] std::span<const u8> ReadBufferX(std::size_t buffer_index = 0) const; + /// Helper function to get a span of a buffer using the appropriate buffer descriptor [[nodiscard]] std::span<const u8> ReadBuffer(std::size_t buffer_index = 0) const; diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp index 4ed3f02e2..0090e8568 100644 --- a/src/core/hle/service/jit/jit_context.cpp +++ b/src/core/hle/service/jit/jit_context.cpp @@ -156,6 +156,8 @@ public: bool LoadNRO(std::span<const u8> data) { local_memory.clear(); + + relocbase = local_memory.size(); local_memory.insert(local_memory.end(), data.begin(), data.end()); if (FixupRelocations()) { @@ -181,8 +183,8 @@ public: // https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html // https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)}; - VAddr rela_dyn = 0; - size_t num_rela = 0; + VAddr rela_dyn = 0, relr_dyn = 0; + size_t num_rela = 0, num_relr = 0; while (true) { const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)}; dynamic_offset += sizeof(Elf64_Dyn); @@ -196,6 +198,12 @@ public: if (dyn.d_tag == ElfDtRelasz) { num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela); } + if (dyn.d_tag == ElfDtRelr) { + relr_dyn = dyn.d_un.d_ptr; + } + if (dyn.d_tag == ElfDtRelrsz) { + num_relr = dyn.d_un.d_val / sizeof(Elf64_Relr); + } } for (size_t i = 0; i < num_rela; i++) { @@ -207,6 +215,29 @@ public: callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend); } + VAddr relr_where = 0; + for (size_t i = 0; i < num_relr; i++) { + const auto relr{callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr))}; + const auto incr{[&](VAddr where) { + callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase); + }}; + + if ((relr & 1) == 0) { + // where pointer + relr_where = relocbase + relr; + incr(relr_where); + relr_where += sizeof(Elf64_Addr); + } else { + // bitmap + for (int bit = 1; bit < 64; bit++) { + if ((relr & (1ULL << bit)) != 0) { + incr(relr_where + i * sizeof(Elf64_Addr)); + } + } + relr_where += 63 * sizeof(Elf64_Addr); + } + } + return true; } @@ -313,6 +344,7 @@ public: Core::Memory::Memory& memory; VAddr top_of_stack; VAddr heap_pointer; + VAddr relocbase; }; void DynarmicCallbacks64::CallSVC(u32 swi) { diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 21b06d10b..22dc55a6d 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -545,6 +545,16 @@ void IGeneralService::IsAnyInternetRequestAccepted(HLERequestContext& ctx) { } } +void IGeneralService::IsAnyForegroundRequestAccepted(HLERequestContext& ctx) { + const bool is_accepted{}; + + LOG_WARNING(Service_NIFM, "(STUBBED) called, is_accepted={}", is_accepted); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u8>(is_accepted); +} + IGeneralService::IGeneralService(Core::System& system_) : ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} { // clang-format off @@ -569,7 +579,7 @@ IGeneralService::IGeneralService(Core::System& system_) {19, nullptr, "SetEthernetCommunicationEnabled"}, {20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"}, {21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"}, - {22, nullptr, "IsAnyForegroundRequestAccepted"}, + {22, &IGeneralService::IsAnyForegroundRequestAccepted, "IsAnyForegroundRequestAccepted"}, {23, nullptr, "PutToSleep"}, {24, nullptr, "WakeUp"}, {25, nullptr, "GetSsidListVersion"}, diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h index ae99c4695..b74b66438 100644 --- a/src/core/hle/service/nifm/nifm.h +++ b/src/core/hle/service/nifm/nifm.h @@ -35,6 +35,7 @@ private: void GetInternetConnectionStatus(HLERequestContext& ctx); void IsEthernetCommunicationEnabled(HLERequestContext& ctx); void IsAnyInternetRequestAccepted(HLERequestContext& ctx); + void IsAnyForegroundRequestAccepted(HLERequestContext& ctx); Network::RoomNetwork& network; }; diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 6e0baf0be..f9e0e272d 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -7,6 +7,7 @@ #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/vfs.h" +#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/glue/glue_manager.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ns/errors.h" @@ -502,8 +503,8 @@ IContentManagementInterface::IContentManagementInterface(Core::System& system_) static const FunctionInfo functions[] = { {11, nullptr, "CalculateApplicationOccupiedSize"}, {43, nullptr, "CheckSdCardMountStatus"}, - {47, nullptr, "GetTotalSpaceSize"}, - {48, nullptr, "GetFreeSpaceSize"}, + {47, &IContentManagementInterface::GetTotalSpaceSize, "GetTotalSpaceSize"}, + {48, &IContentManagementInterface::GetFreeSpaceSize, "GetFreeSpaceSize"}, {600, nullptr, "CountApplicationContentMeta"}, {601, nullptr, "ListApplicationContentMetaStatus"}, {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, @@ -516,6 +517,28 @@ IContentManagementInterface::IContentManagementInterface(Core::System& system_) IContentManagementInterface::~IContentManagementInterface() = default; +void IContentManagementInterface::GetTotalSpaceSize(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto storage{rp.PopEnum<FileSys::StorageId>()}; + + LOG_INFO(Service_Capture, "called, storage={}", storage); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(system.GetFileSystemController().GetTotalSpaceSize(storage)); +} + +void IContentManagementInterface::GetFreeSpaceSize(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto storage{rp.PopEnum<FileSys::StorageId>()}; + + LOG_INFO(Service_Capture, "called, storage={}", storage); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(system.GetFileSystemController().GetFreeSpaceSize(storage)); +} + IDocumentInterface::IDocumentInterface(Core::System& system_) : ServiceFramework{system_, "IDocumentInterface"} { // clang-format off diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h index 175dad780..34d2a45dc 100644 --- a/src/core/hle/service/ns/ns.h +++ b/src/core/hle/service/ns/ns.h @@ -48,6 +48,10 @@ class IContentManagementInterface final : public ServiceFramework<IContentManage public: explicit IContentManagementInterface(Core::System& system_); ~IContentManagementInterface() override; + +private: + void GetTotalSpaceSize(HLERequestContext& ctx); + void GetFreeSpaceSize(HLERequestContext& ctx); }; class IDocumentInterface final : public ServiceFramework<IDocumentInterface> { diff --git a/src/core/hle/service/pctl/pctl_module.cpp b/src/core/hle/service/pctl/pctl_module.cpp index 5db1703d1..938330dd0 100644 --- a/src/core/hle/service/pctl/pctl_module.cpp +++ b/src/core/hle/service/pctl/pctl_module.cpp @@ -33,7 +33,7 @@ public: {1001, &IParentalControlService::CheckFreeCommunicationPermission, "CheckFreeCommunicationPermission"}, {1002, nullptr, "ConfirmLaunchApplicationPermission"}, {1003, nullptr, "ConfirmResumeApplicationPermission"}, - {1004, nullptr, "ConfirmSnsPostPermission"}, + {1004, &IParentalControlService::ConfirmSnsPostPermission, "ConfirmSnsPostPermission"}, {1005, nullptr, "ConfirmSystemSettingsPermission"}, {1006, &IParentalControlService::IsRestrictionTemporaryUnlocked, "IsRestrictionTemporaryUnlocked"}, {1007, nullptr, "RevertRestrictionTemporaryUnlocked"}, @@ -236,6 +236,13 @@ private: states.free_communication = true; } + void ConfirmSnsPostPermission(HLERequestContext& ctx) { + LOG_WARNING(Service_PCTL, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(Error::ResultNoFreeCommunication); + } + void IsRestrictionTemporaryUnlocked(HLERequestContext& ctx) { const bool is_temporary_unlocked = false; diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index ec4a84989..14e8df63a 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -58,14 +58,8 @@ private: IPC::RequestParser rp{ctx}; const auto process_id = rp.PopRaw<u64>(); - const auto data1 = ctx.ReadBuffer(0); - const auto data2 = [&ctx] { - if (ctx.CanReadBuffer(1)) { - return ctx.ReadBuffer(1); - } - - return std::span<const u8>{}; - }(); + const auto data1 = ctx.ReadBufferA(0); + const auto data2 = ctx.ReadBufferX(0); LOG_DEBUG(Service_PREPO, "called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}", @@ -85,14 +79,8 @@ private: const auto user_id = rp.PopRaw<u128>(); const auto process_id = rp.PopRaw<u64>(); - const auto data1 = ctx.ReadBuffer(0); - const auto data2 = [&ctx] { - if (ctx.CanReadBuffer(1)) { - return ctx.ReadBuffer(1); - } - - return std::span<const u8>{}; - }(); + const auto data1 = ctx.ReadBufferA(0); + const auto data2 = ctx.ReadBufferX(0); LOG_DEBUG(Service_PREPO, "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, " @@ -137,14 +125,8 @@ private: IPC::RequestParser rp{ctx}; const auto title_id = rp.PopRaw<u64>(); - const auto data1 = ctx.ReadBuffer(0); - const auto data2 = [&ctx] { - if (ctx.CanReadBuffer(1)) { - return ctx.ReadBuffer(1); - } - - return std::span<const u8>{}; - }(); + const auto data1 = ctx.ReadBufferA(0); + const auto data2 = ctx.ReadBufferX(0); LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}", title_id, data1.size(), data2.size()); @@ -161,14 +143,8 @@ private: const auto user_id = rp.PopRaw<u128>(); const auto title_id = rp.PopRaw<u64>(); - const auto data1 = ctx.ReadBuffer(0); - const auto data2 = [&ctx] { - if (ctx.CanReadBuffer(1)) { - return ctx.ReadBuffer(1); - } - - return std::span<const u8>{}; - }(); + const auto data1 = ctx.ReadBufferA(0); + const auto data2 = ctx.ReadBufferX(0); LOG_DEBUG(Service_PREPO, "called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, " diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 5a42dea48..5c36b71e5 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -118,7 +118,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect return {ResultStatus::ErrorMissingNPDM, {}}; } - const ResultStatus result2 = metadata.Load(npdm); + const ResultStatus result2 = metadata.Reload(npdm); if (result2 != ResultStatus::Success) { return {result2, {}}; } |