mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Ensure all of Shim.SaveDataManagement is updated for 13.1.0
This commit is contained in:
parent
dc2a1cfaef
commit
74ed435dee
12 changed files with 2230 additions and 1845 deletions
|
@ -1135,5 +1135,33 @@ namespace LibHac.Fs.Impl
|
||||||
(byte)',', (byte)' ', (byte)'f', (byte)'u', (byte)'n', (byte)'c', (byte)'t', (byte)'i',
|
(byte)',', (byte)' ', (byte)'f', (byte)'u', (byte)'n', (byte)'c', (byte)'t', (byte)'i',
|
||||||
(byte)'o', (byte)'n', (byte)':', (byte)' ', (byte)'"'
|
(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)' '
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -29,7 +29,7 @@ public static class ApplicationSaveDataManagement
|
||||||
|
|
||||||
Result CreateAccountSaveFunc()
|
Result CreateAccountSaveFunc()
|
||||||
{
|
{
|
||||||
UserId userId = ConvertAccountUidToFsUserId(uidLocal);
|
UserId userId = Utility.ConvertAccountUidToFsUserId(uidLocal);
|
||||||
return fs.CreateSaveData(applicationId, userId, saveDataOwnerId, accountSaveDataSize,
|
return fs.CreateSaveData(applicationId, userId, saveDataOwnerId, accountSaveDataSize,
|
||||||
accountSaveJournalSize, SaveDataFlags.None);
|
accountSaveJournalSize, SaveDataFlags.None);
|
||||||
}
|
}
|
||||||
|
@ -465,9 +465,4 @@ public static class ApplicationSaveDataManagement
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UserId ConvertAccountUidToFsUserId(Uid uid)
|
|
||||||
{
|
|
||||||
return new UserId(uid.Id.High, uid.Id.Low);
|
|
||||||
}
|
|
||||||
}
|
}
|
19
src/LibHac/Fs/CacheStorageTypes.cs
Normal file
19
src/LibHac/Fs/CacheStorageTypes.cs
Normal 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;
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Diag;
|
using LibHac.Diag;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
|
@ -12,8 +14,12 @@ using LibHac.Sf;
|
||||||
using LibHac.Time;
|
using LibHac.Time;
|
||||||
using static LibHac.Fs.Impl.AccessLogStrings;
|
using static LibHac.Fs.Impl.AccessLogStrings;
|
||||||
|
|
||||||
namespace LibHac.Fs.Shim;
|
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
|
public class SaveDataIterator : IDisposable
|
||||||
{
|
{
|
||||||
private readonly FileSystemClient _fsClient;
|
private readonly FileSystemClient _fsClient;
|
||||||
|
@ -66,10 +72,60 @@ public class SaveDataIterator : IDisposable
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
[SkipLocalsInit]
|
||||||
public static class SaveDataManagement
|
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,
|
public static Result ReadSaveDataFileSystemExtraData(this FileSystemClientImpl fs,
|
||||||
out SaveDataExtraData extraData, ulong saveDataId)
|
out SaveDataExtraData extraData, ulong saveDataId)
|
||||||
{
|
{
|
||||||
|
@ -241,7 +297,7 @@ public static class SaveDataManagement
|
||||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
|
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Account,
|
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Account,
|
||||||
userId, 0);
|
userId, staticSaveDataId: 0);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags,
|
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags,
|
||||||
|
@ -263,7 +319,7 @@ public static class SaveDataManagement
|
||||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
|
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Bcat,
|
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Bcat,
|
||||||
Fs.SaveData.InvalidUserId, 0);
|
Fs.SaveData.InvalidUserId, staticSaveDataId: 0);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size,
|
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size,
|
||||||
|
@ -289,7 +345,7 @@ public static class SaveDataManagement
|
||||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
|
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Device,
|
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Device,
|
||||||
Fs.SaveData.InvalidUserId, 0);
|
Fs.SaveData.InvalidUserId, staticSaveDataId: 0);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags,
|
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags,
|
||||||
|
@ -311,7 +367,7 @@ public static class SaveDataManagement
|
||||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
|
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Cache,
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags,
|
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();
|
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, new ProgramId(applicationId.Value),
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
return fileSystemProxy.Get.DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId.User, in attribute);
|
return fileSystemProxy.Get.DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId.User, in attribute);
|
||||||
|
@ -530,8 +586,8 @@ public static class SaveDataManagement
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result ReadSaveDataIteratorSaveDataInfo(out long readCount, Span<SaveDataInfo> buffer,
|
public static Result ReadSaveDataIteratorSaveDataInfo(this FileSystemClientImpl fs, out long readCount,
|
||||||
in SaveDataIterator iterator)
|
Span<SaveDataInfo> buffer, in SaveDataIterator iterator)
|
||||||
{
|
{
|
||||||
Result rc = iterator.ReadSaveDataInfo(out readCount, buffer);
|
Result rc = iterator.ReadSaveDataInfo(out readCount, buffer);
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
@ -687,7 +743,7 @@ public static class SaveDataManagement
|
||||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
|
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Account,
|
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Account,
|
||||||
userId, 0);
|
userId, staticSaveDataId: 0);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags,
|
rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags,
|
||||||
|
@ -764,10 +820,10 @@ public static class SaveDataManagement
|
||||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
|
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Temporary,
|
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Temporary,
|
||||||
Fs.SaveData.InvalidUserId, 0);
|
Fs.SaveData.InvalidUserId, staticSaveDataId: 0);
|
||||||
if (rc.IsFailure()) return rc;
|
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);
|
SaveDataSpaceId.Temporary);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
@ -844,7 +900,7 @@ public static class SaveDataManagement
|
||||||
public static Result CreateCacheStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId,
|
public static Result CreateCacheStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId,
|
||||||
SaveDataSpaceId spaceId, ulong ownerId, long size, long journalSize, SaveDataFlags flags)
|
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,
|
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,
|
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, UserId userId, long size,
|
||||||
long journalSize, SaveDataFlags flags)
|
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,
|
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,
|
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, long size,
|
||||||
long journalSize, SaveDataFlags flags)
|
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,
|
public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId,
|
||||||
|
@ -1006,22 +1062,23 @@ public static class SaveDataManagement
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result QuerySaveDataTotalSize(this FileSystemClientImpl fs, out long totalSize, long size,
|
public static Result QuerySaveDataTotalSize(this FileSystemClientImpl fs, out long totalSize, long saveDataSize,
|
||||||
long journalSize)
|
long saveDataJournalSize)
|
||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out totalSize);
|
UnsafeHelpers.SkipParamInit(out totalSize);
|
||||||
|
|
||||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
|
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();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
totalSize = tempTotalSize;
|
totalSize = tempTotalSize;
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result QuerySaveDataTotalSize(this FileSystemClient fs, out long totalSize, long size,
|
public static Result QuerySaveDataTotalSize(this FileSystemClient fs, out long totalSize, long saveDataSize,
|
||||||
long journalSize)
|
long saveDataJournalSize)
|
||||||
{
|
{
|
||||||
Result rc;
|
Result rc;
|
||||||
Span<byte> logBuffer = stackalloc byte[0x50];
|
Span<byte> logBuffer = stackalloc byte[0x50];
|
||||||
|
@ -1029,18 +1086,18 @@ public static class SaveDataManagement
|
||||||
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null))
|
if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null))
|
||||||
{
|
{
|
||||||
Tick start = fs.Hos.Os.GetSystemTick();
|
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();
|
Tick end = fs.Hos.Os.GetSystemTick();
|
||||||
|
|
||||||
var sb = new U8StringBuilder(logBuffer, true);
|
var sb = new U8StringBuilder(logBuffer, true);
|
||||||
sb.Append(LogSaveDataSize).AppendFormat(size, 'd')
|
sb.Append(LogSaveDataSize).AppendFormat(saveDataSize, 'd')
|
||||||
.Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd');
|
.Append(LogSaveDataJournalSize).AppendFormat(saveDataJournalSize, 'd');
|
||||||
|
|
||||||
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer));
|
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
rc = fs.Impl.QuerySaveDataTotalSize(out totalSize, size, journalSize);
|
rc = fs.Impl.QuerySaveDataTotalSize(out totalSize, saveDataSize, saveDataJournalSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.Impl.AbortIfNeeded(rc);
|
fs.Impl.AbortIfNeeded(rc);
|
||||||
|
@ -1329,10 +1386,10 @@ public static class SaveDataManagement
|
||||||
static Result SetFlags(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId,
|
static Result SetFlags(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId,
|
||||||
SaveDataFlags flags)
|
SaveDataFlags flags)
|
||||||
{
|
{
|
||||||
var extraDataMask = new SaveDataExtraData();
|
SaveDataExtraData extraDataMask = default;
|
||||||
extraDataMask.Flags = unchecked((SaveDataFlags)0xFFFFFFFF);
|
extraDataMask.Flags = unchecked((SaveDataFlags)0xFFFFFFFF);
|
||||||
|
|
||||||
var extraData = new SaveDataExtraData();
|
SaveDataExtraData extraData = default;
|
||||||
extraData.Flags = flags;
|
extraData.Flags = flags;
|
||||||
|
|
||||||
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId,
|
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,
|
static Result SetTimeStamp(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId,
|
||||||
PosixTime timeStamp)
|
PosixTime timeStamp)
|
||||||
{
|
{
|
||||||
var extraDataMask = new SaveDataExtraData();
|
SaveDataExtraData extraDataMask = default;
|
||||||
extraDataMask.TimeStamp = unchecked((long)0xFFFFFFFFFFFFFFFF);
|
extraDataMask.TimeStamp = unchecked((long)0xFFFFFFFFFFFFFFFF);
|
||||||
|
|
||||||
var extraData = new SaveDataExtraData();
|
SaveDataExtraData extraData = default;
|
||||||
extraData.TimeStamp = timeStamp.Value;
|
extraData.TimeStamp = timeStamp.Value;
|
||||||
|
|
||||||
return fs.Impl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData, in extraDataMask);
|
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)
|
static Result SetCommitId(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, long commitId)
|
||||||
{
|
{
|
||||||
var extraDataMask = new SaveDataExtraData();
|
SaveDataExtraData extraDataMask = default;
|
||||||
extraDataMask.CommitId = unchecked((long)0xFFFFFFFFFFFFFFFF);
|
extraDataMask.CommitId = unchecked((long)0xFFFFFFFFFFFFFFFF);
|
||||||
|
|
||||||
var extraData = new SaveDataExtraData();
|
SaveDataExtraData extraData = default;
|
||||||
extraData.CommitId = commitId;
|
extraData.CommitId = commitId;
|
||||||
|
|
||||||
return fs.Impl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData, in extraDataMask);
|
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,
|
public static Result UpdateSaveDataMacForDebug(this FileSystemClient fs, SaveDataSpaceId spaceId,
|
||||||
ulong saveDataId)
|
ulong saveDataId)
|
||||||
{
|
{
|
||||||
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
|
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,
|
public static Result ListApplicationAccessibleSaveDataOwnerId(this FileSystemClient fs, out int readCount,
|
||||||
|
@ -1979,7 +2189,7 @@ public static class SaveDataManagement
|
||||||
if (attribute.ProgramId == Fs.SaveData.InvalidProgramId)
|
if (attribute.ProgramId == Fs.SaveData.InvalidProgramId)
|
||||||
attribute.ProgramId = Fs.SaveData.AutoResolveCallerProgramId;
|
attribute.ProgramId = Fs.SaveData.AutoResolveCallerProgramId;
|
||||||
|
|
||||||
var extraDataMask = new SaveDataExtraData();
|
SaveDataExtraData extraDataMask = default;
|
||||||
extraDataMask.Flags = SaveDataFlags.Restore;
|
extraDataMask.Flags = SaveDataFlags.Restore;
|
||||||
|
|
||||||
rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, SaveDataSpaceId.User,
|
rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, SaveDataSpaceId.User,
|
||||||
|
@ -2024,12 +2234,12 @@ public static class SaveDataManagement
|
||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out saveSize, out journalSize);
|
UnsafeHelpers.SkipParamInit(out saveSize, out journalSize);
|
||||||
|
|
||||||
var extraDataMask = new SaveDataExtraData();
|
SaveDataExtraData extraDataMask = default;
|
||||||
extraDataMask.DataSize = unchecked((long)0xFFFFFFFFFFFFFFFF);
|
extraDataMask.DataSize = unchecked((long)0xFFFFFFFFFFFFFFFF);
|
||||||
extraDataMask.JournalSize = unchecked((long)0xFFFFFFFFFFFFFFFF);
|
extraDataMask.JournalSize = unchecked((long)0xFFFFFFFFFFFFFFFF);
|
||||||
|
|
||||||
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, new ProgramId(applicationId.Value),
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, SaveDataSpaceId.User,
|
rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, SaveDataSpaceId.User,
|
||||||
|
@ -2043,3 +2253,4 @@ public static class SaveDataManagement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
50
src/LibHac/Fs/Utility.cs
Normal file
50
src/LibHac/Fs/Utility.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,8 @@ using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.Fs.Shim;
|
using LibHac.Fs.Shim;
|
||||||
using LibHac.FsSrv.FsCreator;
|
using LibHac.FsSrv.FsCreator;
|
||||||
using LibHac.FsSrv.Impl;
|
|
||||||
|
using Utility = LibHac.FsSrv.Impl.Utility;
|
||||||
|
|
||||||
namespace LibHac.FsSrv;
|
namespace LibHac.FsSrv;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSrv.Impl;
|
|
||||||
|
using Utility = LibHac.FsSrv.Impl.Utility;
|
||||||
|
|
||||||
namespace LibHac.FsSrv.FsCreator;
|
namespace LibHac.FsSrv.FsCreator;
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSrv.Impl;
|
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
|
using Utility = LibHac.FsSrv.Impl.Utility;
|
||||||
|
|
||||||
namespace LibHac.FsSrv.FsCreator;
|
namespace LibHac.FsSrv.FsCreator;
|
||||||
|
|
||||||
public class EmulatedSdCardFileSystemCreator : ISdCardProxyFileSystemCreator, IDisposable
|
public class EmulatedSdCardFileSystemCreator : ISdCardProxyFileSystemCreator, IDisposable
|
||||||
|
|
|
@ -10,6 +10,7 @@ using LibHac.FsSystem;
|
||||||
using LibHac.Tools.Fs;
|
using LibHac.Tools.Fs;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
using Path = LibHac.Fs.Path;
|
using Path = LibHac.Fs.Path;
|
||||||
|
using Utility = LibHac.FsSystem.Utility;
|
||||||
|
|
||||||
namespace LibHac.Tools.FsSystem;
|
namespace LibHac.Tools.FsSystem;
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ public class ApplicationSaveDataManagementTests
|
||||||
|
|
||||||
Assert.Equal(1, entriesRead);
|
Assert.Equal(1, entriesRead);
|
||||||
Assert.Equal(applicationId, info[0].ProgramId);
|
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);
|
Assert.Equal(SaveDataType.Account, info[0].Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -577,6 +577,73 @@ public class SaveDataManagement
|
||||||
Assert.Equal(timeStamp, actualTimeStamp);
|
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)
|
private static Result PopulateSaveData(FileSystemClient fs, int count, int seed = -1)
|
||||||
{
|
{
|
||||||
if (seed == -1)
|
if (seed == -1)
|
||||||
|
|
|
@ -354,4 +354,15 @@ public class TypeLayoutTests
|
||||||
Assert.Equal(3, GetOffset(in s, in s.CompressionRate));
|
Assert.Equal(3, GetOffset(in s, in s.CompressionRate));
|
||||||
Assert.Equal(4, GetOffset(in s, in s.Reserved));
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue