caps_manager.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <sstream>
  4. #include <stb_image.h>
  5. #include <stb_image_resize.h>
  6. #include "common/fs/file.h"
  7. #include "common/fs/path_util.h"
  8. #include "common/logging/log.h"
  9. #include "core/hle/service/caps/caps_manager.h"
  10. #include "core/hle/service/caps/caps_result.h"
  11. namespace Service::Capture {
  12. AlbumManager::AlbumManager() {}
  13. AlbumManager::~AlbumManager() = default;
  14. Result AlbumManager::DeleteAlbumFile(const AlbumFileId& file_id) {
  15. if (file_id.storage > AlbumStorage::Sd) {
  16. return ResultInvalidStorage;
  17. }
  18. if (!is_mounted) {
  19. return ResultIsNotMounted;
  20. }
  21. std::filesystem::path path;
  22. const auto result = GetFile(path, file_id);
  23. if (result.IsError()) {
  24. return result;
  25. }
  26. if (!Common::FS::RemoveFile(path)) {
  27. return ResultFileNotFound;
  28. }
  29. return ResultSuccess;
  30. }
  31. Result AlbumManager::IsAlbumMounted(AlbumStorage storage) {
  32. if (storage > AlbumStorage::Sd) {
  33. return ResultInvalidStorage;
  34. }
  35. is_mounted = true;
  36. if (storage == AlbumStorage::Sd) {
  37. FindScreenshots();
  38. }
  39. return is_mounted ? ResultSuccess : ResultIsNotMounted;
  40. }
  41. Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage,
  42. u8 flags) const {
  43. if (storage > AlbumStorage::Sd) {
  44. return ResultInvalidStorage;
  45. }
  46. if (!is_mounted) {
  47. return ResultIsNotMounted;
  48. }
  49. for (auto& [file_id, path] : album_files) {
  50. if (file_id.storage != storage) {
  51. continue;
  52. }
  53. if (out_entries.size() >= SdAlbumFileLimit) {
  54. break;
  55. }
  56. const auto entry_size = Common::FS::GetSize(path);
  57. out_entries.push_back({
  58. .entry_size = entry_size,
  59. .file_id = file_id,
  60. });
  61. }
  62. return ResultSuccess;
  63. }
  64. Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries,
  65. ContentType contex_type, AlbumFileDateTime start_date,
  66. AlbumFileDateTime end_date, u64 aruid) const {
  67. if (!is_mounted) {
  68. return ResultIsNotMounted;
  69. }
  70. for (auto& [file_id, path] : album_files) {
  71. if (file_id.type != contex_type) {
  72. continue;
  73. }
  74. if (file_id.date > start_date) {
  75. continue;
  76. }
  77. if (file_id.date < end_date) {
  78. continue;
  79. }
  80. if (out_entries.size() >= SdAlbumFileLimit) {
  81. break;
  82. }
  83. const auto entry_size = Common::FS::GetSize(path);
  84. ApplicationAlbumFileEntry entry{.entry =
  85. {
  86. .size = entry_size,
  87. .hash{},
  88. .datetime = file_id.date,
  89. .storage = file_id.storage,
  90. .content = contex_type,
  91. .unknown = 1,
  92. },
  93. .datetime = file_id.date,
  94. .unknown = {}};
  95. out_entries.push_back(entry);
  96. }
  97. return ResultSuccess;
  98. }
  99. Result AlbumManager::GetAutoSavingStorage(bool& out_is_autosaving) const {
  100. out_is_autosaving = false;
  101. return ResultSuccess;
  102. }
  103. Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output,
  104. std::vector<u8>& out_image,
  105. const AlbumFileId& file_id,
  106. const ScreenShotDecodeOption& decoder_options) const {
  107. if (file_id.storage > AlbumStorage::Sd) {
  108. return ResultInvalidStorage;
  109. }
  110. if (!is_mounted) {
  111. return ResultIsNotMounted;
  112. }
  113. out_image_output = {
  114. .width = 1280,
  115. .height = 720,
  116. .attribute =
  117. {
  118. .unknown_0{},
  119. .orientation = AlbumImageOrientation::None,
  120. .unknown_1{},
  121. .unknown_2{},
  122. },
  123. };
  124. std::filesystem::path path;
  125. const auto result = GetFile(path, file_id);
  126. if (result.IsError()) {
  127. return result;
  128. }
  129. out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha);
  130. return LoadImage(out_image, path, static_cast<int>(out_image_output.width),
  131. +static_cast<int>(out_image_output.height), decoder_options.flags);
  132. }
  133. Result AlbumManager::LoadAlbumScreenShotThumbnail(
  134. LoadAlbumScreenShotImageOutput& out_image_output, std::vector<u8>& out_image,
  135. const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options) const {
  136. if (file_id.storage > AlbumStorage::Sd) {
  137. return ResultInvalidStorage;
  138. }
  139. if (!is_mounted) {
  140. return ResultIsNotMounted;
  141. }
  142. out_image_output = {
  143. .width = 320,
  144. .height = 180,
  145. .attribute =
  146. {
  147. .unknown_0{},
  148. .orientation = AlbumImageOrientation::None,
  149. .unknown_1{},
  150. .unknown_2{},
  151. },
  152. };
  153. std::filesystem::path path;
  154. const auto result = GetFile(path, file_id);
  155. if (result.IsError()) {
  156. return result;
  157. }
  158. out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha);
  159. return LoadImage(out_image, path, static_cast<int>(out_image_output.width),
  160. +static_cast<int>(out_image_output.height), decoder_options.flags);
  161. }
  162. Result AlbumManager::GetFile(std::filesystem::path& out_path, const AlbumFileId& file_id) const {
  163. const auto file = album_files.find(file_id);
  164. if (file == album_files.end()) {
  165. return ResultFileNotFound;
  166. }
  167. out_path = file->second;
  168. return ResultSuccess;
  169. }
  170. void AlbumManager::FindScreenshots() {
  171. is_mounted = false;
  172. album_files.clear();
  173. // TODO: Swap this with a blocking operation.
  174. const auto screenshots_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ScreenshotsDir);
  175. Common::FS::IterateDirEntries(
  176. screenshots_dir,
  177. [this](const std::filesystem::path& full_path) {
  178. AlbumEntry entry;
  179. if (GetAlbumEntry(entry, full_path).IsError()) {
  180. return true;
  181. }
  182. while (album_files.contains(entry.file_id)) {
  183. if (++entry.file_id.date.unique_id == 0) {
  184. break;
  185. }
  186. }
  187. album_files[entry.file_id] = full_path;
  188. return true;
  189. },
  190. Common::FS::DirEntryFilter::File);
  191. is_mounted = true;
  192. }
  193. Result AlbumManager::GetAlbumEntry(AlbumEntry& out_entry, const std::filesystem::path& path) const {
  194. std::istringstream line_stream(path.filename().string());
  195. std::string date;
  196. std::string application;
  197. std::string time;
  198. // Parse filename to obtain entry properties
  199. std::getline(line_stream, application, '_');
  200. std::getline(line_stream, date, '_');
  201. std::getline(line_stream, time, '_');
  202. std::istringstream date_stream(date);
  203. std::istringstream time_stream(time);
  204. std::string year;
  205. std::string month;
  206. std::string day;
  207. std::string hour;
  208. std::string minute;
  209. std::string second;
  210. std::getline(date_stream, year, '-');
  211. std::getline(date_stream, month, '-');
  212. std::getline(date_stream, day, '-');
  213. std::getline(time_stream, hour, '-');
  214. std::getline(time_stream, minute, '-');
  215. std::getline(time_stream, second, '-');
  216. try {
  217. out_entry = {
  218. .entry_size = 1,
  219. .file_id{
  220. .application_id = static_cast<u64>(std::stoll(application, 0, 16)),
  221. .date =
  222. {
  223. .year = static_cast<u16>(std::stoi(year)),
  224. .month = static_cast<u8>(std::stoi(month)),
  225. .day = static_cast<u8>(std::stoi(day)),
  226. .hour = static_cast<u8>(std::stoi(hour)),
  227. .minute = static_cast<u8>(std::stoi(minute)),
  228. .second = static_cast<u8>(std::stoi(second)),
  229. .unique_id = 0,
  230. },
  231. .storage = AlbumStorage::Sd,
  232. .type = ContentType::Screenshot,
  233. .unknown = 1,
  234. },
  235. };
  236. } catch (const std::invalid_argument&) {
  237. return ResultUnknown;
  238. } catch (const std::out_of_range&) {
  239. return ResultUnknown;
  240. } catch (const std::exception&) {
  241. return ResultUnknown;
  242. }
  243. return ResultSuccess;
  244. }
  245. Result AlbumManager::LoadImage(std::span<u8> out_image, const std::filesystem::path& path,
  246. int width, int height, ScreenShotDecoderFlag flag) const {
  247. if (out_image.size() != static_cast<std::size_t>(width * height * STBI_rgb_alpha)) {
  248. return ResultUnknown;
  249. }
  250. const Common::FS::IOFile db_file{path, Common::FS::FileAccessMode::Read,
  251. Common::FS::FileType::BinaryFile};
  252. std::vector<u8> raw_file(db_file.GetSize());
  253. if (db_file.Read(raw_file) != raw_file.size()) {
  254. return ResultUnknown;
  255. }
  256. int filter_flag = STBIR_FILTER_DEFAULT;
  257. int original_width, original_height, color_channels;
  258. const auto dbi_image =
  259. stbi_load_from_memory(raw_file.data(), static_cast<int>(raw_file.size()), &original_width,
  260. &original_height, &color_channels, STBI_rgb_alpha);
  261. if (dbi_image == nullptr) {
  262. return ResultUnknown;
  263. }
  264. switch (flag) {
  265. case ScreenShotDecoderFlag::EnableFancyUpsampling:
  266. filter_flag = STBIR_FILTER_TRIANGLE;
  267. break;
  268. case ScreenShotDecoderFlag::EnableBlockSmoothing:
  269. filter_flag = STBIR_FILTER_BOX;
  270. break;
  271. default:
  272. filter_flag = STBIR_FILTER_DEFAULT;
  273. break;
  274. }
  275. stbir_resize_uint8_srgb(dbi_image, original_width, original_height, 0, out_image.data(), width,
  276. height, 0, STBI_rgb_alpha, 3, filter_flag);
  277. return ResultSuccess;
  278. }
  279. } // namespace Service::Capture