mii_manager.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <cstring>
  6. #include "common/assert.h"
  7. #include "common/file_util.h"
  8. #include "common/logging/log.h"
  9. #include "common/string_util.h"
  10. #include "core/hle/service/mii/mii_manager.h"
  11. namespace Service::Mii {
  12. namespace {
  13. constexpr char MII_SAVE_DATABASE_PATH[] = "/system/save/8000000000000030/MiiDatabase.dat";
  14. constexpr std::array<char16_t, 11> DEFAULT_MII_NAME = {u'y', u'u', u'z', u'u', u'\0'};
  15. // This value was retrieved from HW test
  16. constexpr MiiStoreData DEFAULT_MII = {
  17. {
  18. 0x21, 0x40, 0x40, 0x01, 0x08, 0x01, 0x13, 0x08, 0x08, 0x02, 0x17, 0x8C, 0x06, 0x01,
  19. 0x69, 0x6D, 0x8A, 0x6A, 0x82, 0x14, 0x00, 0x00, 0x00, 0x20, 0x64, 0x72, 0x44, 0x44,
  20. },
  21. {'y', 'u', 'z', 'u', '\0'},
  22. Common::UUID{1, 0},
  23. 0,
  24. 0,
  25. };
  26. // Default values taken from multiple real databases
  27. const MiiDatabase DEFAULT_MII_DATABASE{Common::MakeMagic('N', 'F', 'D', 'B'), {}, {1}, 0, 0};
  28. constexpr std::array<const char*, 4> SOURCE_NAMES{
  29. "Database",
  30. "Default",
  31. "Account",
  32. "Friend",
  33. };
  34. template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
  35. std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
  36. std::array<T, DestArraySize> out{};
  37. std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
  38. return out;
  39. }
  40. MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
  41. MiiStoreBitFields bf{};
  42. std::memcpy(&bf, data.data.data(), sizeof(MiiStoreBitFields));
  43. return {
  44. data.uuid,
  45. ResizeArray<char16_t, 10, 11>(data.name),
  46. static_cast<u8>(bf.font_region.Value()),
  47. static_cast<u8>(bf.favorite_color.Value()),
  48. static_cast<u8>(bf.gender.Value()),
  49. static_cast<u8>(bf.height.Value()),
  50. static_cast<u8>(bf.weight.Value()),
  51. static_cast<u8>(bf.mii_type.Value()),
  52. static_cast<u8>(bf.mii_region.Value()),
  53. static_cast<u8>(bf.face_type.Value()),
  54. static_cast<u8>(bf.face_color.Value()),
  55. static_cast<u8>(bf.face_wrinkle.Value()),
  56. static_cast<u8>(bf.face_makeup.Value()),
  57. static_cast<u8>(bf.hair_type.Value()),
  58. static_cast<u8>(bf.hair_color.Value()),
  59. static_cast<bool>(bf.hair_flip.Value()),
  60. static_cast<u8>(bf.eye_type.Value()),
  61. static_cast<u8>(bf.eye_color.Value()),
  62. static_cast<u8>(bf.eye_scale.Value()),
  63. static_cast<u8>(bf.eye_aspect.Value()),
  64. static_cast<u8>(bf.eye_rotate.Value()),
  65. static_cast<u8>(bf.eye_x.Value()),
  66. static_cast<u8>(bf.eye_y.Value()),
  67. static_cast<u8>(bf.eyebrow_type.Value()),
  68. static_cast<u8>(bf.eyebrow_color.Value()),
  69. static_cast<u8>(bf.eyebrow_scale.Value()),
  70. static_cast<u8>(bf.eyebrow_aspect.Value()),
  71. static_cast<u8>(bf.eyebrow_rotate.Value()),
  72. static_cast<u8>(bf.eyebrow_x.Value()),
  73. static_cast<u8>(bf.eyebrow_y.Value()),
  74. static_cast<u8>(bf.nose_type.Value()),
  75. static_cast<u8>(bf.nose_scale.Value()),
  76. static_cast<u8>(bf.nose_y.Value()),
  77. static_cast<u8>(bf.mouth_type.Value()),
  78. static_cast<u8>(bf.mouth_color.Value()),
  79. static_cast<u8>(bf.mouth_scale.Value()),
  80. static_cast<u8>(bf.mouth_aspect.Value()),
  81. static_cast<u8>(bf.mouth_y.Value()),
  82. static_cast<u8>(bf.facial_hair_color.Value()),
  83. static_cast<u8>(bf.beard_type.Value()),
  84. static_cast<u8>(bf.mustache_type.Value()),
  85. static_cast<u8>(bf.mustache_scale.Value()),
  86. static_cast<u8>(bf.mustache_y.Value()),
  87. static_cast<u8>(bf.glasses_type.Value()),
  88. static_cast<u8>(bf.glasses_color.Value()),
  89. static_cast<u8>(bf.glasses_scale.Value()),
  90. static_cast<u8>(bf.glasses_y.Value()),
  91. static_cast<u8>(bf.mole_type.Value()),
  92. static_cast<u8>(bf.mole_scale.Value()),
  93. static_cast<u8>(bf.mole_x.Value()),
  94. static_cast<u8>(bf.mole_y.Value()),
  95. 0x00,
  96. };
  97. }
  98. MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) {
  99. MiiStoreData out{};
  100. out.name = ResizeArray<char16_t, 11, 10>(info.name);
  101. out.uuid = info.uuid;
  102. MiiStoreBitFields bf{};
  103. bf.hair_type.Assign(info.hair_type);
  104. bf.mole_type.Assign(info.mole_type);
  105. bf.height.Assign(info.height);
  106. bf.hair_flip.Assign(info.hair_flip);
  107. bf.weight.Assign(info.weight);
  108. bf.hair_color.Assign(info.hair_color);
  109. bf.gender.Assign(info.gender);
  110. bf.eye_color.Assign(info.eye_color);
  111. bf.eyebrow_color.Assign(info.eyebrow_color);
  112. bf.mouth_color.Assign(info.mouth_color);
  113. bf.facial_hair_color.Assign(info.facial_hair_color);
  114. bf.mii_type.Assign(info.mii_type);
  115. bf.glasses_color.Assign(info.glasses_color);
  116. bf.font_region.Assign(info.font_region);
  117. bf.eye_type.Assign(info.eye_type);
  118. bf.mii_region.Assign(info.mii_region);
  119. bf.mouth_type.Assign(info.mouth_type);
  120. bf.glasses_scale.Assign(info.glasses_scale);
  121. bf.eye_y.Assign(info.eye_y);
  122. bf.mustache_type.Assign(info.mustache_type);
  123. bf.eyebrow_type.Assign(info.eyebrow_type);
  124. bf.beard_type.Assign(info.beard_type);
  125. bf.nose_type.Assign(info.nose_type);
  126. bf.mouth_aspect.Assign(info.mouth_aspect_ratio);
  127. bf.nose_y.Assign(info.nose_y);
  128. bf.eyebrow_aspect.Assign(info.eyebrow_aspect_ratio);
  129. bf.mouth_y.Assign(info.mouth_y);
  130. bf.eye_rotate.Assign(info.eye_rotate);
  131. bf.mustache_y.Assign(info.mustache_y);
  132. bf.eye_aspect.Assign(info.eye_aspect_ratio);
  133. bf.glasses_y.Assign(info.glasses_y);
  134. bf.eye_scale.Assign(info.eye_scale);
  135. bf.mole_x.Assign(info.mole_x);
  136. bf.mole_y.Assign(info.mole_y);
  137. bf.glasses_type.Assign(info.glasses_type);
  138. bf.face_type.Assign(info.face_type);
  139. bf.favorite_color.Assign(info.favorite_color);
  140. bf.face_wrinkle.Assign(info.face_wrinkle);
  141. bf.face_color.Assign(info.face_color);
  142. bf.eye_x.Assign(info.eye_x);
  143. bf.face_makeup.Assign(info.face_makeup);
  144. bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
  145. bf.eyebrow_scale.Assign(info.eyebrow_scale);
  146. bf.eyebrow_y.Assign(info.eyebrow_y);
  147. bf.eyebrow_x.Assign(info.eyebrow_x);
  148. bf.mouth_scale.Assign(info.mouth_scale);
  149. bf.nose_scale.Assign(info.nose_scale);
  150. bf.mole_scale.Assign(info.mole_scale);
  151. bf.mustache_scale.Assign(info.mustache_scale);
  152. std::memcpy(out.data.data(), &bf, sizeof(MiiStoreBitFields));
  153. return out;
  154. }
  155. } // namespace
  156. std::ostream& operator<<(std::ostream& os, Source source) {
  157. os << SOURCE_NAMES.at(static_cast<std::size_t>(source));
  158. return os;
  159. }
  160. std::u16string MiiInfo::Name() const {
  161. return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
  162. }
  163. bool operator==(const MiiInfo& lhs, const MiiInfo& rhs) {
  164. return std::memcmp(&lhs, &rhs, sizeof(MiiInfo)) == 0;
  165. }
  166. bool operator!=(const MiiInfo& lhs, const MiiInfo& rhs) {
  167. return !operator==(lhs, rhs);
  168. }
  169. std::u16string MiiStoreData::Name() const {
  170. return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size());
  171. }
  172. MiiManager::MiiManager() = default;
  173. MiiManager::~MiiManager() = default;
  174. MiiInfo MiiManager::CreateRandom(RandomParameters params) {
  175. LOG_WARNING(Service_Mii,
  176. "(STUBBED) called with params={:08X}{:08X}{:08X}, returning default Mii",
  177. params.unknown_1, params.unknown_2, params.unknown_3);
  178. return ConvertStoreDataToInfo(CreateMiiWithUniqueUUID());
  179. }
  180. MiiInfo MiiManager::CreateDefault(u32 index) {
  181. const auto new_mii = CreateMiiWithUniqueUUID();
  182. database.miis.at(index) = new_mii;
  183. EnsureDatabasePartition();
  184. return ConvertStoreDataToInfo(new_mii);
  185. }
  186. bool MiiManager::CheckUpdatedFlag() const {
  187. return updated_flag;
  188. }
  189. void MiiManager::ResetUpdatedFlag() {
  190. updated_flag = false;
  191. }
  192. bool MiiManager::IsTestModeEnabled() const {
  193. return is_test_mode_enabled;
  194. }
  195. bool MiiManager::Empty() const {
  196. return Size() == 0;
  197. }
  198. bool MiiManager::Full() const {
  199. return Size() == MAX_MIIS;
  200. }
  201. void MiiManager::Clear() {
  202. updated_flag = true;
  203. std::fill(database.miis.begin(), database.miis.end(), MiiStoreData{});
  204. }
  205. u32 MiiManager::Size() const {
  206. return static_cast<u32>(std::count_if(database.miis.begin(), database.miis.end(),
  207. [](const MiiStoreData& elem) { return elem.uuid; }));
  208. }
  209. MiiInfo MiiManager::GetInfo(u32 index) const {
  210. return ConvertStoreDataToInfo(GetStoreData(index));
  211. }
  212. MiiInfoElement MiiManager::GetInfoElement(u32 index) const {
  213. return {GetInfo(index), Source::Database};
  214. }
  215. MiiStoreData MiiManager::GetStoreData(u32 index) const {
  216. return database.miis.at(index);
  217. }
  218. MiiStoreDataElement MiiManager::GetStoreDataElement(u32 index) const {
  219. return {GetStoreData(index), Source::Database};
  220. }
  221. bool MiiManager::Remove(Common::UUID uuid) {
  222. const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
  223. [uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
  224. if (iter == database.miis.end())
  225. return false;
  226. updated_flag = true;
  227. *iter = MiiStoreData{};
  228. EnsureDatabasePartition();
  229. return true;
  230. }
  231. u32 MiiManager::IndexOf(Common::UUID uuid) const {
  232. const auto iter = std::find_if(database.miis.begin(), database.miis.end(),
  233. [uuid](const MiiStoreData& elem) { return elem.uuid == uuid; });
  234. if (iter == database.miis.end())
  235. return INVALID_INDEX;
  236. return static_cast<u32>(std::distance(database.miis.begin(), iter));
  237. }
  238. u32 MiiManager::IndexOf(const MiiInfo& info) const {
  239. const auto iter =
  240. std::find_if(database.miis.begin(), database.miis.end(), [&info](const MiiStoreData& elem) {
  241. return ConvertStoreDataToInfo(elem) == info;
  242. });
  243. if (iter == database.miis.end())
  244. return INVALID_INDEX;
  245. return static_cast<u32>(std::distance(database.miis.begin(), iter));
  246. }
  247. bool MiiManager::Move(Common::UUID uuid, u32 new_index) {
  248. const auto index = IndexOf(uuid);
  249. if (index == INVALID_INDEX || new_index >= MAX_MIIS)
  250. return false;
  251. updated_flag = true;
  252. const auto moving = database.miis[index];
  253. const auto replacing = database.miis[new_index];
  254. if (replacing.uuid) {
  255. database.miis[index] = replacing;
  256. database.miis[new_index] = moving;
  257. } else {
  258. database.miis[index] = MiiStoreData{};
  259. database.miis[new_index] = moving;
  260. }
  261. EnsureDatabasePartition();
  262. return true;
  263. }
  264. bool MiiManager::AddOrReplace(const MiiStoreData& data) {
  265. const auto index = IndexOf(data.uuid);
  266. updated_flag = true;
  267. if (index == INVALID_INDEX) {
  268. const auto size = Size();
  269. if (size == MAX_MIIS)
  270. return false;
  271. database.miis[size] = data;
  272. } else {
  273. database.miis[index] = data;
  274. }
  275. return true;
  276. }
  277. bool MiiManager::DestroyFile() {
  278. database = DEFAULT_MII_DATABASE;
  279. updated_flag = false;
  280. return DeleteFile();
  281. }
  282. bool MiiManager::DeleteFile() {
  283. const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
  284. return FileUtil::Exists(path) && FileUtil::Delete(path);
  285. }
  286. void MiiManager::WriteToFile() {
  287. const auto raw_path =
  288. FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000030";
  289. if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
  290. FileUtil::Delete(raw_path);
  291. const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH;
  292. if (!FileUtil::CreateFullPath(path)) {
  293. LOG_WARNING(Service_Mii,
  294. "Failed to create full path of MiiDatabase.dat. Create the directory "
  295. "nand/system/save/8000000000000030 to mitigate this "
  296. "issue.");
  297. return;
  298. }
  299. FileUtil::IOFile save(path, "wb");
  300. if (!save.IsOpen()) {
  301. LOG_WARNING(Service_Mii, "Failed to write save data to file... No changes to user data "
  302. "made in current session will be saved.");
  303. return;
  304. }
  305. save.Resize(sizeof(MiiDatabase));
  306. if (save.WriteBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
  307. LOG_WARNING(Service_Mii, "Failed to write all data to save file... Data may be malformed "
  308. "and/or regenerated on next run.");
  309. save.Resize(0);
  310. }
  311. }
  312. void MiiManager::ReadFromFile() {
  313. FileUtil::IOFile save(
  314. FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH, "rb");
  315. if (!save.IsOpen()) {
  316. LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
  317. "blank Mii database with no Miis.");
  318. std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
  319. return;
  320. }
  321. if (save.ReadBytes(&database, sizeof(MiiDatabase)) != sizeof(MiiDatabase)) {
  322. LOG_WARNING(Service_ACC, "MiiDatabase.dat is smaller than expected... Generating new blank "
  323. "Mii database with no Miis.");
  324. std::memcpy(&database, &DEFAULT_MII_DATABASE, sizeof(MiiDatabase));
  325. return;
  326. }
  327. EnsureDatabasePartition();
  328. }
  329. MiiStoreData MiiManager::CreateMiiWithUniqueUUID() const {
  330. auto new_mii = DEFAULT_MII;
  331. do {
  332. new_mii.uuid = Common::UUID::Generate();
  333. } while (IndexOf(new_mii.uuid) != INVALID_INDEX);
  334. return new_mii;
  335. }
  336. void MiiManager::EnsureDatabasePartition() {
  337. std::stable_partition(database.miis.begin(), database.miis.end(),
  338. [](const MiiStoreData& elem) { return elem.uuid; });
  339. }
  340. } // namespace Service::Mii