mii.cpp 14 KB

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