mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Use FlatMapKeyValueStore in SaveDataIndexer
FS 10.0 tweaks how the SaveDataIndexerManager is accessed. SaveDataFileSystemServiceImpl's configuration now includes an ISaveDataIndexerManager instead of using global state. FS 10.0 uses the new nn::fssrv::storage namespace for abstracting the interface to nn::gc and nn::sdmmc. A tiny bit of the namespace has been added so we can create mock SD card handles for SaveDataIndexerManager. ReferenceCountedDisposable<T> is now being used for returning ISaveDataInfoReader objects from IFileSystemProxy. I've tried coming up with a pattern to cleanly return a ReferenceCountedDisposable<T>, but the lack of features like RAII or move semantics makes this difficult without either lots of allocations or lots of boilerplate code.
This commit is contained in:
parent
37251968c0
commit
a551360da0
@ -1,4 +1,5 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace LibHac.Diag
|
||||
{
|
||||
@ -22,5 +23,10 @@ namespace LibHac.Diag
|
||||
|
||||
DoAbort(message);
|
||||
}
|
||||
|
||||
public static void UnexpectedDefault([CallerMemberName] string caller = "")
|
||||
{
|
||||
throw new LibHacException($"Unexpected value passed to switch statement in {caller}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
20
src/LibHac/Fs/Impl/SdHandleManager.cs
Normal file
20
src/LibHac/Fs/Impl/SdHandleManager.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using LibHac.FsService;
|
||||
using LibHac.FsService.Storage;
|
||||
|
||||
namespace LibHac.Fs.Impl
|
||||
{
|
||||
internal class SdHandleManager : IDeviceHandleManager
|
||||
{
|
||||
public Result GetHandle(out StorageDeviceHandle handle)
|
||||
{
|
||||
return SdCardManagement.GetCurrentSdCardHandle(out handle);
|
||||
}
|
||||
|
||||
public bool IsValid(in StorageDeviceHandle handle)
|
||||
{
|
||||
// Note: Nintendo ignores the result here.
|
||||
SdCardManagement.IsSdCardHandleValid(out bool isValid, in handle).IgnoreResult();
|
||||
return isValid;
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ namespace LibHac.Fs
|
||||
{
|
||||
[FieldOffset(0x00)] public ProgramId ProgramId;
|
||||
[FieldOffset(0x08)] public UserId UserId;
|
||||
[FieldOffset(0x18)] public ulong SaveDataId;
|
||||
[FieldOffset(0x18)] public ulong StaticSaveDataId;
|
||||
[FieldOffset(0x20)] public SaveDataType Type;
|
||||
[FieldOffset(0x21)] public SaveDataRank Rank;
|
||||
[FieldOffset(0x22)] public short Index;
|
||||
@ -25,15 +25,18 @@ namespace LibHac.Fs
|
||||
return ProgramId == other.ProgramId &&
|
||||
Type == other.Type &&
|
||||
UserId.Equals(other.UserId) &&
|
||||
SaveDataId == other.SaveDataId &&
|
||||
StaticSaveDataId == other.StaticSaveDataId &&
|
||||
Rank == other.Rank &&
|
||||
Index == other.Index;
|
||||
}
|
||||
|
||||
public static bool operator ==(SaveDataAttribute left, SaveDataAttribute right) => left.Equals(right);
|
||||
public static bool operator !=(SaveDataAttribute left, SaveDataAttribute right) => !(left == right);
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
// ReSharper disable NonReadonlyMemberInGetHashCode
|
||||
return HashCode.Combine(ProgramId, Type, UserId, SaveDataId, Rank, Index);
|
||||
return HashCode.Combine(ProgramId, Type, UserId, StaticSaveDataId, Rank, Index);
|
||||
// ReSharper restore NonReadonlyMemberInGetHashCode
|
||||
}
|
||||
|
||||
@ -45,7 +48,7 @@ namespace LibHac.Fs
|
||||
if (typeComparison != 0) return typeComparison;
|
||||
int userIdComparison = UserId.CompareTo(other.UserId);
|
||||
if (userIdComparison != 0) return userIdComparison;
|
||||
int saveDataIdComparison = SaveDataId.CompareTo(other.SaveDataId);
|
||||
int saveDataIdComparison = StaticSaveDataId.CompareTo(other.StaticSaveDataId);
|
||||
if (saveDataIdComparison != 0) return saveDataIdComparison;
|
||||
int rankComparison = Rank.CompareTo(other.Rank);
|
||||
if (rankComparison != 0) return rankComparison;
|
||||
@ -143,7 +146,7 @@ namespace LibHac.Fs
|
||||
[FieldOffset(0x08)] public SaveDataSpaceId SpaceId;
|
||||
[FieldOffset(0x09)] public SaveDataType Type;
|
||||
[FieldOffset(0x10)] public UserId UserId;
|
||||
[FieldOffset(0x20)] public ulong SaveDataIdFromKey;
|
||||
[FieldOffset(0x20)] public ulong StaticSaveDataId;
|
||||
[FieldOffset(0x28)] public ProgramId ProgramId;
|
||||
[FieldOffset(0x30)] public long Size;
|
||||
[FieldOffset(0x38)] public short Index;
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.FsService;
|
||||
using LibHac.Ncm;
|
||||
|
||||
@ -228,7 +229,7 @@ namespace LibHac.Fs.Shim
|
||||
var attribute = new SaveDataAttribute
|
||||
{
|
||||
UserId = userId,
|
||||
SaveDataId = saveDataId
|
||||
StaticSaveDataId = saveDataId
|
||||
};
|
||||
|
||||
var createInfo = new SaveDataCreationInfo
|
||||
@ -364,7 +365,8 @@ namespace LibHac.Fs.Shim
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fsProxy.OpenSaveDataInfoReaderBySaveDataSpaceId(out ISaveDataInfoReader reader, spaceId);
|
||||
Result rc = fsProxy.OpenSaveDataInfoReaderBySaveDataSpaceId(
|
||||
out ReferenceCountedDisposable<ISaveDataInfoReader> reader, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
tempIterator = new SaveDataIterator(fs, reader);
|
||||
@ -388,7 +390,8 @@ namespace LibHac.Fs.Shim
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fsProxy.OpenSaveDataInfoReaderWithFilter(out ISaveDataInfoReader reader, spaceId, ref tempFilter);
|
||||
Result rc = fsProxy.OpenSaveDataInfoReaderWithFilter(
|
||||
out ReferenceCountedDisposable<ISaveDataInfoReader> reader, spaceId, ref tempFilter);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
tempIterator = new SaveDataIterator(fs, reader);
|
||||
@ -402,20 +405,25 @@ namespace LibHac.Fs.Shim
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result DisableAutoSaveDataCreation(this FileSystemClient fsClient)
|
||||
public static void DisableAutoSaveDataCreation(this FileSystemClient fsClient)
|
||||
{
|
||||
IFileSystemProxy fsProxy = fsClient.GetFileSystemProxyServiceObject();
|
||||
|
||||
return fsProxy.DisableAutoSaveDataCreation();
|
||||
Result rc = fsProxy.DisableAutoSaveDataCreation();
|
||||
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
Abort.DoAbort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct SaveDataIterator : IDisposable
|
||||
{
|
||||
private FileSystemClient FsClient { get; }
|
||||
private ISaveDataInfoReader Reader { get; }
|
||||
private ReferenceCountedDisposable<ISaveDataInfoReader> Reader { get; }
|
||||
|
||||
internal SaveDataIterator(FileSystemClient fsClient, ISaveDataInfoReader reader)
|
||||
internal SaveDataIterator(FileSystemClient fsClient, ReferenceCountedDisposable<ISaveDataInfoReader> reader)
|
||||
{
|
||||
FsClient = fsClient;
|
||||
Reader = reader;
|
||||
@ -430,14 +438,14 @@ namespace LibHac.Fs.Shim
|
||||
if (FsClient.IsEnabledAccessLog(AccessLogTarget.System))
|
||||
{
|
||||
TimeSpan startTime = FsClient.Time.GetCurrent();
|
||||
rc = Reader.ReadSaveDataInfo(out readCount, byteBuffer);
|
||||
rc = Reader.Target.Read(out readCount, byteBuffer);
|
||||
TimeSpan endTime = FsClient.Time.GetCurrent();
|
||||
|
||||
FsClient.OutputAccessLog(rc, startTime, endTime, $", size: {buffer.Length}");
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = Reader.ReadSaveDataInfo(out readCount, byteBuffer);
|
||||
rc = Reader.Target.Read(out readCount, byteBuffer);
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
@ -21,7 +21,7 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
SaveDataAttribute attribute = default;
|
||||
attribute.UserId = userId;
|
||||
attribute.SaveDataId = saveDataId;
|
||||
attribute.StaticSaveDataId = saveDataId;
|
||||
|
||||
rc = fsProxy.OpenSaveDataFileSystemBySystemSaveDataId(out IFileSystem fileSystem, spaceId, ref attribute);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsService.Impl;
|
||||
@ -129,13 +130,11 @@ namespace LibHac.FsService
|
||||
|
||||
private Result DeleteSaveDataFileSystemImpl(SaveDataSpaceId spaceId, ulong saveDataId)
|
||||
{
|
||||
SaveDataIndexerReader reader = default;
|
||||
Result rc = OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
try
|
||||
using (accessor)
|
||||
{
|
||||
Result rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out reader, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (saveDataId == FileSystemServer.SaveIndexerId)
|
||||
{
|
||||
// missing: This save can only be deleted by the FS process itself
|
||||
@ -144,13 +143,13 @@ namespace LibHac.FsService
|
||||
{
|
||||
if (spaceId != SaveDataSpaceId.ProperSystem && spaceId != SaveDataSpaceId.SafeMode)
|
||||
{
|
||||
rc = reader.Indexer.GetBySaveDataId(out SaveDataIndexerValue value, saveDataId);
|
||||
rc = accessor.Indexer.GetValue(out SaveDataIndexerValue value, saveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
spaceId = value.SpaceId;
|
||||
}
|
||||
|
||||
rc = reader.Indexer.GetKey(out SaveDataAttribute key, saveDataId);
|
||||
rc = accessor.Indexer.GetKey(out SaveDataAttribute key, saveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (key.Type == SaveDataType.System || key.Type == SaveDataType.SystemBcat)
|
||||
@ -162,10 +161,10 @@ namespace LibHac.FsService
|
||||
// Check if permissions allow deleting save data
|
||||
}
|
||||
|
||||
rc = reader.Indexer.SetState(saveDataId, SaveDataState.Creating);
|
||||
rc = accessor.Indexer.SetState(saveDataId, SaveDataState.Creating);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = reader.Indexer.Commit();
|
||||
rc = accessor.Indexer.Commit();
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
@ -174,19 +173,15 @@ namespace LibHac.FsService
|
||||
|
||||
if (saveDataId != FileSystemServer.SaveIndexerId)
|
||||
{
|
||||
rc = reader.Indexer.Delete(saveDataId);
|
||||
rc = accessor.Indexer.Delete(saveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = reader.Indexer.Commit();
|
||||
rc = accessor.Indexer.Commit();
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
reader.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private Result DeleteSaveDataFileSystemImpl2(SaveDataSpaceId spaceId, ulong saveDataId)
|
||||
@ -215,23 +210,18 @@ namespace LibHac.FsService
|
||||
{
|
||||
if (saveDataId != FileSystemServer.SaveIndexerId)
|
||||
{
|
||||
SaveDataIndexerReader reader = default;
|
||||
Result rc = OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
try
|
||||
using (accessor)
|
||||
{
|
||||
Result rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out reader, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = reader.Indexer.GetBySaveDataId(out SaveDataIndexerValue value, saveDataId);
|
||||
rc = accessor.Indexer.GetValue(out SaveDataIndexerValue value, saveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (value.SpaceId != GetSpaceIdForIndexer(spaceId))
|
||||
return ResultFs.TargetNotFound.Log();
|
||||
}
|
||||
finally
|
||||
{
|
||||
reader.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return DeleteSaveDataFileSystemImpl(spaceId, saveDataId);
|
||||
@ -239,25 +229,19 @@ namespace LibHac.FsService
|
||||
|
||||
private Result GetSaveDataInfo(out SaveDataInfo info, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute)
|
||||
{
|
||||
info = default;
|
||||
Unsafe.SkipInit(out info);
|
||||
|
||||
SaveDataIndexerReader reader = default;
|
||||
Result rc = OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
try
|
||||
using (accessor)
|
||||
{
|
||||
Result rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out reader, spaceId);
|
||||
rc = accessor.Indexer.Get(out SaveDataIndexerValue value, ref attribute);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = reader.Indexer.Get(out SaveDataIndexerValue value, ref attribute);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
SaveDataIndexer.GetSaveDataInfo(out info, ref attribute, ref value);
|
||||
SaveDataIndexer.GenerateSaveDataInfo(out info, in attribute, in value);
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
reader.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId spaceId, ref SaveDataAttribute attribute)
|
||||
@ -280,11 +264,11 @@ namespace LibHac.FsService
|
||||
bool isDeleteNeeded = false;
|
||||
Result rc;
|
||||
|
||||
SaveDataIndexerReader reader = default;
|
||||
SaveDataIndexerAccessor accessor = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (attribute.SaveDataId == FileSystemServer.SaveIndexerId)
|
||||
if (attribute.StaticSaveDataId == FileSystemServer.SaveIndexerId)
|
||||
{
|
||||
saveDataId = FileSystemServer.SaveIndexerId;
|
||||
rc = FsProxyCore.DoesSaveDataExist(out bool saveExists, creationInfo.SpaceId, saveDataId);
|
||||
@ -298,29 +282,29 @@ namespace LibHac.FsService
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out reader, creationInfo.SpaceId);
|
||||
rc = OpenSaveDataIndexerAccessor(out accessor, creationInfo.SpaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
SaveDataAttribute indexerKey = attribute;
|
||||
|
||||
if (attribute.SaveDataId != 0 && attribute.UserId == UserId.Zero)
|
||||
if (attribute.StaticSaveDataId != 0 && attribute.UserId == UserId.Zero)
|
||||
{
|
||||
saveDataId = attribute.SaveDataId;
|
||||
saveDataId = attribute.StaticSaveDataId;
|
||||
|
||||
rc = reader.Indexer.AddSystemSaveData(ref indexerKey);
|
||||
rc = accessor.Indexer.PutStaticSaveDataIdIndex(ref indexerKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (attribute.Type != SaveDataType.System &&
|
||||
attribute.Type != SaveDataType.SystemBcat)
|
||||
{
|
||||
if (reader.Indexer.IsFull())
|
||||
if (accessor.Indexer.IsRemainedReservedOnly())
|
||||
{
|
||||
return ResultKvdb.OutOfKeyResource.Log();
|
||||
}
|
||||
}
|
||||
|
||||
rc = reader.Indexer.Add(out saveDataId, ref indexerKey);
|
||||
rc = accessor.Indexer.Publish(out saveDataId, ref indexerKey);
|
||||
}
|
||||
|
||||
if (ResultFs.SaveDataPathAlreadyExists.Includes(rc))
|
||||
@ -330,21 +314,21 @@ namespace LibHac.FsService
|
||||
|
||||
isDeleteNeeded = true;
|
||||
|
||||
rc = reader.Indexer.SetState(saveDataId, SaveDataState.Creating);
|
||||
rc = accessor.Indexer.SetState(saveDataId, SaveDataState.Creating);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
SaveDataSpaceId indexerSpaceId = GetSpaceIdForIndexer(creationInfo.SpaceId);
|
||||
|
||||
rc = reader.Indexer.SetSpaceId(saveDataId, indexerSpaceId);
|
||||
rc = accessor.Indexer.SetSpaceId(saveDataId, indexerSpaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// todo: calculate size
|
||||
long size = 0;
|
||||
|
||||
rc = reader.Indexer.SetSize(saveDataId, size);
|
||||
rc = accessor.Indexer.SetSize(saveDataId, size);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = reader.Indexer.Commit();
|
||||
rc = accessor.Indexer.Commit();
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
@ -386,17 +370,20 @@ namespace LibHac.FsService
|
||||
}
|
||||
}
|
||||
|
||||
if (attribute.SaveDataId == FileSystemServer.SaveIndexerId || something)
|
||||
if (attribute.StaticSaveDataId == FileSystemServer.SaveIndexerId || something)
|
||||
{
|
||||
isDeleteNeeded = false;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
rc = reader.Indexer.SetState(saveDataId, SaveDataState.Normal);
|
||||
// accessor shouldn't ever be null, but checking makes the analyzers happy
|
||||
Abort.DoAbortUnless(accessor != null);
|
||||
|
||||
rc = accessor.Indexer.SetState(saveDataId, SaveDataState.Normal);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = reader.Indexer.Commit();
|
||||
rc = accessor.Indexer.Commit();
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
isDeleteNeeded = false;
|
||||
@ -410,19 +397,19 @@ namespace LibHac.FsService
|
||||
{
|
||||
DeleteSaveDataFileSystemImpl2(creationInfo.SpaceId, saveDataId).IgnoreResult();
|
||||
|
||||
if (reader.IsInitialized && saveDataId != FileSystemServer.SaveIndexerId)
|
||||
if (accessor != null && saveDataId != FileSystemServer.SaveIndexerId)
|
||||
{
|
||||
rc = reader.Indexer.GetBySaveDataId(out SaveDataIndexerValue value, saveDataId);
|
||||
rc = accessor.Indexer.GetValue(out SaveDataIndexerValue value, saveDataId);
|
||||
|
||||
if (rc.IsSuccess() && value.SpaceId == creationInfo.SpaceId)
|
||||
{
|
||||
reader.Indexer.Delete(saveDataId).IgnoreResult();
|
||||
reader.Indexer.Commit().IgnoreResult();
|
||||
accessor.Indexer.Delete(saveDataId).IgnoreResult();
|
||||
accessor.Indexer.Commit().IgnoreResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reader.Dispose();
|
||||
accessor?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@ -454,7 +441,7 @@ namespace LibHac.FsService
|
||||
|
||||
public Result CreateSaveDataFileSystemBySystemSaveDataId(ref SaveDataAttribute attribute, ref SaveDataCreationInfo creationInfo)
|
||||
{
|
||||
if (!IsSystemSaveDataId(attribute.SaveDataId))
|
||||
if (!IsSystemSaveDataId(attribute.StaticSaveDataId))
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
SaveDataCreationInfo newCreationInfo = creationInfo;
|
||||
@ -490,21 +477,21 @@ namespace LibHac.FsService
|
||||
fileSystem = default;
|
||||
saveDataId = default;
|
||||
|
||||
bool hasFixedId = attribute.SaveDataId != 0 && attribute.UserId == UserId.Zero;
|
||||
bool hasFixedId = attribute.StaticSaveDataId != 0 && attribute.UserId == UserId.Zero;
|
||||
|
||||
if (hasFixedId)
|
||||
{
|
||||
saveDataId = attribute.SaveDataId;
|
||||
saveDataId = attribute.StaticSaveDataId;
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveDataAttribute indexerKey = attribute;
|
||||
|
||||
Result rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out SaveDataIndexerReader tmpReader, spaceId);
|
||||
using SaveDataIndexerReader reader = tmpReader;
|
||||
Result rc = OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor tempAccessor, spaceId);
|
||||
using SaveDataIndexerAccessor accessor = tempAccessor;
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = reader.Indexer.Get(out SaveDataIndexerValue indexerValue, ref indexerKey);
|
||||
rc = accessor.Indexer.Get(out SaveDataIndexerValue indexerValue, ref indexerKey);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
SaveDataSpaceId indexerSpaceId = GetSpaceIdForIndexer(spaceId);
|
||||
@ -605,7 +592,7 @@ namespace LibHac.FsService
|
||||
// Missing permission check, speed emulation storage type wrapper, and FileSystemInterfaceAdapter
|
||||
fileSystem = default;
|
||||
|
||||
if (!IsSystemSaveDataId(attribute.SaveDataId)) return ResultFs.InvalidArgument.Log();
|
||||
if (!IsSystemSaveDataId(attribute.StaticSaveDataId)) return ResultFs.InvalidArgument.Log();
|
||||
|
||||
Result rc = OpenSaveDataFileSystemImpl(out IFileSystem saveFs, out _, spaceId,
|
||||
ref attribute, false, true);
|
||||
@ -732,41 +719,34 @@ namespace LibHac.FsService
|
||||
return FsProxyCore.OpenDeviceOperator(out deviceOperator);
|
||||
}
|
||||
|
||||
public Result OpenSaveDataInfoReader(out ISaveDataInfoReader infoReader)
|
||||
public Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader)
|
||||
{
|
||||
infoReader = default;
|
||||
|
||||
// Missing permission check
|
||||
|
||||
SaveDataIndexerReader indexReader = default;
|
||||
Result rc = OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, SaveDataSpaceId.System);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
try
|
||||
using (accessor)
|
||||
{
|
||||
Result rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out indexReader, SaveDataSpaceId.System);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return indexReader.Indexer.OpenSaveDataInfoReader(out infoReader);
|
||||
}
|
||||
finally
|
||||
{
|
||||
indexReader.Dispose();
|
||||
return accessor.Indexer.OpenSaveDataInfoReader(out infoReader);
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenSaveDataInfoReaderBySaveDataSpaceId(out ISaveDataInfoReader infoReader, SaveDataSpaceId spaceId)
|
||||
public Result OpenSaveDataInfoReaderBySaveDataSpaceId(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader, SaveDataSpaceId spaceId)
|
||||
{
|
||||
infoReader = default;
|
||||
|
||||
// Missing permission check
|
||||
|
||||
SaveDataIndexerReader indexReader = default;
|
||||
Result rc = OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
try
|
||||
using (accessor)
|
||||
{
|
||||
Result rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out indexReader, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = indexReader.Indexer.OpenSaveDataInfoReader(out ISaveDataInfoReader baseInfoReader);
|
||||
rc = accessor.Indexer.OpenSaveDataInfoReader(
|
||||
out ReferenceCountedDisposable<ISaveDataInfoReader> baseInfoReader);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var filter = new SaveDataFilterInternal
|
||||
@ -775,43 +755,36 @@ namespace LibHac.FsService
|
||||
SpaceId = GetSpaceIdForIndexer(spaceId)
|
||||
};
|
||||
|
||||
infoReader = new SaveDataInfoFilterReader(baseInfoReader, ref filter);
|
||||
var filterReader = new SaveDataInfoFilterReader(baseInfoReader, ref filter);
|
||||
infoReader = new ReferenceCountedDisposable<ISaveDataInfoReader>(filterReader);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
indexReader.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenSaveDataInfoReaderWithFilter(out ISaveDataInfoReader infoReader, SaveDataSpaceId spaceId,
|
||||
public Result OpenSaveDataInfoReaderWithFilter(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader, SaveDataSpaceId spaceId,
|
||||
ref SaveDataFilter filter)
|
||||
{
|
||||
infoReader = default;
|
||||
|
||||
// Missing permission check
|
||||
|
||||
SaveDataIndexerReader indexReader = default;
|
||||
Result rc = OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
try
|
||||
using (accessor)
|
||||
{
|
||||
Result rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out indexReader, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = indexReader.Indexer.OpenSaveDataInfoReader(out ISaveDataInfoReader baseInfoReader);
|
||||
rc = accessor.Indexer.OpenSaveDataInfoReader(
|
||||
out ReferenceCountedDisposable<ISaveDataInfoReader> baseInfoReader);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var filterInternal = new SaveDataFilterInternal(ref filter, spaceId);
|
||||
|
||||
infoReader = new SaveDataInfoFilterReader(baseInfoReader, ref filterInternal);
|
||||
var filterReader = new SaveDataInfoFilterReader(baseInfoReader, ref filterInternal);
|
||||
infoReader = new ReferenceCountedDisposable<ISaveDataInfoReader>(filterReader);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
indexReader.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result FindSaveDataWithFilter(out long count, Span<byte> saveDataInfoBuffer, SaveDataSpaceId spaceId,
|
||||
@ -839,23 +812,20 @@ namespace LibHac.FsService
|
||||
count = default;
|
||||
info = default;
|
||||
|
||||
SaveDataIndexerReader indexReader = default;
|
||||
|
||||
try
|
||||
Result rc = OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (accessor)
|
||||
{
|
||||
Result rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out indexReader, spaceId);
|
||||
rc = accessor.Indexer.OpenSaveDataInfoReader(
|
||||
out ReferenceCountedDisposable<ISaveDataInfoReader> baseInfoReader);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = indexReader.Indexer.OpenSaveDataInfoReader(out ISaveDataInfoReader baseInfoReader);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var infoReader = new SaveDataInfoFilterReader(baseInfoReader, ref filter);
|
||||
|
||||
return infoReader.ReadSaveDataInfo(out count, SpanHelpers.AsByteSpan(ref info));
|
||||
}
|
||||
finally
|
||||
{
|
||||
indexReader.Dispose();
|
||||
using (var infoReader = new SaveDataInfoFilterReader(baseInfoReader, ref filter))
|
||||
{
|
||||
return infoReader.Read(out count, SpanHelpers.AsByteSpan(ref info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -874,7 +844,7 @@ namespace LibHac.FsService
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenSaveDataInfoReaderOnlyCacheStorage(out ISaveDataInfoReader infoReader)
|
||||
public Result OpenSaveDataInfoReaderOnlyCacheStorage(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@ -1053,8 +1023,6 @@ namespace LibHac.FsService
|
||||
Result rc = FsProxyCore.SetSdCardEncryptionSeed(ref seed);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
FsServer.SaveDataIndexerManager.ResetSdCardIndexer();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
@ -1146,21 +1114,9 @@ namespace LibHac.FsService
|
||||
rc = saveDirFs.CleanDirectoryRecursively("/".ToU8Span());
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
SaveDataIndexerReader reader = default;
|
||||
FsProxyCore.SaveDataIndexerManager.ResetTemporaryStorageIndexer(SaveDataSpaceId.Temporary);
|
||||
|
||||
try
|
||||
{
|
||||
rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out reader, SaveDataSpaceId.Temporary);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
reader.Indexer.Reset().IgnoreResult();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
reader.Dispose();
|
||||
}
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result SetSdCardAccessibility(bool isAccessible)
|
||||
@ -1189,7 +1145,7 @@ namespace LibHac.FsService
|
||||
|
||||
SaveDataAttribute attribute = default;
|
||||
attribute.ProgramId = new ProgramId(MultiCommitManager.ProgramId);
|
||||
attribute.SaveDataId = MultiCommitManager.SaveDataId;
|
||||
attribute.StaticSaveDataId = MultiCommitManager.SaveDataId;
|
||||
|
||||
Result rc = OpenSaveDataFileSystemImpl(out IFileSystem saveFs, out _, SaveDataSpaceId.System, ref attribute,
|
||||
false, true);
|
||||
@ -1199,6 +1155,35 @@ namespace LibHac.FsService
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
// todo: split the FileSystemProxy classes
|
||||
// nn::fssrv::SaveDataFileSystemService::GetSaveDataIndexerAccessor
|
||||
private Result OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, SaveDataSpaceId spaceId)
|
||||
{
|
||||
accessor = default;
|
||||
|
||||
Result rc = FsProxyCore.OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessorTemp,
|
||||
out bool neededInit, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
try
|
||||
{
|
||||
if (neededInit)
|
||||
{
|
||||
// todo: nn::fssrv::SaveDataFileSystemService::CleanUpSaveDataCore
|
||||
// nn::fssrv::SaveDataFileSystemService::CompleteSaveDataExtensionCore
|
||||
}
|
||||
|
||||
accessor = accessorTemp;
|
||||
accessorTemp = null;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
accessorTemp?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsSystemSaveDataId(ulong id)
|
||||
{
|
||||
return (long)id < 0;
|
||||
|
@ -27,6 +27,8 @@ namespace LibHac.FsService
|
||||
private GlobalAccessLogMode LogMode { get; set; }
|
||||
public bool IsSdCardAccessible { get; set; }
|
||||
|
||||
internal ISaveDataIndexerManager SaveDataIndexerManager { get; private set; }
|
||||
|
||||
public FileSystemProxyCore(FileSystemCreators fsCreators, ExternalKeySet externalKeys, IDeviceOperator deviceOperator)
|
||||
{
|
||||
FsCreators = fsCreators;
|
||||
@ -813,6 +815,9 @@ namespace LibHac.FsService
|
||||
seed.Value.CopyTo(SdEncryptionSeed);
|
||||
// todo: FsCreators.SaveDataFileSystemCreator.SetSdCardEncryptionSeed(seed);
|
||||
|
||||
SaveDataIndexerManager.InvalidateSdCardIndexer(SaveDataSpaceId.SdSystem);
|
||||
SaveDataIndexerManager.InvalidateSdCardIndexer(SaveDataSpaceId.SdCache);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
@ -1058,6 +1063,16 @@ namespace LibHac.FsService
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
internal void SetSaveDataIndexerManager(ISaveDataIndexerManager manager)
|
||||
{
|
||||
SaveDataIndexerManager = manager;
|
||||
}
|
||||
|
||||
internal Result OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit, SaveDataSpaceId spaceId)
|
||||
{
|
||||
return SaveDataIndexerManager.OpenAccessor(out accessor, out neededInit, spaceId);
|
||||
}
|
||||
|
||||
private string GetSaveDataIdPath(ulong saveDataId)
|
||||
{
|
||||
return $"/{saveDataId:x16}";
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.FsService.Creators;
|
||||
|
||||
@ -15,8 +16,6 @@ namespace LibHac.FsService
|
||||
public FileSystemClient FsClient { get; }
|
||||
private ITimeSpanGenerator Timer { get; }
|
||||
|
||||
internal SaveDataIndexerManager SaveDataIndexerManager { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="FileSystemServer"/>.
|
||||
/// </summary>
|
||||
@ -40,7 +39,8 @@ namespace LibHac.FsService
|
||||
if (FsClient.IsSdCardInserted())
|
||||
FsClient.SetSdCardAccessibility(true);
|
||||
|
||||
SaveDataIndexerManager = new SaveDataIndexerManager(FsClient, SaveIndexerId);
|
||||
FsProxyCore.SetSaveDataIndexerManager(new SaveDataIndexerManager(FsClient, SaveIndexerId,
|
||||
new ArrayPoolMemoryResource(), new SdHandleManager(), false));
|
||||
|
||||
fsProxy.CleanUpTemporaryStorage().IgnoreResult();
|
||||
}
|
||||
|
10
src/LibHac/FsService/IDeviceHandleManager.cs
Normal file
10
src/LibHac/FsService/IDeviceHandleManager.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using LibHac.FsService.Storage;
|
||||
|
||||
namespace LibHac.FsService
|
||||
{
|
||||
public interface IDeviceHandleManager
|
||||
{
|
||||
Result GetHandle(out StorageDeviceHandle handle);
|
||||
bool IsValid(in StorageDeviceHandle handle);
|
||||
}
|
||||
}
|
@ -42,14 +42,14 @@ namespace LibHac.FsService
|
||||
Result ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(Span<byte> extraDataBuffer, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||
Result ReadSaveDataFileSystemExtraData(Span<byte> extraDataBuffer, ulong saveDataId);
|
||||
Result WriteSaveDataFileSystemExtraData(ulong saveDataId, SaveDataSpaceId spaceId, ReadOnlySpan<byte> extraDataBuffer);
|
||||
Result OpenSaveDataInfoReader(out ISaveDataInfoReader infoReader);
|
||||
Result OpenSaveDataInfoReaderBySaveDataSpaceId(out ISaveDataInfoReader infoReader, SaveDataSpaceId spaceId);
|
||||
Result OpenSaveDataInfoReaderOnlyCacheStorage(out ISaveDataInfoReader infoReader);
|
||||
Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader);
|
||||
Result OpenSaveDataInfoReaderBySaveDataSpaceId(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader, SaveDataSpaceId spaceId);
|
||||
Result OpenSaveDataInfoReaderOnlyCacheStorage(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader);
|
||||
Result OpenSaveDataInternalStorageFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||
Result UpdateSaveDataMacForDebug(SaveDataSpaceId spaceId, ulong saveDataId);
|
||||
Result WriteSaveDataFileSystemExtraDataWithMask(ulong saveDataId, SaveDataSpaceId spaceId, ReadOnlySpan<byte> extraDataBuffer, ReadOnlySpan<byte> maskBuffer);
|
||||
Result FindSaveDataWithFilter(out long count, Span<byte> saveDataInfoBuffer, SaveDataSpaceId spaceId, ref SaveDataFilter filter);
|
||||
Result OpenSaveDataInfoReaderWithFilter(out ISaveDataInfoReader infoReader, SaveDataSpaceId spaceId, ref SaveDataFilter filter);
|
||||
Result OpenSaveDataInfoReaderWithFilter(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader, SaveDataSpaceId spaceId, ref SaveDataFilter filter);
|
||||
Result ReadSaveDataFileSystemExtraDataBySaveDataAttribute(Span<byte> extraDataBuffer, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
|
||||
Result WriteSaveDataFileSystemExtraDataBySaveDataAttribute(ref SaveDataAttribute attribute, SaveDataSpaceId spaceId, ReadOnlySpan<byte> extraDataBuffer, ReadOnlySpan<byte> maskBuffer);
|
||||
Result OpenSaveDataMetaFile(out IFile file, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute, SaveDataMetaType type);
|
||||
|
@ -1,22 +1,25 @@
|
||||
using LibHac.Fs;
|
||||
using System;
|
||||
using LibHac.Fs;
|
||||
|
||||
namespace LibHac.FsService
|
||||
{
|
||||
public interface ISaveDataIndexer
|
||||
public interface ISaveDataIndexer : IDisposable
|
||||
{
|
||||
Result Commit();
|
||||
Result Rollback();
|
||||
Result Reset();
|
||||
Result Add(out ulong saveDataId, ref SaveDataAttribute key);
|
||||
Result Publish(out ulong saveDataId, ref SaveDataAttribute key);
|
||||
Result Get(out SaveDataIndexerValue value, ref SaveDataAttribute key);
|
||||
Result AddSystemSaveData(ref SaveDataAttribute key);
|
||||
bool IsFull();
|
||||
Result PutStaticSaveDataIdIndex(ref SaveDataAttribute key);
|
||||
bool IsRemainedReservedOnly();
|
||||
Result Delete(ulong saveDataId);
|
||||
Result SetSpaceId(ulong saveDataId, SaveDataSpaceId spaceId);
|
||||
Result SetSize(ulong saveDataId, long size);
|
||||
Result SetState(ulong saveDataId, SaveDataState state);
|
||||
Result GetKey(out SaveDataAttribute key, ulong saveDataId);
|
||||
Result GetBySaveDataId(out SaveDataIndexerValue value, ulong saveDataId);
|
||||
int GetCount();
|
||||
Result OpenSaveDataInfoReader(out ISaveDataInfoReader infoReader);
|
||||
Result GetValue(out SaveDataIndexerValue value, ulong saveDataId);
|
||||
Result SetValue(ref SaveDataAttribute key, ref SaveDataIndexerValue value);
|
||||
int GetIndexCount();
|
||||
Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader);
|
||||
}
|
||||
}
|
11
src/LibHac/FsService/ISaveDataIndexerManager.cs
Normal file
11
src/LibHac/FsService/ISaveDataIndexerManager.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using LibHac.Fs;
|
||||
|
||||
namespace LibHac.FsService
|
||||
{
|
||||
public interface ISaveDataIndexerManager
|
||||
{
|
||||
Result OpenAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit, SaveDataSpaceId spaceId);
|
||||
void ResetTemporaryStorageIndexer(SaveDataSpaceId spaceId);
|
||||
void InvalidateSdCardIndexer(SaveDataSpaceId spaceId);
|
||||
}
|
||||
}
|
@ -4,6 +4,6 @@ namespace LibHac.FsService
|
||||
{
|
||||
public interface ISaveDataInfoReader : IDisposable
|
||||
{
|
||||
Result ReadSaveDataInfo(out long readCount, Span<byte> saveDataInfoBuffer);
|
||||
Result Read(out long readCount, Span<byte> saveDataInfoBuffer);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,11 @@ namespace LibHac.FsService
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result Rollback()
|
||||
{
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result Reset()
|
||||
{
|
||||
lock (Locker)
|
||||
@ -27,11 +32,11 @@ namespace LibHac.FsService
|
||||
}
|
||||
}
|
||||
|
||||
public Result Add(out ulong saveDataId, ref SaveDataAttribute key)
|
||||
public Result Publish(out ulong saveDataId, ref SaveDataAttribute key)
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
if (IsKeyValueSet && _key.Equals(key))
|
||||
if (IsKeyValueSet && _key == key)
|
||||
{
|
||||
saveDataId = default;
|
||||
return ResultFs.SaveDataPathAlreadyExists.Log();
|
||||
@ -52,7 +57,7 @@ namespace LibHac.FsService
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
if (IsKeyValueSet && _key.Equals(key))
|
||||
if (IsKeyValueSet && _key == key)
|
||||
{
|
||||
value = _value;
|
||||
return Result.Success;
|
||||
@ -63,11 +68,11 @@ namespace LibHac.FsService
|
||||
}
|
||||
}
|
||||
|
||||
public Result AddSystemSaveData(ref SaveDataAttribute key)
|
||||
public Result PutStaticSaveDataIdIndex(ref SaveDataAttribute key)
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
if (IsKeyValueSet && _key.Equals(key))
|
||||
if (IsKeyValueSet && _key == key)
|
||||
{
|
||||
return ResultFs.SaveDataPathAlreadyExists.Log();
|
||||
}
|
||||
@ -81,7 +86,7 @@ namespace LibHac.FsService
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFull()
|
||||
public bool IsRemainedReservedOnly()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -159,7 +164,7 @@ namespace LibHac.FsService
|
||||
}
|
||||
}
|
||||
|
||||
public Result GetBySaveDataId(out SaveDataIndexerValue value, ulong saveDataId)
|
||||
public Result GetValue(out SaveDataIndexerValue value, ulong saveDataId)
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
@ -174,22 +179,40 @@ namespace LibHac.FsService
|
||||
}
|
||||
}
|
||||
|
||||
public int GetCount()
|
||||
public Result SetValue(ref SaveDataAttribute key, ref SaveDataIndexerValue value)
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
if (IsKeyValueSet && _key == key)
|
||||
{
|
||||
_value = value;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return ResultFs.TargetNotFound.Log();
|
||||
}
|
||||
}
|
||||
|
||||
public int GetIndexCount()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public Result OpenSaveDataInfoReader(out ISaveDataInfoReader infoReader)
|
||||
public Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader)
|
||||
{
|
||||
SaveDataIndexerLiteInfoReader reader;
|
||||
|
||||
if (IsKeyValueSet)
|
||||
{
|
||||
infoReader = new SaveDataIndexerLiteInfoReader(ref _key, ref _value);
|
||||
reader = new SaveDataIndexerLiteInfoReader(ref _key, ref _value);
|
||||
}
|
||||
else
|
||||
{
|
||||
infoReader = new SaveDataIndexerLiteInfoReader();
|
||||
reader = new SaveDataIndexerLiteInfoReader();
|
||||
}
|
||||
|
||||
infoReader = new ReferenceCountedDisposable<ISaveDataInfoReader>(reader);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
@ -205,14 +228,14 @@ namespace LibHac.FsService
|
||||
|
||||
public SaveDataIndexerLiteInfoReader(ref SaveDataAttribute key, ref SaveDataIndexerValue value)
|
||||
{
|
||||
SaveDataIndexer.GetSaveDataInfo(out _info, ref key, ref value);
|
||||
SaveDataIndexer.GenerateSaveDataInfo(out _info, in key, in value);
|
||||
}
|
||||
|
||||
public Result ReadSaveDataInfo(out long readCount, Span<byte> saveDataInfoBuffer)
|
||||
public Result Read(out long readCount, Span<byte> saveDataInfoBuffer)
|
||||
{
|
||||
Span<SaveDataInfo> outInfo = MemoryMarshal.Cast<byte, SaveDataInfo>(saveDataInfoBuffer);
|
||||
|
||||
// Nintendo doesn't check if the buffer is too small here
|
||||
// Note: Nintendo doesn't check if the buffer is too small here
|
||||
if (_finishedIterating || outInfo.IsEmpty)
|
||||
{
|
||||
readCount = 0;
|
||||
@ -229,5 +252,7 @@ namespace LibHac.FsService
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,51 @@
|
||||
using System.Threading;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsService.Storage;
|
||||
|
||||
namespace LibHac.FsService
|
||||
{
|
||||
internal class SaveDataIndexerManager
|
||||
internal class SaveDataIndexerManager : ISaveDataIndexerManager
|
||||
{
|
||||
private FileSystemClient FsClient { get; }
|
||||
private MemoryResource MemoryResource { get; }
|
||||
private ulong SaveDataId { get; }
|
||||
|
||||
private IndexerHolder _bisIndexer = new IndexerHolder(new object());
|
||||
private IndexerHolder _sdCardIndexer = new IndexerHolder(new object());
|
||||
private IndexerHolder _tempIndexer = new IndexerHolder(new object());
|
||||
|
||||
private IndexerHolder _sdCardIndexer = new IndexerHolder(new object());
|
||||
private StorageDeviceHandle _sdCardHandle;
|
||||
private IDeviceHandleManager _sdCardHandleManager;
|
||||
|
||||
private IndexerHolder _safeIndexer = new IndexerHolder(new object());
|
||||
private IndexerHolder _properSystemIndexer = new IndexerHolder(new object());
|
||||
|
||||
public SaveDataIndexerManager(FileSystemClient fsClient, ulong saveDataId)
|
||||
private bool IsBisUserRedirectionEnabled { get; }
|
||||
|
||||
public SaveDataIndexerManager(FileSystemClient fsClient, ulong saveDataId, MemoryResource memoryResource,
|
||||
IDeviceHandleManager sdCardHandleManager, bool isBisUserRedirectionEnabled)
|
||||
{
|
||||
FsClient = fsClient;
|
||||
SaveDataId = saveDataId;
|
||||
MemoryResource = memoryResource;
|
||||
_sdCardHandleManager = sdCardHandleManager;
|
||||
IsBisUserRedirectionEnabled = isBisUserRedirectionEnabled;
|
||||
|
||||
_tempIndexer.Indexer = new SaveDataIndexerLite();
|
||||
}
|
||||
|
||||
public Result GetSaveDataIndexer(out SaveDataIndexerReader reader, SaveDataSpaceId spaceId)
|
||||
public Result OpenAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit, SaveDataSpaceId spaceId)
|
||||
{
|
||||
neededInit = false;
|
||||
|
||||
if (IsBisUserRedirectionEnabled && spaceId == SaveDataSpaceId.User)
|
||||
{
|
||||
spaceId = SaveDataSpaceId.ProperSystem;
|
||||
}
|
||||
|
||||
switch (spaceId)
|
||||
{
|
||||
case SaveDataSpaceId.System:
|
||||
@ -31,70 +54,106 @@ namespace LibHac.FsService
|
||||
|
||||
if (!_bisIndexer.IsInitialized)
|
||||
{
|
||||
_bisIndexer.Indexer = new SaveDataIndexer(FsClient, "saveDataIxrDb".ToU8Span(), SaveDataSpaceId.System, SaveDataId);
|
||||
_bisIndexer.Indexer = new SaveDataIndexer(FsClient, new U8Span(SystemIndexerMountName),
|
||||
SaveDataSpaceId.System, SaveDataId, MemoryResource);
|
||||
|
||||
neededInit = true;
|
||||
}
|
||||
|
||||
reader = new SaveDataIndexerReader(_bisIndexer.Indexer, _bisIndexer.Locker);
|
||||
accessor = new SaveDataIndexerAccessor(_bisIndexer.Indexer, _bisIndexer.Locker);
|
||||
return Result.Success;
|
||||
|
||||
case SaveDataSpaceId.SdSystem:
|
||||
case SaveDataSpaceId.SdCache:
|
||||
Monitor.Enter(_sdCardIndexer.Locker);
|
||||
|
||||
// todo: Missing reinitialize if SD handle is old
|
||||
// We need to reinitialize the indexer if the SD card has changed
|
||||
if (!_sdCardHandleManager.IsValid(in _sdCardHandle) && _sdCardIndexer.IsInitialized)
|
||||
{
|
||||
_sdCardIndexer.Indexer.Dispose();
|
||||
_sdCardIndexer.Indexer = null;
|
||||
}
|
||||
|
||||
if (!_sdCardIndexer.IsInitialized)
|
||||
{
|
||||
_sdCardIndexer.Indexer = new SaveDataIndexer(FsClient, "saveDataIxrDbSd".ToU8Span(), SaveDataSpaceId.SdSystem, SaveDataId);
|
||||
_sdCardIndexer.Indexer = new SaveDataIndexer(FsClient, new U8Span(SdCardIndexerMountName),
|
||||
SaveDataSpaceId.SdSystem, SaveDataId, MemoryResource);
|
||||
|
||||
_sdCardHandleManager.GetHandle(out _sdCardHandle).IgnoreResult();
|
||||
|
||||
neededInit = true;
|
||||
}
|
||||
|
||||
reader = new SaveDataIndexerReader(_sdCardIndexer.Indexer, _sdCardIndexer.Locker);
|
||||
accessor = new SaveDataIndexerAccessor(_sdCardIndexer.Indexer, _sdCardIndexer.Locker);
|
||||
return Result.Success;
|
||||
|
||||
case SaveDataSpaceId.Temporary:
|
||||
Monitor.Enter(_tempIndexer.Locker);
|
||||
|
||||
if (!_tempIndexer.IsInitialized)
|
||||
{
|
||||
_tempIndexer.Indexer = new SaveDataIndexerLite();
|
||||
}
|
||||
|
||||
reader = new SaveDataIndexerReader(_tempIndexer.Indexer, _tempIndexer.Locker);
|
||||
accessor = new SaveDataIndexerAccessor(_tempIndexer.Indexer, _tempIndexer.Locker);
|
||||
return Result.Success;
|
||||
|
||||
case SaveDataSpaceId.ProperSystem:
|
||||
Monitor.Enter(_safeIndexer.Locker);
|
||||
|
||||
if (!_safeIndexer.IsInitialized)
|
||||
{
|
||||
_safeIndexer.Indexer = new SaveDataIndexer(FsClient, "saveDataIxrDbPr".ToU8Span(), SaveDataSpaceId.ProperSystem, SaveDataId);
|
||||
}
|
||||
|
||||
reader = new SaveDataIndexerReader(_safeIndexer.Indexer, _safeIndexer.Locker);
|
||||
return Result.Success;
|
||||
|
||||
case SaveDataSpaceId.SafeMode:
|
||||
Monitor.Enter(_properSystemIndexer.Locker);
|
||||
|
||||
if (!_properSystemIndexer.IsInitialized)
|
||||
{
|
||||
_properSystemIndexer.Indexer = new SaveDataIndexer(FsClient, "saveDataIxrDbSf".ToU8Span(), SaveDataSpaceId.SafeMode, SaveDataId);
|
||||
_properSystemIndexer.Indexer = new SaveDataIndexer(FsClient, new U8Span(ProperSystemIndexerMountName),
|
||||
SaveDataSpaceId.ProperSystem, SaveDataId, MemoryResource);
|
||||
|
||||
neededInit = true;
|
||||
}
|
||||
|
||||
reader = new SaveDataIndexerReader(_properSystemIndexer.Indexer, _properSystemIndexer.Locker);
|
||||
accessor = new SaveDataIndexerAccessor(_properSystemIndexer.Indexer, _properSystemIndexer.Locker);
|
||||
return Result.Success;
|
||||
|
||||
case SaveDataSpaceId.SafeMode:
|
||||
Monitor.Enter(_safeIndexer.Locker);
|
||||
|
||||
if (!_safeIndexer.IsInitialized)
|
||||
{
|
||||
_safeIndexer.Indexer = new SaveDataIndexer(FsClient, new U8Span(SafeModeIndexerMountName),
|
||||
SaveDataSpaceId.SafeMode, SaveDataId, MemoryResource);
|
||||
|
||||
neededInit = true;
|
||||
}
|
||||
|
||||
accessor = new SaveDataIndexerAccessor(_safeIndexer.Indexer, _safeIndexer.Locker);
|
||||
return Result.Success;
|
||||
|
||||
default:
|
||||
reader = default;
|
||||
accessor = default;
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
}
|
||||
|
||||
internal void ResetSdCardIndexer()
|
||||
public void ResetTemporaryStorageIndexer(SaveDataSpaceId spaceId)
|
||||
{
|
||||
if (spaceId != SaveDataSpaceId.Temporary)
|
||||
{
|
||||
Abort.UnexpectedDefault();
|
||||
}
|
||||
|
||||
// ReSharper disable once RedundantAssignment
|
||||
Result rc = _tempIndexer.Indexer.Reset();
|
||||
Assert.AssertTrue(rc.IsSuccess());
|
||||
}
|
||||
|
||||
public void InvalidateSdCardIndexer(SaveDataSpaceId spaceId)
|
||||
{
|
||||
// Note: Nintendo doesn't lock when doing this operation
|
||||
lock (_sdCardIndexer.Locker)
|
||||
{
|
||||
_sdCardIndexer.Indexer = null;
|
||||
if (spaceId != SaveDataSpaceId.SdCache && spaceId != SaveDataSpaceId.SdSystem)
|
||||
{
|
||||
Abort.UnexpectedDefault();
|
||||
}
|
||||
|
||||
if (_sdCardIndexer.IsInitialized)
|
||||
{
|
||||
_sdCardIndexer.Indexer.Dispose();
|
||||
_sdCardIndexer.Indexer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,30 +170,56 @@ namespace LibHac.FsService
|
||||
|
||||
public bool IsInitialized => Indexer != null;
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> SystemIndexerMountName => // saveDataIxrDb
|
||||
new[]
|
||||
{
|
||||
(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'
|
||||
};
|
||||
|
||||
private static ReadOnlySpan<byte> SdCardIndexerMountName => // saveDataIxrDbSd
|
||||
new[]
|
||||
{
|
||||
(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'
|
||||
};
|
||||
|
||||
private static ReadOnlySpan<byte> ProperSystemIndexerMountName => // saveDataIxrDbPr
|
||||
new[]
|
||||
{
|
||||
(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'
|
||||
};
|
||||
|
||||
private static ReadOnlySpan<byte> SafeModeIndexerMountName => // saveDataIxrDbSf
|
||||
new[]
|
||||
{
|
||||
(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) 'f'
|
||||
};
|
||||
}
|
||||
|
||||
public ref struct SaveDataIndexerReader
|
||||
public class SaveDataIndexerAccessor : IDisposable
|
||||
{
|
||||
private bool _isInitialized;
|
||||
private object _locker;
|
||||
public ISaveDataIndexer Indexer { get; }
|
||||
private object Locker { get; }
|
||||
private bool HasLock { get; set; }
|
||||
|
||||
public ISaveDataIndexer Indexer;
|
||||
public bool IsInitialized => _isInitialized;
|
||||
|
||||
internal SaveDataIndexerReader(ISaveDataIndexer indexer, object locker)
|
||||
public SaveDataIndexerAccessor(ISaveDataIndexer indexer, object locker)
|
||||
{
|
||||
_isInitialized = true;
|
||||
_locker = locker;
|
||||
Indexer = indexer;
|
||||
Locker = locker;
|
||||
HasLock = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isInitialized)
|
||||
if (HasLock)
|
||||
{
|
||||
Monitor.Exit(_locker);
|
||||
Monitor.Exit(Locker);
|
||||
|
||||
_isInitialized = false;
|
||||
HasLock = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,16 +8,16 @@ namespace LibHac.FsService
|
||||
{
|
||||
internal class SaveDataInfoFilterReader : ISaveDataInfoReader
|
||||
{
|
||||
private ISaveDataInfoReader Reader { get; }
|
||||
private ReferenceCountedDisposable<ISaveDataInfoReader> Reader { get; }
|
||||
private SaveDataFilterInternal Filter { get; }
|
||||
|
||||
public SaveDataInfoFilterReader(ISaveDataInfoReader reader, ref SaveDataFilterInternal filter)
|
||||
public SaveDataInfoFilterReader(ReferenceCountedDisposable<ISaveDataInfoReader> reader, ref SaveDataFilterInternal filter)
|
||||
{
|
||||
Reader = reader;
|
||||
Filter = filter;
|
||||
}
|
||||
|
||||
public Result ReadSaveDataInfo(out long readCount, Span<byte> saveDataInfoBuffer)
|
||||
public Result Read(out long readCount, Span<byte> saveDataInfoBuffer)
|
||||
{
|
||||
readCount = default;
|
||||
|
||||
@ -26,11 +26,12 @@ namespace LibHac.FsService
|
||||
SaveDataInfo tempInfo = default;
|
||||
Span<byte> tempInfoBytes = SpanHelpers.AsByteSpan(ref tempInfo);
|
||||
|
||||
ISaveDataInfoReader reader = Reader.Target;
|
||||
int count = 0;
|
||||
|
||||
while (count < outInfo.Length)
|
||||
{
|
||||
Result rc = Reader.ReadSaveDataInfo(out long baseReadCount, tempInfoBytes);
|
||||
Result rc = reader.Read(out long baseReadCount, tempInfoBytes);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (baseReadCount == 0) break;
|
||||
|
20
src/LibHac/FsService/Storage/SdCardManagement.cs
Normal file
20
src/LibHac/FsService/Storage/SdCardManagement.cs
Normal file
@ -0,0 +1,20 @@
|
||||
namespace LibHac.FsService.Storage
|
||||
{
|
||||
public static class SdCardManagement
|
||||
{
|
||||
public static Result GetCurrentSdCardHandle(out StorageDeviceHandle handle)
|
||||
{
|
||||
// todo: StorageDevice interfaces
|
||||
handle = new StorageDeviceHandle(1, StorageDevicePortId.SdCard);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result IsSdCardHandleValid(out bool isValid, in StorageDeviceHandle handle)
|
||||
{
|
||||
// todo: StorageDevice interfaces
|
||||
isValid = handle.PortId == StorageDevicePortId.SdCard;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
26
src/LibHac/FsService/Storage/StorageDeviceHandle.cs
Normal file
26
src/LibHac/FsService/Storage/StorageDeviceHandle.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LibHac.FsService.Storage
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||
public readonly struct StorageDeviceHandle : IEquatable<StorageDeviceHandle>
|
||||
{
|
||||
public readonly uint Value;
|
||||
public readonly StorageDevicePortId PortId;
|
||||
|
||||
public StorageDeviceHandle(uint value, StorageDevicePortId portId)
|
||||
{
|
||||
Value = value;
|
||||
PortId = portId;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) => obj is StorageDeviceHandle other && Equals(other);
|
||||
public bool Equals(StorageDeviceHandle other) => Value == other.Value && PortId == other.PortId;
|
||||
|
||||
public static bool operator ==(StorageDeviceHandle left, StorageDeviceHandle right) => left.Equals(right);
|
||||
public static bool operator !=(StorageDeviceHandle left, StorageDeviceHandle right) => !(left == right);
|
||||
|
||||
public override int GetHashCode() => HashCode.Combine(Value, (int) PortId);
|
||||
}
|
||||
}
|
10
src/LibHac/FsService/Storage/StorageDevicePortId.cs
Normal file
10
src/LibHac/FsService/Storage/StorageDevicePortId.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace LibHac.FsService.Storage
|
||||
{
|
||||
public enum StorageDevicePortId : byte
|
||||
{
|
||||
Invalid = 0,
|
||||
Mmc = 1,
|
||||
SdCard = 2,
|
||||
GameCard = 3
|
||||
}
|
||||
}
|
@ -25,8 +25,8 @@ namespace LibHac.Kvdb
|
||||
private static ReadOnlySpan<byte> ArchiveFileName => // /imkvdb.arc
|
||||
new[]
|
||||
{
|
||||
(byte) '/', (byte) 'i', (byte) 'm', (byte) 'k', (byte) 'v', (byte) 'd', (byte) 'b', (byte) '.',
|
||||
(byte) 'a', (byte) 'r', (byte) 'c'
|
||||
(byte) '/', (byte) 'i', (byte) 'm', (byte) 'k', (byte) 'v', (byte) 'd', (byte) 'b', (byte) '.',
|
||||
(byte) 'a', (byte) 'r', (byte) 'c'
|
||||
};
|
||||
|
||||
public int Count => _index.Count;
|
||||
@ -646,6 +646,11 @@ namespace LibHac.Kvdb
|
||||
public ref KeyValue Get() => ref _entries[_index];
|
||||
public Span<byte> GetValue() => _entries[_index].Value.Get();
|
||||
|
||||
public ref T GetValue<T>() where T : unmanaged
|
||||
{
|
||||
return ref SpanHelpers.AsStruct<T>(_entries[_index].Value.Get());
|
||||
}
|
||||
|
||||
public void Next() => _index++;
|
||||
public bool IsEnd() => _index == _length;
|
||||
|
||||
|
@ -134,6 +134,54 @@ namespace LibHac
|
||||
=> TryAddReferenceImpl(_instance, _boxedReferenceCount) ??
|
||||
throw new ObjectDisposedException(nameof(ReferenceCountedDisposable<T>));
|
||||
|
||||
/// <summary>
|
||||
/// Increments the reference count for the disposable object, and returns a new disposable reference to it
|
||||
/// of type <typeparamref name="TTo"/>. The type of the disposable object must be compatible with type
|
||||
/// <typeparamref name="TTo"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The returned object is an independent reference to the same underlying object. Disposing of the
|
||||
/// returned value multiple times will only cause the reference count to be decreased once.</para>
|
||||
/// </remarks>
|
||||
/// <typeparam name="TTo">The type of the new reference to the disposable object.</typeparam>
|
||||
/// <returns>A new <see cref="ReferenceCountedDisposable{TTo}"/> pointing to the same underlying object, if it
|
||||
/// has not yet been disposed; otherwise, <see cref="ObjectDisposedException"/> is thrown if this reference
|
||||
/// to the underlying object has already been disposed, and <see cref="InvalidCastException"/> is thrown if
|
||||
/// <typeparamref name="T"/> is not compatible with <typeparamref name="TTo"/>.</returns>
|
||||
public ReferenceCountedDisposable<TTo> AddReference<TTo>() where TTo : class, IDisposable
|
||||
{
|
||||
ReferenceCountedDisposable<TTo>? newReference =
|
||||
TryAddReferenceImpl<T, TTo>(_instance, _boxedReferenceCount, out CreateResult result);
|
||||
|
||||
if (newReference != null)
|
||||
{
|
||||
return newReference;
|
||||
}
|
||||
|
||||
throw result switch
|
||||
{
|
||||
CreateResult.Disposed => new ObjectDisposedException(nameof(ReferenceCountedDisposable<T>)),
|
||||
CreateResult.NotCastable => new InvalidCastException(),
|
||||
_ => new NotSupportedException("This exception should never be thrown.")
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increments the reference count for the disposable object, and returns a new disposable reference to it
|
||||
/// of type <typeparamref name="TTo"/>. The type of the disposable object must be compatible with type
|
||||
/// <typeparamref name="TTo"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The returned object is an independent reference to the same underlying object. Disposing of the
|
||||
/// returned value multiple times will only cause the reference count to be decreased once.</para>
|
||||
/// </remarks>
|
||||
/// <returns>A new <see cref="ReferenceCountedDisposable{TTo}"/> pointing to the same underlying object, if it
|
||||
/// has not yet been disposed, and <typeparamref name="T"/> is compatible with <typeparamref name="TTo"/>;
|
||||
/// otherwise, <see langword="null"/> if this reference to the underlying object has already been disposed.
|
||||
/// </returns>
|
||||
public ReferenceCountedDisposable<TTo>? TryAddReference<TTo>() where TTo : class, IDisposable
|
||||
=> TryAddReferenceImpl<T, TTo>(_instance, _boxedReferenceCount, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Increments the reference count for the disposable object, and returns a new disposable reference to it.
|
||||
/// </summary>
|
||||
@ -179,6 +227,51 @@ namespace LibHac
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides the implementation for <see cref="TryAddReference"/> and
|
||||
/// <see cref="WeakReference.TryAddReference"/> when casting the underlying object to another type.
|
||||
/// </summary>
|
||||
private static ReferenceCountedDisposable<TTo>? TryAddReferenceImpl<TFrom, TTo>(TFrom? target, StrongBox<int> referenceCount,
|
||||
out CreateResult result)
|
||||
where TFrom : class, IDisposable
|
||||
where TTo : class, IDisposable
|
||||
{
|
||||
lock (referenceCount)
|
||||
{
|
||||
if (referenceCount.Value == 0)
|
||||
{
|
||||
// The target is already disposed, and cannot be reused
|
||||
result = CreateResult.Disposed;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
// The current reference has been disposed, so even though it isn't disposed yet we don't have a
|
||||
// reference to the target
|
||||
result = CreateResult.Disposed;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!(target is TTo castedTarget))
|
||||
{
|
||||
// The target cannot be casted to type TTo
|
||||
result = CreateResult.NotCastable;
|
||||
return null;
|
||||
}
|
||||
|
||||
checked
|
||||
{
|
||||
referenceCount.Value++;
|
||||
}
|
||||
|
||||
// Must return a new instance, in order for the Dispose operation on each individual instance to
|
||||
// be idempotent.
|
||||
result = CreateResult.Success;
|
||||
return new ReferenceCountedDisposable<TTo>(castedTarget, referenceCount);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the current reference, causing the underlying object to be disposed if this was the last
|
||||
/// reference.
|
||||
@ -212,6 +305,16 @@ namespace LibHac
|
||||
instanceToDispose?.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the result of the TryAddReferenceImpl method.
|
||||
/// </summary>
|
||||
private enum CreateResult
|
||||
{
|
||||
Success,
|
||||
Disposed,
|
||||
NotCastable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a weak reference to a <see cref="ReferenceCountedDisposable{T}"/> which is capable of
|
||||
/// obtaining a new counted reference up until the point when the object is no longer accessible.
|
||||
|
Loading…
x
Reference in New Issue
Block a user