archive.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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 { return "Archive"; }
  39. std::string GetName() const { return name; }
  40. static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; }
  41. Kernel::HandleType GetHandleType() const { 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) {
  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) {
  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 { return "File"; }
  117. std::string GetName() const { return path; }
  118. static Kernel::HandleType GetStaticHandleType() { return HandleType::File; }
  119. Kernel::HandleType GetHandleType() const { 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) {
  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::Close:
  163. {
  164. DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
  165. Kernel::g_object_pool.Destroy<File>(GetHandle());
  166. break;
  167. }
  168. // Unknown command...
  169. default:
  170. ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
  171. cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
  172. return -1;
  173. }
  174. cmd_buff[1] = 0; // No error
  175. return 0;
  176. }
  177. /**
  178. * Wait for kernel object to synchronize
  179. * @param wait Boolean wait set if current thread should wait as a result of sync operation
  180. * @return Result of operation, 0 on success, otherwise error code
  181. */
  182. Result WaitSynchronization(bool* wait) {
  183. // TODO(bunnei): ImplementMe
  184. ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
  185. return 0;
  186. }
  187. };
  188. class Directory : public Object {
  189. public:
  190. std::string GetTypeName() const { return "Directory"; }
  191. std::string GetName() const { return path; }
  192. static Kernel::HandleType GetStaticHandleType() { return HandleType::Directory; }
  193. Kernel::HandleType GetHandleType() const { return HandleType::Directory; }
  194. std::string path; ///< Path of the directory
  195. std::unique_ptr<FileSys::Directory> backend; ///< File backend interface
  196. /**
  197. * Synchronize kernel object
  198. * @param wait Boolean wait set if current thread should wait as a result of sync operation
  199. * @return Result of operation, 0 on success, otherwise error code
  200. */
  201. Result SyncRequest(bool* wait) {
  202. u32* cmd_buff = Service::GetCommandBuffer();
  203. DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
  204. switch (cmd) {
  205. // Read from directory...
  206. case DirectoryCommand::Read:
  207. {
  208. u32 count = cmd_buff[1];
  209. u32 address = cmd_buff[3];
  210. FileSys::Entry* entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
  211. DEBUG_LOG(KERNEL, "Read %s %s: count=%d", GetTypeName().c_str(), GetName().c_str(), count);
  212. // Number of entries actually read
  213. cmd_buff[2] = backend->Read(count, entries);
  214. break;
  215. }
  216. case DirectoryCommand::Close:
  217. {
  218. DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
  219. Kernel::g_object_pool.Destroy<Directory>(GetHandle());
  220. break;
  221. }
  222. // Unknown command...
  223. default:
  224. ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
  225. cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that.
  226. return -1;
  227. }
  228. cmd_buff[1] = 0; // No error
  229. return 0;
  230. }
  231. /**
  232. * Wait for kernel object to synchronize
  233. * @param wait Boolean wait set if current thread should wait as a result of sync operation
  234. * @return Result of operation, 0 on success, otherwise error code
  235. */
  236. Result WaitSynchronization(bool* wait) {
  237. // TODO(bunnei): ImplementMe
  238. ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
  239. return 0;
  240. }
  241. };
  242. ////////////////////////////////////////////////////////////////////////////////////////////////////
  243. std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode
  244. /**
  245. * Opens an archive
  246. * @param id_code IdCode of the archive to open
  247. * @return Handle to archive if it exists, otherwise a null handle (0)
  248. */
  249. Handle OpenArchive(FileSys::Archive::IdCode id_code) {
  250. auto itr = g_archive_map.find(id_code);
  251. if (itr == g_archive_map.end()) {
  252. return 0;
  253. }
  254. return itr->second;
  255. }
  256. /**
  257. * Closes an archive
  258. * @param id_code IdCode of the archive to open
  259. * @return Result of operation, 0 on success, otherwise error code
  260. */
  261. Result CloseArchive(FileSys::Archive::IdCode id_code) {
  262. if (1 != g_archive_map.erase(id_code)) {
  263. ERROR_LOG(KERNEL, "Cannot close archive %d", (int) id_code);
  264. return -1;
  265. }
  266. INFO_LOG(KERNEL, "Closed archive %d", (int) id_code);
  267. return 0;
  268. }
  269. /**
  270. * Mounts an archive
  271. * @param archive Pointer to the archive to mount
  272. * @return Result of operation, 0 on success, otherwise error code
  273. */
  274. Result MountArchive(Archive* archive) {
  275. FileSys::Archive::IdCode id_code = archive->backend->GetIdCode();
  276. if (0 != OpenArchive(id_code)) {
  277. ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code);
  278. return -1;
  279. }
  280. g_archive_map[id_code] = archive->GetHandle();
  281. INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str());
  282. return 0;
  283. }
  284. /**
  285. * Creates an Archive
  286. * @param handle Handle to newly created archive object
  287. * @param backend File system backend interface to the archive
  288. * @param name Optional name of Archive
  289. * @return Newly created Archive object
  290. */
  291. Archive* CreateArchive(Handle& handle, FileSys::Archive* backend, const std::string& name) {
  292. Archive* archive = new Archive;
  293. handle = Kernel::g_object_pool.Create(archive);
  294. archive->name = name;
  295. archive->backend = backend;
  296. MountArchive(archive);
  297. return archive;
  298. }
  299. /**
  300. * Creates an Archive
  301. * @param backend File system backend interface to the archive
  302. * @param name Optional name of Archive
  303. * @return Handle to newly created Archive object
  304. */
  305. Handle CreateArchive(FileSys::Archive* backend, const std::string& name) {
  306. Handle handle;
  307. CreateArchive(handle, backend, name);
  308. return handle;
  309. }
  310. /**
  311. * Open a File from an Archive
  312. * @param archive_handle Handle to an open Archive object
  313. * @param path Path to the File inside of the Archive
  314. * @param mode Mode under which to open the File
  315. * @return Opened File object
  316. */
  317. Handle OpenFileFromArchive(Handle archive_handle, const std::string& path, const FileSys::Mode mode) {
  318. File* file = new File;
  319. Handle handle = Kernel::g_object_pool.Create(file);
  320. Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
  321. file->path = path;
  322. file->backend = archive->backend->OpenFile(path, mode);
  323. return handle;
  324. }
  325. /**
  326. * Open a Directory from an Archive
  327. * @param archive_handle Handle to an open Archive object
  328. * @param path Path to the Directory inside of the Archive
  329. * @return Opened Directory object
  330. */
  331. Handle OpenDirectoryFromArchive(Handle archive_handle, const std::string& path) {
  332. Directory* directory = new Directory;
  333. Handle handle = Kernel::g_object_pool.Create(directory);
  334. Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
  335. directory->path = path;
  336. directory->backend = archive->backend->OpenDirectory(path);
  337. return handle;
  338. }
  339. /// Initialize archives
  340. void ArchiveInit() {
  341. g_archive_map.clear();
  342. // TODO(Link Mauve): Add the other archive types (see here for the known types:
  343. // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished
  344. // archive type is SDMC, so it is the only one getting exposed.
  345. std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
  346. auto archive = new FileSys::Archive_SDMC(sdmc_directory);
  347. if (archive->Initialize())
  348. CreateArchive(archive, "SDMC");
  349. else
  350. ERROR_LOG(KERNEL, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
  351. }
  352. /// Shutdown archives
  353. void ArchiveShutdown() {
  354. g_archive_map.clear();
  355. }
  356. } // namespace Kernel