Ensure all of Shim.SaveDataManagement is updated for 13.1.0

This commit is contained in:
Alex Barney 2022-01-18 20:45:48 -07:00
parent dc2a1cfaef
commit 74ed435dee
12 changed files with 2230 additions and 1845 deletions

View File

@ -1135,5 +1135,33 @@ namespace LibHac.Fs.Impl
(byte)',', (byte)' ', (byte)'f', (byte)'u', (byte)'n', (byte)'c', (byte)'t', (byte)'i',
(byte)'o', (byte)'n', (byte)':', (byte)' ', (byte)'"'
};
/// <summary>"<c>, cachestoragelist_handle: 0x</c>"</summary>
public static ReadOnlySpan<byte> LogCacheStorageListHandle =>
new[]
{
(byte)',', (byte)' ', (byte)'c', (byte)'a', (byte)'c', (byte)'h', (byte)'e', (byte)'s',
(byte)'t', (byte)'o', (byte)'r', (byte)'a', (byte)'g', (byte)'e', (byte)'l', (byte)'i',
(byte)'s', (byte)'t', (byte)'_', (byte)'h', (byte)'a', (byte)'n', (byte)'d', (byte)'l',
(byte)'e', (byte)':', (byte)' ', (byte)'0', (byte)'x'
};
/// <summary>"<c>, infobuffercount: 0x</c>"</summary>
public static ReadOnlySpan<byte> LogInfoBufferCount =>
new[]
{
(byte)',', (byte)' ', (byte)'i', (byte)'n', (byte)'f', (byte)'o', (byte)'b', (byte)'u',
(byte)'f', (byte)'f', (byte)'e', (byte)'r', (byte)'c', (byte)'o', (byte)'u', (byte)'n',
(byte)'t', (byte)':', (byte)' ', (byte)'0', (byte)'x'
};
/// <summary>"<c>, cache_storage_count: </c>"</summary>
public static ReadOnlySpan<byte> LogCacheStorageCount =>
new[]
{
(byte)',', (byte)' ', (byte)'c', (byte)'a', (byte)'c', (byte)'h', (byte)'e', (byte)'_',
(byte)'s', (byte)'t', (byte)'o', (byte)'r', (byte)'a', (byte)'g', (byte)'e', (byte)'_',
(byte)'c', (byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)':', (byte)' '
};
}
}

View File

@ -29,7 +29,7 @@ public static class ApplicationSaveDataManagement
Result CreateAccountSaveFunc()
{
UserId userId = ConvertAccountUidToFsUserId(uidLocal);
UserId userId = Utility.ConvertAccountUidToFsUserId(uidLocal);
return fs.CreateSaveData(applicationId, userId, saveDataOwnerId, accountSaveDataSize,
accountSaveJournalSize, SaveDataFlags.None);
}
@ -465,9 +465,4 @@ public static class ApplicationSaveDataManagement
return rc;
}
public static UserId ConvertAccountUidToFsUserId(Uid uid)
{
return new UserId(uid.Id.High, uid.Id.Low);
}
}

View File

@ -0,0 +1,19 @@
using LibHac.Common.FixedArrays;
namespace LibHac.Fs;
public readonly struct CacheStorageListHandle
{
internal readonly object Cache;
internal CacheStorageListHandle(object cache)
{
Cache = cache;
}
}
public struct CacheStorageInfo
{
public int Index;
public Array28<byte> Reserved;
}

View File

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs.Fsa;
@ -12,10 +14,14 @@ using LibHac.Sf;
using LibHac.Time;
using static LibHac.Fs.Impl.AccessLogStrings;
namespace LibHac.Fs.Shim;
public class SaveDataIterator : IDisposable
namespace LibHac.Fs
{
/// <summary>
/// Allows iterating through the <see cref="SaveDataInfo"/> of a list of save data.
/// </summary>
/// <remarks>Based on nnSdk 13.4.0</remarks>
public class SaveDataIterator : IDisposable
{
private readonly FileSystemClient _fsClient;
private SharedRef<ISaveDataInfoReader> _reader;
@ -65,11 +71,61 @@ public class SaveDataIterator : IDisposable
return Result.Success;
}
}
}
[SkipLocalsInit]
public static class SaveDataManagement
namespace LibHac.Fs.Shim
{
/// <summary>
/// Contains functions for creating, deleting, and otherwise managing save data.
/// </summary>
/// <remarks>Based on nnSdk 13.4.0</remarks>
[SkipLocalsInit]
public static class SaveDataManagement
{
private class CacheStorageListCache : IDisposable
{
public readonly struct CacheEntry
{
private readonly int _index;
public CacheEntry(int index) => _index = index;
public int GetCacheStorageIndex() => _index;
}
private int _position;
private List<CacheEntry> _entryList;
public CacheStorageListCache()
{
_position = 0;
_entryList = new List<CacheEntry>();
}
public void Dispose() { }
public Result PushBack(in CacheEntry entry)
{
_entryList.Add(entry);
// The original code can have allocation failures here
return Result.Success;
}
public ref readonly CacheEntry PopFront()
{
if (_position >= _entryList.Count)
return ref Unsafe.NullRef<CacheEntry>();
return ref CollectionsMarshal.AsSpan(_entryList)[_position++];
}
public static CacheStorageListCache GetCacheStorageListCache(CacheStorageListHandle handle)
{
return (CacheStorageListCache)handle.Cache;
}
}
public static Result ReadSaveDataFileSystemExtraData(this FileSystemClientImpl fs,
out SaveDataExtraData extraData, ulong saveDataId)
{
@ -241,7 +297,7 @@ public static class SaveDataManagement
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Account,
userId, 0);
userId, staticSaveDataId: 0);
if (rc.IsFailure()) return rc;
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags,
@ -263,7 +319,7 @@ public static class SaveDataManagement
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Bcat,
Fs.SaveData.InvalidUserId, 0);
Fs.SaveData.InvalidUserId, staticSaveDataId: 0);
if (rc.IsFailure()) return rc;
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size,
@ -289,7 +345,7 @@ public static class SaveDataManagement
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Device,
Fs.SaveData.InvalidUserId, 0);
Fs.SaveData.InvalidUserId, staticSaveDataId: 0);
if (rc.IsFailure()) return rc;
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags,
@ -311,7 +367,7 @@ public static class SaveDataManagement
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Cache,
Fs.SaveData.InvalidUserId, 0, index);
Fs.SaveData.InvalidUserId, staticSaveDataId: 0, index);
if (rc.IsFailure()) return rc;
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags,
@ -473,7 +529,7 @@ public static class SaveDataManagement
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, new ProgramId(applicationId.Value),
SaveDataType.Device, Fs.SaveData.InvalidUserId, 0);
SaveDataType.Device, Fs.SaveData.InvalidUserId, staticSaveDataId: 0);
if (rc.IsFailure()) return rc;
return fileSystemProxy.Get.DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId.User, in attribute);
@ -530,8 +586,8 @@ public static class SaveDataManagement
return Result.Success;
}
public static Result ReadSaveDataIteratorSaveDataInfo(out long readCount, Span<SaveDataInfo> buffer,
in SaveDataIterator iterator)
public static Result ReadSaveDataIteratorSaveDataInfo(this FileSystemClientImpl fs, out long readCount,
Span<SaveDataInfo> buffer, in SaveDataIterator iterator)
{
Result rc = iterator.ReadSaveDataInfo(out readCount, buffer);
if (rc.IsFailure()) return rc.Miss();
@ -687,7 +743,7 @@ public static class SaveDataManagement
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Account,
userId, 0);
userId, staticSaveDataId: 0);
if (rc.IsFailure()) return rc;
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags,
@ -764,10 +820,10 @@ public static class SaveDataManagement
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Temporary,
Fs.SaveData.InvalidUserId, 0);
Fs.SaveData.InvalidUserId, staticSaveDataId: 0);
if (rc.IsFailure()) return rc;
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, 0, ownerId, flags,
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize: 0, ownerId, flags,
SaveDataSpaceId.Temporary);
if (rc.IsFailure()) return rc;
@ -844,7 +900,7 @@ public static class SaveDataManagement
public static Result CreateCacheStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId,
SaveDataSpaceId spaceId, ulong ownerId, long size, long journalSize, SaveDataFlags flags)
{
return CreateCacheStorage(fs, applicationId, spaceId, ownerId, 0, size, journalSize, flags);
return CreateCacheStorage(fs, applicationId, spaceId, ownerId, index: 0, size, journalSize, flags);
}
public static Result CreateCacheStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId,
@ -912,7 +968,7 @@ public static class SaveDataManagement
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, UserId userId, long size,
long journalSize, SaveDataFlags flags)
{
return CreateSystemSaveData(fs, saveDataId, userId, 0, size, journalSize, flags);
return CreateSystemSaveData(fs, saveDataId, userId, ownerId: 0, size, journalSize, flags);
}
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, ulong ownerId, long size,
@ -924,7 +980,7 @@ public static class SaveDataManagement
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, long size,
long journalSize, SaveDataFlags flags)
{
return CreateSystemSaveData(fs, saveDataId, Fs.SaveData.InvalidUserId, 0, size, journalSize, flags);
return CreateSystemSaveData(fs, saveDataId, Fs.SaveData.InvalidUserId, ownerId: 0, size, journalSize, flags);
}
public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId,
@ -1006,22 +1062,23 @@ public static class SaveDataManagement
return rc;
}
public static Result QuerySaveDataTotalSize(this FileSystemClientImpl fs, out long totalSize, long size,
long journalSize)
public static Result QuerySaveDataTotalSize(this FileSystemClientImpl fs, out long totalSize, long saveDataSize,
long saveDataJournalSize)
{
UnsafeHelpers.SkipParamInit(out totalSize);
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
Result rc = fileSystemProxy.Get.QuerySaveDataTotalSize(out long tempTotalSize, size, journalSize);
Result rc = fileSystemProxy.Get.QuerySaveDataTotalSize(out long tempTotalSize, saveDataSize,
saveDataJournalSize);
if (rc.IsFailure()) return rc.Miss();
totalSize = tempTotalSize;
return Result.Success;
}
public static Result QuerySaveDataTotalSize(this FileSystemClient fs, out long totalSize, long size,
long journalSize)
public static Result QuerySaveDataTotalSize(this FileSystemClient fs, out long totalSize, long saveDataSize,
long saveDataJournalSize)
{
Result rc;
Span<byte> logBuffer = stackalloc byte[0x50];
@ -1029,18 +1086,18 @@ public static class SaveDataManagement
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null))
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.QuerySaveDataTotalSize(out totalSize, size, journalSize);
rc = fs.Impl.QuerySaveDataTotalSize(out totalSize, saveDataSize, saveDataJournalSize);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogSaveDataSize).AppendFormat(size, 'd')
.Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd');
sb.Append(LogSaveDataSize).AppendFormat(saveDataSize, 'd')
.Append(LogSaveDataJournalSize).AppendFormat(saveDataJournalSize, 'd');
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer));
}
else
{
rc = fs.Impl.QuerySaveDataTotalSize(out totalSize, size, journalSize);
rc = fs.Impl.QuerySaveDataTotalSize(out totalSize, saveDataSize, saveDataJournalSize);
}
fs.Impl.AbortIfNeeded(rc);
@ -1329,10 +1386,10 @@ public static class SaveDataManagement
static Result SetFlags(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId,
SaveDataFlags flags)
{
var extraDataMask = new SaveDataExtraData();
SaveDataExtraData extraDataMask = default;
extraDataMask.Flags = unchecked((SaveDataFlags)0xFFFFFFFF);
var extraData = new SaveDataExtraData();
SaveDataExtraData extraData = default;
extraData.Flags = flags;
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId,
@ -1411,10 +1468,10 @@ public static class SaveDataManagement
static Result SetTimeStamp(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId,
PosixTime timeStamp)
{
var extraDataMask = new SaveDataExtraData();
SaveDataExtraData extraDataMask = default;
extraDataMask.TimeStamp = unchecked((long)0xFFFFFFFFFFFFFFFF);
var extraData = new SaveDataExtraData();
SaveDataExtraData extraData = default;
extraData.TimeStamp = timeStamp.Value;
return fs.Impl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData, in extraDataMask);
@ -1689,10 +1746,10 @@ public static class SaveDataManagement
static Result SetCommitId(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, long commitId)
{
var extraDataMask = new SaveDataExtraData();
SaveDataExtraData extraDataMask = default;
extraDataMask.CommitId = unchecked((long)0xFFFFFFFFFFFFFFFF);
var extraData = new SaveDataExtraData();
SaveDataExtraData extraData = default;
extraData.CommitId = commitId;
return fs.Impl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData, in extraDataMask);
@ -1885,12 +1942,165 @@ public static class SaveDataManagement
}
}
public static Result OpenCacheStorageList(this FileSystemClient fs, out CacheStorageListHandle handle)
{
UnsafeHelpers.SkipParamInit(out handle);
Result rc;
Span<byte> logBuffer = stackalloc byte[0x40];
CacheStorageListCache listCache;
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null))
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = Open(fs, out listCache);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogCacheStorageListHandle).AppendFormat(listCache.GetHashCode(), 'x');
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer));
}
else
{
rc = Open(fs, out listCache);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
handle = new CacheStorageListHandle(listCache);
return Result.Success;
static Result Open(FileSystemClient fs, out CacheStorageListCache listCache)
{
UnsafeHelpers.SkipParamInit(out listCache);
CacheStorageListCache tempListCache = null;
Result rc = Utility.DoContinuouslyUntilSaveDataListFetched(fs.Hos, () =>
{
// Note: Nintendo uses the same CacheStorageListCache for every attempt to fetch the save data list
// without clearing it between runs. This means that if it has to retry fetching the list, the
// CacheStorageListCache may contain duplicate entries if the save data indexer was reset while this
// function was running.
tempListCache = new CacheStorageListCache();
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
using var reader = new SharedRef<ISaveDataInfoReader>();
Result result = fileSystemProxy.Get.OpenSaveDataInfoReaderOnlyCacheStorage(ref reader.Ref());
if (result.IsFailure()) return result.Miss();
while (true)
{
Unsafe.SkipInit(out SaveDataInfo info);
result = reader.Get.Read(out long readCount, new OutBuffer(SpanHelpers.AsByteSpan(ref info)));
if (result.IsFailure()) return result.Miss();
if (readCount == 0)
break;
var cacheEntry = new CacheStorageListCache.CacheEntry(info.Index);
result = tempListCache.PushBack(in cacheEntry);
if (result.IsFailure()) return result.Miss();
}
return Result.Success;
});
if (rc.IsFailure()) return rc.Miss();
Assert.SdkRequiresNotNull(tempListCache);
listCache = tempListCache;
return Result.Success;
}
}
public static Result ReadCacheStorageList(this FileSystemClient fs, out int storageInfoReadCount,
Span<CacheStorageInfo> storageInfoBuffer, CacheStorageListHandle handle)
{
UnsafeHelpers.SkipParamInit(out storageInfoReadCount);
Result rc;
Span<byte> logBuffer = stackalloc byte[0x70];
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null))
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = Read(out storageInfoReadCount, storageInfoBuffer, handle);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogCacheStorageListHandle).AppendFormat(handle.Cache.GetHashCode(), 'x')
.Append(LogInfoBufferCount).AppendFormat(storageInfoBuffer.Length, 'X')
.Append(LogCacheStorageCount).AppendFormat(AccessLogImpl.DereferenceOutValue(in storageInfoReadCount, rc), 'd');
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer));
}
else
{
rc = Read(out storageInfoReadCount, storageInfoBuffer, handle);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
static Result Read(out int storageCount, Span<CacheStorageInfo> infoBuffer, CacheStorageListHandle handle)
{
int count = 0;
var listCache = CacheStorageListCache.GetCacheStorageListCache(handle);
while (count < infoBuffer.Length)
{
ref readonly CacheStorageListCache.CacheEntry entry = ref listCache.PopFront();
// We're done iterating if we get a null ref
if (Unsafe.IsNullRef(ref Unsafe.AsRef(in entry)))
break;
infoBuffer[count] = default;
infoBuffer[count].Index = entry.GetCacheStorageIndex();
count++;
}
storageCount = count;
return Result.Success;
}
}
public static void CloseCacheStorageList(this FileSystemClient fs, CacheStorageListHandle handle)
{
Span<byte> logBuffer = stackalloc byte[0x40];
var listCache = CacheStorageListCache.GetCacheStorageListCache(handle);
listCache?.Dispose();
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null))
{
Tick start = fs.Hos.Os.GetSystemTick();
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogCacheStorageListHandle).AppendFormat(handle.Cache.GetHashCode(), 'x');
fs.Impl.OutputAccessLog(Result.Success, start, end, null, new U8Span(sb.Buffer));
}
}
public static Result UpdateSaveDataMacForDebug(this FileSystemClient fs, SaveDataSpaceId spaceId,
ulong saveDataId)
{
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
return fileSystemProxy.Get.UpdateSaveDataMacForDebug(spaceId, saveDataId);
Result rc = fileSystemProxy.Get.UpdateSaveDataMacForDebug(spaceId, saveDataId);
fs.Impl.AbortIfNeeded(rc);
return rc;
}
public static Result ListApplicationAccessibleSaveDataOwnerId(this FileSystemClient fs, out int readCount,
@ -1979,7 +2189,7 @@ public static class SaveDataManagement
if (attribute.ProgramId == Fs.SaveData.InvalidProgramId)
attribute.ProgramId = Fs.SaveData.AutoResolveCallerProgramId;
var extraDataMask = new SaveDataExtraData();
SaveDataExtraData extraDataMask = default;
extraDataMask.Flags = SaveDataFlags.Restore;
rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, SaveDataSpaceId.User,
@ -2024,12 +2234,12 @@ public static class SaveDataManagement
{
UnsafeHelpers.SkipParamInit(out saveSize, out journalSize);
var extraDataMask = new SaveDataExtraData();
SaveDataExtraData extraDataMask = default;
extraDataMask.DataSize = unchecked((long)0xFFFFFFFFFFFFFFFF);
extraDataMask.JournalSize = unchecked((long)0xFFFFFFFFFFFFFFFF);
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, new ProgramId(applicationId.Value),
SaveDataType.Device, Fs.SaveData.InvalidUserId, 0);
SaveDataType.Device, Fs.SaveData.InvalidUserId, staticSaveDataId: 0);
if (rc.IsFailure()) return rc;
rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, SaveDataSpaceId.User,
@ -2042,4 +2252,5 @@ public static class SaveDataManagement
return Result.Success;
}
}
}
}

50
src/LibHac/Fs/Utility.cs Normal file
View File

@ -0,0 +1,50 @@
using System;
using LibHac.Account;
using LibHac.Os;
namespace LibHac.Fs;
/// <summary>
/// Contains various utility functions used by FS client code.
/// </summary>
/// <remarks>Based on nnSdk 13.4.0</remarks>
public static class Utility
{
public static UserId ConvertAccountUidToFsUserId(Uid uid)
{
return new UserId(uid.Id.High, uid.Id.Low);
}
public static Result CheckUid(HorizonClient hos, Uid uid)
{
throw new NotImplementedException();
}
public static Result DoContinuouslyUntilSaveDataListFetched(HorizonClient hos, Func<Result> listGetter)
{
const int maxTryCount = 5;
const int initialSleepTimeMs = 5;
const int sleepTimeMultiplier = 2;
Result lastResult = Result.Success;
long sleepTime = initialSleepTimeMs;
for (int i = 0; i < maxTryCount; i++)
{
Result rc = listGetter();
if (rc.IsSuccess())
return rc;
// Try again if any save data were added or removed while getting the list
if (!ResultFs.InvalidHandle.Includes(rc))
return rc.Miss();
lastResult = rc;
hos.Os.SleepThread(TimeSpan.FromMilliSeconds(sleepTime));
sleepTime *= sleepTimeMultiplier;
}
return lastResult.Log();
}
}

View File

@ -6,7 +6,8 @@ using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Fs.Shim;
using LibHac.FsSrv.FsCreator;
using LibHac.FsSrv.Impl;
using Utility = LibHac.FsSrv.Impl.Utility;
namespace LibHac.FsSrv;

View File

@ -2,7 +2,8 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSrv.Impl;
using Utility = LibHac.FsSrv.Impl.Utility;
namespace LibHac.FsSrv.FsCreator;

View File

@ -2,9 +2,10 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSrv.Impl;
using LibHac.Util;
using Utility = LibHac.FsSrv.Impl.Utility;
namespace LibHac.FsSrv.FsCreator;
public class EmulatedSdCardFileSystemCreator : ISdCardProxyFileSystemCreator, IDisposable

View File

@ -10,6 +10,7 @@ using LibHac.FsSystem;
using LibHac.Tools.Fs;
using LibHac.Util;
using Path = LibHac.Fs.Path;
using Utility = LibHac.FsSystem.Utility;
namespace LibHac.Tools.FsSystem;

View File

@ -35,7 +35,7 @@ public class ApplicationSaveDataManagementTests
Assert.Equal(1, entriesRead);
Assert.Equal(applicationId, info[0].ProgramId);
Assert.Equal(ConvertAccountUidToFsUserId(userId), info[0].UserId);
Assert.Equal(Utility.ConvertAccountUidToFsUserId(userId), info[0].UserId);
Assert.Equal(SaveDataType.Account, info[0].Type);
}

View File

@ -577,6 +577,73 @@ public class SaveDataManagement
Assert.Equal(timeStamp, actualTimeStamp);
}
[Fact]
public void OpenCacheStorageList_ReadIntoLargeBuffer_AllIndexesAreRead()
{
var applicationId = new Ncm.ApplicationId(1);
Horizon hos = HorizonFactory.CreateBasicHorizon();
HorizonClient client = hos.CreateHorizonClient(new ProgramLocation(applicationId, StorageId.BuiltInSystem),
(AccessControlBits.Bits)ulong.MaxValue);
Assert.Success(client.Fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId.Value, 0, 0, 0, SaveDataFlags.None));
Assert.Success(client.Fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId.Value, 6, 0, 0, SaveDataFlags.None));
Assert.Success(client.Fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId.Value, 2, 0, 0, SaveDataFlags.None));
Assert.Success(client.Fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId.Value, 3, 0, 0, SaveDataFlags.None));
Assert.Success(client.Fs.OpenCacheStorageList(out CacheStorageListHandle handle));
var infoBuffer = new CacheStorageInfo[5];
Assert.Success(client.Fs.ReadCacheStorageList(out int readCount, infoBuffer, handle));
Assert.Equal(4, readCount);
Assert.Equal(0, infoBuffer[0].Index);
Assert.Equal(2, infoBuffer[1].Index);
Assert.Equal(3, infoBuffer[2].Index);
Assert.Equal(6, infoBuffer[3].Index);
client.Fs.CloseCacheStorageList(handle);
}
[Fact]
public void OpenCacheStorageList_ReadIntoMultipleBuffers_AllIndexesAreRead()
{
var applicationId = new Ncm.ApplicationId(1);
Horizon hos = HorizonFactory.CreateBasicHorizon();
HorizonClient client = hos.CreateHorizonClient(new ProgramLocation(applicationId, StorageId.BuiltInSystem),
(AccessControlBits.Bits)ulong.MaxValue);
Assert.Success(client.Fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId.Value, 0, 0, 0, SaveDataFlags.None));
Assert.Success(client.Fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId.Value, 6, 0, 0, SaveDataFlags.None));
Assert.Success(client.Fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId.Value, 2, 0, 0, SaveDataFlags.None));
Assert.Success(client.Fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId.Value, 3, 0, 0, SaveDataFlags.None));
Assert.Success(client.Fs.OpenCacheStorageList(out CacheStorageListHandle handle));
var infoBuffer = new CacheStorageInfo[2];
Assert.Success(client.Fs.ReadCacheStorageList(out int readCount, infoBuffer, handle));
Assert.Equal(2, readCount);
Assert.Equal(0, infoBuffer[0].Index);
Assert.Equal(2, infoBuffer[1].Index);
Assert.Success(client.Fs.ReadCacheStorageList(out readCount, infoBuffer, handle));
Assert.Equal(2, readCount);
Assert.Equal(3, infoBuffer[0].Index);
Assert.Equal(6, infoBuffer[1].Index);
Assert.Success(client.Fs.ReadCacheStorageList(out readCount, infoBuffer, handle));
Assert.Equal(0, readCount);
client.Fs.CloseCacheStorageList(handle);
}
private static Result PopulateSaveData(FileSystemClient fs, int count, int seed = -1)
{
if (seed == -1)

View File

@ -354,4 +354,15 @@ public class TypeLayoutTests
Assert.Equal(3, GetOffset(in s, in s.CompressionRate));
Assert.Equal(4, GetOffset(in s, in s.Reserved));
}
[Fact]
public static void CacheStorageInfo_Layout()
{
var s = new CacheStorageInfo();
Assert.Equal(0x20, Unsafe.SizeOf<CacheStorageInfo>());
Assert.Equal(0, GetOffset(in s, in s.Index));
Assert.Equal(4, GetOffset(in s, in s.Reserved));
}
}