| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501 |
- using LibHac;
- using LibHac.Common;
- using LibHac.Fs;
- using LibHac.Fs.Fsa;
- using LibHac.Fs.Shim;
- using LibHac.Ncm;
- using Ryujinx.HLE.HOS.Services.Mii.Types;
- using System.Runtime.CompilerServices;
- namespace Ryujinx.HLE.HOS.Services.Mii
- {
- class MiiDatabaseManager
- {
- private static bool IsTestModeEnabled = false;
- private static uint MountCounter = 0;
- private const ulong DatabaseTestSaveDataId = 0x8000000000000031;
- private const ulong DatabaseSaveDataId = 0x8000000000000030;
- private static U8String DatabasePath = new U8String("mii:/MiiDatabase.dat");
- private static U8String MountName = new U8String("mii");
- private NintendoFigurineDatabase _database;
- private bool _isDirty;
- private HorizonClient _horizonClient;
- protected ulong UpdateCounter { get; private set; }
- public MiiDatabaseManager()
- {
- _database = new NintendoFigurineDatabase();
- _isDirty = false;
- UpdateCounter = 0;
- }
- private void ResetDatabase()
- {
- _database = new NintendoFigurineDatabase();
- _database.Format();
- }
- private void MarkDirty(DatabaseSessionMetadata metadata)
- {
- _isDirty = true;
- UpdateCounter++;
- metadata.UpdateCounter = UpdateCounter;
- }
- private bool GetAtVirtualIndex(int index, out int realIndex, out StoreData storeData)
- {
- realIndex = -1;
- storeData = new StoreData();
- int virtualIndex = 0;
- for (int i = 0; i < _database.Length; i++)
- {
- StoreData tmp = _database.Get(i);
- if (!tmp.IsSpecial())
- {
- if (index == virtualIndex)
- {
- realIndex = i;
- storeData = tmp;
- return true;
- }
- virtualIndex++;
- }
- }
- return false;
- }
- private int ConvertRealIndexToVirtualIndex(int realIndex)
- {
- int virtualIndex = 0;
- for (int i = 0; i < realIndex; i++)
- {
- StoreData tmp = _database.Get(i);
- if (!tmp.IsSpecial())
- {
- virtualIndex++;
- }
- }
- return virtualIndex;
- }
- public void InitializeDatabase(HorizonClient horizonClient)
- {
- _horizonClient = horizonClient;
- // Ensure we have valid data in the database
- _database.Format();
- MountSave();
- }
- private Result MountSave()
- {
- if (MountCounter != 0)
- {
- MountCounter++;
- return Result.Success;
- }
- ulong saveDataId = IsTestModeEnabled ? DatabaseTestSaveDataId : DatabaseSaveDataId;
- Result result = _horizonClient.Fs.MountSystemSaveData(MountName, SaveDataSpaceId.System, saveDataId);
- if (result.IsFailure())
- {
- if (!ResultFs.TargetNotFound.Includes(result))
- return result;
- if (IsTestModeEnabled)
- {
- result = _horizonClient.Fs.CreateSystemSaveData(saveDataId, 0x10000, 0x10000,
- SaveDataFlags.KeepAfterResettingSystemSaveDataWithoutUserSaveData);
- if (result.IsFailure()) return result;
- }
- else
- {
- result = _horizonClient.Fs.CreateSystemSaveData(saveDataId, SystemProgramId.Ns.Value, 0x10000,
- 0x10000, SaveDataFlags.KeepAfterResettingSystemSaveDataWithoutUserSaveData);
- if (result.IsFailure()) return result;
- }
- result = _horizonClient.Fs.MountSystemSaveData(MountName, SaveDataSpaceId.System, saveDataId);
- if (result.IsFailure()) return result;
- }
- if (result == Result.Success)
- {
- MountCounter++;
- }
- return result;
- }
- public ResultCode DeleteFile()
- {
- ResultCode result = (ResultCode)_horizonClient.Fs.DeleteFile(DatabasePath).Value;
- _horizonClient.Fs.Commit(MountName);
- return result;
- }
- public ResultCode LoadFromFile(out bool isBroken)
- {
- isBroken = false;
- if (MountCounter == 0)
- {
- return ResultCode.InvalidArgument;
- }
- UpdateCounter++;
- ResetDatabase();
- Result result = _horizonClient.Fs.OpenFile(out FileHandle handle, DatabasePath, OpenMode.Read);
- if (result.IsSuccess())
- {
- result = _horizonClient.Fs.GetFileSize(out long fileSize, handle);
- if (result.IsSuccess())
- {
- if (fileSize == Unsafe.SizeOf<NintendoFigurineDatabase>())
- {
- result = _horizonClient.Fs.ReadFile(handle, 0, _database.AsSpan());
- if (result.IsSuccess())
- {
- if (_database.Verify() != ResultCode.Success)
- {
- ResetDatabase();
- isBroken = true;
- }
- else
- {
- isBroken = _database.FixDatabase();
- }
- }
- }
- else
- {
- isBroken = true;
- }
- }
- _horizonClient.Fs.CloseFile(handle);
- return (ResultCode)result.Value;
- }
- else if (ResultFs.PathNotFound.Includes(result))
- {
- return (ResultCode)ForceSaveDatabase().Value;
- }
- return ResultCode.Success;
- }
- private Result ForceSaveDatabase()
- {
- Result result = _horizonClient.Fs.CreateFile(DatabasePath, Unsafe.SizeOf<NintendoFigurineDatabase>());
- if (result.IsSuccess() || ResultFs.PathAlreadyExists.Includes(result))
- {
- result = _horizonClient.Fs.OpenFile(out FileHandle handle, DatabasePath, OpenMode.Write);
- if (result.IsSuccess())
- {
- result = _horizonClient.Fs.GetFileSize(out long fileSize, handle);
- if (result.IsSuccess())
- {
- // If the size doesn't match, recreate the file
- if (fileSize != Unsafe.SizeOf<NintendoFigurineDatabase>())
- {
- _horizonClient.Fs.CloseFile(handle);
- result = _horizonClient.Fs.DeleteFile(DatabasePath);
- if (result.IsSuccess())
- {
- result = _horizonClient.Fs.CreateFile(DatabasePath, Unsafe.SizeOf<NintendoFigurineDatabase>());
- if (result.IsSuccess())
- {
- result = _horizonClient.Fs.OpenFile(out handle, DatabasePath, OpenMode.Write);
- }
- }
- if (result.IsFailure())
- {
- return result;
- }
- }
- result = _horizonClient.Fs.WriteFile(handle, 0, _database.AsReadOnlySpan(), WriteOption.Flush);
- }
- _horizonClient.Fs.CloseFile(handle);
- }
- }
- if (result.IsSuccess())
- {
- _isDirty = false;
- result = _horizonClient.Fs.Commit(MountName);
- }
- return result;
- }
- public DatabaseSessionMetadata CreateSessionMetadata(SpecialMiiKeyCode miiKeyCode)
- {
- return new DatabaseSessionMetadata(UpdateCounter, miiKeyCode);
- }
- public void SetInterfaceVersion(DatabaseSessionMetadata metadata, uint interfaceVersion)
- {
- metadata.InterfaceVersion = interfaceVersion;
- }
- public bool IsUpdated(DatabaseSessionMetadata metadata)
- {
- bool result = metadata.UpdateCounter != UpdateCounter;
- metadata.UpdateCounter = UpdateCounter;
- return result;
- }
- public int GetCount(DatabaseSessionMetadata metadata)
- {
- if (!metadata.MiiKeyCode.IsEnabledSpecialMii())
- {
- int count = 0;
- for (int i = 0; i < _database.Length; i++)
- {
- StoreData tmp = _database.Get(i);
- if (!tmp.IsSpecial())
- {
- count++;
- }
- }
- return count;
- }
- else
- {
- return _database.Length;
- }
- }
- public void Get(DatabaseSessionMetadata metadata, int index, out StoreData storeData)
- {
- if (!metadata.MiiKeyCode.IsEnabledSpecialMii())
- {
- if (GetAtVirtualIndex(index, out int realIndex, out _))
- {
- index = realIndex;
- }
- else
- {
- index = 0;
- }
- }
- storeData = _database.Get(index);
- }
- public ResultCode FindIndex(DatabaseSessionMetadata metadata, out int index, CreateId createId)
- {
- return FindIndex(out index, createId, metadata.MiiKeyCode.IsEnabledSpecialMii());
- }
- public ResultCode FindIndex(out int index, CreateId createId, bool isSpecial)
- {
- if (_database.GetIndexByCreatorId(out int realIndex, createId))
- {
- if (isSpecial)
- {
- index = realIndex;
- return ResultCode.Success;
- }
- StoreData storeData = _database.Get(realIndex);
- if (!storeData.IsSpecial())
- {
- if (realIndex < 1)
- {
- index = 0;
- }
- else
- {
- index = ConvertRealIndexToVirtualIndex(realIndex);
- }
- return ResultCode.Success;
- }
- }
- index = -1;
- return ResultCode.NotFound;
- }
- public ResultCode Move(DatabaseSessionMetadata metadata, int newIndex, CreateId createId)
- {
- if (!metadata.MiiKeyCode.IsEnabledSpecialMii())
- {
- if (GetAtVirtualIndex(newIndex, out int realIndex, out _))
- {
- newIndex = realIndex;
- }
- else
- {
- newIndex = 0;
- }
- }
- if (_database.GetIndexByCreatorId(out int oldIndex, createId))
- {
- StoreData realStoreData = _database.Get(oldIndex);
- if (!metadata.MiiKeyCode.IsEnabledSpecialMii() && realStoreData.IsSpecial())
- {
- return ResultCode.InvalidOperationOnSpecialMii;
- }
- ResultCode result = _database.Move(newIndex, oldIndex);
- if (result == ResultCode.Success)
- {
- MarkDirty(metadata);
- }
- return result;
- }
- return ResultCode.NotFound;
- }
- public ResultCode AddOrReplace(DatabaseSessionMetadata metadata, StoreData storeData)
- {
- if (!storeData.IsValid())
- {
- return ResultCode.InvalidStoreData;
- }
- if (!metadata.MiiKeyCode.IsEnabledSpecialMii() && storeData.IsSpecial())
- {
- return ResultCode.InvalidOperationOnSpecialMii;
- }
- if (_database.GetIndexByCreatorId(out int index, storeData.CreateId))
- {
- StoreData oldStoreData = _database.Get(index);
- if (oldStoreData.IsSpecial())
- {
- return ResultCode.InvalidOperationOnSpecialMii;
- }
- _database.Replace(index, storeData);
- }
- else
- {
- if (_database.IsFull())
- {
- return ResultCode.DatabaseFull;
- }
- _database.Add(storeData);
- }
- MarkDirty(metadata);
- return ResultCode.Success;
- }
- public ResultCode Delete(DatabaseSessionMetadata metadata, CreateId createId)
- {
- if (!_database.GetIndexByCreatorId(out int index, createId))
- {
- return ResultCode.NotFound;
- }
- if (!metadata.MiiKeyCode.IsEnabledSpecialMii())
- {
- StoreData storeData = _database.Get(index);
- if (storeData.IsSpecial())
- {
- return ResultCode.InvalidOperationOnSpecialMii;
- }
- }
- _database.Delete(index);
- MarkDirty(metadata);
- return ResultCode.Success;
- }
- public ResultCode DestroyFile(DatabaseSessionMetadata metadata)
- {
- _database.CorruptDatabase();
- MarkDirty(metadata);
- ResultCode result = SaveDatabase();
- ResetDatabase();
- return result;
- }
- public ResultCode SaveDatabase()
- {
- if (_isDirty)
- {
- return (ResultCode)ForceSaveDatabase().Value;
- }
- else
- {
- return ResultCode.NotUpdated;
- }
- }
- public void FormatDatabase(DatabaseSessionMetadata metadata)
- {
- _database.Format();
- MarkDirty(metadata);
- }
- public bool IsFullDatabase()
- {
- return _database.IsFull();
- }
- }
- }
|