Add some SaveDataInfoReader functions

This commit is contained in:
Alex Barney 2019-10-22 17:52:55 -05:00
parent 3af543e4e1
commit f0aac13fab
10 changed files with 426 additions and 36 deletions

View file

@ -108,7 +108,7 @@ namespace LibHac.Fs
int mountLen = 0; int mountLen = 0;
int maxMountLen = Math.Min(path.Length, PathTools.MountNameLength); int maxMountLen = Math.Min(path.Length, PathTools.MountNameLength);
for (int i = 0; i < maxMountLen; i++) for (int i = 0; i <= maxMountLen; i++)
{ {
if (path[i] == PathTools.MountSeparator) if (path[i] == PathTools.MountSeparator)
{ {
@ -122,7 +122,7 @@ namespace LibHac.Fs
mountName = default; mountName = default;
subPath = default; subPath = default;
return ResultFs.InvalidMountName; return ResultFs.InvalidMountName.Log();
} }
mountName = path.Slice(0, mountLen); mountName = path.Slice(0, mountLen);

View file

@ -97,6 +97,8 @@
public static Result InvalidMountName => new Result(ModuleFs, 6065); public static Result InvalidMountName => new Result(ModuleFs, 6065);
public static Result ExtensionSizeTooLarge => new Result(ModuleFs, 6066); public static Result ExtensionSizeTooLarge => new Result(ModuleFs, 6066);
public static Result ExtensionSizeInvalid => new Result(ModuleFs, 6067); public static Result ExtensionSizeInvalid => new Result(ModuleFs, 6067);
public static Result ReadOldSaveDataInfoReader => new Result(ModuleFs, 6068);
public static Result InvalidSaveDataSpaceId => new Result(ModuleFs, 6082);
public static Result InvalidOpenModeOperation => new Result(ModuleFs, 6200); public static Result InvalidOpenModeOperation => new Result(ModuleFs, 6200);
public static Result FileExtensionWithoutOpenModeAllowAppend => new Result(ModuleFs, 6201); public static Result FileExtensionWithoutOpenModeAllowAppend => new Result(ModuleFs, 6201);

View file

@ -78,30 +78,6 @@ namespace LibHac.Fs
[FieldOffset(0x2A)] public short Index; [FieldOffset(0x2A)] public short Index;
} }
[StructLayout(LayoutKind.Explicit, Size = 0x50)]
public struct SaveDataFilterInternal
{
[FieldOffset(0x00)] public bool FilterBySaveDataSpaceId;
[FieldOffset(0x01)] public SaveDataSpaceId SpaceId;
[FieldOffset(0x08)] public bool FilterByTitleId;
[FieldOffset(0x10)] public TitleId TitleID;
[FieldOffset(0x18)] public bool FilterBySaveDataType;
[FieldOffset(0x19)] public SaveDataType SaveDataType;
[FieldOffset(0x20)] public bool FilterByUserId;
[FieldOffset(0x28)] public UserId UserId;
[FieldOffset(0x38)] public bool FilterBySaveDataId;
[FieldOffset(0x40)] public ulong SaveDataId;
[FieldOffset(0x48)] public bool FilterByIndex;
[FieldOffset(0x4A)] public short Index;
[FieldOffset(0x4C)] public int Rank;
}
[StructLayout(LayoutKind.Explicit, Size = HashLength)] [StructLayout(LayoutKind.Explicit, Size = HashLength)]
public struct HashSalt public struct HashSalt
{ {

View file

@ -1,4 +1,7 @@
using LibHac.FsService; using System;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.FsService;
using LibHac.FsSystem.Save; using LibHac.FsSystem.Save;
using LibHac.Ncm; using LibHac.Ncm;
@ -159,6 +162,63 @@ namespace LibHac.Fs.Shim
() => $", savedataspaceid: {spaceId}, savedataid: 0x{saveDataId:X}"); () => $", savedataspaceid: {spaceId}, savedataid: 0x{saveDataId:X}");
} }
public static Result FindSaveDataWithFilter(this FileSystemClient fs, out SaveDataInfo info, SaveDataSpaceId spaceId,
ref SaveDataFilter filter)
{
info = default;
SaveDataFilter tempFilter = filter;
var tempInfo = new SaveDataInfo();
Result result = fs.RunOperationWithAccessLog(LocalAccessLogMode.System,
() =>
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
tempInfo = new SaveDataInfo();
Result rc = fsProxy.FindSaveDataWithFilter(out long count, SpanHelpers.AsByteSpan(ref tempInfo),
spaceId, ref tempFilter);
if (rc.IsFailure()) return rc;
if (count == 0)
return ResultFs.TargetNotFound.Log();
return Result.Success;
},
() => $", savedataspaceid: {spaceId}");
if (result.IsSuccess())
{
info = tempInfo;
}
return result;
}
public static Result OpenSaveDataIterator(this FileSystemClient fs, out SaveDataIterator iterator, SaveDataSpaceId spaceId)
{
var tempIterator = new SaveDataIterator();
Result result = fs.RunOperationWithAccessLog(LocalAccessLogMode.System,
() =>
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
Result rc = fsProxy.OpenSaveDataInfoReaderBySaveDataSpaceId(out ISaveDataInfoReader reader, spaceId);
if (rc.IsFailure()) return rc;
tempIterator = new SaveDataIterator(fs, reader);
return Result.Success;
},
() => $", savedataspaceid: {spaceId}");
iterator = result.IsSuccess() ? tempIterator : default;
return result;
}
public static Result DisableAutoSaveDataCreation(this FileSystemClient fsClient) public static Result DisableAutoSaveDataCreation(this FileSystemClient fsClient)
{ {
IFileSystemProxy fsProxy = fsClient.GetFileSystemProxyServiceObject(); IFileSystemProxy fsProxy = fsClient.GetFileSystemProxyServiceObject();
@ -166,4 +226,38 @@ namespace LibHac.Fs.Shim
return fsProxy.DisableAutoSaveDataCreation(); return fsProxy.DisableAutoSaveDataCreation();
} }
} }
public struct SaveDataIterator
{
private FileSystemClient FsClient { get; }
private ISaveDataInfoReader Reader { get; }
internal SaveDataIterator(FileSystemClient fsClient, ISaveDataInfoReader reader)
{
FsClient = fsClient;
Reader = reader;
}
public Result ReadSaveDataInfo(out long readCount, Span<SaveDataInfo> buffer)
{
Result rc;
Span<byte> byteBuffer = MemoryMarshal.Cast<SaveDataInfo, byte>(buffer);
if (FsClient.IsEnabledAccessLog(LocalAccessLogMode.System))
{
TimeSpan startTime = FsClient.Time.GetCurrent();
rc = Reader.ReadSaveDataInfo(out readCount, byteBuffer);
TimeSpan endTime = FsClient.Time.GetCurrent();
FsClient.OutputAccessLog(rc, startTime, endTime, $", size: {buffer.Length}");
}
else
{
rc = Reader.ReadSaveDataInfo(out readCount, byteBuffer);
}
return rc;
}
}
} }

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Runtime.CompilerServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.FsSystem; using LibHac.FsSystem;
@ -707,7 +708,34 @@ namespace LibHac.FsService
public Result OpenSaveDataInfoReaderBySaveDataSpaceId(out ISaveDataInfoReader infoReader, SaveDataSpaceId spaceId) public Result OpenSaveDataInfoReaderBySaveDataSpaceId(out ISaveDataInfoReader infoReader, SaveDataSpaceId spaceId)
{ {
throw new NotImplementedException(); infoReader = default;
// Missing permission check
SaveDataIndexerReader indexReader = default;
try
{
Result rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out indexReader, spaceId);
if (rc.IsFailure()) return rc;
rc = indexReader.Indexer.OpenSaveDataInfoReader(out ISaveDataInfoReader baseInfoReader);
if (rc.IsFailure()) return rc;
var filter = new SaveDataFilterInternal
{
FilterBySaveDataSpaceId = true,
SpaceId = GetSpaceIdForIndexer(spaceId)
};
infoReader = new SaveDataInfoFilterReader(baseInfoReader, ref filter);
return Result.Success;
}
finally
{
indexReader.Dispose();
}
} }
public Result OpenSaveDataInfoReaderWithFilter(out ISaveDataInfoReader infoReader, SaveDataSpaceId spaceId, public Result OpenSaveDataInfoReaderWithFilter(out ISaveDataInfoReader infoReader, SaveDataSpaceId spaceId,
@ -719,7 +747,46 @@ namespace LibHac.FsService
public Result FindSaveDataWithFilter(out long count, Span<byte> saveDataInfoBuffer, SaveDataSpaceId spaceId, public Result FindSaveDataWithFilter(out long count, Span<byte> saveDataInfoBuffer, SaveDataSpaceId spaceId,
ref SaveDataFilter filter) ref SaveDataFilter filter)
{ {
throw new NotImplementedException(); count = default;
if (saveDataInfoBuffer.Length != Unsafe.SizeOf<SaveDataInfo>())
{
return ResultFs.InvalidArgument.Log();
}
// Missing permission check
var internalFilter = new SaveDataFilterInternal(ref filter, GetSpaceIdForIndexer(spaceId));
ref SaveDataInfo saveDataInfo = ref Unsafe.As<byte, SaveDataInfo>(ref saveDataInfoBuffer[0]);
return FindSaveDataWithFilterImpl(out count, out saveDataInfo, spaceId, ref internalFilter);
}
private Result FindSaveDataWithFilterImpl(out long count, out SaveDataInfo info, SaveDataSpaceId spaceId,
ref SaveDataFilterInternal filter)
{
count = default;
info = default;
SaveDataIndexerReader indexReader = default;
try
{
Result rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out indexReader, spaceId);
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();
}
} }
public Result OpenSaveDataInternalStorageFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ulong saveDataId) public Result OpenSaveDataInternalStorageFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ulong saveDataId)

View file

@ -15,5 +15,6 @@ namespace LibHac.FsService
Result SetState(ulong saveDataId, SaveDataState state); Result SetState(ulong saveDataId, SaveDataState state);
Result GetKey(out SaveDataAttribute key, ulong saveDataId); Result GetKey(out SaveDataAttribute key, ulong saveDataId);
Result GetBySaveDataId(out SaveDataIndexerValue value, ulong saveDataId); Result GetBySaveDataId(out SaveDataIndexerValue value, ulong saveDataId);
Result OpenSaveDataInfoReader(out ISaveDataInfoReader infoReader);
} }
} }

View file

@ -1,6 +1,8 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Shim; using LibHac.Fs.Shim;
@ -23,6 +25,7 @@ namespace LibHac.FsService
private bool IsInitialized { get; set; } private bool IsInitialized { get; set; }
private bool IsKvdbLoaded { get; set; } private bool IsKvdbLoaded { get; set; }
private ulong LastPublishedId { get; set; } private ulong LastPublishedId { get; set; }
private int Version { get; set; }
public SaveDataIndexer(FileSystemClient fsClient, string mountName, SaveDataSpaceId spaceId, ulong saveDataId) public SaveDataIndexer(FileSystemClient fsClient, string mountName, SaveDataSpaceId spaceId, ulong saveDataId)
{ {
@ -30,6 +33,7 @@ namespace LibHac.FsService
MountName = mountName; MountName = mountName;
SaveDataId = saveDataId; SaveDataId = saveDataId;
SpaceId = spaceId; SpaceId = spaceId;
Version = 1;
} }
public static void GetSaveDataInfo(out SaveDataInfo info, ref SaveDataAttribute key, ref SaveDataIndexerValue value) public static void GetSaveDataInfo(out SaveDataInfo info, ref SaveDataAttribute key, ref SaveDataIndexerValue value)
@ -140,6 +144,9 @@ namespace LibHac.FsService
return rc; return rc;
} }
rc = AdjustOpenedInfoReaders(ref key);
if (rc.IsFailure()) return rc;
saveDataId = newSaveDataId; saveDataId = newSaveDataId;
return Result.Success; return Result.Success;
} }
@ -194,11 +201,10 @@ namespace LibHac.FsService
}; };
rc = KvDatabase.Set(ref key, SpanHelpers.AsByteSpan(ref newValue)); rc = KvDatabase.Set(ref key, SpanHelpers.AsByteSpan(ref newValue));
if (rc.IsFailure()) return rc;
if (rc.IsFailure()) rc = AdjustOpenedInfoReaders(ref key);
{ if (rc.IsFailure()) return rc;
// todo: Missing some function call here
}
return rc; return rc;
} }
@ -224,7 +230,10 @@ namespace LibHac.FsService
return ResultFs.TargetNotFound.Log(); return ResultFs.TargetNotFound.Log();
} }
return KvDatabase.Delete(ref key); rc = KvDatabase.Delete(ref key);
if (rc.IsFailure()) return rc;
return AdjustOpenedInfoReaders(ref key);
} }
} }
@ -333,6 +342,26 @@ namespace LibHac.FsService
} }
} }
public Result OpenSaveDataInfoReader(out ISaveDataInfoReader infoReader)
{
infoReader = default;
lock (Locker)
{
Result rc = Initialize();
if (rc.IsFailure()) return rc;
rc = EnsureKvDatabaseLoaded(false);
if (rc.IsFailure()) return rc;
var reader = new SaveDataInfoReader(this);
infoReader = reader;
return Result.Success;
}
}
private bool TryGetBySaveDataIdInternal(out SaveDataAttribute key, out SaveDataIndexerValue value, ulong saveDataId) private bool TryGetBySaveDataIdInternal(out SaveDataAttribute key, out SaveDataIndexerValue value, ulong saveDataId)
{ {
foreach (KeyValuePair<SaveDataAttribute, byte[]> kvp in KvDatabase) foreach (KeyValuePair<SaveDataAttribute, byte[]> kvp in KvDatabase)
@ -459,6 +488,12 @@ namespace LibHac.FsService
} }
} }
private Result AdjustOpenedInfoReaders(ref SaveDataAttribute key)
{
// todo
return Result.Success;
}
private ref struct Mounter private ref struct Mounter
{ {
private FileSystemClient FsClient { get; set; } private FileSystemClient FsClient { get; set; }
@ -516,5 +551,57 @@ namespace LibHac.FsService
} }
} }
} }
private class SaveDataInfoReader : ISaveDataInfoReader
{
private SaveDataIndexer Indexer { get; }
private int Version { get; }
public int Position { get; set; }
public SaveDataInfoReader(SaveDataIndexer indexer)
{
Indexer = indexer;
Version = indexer.Version;
}
public Result ReadSaveDataInfo(out long readCount, Span<byte> saveDataInfoBuffer)
{
readCount = default;
lock (Indexer.Locker)
{
// Indexer has been reloaded since this info reader was created
if (Version != Indexer.Version)
{
return ResultFs.ReadOldSaveDataInfoReader.Log();
}
// No more to iterate
if (Position == Indexer.KvDatabase.Count)
{
readCount = 0;
return Result.Success;
}
Span<SaveDataInfo> outInfo = MemoryMarshal.Cast<byte, SaveDataInfo>(saveDataInfoBuffer);
// Todo: A more efficient way of doing this
List<(SaveDataAttribute key, byte[] value)> list = Indexer.KvDatabase.ToList();
int i;
for (i = 0; i < outInfo.Length && Position < list.Count; i++, Position++)
{
SaveDataAttribute key = list[Position].key;
ref SaveDataIndexerValue value = ref Unsafe.As<byte, SaveDataIndexerValue>(ref list[Position].value[0]);
GetSaveDataInfo(out outInfo[i], ref key, ref value);
}
readCount = i;
return Result.Success;
}
}
}
} }
} }

View file

@ -47,7 +47,7 @@ namespace LibHac.FsService
_sdCardIndexer.Indexer = new SaveDataIndexer(FsClient, "saveDataIxrDbSd", SaveDataSpaceId.SdSystem, SaveDataId); _sdCardIndexer.Indexer = new SaveDataIndexer(FsClient, "saveDataIxrDbSd", SaveDataSpaceId.SdSystem, SaveDataId);
} }
reader = new SaveDataIndexerReader(_bisIndexer.Indexer, _bisIndexer.Locker); reader = new SaveDataIndexerReader(_sdCardIndexer.Indexer, _sdCardIndexer.Locker);
return Result.Success; return Result.Success;
case SaveDataSpaceId.TemporaryStorage: case SaveDataSpaceId.TemporaryStorage:

View file

@ -0,0 +1,156 @@
using System;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSystem.Save;
using LibHac.Ncm;
namespace LibHac.FsService
{
internal class SaveDataInfoFilterReader : ISaveDataInfoReader
{
private ISaveDataInfoReader Reader { get; }
private SaveDataFilterInternal Filter { get; }
public SaveDataInfoFilterReader(ISaveDataInfoReader reader, ref SaveDataFilterInternal filter)
{
Reader = reader;
Filter = filter;
}
public Result ReadSaveDataInfo(out long readCount, Span<byte> saveDataInfoBuffer)
{
readCount = default;
Span<SaveDataInfo> outInfo = MemoryMarshal.Cast<byte, SaveDataInfo>(saveDataInfoBuffer);
SaveDataInfo tempInfo = default;
Span<byte> tempInfoBytes = SpanHelpers.AsByteSpan(ref tempInfo);
int count = 0;
while (count < outInfo.Length)
{
Result rc = Reader.ReadSaveDataInfo(out long baseReadCount, tempInfoBytes);
if (rc.IsFailure()) return rc;
if (baseReadCount == 0) break;
if (Filter.Matches(ref tempInfo))
{
outInfo[count] = tempInfo;
count++;
}
}
readCount = count;
return Result.Success;
}
}
[StructLayout(LayoutKind.Explicit, Size = 0x50)]
internal struct SaveDataFilterInternal
{
[FieldOffset(0x00)] public bool FilterBySaveDataSpaceId;
[FieldOffset(0x01)] public SaveDataSpaceId SpaceId;
[FieldOffset(0x08)] public bool FilterByTitleId;
[FieldOffset(0x10)] public TitleId TitleId;
[FieldOffset(0x18)] public bool FilterBySaveDataType;
[FieldOffset(0x19)] public SaveDataType SaveDataType;
[FieldOffset(0x20)] public bool FilterByUserId;
[FieldOffset(0x28)] public UserId UserId;
[FieldOffset(0x38)] public bool FilterBySaveDataId;
[FieldOffset(0x40)] public ulong SaveDataId;
[FieldOffset(0x48)] public bool FilterByIndex;
[FieldOffset(0x4A)] public short Index;
[FieldOffset(0x4C)] public int Rank;
public SaveDataFilterInternal(ref SaveDataFilter filter, SaveDataSpaceId spaceId)
{
this = default;
FilterBySaveDataSpaceId = true;
SpaceId = spaceId;
Rank = filter.Rank;
if (filter.FilterByTitleId)
{
FilterByTitleId = true;
TitleId = filter.TitleId;
}
if (filter.FilterBySaveDataType)
{
FilterBySaveDataType = true;
SaveDataType = filter.SaveDataType;
}
if (filter.FilterByUserId)
{
FilterByUserId = true;
UserId = filter.UserId;
}
if (filter.FilterBySaveDataId)
{
FilterBySaveDataId = true;
SaveDataId = filter.SaveDataId;
}
if (filter.FilterByIndex)
{
FilterByIndex = true;
Index = filter.Index;
}
}
public bool Matches(ref SaveDataInfo info)
{
if (FilterBySaveDataSpaceId && info.SpaceId != SpaceId)
{
return false;
}
if (FilterByTitleId && info.TitleId != TitleId)
{
return false;
}
if (FilterBySaveDataType && info.Type != SaveDataType)
{
return false;
}
if (FilterByUserId && info.UserId != UserId)
{
return false;
}
if (FilterBySaveDataId && info.SaveDataId != SaveDataId)
{
return false;
}
if (FilterByIndex && info.Index != Index)
{
return false;
}
if ((Rank & 1) == 0 && info.Rank != 0)
{
return false;
}
return true;
}
}
}

View file

@ -15,6 +15,8 @@ namespace LibHac.Kvdb
private FileSystemClient FsClient { get; } private FileSystemClient FsClient { get; }
private string FileName { get; } private string FileName { get; }
public int Count => KvDict.Count;
public KeyValueDatabase() { } public KeyValueDatabase() { }
public KeyValueDatabase(FileSystemClient fsClient, string fileName) public KeyValueDatabase(FileSystemClient fsClient, string fileName)
@ -147,6 +149,11 @@ namespace LibHac.Kvdb
return size; return size;
} }
public List<(TKey key, byte[] value)> ToList()
{
return KvDict.OrderBy(x => x.Key).Select(entry => (entry.Key, entry.Value)).ToList();
}
private Result ReadFile(out byte[] data) private Result ReadFile(out byte[] data)
{ {
Debug.Assert(FsClient != null); Debug.Assert(FsClient != null);