service: caps: Partially implement IAlbumAccessorService
This commit is contained in:
parent
2fa53ec1d9
commit
c334959440
|
@ -16,7 +16,7 @@ namespace Service::Capture {
|
||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("caps:a", std::make_shared<CAPS_A>(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:c", std::make_shared<CAPS_C>(system));
|
||||||
server_manager->RegisterNamedService("caps:u", std::make_shared<CAPS_U>(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:sc", std::make_shared<CAPS_SC>(system));
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// 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 "core/hle/service/caps/caps_a.h"
|
#include "core/hle/service/caps/caps_a.h"
|
||||||
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
|
|
||||||
namespace Service::Capture {
|
namespace Service::Capture {
|
||||||
|
|
||||||
|
@ -26,15 +33,16 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CAPS_A::CAPS_A(Core::System& system_) : ServiceFramework{system_, "caps:a"} {
|
IAlbumAccessorService::IAlbumAccessorService(Core::System& system_)
|
||||||
|
: ServiceFramework{system_, "caps:a"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, nullptr, "GetAlbumFileCount"},
|
{0, nullptr, "GetAlbumFileCount"},
|
||||||
{1, nullptr, "GetAlbumFileList"},
|
{1, nullptr, "GetAlbumFileList"},
|
||||||
{2, nullptr, "LoadAlbumFile"},
|
{2, nullptr, "LoadAlbumFile"},
|
||||||
{3, nullptr, "DeleteAlbumFile"},
|
{3, &IAlbumAccessorService::DeleteAlbumFile, "DeleteAlbumFile"},
|
||||||
{4, nullptr, "StorageCopyAlbumFile"},
|
{4, nullptr, "StorageCopyAlbumFile"},
|
||||||
{5, nullptr, "IsAlbumMounted"},
|
{5, &IAlbumAccessorService::IsAlbumMounted, "IsAlbumMounted"},
|
||||||
{6, nullptr, "GetAlbumUsage"},
|
{6, nullptr, "GetAlbumUsage"},
|
||||||
{7, nullptr, "GetAlbumFileSize"},
|
{7, nullptr, "GetAlbumFileSize"},
|
||||||
{8, nullptr, "LoadAlbumFileThumbnail"},
|
{8, nullptr, "LoadAlbumFileThumbnail"},
|
||||||
|
@ -47,18 +55,18 @@ CAPS_A::CAPS_A(Core::System& system_) : ServiceFramework{system_, "caps:a"} {
|
||||||
{15, nullptr, "GetAlbumUsage3"},
|
{15, nullptr, "GetAlbumUsage3"},
|
||||||
{16, nullptr, "GetAlbumMountResult"},
|
{16, nullptr, "GetAlbumMountResult"},
|
||||||
{17, nullptr, "GetAlbumUsage16"},
|
{17, nullptr, "GetAlbumUsage16"},
|
||||||
{18, nullptr, "Unknown18"},
|
{18, &IAlbumAccessorService::Unknown18, "Unknown18"},
|
||||||
{19, nullptr, "Unknown19"},
|
{19, nullptr, "Unknown19"},
|
||||||
{100, nullptr, "GetAlbumFileCountEx0"},
|
{100, nullptr, "GetAlbumFileCountEx0"},
|
||||||
{101, nullptr, "GetAlbumFileListEx0"},
|
{101, &IAlbumAccessorService::GetAlbumFileListEx0, "GetAlbumFileListEx0"},
|
||||||
{202, nullptr, "SaveEditedScreenShot"},
|
{202, nullptr, "SaveEditedScreenShot"},
|
||||||
{301, nullptr, "GetLastThumbnail"},
|
{301, nullptr, "GetLastThumbnail"},
|
||||||
{302, nullptr, "GetLastOverlayMovieThumbnail"},
|
{302, nullptr, "GetLastOverlayMovieThumbnail"},
|
||||||
{401, nullptr, "GetAutoSavingStorage"},
|
{401, &IAlbumAccessorService::GetAutoSavingStorage, "GetAutoSavingStorage"},
|
||||||
{501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"},
|
{501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"},
|
||||||
{1001, nullptr, "LoadAlbumScreenShotThumbnailImageEx0"},
|
{1001, nullptr, "LoadAlbumScreenShotThumbnailImageEx0"},
|
||||||
{1002, nullptr, "LoadAlbumScreenShotImageEx1"},
|
{1002, &IAlbumAccessorService::LoadAlbumScreenShotImageEx1, "LoadAlbumScreenShotImageEx1"},
|
||||||
{1003, nullptr, "LoadAlbumScreenShotThumbnailImageEx1"},
|
{1003, &IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1, "LoadAlbumScreenShotThumbnailImageEx1"},
|
||||||
{8001, nullptr, "ForceAlbumUnmounted"},
|
{8001, nullptr, "ForceAlbumUnmounted"},
|
||||||
{8002, nullptr, "ResetAlbumMountStatus"},
|
{8002, nullptr, "ResetAlbumMountStatus"},
|
||||||
{8011, nullptr, "RefreshAlbumCache"},
|
{8011, nullptr, "RefreshAlbumCache"},
|
||||||
|
@ -74,6 +82,294 @@ CAPS_A::CAPS_A(Core::System& system_) : ServiceFramework{system_, "caps:a"} {
|
||||||
RegisterHandlers(functions);
|
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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (storage == AlbumStorage::Sd) {
|
||||||
|
FindScreenshots();
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
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>()};
|
||||||
|
|
||||||
|
LOG_INFO(Service_Capture, "called, storage={}, flags={}", storage, flags);
|
||||||
|
|
||||||
|
std::vector<AlbumEntry> entries{};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entries.empty()) {
|
||||||
|
ctx.WriteBuffer(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push(entries.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void IAlbumAccessorService::GetAutoSavingStorage(HLERequestContext& ctx) {
|
||||||
|
bool is_autosaving{};
|
||||||
|
|
||||||
|
LOG_WARNING(Service_Capture, "(STUBBED) called, is_autosaving={}", is_autosaving);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
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>()};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.WriteBuffer(image_output, 0);
|
||||||
|
ctx.WriteBuffer(image, 1);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.WriteBuffer(image_output, 0);
|
||||||
|
ctx.WriteBuffer(image, 1);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
} // namespace Service::Capture
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/fs/fs.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
@ -11,10 +12,121 @@ class System;
|
||||||
|
|
||||||
namespace Service::Capture {
|
namespace Service::Capture {
|
||||||
|
|
||||||
class CAPS_A final : public ServiceFramework<CAPS_A> {
|
class IAlbumAccessorService final : public ServiceFramework<IAlbumAccessorService> {
|
||||||
public:
|
public:
|
||||||
explicit CAPS_A(Core::System& system_);
|
explicit IAlbumAccessorService(Core::System& system_);
|
||||||
~CAPS_A() override;
|
~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);
|
||||||
|
void GetAlbumFileListEx0(HLERequestContext& ctx);
|
||||||
|
void GetAutoSavingStorage(HLERequestContext& ctx);
|
||||||
|
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);
|
||||||
|
|
||||||
|
bool is_mounted{};
|
||||||
|
std::vector<std::filesystem::path> sd_image_paths{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::Capture
|
} // namespace Service::Capture
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "core/file_sys/control_metadata.h"
|
#include "core/file_sys/control_metadata.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
#include "core/file_sys/vfs.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/glue/glue_manager.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
#include "core/hle/service/ns/errors.h"
|
#include "core/hle/service/ns/errors.h"
|
||||||
|
@ -502,8 +503,8 @@ IContentManagementInterface::IContentManagementInterface(Core::System& system_)
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
{11, nullptr, "CalculateApplicationOccupiedSize"},
|
||||||
{43, nullptr, "CheckSdCardMountStatus"},
|
{43, nullptr, "CheckSdCardMountStatus"},
|
||||||
{47, nullptr, "GetTotalSpaceSize"},
|
{47, &IContentManagementInterface::GetTotalSpaceSize, "GetTotalSpaceSize"},
|
||||||
{48, nullptr, "GetFreeSpaceSize"},
|
{48, &IContentManagementInterface::GetFreeSpaceSize, "GetFreeSpaceSize"},
|
||||||
{600, nullptr, "CountApplicationContentMeta"},
|
{600, nullptr, "CountApplicationContentMeta"},
|
||||||
{601, nullptr, "ListApplicationContentMetaStatus"},
|
{601, nullptr, "ListApplicationContentMetaStatus"},
|
||||||
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
|
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
|
||||||
|
@ -516,6 +517,28 @@ IContentManagementInterface::IContentManagementInterface(Core::System& system_)
|
||||||
|
|
||||||
IContentManagementInterface::~IContentManagementInterface() = default;
|
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_)
|
IDocumentInterface::IDocumentInterface(Core::System& system_)
|
||||||
: ServiceFramework{system_, "IDocumentInterface"} {
|
: ServiceFramework{system_, "IDocumentInterface"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|
|
@ -48,6 +48,10 @@ class IContentManagementInterface final : public ServiceFramework<IContentManage
|
||||||
public:
|
public:
|
||||||
explicit IContentManagementInterface(Core::System& system_);
|
explicit IContentManagementInterface(Core::System& system_);
|
||||||
~IContentManagementInterface() override;
|
~IContentManagementInterface() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void GetTotalSpaceSize(HLERequestContext& ctx);
|
||||||
|
void GetFreeSpaceSize(HLERequestContext& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
|
class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
|
||||||
|
|
Reference in New Issue