archive.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2
  3. // Refer to the license.txt file included.
  4. #include "common/common_types.h"
  5. #include "common/file_util.h"
  6. #include "common/math_util.h"
  7. #include "core/file_sys/archive.h"
  8. #include "core/file_sys/archive_sdmc.h"
  9. #include "core/file_sys/directory.h"
  10. #include "core/hle/service/service.h"
  11. #include "core/hle/kernel/archive.h"
  12. ////////////////////////////////////////////////////////////////////////////////////////////////////
  13. // Kernel namespace
  14. namespace Kernel {
  15. // Command to access archive file
  16. enum class FileCommand : u32 {
  17. Dummy1 = 0x000100C6,
  18. Control = 0x040100C4,
  19. OpenSubFile = 0x08010100,
  20. Read = 0x080200C2,
  21. Write = 0x08030102,
  22. GetSize = 0x08040000,
  23. SetSize = 0x08050080,
  24. GetAttributes = 0x08060000,
  25. SetAttributes = 0x08070040,
  26. Close = 0x08080000,
  27. Flush = 0x08090000,
  28. };
  29. // Command to access directory
  30. enum class DirectoryCommand : u32 {
  31. Dummy1 = 0x000100C6,
  32. Control = 0x040100C4,
  33. Read = 0x08010042,
  34. Close = 0x08020000,
  35. };
  36. class Archive : public Object {
  37. public:
  38. std::string GetTypeName() const override { return "Archive"; }
  39. std::string GetName() const override { return name; }
  40. static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; }
  41. Kernel::HandleType GetHandleType() const override { return HandleType::Archive; }
  42. std::string name; ///< Name of archive (optional)
  43. FileSys::Archive* backend; ///< Archive backend interface
  44. /**
  45. * Synchronize kernel object
  46. * @param wait Boolean wait set if current thread should wait as a result of sync operation
  47. * @return Result of operation, 0 on success, otherwise error code
  48. */
  49. Result SyncRequest(bool* wait) override {
  50. u32* cmd_buff = Service::GetCommandBuffer();
  51. FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
  52. switch (cmd) {
  53. // Read from archive...
  54. case FileCommand::Read:
  55. {
  56. u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
  57. u32 length = cmd_buff[3];
  58. u32 address = cmd_buff[5];
  59. // Number of bytes read
  60. cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
  61. break;
  62. }
  63. // Write to archive...
  64. case FileCommand::Write:
  65. {
  66. u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
  67. u32 length = cmd_buff[3];
  68. u32 flush = cmd_buff[4];
  69. u32 address = cmd_buff[6];
  70. // Number of bytes written
  71. cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
  72. break;
  73. }
  74. case FileCommand::GetSize:
  75. {
  76. u64 filesize = (u64) backend->GetSize();
  77. cmd_buff[2] = (u32) filesize; // Lower word
  78. cmd_buff[3] = (u32) (filesize >> 32); // Upper word
  79. break;
  80. }
  81. case FileCommand::SetSize:
  82. {
  83. backend->SetSize(cmd_buff[1] | ((u64)cmd_buff[2] << 32));
  84. break;
  85. }
  86. case FileCommand::Close:
  87. {
  88. DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
  89. Kernel::g_object_pool.Destroy<Archive>(GetHandle());
  90. CloseArchive(backend->GetIdCode());
  91. break;
  92. }
  93. // Unknown command...
  94. default:
  95. {
  96. ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
  97. return -1;
  98. }
  99. }
  100. cmd_buff[1] = 0; // No error
  101. return 0;
  102. }
  103. /**
  104. * Wait for kernel object to synchronize
  105. * @param wait Boolean wait set if current thread should wait as a result of sync operation
  106. * @return Result of operation, 0 on success, otherwise error code
  107. */
  108. Result WaitSynchronization(bool* wait) override {
  109. // TODO(bunnei): ImplementMe
  110. ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
  111. return 0;
  112. }
  113. };
  114. class File : public Object {
  115. public:
  116. std::string GetTypeName() const override { return "File"; }
  117. std::string GetName() const override { return path; }
  118. static Kernel::HandleType GetStaticHandleType() { return HandleType::File; }
  119. Kernel::HandleType GetHandleType() const override { return HandleType::File; }
  120. std::string path; ///< Path of the file
  121. std::unique_ptr<FileSys::File> backend; ///< File backend interface
  122. /**
  123. * Synchronize kernel object
  124. * @param wait Boolean wait set if current thread should wait as a result of sync operation
  125. * @return Result of operation, 0 on success, otherwise error code
  126. */
  127. Result SyncRequest(bool* wait) override {
  128. u32* cmd_buff = Service::GetCommandBuffer();
  129. FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
  130. switch (cmd) {
  131. // Read from file...
  132. case FileCommand::Read:
  133. {
  134. u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
  135. u32 length = cmd_buff[3];
  136. u32 address = cmd_buff[5];
  137. DEBUG_LOG(KERNEL, "Read %s %s: offset=0x%x length=%d address=0x%x",
  138. GetTypeName().c_str(), GetName().c_str(), offset, length, address);
  139. cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
  140. break;
  141. }
  142. // Write to file...
  143. case FileCommand::Write:
  144. {
  145. u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
  146. u32 length = cmd_buff[3];
  147. u32 flush = cmd_buff[4];
  148. u32 address = cmd_buff[6];
  149. DEBUG_LOG(KERNEL, "Write %s %s: offset=0x%x length=%d address=0x%x, flush=0x%x",
  150. GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
  151. cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
  152. break;
  153. }
  154. case FileCommand::GetSize:
  155. {
  156. DEBUG_LOG(KERNEL, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str());
  157. u64 size = backend->GetSize();
  158. cmd_buff[2] = (u32)size;
  159. cmd_buff[3] = size >> 32;
  160. break;
  161. }
  162. case FileCommand::SetSize:
  163. {
  164. u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
  165. DEBUG_LOG(KERNEL, "SetSize %s %s size=%d", GetTypeName().c_str(), GetName().c_str(), size);
  166. backend->SetSize(size);
  167. break;
  168. }
  169. case FileCommand::Close:
  170. {
  171. DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
  172. Kernel::g_object_pool.Destroy<File>(GetHandle());
  173. break;
  174. }
  175. // Unknown command...
  176. default:
  177. ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
  178. cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
  179. return -1;
  180. }
  181. cmd_buff[1] = 0; // No error
  182. return 0;
  183. }
  184. /**
  185. * Wait for kernel object to synchronize
  186. * @param wait Boolean wait set if current thread should wait as a result of sync operation
  187. * @return Result of operation, 0 on success, otherwise error code
  188. */
  189. Result WaitSynchronization(bool* wait) override {
  190. // TODO(bunnei): ImplementMe
  191. ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
  192. return 0;
  193. }
  194. };
  195. class Directory : public Object {
  196. public:
  197. std::string GetTypeName() const override { return "Directory"; }
  198. std::string GetName() const override { return path; }
  199. static Kernel::HandleType GetStaticHandleType() { return HandleType::Directory; }
  200. Kernel::HandleType GetHandleType() const override { return HandleType::Directory; }
  201. std::string path; ///< Path of the directory
  202. std::unique_ptr<FileSys::Directory> backend; ///< File backend interface
  203. /**
  204. * Synchronize kernel object
  205. * @param wait Boolean wait set if current thread should wait as a result of sync operation
  206. * @return Result of operation, 0 on success, otherwise error code
  207. */
  208. Result SyncRequest(bool* wait) override {
  209. u32* cmd_buff = Service::GetCommandBuffer();
  210. DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
  211. switch (cmd) {
  212. // Read from directory...
  213. case DirectoryCommand::Read:
  214. {
  215. u32 count = cmd_buff[1];
  216. u32 address = cmd_buff[3];
  217. FileSys::Entry* entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
  218. DEBUG_LOG(KERNEL, "Read %s %s: count=%d", GetTypeName().c_str(), GetName().c_str(), count);
  219. // Number of entries actually read
  220. cmd_buff[2] = backend->Read(count, entries);
  221. break;
  222. }
  223. case DirectoryCommand::Close:
  224. {
  225. DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
  226. Kernel::g_object_pool.Destroy<Directory>(GetHandle());
  227. break;
  228. }
  229. // Unknown command...
  230. default:
  231. ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
  232. cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
  233. return -1;
  234. }
  235. cmd_buff[1] = 0; // No error
  236. return 0;
  237. }
  238. /**
  239. * Wait for kernel object to synchronize
  240. * @param wait Boolean wait set if current thread should wait as a result of sync operation
  241. * @return Result of operation, 0 on success, otherwise error code
  242. */
  243. Result WaitSynchronization(bool* wait) override {
  244. // TODO(bunnei): ImplementMe
  245. ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
  246. return 0;
  247. }
  248. };
  249. ////////////////////////////////////////////////////////////////////////////////////////////////////
  250. std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode
  251. /**
  252. * Opens an archive
  253. * @param id_code IdCode of the archive to open
  254. * @return Handle to archive if it exists, otherwise a null handle (0)
  255. */
  256. Handle OpenArchive(FileSys::Archive::IdCode id_code) {
  257. auto itr = g_archive_map.find(id_code);
  258. if (itr == g_archive_map.end()) {
  259. return 0;
  260. }
  261. return itr->second;
  262. }
  263. /**
  264. * Closes an archive
  265. * @param id_code IdCode of the archive to open
  266. * @return Result of operation, 0 on success, otherwise error code
  267. */
  268. Result CloseArchive(FileSys::Archive::IdCode id_code) {
  269. if (1 != g_archive_map.erase(id_code)) {
  270. ERROR_LOG(KERNEL, "Cannot close archive %d", (int) id_code);
  271. return -1;
  272. }
  273. INFO_LOG(KERNEL, "Closed archive %d", (int) id_code);
  274. return 0;
  275. }
  276. /**
  277. * Mounts an archive
  278. * @param archive Pointer to the archive to mount
  279. * @return Result of operation, 0 on success, otherwise error code
  280. */
  281. Result MountArchive(Archive* archive) {
  282. FileSys::Archive::IdCode id_code = archive->backend->GetIdCode();
  283. if (0 != OpenArchive(id_code)) {
  284. ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code);
  285. return -1;
  286. }
  287. g_archive_map[id_code] = archive->GetHandle();
  288. INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str());
  289. return 0;
  290. }
  291. /**
  292. * Creates an Archive
  293. * @param handle Handle to newly created archive object
  294. * @param backend File system backend interface to the archive
  295. * @param name Optional name of Archive
  296. * @return Newly created Archive object
  297. */
  298. Archive* CreateArchive(Handle& handle, FileSys::Archive* backend, const std::string& name) {
  299. Archive* archive = new Archive;
  300. handle = Kernel::g_object_pool.Create(archive);
  301. archive->name = name;
  302. archive->backend = backend;
  303. MountArchive(archive);
  304. return archive;
  305. }
  306. /**
  307. * Creates an Archive
  308. * @param backend File system backend interface to the archive
  309. * @param name Optional name of Archive
  310. * @return Handle to newly created Archive object
  311. */
  312. Handle CreateArchive(FileSys::Archive* backend, const std::string& name) {
  313. Handle handle;
  314. CreateArchive(handle, backend, name);
  315. return handle;
  316. }
  317. /**
  318. * Open a File from an Archive
  319. * @param archive_handle Handle to an open Archive object
  320. * @param path Path to the File inside of the Archive
  321. * @param mode Mode under which to open the File
  322. * @return Opened File object
  323. */
  324. Handle OpenFileFromArchive(Handle archive_handle, const std::string& path, const FileSys::Mode mode) {
  325. File* file = new File;
  326. Handle handle = Kernel::g_object_pool.Create(file);
  327. Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
  328. file->path = path;
  329. file->backend = archive->backend->OpenFile(path, mode);
  330. if (!file->backend)
  331. return 0;
  332. return handle;
  333. }
  334. /**
  335. * Open a Directory from an Archive
  336. * @param archive_handle Handle to an open Archive object
  337. * @param path Path to the Directory inside of the Archive
  338. * @return Opened Directory object
  339. */
  340. Handle OpenDirectoryFromArchive(Handle archive_handle, const std::string& path) {
  341. Directory* directory = new Directory;
  342. Handle handle = Kernel::g_object_pool.Create(directory);
  343. Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
  344. directory->path = path;
  345. directory->backend = archive->backend->OpenDirectory(path);
  346. return handle;
  347. }
  348. /// Initialize archives
  349. void ArchiveInit() {
  350. g_archive_map.clear();
  351. // TODO(Link Mauve): Add the other archive types (see here for the known types:
  352. // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished
  353. // archive type is SDMC, so it is the only one getting exposed.
  354. std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
  355. auto archive = new FileSys::Archive_SDMC(sdmc_directory);
  356. if (archive->Initialize())
  357. CreateArchive(archive, "SDMC");
  358. else
  359. ERROR_LOG(KERNEL, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
  360. }
  361. /// Shutdown archives
  362. void ArchiveShutdown() {
  363. g_archive_map.clear();
  364. }
  365. } // namespace Kernel