mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add SaveDataIndexer
This commit is contained in:
parent
dee7c93285
commit
d330d11de2
13 changed files with 533 additions and 16 deletions
|
@ -137,5 +137,6 @@
|
|||
|
||||
public static Result SubStorageNotInitialized => new Result(ModuleFs, 6902);
|
||||
public static Result MountNameNotFound => new Result(ModuleFs, 6905);
|
||||
public static Result Result6906 => new Result(ModuleFs, 6906);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
public static ResultRange FatFsCorrupted => new ResultRange(ResultFs.ModuleFs, 4681, 4699);
|
||||
public static ResultRange HostFsCorrupted => new ResultRange(ResultFs.ModuleFs, 4701, 4719);
|
||||
public static ResultRange FileTableCorrupted => new ResultRange(ResultFs.ModuleFs, 4721, 4739);
|
||||
public static ResultRange Range4771To4779 => new ResultRange(ResultFs.ModuleFs, 4771, 4779);
|
||||
public static ResultRange Range4811To4819 => new ResultRange(ResultFs.ModuleFs, 4811, 4819);
|
||||
|
||||
public static ResultRange UnexpectedFailure => new ResultRange(ResultFs.ModuleFs, 5000, 5999);
|
||||
|
|
|
@ -1,10 +1,68 @@
|
|||
using LibHac.Common;
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.FsService;
|
||||
using LibHac.FsSystem.Save;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
public static class SaveData
|
||||
{
|
||||
public static Result MountSaveData(this FileSystemClient fs, U8Span mountName, TitleId titleId, UserId userId)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (fs.IsEnabledAccessLog(LocalAccessLogMode.Application))
|
||||
{
|
||||
TimeSpan startTime = fs.Time.GetCurrent();
|
||||
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, titleId, userId, SaveDataType.SaveData, false, 0);
|
||||
TimeSpan endTime = fs.Time.GetCurrent();
|
||||
|
||||
fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\", applicationid: 0x{titleId}, userid: {userId}");
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, titleId, userId, SaveDataType.SaveData, false, 0);
|
||||
}
|
||||
|
||||
if (rc.IsSuccess() && fs.IsEnabledAccessLog(LocalAccessLogMode.Application))
|
||||
{
|
||||
fs.EnableFileSystemAccessorAccessLog(mountName);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
private static Result MountSaveDataImpl(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId,
|
||||
TitleId titleId, UserId userId, SaveDataType type, bool openReadOnly, short index)
|
||||
{
|
||||
Result rc = MountHelpers.CheckMountName(mountName);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
SaveDataAttribute attribute = default;
|
||||
attribute.TitleId = titleId;
|
||||
attribute.UserId = userId;
|
||||
attribute.Type = type;
|
||||
attribute.Index = index;
|
||||
|
||||
IFileSystem saveFs;
|
||||
|
||||
if (openReadOnly)
|
||||
{
|
||||
rc = fsProxy.OpenReadOnlySaveDataFileSystem(out saveFs, spaceId, ref attribute);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = fsProxy.OpenSaveDataFileSystem(out saveFs, spaceId, ref attribute);
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fs.Register(mountName, saveFs);
|
||||
}
|
||||
|
||||
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, ulong saveDataId)
|
||||
{
|
||||
return MountSystemSaveData(fs, mountName, spaceId, saveDataId, UserId.Zero);
|
||||
|
@ -98,6 +156,17 @@ namespace LibHac.Fs
|
|||
() => $", savedataid: 0x{saveDataId:X}");
|
||||
}
|
||||
|
||||
public static Result DeleteSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId)
|
||||
{
|
||||
return fs.RunOperationWithAccessLog(LocalAccessLogMode.System,
|
||||
() =>
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
return fsProxy.DeleteSaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId);
|
||||
},
|
||||
() => $", savedataspaceid: {spaceId}, savedataid: 0x{saveDataId:X}");
|
||||
}
|
||||
|
||||
public static Result DisableAutoSaveDataCreation(this FileSystemClient fsClient)
|
||||
{
|
||||
IFileSystemProxy fsProxy = fsClient.GetFileSystemProxyServiceObject();
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace LibHac.Fs
|
|||
[StructLayout(LayoutKind.Explicit, Size = 0x40)]
|
||||
public struct SaveDataAttribute : IEquatable<SaveDataAttribute>, IComparable<SaveDataAttribute>
|
||||
{
|
||||
[FieldOffset(0x00)] public ulong TitleId;
|
||||
[FieldOffset(0x00)] public TitleId TitleId;
|
||||
[FieldOffset(0x08)] public UserId UserId;
|
||||
[FieldOffset(0x18)] public ulong SaveDataId;
|
||||
[FieldOffset(0x20)] public SaveDataType Type;
|
||||
|
@ -71,7 +71,7 @@ namespace LibHac.Fs
|
|||
[FieldOffset(0x04)] public bool FilterByIndex;
|
||||
[FieldOffset(0x05)] public byte Rank;
|
||||
|
||||
[FieldOffset(0x08)] public TitleId TitleID;
|
||||
[FieldOffset(0x08)] public TitleId TitleId;
|
||||
[FieldOffset(0x10)] public UserId UserId;
|
||||
[FieldOffset(0x20)] public ulong SaveDataId;
|
||||
[FieldOffset(0x28)] public SaveDataType SaveDataType;
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace LibHac.Fs
|
|||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"0x{Id.High:x8}{Id.Low:x8}";
|
||||
return $"0x{Id.High:X16}{Id.Low:X16}";
|
||||
}
|
||||
|
||||
public bool Equals(UserId other) => Id == other.Id;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.FsSystem.Save;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Spl;
|
||||
|
||||
|
@ -14,6 +15,7 @@ namespace LibHac.FsService
|
|||
/// <summary>The client instance to be used for internal operations like save indexer access.</summary>
|
||||
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||
private FileSystemClient FsClient { get; }
|
||||
private FileSystemServer FsServer { get; }
|
||||
|
||||
public long CurrentProcess { get; private set; }
|
||||
|
||||
|
@ -24,10 +26,11 @@ namespace LibHac.FsService
|
|||
|
||||
private const ulong SaveIndexerId = 0x8000000000000000;
|
||||
|
||||
internal FileSystemProxy(FileSystemProxyCore fsProxyCore, FileSystemClient fsClient)
|
||||
internal FileSystemProxy(FileSystemProxyCore fsProxyCore, FileSystemClient fsClient, FileSystemServer fsServer)
|
||||
{
|
||||
FsProxyCore = fsProxyCore;
|
||||
FsClient = fsClient;
|
||||
FsServer = fsServer;
|
||||
|
||||
CurrentProcess = -1;
|
||||
SaveDataSize = 0x2000000;
|
||||
|
@ -147,6 +150,9 @@ namespace LibHac.FsService
|
|||
private Result OpenSaveDataFileSystemImpl(out IFileSystem fileSystem, out ulong saveDataId,
|
||||
SaveDataSpaceId spaceId, ref SaveDataAttribute attribute, bool openReadOnly, bool cacheExtraData)
|
||||
{
|
||||
fileSystem = default;
|
||||
saveDataId = default;
|
||||
|
||||
bool hasFixedId = attribute.SaveDataId != 0 && attribute.UserId == UserId.Zero;
|
||||
|
||||
if (hasFixedId)
|
||||
|
@ -155,7 +161,25 @@ namespace LibHac.FsService
|
|||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
SaveDataAttribute indexerKey = attribute;
|
||||
|
||||
Result rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out SaveDataIndexerReader reader, spaceId);
|
||||
using SaveDataIndexerReader c = reader;
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
c.Indexer.Get(out SaveDataIndexerValue indexerValue, ref indexerKey);
|
||||
|
||||
SaveDataSpaceId indexerSpaceId = spaceId == SaveDataSpaceId.ProperSystem || spaceId == SaveDataSpaceId.Safe
|
||||
? SaveDataSpaceId.System
|
||||
: spaceId;
|
||||
|
||||
if (indexerValue.SpaceId != indexerSpaceId)
|
||||
return ResultFs.TargetNotFound.Log();
|
||||
|
||||
if (indexerValue.State == 4)
|
||||
return ResultFs.Result6906.Log();
|
||||
|
||||
saveDataId = indexerValue.SaveDataId;
|
||||
}
|
||||
|
||||
Result saveFsResult = FsProxyCore.OpenSaveDataFileSystem(out fileSystem, spaceId, saveDataId,
|
||||
|
@ -167,7 +191,6 @@ namespace LibHac.FsService
|
|||
|
||||
if (saveDataId != SaveIndexerId)
|
||||
{
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||
if (hasFixedId)
|
||||
{
|
||||
// todo: remove save indexer entry
|
||||
|
@ -177,15 +200,61 @@ namespace LibHac.FsService
|
|||
return ResultFs.TargetNotFound;
|
||||
}
|
||||
|
||||
private Result OpenSaveDataFileSystem3(out IFileSystem fileSystem, SaveDataSpaceId spaceId,
|
||||
ref SaveDataAttribute attribute, bool openReadOnly)
|
||||
{
|
||||
// Missing check if the open limit has been hit
|
||||
|
||||
Result rc = OpenSaveDataFileSystemImpl(out fileSystem, out _, spaceId, ref attribute, openReadOnly, true);
|
||||
|
||||
// Missing permission check based on the save's owner ID,
|
||||
// speed emulation storage type wrapper, and FileSystemInterfaceAdapter
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
private Result OpenSaveDataFileSystem2(out IFileSystem fileSystem, SaveDataSpaceId spaceId,
|
||||
ref SaveDataAttribute attribute, bool openReadOnly)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
// Missing permission checks
|
||||
|
||||
SaveDataAttribute attributeCopy;
|
||||
|
||||
if (attribute.TitleId == TitleId.Zero)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
else
|
||||
{
|
||||
attributeCopy = attribute;
|
||||
}
|
||||
|
||||
SaveDataSpaceId actualSpaceId;
|
||||
|
||||
if (attributeCopy.Type == SaveDataType.CacheStorage)
|
||||
{
|
||||
// Check whether the save is on the SD card or the BIS
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
else
|
||||
{
|
||||
actualSpaceId = spaceId;
|
||||
}
|
||||
|
||||
return OpenSaveDataFileSystem3(out fileSystem, actualSpaceId, ref attributeCopy, openReadOnly);
|
||||
}
|
||||
|
||||
public Result OpenSaveDataFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return OpenSaveDataFileSystem2(out fileSystem, spaceId, ref attribute, false);
|
||||
}
|
||||
|
||||
public Result OpenReadOnlySaveDataFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId,
|
||||
ref SaveDataAttribute attribute)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return OpenSaveDataFileSystem2(out fileSystem, spaceId, ref attribute, true);
|
||||
}
|
||||
|
||||
public Result OpenSaveDataFileSystemBySystemSaveDataId(out IFileSystem fileSystem, SaveDataSpaceId spaceId,
|
||||
|
|
|
@ -6,22 +6,26 @@ namespace LibHac.FsService
|
|||
{
|
||||
public class FileSystemServer
|
||||
{
|
||||
internal const ulong SaveDataIndexerSaveId = 0x8000000000000000;
|
||||
|
||||
private FileSystemProxyCore FsProxyCore { get; }
|
||||
|
||||
/// <summary>The client instance to be used for internal operations like save indexer access.</summary>
|
||||
private FileSystemClient FsClient { get; }
|
||||
private ITimeSpanGenerator Timer { get; }
|
||||
|
||||
internal SaveDataIndexerManager SaveDataIndexerManager { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="FileSystemServer"/>.
|
||||
/// </summary>
|
||||
/// <param name="config">The configuration for the created <see cref="FileSystemServer"/>.</param>
|
||||
public FileSystemServer(FileSystemServerConfig config)
|
||||
{
|
||||
if(config.FsCreators == null)
|
||||
if (config.FsCreators == null)
|
||||
throw new ArgumentException("FsCreators must not be null");
|
||||
|
||||
if(config.DeviceOperator == null)
|
||||
if (config.DeviceOperator == null)
|
||||
throw new ArgumentException("DeviceOperator must not be null");
|
||||
|
||||
ExternalKeySet externalKeySet = config.ExternalKeySet ?? new ExternalKeySet();
|
||||
|
@ -30,6 +34,8 @@ namespace LibHac.FsService
|
|||
FsProxyCore = new FileSystemProxyCore(config.FsCreators, externalKeySet, config.DeviceOperator);
|
||||
FsClient = new FileSystemClient(this, timer);
|
||||
Timer = timer;
|
||||
|
||||
SaveDataIndexerManager = new SaveDataIndexerManager(FsClient, SaveDataIndexerSaveId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -52,7 +58,7 @@ namespace LibHac.FsService
|
|||
|
||||
public IFileSystemProxy CreateFileSystemProxyService()
|
||||
{
|
||||
return new FileSystemProxy(FsProxyCore, FsClient);
|
||||
return new FileSystemProxy(FsProxyCore, FsClient, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
9
src/LibHac/FsService/ISaveDataIndexer.cs
Normal file
9
src/LibHac/FsService/ISaveDataIndexer.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using LibHac.Fs;
|
||||
|
||||
namespace LibHac.FsService
|
||||
{
|
||||
public interface ISaveDataIndexer
|
||||
{
|
||||
Result Get(out SaveDataIndexerValue value, ref SaveDataAttribute key);
|
||||
}
|
||||
}
|
219
src/LibHac/FsService/SaveDataIndexer.cs
Normal file
219
src/LibHac/FsService/SaveDataIndexer.cs
Normal file
|
@ -0,0 +1,219 @@
|
|||
using System.Diagnostics;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Kvdb;
|
||||
|
||||
namespace LibHac.FsService
|
||||
{
|
||||
public class SaveDataIndexer : ISaveDataIndexer
|
||||
{
|
||||
private const string LastIdFileName = "lastPublishedId";
|
||||
private const long LastIdFileSize = 8;
|
||||
|
||||
private FileSystemClient FsClient { get; }
|
||||
private string MountName { get; }
|
||||
private ulong SaveDataId { get; }
|
||||
private SaveDataSpaceId SpaceId { get; }
|
||||
private KeyValueDatabase<SaveDataAttribute> KvDatabase { get; set; }
|
||||
private object Locker { get; } = new object();
|
||||
private bool IsInitialized { get; set; }
|
||||
private bool IsKvdbLoaded { get; set; }
|
||||
private long LastPublishedId { get; set; }
|
||||
|
||||
public SaveDataIndexer(FileSystemClient fsClient, string mountName, SaveDataSpaceId spaceId, ulong saveDataId)
|
||||
{
|
||||
FsClient = fsClient;
|
||||
MountName = mountName;
|
||||
SaveDataId = saveDataId;
|
||||
SpaceId = spaceId;
|
||||
}
|
||||
|
||||
public Result Get(out SaveDataIndexerValue value, ref SaveDataAttribute key)
|
||||
{
|
||||
value = default;
|
||||
|
||||
lock (Locker)
|
||||
{
|
||||
Result rc = Initialize();
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = EnsureKvDatabaseLoaded(false);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = KvDatabase.Get(ref key, SpanHelpers.AsByteSpan(ref value));
|
||||
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
return ResultFs.TargetNotFound.LogConverted(rc);
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
private Result Initialize()
|
||||
{
|
||||
if (IsInitialized) return Result.Success;
|
||||
|
||||
var mount = new Mounter();
|
||||
|
||||
try
|
||||
{
|
||||
Result rc = mount.Initialize(FsClient, MountName, SpaceId, SaveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
string dbDirectory = $"{MountName}:/";
|
||||
|
||||
rc = FsClient.GetEntryType(out DirectoryEntryType entryType, dbDirectory);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (entryType == DirectoryEntryType.File)
|
||||
return ResultFs.PathNotFound.Log();
|
||||
|
||||
string dbArchiveFile = $"{dbDirectory}imkvdb.arc";
|
||||
|
||||
KvDatabase = new KeyValueDatabase<SaveDataAttribute>(FsClient, dbArchiveFile);
|
||||
|
||||
IsInitialized = true;
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
mount.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private Result EnsureKvDatabaseLoaded(bool forceLoad)
|
||||
{
|
||||
Debug.Assert(KvDatabase != null);
|
||||
|
||||
if (forceLoad)
|
||||
{
|
||||
IsKvdbLoaded = false;
|
||||
}
|
||||
else if (IsKvdbLoaded)
|
||||
{
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
var mount = new Mounter();
|
||||
|
||||
try
|
||||
{
|
||||
Result rc = mount.Initialize(FsClient, MountName, SpaceId, SaveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = KvDatabase.ReadDatabaseFromFile();
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
bool createdNewFile = false;
|
||||
string idFilePath = $"{MountName}:/{LastIdFileName}";
|
||||
|
||||
rc = FsClient.OpenFile(out FileHandle handle, idFilePath, OpenMode.Read);
|
||||
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
if (rc != ResultFs.PathNotFound) return rc;
|
||||
|
||||
rc = FsClient.CreateFile(idFilePath, LastIdFileSize);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = FsClient.OpenFile(out handle, idFilePath, OpenMode.Read);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
createdNewFile = true;
|
||||
|
||||
LastPublishedId = 0;
|
||||
IsKvdbLoaded = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!createdNewFile)
|
||||
{
|
||||
long lastId = default;
|
||||
|
||||
rc = FsClient.ReadFile(handle, 0, SpanHelpers.AsByteSpan(ref lastId));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
LastPublishedId = lastId;
|
||||
IsKvdbLoaded = true;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
FsClient.CloseFile(handle);
|
||||
|
||||
if (createdNewFile)
|
||||
{
|
||||
FsClient.Commit(MountName);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mount.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private ref struct Mounter
|
||||
{
|
||||
private FileSystemClient FsClient { get; set; }
|
||||
private string MountName { get; set; }
|
||||
private bool IsMounted { get; set; }
|
||||
|
||||
public Result Initialize(FileSystemClient fsClient, string mountName, SaveDataSpaceId spaceId,
|
||||
ulong saveDataId)
|
||||
{
|
||||
FsClient = fsClient;
|
||||
MountName = mountName;
|
||||
|
||||
FsClient.DisableAutoSaveDataCreation();
|
||||
|
||||
Result rc = FsClient.MountSystemSaveData(MountName.ToU8Span(), spaceId, saveDataId);
|
||||
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
if (rc == ResultFs.TargetNotFound)
|
||||
{
|
||||
rc = FsClient.CreateSystemSaveData(spaceId, saveDataId, 0, 0xC0000, 0xC0000, 0);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = FsClient.MountSystemSaveData(MountName.ToU8Span(), spaceId, saveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ResultRangeFs.Range4771To4779.Contains(rc)) return rc;
|
||||
if (!ResultRangeFs.DataCorrupted.Contains(rc)) return rc;
|
||||
|
||||
if (spaceId == SaveDataSpaceId.SdSystem) return rc;
|
||||
|
||||
rc = FsClient.DeleteSaveData(spaceId, saveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = FsClient.CreateSystemSaveData(spaceId, saveDataId, 0, 0xC0000, 0xC0000, 0);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = FsClient.MountSystemSaveData(MountName.ToU8Span(), spaceId, saveDataId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
}
|
||||
|
||||
IsMounted = true;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (IsMounted)
|
||||
{
|
||||
FsClient.Unmount(MountName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
115
src/LibHac/FsService/SaveDataIndexerManager.cs
Normal file
115
src/LibHac/FsService/SaveDataIndexerManager.cs
Normal file
|
@ -0,0 +1,115 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using LibHac.Fs;
|
||||
|
||||
namespace LibHac.FsService
|
||||
{
|
||||
internal class SaveDataIndexerManager
|
||||
{
|
||||
private FileSystemClient FsClient { get; }
|
||||
private ulong SaveDataId { get; }
|
||||
|
||||
private IndexerHolder _bisIndexer = new IndexerHolder(new object());
|
||||
private IndexerHolder _sdCardIndexer = new IndexerHolder(new object());
|
||||
private IndexerHolder _safeIndexer = new IndexerHolder(new object());
|
||||
private IndexerHolder _properSystemIndexer = new IndexerHolder(new object());
|
||||
|
||||
public SaveDataIndexerManager(FileSystemClient fsClient, ulong saveDataId)
|
||||
{
|
||||
FsClient = fsClient;
|
||||
SaveDataId = saveDataId;
|
||||
}
|
||||
|
||||
public Result GetSaveDataIndexer(out SaveDataIndexerReader reader, SaveDataSpaceId spaceId)
|
||||
{
|
||||
switch (spaceId)
|
||||
{
|
||||
case SaveDataSpaceId.System:
|
||||
case SaveDataSpaceId.User:
|
||||
Monitor.Enter(_bisIndexer.Locker);
|
||||
|
||||
if (!_bisIndexer.IsInitialized)
|
||||
{
|
||||
_bisIndexer.Indexer = new SaveDataIndexer(FsClient, "saveDataIxrDb", SaveDataSpaceId.System, SaveDataId);
|
||||
}
|
||||
|
||||
reader = new SaveDataIndexerReader(_bisIndexer.Indexer, _bisIndexer.Locker);
|
||||
return Result.Success;
|
||||
|
||||
case SaveDataSpaceId.SdSystem:
|
||||
case SaveDataSpaceId.SdCache:
|
||||
Monitor.Enter(_sdCardIndexer.Locker);
|
||||
|
||||
// Missing reinitialize if SD handle is old
|
||||
|
||||
if (!_sdCardIndexer.IsInitialized)
|
||||
{
|
||||
_sdCardIndexer.Indexer = new SaveDataIndexer(FsClient, "saveDataIxrDbSd", SaveDataSpaceId.SdSystem, SaveDataId);
|
||||
}
|
||||
|
||||
reader = new SaveDataIndexerReader(_bisIndexer.Indexer, _bisIndexer.Locker);
|
||||
return Result.Success;
|
||||
|
||||
case SaveDataSpaceId.TemporaryStorage:
|
||||
throw new NotImplementedException();
|
||||
|
||||
case SaveDataSpaceId.ProperSystem:
|
||||
Monitor.Enter(_safeIndexer.Locker);
|
||||
|
||||
if (!_safeIndexer.IsInitialized)
|
||||
{
|
||||
_safeIndexer.Indexer = new SaveDataIndexer(FsClient, "saveDataIxrDbPr", SaveDataSpaceId.ProperSystem, SaveDataId);
|
||||
}
|
||||
|
||||
reader = new SaveDataIndexerReader(_safeIndexer.Indexer, _safeIndexer.Locker);
|
||||
return Result.Success;
|
||||
|
||||
case SaveDataSpaceId.Safe:
|
||||
Monitor.Enter(_properSystemIndexer.Locker);
|
||||
|
||||
if (!_properSystemIndexer.IsInitialized)
|
||||
{
|
||||
_properSystemIndexer.Indexer = new SaveDataIndexer(FsClient, "saveDataIxrDbSf", SaveDataSpaceId.Safe, SaveDataId);
|
||||
}
|
||||
|
||||
reader = new SaveDataIndexerReader(_properSystemIndexer.Indexer, _properSystemIndexer.Locker);
|
||||
return Result.Success;
|
||||
|
||||
default:
|
||||
reader = default;
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
}
|
||||
|
||||
private struct IndexerHolder
|
||||
{
|
||||
public object Locker { get; }
|
||||
public ISaveDataIndexer Indexer { get; set; }
|
||||
|
||||
public IndexerHolder(object locker)
|
||||
{
|
||||
Locker = locker;
|
||||
Indexer = null;
|
||||
}
|
||||
|
||||
public bool IsInitialized => Indexer != null;
|
||||
}
|
||||
}
|
||||
|
||||
public ref struct SaveDataIndexerReader
|
||||
{
|
||||
private object Locker;
|
||||
public ISaveDataIndexer Indexer;
|
||||
|
||||
internal SaveDataIndexerReader(ISaveDataIndexer indexer, object locker)
|
||||
{
|
||||
Locker = locker;
|
||||
Indexer = indexer;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Monitor.Exit(Locker);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ using LibHac.Fs;
|
|||
namespace LibHac.FsService
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x40)]
|
||||
public struct SaveDataIndexerEntry
|
||||
public struct SaveDataIndexerValue
|
||||
{
|
||||
[FieldOffset(0x00)] public ulong SaveDataId;
|
||||
[FieldOffset(0x08)] public ulong Size;
|
|
@ -1,10 +1,13 @@
|
|||
using System.Diagnostics;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace LibHac.Ncm
|
||||
{
|
||||
[DebuggerDisplay("{" + nameof(Value) + "}")]
|
||||
public struct TitleId
|
||||
public struct TitleId : IEquatable<TitleId>, IComparable<TitleId>, IComparable
|
||||
{
|
||||
public static TitleId Zero => default;
|
||||
|
||||
public readonly ulong Value;
|
||||
|
||||
public TitleId(ulong value)
|
||||
|
@ -13,5 +16,20 @@ namespace LibHac.Ncm
|
|||
}
|
||||
|
||||
public static explicit operator ulong(TitleId titleId) => titleId.Value;
|
||||
|
||||
public override string ToString() => $"{Value:X16}";
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return 1;
|
||||
return obj is TitleId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(TitleId)}");
|
||||
}
|
||||
|
||||
public int CompareTo(TitleId other) => Value.CompareTo(other.Value);
|
||||
public bool Equals(TitleId other) => Value == other.Value;
|
||||
public override bool Equals(object obj) => obj is TitleId other && Equals(other);
|
||||
public override int GetHashCode() => Value.GetHashCode();
|
||||
public static bool operator ==(TitleId left, TitleId right) => left.Equals(right);
|
||||
public static bool operator !=(TitleId left, TitleId right) => !left.Equals(right);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,16 @@ namespace LibHac
|
|||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="Log"/>, but for when one result is converted to another.
|
||||
/// </summary>
|
||||
/// <param name="originalResult">The original <see cref="Result"/> value.</param>
|
||||
/// <returns>The called <see cref="Result"/> value.</returns>
|
||||
public Result LogConverted(Result originalResult)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return IsSuccess() ? "Success" : ErrorCode;
|
||||
|
|
Loading…
Reference in a new issue