diff options
Diffstat (limited to 'src/core/hle')
32 files changed, 1328 insertions, 261 deletions
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 5b51edf30..1fbfbf31f 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, 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/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/am/am.cpp b/src/core/hle/service/am/am.cpp index 819dea6a7..ac376b55a 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" @@ -764,6 +764,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 +847,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 +865,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 +946,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 +1042,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>()}; @@ -1493,6 +1573,9 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) case Applets::AppletId::MiiEdit: PushInShowMiiEditData(); break; + case Applets::AppletId::PhotoViewer: + PushInShowAlbum(); + break; default: break; } @@ -1569,6 +1652,23 @@ void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& rb.PushRaw(applet_info); } +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..4a045cfd4 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -195,6 +195,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 +254,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 +266,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); @@ -327,6 +346,7 @@ private: void ExitProcessAndReturn(HLERequestContext& ctx); void GetCallerAppletIdentityInfo(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..286f9fd10 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>(); + + 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..e22f72bf6 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, 3}; + rb.Push(result); + rb.Push(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..2df6a930a --- /dev/null +++ b/src/core/hle/service/caps/caps_manager.cpp @@ -0,0 +1,342 @@ +// 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/hle/service/caps/caps_manager.h" +#include "core/hle/service/caps/caps_result.h" + +namespace Service::Capture { + +AlbumManager::AlbumManager() {} + +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, 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); + ApplicationAlbumFileEntry entry{.entry = + { + .size = entry_size, + .hash{}, + .datetime = file_id.date, + .storage = file_id.storage, + .content = contex_type, + .unknown = 1, + }, + .datetime = file_id.date, + .unknown = {}}; + 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<u16>(std::stoi(year)), + .month = static_cast<u8>(std::stoi(month)), + .day = static_cast<u8>(std::stoi(day)), + .hour = static_cast<u8>(std::stoi(hour)), + .minute = static_cast<u8>(std::stoi(minute)), + .second = static_cast<u8>(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; +} +} // 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..8337c655c --- /dev/null +++ b/src/core/hle/service/caps/caps_manager.h @@ -0,0 +1,72 @@ +// 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(); + ~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, 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; + + bool is_mounted{}; + std::unordered_map<AlbumFileId, std::filesystem::path> album_files; +}; + +} // 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..bf6061273 --- /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 { + u16 year{}; + u8 month{}; + u8 day{}; + u8 hour{}; + u8 minute{}; + u8 second{}; + u8 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..260f25490 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,10 +48,7 @@ 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>()}; @@ -75,26 +56,49 @@ void CAPS_U::GetAlbumContentsFileListForApplication(HLERequestContext& ctx) { const auto end_posix_time{rp.Pop<s64>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - // 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{}; + LOG_WARNING(Service_Capture, + "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, " + "end_posix_time={}, applet_resource_user_id={}", + pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id); + + // TODO: Translate posix to DateTime + + std::vector<ApplicationAlbumFileEntry> entries; + const Result result = + manager->GetAlbumFileList(entries, content_type, {}, {}, applet_resource_user_id); - 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); + 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}; + const auto pid{rp.Pop<s32>()}; + const auto content_type{rp.PopEnum<ContentType>()}; + const auto start_date_time{rp.PopRaw<AlbumFileDateTime>()}; + const auto end_date_time{rp.PopRaw<AlbumFileDateTime>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_Capture, + "(STUBBED) called. pid={}, content_type={}, applet_resource_user_id={}", pid, + content_type, applet_resource_user_id); + + std::vector<ApplicationAlbumFileEntry> entries; + const Result result = manager->GetAlbumFileList(entries, content_type, start_date_time, + end_date_time, 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/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; |