archive.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2
  3. // Refer to the license.txt file included.
  4. #include <memory>
  5. #include <unordered_map>
  6. #include "common/common_types.h"
  7. #include "common/file_util.h"
  8. #include "common/math_util.h"
  9. #include "core/file_sys/archive_savedata.h"
  10. #include "core/file_sys/archive_backend.h"
  11. #include "core/file_sys/archive_sdmc.h"
  12. #include "core/file_sys/directory_backend.h"
  13. #include "core/hle/service/fs/archive.h"
  14. #include "core/hle/kernel/session.h"
  15. #include "core/hle/result.h"
  16. // Specializes std::hash for ArchiveIdCode, so that we can use it in std::unordered_map.
  17. // Workaroung for libstdc++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60970
  18. namespace std {
  19. template <>
  20. struct hash<Service::FS::ArchiveIdCode> {
  21. typedef Service::FS::ArchiveIdCode argument_type;
  22. typedef std::size_t result_type;
  23. result_type operator()(const argument_type& id_code) const {
  24. typedef std::underlying_type<argument_type>::type Type;
  25. return std::hash<Type>()(static_cast<Type>(id_code));
  26. }
  27. };
  28. }
  29. namespace Service {
  30. namespace FS {
  31. // Command to access archive file
  32. enum class FileCommand : u32 {
  33. Dummy1 = 0x000100C6,
  34. Control = 0x040100C4,
  35. OpenSubFile = 0x08010100,
  36. Read = 0x080200C2,
  37. Write = 0x08030102,
  38. GetSize = 0x08040000,
  39. SetSize = 0x08050080,
  40. GetAttributes = 0x08060000,
  41. SetAttributes = 0x08070040,
  42. Close = 0x08080000,
  43. Flush = 0x08090000,
  44. };
  45. // Command to access directory
  46. enum class DirectoryCommand : u32 {
  47. Dummy1 = 0x000100C6,
  48. Control = 0x040100C4,
  49. Read = 0x08010042,
  50. Close = 0x08020000,
  51. };
  52. class Archive {
  53. public:
  54. Archive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code)
  55. : backend(std::move(backend)), id_code(id_code) {
  56. }
  57. std::string GetName() const { return "Archive: " + backend->GetName(); }
  58. ArchiveIdCode id_code; ///< Id code of the archive
  59. std::unique_ptr<FileSys::ArchiveBackend> backend; ///< Archive backend interface
  60. };
  61. class File : public Kernel::Session {
  62. public:
  63. File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path)
  64. : backend(std::move(backend)), path(path) {
  65. }
  66. std::string GetName() const override { return "Path: " + path.DebugStr(); }
  67. FileSys::Path path; ///< Path of the file
  68. std::unique_ptr<FileSys::FileBackend> backend; ///< File backend interface
  69. ResultVal<bool> SyncRequest() override {
  70. u32* cmd_buff = Kernel::GetCommandBuffer();
  71. FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
  72. switch (cmd) {
  73. // Read from file...
  74. case FileCommand::Read:
  75. {
  76. u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
  77. u32 length = cmd_buff[3];
  78. u32 address = cmd_buff[5];
  79. LOG_TRACE(Service_FS, "Read %s %s: offset=0x%llx length=%d address=0x%x",
  80. GetTypeName().c_str(), GetName().c_str(), offset, length, address);
  81. cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
  82. break;
  83. }
  84. // Write to file...
  85. case FileCommand::Write:
  86. {
  87. u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
  88. u32 length = cmd_buff[3];
  89. u32 flush = cmd_buff[4];
  90. u32 address = cmd_buff[6];
  91. LOG_TRACE(Service_FS, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
  92. GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
  93. cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
  94. break;
  95. }
  96. case FileCommand::GetSize:
  97. {
  98. LOG_TRACE(Service_FS, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str());
  99. u64 size = backend->GetSize();
  100. cmd_buff[2] = (u32)size;
  101. cmd_buff[3] = size >> 32;
  102. break;
  103. }
  104. case FileCommand::SetSize:
  105. {
  106. u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
  107. LOG_TRACE(Service_FS, "SetSize %s %s size=%llu",
  108. GetTypeName().c_str(), GetName().c_str(), size);
  109. backend->SetSize(size);
  110. break;
  111. }
  112. case FileCommand::Close:
  113. {
  114. LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
  115. Kernel::g_object_pool.Destroy<File>(GetHandle());
  116. break;
  117. }
  118. case FileCommand::Flush:
  119. {
  120. LOG_TRACE(Service_FS, "Flush");
  121. backend->Flush();
  122. break;
  123. }
  124. // Unknown command...
  125. default:
  126. LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd);
  127. ResultCode error = UnimplementedFunction(ErrorModule::FS);
  128. cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
  129. return error;
  130. }
  131. cmd_buff[1] = 0; // No error
  132. return MakeResult<bool>(false);
  133. }
  134. };
  135. class Directory : public Kernel::Session {
  136. public:
  137. Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path)
  138. : backend(std::move(backend)), path(path) {
  139. }
  140. std::string GetName() const override { return "Directory: " + path.DebugStr(); }
  141. FileSys::Path path; ///< Path of the directory
  142. std::unique_ptr<FileSys::DirectoryBackend> backend; ///< File backend interface
  143. ResultVal<bool> SyncRequest() override {
  144. u32* cmd_buff = Kernel::GetCommandBuffer();
  145. DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
  146. switch (cmd) {
  147. // Read from directory...
  148. case DirectoryCommand::Read:
  149. {
  150. u32 count = cmd_buff[1];
  151. u32 address = cmd_buff[3];
  152. auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
  153. LOG_TRACE(Service_FS, "Read %s %s: count=%d",
  154. GetTypeName().c_str(), GetName().c_str(), count);
  155. // Number of entries actually read
  156. cmd_buff[2] = backend->Read(count, entries);
  157. break;
  158. }
  159. case DirectoryCommand::Close:
  160. {
  161. LOG_TRACE(Service_FS, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
  162. Kernel::g_object_pool.Destroy<Directory>(GetHandle());
  163. break;
  164. }
  165. // Unknown command...
  166. default:
  167. LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd);
  168. ResultCode error = UnimplementedFunction(ErrorModule::FS);
  169. cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
  170. return MakeResult<bool>(false);
  171. }
  172. cmd_buff[1] = 0; // No error
  173. return MakeResult<bool>(false);
  174. }
  175. };
  176. ////////////////////////////////////////////////////////////////////////////////////////////////////
  177. /**
  178. * Map of registered archives, identified by id code. Once an archive is registered here, it is
  179. * never removed until the FS service is shut down.
  180. */
  181. static std::unordered_map<ArchiveIdCode, std::unique_ptr<Archive>> id_code_map;
  182. /**
  183. * Map of active archive handles. Values are pointers to the archives in `idcode_map`.
  184. */
  185. static std::unordered_map<ArchiveHandle, Archive*> handle_map;
  186. static ArchiveHandle next_handle;
  187. static Archive* GetArchive(ArchiveHandle handle) {
  188. auto itr = handle_map.find(handle);
  189. return (itr == handle_map.end()) ? nullptr : itr->second;
  190. }
  191. ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code) {
  192. LOG_TRACE(Service_FS, "Opening archive with id code 0x%08X", id_code);
  193. auto itr = id_code_map.find(id_code);
  194. if (itr == id_code_map.end()) {
  195. if (id_code == ArchiveIdCode::SaveData) {
  196. // When a SaveData archive is created for the first time, it is not yet formatted
  197. // and the save file/directory structure expected by the game has not yet been initialized.
  198. // Returning the NotFormatted error code will signal the game to provision the SaveData archive
  199. // with the files and folders that it expects.
  200. // The FormatSaveData service call will create the SaveData archive when it is called.
  201. return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS,
  202. ErrorSummary::InvalidState, ErrorLevel::Status);
  203. }
  204. // TODO: Verify error against hardware
  205. return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
  206. ErrorSummary::NotFound, ErrorLevel::Permanent);
  207. }
  208. // This should never even happen in the first place with 64-bit handles,
  209. while (handle_map.count(next_handle) != 0) {
  210. ++next_handle;
  211. }
  212. handle_map.emplace(next_handle, itr->second.get());
  213. return MakeResult<ArchiveHandle>(next_handle++);
  214. }
  215. ResultCode CloseArchive(ArchiveHandle handle) {
  216. if (handle_map.erase(handle) == 0)
  217. return InvalidHandle(ErrorModule::FS);
  218. else
  219. return RESULT_SUCCESS;
  220. }
  221. // TODO(yuriks): This might be what the fs:REG service is for. See the Register/Unregister calls in
  222. // http://3dbrew.org/wiki/Filesystem_services#ProgramRegistry_service_.22fs:REG.22
  223. ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, ArchiveIdCode id_code) {
  224. auto result = id_code_map.emplace(id_code, std::make_unique<Archive>(std::move(backend), id_code));
  225. bool inserted = result.second;
  226. _dbg_assert_msg_(Service_FS, inserted, "Tried to register more than one archive with same id code");
  227. auto& archive = result.first->second;
  228. LOG_DEBUG(Service_FS, "Registered archive %s with id code 0x%08X", archive->GetName().c_str(), id_code);
  229. return RESULT_SUCCESS;
  230. }
  231. ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) {
  232. Archive* archive = GetArchive(archive_handle);
  233. if (archive == nullptr)
  234. return InvalidHandle(ErrorModule::FS);
  235. std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode);
  236. if (backend == nullptr) {
  237. return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS,
  238. ErrorSummary::NotFound, ErrorLevel::Status);
  239. }
  240. auto file = std::make_unique<File>(std::move(backend), path);
  241. Handle handle = Kernel::g_object_pool.Create(file.release());
  242. return MakeResult<Handle>(handle);
  243. }
  244. ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
  245. Archive* archive = GetArchive(archive_handle);
  246. if (archive == nullptr)
  247. return InvalidHandle(ErrorModule::FS);
  248. if (archive->backend->DeleteFile(path))
  249. return RESULT_SUCCESS;
  250. return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
  251. ErrorSummary::Canceled, ErrorLevel::Status);
  252. }
  253. ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
  254. ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) {
  255. Archive* src_archive = GetArchive(src_archive_handle);
  256. Archive* dest_archive = GetArchive(dest_archive_handle);
  257. if (src_archive == nullptr || dest_archive == nullptr)
  258. return InvalidHandle(ErrorModule::FS);
  259. if (src_archive == dest_archive) {
  260. if (src_archive->backend->RenameFile(src_path, dest_path))
  261. return RESULT_SUCCESS;
  262. } else {
  263. // TODO: Implement renaming across archives
  264. return UnimplementedFunction(ErrorModule::FS);
  265. }
  266. // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
  267. // exist or similar. Verify.
  268. return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
  269. ErrorSummary::NothingHappened, ErrorLevel::Status);
  270. }
  271. ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
  272. Archive* archive = GetArchive(archive_handle);
  273. if (archive == nullptr)
  274. return InvalidHandle(ErrorModule::FS);
  275. if (archive->backend->DeleteDirectory(path))
  276. return RESULT_SUCCESS;
  277. return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
  278. ErrorSummary::Canceled, ErrorLevel::Status);
  279. }
  280. ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
  281. Archive* archive = GetArchive(archive_handle);
  282. if (archive == nullptr)
  283. return InvalidHandle(ErrorModule::FS);
  284. if (archive->backend->CreateDirectory(path))
  285. return RESULT_SUCCESS;
  286. return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
  287. ErrorSummary::Canceled, ErrorLevel::Status);
  288. }
  289. ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, const FileSys::Path& src_path,
  290. ArchiveHandle dest_archive_handle, const FileSys::Path& dest_path) {
  291. Archive* src_archive = GetArchive(src_archive_handle);
  292. Archive* dest_archive = GetArchive(dest_archive_handle);
  293. if (src_archive == nullptr || dest_archive == nullptr)
  294. return InvalidHandle(ErrorModule::FS);
  295. if (src_archive == dest_archive) {
  296. if (src_archive->backend->RenameDirectory(src_path, dest_path))
  297. return RESULT_SUCCESS;
  298. } else {
  299. // TODO: Implement renaming across archives
  300. return UnimplementedFunction(ErrorModule::FS);
  301. }
  302. // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
  303. // exist or similar. Verify.
  304. return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
  305. ErrorSummary::NothingHappened, ErrorLevel::Status);
  306. }
  307. /**
  308. * Open a Directory from an Archive
  309. * @param archive_handle Handle to an open Archive object
  310. * @param path Path to the Directory inside of the Archive
  311. * @return Opened Directory object
  312. */
  313. ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) {
  314. Archive* archive = GetArchive(archive_handle);
  315. if (archive == nullptr)
  316. return InvalidHandle(ErrorModule::FS);
  317. std::unique_ptr<FileSys::DirectoryBackend> backend = archive->backend->OpenDirectory(path);
  318. if (backend == nullptr) {
  319. return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
  320. ErrorSummary::NotFound, ErrorLevel::Permanent);
  321. }
  322. auto directory = std::make_unique<Directory>(std::move(backend), path);
  323. Handle handle = Kernel::g_object_pool.Create(directory.release());
  324. return MakeResult<Handle>(handle);
  325. }
  326. ResultCode FormatSaveData() {
  327. // TODO(Subv): Actually wipe the savedata folder after creating or opening it
  328. // Do not create the archive again if it already exists
  329. if (id_code_map.find(ArchiveIdCode::SaveData) != id_code_map.end())
  330. return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the correct error code
  331. // Create the SaveData archive
  332. std::string savedata_directory = FileUtil::GetUserPath(D_SAVEDATA_IDX);
  333. auto savedata_archive = std::make_unique<FileSys::Archive_SaveData>(savedata_directory,
  334. Kernel::g_program_id);
  335. if (savedata_archive->Initialize()) {
  336. CreateArchive(std::move(savedata_archive), ArchiveIdCode::SaveData);
  337. return RESULT_SUCCESS;
  338. } else {
  339. LOG_ERROR(Service_FS, "Can't instantiate SaveData archive with path %s",
  340. savedata_archive->GetMountPoint().c_str());
  341. return UnimplementedFunction(ErrorModule::FS); // TODO(Subv): Find the proper error code
  342. }
  343. }
  344. /// Initialize archives
  345. void ArchiveInit() {
  346. next_handle = 1;
  347. // TODO(Link Mauve): Add the other archive types (see here for the known types:
  348. // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished
  349. // archive type is SDMC, so it is the only one getting exposed.
  350. std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
  351. auto sdmc_archive = std::make_unique<FileSys::Archive_SDMC>(sdmc_directory);
  352. if (sdmc_archive->Initialize())
  353. CreateArchive(std::move(sdmc_archive), ArchiveIdCode::SDMC);
  354. else
  355. LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
  356. }
  357. /// Shutdown archives
  358. void ArchiveShutdown() {
  359. handle_map.clear();
  360. id_code_map.clear();
  361. }
  362. } // namespace FS
  363. } // namespace Service