Fixup save data indexer classes and update them for 13.1.0

- SaveDataIndexer
- SaveDataIndexerLite
- SaveDataIndexerLiteInfoReader
- SaveDataIndexerManager
- SaveDataIndexerAccessor
This commit is contained in:
Alex Barney 2022-01-15 03:02:21 -07:00
parent ecb85269eb
commit 085511660d
9 changed files with 1038 additions and 914 deletions

View file

@ -4,9 +4,9 @@ namespace LibHac.Fs;
public static class SaveData public static class SaveData
{ {
public const ulong SaveIndexerId = 0x8000000000000000; public static readonly ulong SaveIndexerId = 0x8000000000000000;
public static ProgramId InvalidProgramId => ProgramId.InvalidId; public static ProgramId InvalidProgramId => ProgramId.InvalidId;
public static ProgramId AutoResolveCallerProgramId => new ProgramId(ulong.MaxValue - 1); public static ProgramId AutoResolveCallerProgramId => new ProgramId(ulong.MaxValue - 1);
public static UserId InvalidUserId => UserId.InvalidId; public static UserId InvalidUserId => UserId.InvalidId;
public static readonly ulong InvalidSystemSaveDataId = 0;
} }

View file

@ -304,7 +304,7 @@ internal class MultiCommitManager : IMultiCommitManager
rc = saveService.OpenSaveDataIndexerAccessor(ref accessor.Ref(), out _, SaveDataSpaceId.User); rc = saveService.OpenSaveDataIndexerAccessor(ref accessor.Ref(), out _, SaveDataSpaceId.User);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.OpenSaveDataInfoReader(ref reader.Ref()); rc = accessor.Get.GetInterface().OpenSaveDataInfoReader(ref reader.Ref());
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
// Iterate through all the saves to find any provisionally committed save data // Iterate through all the saves to find any provisionally committed save data
@ -385,7 +385,7 @@ internal class MultiCommitManager : IMultiCommitManager
rc = saveService.OpenSaveDataIndexerAccessor(ref accessor.Ref(), out _, SaveDataSpaceId.User); rc = saveService.OpenSaveDataIndexerAccessor(ref accessor.Ref(), out _, SaveDataSpaceId.User);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.OpenSaveDataInfoReader(ref reader.Ref()); rc = accessor.Get.GetInterface().OpenSaveDataInfoReader(ref reader.Ref());
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
// Iterate through all the saves to find any provisionally committed save data // Iterate through all the saves to find any provisionally committed save data

View file

@ -484,7 +484,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId); Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.GetValue(out SaveDataIndexerValue value, saveDataId); rc = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue value, saveDataId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (value.SpaceId != ConvertToRealSpaceId(spaceId)) if (value.SpaceId != ConvertToRealSpaceId(spaceId))
@ -534,14 +534,14 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
} }
else else
{ {
rc = accessor.Get.Indexer.GetValue(out SaveDataIndexerValue value, saveDataId); rc = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue value, saveDataId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
actualSpaceId = value.SpaceId; actualSpaceId = value.SpaceId;
} }
// Check if the caller has permission to delete this save. // Check if the caller has permission to delete this save.
rc = accessor.Get.Indexer.GetKey(out SaveDataAttribute key, saveDataId); rc = accessor.Get.GetInterface().GetKey(out SaveDataAttribute key, saveDataId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
Result GetExtraData(out SaveDataExtraData data) => Result GetExtraData(out SaveDataExtraData data) =>
@ -552,10 +552,10 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
// Pre-delete checks successful. Put the save in the Processing state until deletion is finished. // Pre-delete checks successful. Put the save in the Processing state until deletion is finished.
rc = accessor.Get.Indexer.SetState(saveDataId, SaveDataState.Processing); rc = accessor.Get.GetInterface().SetState(saveDataId, SaveDataState.Processing);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.Commit(); rc = accessor.Get.GetInterface().Commit();
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
@ -567,10 +567,10 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
// The indexer doesn't track itself, so skip if deleting its save data. // The indexer doesn't track itself, so skip if deleting its save data.
if (saveDataId != SaveData.SaveIndexerId) if (saveDataId != SaveData.SaveIndexerId)
{ {
rc = accessor.Get.Indexer.Delete(saveDataId); rc = accessor.Get.GetInterface().Delete(saveDataId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.Commit(); rc = accessor.Get.GetInterface().Commit();
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
@ -725,7 +725,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
// If a static save data ID is specified that ID is always used // If a static save data ID is specified that ID is always used
saveDataId = attribute.StaticSaveDataId; saveDataId = attribute.StaticSaveDataId;
rc = accessor.Get.Indexer.PutStaticSaveDataIdIndex(in indexerKey); rc = accessor.Get.GetInterface().PutStaticSaveDataIdIndex(in indexerKey);
} }
else else
{ {
@ -734,14 +734,14 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
// end up in a situation where it can't create a required system save. // end up in a situation where it can't create a required system save.
if (!SaveDataProperties.CanUseIndexerReservedArea(attribute.Type)) if (!SaveDataProperties.CanUseIndexerReservedArea(attribute.Type))
{ {
if (accessor.Get.Indexer.IsRemainedReservedOnly()) if (accessor.Get.GetInterface().IsRemainedReservedOnly())
{ {
return ResultKvdb.OutOfKeyResource.Log(); return ResultKvdb.OutOfKeyResource.Log();
} }
} }
// If a static save data ID is no specified we're assigned a new save ID // If a static save data ID is no specified we're assigned a new save ID
rc = accessor.Get.Indexer.Publish(out saveDataId, in indexerKey); rc = accessor.Get.GetInterface().Publish(out saveDataId, in indexerKey);
} }
if (rc.IsFailure()) if (rc.IsFailure())
@ -757,19 +757,19 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
creating = true; creating = true;
// Set the state, space ID and size on the new save indexer entry. // Set the state, space ID and size on the new save indexer entry.
rc = accessor.Get.Indexer.SetState(saveDataId, SaveDataState.Processing); rc = accessor.Get.GetInterface().SetState(saveDataId, SaveDataState.Processing);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.SetSpaceId(saveDataId, ConvertToRealSpaceId(creationInfo.SpaceId)); rc = accessor.Get.GetInterface().SetSpaceId(saveDataId, ConvertToRealSpaceId(creationInfo.SpaceId));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = QuerySaveDataTotalSize(out long saveDataSize, creationInfo.Size, creationInfo.JournalSize); rc = QuerySaveDataTotalSize(out long saveDataSize, creationInfo.Size, creationInfo.JournalSize);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.SetSize(saveDataId, saveDataSize); rc = accessor.Get.GetInterface().SetSize(saveDataId, saveDataSize);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.Commit(); rc = accessor.Get.GetInterface().Commit();
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
@ -826,10 +826,10 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (attribute.StaticSaveDataId != SaveData.SaveIndexerId) if (attribute.StaticSaveDataId != SaveData.SaveIndexerId)
{ {
// Mark the save data as being successfully created // Mark the save data as being successfully created
rc = accessor.Get.Indexer.SetState(saveDataId, SaveDataState.Normal); rc = accessor.Get.GetInterface().SetState(saveDataId, SaveDataState.Normal);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.Commit(); rc = accessor.Get.GetInterface().Commit();
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
@ -845,12 +845,12 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (accessorInitialized && saveDataId != SaveData.SaveIndexerId) if (accessorInitialized && saveDataId != SaveData.SaveIndexerId)
{ {
rc = accessor.Get.Indexer.GetValue(out SaveDataIndexerValue value, saveDataId); rc = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue value, saveDataId);
if (rc.IsSuccess() && value.SpaceId == creationInfo.SpaceId) if (rc.IsSuccess() && value.SpaceId == creationInfo.SpaceId)
{ {
accessor.Get.Indexer.Delete(saveDataId).IgnoreResult(); accessor.Get.GetInterface().Delete(saveDataId).IgnoreResult();
accessor.Get.Indexer.Commit().IgnoreResult(); accessor.Get.GetInterface().Commit().IgnoreResult();
} }
} }
} }
@ -867,7 +867,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId); Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.Get(out SaveDataIndexerValue value, in attribute); rc = accessor.Get.GetInterface().Get(out SaveDataIndexerValue value, in attribute);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
SaveDataIndexer.GenerateSaveDataInfo(out info, in attribute, in value); SaveDataIndexer.GenerateSaveDataInfo(out info, in attribute, in value);
@ -1007,7 +1007,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId); Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.Get(out SaveDataIndexerValue indexerValue, in attribute); rc = accessor.Get.GetInterface().Get(out SaveDataIndexerValue indexerValue, in attribute);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (indexerValue.SpaceId != ConvertToRealSpaceId(spaceId)) if (indexerValue.SpaceId != ConvertToRealSpaceId(spaceId))
@ -1062,7 +1062,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
// Check the space ID of the save data // Check the space ID of the save data
rc = accessor.Get.Indexer.Get(out SaveDataIndexerValue value, in key); rc = accessor.Get.GetInterface().Get(out SaveDataIndexerValue value, in key);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (value.SpaceId != ConvertToRealSpaceId(spaceId)) if (value.SpaceId != ConvertToRealSpaceId(spaceId))
@ -1070,8 +1070,8 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
} }
// Remove the indexer entry. Nintendo ignores these results // Remove the indexer entry. Nintendo ignores these results
accessor.Get.Indexer.Delete(tempSaveDataId).IgnoreResult(); accessor.Get.GetInterface().Delete(tempSaveDataId).IgnoreResult();
accessor.Get.Indexer.Commit().IgnoreResult(); accessor.Get.GetInterface().Commit().IgnoreResult();
return Result.Success; return Result.Success;
} }
@ -1275,7 +1275,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId); Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.GetKey(out SaveDataAttribute key, saveDataId); rc = accessor.Get.GetInterface().GetKey(out SaveDataAttribute key, saveDataId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath(); using Path saveDataRootPath = _saveDataRootPath.DangerousGetPath();
@ -1311,12 +1311,12 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
rc = accessor.Get.Indexer.GetValue(out SaveDataIndexerValue value, saveDataId); rc = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue value, saveDataId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
resolvedSpaceId = value.SpaceId; resolvedSpaceId = value.SpaceId;
rc = accessor.Get.Indexer.GetKey(out key, saveDataId); rc = accessor.Get.GetInterface().GetKey(out key, saveDataId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
else else
@ -1326,12 +1326,12 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId); rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.GetValue(out SaveDataIndexerValue value, saveDataId); rc = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue value, saveDataId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
resolvedSpaceId = value.SpaceId; resolvedSpaceId = value.SpaceId;
rc = accessor.Get.Indexer.GetKey(out key, saveDataId); rc = accessor.Get.GetInterface().GetKey(out key, saveDataId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
@ -1462,7 +1462,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId); rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.GetKey(out SaveDataAttribute key, saveDataId); rc = accessor.Get.GetInterface().GetKey(out SaveDataAttribute key, saveDataId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
Result ReadExtraData(out SaveDataExtraData data) => _serviceImpl.ReadSaveDataFileSystemExtraData(out data, Result ReadExtraData(out SaveDataExtraData data) => _serviceImpl.ReadSaveDataFileSystemExtraData(out data,
@ -1554,7 +1554,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), SaveDataSpaceId.System); rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), SaveDataSpaceId.System);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.OpenSaveDataInfoReader(ref reader.Ref()); rc = accessor.Get.GetInterface().OpenSaveDataInfoReader(ref reader.Ref());
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
@ -1583,7 +1583,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var reader = new SharedRef<SaveDataInfoReaderImpl>(); using var reader = new SharedRef<SaveDataInfoReaderImpl>();
rc = accessor.Get.Indexer.OpenSaveDataInfoReader(ref reader.Ref()); rc = accessor.Get.GetInterface().OpenSaveDataInfoReader(ref reader.Ref());
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
var filter = new SaveDataInfoFilter(ConvertToRealSpaceId(spaceId), programId: default, var filter = new SaveDataInfoFilter(ConvertToRealSpaceId(spaceId), programId: default,
@ -1620,7 +1620,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
using var reader = new SharedRef<SaveDataInfoReaderImpl>(); using var reader = new SharedRef<SaveDataInfoReaderImpl>();
rc = accessor.Get.Indexer.OpenSaveDataInfoReader(ref reader.Ref()); rc = accessor.Get.GetInterface().OpenSaveDataInfoReader(ref reader.Ref());
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
var infoFilter = new SaveDataInfoFilter(ConvertToRealSpaceId(spaceId), in filter); var infoFilter = new SaveDataInfoFilter(ConvertToRealSpaceId(spaceId), in filter);
@ -1644,7 +1644,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId); Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.OpenSaveDataInfoReader(ref reader.Ref()); rc = accessor.Get.GetInterface().OpenSaveDataInfoReader(ref reader.Ref());
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
using var filterReader = using var filterReader =
@ -1762,7 +1762,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave
rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId); rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Get.Indexer.OpenSaveDataInfoReader(ref reader.Ref()); rc = accessor.Get.GetInterface().OpenSaveDataInfoReader(ref reader.Ref());
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
ProgramId resolvedProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId); ProgramId resolvedProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId);

View file

@ -803,7 +803,7 @@ public class SaveDataFileSystemServiceImpl
Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), out bool _, SaveDataSpaceId.User); Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), out bool _, SaveDataSpaceId.User);
if (rc.IsFailure()) return rc.Miss(); if (rc.IsFailure()) return rc.Miss();
count = accessor.Get.Indexer.GetIndexCount(); count = accessor.Get.GetInterface().GetIndexCount();
return Result.Success; return Result.Success;
} }

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,66 @@
using System; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Os;
using LibHac.Sf; using LibHac.Sf;
using LibHac.Util;
namespace LibHac.FsSrv; namespace LibHac.FsSrv;
/// <summary>
/// Iterates through all the save data indexed in a <see cref="SaveDataIndexerLite"/>.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
internal class SaveDataIndexerLiteInfoReader : SaveDataInfoReaderImpl
{
private bool _finishedIterating;
private SaveDataInfo _info;
public SaveDataIndexerLiteInfoReader()
{
_finishedIterating = true;
_info = default;
}
public void Dispose() { }
public SaveDataIndexerLiteInfoReader(in SaveDataAttribute key, in SaveDataIndexerValue value)
{
_finishedIterating = false;
_info = default;
// Don't set the State, Index, or Rank of the returned SaveDataInfo
_info.SaveDataId = value.SaveDataId;
_info.SpaceId = value.SpaceId;
_info.Size = value.Size;
_info.StaticSaveDataId = key.StaticSaveDataId;
_info.ProgramId = key.ProgramId;
_info.Type = key.Type;
_info.UserId = key.UserId;
}
public Result Read(out long readCount, OutBuffer saveDataInfoBuffer)
{
UnsafeHelpers.SkipParamInit(out readCount);
if (_finishedIterating || saveDataInfoBuffer.Size == 0)
{
readCount = 0;
return Result.Success;
}
if (saveDataInfoBuffer.Size < Unsafe.SizeOf<SaveDataInfo>())
return ResultFs.InvalidSize.Log();
Unsafe.As<byte, SaveDataInfo>(ref MemoryMarshal.GetReference(saveDataInfoBuffer.Buffer)) = _info;
readCount = 1;
_finishedIterating = true;
return Result.Success;
}
}
/// <summary> /// <summary>
/// Indexes metadata for temporary save data, holding a key-value pair of types /// Indexes metadata for temporary save data, holding a key-value pair of types
/// <see cref="SaveDataAttribute"/> and <see cref="SaveDataIndexerValue"/> respectively. /// <see cref="SaveDataAttribute"/> and <see cref="SaveDataIndexerValue"/> respectively.
@ -13,87 +68,89 @@ namespace LibHac.FsSrv;
/// <remarks> /// <remarks>
/// Only one temporary save data may exist at a time. When a new /// Only one temporary save data may exist at a time. When a new
/// save data is added to the index, the existing key-value pair is replaced. /// save data is added to the index, the existing key-value pair is replaced.
/// <br/>Based on FS 10.0.0 (nnSdk 10.4.0) /// <para>Based on FS 13.1.0 (nnSdk 13.4.0)</para>
/// </remarks> /// </remarks>
public class SaveDataIndexerLite : ISaveDataIndexer public class SaveDataIndexerLite : ISaveDataIndexer
{ {
private object Locker { get; } = new object(); private SdkMutex _mutex;
private ulong CurrentSaveDataId { get; set; } = 0x4000000000000000; private ulong _nextSaveDataId;
private Optional<SaveDataAttribute> _key;
// Todo: Use Optional<T>
private bool IsKeyValueSet { get; set; }
private SaveDataAttribute _key;
private SaveDataIndexerValue _value; private SaveDataIndexerValue _value;
public SaveDataIndexerLite()
{
_mutex = new SdkMutex();
_nextSaveDataId = 0x4000000000000000;
}
public void Dispose() { }
public Result Commit() public Result Commit()
{ {
using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
return Result.Success; return Result.Success;
} }
public Result Rollback() public Result Rollback()
{ {
using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
return Result.Success; return Result.Success;
} }
public Result Reset() public Result Reset()
{ {
lock (Locker) using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
{
IsKeyValueSet = false; _key.Clear();
return Result.Success; return Result.Success;
}
} }
public Result Publish(out ulong saveDataId, in SaveDataAttribute key) public Result Publish(out ulong saveDataId, in SaveDataAttribute key)
{ {
UnsafeHelpers.SkipParamInit(out saveDataId); UnsafeHelpers.SkipParamInit(out saveDataId);
lock (Locker) using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
{
if (IsKeyValueSet && _key == key)
return ResultFs.AlreadyExists.Log();
_key = key; if (_key.HasValue && key == _key.ValueRo)
IsKeyValueSet = true; return ResultFs.AlreadyExists.Log();
_value = new SaveDataIndexerValue { SaveDataId = CurrentSaveDataId }; _key.Set(in key);
saveDataId = CurrentSaveDataId;
CurrentSaveDataId++;
return Result.Success; saveDataId = _nextSaveDataId;
} _value = new SaveDataIndexerValue { SaveDataId = _nextSaveDataId };
_nextSaveDataId++;
return Result.Success;
} }
public Result Get(out SaveDataIndexerValue value, in SaveDataAttribute key) public Result Get(out SaveDataIndexerValue value, in SaveDataAttribute key)
{ {
UnsafeHelpers.SkipParamInit(out value); UnsafeHelpers.SkipParamInit(out value);
lock (Locker) using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
{
if (IsKeyValueSet && _key == key)
{
value = _value;
return Result.Success;
}
return ResultFs.TargetNotFound.Log(); if (_key.HasValue && key == _key.ValueRo)
{
value = _value;
return Result.Success;
} }
return ResultFs.TargetNotFound.Log();
} }
public Result PutStaticSaveDataIdIndex(in SaveDataAttribute key) public Result PutStaticSaveDataIdIndex(in SaveDataAttribute key)
{ {
lock (Locker) using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
{
if (IsKeyValueSet && _key == key)
return ResultFs.AlreadyExists.Log();
_key = key; if (_key.HasValue && key == _key.ValueRo)
IsKeyValueSet = true; return ResultFs.AlreadyExists.Log();
_value = new SaveDataIndexerValue(); _key.Set(in key);
return Result.Success; _value = default;
}
return Result.Success;
} }
public bool IsRemainedReservedOnly() public bool IsRemainedReservedOnly()
@ -103,106 +160,99 @@ public class SaveDataIndexerLite : ISaveDataIndexer
public Result Delete(ulong saveDataId) public Result Delete(ulong saveDataId)
{ {
lock (Locker) using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
{
if (IsKeyValueSet && _value.SaveDataId == saveDataId)
{
IsKeyValueSet = false;
return Result.Success;
}
return ResultFs.TargetNotFound.Log(); if (_key.HasValue && saveDataId == _value.SaveDataId)
{
_key.Clear();
return Result.Success;
} }
return ResultFs.TargetNotFound.Log();
} }
public Result SetSpaceId(ulong saveDataId, SaveDataSpaceId spaceId) public Result SetSpaceId(ulong saveDataId, SaveDataSpaceId spaceId)
{ {
lock (Locker) using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
{
if (IsKeyValueSet && _value.SaveDataId == saveDataId)
{
_value.SpaceId = spaceId;
return Result.Success;
}
return ResultFs.TargetNotFound.Log(); if (_key.HasValue && saveDataId == _value.SaveDataId)
{
_value.SpaceId = spaceId;
return Result.Success;
} }
return ResultFs.TargetNotFound.Log();
} }
public Result SetSize(ulong saveDataId, long size) public Result SetSize(ulong saveDataId, long size)
{ {
// Nintendo doesn't lock in this function for some reason // Note: Nintendo doesn't lock in this function for some reason
lock (Locker) using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
{
if (IsKeyValueSet && _value.SaveDataId == saveDataId)
{
_value.Size = size;
return Result.Success;
}
return ResultFs.TargetNotFound.Log(); if (_key.HasValue && saveDataId == _value.SaveDataId)
{
_value.Size = size;
return Result.Success;
} }
return ResultFs.TargetNotFound.Log();
} }
public Result SetState(ulong saveDataId, SaveDataState state) public Result SetState(ulong saveDataId, SaveDataState state)
{ {
// Nintendo doesn't lock in this function for some reason // Note: Nintendo doesn't lock in this function for some reason
lock (Locker) using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
{
if (IsKeyValueSet && _value.SaveDataId == saveDataId)
{
_value.State = state;
return Result.Success;
}
return ResultFs.TargetNotFound.Log(); if (_key.HasValue && saveDataId == _value.SaveDataId)
{
_value.State = state;
return Result.Success;
} }
return ResultFs.TargetNotFound.Log();
} }
public Result GetKey(out SaveDataAttribute key, ulong saveDataId) public Result GetKey(out SaveDataAttribute key, ulong saveDataId)
{ {
UnsafeHelpers.SkipParamInit(out key); UnsafeHelpers.SkipParamInit(out key);
lock (Locker) using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
{
if (IsKeyValueSet && _value.SaveDataId == saveDataId)
{
key = _key;
return Result.Success;
}
return ResultFs.TargetNotFound.Log(); if (_key.HasValue && saveDataId == _value.SaveDataId)
{
key = _key.ValueRo;
return Result.Success;
} }
return ResultFs.TargetNotFound.Log();
} }
public Result GetValue(out SaveDataIndexerValue value, ulong saveDataId) public Result GetValue(out SaveDataIndexerValue value, ulong saveDataId)
{ {
UnsafeHelpers.SkipParamInit(out value); UnsafeHelpers.SkipParamInit(out value);
lock (Locker) using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
{
if (IsKeyValueSet && _value.SaveDataId == saveDataId)
{
value = _value;
return Result.Success;
}
return ResultFs.TargetNotFound.Log(); if (_key.HasValue && saveDataId == _value.SaveDataId)
{
value = _value;
return Result.Success;
} }
return ResultFs.TargetNotFound.Log();
} }
public Result SetValue(in SaveDataAttribute key, in SaveDataIndexerValue value) public Result SetValue(in SaveDataAttribute key, in SaveDataIndexerValue value)
{ {
lock (Locker) using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
{
if (IsKeyValueSet && _key == key)
{
_value = value;
return Result.Success;
}
return ResultFs.TargetNotFound.Log(); if (_key.HasValue && _key.ValueRo == key)
{
_value = value;
return Result.Success;
} }
return ResultFs.TargetNotFound.Log();
} }
public int GetIndexCount() public int GetIndexCount()
@ -212,58 +262,17 @@ public class SaveDataIndexerLite : ISaveDataIndexer
public Result OpenSaveDataInfoReader(ref SharedRef<SaveDataInfoReaderImpl> outInfoReader) public Result OpenSaveDataInfoReader(ref SharedRef<SaveDataInfoReaderImpl> outInfoReader)
{ {
SaveDataIndexerLiteInfoReader reader; using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _mutex);
if (IsKeyValueSet) if (_key.HasValue)
{ {
reader = new SaveDataIndexerLiteInfoReader(in _key, in _value); outInfoReader.Reset(new SaveDataIndexerLiteInfoReader(in _key.Value, in _value));
} }
else else
{ {
reader = new SaveDataIndexerLiteInfoReader(); outInfoReader.Reset(new SaveDataIndexerLiteInfoReader());
} }
outInfoReader.Reset(reader);
return Result.Success; return Result.Success;
} }
private class SaveDataIndexerLiteInfoReader : SaveDataInfoReaderImpl
{
private bool _finishedIterating;
private SaveDataInfo _info;
public SaveDataIndexerLiteInfoReader()
{
_finishedIterating = true;
}
public SaveDataIndexerLiteInfoReader(in SaveDataAttribute key, in SaveDataIndexerValue value)
{
SaveDataIndexer.GenerateSaveDataInfo(out _info, in key, in value);
}
public Result Read(out long readCount, OutBuffer saveDataInfoBuffer)
{
Span<SaveDataInfo> outInfo = MemoryMarshal.Cast<byte, SaveDataInfo>(saveDataInfoBuffer.Buffer);
// Note: Nintendo doesn't check if the buffer is large enough here
if (_finishedIterating || outInfo.IsEmpty)
{
readCount = 0;
}
else
{
outInfo[0] = _info;
readCount = 1;
_finishedIterating = true;
}
return Result.Success;
}
public void Dispose() { }
}
public void Dispose() { }
} }

View file

@ -3,6 +3,7 @@ using LibHac.Common;
using LibHac.Diag; using LibHac.Diag;
using LibHac.Fs; using LibHac.Fs;
using LibHac.FsSrv.Storage; using LibHac.FsSrv.Storage;
using LibHac.Os;
using LibHac.Util; using LibHac.Util;
namespace LibHac.FsSrv; namespace LibHac.FsSrv;
@ -11,35 +12,106 @@ namespace LibHac.FsSrv;
/// Initializes and holds <see cref="ISaveDataIndexer"/>s for each save data space. /// Initializes and holds <see cref="ISaveDataIndexer"/>s for each save data space.
/// Creates accessors for individual SaveDataIndexers. /// Creates accessors for individual SaveDataIndexers.
/// </summary> /// </summary>
/// <remarks>Based on FS 10.0.0 (nnSdk 10.4.0)</remarks> /// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
internal class SaveDataIndexerManager : ISaveDataIndexerManager internal class SaveDataIndexerManager : ISaveDataIndexerManager, IDisposable
{ {
private FileSystemClient FsClient { get; } private MemoryResource _memoryResource;
private MemoryResource MemoryResource { get; } private readonly ulong _indexerSaveDataId;
private ulong SaveDataId { get; } private SdkMutex _bisIndexerMutex;
private Optional<SaveDataIndexer> _bisIndexer;
private IndexerHolder _bisIndexer = new IndexerHolder(new object()); private SdkMutex _tempIndexerMutex;
private IndexerHolder _tempIndexer = new IndexerHolder(new object()); private SaveDataIndexerLite _tempIndexer;
private SdkMutex _sdIndexerMutex;
private IndexerHolder _sdCardIndexer = new IndexerHolder(new object()); private Optional<SaveDataIndexer> _sdIndexer;
private StorageDeviceHandle _sdCardHandle; private StorageDeviceHandle _sdCardHandle;
private IDeviceHandleManager _sdCardHandleManager; private IDeviceHandleManager _sdHandleManager;
private SdkMutex _properBisIndexerMutex;
private Optional<SaveDataIndexer> _properBisIndexer;
private SdkMutex _safeIndexerMutex;
private Optional<SaveDataIndexer> _safeIndexer;
private readonly bool _isBisUserRedirectionEnabled;
private IndexerHolder _safeIndexer = new IndexerHolder(new object()); // LibHac addition
private IndexerHolder _properSystemIndexer = new IndexerHolder(new object()); private FileSystemClient _fsClient;
private bool IsBisUserRedirectionEnabled { get; }
public SaveDataIndexerManager(FileSystemClient fsClient, ulong saveDataId, MemoryResource memoryResource, public SaveDataIndexerManager(FileSystemClient fsClient, ulong saveDataId, MemoryResource memoryResource,
IDeviceHandleManager sdCardHandleManager, bool isBisUserRedirectionEnabled) IDeviceHandleManager sdCardHandleManager, bool isBisUserRedirectionEnabled)
{ {
FsClient = fsClient; _memoryResource = memoryResource;
SaveDataId = saveDataId; _indexerSaveDataId = saveDataId;
MemoryResource = memoryResource;
_sdCardHandleManager = sdCardHandleManager;
IsBisUserRedirectionEnabled = isBisUserRedirectionEnabled;
_tempIndexer.Indexer = new SaveDataIndexerLite(); _bisIndexerMutex = new SdkMutex();
_tempIndexerMutex = new SdkMutex();
_tempIndexer = new SaveDataIndexerLite();
_sdIndexerMutex = new SdkMutex();
_sdHandleManager = sdCardHandleManager;
_properBisIndexerMutex = new SdkMutex();
_safeIndexerMutex = new SdkMutex();
_isBisUserRedirectionEnabled = isBisUserRedirectionEnabled;
_fsClient = fsClient;
}
public void Dispose()
{
InvalidateIndexerImpl(ref _bisIndexer);
_tempIndexer.Dispose();
InvalidateIndexerImpl(ref _sdIndexer);
InvalidateIndexerImpl(ref _properBisIndexer);
InvalidateIndexerImpl(ref _safeIndexer);
}
public void InvalidateAllIndexers()
{
InvalidateIndexerImpl(ref _bisIndexer);
InvalidateIndexerImpl(ref _sdIndexer);
InvalidateIndexerImpl(ref _properBisIndexer);
InvalidateIndexerImpl(ref _safeIndexer);
}
public void InvalidateIndexer(SaveDataSpaceId spaceId)
{
switch (spaceId)
{
case SaveDataSpaceId.SdSystem:
case SaveDataSpaceId.SdUser:
{
// Note: Nintendo doesn't lock when doing this operation
using ScopedLock<SdkMutex> scopedLock = ScopedLock.Lock(ref _sdIndexerMutex);
InvalidateIndexerImpl(ref _sdIndexer);
break;
}
default:
Abort.UnexpectedDefault();
break;
}
}
// Todo: Figure out how to add generic disposal to Optional<T>
private static void InvalidateIndexerImpl(ref Optional<SaveDataIndexer> indexer)
{
if (indexer.HasValue)
indexer.Value.Dispose();
indexer.Clear();
}
public void ResetIndexer(SaveDataSpaceId spaceId)
{
switch (spaceId)
{
case SaveDataSpaceId.Temporary:
// ReSharper disable once RedundantAssignment
Result rc = _tempIndexer.Reset();
Assert.SdkAssert(rc.IsSuccess());
break;
default:
Abort.UnexpectedDefault();
break;
}
} }
/// <summary> /// <summary>
@ -59,172 +131,131 @@ internal class SaveDataIndexerManager : ISaveDataIndexerManager
{ {
UnsafeHelpers.SkipParamInit(out neededInit); UnsafeHelpers.SkipParamInit(out neededInit);
if (IsBisUserRedirectionEnabled && spaceId == SaveDataSpaceId.User) if (_isBisUserRedirectionEnabled && spaceId == SaveDataSpaceId.User)
{ {
spaceId = SaveDataSpaceId.ProperSystem; spaceId = SaveDataSpaceId.ProperSystem;
} }
UniqueLock indexerLock = default; ISaveDataIndexer indexer;
try using var indexerLock = new UniqueLock<SdkMutex>();
bool wasIndexerInitialized = false;
switch (spaceId)
{ {
ISaveDataIndexer indexer; case SaveDataSpaceId.System:
switch (spaceId) case SaveDataSpaceId.User:
{ {
case SaveDataSpaceId.System: indexerLock.Reset(_bisIndexerMutex);
case SaveDataSpaceId.User:
indexerLock = new UniqueLock(_bisIndexer.Locker);
if (!_bisIndexer.IsInitialized) if (!_bisIndexer.HasValue)
{ {
_bisIndexer.Indexer = new SaveDataIndexer(FsClient, new U8Span(SystemIndexerMountName), _bisIndexer.Set(new SaveDataIndexer(_fsClient, new U8Span(BisIndexerMountName),
SaveDataSpaceId.System, SaveDataId, MemoryResource); SaveDataSpaceId.System, _indexerSaveDataId, _memoryResource));
wasIndexerInitialized = true;
}
neededInit = true; indexer = _bisIndexer.Value;
} break;
}
indexer = _bisIndexer.Indexer; case SaveDataSpaceId.Temporary:
break; {
case SaveDataSpaceId.SdSystem: indexerLock.Reset(_tempIndexerMutex);
case SaveDataSpaceId.SdUser: indexer = _tempIndexer;
// ReSharper doesn't realize that UniqueLock locks the indexer's lock object break;
// ReSharper disable InconsistentlySynchronizedField }
indexerLock = new UniqueLock(_sdCardIndexer.Locker); case SaveDataSpaceId.SdSystem:
case SaveDataSpaceId.SdUser:
// We need to reinitialize the indexer if the SD card has changed {
if (!_sdCardHandleManager.IsValid(in _sdCardHandle) && _sdCardIndexer.IsInitialized) if (_sdHandleManager is null)
{
_sdCardIndexer.Indexer.Dispose();
_sdCardIndexer.Indexer = null;
}
if (!_sdCardIndexer.IsInitialized)
{
_sdCardIndexer.Indexer = new SaveDataIndexer(FsClient, new U8Span(SdCardIndexerMountName),
SaveDataSpaceId.SdSystem, SaveDataId, MemoryResource);
_sdCardHandleManager.GetHandle(out _sdCardHandle).IgnoreResult();
neededInit = true;
}
indexer = _sdCardIndexer.Indexer;
// ReSharper restore InconsistentlySynchronizedField
break;
case SaveDataSpaceId.Temporary:
indexerLock = new UniqueLock(_tempIndexer.Locker);
indexer = _tempIndexer.Indexer;
break;
case SaveDataSpaceId.ProperSystem:
indexerLock = new UniqueLock(_properSystemIndexer.Locker);
if (!_properSystemIndexer.IsInitialized)
{
_properSystemIndexer.Indexer = new SaveDataIndexer(FsClient,
new U8Span(ProperSystemIndexerMountName),
SaveDataSpaceId.ProperSystem, SaveDataId, MemoryResource);
neededInit = true;
}
indexer = _properSystemIndexer.Indexer;
break;
case SaveDataSpaceId.SafeMode:
indexerLock = new UniqueLock(_safeIndexer.Locker);
if (!_safeIndexer.IsInitialized)
{
_safeIndexer.Indexer = new SaveDataIndexer(FsClient, new U8Span(SafeModeIndexerMountName),
SaveDataSpaceId.SafeMode, SaveDataId, MemoryResource);
neededInit = true;
}
indexer = _safeIndexer.Indexer;
break;
default:
outAccessor = default;
return ResultFs.InvalidArgument.Log(); return ResultFs.InvalidArgument.Log();
indexerLock.Reset(_sdIndexerMutex);
// We need to reinitialize the indexer if the SD card has changed
if (!_sdHandleManager.IsValid(in _sdCardHandle) && _sdIndexer.HasValue)
{
_sdIndexer.Value.Dispose();
_sdIndexer.Clear();
}
if (!_sdIndexer.HasValue)
{
_sdIndexer.Set(new SaveDataIndexer(_fsClient, new U8Span(SdCardIndexerMountName),
SaveDataSpaceId.SdSystem, _indexerSaveDataId, _memoryResource));
_sdHandleManager.GetHandle(out _sdCardHandle).IgnoreResult();
wasIndexerInitialized = true;
}
indexer = _sdIndexer.Value;
break;
} }
case SaveDataSpaceId.ProperSystem:
outAccessor.Reset(new SaveDataIndexerAccessor(indexer, ref indexerLock));
return Result.Success;
}
finally
{
indexerLock.Dispose();
}
}
public void ResetIndexer(SaveDataSpaceId spaceId)
{
if (spaceId != SaveDataSpaceId.Temporary)
{
Abort.UnexpectedDefault();
}
// ReSharper disable once RedundantAssignment
Result rc = _tempIndexer.Indexer.Reset();
Assert.SdkAssert(rc.IsSuccess());
}
public void InvalidateIndexer(SaveDataSpaceId spaceId)
{
// Note: Nintendo doesn't lock when doing this operation
lock (_sdCardIndexer.Locker)
{
if (spaceId != SaveDataSpaceId.SdUser && spaceId != SaveDataSpaceId.SdSystem)
{ {
Abort.UnexpectedDefault(); indexerLock.Reset(_properBisIndexerMutex);
}
if (_sdCardIndexer.IsInitialized) if (!_properBisIndexer.HasValue)
{
_properBisIndexer.Set(new SaveDataIndexer(_fsClient, new U8Span(ProperBisIndexerMountName),
SaveDataSpaceId.ProperSystem, _indexerSaveDataId, _memoryResource));
wasIndexerInitialized = true;
}
indexer = _properBisIndexer.Value;
break;
}
case SaveDataSpaceId.SafeMode:
{ {
_sdCardIndexer.Indexer.Dispose(); indexerLock.Reset(_safeIndexerMutex);
_sdCardIndexer.Indexer = null;
if (!_safeIndexer.HasValue)
{
_safeIndexer.Set(new SaveDataIndexer(_fsClient, new U8Span(SafeModeIndexerMountName),
SaveDataSpaceId.SafeMode, _indexerSaveDataId, _memoryResource));
wasIndexerInitialized = true;
}
indexer = _safeIndexer.Value;
break;
} }
}
}
private struct IndexerHolder default:
{ return ResultFs.InvalidArgument.Log();
public object Locker { get; }
public ISaveDataIndexer Indexer { get; set; }
public IndexerHolder(object locker)
{
Locker = locker;
Indexer = null;
} }
public bool IsInitialized => Indexer != null; outAccessor.Reset(new SaveDataIndexerAccessor(indexer, ref indexerLock.Ref()));
neededInit = wasIndexerInitialized;
return Result.Success;
} }
private static ReadOnlySpan<byte> SystemIndexerMountName => // saveDataIxrDb /// <summary>"<c>saveDataIxrDb</c>"</summary>
private static ReadOnlySpan<byte> BisIndexerMountName =>
new[] new[]
{ {
(byte) 's', (byte) 'a', (byte) 'v', (byte) 'e', (byte) 'D', (byte) 'a', (byte) 't', (byte) 'a', (byte) 's', (byte) 'a', (byte) 'v', (byte) 'e', (byte) 'D', (byte) 'a', (byte) 't', (byte) 'a',
(byte) 'I', (byte) 'x', (byte) 'r', (byte) 'D', (byte) 'b' (byte) 'I', (byte) 'x', (byte) 'r', (byte) 'D', (byte) 'b'
}; };
private static ReadOnlySpan<byte> SdCardIndexerMountName => // saveDataIxrDbSd /// <summary>"<c>saveDataIxrDbSd</c>"</summary>
private static ReadOnlySpan<byte> SdCardIndexerMountName =>
new[] new[]
{ {
(byte) 's', (byte) 'a', (byte) 'v', (byte) 'e', (byte) 'D', (byte) 'a', (byte) 't', (byte) 'a', (byte) 's', (byte) 'a', (byte) 'v', (byte) 'e', (byte) 'D', (byte) 'a', (byte) 't', (byte) 'a',
(byte) 'I', (byte) 'x', (byte) 'r', (byte) 'D', (byte) 'b', (byte) 'S', (byte) 'd' (byte) 'I', (byte) 'x', (byte) 'r', (byte) 'D', (byte) 'b', (byte) 'S', (byte) 'd'
}; };
private static ReadOnlySpan<byte> ProperSystemIndexerMountName => // saveDataIxrDbPr /// <summary>"<c>saveDataIxrDbPr</c>"</summary>
private static ReadOnlySpan<byte> ProperBisIndexerMountName =>
new[] new[]
{ {
(byte) 's', (byte) 'a', (byte) 'v', (byte) 'e', (byte) 'D', (byte) 'a', (byte) 't', (byte) 'a', (byte) 's', (byte) 'a', (byte) 'v', (byte) 'e', (byte) 'D', (byte) 'a', (byte) 't', (byte) 'a',
(byte) 'I', (byte) 'x', (byte) 'r', (byte) 'D', (byte) 'b', (byte) 'P', (byte) 'r' (byte) 'I', (byte) 'x', (byte) 'r', (byte) 'D', (byte) 'b', (byte) 'P', (byte) 'r'
}; };
private static ReadOnlySpan<byte> SafeModeIndexerMountName => // saveDataIxrDbSf /// <summary>"<c>saveDataIxrDbSf</c>"</summary>
private static ReadOnlySpan<byte> SafeModeIndexerMountName =>
new[] new[]
{ {
(byte) 's', (byte) 'a', (byte) 'v', (byte) 'e', (byte) 'D', (byte) 'a', (byte) 't', (byte) 'a', (byte) 's', (byte) 'a', (byte) 'v', (byte) 'e', (byte) 'D', (byte) 'a', (byte) 't', (byte) 'a',
@ -236,20 +267,25 @@ internal class SaveDataIndexerManager : ISaveDataIndexerManager
/// Gives exclusive access to an <see cref="ISaveDataIndexer"/>. /// Gives exclusive access to an <see cref="ISaveDataIndexer"/>.
/// Releases the lock to the <see cref="ISaveDataIndexer"/> upon disposal. /// Releases the lock to the <see cref="ISaveDataIndexer"/> upon disposal.
/// </summary> /// </summary>
/// <remarks>Based on FS 10.0.0 (nnSdk 10.4.0)</remarks> /// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public class SaveDataIndexerAccessor : IDisposable public class SaveDataIndexerAccessor : IDisposable
{ {
public ISaveDataIndexer Indexer { get; } private readonly ISaveDataIndexer _indexer;
private UniqueLock _locker; private UniqueLock<SdkMutex> _lock;
public SaveDataIndexerAccessor(ISaveDataIndexer indexer, ref UniqueLock locker) public SaveDataIndexerAccessor(ISaveDataIndexer indexer, ref UniqueLock<SdkMutex> indexerLock)
{ {
Indexer = indexer; _indexer = indexer;
_locker = new UniqueLock(ref locker); _lock = new UniqueLock<SdkMutex>(ref indexerLock);
} }
public void Dispose() public void Dispose()
{ {
_locker.Dispose(); _lock.Dispose();
}
public ISaveDataIndexer GetInterface()
{
return _indexer;
} }
} }

View file

@ -156,6 +156,16 @@ public struct UniqueLock<TMutex> : IDisposable where TMutex : class, ILockable
other = default; other = default;
} }
public void Reset(TMutex mutex)
{
if (_ownsLock)
_mutex.Unlock();
_mutex = mutex;
mutex.Lock();
_ownsLock = true;
}
public void Lock() public void Lock()
{ {
if (_mutex is null) if (_mutex is null)

View file

@ -1,4 +1,4 @@
using System; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Diag; using LibHac.Diag;
@ -52,6 +52,11 @@ public struct Optional<T>
public void Clear() public void Clear()
{ {
_hasValue = false; _hasValue = false;
_value = default;
// Clear types with references so the GC doesn't think we still need any contained objects
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
_value = default;
}
} }
} }