mirror of https://git.suyu.dev/suyu/suyu
service: caps: Implement album manager and reorganize service
parent
dac53b4ba0
commit
8347e5cdb9
@ -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
|
@ -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
|
@ -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
|
@ -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
|
Loading…
Reference in New Issue