mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Fixup save data indexer classes and update them for 13.1.0
- SaveDataIndexer - SaveDataIndexerLite - SaveDataIndexerLiteInfoReader - SaveDataIndexerManager - SaveDataIndexerAccessor
This commit is contained in:
parent
ecb85269eb
commit
085511660d
9 changed files with 1038 additions and 914 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
@ -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() { }
|
|
||||||
}
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue