archive.cpp 17 KB

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