summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt8
-rw-r--r--src/core/hle/service/am/am.cpp2
-rw-r--r--src/core/hle/service/caps/caps.cpp21
-rw-r--r--src/core/hle/service/caps/caps.h81
-rw-r--r--src/core/hle/service/caps/caps_a.cpp311
-rw-r--r--src/core/hle/service/caps/caps_a.h107
-rw-r--r--src/core/hle/service/caps/caps_c.cpp50
-rw-r--r--src/core/hle/service/caps/caps_c.h10
-rw-r--r--src/core/hle/service/caps/caps_manager.cpp342
-rw-r--r--src/core/hle/service/caps/caps_manager.h72
-rw-r--r--src/core/hle/service/caps/caps_result.h35
-rw-r--r--src/core/hle/service/caps/caps_sc.cpp5
-rw-r--r--src/core/hle/service/caps/caps_sc.h6
-rw-r--r--src/core/hle/service/caps/caps_ss.cpp5
-rw-r--r--src/core/hle/service/caps/caps_ss.h6
-rw-r--r--src/core/hle/service/caps/caps_su.cpp9
-rw-r--r--src/core/hle/service/caps/caps_su.h6
-rw-r--r--src/core/hle/service/caps/caps_types.h184
-rw-r--r--src/core/hle/service/caps/caps_u.cpp104
-rw-r--r--src/core/hle/service/caps/caps_u.h12
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
466 hle/service/caps/caps_a.h 466 hle/service/caps/caps_a.h
467 hle/service/caps/caps_c.cpp 467 hle/service/caps/caps_c.cpp
468 hle/service/caps/caps_c.h 468 hle/service/caps/caps_c.h
469 hle/service/caps/caps_u.cpp 469 hle/service/caps/caps_manager.cpp
470 hle/service/caps/caps_u.h 470 hle/service/caps/caps_manager.h
471 hle/service/caps/caps_result.h
471 hle/service/caps/caps_sc.cpp 472 hle/service/caps/caps_sc.cpp
472 hle/service/caps/caps_sc.h 473 hle/service/caps/caps_sc.h
473 hle/service/caps/caps_ss.cpp 474 hle/service/caps/caps_ss.cpp
474 hle/service/caps/caps_ss.h 475 hle/service/caps/caps_ss.h
475 hle/service/caps/caps_su.cpp 476 hle/service/caps/caps_su.cpp
476 hle/service/caps/caps_su.h 477 hle/service/caps/caps_su.h
478 hle/service/caps/caps_types.h
479 hle/service/caps/caps_u.cpp
480 hle/service/caps/caps_u.h
477 hle/service/erpt/erpt.cpp 481 hle/service/erpt/erpt.cpp
478 hle/service/erpt/erpt.h 482 hle/service/erpt/erpt.h
479 hle/service/es/es.cpp 483 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 @@
31#include "core/hle/service/apm/apm_controller.h" 31#include "core/hle/service/apm/apm_controller.h"
32#include "core/hle/service/apm/apm_interface.h" 32#include "core/hle/service/apm/apm_interface.h"
33#include "core/hle/service/bcat/backend/backend.h" 33#include "core/hle/service/bcat/backend/backend.h"
34#include "core/hle/service/caps/caps.h" 34#include "core/hle/service/caps/caps_types.h"
35#include "core/hle/service/filesystem/filesystem.h" 35#include "core/hle/service/filesystem/filesystem.h"
36#include "core/hle/service/ipc_helpers.h" 36#include "core/hle/service/ipc_helpers.h"
37#include "core/hle/service/ns/ns.h" 37#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 @@
4#include "core/hle/service/caps/caps.h" 4#include "core/hle/service/caps/caps.h"
5#include "core/hle/service/caps/caps_a.h" 5#include "core/hle/service/caps/caps_a.h"
6#include "core/hle/service/caps/caps_c.h" 6#include "core/hle/service/caps/caps_c.h"
7#include "core/hle/service/caps/caps_manager.h"
7#include "core/hle/service/caps/caps_sc.h" 8#include "core/hle/service/caps/caps_sc.h"
8#include "core/hle/service/caps/caps_ss.h" 9#include "core/hle/service/caps/caps_ss.h"
9#include "core/hle/service/caps/caps_su.h" 10#include "core/hle/service/caps/caps_su.h"
@@ -15,13 +16,21 @@ namespace Service::Capture {
15 16
16void LoopProcess(Core::System& system) { 17void LoopProcess(Core::System& system) {
17 auto server_manager = std::make_unique<ServerManager>(system); 18 auto server_manager = std::make_unique<ServerManager>(system);
19 auto album_manager = std::make_shared<AlbumManager>();
20
21 server_manager->RegisterNamedService(
22 "caps:a", std::make_shared<IAlbumAccessorService>(system, album_manager));
23 server_manager->RegisterNamedService(
24 "caps:c", std::make_shared<IAlbumControlService>(system, album_manager));
25 server_manager->RegisterNamedService(
26 "caps:u", std::make_shared<IAlbumApplicationService>(system, album_manager));
27
28 server_manager->RegisterNamedService("caps:ss", std::make_shared<IScreenShotService>(system));
29 server_manager->RegisterNamedService("caps:sc",
30 std::make_shared<IScreenShotControlService>(system));
31 server_manager->RegisterNamedService("caps:su",
32 std::make_shared<IScreenShotApplicationService>(system));
18 33
19 server_manager->RegisterNamedService("caps:a", std::make_shared<IAlbumAccessorService>(system));
20 server_manager->RegisterNamedService("caps:c", std::make_shared<CAPS_C>(system));
21 server_manager->RegisterNamedService("caps:u", std::make_shared<CAPS_U>(system));
22 server_manager->RegisterNamedService("caps:sc", std::make_shared<CAPS_SC>(system));
23 server_manager->RegisterNamedService("caps:ss", std::make_shared<CAPS_SS>(system));
24 server_manager->RegisterNamedService("caps:su", std::make_shared<CAPS_SU>(system));
25 ServerManager::RunServer(std::move(server_manager)); 34 ServerManager::RunServer(std::move(server_manager));
26} 35}
27 36
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 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace Core { 6namespace Core {
10class System; 7class System;
11} 8}
12 9
13namespace Service::SM {
14class ServiceManager;
15}
16
17namespace Service::Capture { 10namespace Service::Capture {
18 11
19enum class AlbumImageOrientation {
20 Orientation0 = 0,
21 Orientation1 = 1,
22 Orientation2 = 2,
23 Orientation3 = 3,
24};
25
26enum class AlbumReportOption : s32 {
27 Disable = 0,
28 Enable = 1,
29};
30
31enum class ContentType : u8 {
32 Screenshot = 0,
33 Movie = 1,
34 ExtraMovie = 3,
35};
36
37enum class AlbumStorage : u8 {
38 NAND = 0,
39 SD = 1,
40};
41
42struct AlbumFileDateTime {
43 s16 year{};
44 s8 month{};
45 s8 day{};
46 s8 hour{};
47 s8 minute{};
48 s8 second{};
49 s8 uid{};
50};
51static_assert(sizeof(AlbumFileDateTime) == 0x8, "AlbumFileDateTime has incorrect size.");
52
53struct AlbumEntry {
54 u64 size{};
55 u64 application_id{};
56 AlbumFileDateTime datetime{};
57 AlbumStorage storage{};
58 ContentType content{};
59 INSERT_PADDING_BYTES(6);
60};
61static_assert(sizeof(AlbumEntry) == 0x20, "AlbumEntry has incorrect size.");
62
63struct AlbumFileEntry {
64 u64 size{}; // Size of the entry
65 u64 hash{}; // AES256 with hardcoded key over AlbumEntry
66 AlbumFileDateTime datetime{};
67 AlbumStorage storage{};
68 ContentType content{};
69 INSERT_PADDING_BYTES(5);
70 u8 unknown{1}; // Set to 1 on official SW
71};
72static_assert(sizeof(AlbumFileEntry) == 0x20, "AlbumFileEntry has incorrect size.");
73
74struct ApplicationAlbumEntry {
75 u64 size{}; // Size of the entry
76 u64 hash{}; // AES256 with hardcoded key over AlbumEntry
77 AlbumFileDateTime datetime{};
78 AlbumStorage storage{};
79 ContentType content{};
80 INSERT_PADDING_BYTES(5);
81 u8 unknown{1}; // Set to 1 on official SW
82};
83static_assert(sizeof(ApplicationAlbumEntry) == 0x20, "ApplicationAlbumEntry has incorrect size.");
84
85struct ApplicationAlbumFileEntry {
86 ApplicationAlbumEntry entry{};
87 AlbumFileDateTime datetime{};
88 u64 unknown{};
89};
90static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30,
91 "ApplicationAlbumFileEntry has incorrect size.");
92
93void LoopProcess(Core::System& system); 12void LoopProcess(Core::System& system);
94 13
95} // namespace Service::Capture 14} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <sstream> 4#include "common/logging/log.h"
5#include <stb_image.h>
6#include <stb_image_resize.h>
7
8#include "common/fs/file.h"
9#include "common/fs/path_util.h"
10#include "core/hle/service/caps/caps_a.h" 5#include "core/hle/service/caps/caps_a.h"
6#include "core/hle/service/caps/caps_manager.h"
7#include "core/hle/service/caps/caps_result.h"
8#include "core/hle/service/caps/caps_types.h"
11#include "core/hle/service/ipc_helpers.h" 9#include "core/hle/service/ipc_helpers.h"
12 10
13namespace Service::Capture { 11namespace Service::Capture {
14 12
15class IAlbumAccessorSession final : public ServiceFramework<IAlbumAccessorSession> { 13IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
16public: 14 std::shared_ptr<AlbumManager> album_manager)
17 explicit IAlbumAccessorSession(Core::System& system_) 15 : ServiceFramework{system_, "caps:a"}, manager{album_manager} {
18 : ServiceFramework{system_, "IAlbumAccessorSession"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {2001, nullptr, "OpenAlbumMovieReadStream"},
22 {2002, nullptr, "CloseAlbumMovieReadStream"},
23 {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"},
24 {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"},
25 {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"},
26 {2006, nullptr, "GetAlbumMovieReadStreamImageDataSize"},
27 {2007, nullptr, "ReadImageDataFromAlbumMovieReadStream"},
28 {2008, nullptr, "ReadFileAttributeFromAlbumMovieReadStream"},
29 };
30 // clang-format on
31
32 RegisterHandlers(functions);
33 }
34};
35
36IAlbumAccessorService::IAlbumAccessorService(Core::System& system_)
37 : ServiceFramework{system_, "caps:a"} {
38 // clang-format off 16 // clang-format off
39 static const FunctionInfo functions[] = { 17 static const FunctionInfo functions[] = {
40 {0, nullptr, "GetAlbumFileCount"}, 18 {0, nullptr, "GetAlbumFileCount"},
@@ -91,30 +69,25 @@ void IAlbumAccessorService::DeleteAlbumFile(HLERequestContext& ctx) {
91 LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}", 69 LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}",
92 file_id.application_id, file_id.storage, file_id.type); 70 file_id.application_id, file_id.storage, file_id.type);
93 71
94 if (file_id.storage == AlbumStorage::Sd) { 72 Result result = manager->DeleteAlbumFile(file_id);
95 if (!Common::FS::RemoveFile(sd_image_paths[file_id.date.unique_id])) { 73 result = TranslateResult(result);
96 IPC::ResponseBuilder rb{ctx, 2};
97 rb.Push(ResultUnknown);
98 return;
99 }
100 }
101 74
102 IPC::ResponseBuilder rb{ctx, 2}; 75 IPC::ResponseBuilder rb{ctx, 2};
103 rb.Push(ResultSuccess); 76 rb.Push(result);
104} 77}
105 78
106void IAlbumAccessorService::IsAlbumMounted(HLERequestContext& ctx) { 79void IAlbumAccessorService::IsAlbumMounted(HLERequestContext& ctx) {
107 IPC::RequestParser rp{ctx}; 80 IPC::RequestParser rp{ctx};
108 const auto storage{rp.PopEnum<AlbumStorage>()}; 81 const auto storage{rp.PopEnum<AlbumStorage>()};
109 82
110 LOG_INFO(Service_Capture, "called, storage={}, is_mounted={}", storage, is_mounted); 83 LOG_INFO(Service_Capture, "called, storage={}", storage);
111 84
112 if (storage == AlbumStorage::Sd) { 85 Result result = manager->IsAlbumMounted(storage);
113 FindScreenshots(); 86 const bool is_mounted = result.IsSuccess();
114 } 87 result = TranslateResult(result);
115 88
116 IPC::ResponseBuilder rb{ctx, 3}; 89 IPC::ResponseBuilder rb{ctx, 3};
117 rb.Push(ResultSuccess); 90 rb.Push(result);
118 rb.Push<u8>(is_mounted); 91 rb.Push<u8>(is_mounted);
119} 92}
120 93
@@ -141,38 +114,34 @@ void IAlbumAccessorService::GetAlbumFileListEx0(HLERequestContext& ctx) {
141 IPC::RequestParser rp{ctx}; 114 IPC::RequestParser rp{ctx};
142 const auto storage{rp.PopEnum<AlbumStorage>()}; 115 const auto storage{rp.PopEnum<AlbumStorage>()};
143 const auto flags{rp.Pop<u8>()}; 116 const auto flags{rp.Pop<u8>()};
117 const auto album_entry_size{ctx.GetWriteBufferNumElements<AlbumEntry>()};
144 118
145 LOG_INFO(Service_Capture, "called, storage={}, flags={}", storage, flags); 119 LOG_INFO(Service_Capture, "called, storage={}, flags={}", storage, flags);
146 120
147 std::vector<AlbumEntry> entries{}; 121 std::vector<AlbumEntry> entries;
122 Result result = manager->GetAlbumFileList(entries, storage, flags);
123 result = TranslateResult(result);
148 124
149 if (storage == AlbumStorage::Sd) { 125 entries.resize(std::min(album_entry_size, entries.size()));
150 AlbumEntry entry;
151 for (u8 i = 0; i < static_cast<u8>(sd_image_paths.size()); i++) {
152 if (GetAlbumEntry(entry, sd_image_paths[i]).IsError()) {
153 continue;
154 }
155 entry.file_id.date.unique_id = i;
156 entries.push_back(entry);
157 }
158 }
159 126
160 if (!entries.empty()) { 127 if (!entries.empty()) {
161 ctx.WriteBuffer(entries); 128 ctx.WriteBuffer(entries);
162 } 129 }
163 130
164 IPC::ResponseBuilder rb{ctx, 3}; 131 IPC::ResponseBuilder rb{ctx, 3};
165 rb.Push(ResultSuccess); 132 rb.Push(result);
166 rb.Push(entries.size()); 133 rb.Push(entries.size());
167} 134}
168 135
169void IAlbumAccessorService::GetAutoSavingStorage(HLERequestContext& ctx) { 136void IAlbumAccessorService::GetAutoSavingStorage(HLERequestContext& ctx) {
170 bool is_autosaving{}; 137 LOG_WARNING(Service_Capture, "(STUBBED) called");
171 138
172 LOG_WARNING(Service_Capture, "(STUBBED) called, is_autosaving={}", is_autosaving); 139 bool is_autosaving{};
140 Result result = manager->GetAutoSavingStorage(is_autosaving);
141 result = TranslateResult(result);
173 142
174 IPC::ResponseBuilder rb{ctx, 3}; 143 IPC::ResponseBuilder rb{ctx, 3};
175 rb.Push(ResultSuccess); 144 rb.Push(result);
176 rb.Push<u8>(is_autosaving); 145 rb.Push<u8>(is_autosaving);
177} 146}
178 147
@@ -180,35 +149,28 @@ void IAlbumAccessorService::LoadAlbumScreenShotImageEx1(HLERequestContext& ctx)
180 IPC::RequestParser rp{ctx}; 149 IPC::RequestParser rp{ctx};
181 const auto file_id{rp.PopRaw<AlbumFileId>()}; 150 const auto file_id{rp.PopRaw<AlbumFileId>()};
182 const auto decoder_options{rp.PopRaw<ScreenShotDecodeOption>()}; 151 const auto decoder_options{rp.PopRaw<ScreenShotDecodeOption>()};
152 const auto image_buffer_size{ctx.GetWriteBufferSize(1)};
183 153
184 LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}", 154 LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}",
185 file_id.application_id, file_id.storage, file_id.type, decoder_options.flags); 155 file_id.application_id, file_id.storage, file_id.type, decoder_options.flags);
186 156
187 const LoadAlbumScreenShotImageOutput image_output{ 157 std::vector<u8> image;
188 .width = 1280, 158 LoadAlbumScreenShotImageOutput image_output;
189 .height = 720, 159 Result result =
190 .attribute = 160 manager->LoadAlbumScreenShotImage(image_output, image, file_id, decoder_options);
191 { 161 result = TranslateResult(result);
192 .unknown_0{},
193 .orientation = ScreenShotOrientation::None,
194 .unknown_1{},
195 .unknown_2{},
196 },
197 };
198
199 std::vector<u8> image(image_output.height * image_output.width * STBI_rgb_alpha);
200 162
201 if (file_id.storage == AlbumStorage::Sd) { 163 if (image.size() > image_buffer_size) {
202 LoadImage(image, sd_image_paths[file_id.date.unique_id], 164 result = ResultWorkMemoryError;
203 static_cast<int>(image_output.width), static_cast<int>(image_output.height),
204 decoder_options.flags);
205 } 165 }
206 166
207 ctx.WriteBuffer(image_output, 0); 167 if (result.IsSuccess()) {
208 ctx.WriteBuffer(image, 1); 168 ctx.WriteBuffer(image_output, 0);
169 ctx.WriteBuffer(image, 1);
170 }
209 171
210 IPC::ResponseBuilder rb{ctx, 2}; 172 IPC::ResponseBuilder rb{ctx, 2};
211 rb.Push(ResultSuccess); 173 rb.Push(result);
212} 174}
213 175
214void IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx) { 176void IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx) {
@@ -219,157 +181,78 @@ void IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(HLERequestConte
219 LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}", 181 LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}",
220 file_id.application_id, file_id.storage, file_id.type, decoder_options.flags); 182 file_id.application_id, file_id.storage, file_id.type, decoder_options.flags);
221 183
222 const LoadAlbumScreenShotImageOutput image_output{ 184 std::vector<u8> image(ctx.GetWriteBufferSize(1));
223 .width = 320, 185 LoadAlbumScreenShotImageOutput image_output;
224 .height = 180, 186 Result result =
225 .attribute = 187 manager->LoadAlbumScreenShotThumbnail(image_output, image, file_id, decoder_options);
226 { 188 result = TranslateResult(result);
227 .unknown_0{},
228 .orientation = ScreenShotOrientation::None,
229 .unknown_1{},
230 .unknown_2{},
231 },
232 };
233
234 std::vector<u8> image(image_output.height * image_output.width * STBI_rgb_alpha);
235 189
236 if (file_id.storage == AlbumStorage::Sd) { 190 if (result.IsSuccess()) {
237 LoadImage(image, sd_image_paths[file_id.date.unique_id], 191 ctx.WriteBuffer(image_output, 0);
238 static_cast<int>(image_output.width), static_cast<int>(image_output.height), 192 ctx.WriteBuffer(image, 1);
239 decoder_options.flags);
240 } 193 }
241 194
242 ctx.WriteBuffer(image_output, 0);
243 ctx.WriteBuffer(image, 1);
244
245 IPC::ResponseBuilder rb{ctx, 2}; 195 IPC::ResponseBuilder rb{ctx, 2};
246 rb.Push(ResultSuccess); 196 rb.Push(result);
247} 197}
248 198
249void IAlbumAccessorService::FindScreenshots() { 199Result IAlbumAccessorService::TranslateResult(Result in_result) {
250 is_mounted = false; 200 if (in_result.IsSuccess()) {
251 sd_image_paths.clear(); 201 return in_result;
252
253 // TODO: Swap this with a blocking operation.
254 const auto screenshots_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ScreenshotsDir);
255 Common::FS::IterateDirEntries(
256 screenshots_dir,
257 [this](const std::filesystem::path& full_path) {
258 AlbumEntry entry;
259 // TODO: Implement proper indexing to allow more images
260 if (sd_image_paths.size() > 0xFF) {
261 return true;
262 }
263 if (GetAlbumEntry(entry, full_path).IsSuccess()) {
264 sd_image_paths.push_back(full_path);
265 }
266 return true;
267 },
268 Common::FS::DirEntryFilter::File);
269
270 is_mounted = true;
271}
272
273Result IAlbumAccessorService::GetAlbumEntry(AlbumEntry& out_entry,
274 const std::filesystem::path& path) {
275 std::istringstream line_stream(path.filename().string());
276 std::string date;
277 std::string application;
278 std::string time;
279
280 // Parse filename to obtain entry properties
281 std::getline(line_stream, application, '_');
282 std::getline(line_stream, date, '_');
283 std::getline(line_stream, time, '_');
284
285 std::istringstream date_stream(date);
286 std::istringstream time_stream(time);
287 std::string year;
288 std::string month;
289 std::string day;
290 std::string hour;
291 std::string minute;
292 std::string second;
293
294 std::getline(date_stream, year, '-');
295 std::getline(date_stream, month, '-');
296 std::getline(date_stream, day, '-');
297
298 std::getline(time_stream, hour, '-');
299 std::getline(time_stream, minute, '-');
300 std::getline(time_stream, second, '-');
301
302 try {
303 out_entry = {
304 .entry_size = 1,
305 .file_id{
306 .application_id = static_cast<u64>(std::stoll(application, 0, 16)),
307 .date =
308 {
309 .year = static_cast<u16>(std::stoi(year)),
310 .month = static_cast<u8>(std::stoi(month)),
311 .day = static_cast<u8>(std::stoi(day)),
312 .hour = static_cast<u8>(std::stoi(hour)),
313 .minute = static_cast<u8>(std::stoi(minute)),
314 .second = static_cast<u8>(std::stoi(second)),
315 .unique_id = 0,
316 },
317 .storage = AlbumStorage::Sd,
318 .type = ContentType::Screenshot,
319 .unknown = 1,
320 },
321 };
322 } catch (const std::invalid_argument&) {
323 return ResultUnknown;
324 } catch (const std::out_of_range&) {
325 return ResultUnknown;
326 } catch (const std::exception&) {
327 return ResultUnknown;
328 }
329
330 return ResultSuccess;
331}
332
333Result IAlbumAccessorService::LoadImage(std::span<u8> out_image, const std::filesystem::path& path,
334 int width, int height, ScreenShotDecoderFlag flag) {
335 if (out_image.size() != static_cast<std::size_t>(width * height * STBI_rgb_alpha)) {
336 return ResultUnknown;
337 } 202 }
338 203
339 const Common::FS::IOFile db_file{path, Common::FS::FileAccessMode::Read, 204 if ((in_result.raw & 0x3801ff) == ResultUnknown1024.raw) {
340 Common::FS::FileType::BinaryFile}; 205 if (in_result.description - 0x514 < 100) {
341 206 return ResultInvalidFileData;
342 std::vector<u8> raw_file(db_file.GetSize()); 207 }
343 if (db_file.Read(raw_file) != raw_file.size()) { 208 if (in_result.description - 0x5dc < 100) {
344 return ResultUnknown; 209 return ResultInvalidFileData;
345 } 210 }
346 211
347 int filter_flag = STBIR_FILTER_DEFAULT; 212 if (in_result.description - 0x578 < 100) {
348 int original_width, original_height, color_channels; 213 if (in_result == ResultFileCountLimit) {
349 const auto dbi_image = 214 return ResultUnknown22;
350 stbi_load_from_memory(raw_file.data(), static_cast<int>(raw_file.size()), &original_width, 215 }
351 &original_height, &color_channels, STBI_rgb_alpha); 216 return ResultUnknown25;
217 }
352 218
353 if (dbi_image == nullptr) { 219 if (in_result.raw < ResultUnknown1801.raw) {
354 return ResultUnknown; 220 if (in_result == ResultUnknown1202) {
221 return ResultUnknown810;
222 }
223 if (in_result == ResultUnknown1203) {
224 return ResultUnknown810;
225 }
226 if (in_result == ResultUnknown1701) {
227 return ResultUnknown5;
228 }
229 } else if (in_result.raw < ResultUnknown1803.raw) {
230 if (in_result == ResultUnknown1801) {
231 return ResultUnknown5;
232 }
233 if (in_result == ResultUnknown1802) {
234 return ResultUnknown6;
235 }
236 } else {
237 if (in_result == ResultUnknown1803) {
238 return ResultUnknown7;
239 }
240 if (in_result == ResultUnknown1804) {
241 return ResultOutOfRange;
242 }
243 }
244 return ResultUnknown1024;
355 } 245 }
356 246
357 switch (flag) { 247 if (in_result.module == ErrorModule::FS) {
358 case ScreenShotDecoderFlag::EnableFancyUpsampling: 248 if ((in_result.description >> 0xc < 0x7d) || (in_result.description - 1000 < 2000) ||
359 filter_flag = STBIR_FILTER_TRIANGLE; 249 (((in_result.description - 3000) >> 3) < 0x271)) {
360 break; 250 // TODO: Translate FS error
361 case ScreenShotDecoderFlag::EnableBlockSmoothing: 251 return in_result;
362 filter_flag = STBIR_FILTER_BOX; 252 }
363 break;
364 default:
365 filter_flag = STBIR_FILTER_DEFAULT;
366 break;
367 } 253 }
368 254
369 stbir_resize_uint8_srgb(dbi_image, original_width, original_height, 0, out_image.data(), width, 255 return in_result;
370 height, 0, STBI_rgb_alpha, 3, filter_flag);
371
372 return ResultSuccess;
373} 256}
374 257
375} // namespace Service::Capture 258} // 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 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "common/fs/fs.h"
7#include "core/hle/service/service.h" 6#include "core/hle/service/service.h"
8 7
9namespace Core { 8namespace Core {
@@ -11,106 +10,15 @@ class System;
11} 10}
12 11
13namespace Service::Capture { 12namespace Service::Capture {
13class AlbumManager;
14 14
15class IAlbumAccessorService final : public ServiceFramework<IAlbumAccessorService> { 15class IAlbumAccessorService final : public ServiceFramework<IAlbumAccessorService> {
16public: 16public:
17 explicit IAlbumAccessorService(Core::System& system_); 17 explicit IAlbumAccessorService(Core::System& system_,
18 std::shared_ptr<AlbumManager> album_manager);
18 ~IAlbumAccessorService() override; 19 ~IAlbumAccessorService() override;
19 20
20private: 21private:
21 enum class ContentType : u8 {
22 Screenshot,
23 Movie,
24 ExtraMovie,
25 };
26
27 enum class AlbumStorage : u8 {
28 Nand,
29 Sd,
30
31 };
32
33 enum class ScreenShotDecoderFlag : u64 {
34 None = 0,
35 EnableFancyUpsampling = 1 << 0,
36 EnableBlockSmoothing = 1 << 1,
37 };
38
39 enum class ScreenShotOrientation : u32 {
40 None,
41 Rotate90,
42 Rotate180,
43 Rotate270,
44 };
45
46 struct ScreenShotAttribute {
47 u32 unknown_0;
48 ScreenShotOrientation orientation;
49 u32 unknown_1;
50 u32 unknown_2;
51 INSERT_PADDING_BYTES(0x30);
52 };
53 static_assert(sizeof(ScreenShotAttribute) == 0x40, "ScreenShotAttribute is an invalid size");
54
55 struct ScreenShotDecodeOption {
56 ScreenShotDecoderFlag flags;
57 INSERT_PADDING_BYTES(0x18);
58 };
59 static_assert(sizeof(ScreenShotDecodeOption) == 0x20,
60 "ScreenShotDecodeOption is an invalid size");
61
62 struct AlbumFileDateTime {
63 u16 year;
64 u8 month;
65 u8 day;
66 u8 hour;
67 u8 minute;
68 u8 second;
69 u8 unique_id;
70 };
71 static_assert(sizeof(AlbumFileDateTime) == 0x8, "AlbumFileDateTime is an invalid size");
72
73 struct AlbumFileId {
74 u64 application_id;
75 AlbumFileDateTime date;
76 AlbumStorage storage;
77 ContentType type;
78 INSERT_PADDING_BYTES(0x5);
79 u8 unknown;
80 };
81 static_assert(sizeof(AlbumFileId) == 0x18, "AlbumFileId is an invalid size");
82
83 struct AlbumEntry {
84 u64 entry_size;
85 AlbumFileId file_id;
86 };
87 static_assert(sizeof(AlbumEntry) == 0x20, "AlbumEntry is an invalid size");
88
89 struct ApplicationData {
90 std::array<u8, 0x400> data;
91 u32 data_size;
92 };
93 static_assert(sizeof(ApplicationData) == 0x404, "ApplicationData is an invalid size");
94
95 struct LoadAlbumScreenShotImageOutput {
96 s64 width;
97 s64 height;
98 ScreenShotAttribute attribute;
99 INSERT_PADDING_BYTES(0x400);
100 };
101 static_assert(sizeof(LoadAlbumScreenShotImageOutput) == 0x450,
102 "LoadAlbumScreenShotImageOutput is an invalid size");
103
104 struct LoadAlbumScreenShotImageOutputForApplication {
105 s64 width;
106 s64 height;
107 ScreenShotAttribute attribute;
108 ApplicationData data;
109 INSERT_PADDING_BYTES(0xAC);
110 };
111 static_assert(sizeof(LoadAlbumScreenShotImageOutputForApplication) == 0x500,
112 "LoadAlbumScreenShotImageOutput is an invalid size");
113
114 void DeleteAlbumFile(HLERequestContext& ctx); 22 void DeleteAlbumFile(HLERequestContext& ctx);
115 void IsAlbumMounted(HLERequestContext& ctx); 23 void IsAlbumMounted(HLERequestContext& ctx);
116 void Unknown18(HLERequestContext& ctx); 24 void Unknown18(HLERequestContext& ctx);
@@ -119,14 +27,9 @@ private:
119 void LoadAlbumScreenShotImageEx1(HLERequestContext& ctx); 27 void LoadAlbumScreenShotImageEx1(HLERequestContext& ctx);
120 void LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx); 28 void LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx);
121 29
122private: 30 Result TranslateResult(Result in_result);
123 void FindScreenshots();
124 Result GetAlbumEntry(AlbumEntry& out_entry, const std::filesystem::path& path);
125 Result LoadImage(std::span<u8> out_image, const std::filesystem::path& path, int width,
126 int height, ScreenShotDecoderFlag flag);
127 31
128 bool is_mounted{}; 32 std::shared_ptr<AlbumManager> manager = nullptr;
129 std::vector<std::filesystem::path> sd_image_paths{};
130}; 33};
131 34
132} // namespace Service::Capture 35} // 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 @@
3 3
4#include "common/logging/log.h" 4#include "common/logging/log.h"
5#include "core/hle/service/caps/caps_c.h" 5#include "core/hle/service/caps/caps_c.h"
6#include "core/hle/service/caps/caps_manager.h"
7#include "core/hle/service/caps/caps_result.h"
8#include "core/hle/service/caps/caps_types.h"
6#include "core/hle/service/ipc_helpers.h" 9#include "core/hle/service/ipc_helpers.h"
7 10
8namespace Service::Capture { 11namespace Service::Capture {
9 12
10class IAlbumControlSession final : public ServiceFramework<IAlbumControlSession> { 13IAlbumControlService::IAlbumControlService(Core::System& system_,
11public: 14 std::shared_ptr<AlbumManager> album_manager)
12 explicit IAlbumControlSession(Core::System& system_) 15 : ServiceFramework{system_, "caps:c"}, manager{album_manager} {
13 : ServiceFramework{system_, "IAlbumControlSession"} {
14 // clang-format off
15 static const FunctionInfo functions[] = {
16 {2001, nullptr, "OpenAlbumMovieReadStream"},
17 {2002, nullptr, "CloseAlbumMovieReadStream"},
18 {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"},
19 {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"},
20 {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"},
21 {2006, nullptr, "GetAlbumMovieReadStreamImageDataSize"},
22 {2007, nullptr, "ReadImageDataFromAlbumMovieReadStream"},
23 {2008, nullptr, "ReadFileAttributeFromAlbumMovieReadStream"},
24 {2401, nullptr, "OpenAlbumMovieWriteStream"},
25 {2402, nullptr, "FinishAlbumMovieWriteStream"},
26 {2403, nullptr, "CommitAlbumMovieWriteStream"},
27 {2404, nullptr, "DiscardAlbumMovieWriteStream"},
28 {2405, nullptr, "DiscardAlbumMovieWriteStreamNoDelete"},
29 {2406, nullptr, "CommitAlbumMovieWriteStreamEx"},
30 {2411, nullptr, "StartAlbumMovieWriteStreamDataSection"},
31 {2412, nullptr, "EndAlbumMovieWriteStreamDataSection"},
32 {2413, nullptr, "StartAlbumMovieWriteStreamMetaSection"},
33 {2414, nullptr, "EndAlbumMovieWriteStreamMetaSection"},
34 {2421, nullptr, "ReadDataFromAlbumMovieWriteStream"},
35 {2422, nullptr, "WriteDataToAlbumMovieWriteStream"},
36 {2424, nullptr, "WriteMetaToAlbumMovieWriteStream"},
37 {2431, nullptr, "GetAlbumMovieWriteStreamBrokenReason"},
38 {2433, nullptr, "GetAlbumMovieWriteStreamDataSize"},
39 {2434, nullptr, "SetAlbumMovieWriteStreamDataSize"},
40 };
41 // clang-format on
42
43 RegisterHandlers(functions);
44 }
45};
46
47CAPS_C::CAPS_C(Core::System& system_) : ServiceFramework{system_, "caps:c"} {
48 // clang-format off 16 // clang-format off
49 static const FunctionInfo functions[] = { 17 static const FunctionInfo functions[] = {
50 {1, nullptr, "CaptureRawImage"}, 18 {1, nullptr, "CaptureRawImage"},
51 {2, nullptr, "CaptureRawImageWithTimeout"}, 19 {2, nullptr, "CaptureRawImageWithTimeout"},
52 {33, &CAPS_C::SetShimLibraryVersion, "SetShimLibraryVersion"}, 20 {33, &IAlbumControlService::SetShimLibraryVersion, "SetShimLibraryVersion"},
53 {1001, nullptr, "RequestTakingScreenShot"}, 21 {1001, nullptr, "RequestTakingScreenShot"},
54 {1002, nullptr, "RequestTakingScreenShotWithTimeout"}, 22 {1002, nullptr, "RequestTakingScreenShotWithTimeout"},
55 {1011, nullptr, "NotifyTakingScreenShotRefused"}, 23 {1011, nullptr, "NotifyTakingScreenShotRefused"},
@@ -72,9 +40,9 @@ CAPS_C::CAPS_C(Core::System& system_) : ServiceFramework{system_, "caps:c"} {
72 RegisterHandlers(functions); 40 RegisterHandlers(functions);
73} 41}
74 42
75CAPS_C::~CAPS_C() = default; 43IAlbumControlService::~IAlbumControlService() = default;
76 44
77void CAPS_C::SetShimLibraryVersion(HLERequestContext& ctx) { 45void IAlbumControlService::SetShimLibraryVersion(HLERequestContext& ctx) {
78 IPC::RequestParser rp{ctx}; 46 IPC::RequestParser rp{ctx};
79 const auto library_version{rp.Pop<u64>()}; 47 const auto library_version{rp.Pop<u64>()};
80 const auto applet_resource_user_id{rp.Pop<u64>()}; 48 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;
10} 10}
11 11
12namespace Service::Capture { 12namespace Service::Capture {
13class AlbumManager;
13 14
14class CAPS_C final : public ServiceFramework<CAPS_C> { 15class IAlbumControlService final : public ServiceFramework<IAlbumControlService> {
15public: 16public:
16 explicit CAPS_C(Core::System& system_); 17 explicit IAlbumControlService(Core::System& system_,
17 ~CAPS_C() override; 18 std::shared_ptr<AlbumManager> album_manager);
19 ~IAlbumControlService() override;
18 20
19private: 21private:
20 void SetShimLibraryVersion(HLERequestContext& ctx); 22 void SetShimLibraryVersion(HLERequestContext& ctx);
23
24 std::shared_ptr<AlbumManager> manager = nullptr;
21}; 25};
22 26
23} // namespace Service::Capture 27} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <sstream>
5#include <stb_image.h>
6#include <stb_image_resize.h>
7
8#include "common/fs/file.h"
9#include "common/fs/path_util.h"
10#include "common/logging/log.h"
11#include "core/hle/service/caps/caps_manager.h"
12#include "core/hle/service/caps/caps_result.h"
13
14namespace Service::Capture {
15
16AlbumManager::AlbumManager() {}
17
18AlbumManager::~AlbumManager() = default;
19
20Result AlbumManager::DeleteAlbumFile(const AlbumFileId& file_id) {
21 if (file_id.storage > AlbumStorage::Sd) {
22 return ResultInvalidStorage;
23 }
24
25 if (!is_mounted) {
26 return ResultIsNotMounted;
27 }
28
29 std::filesystem::path path;
30 const auto result = GetFile(path, file_id);
31
32 if (result.IsError()) {
33 return result;
34 }
35
36 if (!Common::FS::RemoveFile(path)) {
37 return ResultFileNotFound;
38 }
39
40 return ResultSuccess;
41}
42
43Result AlbumManager::IsAlbumMounted(AlbumStorage storage) {
44 if (storage > AlbumStorage::Sd) {
45 return ResultInvalidStorage;
46 }
47
48 is_mounted = true;
49
50 if (storage == AlbumStorage::Sd) {
51 FindScreenshots();
52 }
53
54 return is_mounted ? ResultSuccess : ResultIsNotMounted;
55}
56
57Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage,
58 u8 flags) const {
59 if (storage > AlbumStorage::Sd) {
60 return ResultInvalidStorage;
61 }
62
63 if (!is_mounted) {
64 return ResultIsNotMounted;
65 }
66
67 for (auto& [file_id, path] : album_files) {
68 if (file_id.storage != storage) {
69 continue;
70 }
71 if (out_entries.size() >= SdAlbumFileLimit) {
72 break;
73 }
74
75 const auto entry_size = Common::FS::GetSize(path);
76 out_entries.push_back({
77 .entry_size = entry_size,
78 .file_id = file_id,
79 });
80 }
81
82 return ResultSuccess;
83}
84
85Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries,
86 ContentType contex_type, AlbumFileDateTime start_date,
87 AlbumFileDateTime end_date, u64 aruid) const {
88 if (!is_mounted) {
89 return ResultIsNotMounted;
90 }
91
92 for (auto& [file_id, path] : album_files) {
93 if (file_id.type != contex_type) {
94 continue;
95 }
96
97 if (file_id.date > start_date) {
98 continue;
99 }
100
101 if (file_id.date < end_date) {
102 continue;
103 }
104
105 if (out_entries.size() >= SdAlbumFileLimit) {
106 break;
107 }
108
109 const auto entry_size = Common::FS::GetSize(path);
110 ApplicationAlbumFileEntry entry{.entry =
111 {
112 .size = entry_size,
113 .hash{},
114 .datetime = file_id.date,
115 .storage = file_id.storage,
116 .content = contex_type,
117 .unknown = 1,
118 },
119 .datetime = file_id.date,
120 .unknown = {}};
121 out_entries.push_back(entry);
122 }
123
124 return ResultSuccess;
125}
126
127Result AlbumManager::GetAutoSavingStorage(bool& out_is_autosaving) const {
128 out_is_autosaving = false;
129 return ResultSuccess;
130}
131
132Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output,
133 std::vector<u8>& out_image,
134 const AlbumFileId& file_id,
135 const ScreenShotDecodeOption& decoder_options) const {
136 if (file_id.storage > AlbumStorage::Sd) {
137 return ResultInvalidStorage;
138 }
139
140 if (!is_mounted) {
141 return ResultIsNotMounted;
142 }
143
144 out_image_output = {
145 .width = 1280,
146 .height = 720,
147 .attribute =
148 {
149 .unknown_0{},
150 .orientation = AlbumImageOrientation::None,
151 .unknown_1{},
152 .unknown_2{},
153 },
154 };
155
156 std::filesystem::path path;
157 const auto result = GetFile(path, file_id);
158
159 if (result.IsError()) {
160 return result;
161 }
162
163 out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha);
164
165 return LoadImage(out_image, path, static_cast<int>(out_image_output.width),
166 +static_cast<int>(out_image_output.height), decoder_options.flags);
167}
168
169Result AlbumManager::LoadAlbumScreenShotThumbnail(
170 LoadAlbumScreenShotImageOutput& out_image_output, std::vector<u8>& out_image,
171 const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options) const {
172 if (file_id.storage > AlbumStorage::Sd) {
173 return ResultInvalidStorage;
174 }
175
176 if (!is_mounted) {
177 return ResultIsNotMounted;
178 }
179
180 out_image_output = {
181 .width = 320,
182 .height = 180,
183 .attribute =
184 {
185 .unknown_0{},
186 .orientation = AlbumImageOrientation::None,
187 .unknown_1{},
188 .unknown_2{},
189 },
190 };
191
192 std::filesystem::path path;
193 const auto result = GetFile(path, file_id);
194
195 if (result.IsError()) {
196 return result;
197 }
198
199 out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha);
200
201 return LoadImage(out_image, path, static_cast<int>(out_image_output.width),
202 +static_cast<int>(out_image_output.height), decoder_options.flags);
203}
204
205Result AlbumManager::GetFile(std::filesystem::path& out_path, const AlbumFileId& file_id) const {
206 const auto file = album_files.find(file_id);
207
208 if (file == album_files.end()) {
209 return ResultFileNotFound;
210 }
211
212 out_path = file->second;
213 return ResultSuccess;
214}
215
216void AlbumManager::FindScreenshots() {
217 is_mounted = false;
218 album_files.clear();
219
220 // TODO: Swap this with a blocking operation.
221 const auto screenshots_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ScreenshotsDir);
222 Common::FS::IterateDirEntries(
223 screenshots_dir,
224 [this](const std::filesystem::path& full_path) {
225 AlbumEntry entry;
226 if (GetAlbumEntry(entry, full_path).IsError()) {
227 return true;
228 }
229 while (album_files.contains(entry.file_id)) {
230 if (++entry.file_id.date.unique_id == 0) {
231 break;
232 }
233 }
234 album_files[entry.file_id] = full_path;
235 return true;
236 },
237 Common::FS::DirEntryFilter::File);
238
239 is_mounted = true;
240}
241
242Result AlbumManager::GetAlbumEntry(AlbumEntry& out_entry, const std::filesystem::path& path) const {
243 std::istringstream line_stream(path.filename().string());
244 std::string date;
245 std::string application;
246 std::string time;
247
248 // Parse filename to obtain entry properties
249 std::getline(line_stream, application, '_');
250 std::getline(line_stream, date, '_');
251 std::getline(line_stream, time, '_');
252
253 std::istringstream date_stream(date);
254 std::istringstream time_stream(time);
255 std::string year;
256 std::string month;
257 std::string day;
258 std::string hour;
259 std::string minute;
260 std::string second;
261
262 std::getline(date_stream, year, '-');
263 std::getline(date_stream, month, '-');
264 std::getline(date_stream, day, '-');
265
266 std::getline(time_stream, hour, '-');
267 std::getline(time_stream, minute, '-');
268 std::getline(time_stream, second, '-');
269
270 try {
271 out_entry = {
272 .entry_size = 1,
273 .file_id{
274 .application_id = static_cast<u64>(std::stoll(application, 0, 16)),
275 .date =
276 {
277 .year = static_cast<u16>(std::stoi(year)),
278 .month = static_cast<u8>(std::stoi(month)),
279 .day = static_cast<u8>(std::stoi(day)),
280 .hour = static_cast<u8>(std::stoi(hour)),
281 .minute = static_cast<u8>(std::stoi(minute)),
282 .second = static_cast<u8>(std::stoi(second)),
283 .unique_id = 0,
284 },
285 .storage = AlbumStorage::Sd,
286 .type = ContentType::Screenshot,
287 .unknown = 1,
288 },
289 };
290 } catch (const std::invalid_argument&) {
291 return ResultUnknown;
292 } catch (const std::out_of_range&) {
293 return ResultUnknown;
294 } catch (const std::exception&) {
295 return ResultUnknown;
296 }
297
298 return ResultSuccess;
299}
300
301Result AlbumManager::LoadImage(std::span<u8> out_image, const std::filesystem::path& path,
302 int width, int height, ScreenShotDecoderFlag flag) const {
303 if (out_image.size() != static_cast<std::size_t>(width * height * STBI_rgb_alpha)) {
304 return ResultUnknown;
305 }
306
307 const Common::FS::IOFile db_file{path, Common::FS::FileAccessMode::Read,
308 Common::FS::FileType::BinaryFile};
309
310 std::vector<u8> raw_file(db_file.GetSize());
311 if (db_file.Read(raw_file) != raw_file.size()) {
312 return ResultUnknown;
313 }
314
315 int filter_flag = STBIR_FILTER_DEFAULT;
316 int original_width, original_height, color_channels;
317 const auto dbi_image =
318 stbi_load_from_memory(raw_file.data(), static_cast<int>(raw_file.size()), &original_width,
319 &original_height, &color_channels, STBI_rgb_alpha);
320
321 if (dbi_image == nullptr) {
322 return ResultUnknown;
323 }
324
325 switch (flag) {
326 case ScreenShotDecoderFlag::EnableFancyUpsampling:
327 filter_flag = STBIR_FILTER_TRIANGLE;
328 break;
329 case ScreenShotDecoderFlag::EnableBlockSmoothing:
330 filter_flag = STBIR_FILTER_BOX;
331 break;
332 default:
333 filter_flag = STBIR_FILTER_DEFAULT;
334 break;
335 }
336
337 stbir_resize_uint8_srgb(dbi_image, original_width, original_height, 0, out_image.data(), width,
338 height, 0, STBI_rgb_alpha, 3, filter_flag);
339
340 return ResultSuccess;
341}
342} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <unordered_map>
7
8#include "common/fs/fs.h"
9#include "core/hle/result.h"
10#include "core/hle/service/caps/caps_types.h"
11
12namespace Core {
13class System;
14}
15
16namespace std {
17// Hash used to create lists from AlbumFileId data
18template <>
19struct hash<Service::Capture::AlbumFileId> {
20 size_t operator()(const Service::Capture::AlbumFileId& pad_id) const noexcept {
21 u64 hash_value = (static_cast<u64>(pad_id.date.year) << 8);
22 hash_value ^= (static_cast<u64>(pad_id.date.month) << 7);
23 hash_value ^= (static_cast<u64>(pad_id.date.day) << 6);
24 hash_value ^= (static_cast<u64>(pad_id.date.hour) << 5);
25 hash_value ^= (static_cast<u64>(pad_id.date.minute) << 4);
26 hash_value ^= (static_cast<u64>(pad_id.date.second) << 3);
27 hash_value ^= (static_cast<u64>(pad_id.date.unique_id) << 2);
28 hash_value ^= (static_cast<u64>(pad_id.storage) << 1);
29 hash_value ^= static_cast<u64>(pad_id.type);
30 return static_cast<size_t>(hash_value);
31 }
32};
33
34} // namespace std
35
36namespace Service::Capture {
37
38class AlbumManager {
39public:
40 explicit AlbumManager();
41 ~AlbumManager();
42
43 Result DeleteAlbumFile(const AlbumFileId& file_id);
44 Result IsAlbumMounted(AlbumStorage storage);
45 Result GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage,
46 u8 flags) const;
47 Result GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries,
48 ContentType contex_type, AlbumFileDateTime start_date,
49 AlbumFileDateTime end_date, u64 aruid) const;
50 Result GetAutoSavingStorage(bool& out_is_autosaving) const;
51 Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output,
52 std::vector<u8>& out_image, const AlbumFileId& file_id,
53 const ScreenShotDecodeOption& decoder_options) const;
54 Result LoadAlbumScreenShotThumbnail(LoadAlbumScreenShotImageOutput& out_image_output,
55 std::vector<u8>& out_image, const AlbumFileId& file_id,
56 const ScreenShotDecodeOption& decoder_options) const;
57
58private:
59 static constexpr std::size_t NandAlbumFileLimit = 1000;
60 static constexpr std::size_t SdAlbumFileLimit = 10000;
61
62 void FindScreenshots();
63 Result GetFile(std::filesystem::path& out_path, const AlbumFileId& file_id) const;
64 Result GetAlbumEntry(AlbumEntry& out_entry, const std::filesystem::path& path) const;
65 Result LoadImage(std::span<u8> out_image, const std::filesystem::path& path, int width,
66 int height, ScreenShotDecoderFlag flag) const;
67
68 bool is_mounted{};
69 std::unordered_map<AlbumFileId, std::filesystem::path> album_files;
70};
71
72} // 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 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7
8namespace Service::Capture {
9
10constexpr Result ResultWorkMemoryError(ErrorModule::Capture, 3);
11constexpr Result ResultUnknown5(ErrorModule::Capture, 5);
12constexpr Result ResultUnknown6(ErrorModule::Capture, 6);
13constexpr Result ResultUnknown7(ErrorModule::Capture, 7);
14constexpr Result ResultOutOfRange(ErrorModule::Capture, 8);
15constexpr Result ResulInvalidTimestamp(ErrorModule::Capture, 12);
16constexpr Result ResultInvalidStorage(ErrorModule::Capture, 13);
17constexpr Result ResultInvalidFileContents(ErrorModule::Capture, 14);
18constexpr Result ResultIsNotMounted(ErrorModule::Capture, 21);
19constexpr Result ResultUnknown22(ErrorModule::Capture, 22);
20constexpr Result ResultFileNotFound(ErrorModule::Capture, 23);
21constexpr Result ResultInvalidFileData(ErrorModule::Capture, 24);
22constexpr Result ResultUnknown25(ErrorModule::Capture, 25);
23constexpr Result ResultReadBufferShortage(ErrorModule::Capture, 30);
24constexpr Result ResultUnknown810(ErrorModule::Capture, 810);
25constexpr Result ResultUnknown1024(ErrorModule::Capture, 1024);
26constexpr Result ResultUnknown1202(ErrorModule::Capture, 1202);
27constexpr Result ResultUnknown1203(ErrorModule::Capture, 1203);
28constexpr Result ResultFileCountLimit(ErrorModule::Capture, 1401);
29constexpr Result ResultUnknown1701(ErrorModule::Capture, 1701);
30constexpr Result ResultUnknown1801(ErrorModule::Capture, 1801);
31constexpr Result ResultUnknown1802(ErrorModule::Capture, 1802);
32constexpr Result ResultUnknown1803(ErrorModule::Capture, 1803);
33constexpr Result ResultUnknown1804(ErrorModule::Capture, 1804);
34
35} // 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 @@
5 5
6namespace Service::Capture { 6namespace Service::Capture {
7 7
8CAPS_SC::CAPS_SC(Core::System& system_) : ServiceFramework{system_, "caps:sc"} { 8IScreenShotControlService::IScreenShotControlService(Core::System& system_)
9 : ServiceFramework{system_, "caps:sc"} {
9 // clang-format off 10 // clang-format off
10 static const FunctionInfo functions[] = { 11 static const FunctionInfo functions[] = {
11 {1, nullptr, "CaptureRawImage"}, 12 {1, nullptr, "CaptureRawImage"},
@@ -34,6 +35,6 @@ CAPS_SC::CAPS_SC(Core::System& system_) : ServiceFramework{system_, "caps:sc"} {
34 RegisterHandlers(functions); 35 RegisterHandlers(functions);
35} 36}
36 37
37CAPS_SC::~CAPS_SC() = default; 38IScreenShotControlService::~IScreenShotControlService() = default;
38 39
39} // namespace Service::Capture 40} // 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;
11 11
12namespace Service::Capture { 12namespace Service::Capture {
13 13
14class CAPS_SC final : public ServiceFramework<CAPS_SC> { 14class IScreenShotControlService final : public ServiceFramework<IScreenShotControlService> {
15public: 15public:
16 explicit CAPS_SC(Core::System& system_); 16 explicit IScreenShotControlService(Core::System& system_);
17 ~CAPS_SC() override; 17 ~IScreenShotControlService() override;
18}; 18};
19 19
20} // namespace Service::Capture 20} // 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 @@
5 5
6namespace Service::Capture { 6namespace Service::Capture {
7 7
8CAPS_SS::CAPS_SS(Core::System& system_) : ServiceFramework{system_, "caps:ss"} { 8IScreenShotService::IScreenShotService(Core::System& system_)
9 : ServiceFramework{system_, "caps:ss"} {
9 // clang-format off 10 // clang-format off
10 static const FunctionInfo functions[] = { 11 static const FunctionInfo functions[] = {
11 {201, nullptr, "SaveScreenShot"}, 12 {201, nullptr, "SaveScreenShot"},
@@ -21,6 +22,6 @@ CAPS_SS::CAPS_SS(Core::System& system_) : ServiceFramework{system_, "caps:ss"} {
21 RegisterHandlers(functions); 22 RegisterHandlers(functions);
22} 23}
23 24
24CAPS_SS::~CAPS_SS() = default; 25IScreenShotService::~IScreenShotService() = default;
25 26
26} // namespace Service::Capture 27} // 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;
11 11
12namespace Service::Capture { 12namespace Service::Capture {
13 13
14class CAPS_SS final : public ServiceFramework<CAPS_SS> { 14class IScreenShotService final : public ServiceFramework<IScreenShotService> {
15public: 15public:
16 explicit CAPS_SS(Core::System& system_); 16 explicit IScreenShotService(Core::System& system_);
17 ~CAPS_SS() override; 17 ~IScreenShotService() override;
18}; 18};
19 19
20} // namespace Service::Capture 20} // 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 @@
7 7
8namespace Service::Capture { 8namespace Service::Capture {
9 9
10CAPS_SU::CAPS_SU(Core::System& system_) : ServiceFramework{system_, "caps:su"} { 10IScreenShotApplicationService::IScreenShotApplicationService(Core::System& system_)
11 : ServiceFramework{system_, "caps:su"} {
11 // clang-format off 12 // clang-format off
12 static const FunctionInfo functions[] = { 13 static const FunctionInfo functions[] = {
13 {32, &CAPS_SU::SetShimLibraryVersion, "SetShimLibraryVersion"}, 14 {32, &IScreenShotApplicationService::SetShimLibraryVersion, "SetShimLibraryVersion"},
14 {201, nullptr, "SaveScreenShot"}, 15 {201, nullptr, "SaveScreenShot"},
15 {203, nullptr, "SaveScreenShotEx0"}, 16 {203, nullptr, "SaveScreenShotEx0"},
16 {205, nullptr, "SaveScreenShotEx1"}, 17 {205, nullptr, "SaveScreenShotEx1"},
@@ -21,9 +22,9 @@ CAPS_SU::CAPS_SU(Core::System& system_) : ServiceFramework{system_, "caps:su"} {
21 RegisterHandlers(functions); 22 RegisterHandlers(functions);
22} 23}
23 24
24CAPS_SU::~CAPS_SU() = default; 25IScreenShotApplicationService::~IScreenShotApplicationService() = default;
25 26
26void CAPS_SU::SetShimLibraryVersion(HLERequestContext& ctx) { 27void IScreenShotApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) {
27 IPC::RequestParser rp{ctx}; 28 IPC::RequestParser rp{ctx};
28 const auto library_version{rp.Pop<u64>()}; 29 const auto library_version{rp.Pop<u64>()};
29 const auto applet_resource_user_id{rp.Pop<u64>()}; 30 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;
11 11
12namespace Service::Capture { 12namespace Service::Capture {
13 13
14class CAPS_SU final : public ServiceFramework<CAPS_SU> { 14class IScreenShotApplicationService final : public ServiceFramework<IScreenShotApplicationService> {
15public: 15public:
16 explicit CAPS_SU(Core::System& system_); 16 explicit IScreenShotApplicationService(Core::System& system_);
17 ~CAPS_SU() override; 17 ~IScreenShotApplicationService() override;
18 18
19private: 19private:
20 void SetShimLibraryVersion(HLERequestContext& ctx); 20 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 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace Service::Capture {
10
11// This is nn::album::ImageOrientation
12enum class AlbumImageOrientation {
13 None,
14 Rotate90,
15 Rotate180,
16 Rotate270,
17};
18
19// This is nn::album::AlbumReportOption
20enum class AlbumReportOption : s32 {
21 Disable,
22 Enable,
23};
24
25enum class ContentType : u8 {
26 Screenshot = 0,
27 Movie = 1,
28 ExtraMovie = 3,
29};
30
31enum class AlbumStorage : u8 {
32 Nand,
33 Sd,
34};
35
36enum class ScreenShotDecoderFlag : u64 {
37 None = 0,
38 EnableFancyUpsampling = 1 << 0,
39 EnableBlockSmoothing = 1 << 1,
40};
41
42// This is nn::capsrv::AlbumFileDateTime
43struct AlbumFileDateTime {
44 u16 year{};
45 u8 month{};
46 u8 day{};
47 u8 hour{};
48 u8 minute{};
49 u8 second{};
50 u8 unique_id{};
51
52 friend constexpr bool operator==(const AlbumFileDateTime&, const AlbumFileDateTime&) = default;
53 friend constexpr bool operator>(const AlbumFileDateTime& a, const AlbumFileDateTime& b) {
54 if (a.year > b.year) {
55 return true;
56 }
57 if (a.month > b.month) {
58 return true;
59 }
60 if (a.day > b.day) {
61 return true;
62 }
63 if (a.hour > b.hour) {
64 return true;
65 }
66 if (a.minute > b.minute) {
67 return true;
68 }
69 return a.second > b.second;
70 };
71 friend constexpr bool operator<(const AlbumFileDateTime& a, const AlbumFileDateTime& b) {
72 if (a.year < b.year) {
73 return true;
74 }
75 if (a.month < b.month) {
76 return true;
77 }
78 if (a.day < b.day) {
79 return true;
80 }
81 if (a.hour < b.hour) {
82 return true;
83 }
84 if (a.minute < b.minute) {
85 return true;
86 }
87 return a.second < b.second;
88 };
89};
90static_assert(sizeof(AlbumFileDateTime) == 0x8, "AlbumFileDateTime has incorrect size.");
91
92// This is nn::album::AlbumEntry
93struct AlbumFileEntry {
94 u64 size{}; // Size of the entry
95 u64 hash{}; // AES256 with hardcoded key over AlbumEntry
96 AlbumFileDateTime datetime{};
97 AlbumStorage storage{};
98 ContentType content{};
99 INSERT_PADDING_BYTES(5);
100 u8 unknown{}; // Set to 1 on official SW
101};
102static_assert(sizeof(AlbumFileEntry) == 0x20, "AlbumFileEntry has incorrect size.");
103
104struct AlbumFileId {
105 u64 application_id{};
106 AlbumFileDateTime date{};
107 AlbumStorage storage{};
108 ContentType type{};
109 INSERT_PADDING_BYTES(0x5);
110 u8 unknown{};
111
112 friend constexpr bool operator==(const AlbumFileId&, const AlbumFileId&) = default;
113};
114static_assert(sizeof(AlbumFileId) == 0x18, "AlbumFileId is an invalid size");
115
116// This is nn::capsrv::AlbumEntry
117struct AlbumEntry {
118 u64 entry_size{};
119 AlbumFileId file_id{};
120};
121static_assert(sizeof(AlbumEntry) == 0x20, "AlbumEntry has incorrect size.");
122
123// This is nn::capsrv::ApplicationAlbumEntry
124struct ApplicationAlbumEntry {
125 u64 size{}; // Size of the entry
126 u64 hash{}; // AES256 with hardcoded key over AlbumEntry
127 AlbumFileDateTime datetime{};
128 AlbumStorage storage{};
129 ContentType content{};
130 INSERT_PADDING_BYTES(5);
131 u8 unknown{1}; // Set to 1 on official SW
132};
133static_assert(sizeof(ApplicationAlbumEntry) == 0x20, "ApplicationAlbumEntry has incorrect size.");
134
135// This is nn::capsrv::ApplicationAlbumFileEntry
136struct ApplicationAlbumFileEntry {
137 ApplicationAlbumEntry entry{};
138 AlbumFileDateTime datetime{};
139 u64 unknown{};
140};
141static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30,
142 "ApplicationAlbumFileEntry has incorrect size.");
143
144struct ApplicationData {
145 std::array<u8, 0x400> data{};
146 u32 data_size{};
147};
148static_assert(sizeof(ApplicationData) == 0x404, "ApplicationData is an invalid size");
149
150struct ScreenShotAttribute {
151 u32 unknown_0{};
152 AlbumImageOrientation orientation{};
153 u32 unknown_1{};
154 u32 unknown_2{};
155 INSERT_PADDING_BYTES(0x30);
156};
157static_assert(sizeof(ScreenShotAttribute) == 0x40, "ScreenShotAttribute is an invalid size");
158
159struct ScreenShotDecodeOption {
160 ScreenShotDecoderFlag flags{};
161 INSERT_PADDING_BYTES(0x18);
162};
163static_assert(sizeof(ScreenShotDecodeOption) == 0x20, "ScreenShotDecodeOption is an invalid size");
164
165struct LoadAlbumScreenShotImageOutput {
166 s64 width{};
167 s64 height{};
168 ScreenShotAttribute attribute{};
169 INSERT_PADDING_BYTES(0x400);
170};
171static_assert(sizeof(LoadAlbumScreenShotImageOutput) == 0x450,
172 "LoadAlbumScreenShotImageOutput is an invalid size");
173
174struct LoadAlbumScreenShotImageOutputForApplication {
175 s64 width{};
176 s64 height{};
177 ScreenShotAttribute attribute{};
178 ApplicationData data{};
179 INSERT_PADDING_BYTES(0xAC);
180};
181static_assert(sizeof(LoadAlbumScreenShotImageOutputForApplication) == 0x500,
182 "LoadAlbumScreenShotImageOutput is an invalid size");
183
184} // 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 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/logging/log.h" 4#include "common/logging/log.h"
5#include "core/hle/service/caps/caps.h" 5#include "core/hle/service/caps/caps_manager.h"
6#include "core/hle/service/caps/caps_types.h"
6#include "core/hle/service/caps/caps_u.h" 7#include "core/hle/service/caps/caps_u.h"
7#include "core/hle/service/ipc_helpers.h" 8#include "core/hle/service/ipc_helpers.h"
8 9
9namespace Service::Capture { 10namespace Service::Capture {
10 11
11class IAlbumAccessorApplicationSession final 12IAlbumApplicationService::IAlbumApplicationService(Core::System& system_,
12 : public ServiceFramework<IAlbumAccessorApplicationSession> { 13 std::shared_ptr<AlbumManager> album_manager)
13public: 14 : ServiceFramework{system_, "caps:u"}, manager{album_manager} {
14 explicit IAlbumAccessorApplicationSession(Core::System& system_)
15 : ServiceFramework{system_, "IAlbumAccessorApplicationSession"} {
16 // clang-format off
17 static const FunctionInfo functions[] = {
18 {2001, nullptr, "OpenAlbumMovieReadStream"},
19 {2002, nullptr, "CloseAlbumMovieReadStream"},
20 {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"},
21 {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"},
22 {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"},
23 };
24 // clang-format on
25
26 RegisterHandlers(functions);
27 }
28};
29
30CAPS_U::CAPS_U(Core::System& system_) : ServiceFramework{system_, "caps:u"} {
31 // clang-format off 15 // clang-format off
32 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
33 {32, &CAPS_U::SetShimLibraryVersion, "SetShimLibraryVersion"}, 17 {32, &IAlbumApplicationService::SetShimLibraryVersion, "SetShimLibraryVersion"},
34 {102, &CAPS_U::GetAlbumContentsFileListForApplication, "GetAlbumContentsFileListForApplication"}, 18 {102, &IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated, "GetAlbumFileList0AafeAruidDeprecated"},
35 {103, nullptr, "DeleteAlbumContentsFileForApplication"}, 19 {103, nullptr, "DeleteAlbumFileByAruid"},
36 {104, nullptr, "GetAlbumContentsFileSizeForApplication"}, 20 {104, nullptr, "GetAlbumFileSizeByAruid"},
37 {105, nullptr, "DeleteAlbumFileByAruidForDebug"}, 21 {105, nullptr, "DeleteAlbumFileByAruidForDebug"},
38 {110, nullptr, "LoadAlbumContentsFileScreenShotImageForApplication"}, 22 {110, nullptr, "LoadAlbumScreenShotImageByAruid"},
39 {120, nullptr, "LoadAlbumContentsFileThumbnailImageForApplication"}, 23 {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"},
40 {130, nullptr, "PrecheckToCreateContentsForApplication"}, 24 {130, nullptr, "PrecheckToCreateContentsByAruid"},
41 {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"}, 25 {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"},
42 {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, 26 {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"},
43 {142, &CAPS_U::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"}, 27 {142, &IAlbumApplicationService::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"},
44 {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, 28 {143, nullptr, "GetAlbumFileList4AaeUidAruid"},
45 {144, nullptr, "GetAllAlbumFileList3AaeAruid"}, 29 {144, nullptr, "GetAllAlbumFileList3AaeAruid"},
46 {60002, nullptr, "OpenAccessorSessionForApplication"}, 30 {60002, nullptr, "OpenAccessorSessionForApplication"},
@@ -50,9 +34,9 @@ CAPS_U::CAPS_U(Core::System& system_) : ServiceFramework{system_, "caps:u"} {
50 RegisterHandlers(functions); 34 RegisterHandlers(functions);
51} 35}
52 36
53CAPS_U::~CAPS_U() = default; 37IAlbumApplicationService::~IAlbumApplicationService() = default;
54 38
55void CAPS_U::SetShimLibraryVersion(HLERequestContext& ctx) { 39void IAlbumApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) {
56 IPC::RequestParser rp{ctx}; 40 IPC::RequestParser rp{ctx};
57 const auto library_version{rp.Pop<u64>()}; 41 const auto library_version{rp.Pop<u64>()};
58 const auto applet_resource_user_id{rp.Pop<u64>()}; 42 const auto applet_resource_user_id{rp.Pop<u64>()};
@@ -64,10 +48,7 @@ void CAPS_U::SetShimLibraryVersion(HLERequestContext& ctx) {
64 rb.Push(ResultSuccess); 48 rb.Push(ResultSuccess);
65} 49}
66 50
67void CAPS_U::GetAlbumContentsFileListForApplication(HLERequestContext& ctx) { 51void IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx) {
68 // Takes a type-0x6 output buffer containing an array of ApplicationAlbumFileEntry, a PID, an
69 // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total
70 // output entries (which is copied to a s32 by official SW).
71 IPC::RequestParser rp{ctx}; 52 IPC::RequestParser rp{ctx};
72 const auto pid{rp.Pop<s32>()}; 53 const auto pid{rp.Pop<s32>()};
73 const auto content_type{rp.PopEnum<ContentType>()}; 54 const auto content_type{rp.PopEnum<ContentType>()};
@@ -75,26 +56,49 @@ void CAPS_U::GetAlbumContentsFileListForApplication(HLERequestContext& ctx) {
75 const auto end_posix_time{rp.Pop<s64>()}; 56 const auto end_posix_time{rp.Pop<s64>()};
76 const auto applet_resource_user_id{rp.Pop<u64>()}; 57 const auto applet_resource_user_id{rp.Pop<u64>()};
77 58
78 // TODO: Update this when we implement the album. 59 LOG_WARNING(Service_Capture,
79 // Currently we do not have a method of accessing album entries, set this to 0 for now. 60 "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, "
80 constexpr u32 total_entries_1{}; 61 "end_posix_time={}, applet_resource_user_id={}",
81 constexpr u32 total_entries_2{}; 62 pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id);
63
64 // TODO: Translate posix to DateTime
65
66 std::vector<ApplicationAlbumFileEntry> entries;
67 const Result result =
68 manager->GetAlbumFileList(entries, content_type, {}, {}, applet_resource_user_id);
82 69
83 LOG_WARNING( 70 if (!entries.empty()) {
84 Service_Capture, 71 ctx.WriteBuffer(entries);
85 "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, " 72 }
86 "end_posix_time={}, applet_resource_user_id={}, total_entries_1={}, total_entries_2={}",
87 pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id,
88 total_entries_1, total_entries_2);
89 73
90 IPC::ResponseBuilder rb{ctx, 4}; 74 IPC::ResponseBuilder rb{ctx, 4};
91 rb.Push(ResultSuccess); 75 rb.Push(result);
92 rb.Push(total_entries_1); 76 rb.Push<u64>(entries.size());
93 rb.Push(total_entries_2);
94} 77}
95 78
96void CAPS_U::GetAlbumFileList3AaeAruid(HLERequestContext& ctx) { 79void IAlbumApplicationService::GetAlbumFileList3AaeAruid(HLERequestContext& ctx) {
97 GetAlbumContentsFileListForApplication(ctx); 80 IPC::RequestParser rp{ctx};
81 const auto pid{rp.Pop<s32>()};
82 const auto content_type{rp.PopEnum<ContentType>()};
83 const auto start_date_time{rp.PopRaw<AlbumFileDateTime>()};
84 const auto end_date_time{rp.PopRaw<AlbumFileDateTime>()};
85 const auto applet_resource_user_id{rp.Pop<u64>()};
86
87 LOG_WARNING(Service_Capture,
88 "(STUBBED) called. pid={}, content_type={}, applet_resource_user_id={}", pid,
89 content_type, applet_resource_user_id);
90
91 std::vector<ApplicationAlbumFileEntry> entries;
92 const Result result = manager->GetAlbumFileList(entries, content_type, start_date_time,
93 end_date_time, applet_resource_user_id);
94
95 if (!entries.empty()) {
96 ctx.WriteBuffer(entries);
97 }
98
99 IPC::ResponseBuilder rb{ctx, 4};
100 rb.Push(result);
101 rb.Push<u64>(entries.size());
98} 102}
99 103
100} // namespace Service::Capture 104} // 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;
10} 10}
11 11
12namespace Service::Capture { 12namespace Service::Capture {
13class AlbumManager;
13 14
14class CAPS_U final : public ServiceFramework<CAPS_U> { 15class IAlbumApplicationService final : public ServiceFramework<IAlbumApplicationService> {
15public: 16public:
16 explicit CAPS_U(Core::System& system_); 17 explicit IAlbumApplicationService(Core::System& system_,
17 ~CAPS_U() override; 18 std::shared_ptr<AlbumManager> album_manager);
19 ~IAlbumApplicationService() override;
18 20
19private: 21private:
20 void SetShimLibraryVersion(HLERequestContext& ctx); 22 void SetShimLibraryVersion(HLERequestContext& ctx);
21 void GetAlbumContentsFileListForApplication(HLERequestContext& ctx); 23 void GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx);
22 void GetAlbumFileList3AaeAruid(HLERequestContext& ctx); 24 void GetAlbumFileList3AaeAruid(HLERequestContext& ctx);
25
26 std::shared_ptr<AlbumManager> manager = nullptr;
23}; 27};
24 28
25} // namespace Service::Capture 29} // namespace Service::Capture