mii.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <memory>
  5. #include <fmt/ostream.h>
  6. #include "common/logging/log.h"
  7. #include "common/string_util.h"
  8. #include "core/hle/ipc_helpers.h"
  9. #include "core/hle/kernel/hle_ipc.h"
  10. #include "core/hle/service/mii/mii.h"
  11. #include "core/hle/service/mii/mii_manager.h"
  12. #include "core/hle/service/service.h"
  13. #include "core/hle/service/sm/sm.h"
  14. namespace Service::Mii {
  15. constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
  16. constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
  17. constexpr ResultCode ERROR_NOT_IN_TEST_MODE{ErrorModule::Mii, 99};
  18. class IDatabaseService final : public ServiceFramework<IDatabaseService> {
  19. public:
  20. explicit IDatabaseService() : ServiceFramework{"IDatabaseService"} {
  21. // clang-format off
  22. static const FunctionInfo functions[] = {
  23. {0, &IDatabaseService::IsUpdated, "IsUpdated"},
  24. {1, &IDatabaseService::IsFullDatabase, "IsFullDatabase"},
  25. {2, &IDatabaseService::GetCount, "GetCount"},
  26. {3, &IDatabaseService::Get, "Get"},
  27. {4, &IDatabaseService::Get1, "Get1"},
  28. {5, nullptr, "UpdateLatest"},
  29. {6, &IDatabaseService::BuildRandom, "BuildRandom"},
  30. {7, &IDatabaseService::BuildDefault, "BuildDefault"},
  31. {8, &IDatabaseService::Get2, "Get2"},
  32. {9, &IDatabaseService::Get3, "Get3"},
  33. {10, nullptr, "UpdateLatest1"},
  34. {11, &IDatabaseService::FindIndex, "FindIndex"},
  35. {12, &IDatabaseService::Move, "Move"},
  36. {13, &IDatabaseService::AddOrReplace, "AddOrReplace"},
  37. {14, &IDatabaseService::Delete, "Delete"},
  38. {15, &IDatabaseService::DestroyFile, "DestroyFile"},
  39. {16, &IDatabaseService::DeleteFile, "DeleteFile"},
  40. {17, &IDatabaseService::Format, "Format"},
  41. {18, nullptr, "Import"},
  42. {19, nullptr, "Export"},
  43. {20, nullptr, "IsBrokenDatabaseWithClearFlag"},
  44. {21, &IDatabaseService::GetIndex, "GetIndex"},
  45. {22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"},
  46. {23, nullptr, "Convert"},
  47. {24, nullptr, "ConvertCoreDataToCharInfo"},
  48. {25, nullptr, "ConvertCharInfoToCoreData"},
  49. };
  50. // clang-format on
  51. RegisterHandlers(functions);
  52. }
  53. private:
  54. template <typename OutType>
  55. std::vector<u8> SerializeArray(OutType (MiiManager::*getter)(u32) const, u32 offset,
  56. u32 requested_size, u32& read_size) {
  57. read_size = std::min(requested_size, db.Size() - offset);
  58. std::vector<u8> out(read_size * sizeof(OutType));
  59. for (u32 i = 0; i < read_size; ++i) {
  60. const auto obj = (db.*getter)(offset + i);
  61. std::memcpy(out.data() + i * sizeof(OutType), &obj, sizeof(OutType));
  62. }
  63. return out;
  64. }
  65. void IsUpdated(Kernel::HLERequestContext& ctx) {
  66. IPC::RequestParser rp{ctx};
  67. const auto source{rp.PopRaw<Source>()};
  68. LOG_DEBUG(Service_Mii, "called with source={}", source);
  69. IPC::ResponseBuilder rb{ctx, 3};
  70. rb.Push(RESULT_SUCCESS);
  71. rb.Push(db.CheckUpdatedFlag());
  72. db.ResetUpdatedFlag();
  73. }
  74. void IsFullDatabase(Kernel::HLERequestContext& ctx) {
  75. LOG_DEBUG(Service_Mii, "called");
  76. IPC::ResponseBuilder rb{ctx, 3};
  77. rb.Push(RESULT_SUCCESS);
  78. rb.Push(db.Full());
  79. }
  80. void GetCount(Kernel::HLERequestContext& ctx) {
  81. IPC::RequestParser rp{ctx};
  82. const auto source{rp.PopRaw<Source>()};
  83. LOG_DEBUG(Service_Mii, "called with source={}", source);
  84. IPC::ResponseBuilder rb{ctx, 3};
  85. rb.Push(RESULT_SUCCESS);
  86. rb.Push<u32>(db.Size());
  87. }
  88. // Gets Miis from database at offset and index in format MiiInfoElement
  89. void Get(Kernel::HLERequestContext& ctx) {
  90. IPC::RequestParser rp{ctx};
  91. const auto size{rp.PopRaw<u32>()};
  92. const auto source{rp.PopRaw<Source>()};
  93. LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
  94. offsets[0], source);
  95. u32 read_size{};
  96. ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfoElement, offsets[0], size, read_size));
  97. offsets[0] += read_size;
  98. IPC::ResponseBuilder rb{ctx, 3};
  99. rb.Push(RESULT_SUCCESS);
  100. rb.Push<u32>(read_size);
  101. }
  102. // Gets Miis from database at offset and index in format MiiInfo
  103. void Get1(Kernel::HLERequestContext& ctx) {
  104. IPC::RequestParser rp{ctx};
  105. const auto size{rp.PopRaw<u32>()};
  106. const auto source{rp.PopRaw<Source>()};
  107. LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
  108. offsets[1], source);
  109. u32 read_size{};
  110. ctx.WriteBuffer(SerializeArray(&MiiManager::GetInfo, offsets[1], size, read_size));
  111. offsets[1] += read_size;
  112. IPC::ResponseBuilder rb{ctx, 3};
  113. rb.Push(RESULT_SUCCESS);
  114. rb.Push<u32>(read_size);
  115. }
  116. void BuildRandom(Kernel::HLERequestContext& ctx) {
  117. IPC::RequestParser rp{ctx};
  118. const auto [unknown1, unknown2, unknown3] = rp.PopRaw<RandomParameters>();
  119. if (unknown1 > 3) {
  120. IPC::ResponseBuilder rb{ctx, 2};
  121. rb.Push(ERROR_INVALID_ARGUMENT);
  122. LOG_ERROR(Service_Mii, "Invalid unknown1 value: {}", unknown1);
  123. return;
  124. }
  125. if (unknown2 > 2) {
  126. IPC::ResponseBuilder rb{ctx, 2};
  127. rb.Push(ERROR_INVALID_ARGUMENT);
  128. LOG_ERROR(Service_Mii, "Invalid unknown2 value: {}", unknown2);
  129. return;
  130. }
  131. if (unknown3 > 3) {
  132. IPC::ResponseBuilder rb{ctx, 2};
  133. rb.Push(ERROR_INVALID_ARGUMENT);
  134. LOG_ERROR(Service_Mii, "Invalid unknown3 value: {}", unknown3);
  135. return;
  136. }
  137. LOG_DEBUG(Service_Mii, "called with param_1={:08X}, param_2={:08X}, param_3={:08X}",
  138. unknown1, unknown2, unknown3);
  139. const auto info = db.CreateRandom({unknown1, unknown2, unknown3});
  140. IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
  141. rb.Push(RESULT_SUCCESS);
  142. rb.PushRaw<MiiInfo>(info);
  143. }
  144. void BuildDefault(Kernel::HLERequestContext& ctx) {
  145. IPC::RequestParser rp{ctx};
  146. const auto index{rp.PopRaw<u32>()};
  147. if (index > 5) {
  148. LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}",
  149. index);
  150. IPC::ResponseBuilder rb{ctx, 2};
  151. rb.Push(ERROR_INVALID_ARGUMENT);
  152. return;
  153. }
  154. LOG_DEBUG(Service_Mii, "called with index={:08X}", index);
  155. const auto info = db.CreateDefault(index);
  156. IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
  157. rb.Push(RESULT_SUCCESS);
  158. rb.PushRaw<MiiInfo>(info);
  159. }
  160. // Gets Miis from database at offset and index in format MiiStoreDataElement
  161. void Get2(Kernel::HLERequestContext& ctx) {
  162. IPC::RequestParser rp{ctx};
  163. const auto size{rp.PopRaw<u32>()};
  164. const auto source{rp.PopRaw<Source>()};
  165. LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
  166. offsets[2], source);
  167. u32 read_size{};
  168. ctx.WriteBuffer(
  169. SerializeArray(&MiiManager::GetStoreDataElement, offsets[2], size, read_size));
  170. offsets[2] += read_size;
  171. IPC::ResponseBuilder rb{ctx, 3};
  172. rb.Push(RESULT_SUCCESS);
  173. rb.Push<u32>(read_size);
  174. }
  175. // Gets Miis from database at offset and index in format MiiStoreData
  176. void Get3(Kernel::HLERequestContext& ctx) {
  177. IPC::RequestParser rp{ctx};
  178. const auto size{rp.PopRaw<u32>()};
  179. const auto source{rp.PopRaw<Source>()};
  180. LOG_DEBUG(Service_Mii, "called with size={:08X}, offset={:08X}, source={}", size,
  181. offsets[3], source);
  182. u32 read_size{};
  183. ctx.WriteBuffer(SerializeArray(&MiiManager::GetStoreData, offsets[3], size, read_size));
  184. offsets[3] += read_size;
  185. IPC::ResponseBuilder rb{ctx, 3};
  186. rb.Push(RESULT_SUCCESS);
  187. rb.Push<u32>(read_size);
  188. }
  189. void FindIndex(Kernel::HLERequestContext& ctx) {
  190. IPC::RequestParser rp{ctx};
  191. const auto uuid{rp.PopRaw<Common::UUID>()};
  192. const auto unknown{rp.PopRaw<bool>()};
  193. LOG_DEBUG(Service_Mii, "called with uuid={}, unknown={}", uuid.FormatSwitch(), unknown);
  194. IPC::ResponseBuilder rb{ctx, 3};
  195. const auto index = db.IndexOf(uuid);
  196. if (index > MAX_MIIS) {
  197. // TODO(DarkLordZach): Find a better error code
  198. rb.Push(ResultCode(-1));
  199. rb.Push(index);
  200. } else {
  201. rb.Push(RESULT_SUCCESS);
  202. rb.Push(index);
  203. }
  204. }
  205. void Move(Kernel::HLERequestContext& ctx) {
  206. IPC::RequestParser rp{ctx};
  207. const auto uuid{rp.PopRaw<Common::UUID>()};
  208. const auto index{rp.PopRaw<s32>()};
  209. if (index < 0) {
  210. LOG_ERROR(Service_Mii, "Index cannot be negative but is {:08X}!", index);
  211. IPC::ResponseBuilder rb{ctx, 2};
  212. rb.Push(ERROR_INVALID_ARGUMENT);
  213. return;
  214. }
  215. LOG_DEBUG(Service_Mii, "called with uuid={}, index={:08X}", uuid.FormatSwitch(), index);
  216. const auto success = db.Move(uuid, index);
  217. IPC::ResponseBuilder rb{ctx, 2};
  218. // TODO(DarkLordZach): Find a better error code
  219. rb.Push(success ? RESULT_SUCCESS : ResultCode(-1));
  220. }
  221. void AddOrReplace(Kernel::HLERequestContext& ctx) {
  222. IPC::RequestParser rp{ctx};
  223. const auto data{rp.PopRaw<MiiStoreData>()};
  224. LOG_DEBUG(Service_Mii, "called with Mii data uuid={}, name={}", data.uuid.FormatSwitch(),
  225. Common::UTF16ToUTF8(data.Name()));
  226. const auto success = db.AddOrReplace(data);
  227. IPC::ResponseBuilder rb{ctx, 2};
  228. // TODO(DarkLordZach): Find a better error code
  229. rb.Push(success ? RESULT_SUCCESS : ResultCode(-1));
  230. }
  231. void Delete(Kernel::HLERequestContext& ctx) {
  232. IPC::RequestParser rp{ctx};
  233. const auto uuid{rp.PopRaw<Common::UUID>()};
  234. LOG_DEBUG(Service_Mii, "called with uuid={}", uuid.FormatSwitch());
  235. const auto success = db.Remove(uuid);
  236. IPC::ResponseBuilder rb{ctx, 2};
  237. rb.Push(success ? RESULT_SUCCESS : ERROR_CANNOT_FIND_ENTRY);
  238. }
  239. void DestroyFile(Kernel::HLERequestContext& ctx) {
  240. LOG_DEBUG(Service_Mii, "called");
  241. if (!db.IsTestModeEnabled()) {
  242. LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot destory database file.");
  243. IPC::ResponseBuilder rb{ctx, 2};
  244. rb.Push(ERROR_NOT_IN_TEST_MODE);
  245. return;
  246. }
  247. IPC::ResponseBuilder rb{ctx, 3};
  248. rb.Push(RESULT_SUCCESS);
  249. rb.Push(db.DestroyFile());
  250. }
  251. void DeleteFile(Kernel::HLERequestContext& ctx) {
  252. LOG_DEBUG(Service_Mii, "called");
  253. if (!db.IsTestModeEnabled()) {
  254. LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot delete database file.");
  255. IPC::ResponseBuilder rb{ctx, 2};
  256. rb.Push(ERROR_NOT_IN_TEST_MODE);
  257. return;
  258. }
  259. IPC::ResponseBuilder rb{ctx, 3};
  260. rb.Push(RESULT_SUCCESS);
  261. rb.Push(db.DeleteFile());
  262. }
  263. void Format(Kernel::HLERequestContext& ctx) {
  264. LOG_DEBUG(Service_Mii, "called");
  265. db.Clear();
  266. IPC::ResponseBuilder rb{ctx, 2};
  267. rb.Push(RESULT_SUCCESS);
  268. }
  269. void GetIndex(Kernel::HLERequestContext& ctx) {
  270. IPC::RequestParser rp{ctx};
  271. const auto info{rp.PopRaw<MiiInfo>()};
  272. LOG_DEBUG(Service_Mii, "called with Mii info uuid={}, name={}", info.uuid.FormatSwitch(),
  273. Common::UTF16ToUTF8(info.Name()));
  274. const auto index = db.IndexOf(info);
  275. IPC::ResponseBuilder rb{ctx, 2};
  276. rb.Push(RESULT_SUCCESS);
  277. rb.Push(index);
  278. }
  279. void SetInterfaceVersion(Kernel::HLERequestContext& ctx) {
  280. IPC::RequestParser rp{ctx};
  281. current_interface_version = rp.PopRaw<u32>();
  282. LOG_DEBUG(Service_Mii, "called, interface_version={:08X}", current_interface_version);
  283. UNIMPLEMENTED_IF(current_interface_version != 1);
  284. IPC::ResponseBuilder rb{ctx, 2};
  285. rb.Push(RESULT_SUCCESS);
  286. }
  287. MiiManager db;
  288. u32 current_interface_version = 0;
  289. // Last read offsets of Get functions
  290. std::array<u32, 4> offsets{};
  291. };
  292. class MiiDBModule final : public ServiceFramework<MiiDBModule> {
  293. public:
  294. explicit MiiDBModule(const char* name) : ServiceFramework{name} {
  295. // clang-format off
  296. static const FunctionInfo functions[] = {
  297. {0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"},
  298. };
  299. // clang-format on
  300. RegisterHandlers(functions);
  301. }
  302. private:
  303. void GetDatabaseService(Kernel::HLERequestContext& ctx) {
  304. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  305. rb.Push(RESULT_SUCCESS);
  306. rb.PushIpcInterface<IDatabaseService>();
  307. LOG_DEBUG(Service_Mii, "called");
  308. }
  309. };
  310. class MiiImg final : public ServiceFramework<MiiImg> {
  311. public:
  312. explicit MiiImg() : ServiceFramework{"miiimg"} {
  313. // clang-format off
  314. static const FunctionInfo functions[] = {
  315. {0, nullptr, "Initialize"},
  316. {10, nullptr, "Reload"},
  317. {11, nullptr, "GetCount"},
  318. {12, nullptr, "IsEmpty"},
  319. {13, nullptr, "IsFull"},
  320. {14, nullptr, "GetAttribute"},
  321. {15, nullptr, "LoadImage"},
  322. {16, nullptr, "AddOrUpdateImage"},
  323. {17, nullptr, "DeleteImages"},
  324. {100, nullptr, "DeleteFile"},
  325. {101, nullptr, "DestroyFile"},
  326. {102, nullptr, "ImportFile"},
  327. {103, nullptr, "ExportFile"},
  328. {104, nullptr, "ForceInitialize"},
  329. };
  330. // clang-format on
  331. RegisterHandlers(functions);
  332. }
  333. };
  334. void InstallInterfaces(SM::ServiceManager& sm) {
  335. std::make_shared<MiiDBModule>("mii:e")->InstallAsService(sm);
  336. std::make_shared<MiiDBModule>("mii:u")->InstallAsService(sm);
  337. std::make_shared<MiiImg>()->InstallAsService(sm);
  338. }
  339. } // namespace Service::Mii