diff options
| author | 2018-12-27 20:54:44 -0500 | |
|---|---|---|
| committer | 2019-04-25 08:07:57 -0400 | |
| commit | 1aa2b99a982e83022c9aae23c6a47eae119d21a4 (patch) | |
| tree | 33f5c35625557c73998d48ca8f0d26dd0f986d84 | |
| parent | mii: Implement IsUpdated command (IPC 0) (diff) | |
| download | yuzu-1aa2b99a982e83022c9aae23c6a47eae119d21a4.tar.gz yuzu-1aa2b99a982e83022c9aae23c6a47eae119d21a4.tar.xz yuzu-1aa2b99a982e83022c9aae23c6a47eae119d21a4.zip | |
mii: Implement Delete and Destroy file
| -rw-r--r-- | src/common/uuid.h | 11 | ||||
| -rw-r--r-- | src/core/hle/service/mii/mii.cpp | 87 | ||||
| -rw-r--r-- | src/core/hle/service/mii/mii_manager.cpp | 27 | ||||
| -rw-r--r-- | src/core/hle/service/mii/mii_manager.h | 10 |
4 files changed, 122 insertions, 13 deletions
diff --git a/src/common/uuid.h b/src/common/uuid.h index b8864b34f..f6ad064fb 100644 --- a/src/common/uuid.h +++ b/src/common/uuid.h | |||
| @@ -19,15 +19,16 @@ struct UUID { | |||
| 19 | constexpr explicit UUID(const u128& id) : uuid{id} {} | 19 | constexpr explicit UUID(const u128& id) : uuid{id} {} |
| 20 | constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} | 20 | constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {} |
| 21 | 21 | ||
| 22 | explicit operator bool() const { | 22 | constexpr explicit operator bool() const { |
| 23 | return uuid != INVALID_UUID; | 23 | return uuid[0] != INVALID_UUID[0] && uuid[1] != INVALID_UUID[1]; |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | bool operator==(const UUID& rhs) const { | 26 | constexpr bool operator==(const UUID& rhs) const { |
| 27 | return uuid == rhs.uuid; | 27 | // TODO(DarkLordZach): Replace with uuid == rhs.uuid with C++20 |
| 28 | return uuid[0] == rhs.uuid[0] && uuid[1] == rhs.uuid[1]; | ||
| 28 | } | 29 | } |
| 29 | 30 | ||
| 30 | bool operator!=(const UUID& rhs) const { | 31 | constexpr bool operator!=(const UUID& rhs) const { |
| 31 | return !operator==(rhs); | 32 | return !operator==(rhs); |
| 32 | } | 33 | } |
| 33 | 34 | ||
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index 39e4e937a..ce84e25ed 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | 6 | ||
| 7 | #include <fmt/ostream.h> | ||
| 8 | |||
| 7 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 8 | #include "common/string_util.h" | 10 | #include "common/string_util.h" |
| 9 | #include "core/hle/ipc_helpers.h" | 11 | #include "core/hle/ipc_helpers.h" |
| @@ -15,6 +17,10 @@ | |||
| 15 | 17 | ||
| 16 | namespace Service::Mii { | 18 | namespace Service::Mii { |
| 17 | 19 | ||
| 20 | constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1}; | ||
| 21 | constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4}; | ||
| 22 | constexpr ResultCode ERROR_NOT_IN_TEST_MODE{ErrorModule::Mii, 99}; | ||
| 23 | |||
| 18 | class IDatabaseService final : public ServiceFramework<IDatabaseService> { | 24 | class IDatabaseService final : public ServiceFramework<IDatabaseService> { |
| 19 | public: | 25 | public: |
| 20 | explicit IDatabaseService() : ServiceFramework{"IDatabaseService"} { | 26 | explicit IDatabaseService() : ServiceFramework{"IDatabaseService"} { |
| @@ -35,8 +41,8 @@ public: | |||
| 35 | {12, &IDatabaseService::Move, "Move"}, | 41 | {12, &IDatabaseService::Move, "Move"}, |
| 36 | {13, &IDatabaseService::AddOrReplace, "AddOrReplace"}, | 42 | {13, &IDatabaseService::AddOrReplace, "AddOrReplace"}, |
| 37 | {14, &IDatabaseService::Delete, "Delete"}, | 43 | {14, &IDatabaseService::Delete, "Delete"}, |
| 38 | {15, nullptr, "DestroyFile"}, | 44 | {15, &IDatabaseService::DestroyFile, "DestroyFile"}, |
| 39 | {16, nullptr, "DeleteFile"}, | 45 | {16, &IDatabaseService::DeleteFile, "DeleteFile"}, |
| 40 | {17, &IDatabaseService::Format, "Format"}, | 46 | {17, &IDatabaseService::Format, "Format"}, |
| 41 | {18, nullptr, "Import"}, | 47 | {18, nullptr, "Import"}, |
| 42 | {19, nullptr, "Export"}, | 48 | {19, nullptr, "Export"}, |
| @@ -135,12 +141,33 @@ private: | |||
| 135 | 141 | ||
| 136 | void BuildRandom(Kernel::HLERequestContext& ctx) { | 142 | void BuildRandom(Kernel::HLERequestContext& ctx) { |
| 137 | IPC::RequestParser rp{ctx}; | 143 | IPC::RequestParser rp{ctx}; |
| 138 | const auto random_params{rp.PopRaw<RandomParameters>()}; | 144 | const auto [unknown1, unknown2, unknown3] = rp.PopRaw<RandomParameters>(); |
| 145 | |||
| 146 | if (unknown1 > 3) { | ||
| 147 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 148 | rb.Push(ERROR_INVALID_ARGUMENT); | ||
| 149 | LOG_ERROR(Service_Mii, "Invalid unknown1 value: {}", unknown1); | ||
| 150 | return; | ||
| 151 | } | ||
| 152 | |||
| 153 | if (unknown2 > 2) { | ||
| 154 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 155 | rb.Push(ERROR_INVALID_ARGUMENT); | ||
| 156 | LOG_ERROR(Service_Mii, "Invalid unknown2 value: {}", unknown2); | ||
| 157 | return; | ||
| 158 | } | ||
| 159 | |||
| 160 | if (unknown3 > 3) { | ||
| 161 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 162 | rb.Push(ERROR_INVALID_ARGUMENT); | ||
| 163 | LOG_ERROR(Service_Mii, "Invalid unknown3 value: {}", unknown3); | ||
| 164 | return; | ||
| 165 | } | ||
| 139 | 166 | ||
| 140 | LOG_DEBUG(Service_Mii, "called with param_1={:08X}, param_2={:08X}, param_3={:08X}", | 167 | LOG_DEBUG(Service_Mii, "called with param_1={:08X}, param_2={:08X}, param_3={:08X}", |
| 141 | random_params.unknown_1, random_params.unknown_2, random_params.unknown_3); | 168 | unknown1, unknown2, unknown3); |
| 142 | 169 | ||
| 143 | const auto info = db.CreateRandom(random_params); | 170 | const auto info = db.CreateRandom({unknown1, unknown2, unknown3}); |
| 144 | IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)}; | 171 | IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)}; |
| 145 | rb.Push(RESULT_SUCCESS); | 172 | rb.Push(RESULT_SUCCESS); |
| 146 | rb.PushRaw<MiiInfo>(info); | 173 | rb.PushRaw<MiiInfo>(info); |
| @@ -150,6 +177,14 @@ private: | |||
| 150 | IPC::RequestParser rp{ctx}; | 177 | IPC::RequestParser rp{ctx}; |
| 151 | const auto index{rp.PopRaw<u32>()}; | 178 | const auto index{rp.PopRaw<u32>()}; |
| 152 | 179 | ||
| 180 | if (index > 5) { | ||
| 181 | LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}", | ||
| 182 | index); | ||
| 183 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 184 | rb.Push(ERROR_INVALID_ARGUMENT); | ||
| 185 | return; | ||
| 186 | } | ||
| 187 | |||
| 153 | LOG_DEBUG(Service_Mii, "called with index={:08X}", index); | 188 | LOG_DEBUG(Service_Mii, "called with index={:08X}", index); |
| 154 | 189 | ||
| 155 | const auto info = db.CreateDefault(index); | 190 | const auto info = db.CreateDefault(index); |
| @@ -218,7 +253,14 @@ private: | |||
| 218 | void Move(Kernel::HLERequestContext& ctx) { | 253 | void Move(Kernel::HLERequestContext& ctx) { |
| 219 | IPC::RequestParser rp{ctx}; | 254 | IPC::RequestParser rp{ctx}; |
| 220 | const auto uuid{rp.PopRaw<Common::UUID>()}; | 255 | const auto uuid{rp.PopRaw<Common::UUID>()}; |
| 221 | const auto index{rp.PopRaw<u32>()}; | 256 | const auto index{rp.PopRaw<s32>()}; |
| 257 | |||
| 258 | if (index < 0) { | ||
| 259 | LOG_ERROR(Service_Mii, "Index cannot be negative but is {:08X}!", index); | ||
| 260 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 261 | rb.Push(ERROR_INVALID_ARGUMENT); | ||
| 262 | return; | ||
| 263 | } | ||
| 222 | 264 | ||
| 223 | LOG_DEBUG(Service_Mii, "called with uuid={}, index={:08X}", uuid.FormatSwitch(), index); | 265 | LOG_DEBUG(Service_Mii, "called with uuid={}, index={:08X}", uuid.FormatSwitch(), index); |
| 224 | 266 | ||
| @@ -252,8 +294,37 @@ private: | |||
| 252 | const auto success = db.Remove(uuid); | 294 | const auto success = db.Remove(uuid); |
| 253 | 295 | ||
| 254 | IPC::ResponseBuilder rb{ctx, 2}; | 296 | IPC::ResponseBuilder rb{ctx, 2}; |
| 255 | // TODO(DarkLordZach): Find a better error code | 297 | rb.Push(success ? RESULT_SUCCESS : ERROR_CANNOT_FIND_ENTRY); |
| 256 | rb.Push(success ? RESULT_SUCCESS : ResultCode(-1)); | 298 | } |
| 299 | |||
| 300 | void DestroyFile(Kernel::HLERequestContext& ctx) { | ||
| 301 | LOG_DEBUG(Service_Mii, "called"); | ||
| 302 | |||
| 303 | if (!db.IsTestModeEnabled()) { | ||
| 304 | LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot destory database file."); | ||
| 305 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 306 | rb.Push(ERROR_NOT_IN_TEST_MODE); | ||
| 307 | return; | ||
| 308 | } | ||
| 309 | |||
| 310 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 311 | rb.Push(RESULT_SUCCESS); | ||
| 312 | rb.Push(db.DestroyFile()); | ||
| 313 | } | ||
| 314 | |||
| 315 | void DeleteFile(Kernel::HLERequestContext& ctx) { | ||
| 316 | LOG_DEBUG(Service_Mii, "called"); | ||
| 317 | |||
| 318 | if (!db.IsTestModeEnabled()) { | ||
| 319 | LOG_ERROR(Service_Mii, "Database is not in test mode -- cannot delete database file."); | ||
| 320 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 321 | rb.Push(ERROR_NOT_IN_TEST_MODE); | ||
| 322 | return; | ||
| 323 | } | ||
| 324 | |||
| 325 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 326 | rb.Push(RESULT_SUCCESS); | ||
| 327 | rb.Push(db.DeleteFile()); | ||
| 257 | } | 328 | } |
| 258 | 329 | ||
| 259 | void Format(Kernel::HLERequestContext& ctx) { | 330 | void Format(Kernel::HLERequestContext& ctx) { |
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 7011ea2bd..04fc2180b 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp | |||
| @@ -32,6 +32,13 @@ constexpr MiiStoreData DEFAULT_MII = { | |||
| 32 | // Default values taken from multiple real databases | 32 | // Default values taken from multiple real databases |
| 33 | const MiiDatabase DEFAULT_MII_DATABASE{Common::MakeMagic('N', 'F', 'D', 'B'), {}, {1}, 0, 0}; | 33 | const MiiDatabase DEFAULT_MII_DATABASE{Common::MakeMagic('N', 'F', 'D', 'B'), {}, {1}, 0, 0}; |
| 34 | 34 | ||
| 35 | constexpr std::array<const char*, 4> SOURCE_NAMES{ | ||
| 36 | "Database", | ||
| 37 | "Default", | ||
| 38 | "Account", | ||
| 39 | "Friend", | ||
| 40 | }; | ||
| 41 | |||
| 35 | template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize> | 42 | template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize> |
| 36 | std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) { | 43 | std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) { |
| 37 | std::array<T, DestArraySize> out{}; | 44 | std::array<T, DestArraySize> out{}; |
| @@ -167,6 +174,11 @@ MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) { | |||
| 167 | 174 | ||
| 168 | } // namespace | 175 | } // namespace |
| 169 | 176 | ||
| 177 | std::ostream& operator<<(std::ostream& os,Source source) { | ||
| 178 | os << SOURCE_NAMES.at(static_cast<std::size_t>(source)); | ||
| 179 | return os; | ||
| 180 | } | ||
| 181 | |||
| 170 | std::u16string MiiInfo::Name() const { | 182 | std::u16string MiiInfo::Name() const { |
| 171 | return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size()); | 183 | return Common::UTF16StringFromFixedZeroTerminatedBuffer(name.data(), name.size()); |
| 172 | } | 184 | } |
| @@ -212,6 +224,10 @@ void MiiManager::ResetUpdatedFlag() { | |||
| 212 | updated_flag = false; | 224 | updated_flag = false; |
| 213 | } | 225 | } |
| 214 | 226 | ||
| 227 | bool MiiManager::IsTestModeEnabled() const { | ||
| 228 | return is_test_mode_enabled; | ||
| 229 | } | ||
| 230 | |||
| 215 | bool MiiManager::Empty() const { | 231 | bool MiiManager::Empty() const { |
| 216 | return Size() == 0; | 232 | return Size() == 0; |
| 217 | } | 233 | } |
| @@ -318,6 +334,17 @@ bool MiiManager::AddOrReplace(const MiiStoreData& data) { | |||
| 318 | return true; | 334 | return true; |
| 319 | } | 335 | } |
| 320 | 336 | ||
| 337 | bool MiiManager::DestroyFile() { | ||
| 338 | database = DEFAULT_MII_DATABASE; | ||
| 339 | updated_flag = false; | ||
| 340 | return DeleteFile(); | ||
| 341 | } | ||
| 342 | |||
| 343 | bool MiiManager::DeleteFile() { | ||
| 344 | const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + MII_SAVE_DATABASE_PATH; | ||
| 345 | return FileUtil::Exists(path) && FileUtil::Delete(path); | ||
| 346 | } | ||
| 347 | |||
| 321 | void MiiManager::WriteToFile() { | 348 | void MiiManager::WriteToFile() { |
| 322 | const auto raw_path = | 349 | const auto raw_path = |
| 323 | FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000030"; | 350 | FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000030"; |
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h index bf955930d..38ad78a0d 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h | |||
| @@ -27,6 +27,8 @@ enum class Source : u32 { | |||
| 27 | Friend = 3, | 27 | Friend = 3, |
| 28 | }; | 28 | }; |
| 29 | 29 | ||
| 30 | std::ostream& operator<<(std::ostream& os, Source source); | ||
| 31 | |||
| 30 | struct MiiInfo { | 32 | struct MiiInfo { |
| 31 | Common::UUID uuid; | 33 | Common::UUID uuid; |
| 32 | std::array<char16_t, 11> name; | 34 | std::array<char16_t, 11> name; |
| @@ -183,6 +185,8 @@ struct MiiStoreBitFields { | |||
| 183 | }; | 185 | }; |
| 184 | }; | 186 | }; |
| 185 | static_assert(sizeof(MiiStoreBitFields) == 0x1C, "MiiStoreBitFields has incorrect size."); | 187 | static_assert(sizeof(MiiStoreBitFields) == 0x1C, "MiiStoreBitFields has incorrect size."); |
| 188 | static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>, | ||
| 189 | "MiiStoreBitFields is not trivially copyable."); | ||
| 186 | 190 | ||
| 187 | struct MiiStoreData { | 191 | struct MiiStoreData { |
| 188 | // This corresponds to the above structure MiiStoreBitFields. I did it like this because the | 192 | // This corresponds to the above structure MiiStoreBitFields. I did it like this because the |
| @@ -229,6 +233,8 @@ public: | |||
| 229 | bool CheckUpdatedFlag() const; | 233 | bool CheckUpdatedFlag() const; |
| 230 | void ResetUpdatedFlag(); | 234 | void ResetUpdatedFlag(); |
| 231 | 235 | ||
| 236 | bool IsTestModeEnabled() const; | ||
| 237 | |||
| 232 | bool Empty() const; | 238 | bool Empty() const; |
| 233 | bool Full() const; | 239 | bool Full() const; |
| 234 | 240 | ||
| @@ -248,6 +254,9 @@ public: | |||
| 248 | bool Move(Common::UUID uuid, u32 new_index); | 254 | bool Move(Common::UUID uuid, u32 new_index); |
| 249 | bool AddOrReplace(const MiiStoreData& data); | 255 | bool AddOrReplace(const MiiStoreData& data); |
| 250 | 256 | ||
| 257 | bool DestroyFile(); | ||
| 258 | bool DeleteFile(); | ||
| 259 | |||
| 251 | private: | 260 | private: |
| 252 | void WriteToFile(); | 261 | void WriteToFile(); |
| 253 | void ReadFromFile(); | 262 | void ReadFromFile(); |
| @@ -258,6 +267,7 @@ private: | |||
| 258 | 267 | ||
| 259 | MiiDatabase database; | 268 | MiiDatabase database; |
| 260 | bool updated_flag = false; | 269 | bool updated_flag = false; |
| 270 | bool is_test_mode_enabled = false; | ||
| 261 | }; | 271 | }; |
| 262 | 272 | ||
| 263 | }; // namespace Service::Mii | 273 | }; // namespace Service::Mii |