diff options
| -rw-r--r-- | src/core/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | src/core/hle/service/am/am.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps.cpp | 21 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps.h | 81 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_a.cpp | 311 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_a.h | 107 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_c.cpp | 50 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_c.h | 10 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_manager.cpp | 342 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_manager.h | 72 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_result.h | 35 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_sc.cpp | 5 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_sc.h | 6 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_ss.cpp | 5 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_ss.h | 6 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_su.cpp | 9 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_su.h | 6 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_types.h | 184 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_u.cpp | 104 | ||||
| -rw-r--r-- | src/core/hle/service/caps/caps_u.h | 12 | 
20 files changed, 855 insertions, 521 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/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 3bb080883..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" diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp index 0dbf04862..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<IAlbumAccessorService>(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 e1f836e08..e22f72bf6 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp @@ -1,40 +1,18 @@  // SPDX-FileCopyrightText: Copyright 2020 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_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); -    } -}; - -IAlbumAccessorService::IAlbumAccessorService(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"}, @@ -91,30 +69,25 @@ void IAlbumAccessorService::DeleteAlbumFile(HLERequestContext& ctx) {      LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}",               file_id.application_id, file_id.storage, file_id.type); -    if (file_id.storage == AlbumStorage::Sd) { -        if (!Common::FS::RemoveFile(sd_image_paths[file_id.date.unique_id])) { -            IPC::ResponseBuilder rb{ctx, 2}; -            rb.Push(ResultUnknown); -            return; -        } -    } +    Result result = manager->DeleteAlbumFile(file_id); +    result = TranslateResult(result);      IPC::ResponseBuilder rb{ctx, 2}; -    rb.Push(ResultSuccess); +    rb.Push(result);  }  void IAlbumAccessorService::IsAlbumMounted(HLERequestContext& ctx) {      IPC::RequestParser rp{ctx};      const auto storage{rp.PopEnum<AlbumStorage>()}; -    LOG_INFO(Service_Capture, "called, storage={}, is_mounted={}", storage, is_mounted); +    LOG_INFO(Service_Capture, "called, storage={}", storage); -    if (storage == AlbumStorage::Sd) { -        FindScreenshots(); -    } +    Result result = manager->IsAlbumMounted(storage); +    const bool is_mounted = result.IsSuccess(); +    result = TranslateResult(result);      IPC::ResponseBuilder rb{ctx, 3}; -    rb.Push(ResultSuccess); +    rb.Push(result);      rb.Push<u8>(is_mounted);  } @@ -141,38 +114,34 @@ 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{}; +    std::vector<AlbumEntry> entries; +    Result result = manager->GetAlbumFileList(entries, storage, flags); +    result = TranslateResult(result); -    if (storage == AlbumStorage::Sd) { -        AlbumEntry entry; -        for (u8 i = 0; i < static_cast<u8>(sd_image_paths.size()); i++) { -            if (GetAlbumEntry(entry, sd_image_paths[i]).IsError()) { -                continue; -            } -            entry.file_id.date.unique_id = i; -            entries.push_back(entry); -        } -    } +    entries.resize(std::min(album_entry_size, entries.size()));      if (!entries.empty()) {          ctx.WriteBuffer(entries);      }      IPC::ResponseBuilder rb{ctx, 3}; -    rb.Push(ResultSuccess); +    rb.Push(result);      rb.Push(entries.size());  }  void IAlbumAccessorService::GetAutoSavingStorage(HLERequestContext& ctx) { -    bool is_autosaving{}; +    LOG_WARNING(Service_Capture, "(STUBBED) called"); -    LOG_WARNING(Service_Capture, "(STUBBED) called, is_autosaving={}", is_autosaving); +    bool is_autosaving{}; +    Result result = manager->GetAutoSavingStorage(is_autosaving); +    result = TranslateResult(result);      IPC::ResponseBuilder rb{ctx, 3}; -    rb.Push(ResultSuccess); +    rb.Push(result);      rb.Push<u8>(is_autosaving);  } @@ -180,35 +149,28 @@ 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); -    const LoadAlbumScreenShotImageOutput image_output{ -        .width = 1280, -        .height = 720, -        .attribute = -            { -                .unknown_0{}, -                .orientation = ScreenShotOrientation::None, -                .unknown_1{}, -                .unknown_2{}, -            }, -    }; - -    std::vector<u8> image(image_output.height * image_output.width * STBI_rgb_alpha); +    std::vector<u8> image; +    LoadAlbumScreenShotImageOutput image_output; +    Result result = +        manager->LoadAlbumScreenShotImage(image_output, image, file_id, decoder_options); +    result = TranslateResult(result); -    if (file_id.storage == AlbumStorage::Sd) { -        LoadImage(image, sd_image_paths[file_id.date.unique_id], -                  static_cast<int>(image_output.width), static_cast<int>(image_output.height), -                  decoder_options.flags); +    if (image.size() > image_buffer_size) { +        result = ResultWorkMemoryError;      } -    ctx.WriteBuffer(image_output, 0); -    ctx.WriteBuffer(image, 1); +    if (result.IsSuccess()) { +        ctx.WriteBuffer(image_output, 0); +        ctx.WriteBuffer(image, 1); +    }      IPC::ResponseBuilder rb{ctx, 2}; -    rb.Push(ResultSuccess); +    rb.Push(result);  }  void IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx) { @@ -219,157 +181,78 @@ void IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(HLERequestConte      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); -    const LoadAlbumScreenShotImageOutput image_output{ -        .width = 320, -        .height = 180, -        .attribute = -            { -                .unknown_0{}, -                .orientation = ScreenShotOrientation::None, -                .unknown_1{}, -                .unknown_2{}, -            }, -    }; - -    std::vector<u8> image(image_output.height * image_output.width * STBI_rgb_alpha); +    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 (file_id.storage == AlbumStorage::Sd) { -        LoadImage(image, sd_image_paths[file_id.date.unique_id], -                  static_cast<int>(image_output.width), static_cast<int>(image_output.height), -                  decoder_options.flags); +    if (result.IsSuccess()) { +        ctx.WriteBuffer(image_output, 0); +        ctx.WriteBuffer(image, 1);      } -    ctx.WriteBuffer(image_output, 0); -    ctx.WriteBuffer(image, 1); -      IPC::ResponseBuilder rb{ctx, 2}; -    rb.Push(ResultSuccess); +    rb.Push(result);  } -void IAlbumAccessorService::FindScreenshots() { -    is_mounted = false; -    sd_image_paths.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; -            // TODO: Implement proper indexing to allow more images -            if (sd_image_paths.size() > 0xFF) { -                return true; -            } -            if (GetAlbumEntry(entry, full_path).IsSuccess()) { -                sd_image_paths.push_back(full_path); -            } -            return true; -        }, -        Common::FS::DirEntryFilter::File); - -    is_mounted = true; -} - -Result IAlbumAccessorService::GetAlbumEntry(AlbumEntry& out_entry, -                                            const std::filesystem::path& path) { -    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 IAlbumAccessorService::LoadImage(std::span<u8> out_image, const std::filesystem::path& path, -                                        int width, int height, ScreenShotDecoderFlag flag) { -    if (out_image.size() != static_cast<std::size_t>(width * height * STBI_rgb_alpha)) { -        return ResultUnknown; +Result IAlbumAccessorService::TranslateResult(Result in_result) { +    if (in_result.IsSuccess()) { +        return in_result;      } -    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; -    } +    if ((in_result.raw & 0x3801ff) == ResultUnknown1024.raw) { +        if (in_result.description - 0x514 < 100) { +            return ResultInvalidFileData; +        } +        if (in_result.description - 0x5dc < 100) { +            return ResultInvalidFileData; +        } -    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 (in_result.description - 0x578 < 100) { +            if (in_result == ResultFileCountLimit) { +                return ResultUnknown22; +            } +            return ResultUnknown25; +        } -    if (dbi_image == nullptr) { -        return ResultUnknown; +        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;      } -    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; +    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; +        }      } -    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; +    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 975c0ebdb..c90cff71e 100644 --- a/src/core/hle/service/caps/caps_a.h +++ b/src/core/hle/service/caps/caps_a.h @@ -3,7 +3,6 @@  #pragma once -#include "common/fs/fs.h"  #include "core/hle/service/service.h"  namespace Core { @@ -11,106 +10,15 @@ class System;  }  namespace Service::Capture { +class AlbumManager;  class IAlbumAccessorService final : public ServiceFramework<IAlbumAccessorService> {  public: -    explicit IAlbumAccessorService(Core::System& system_); +    explicit IAlbumAccessorService(Core::System& system_, +                                   std::shared_ptr<AlbumManager> album_manager);      ~IAlbumAccessorService() override;  private: -    enum class ContentType : u8 { -        Screenshot, -        Movie, -        ExtraMovie, -    }; - -    enum class AlbumStorage : u8 { -        Nand, -        Sd, - -    }; - -    enum class ScreenShotDecoderFlag : u64 { -        None = 0, -        EnableFancyUpsampling = 1 << 0, -        EnableBlockSmoothing = 1 << 1, -    }; - -    enum class ScreenShotOrientation : u32 { -        None, -        Rotate90, -        Rotate180, -        Rotate270, -    }; - -    struct ScreenShotAttribute { -        u32 unknown_0; -        ScreenShotOrientation 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 AlbumFileDateTime { -        u16 year; -        u8 month; -        u8 day; -        u8 hour; -        u8 minute; -        u8 second; -        u8 unique_id; -    }; -    static_assert(sizeof(AlbumFileDateTime) == 0x8, "AlbumFileDateTime is an invalid size"); - -    struct AlbumFileId { -        u64 application_id; -        AlbumFileDateTime date; -        AlbumStorage storage; -        ContentType type; -        INSERT_PADDING_BYTES(0x5); -        u8 unknown; -    }; -    static_assert(sizeof(AlbumFileId) == 0x18, "AlbumFileId is an invalid size"); - -    struct AlbumEntry { -        u64 entry_size; -        AlbumFileId file_id; -    }; -    static_assert(sizeof(AlbumEntry) == 0x20, "AlbumEntry is an invalid size"); - -    struct ApplicationData { -        std::array<u8, 0x400> data; -        u32 data_size; -    }; -    static_assert(sizeof(ApplicationData) == 0x404, "ApplicationData 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"); -      void DeleteAlbumFile(HLERequestContext& ctx);      void IsAlbumMounted(HLERequestContext& ctx);      void Unknown18(HLERequestContext& ctx); @@ -119,14 +27,9 @@ private:      void LoadAlbumScreenShotImageEx1(HLERequestContext& ctx);      void LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx); -private: -    void FindScreenshots(); -    Result GetAlbumEntry(AlbumEntry& out_entry, const std::filesystem::path& path); -    Result LoadImage(std::span<u8> out_image, const std::filesystem::path& path, int width, -                     int height, ScreenShotDecoderFlag flag); +    Result TranslateResult(Result in_result); -    bool is_mounted{}; -    std::vector<std::filesystem::path> sd_image_paths{}; +    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 | 
