|
|
@@ -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
|