From 527d81a3b26e70e38331d6dbf8fcf3c294aed8e9 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 14 Dec 2021 23:03:11 -0700 Subject: [PATCH 01/34] Add default constructors to some mutex types --- src/LibHac/Diag/Assert.cs | 11 ++--------- src/LibHac/Fs/Fsa/MountTable.cs | 3 +-- src/LibHac/FsSystem/BucketTree.cs | 3 +-- .../Impl/InternalConditionVariable-os.net.cs | 4 ++-- .../Os/Impl/InternalConditionVariable.cs | 4 ++-- .../Os/Impl/InternalCriticalSection-os.net.cs | 7 ++++++- src/LibHac/Os/Impl/InternalCriticalSection.cs | 7 ++++++- src/LibHac/Os/SdkMutex.cs | 18 +++++++++++++++++- 8 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/LibHac/Diag/Assert.cs b/src/LibHac/Diag/Assert.cs index 4c22a26a..02b4353d 100644 --- a/src/LibHac/Diag/Assert.cs +++ b/src/LibHac/Diag/Assert.cs @@ -37,16 +37,9 @@ public static class Assert { private const string AssertCondition = "ENABLE_ASSERTS"; - private static SdkMutexType _mutex = InitMutex(); + private static SdkMutexType _mutex = new SdkMutexType(); private static AssertionFailureHandler _assertionFailureHandler = DefaultAssertionFailureHandler; - private static SdkMutexType InitMutex() - { - var mutex = new SdkMutexType(); - mutex.Initialize(); - return mutex; - } - private static AbortReason ToAbortReason(AssertionType assertionType) { switch (assertionType) @@ -1182,4 +1175,4 @@ public static class Assert AlignedImpl(AssertionType.SdkRequires, value, alignment, valueText, alignmentText, functionName, fileName, lineNumber); } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/MountTable.cs b/src/LibHac/Fs/Fsa/MountTable.cs index a08897fa..2c2609ad 100644 --- a/src/LibHac/Fs/Fsa/MountTable.cs +++ b/src/LibHac/Fs/Fsa/MountTable.cs @@ -27,7 +27,6 @@ internal class MountTable : IDisposable { _fileSystemList = new LinkedList(); _mutex = new SdkMutexType(); - _mutex.Initialize(); _fsClient = fsClient; } @@ -162,4 +161,4 @@ internal class MountTable : IDisposable dataIdCount = count; return Result.Success; } -} +} \ No newline at end of file diff --git a/src/LibHac/FsSystem/BucketTree.cs b/src/LibHac/FsSystem/BucketTree.cs index 79ab8d85..44898782 100644 --- a/src/LibHac/FsSystem/BucketTree.cs +++ b/src/LibHac/FsSystem/BucketTree.cs @@ -291,7 +291,6 @@ public partial class BucketTree : IDisposable public OffsetCache() { Mutex = new SdkMutexType(); - Mutex.Initialize(); IsInitialized = false; Offsets.StartOffset = -1; Offsets.EndOffset = -1; @@ -1295,4 +1294,4 @@ public partial class BucketTree : IDisposable return _tree.ScanContinuousReading(out info, in param); } } -} +} \ No newline at end of file diff --git a/src/LibHac/Os/Impl/InternalConditionVariable-os.net.cs b/src/LibHac/Os/Impl/InternalConditionVariable-os.net.cs index c02144ac..5a5da6d4 100644 --- a/src/LibHac/Os/Impl/InternalConditionVariable-os.net.cs +++ b/src/LibHac/Os/Impl/InternalConditionVariable-os.net.cs @@ -7,7 +7,7 @@ internal struct InternalConditionVariableImpl { private object _obj; - public InternalConditionVariableImpl(nint _ = 0) => _obj = new object(); + public InternalConditionVariableImpl() => _obj = new object(); public void Initialize() => _obj = new object(); public void Signal() @@ -73,4 +73,4 @@ internal struct InternalConditionVariableImpl return ConditionVariableStatus.Success; } -} +} \ No newline at end of file diff --git a/src/LibHac/Os/Impl/InternalConditionVariable.cs b/src/LibHac/Os/Impl/InternalConditionVariable.cs index 594cc681..143a1db0 100644 --- a/src/LibHac/Os/Impl/InternalConditionVariable.cs +++ b/src/LibHac/Os/Impl/InternalConditionVariable.cs @@ -4,7 +4,7 @@ internal struct InternalConditionVariable { private InternalConditionVariableImpl _impl; - public InternalConditionVariable(nint _ = 0) + public InternalConditionVariable() { _impl = new InternalConditionVariableImpl(); } @@ -16,4 +16,4 @@ internal struct InternalConditionVariable public void TimedWait(ref InternalCriticalSection cs, in TimeoutHelper timeoutHelper) => _impl.TimedWait(ref cs, in timeoutHelper); -} +} \ No newline at end of file diff --git a/src/LibHac/Os/Impl/InternalCriticalSection-os.net.cs b/src/LibHac/Os/Impl/InternalCriticalSection-os.net.cs index 9d3590b8..fcb852e3 100644 --- a/src/LibHac/Os/Impl/InternalCriticalSection-os.net.cs +++ b/src/LibHac/Os/Impl/InternalCriticalSection-os.net.cs @@ -6,6 +6,11 @@ internal struct InternalCriticalSectionImpl { private object _obj; + public InternalCriticalSectionImpl() + { + _obj = new object(); + } + public void Initialize() { _obj = new object(); @@ -32,4 +37,4 @@ internal struct InternalCriticalSectionImpl { return Monitor.IsEntered(_obj); } -} +} \ No newline at end of file diff --git a/src/LibHac/Os/Impl/InternalCriticalSection.cs b/src/LibHac/Os/Impl/InternalCriticalSection.cs index 9daa0e59..2a937ce1 100644 --- a/src/LibHac/Os/Impl/InternalCriticalSection.cs +++ b/src/LibHac/Os/Impl/InternalCriticalSection.cs @@ -4,6 +4,11 @@ public struct InternalCriticalSection : ILockable { private InternalCriticalSectionImpl _impl; + public InternalCriticalSection() + { + _impl = new InternalCriticalSectionImpl(); + } + public void Initialize() => _impl.Initialize(); public void FinalizeObject() => _impl.FinalizeObject(); @@ -16,4 +21,4 @@ public struct InternalCriticalSection : ILockable public void Unlock() => _impl.Leave(); public bool IsLockedByCurrentThread() => _impl.IsLockedByCurrentThread(); -} +} \ No newline at end of file diff --git a/src/LibHac/Os/SdkMutex.cs b/src/LibHac/Os/SdkMutex.cs index 310543f5..d43d6974 100644 --- a/src/LibHac/Os/SdkMutex.cs +++ b/src/LibHac/Os/SdkMutex.cs @@ -7,6 +7,11 @@ public class SdkMutex : ILockable { private SdkMutexType _mutex; + public SdkMutex() + { + _mutex.Initialize(); + } + public void Initialize() { _mutex.Initialize(); @@ -37,6 +42,11 @@ public struct SdkMutexType : ILockable { private InternalCriticalSection _cs; + public SdkMutexType() + { + _cs = new InternalCriticalSection(); + } + public void Initialize() { _cs.Initialize(); @@ -96,6 +106,12 @@ public struct SdkRecursiveMutexType : ILockable private InternalCriticalSection _cs; private int _recursiveCount; + public SdkRecursiveMutexType() + { + _cs = new InternalCriticalSection(); + _recursiveCount = 0; + } + public void Initialize() { _cs.Initialize(); @@ -144,4 +160,4 @@ public struct SdkRecursiveMutexType : ILockable { return _cs.IsLockedByCurrentThread(); } -} +} \ No newline at end of file From 634ab5974233758d4759b498fc43a9b6b7ed6719 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 15 Dec 2021 01:17:56 -0700 Subject: [PATCH 02/34] Add BlockCacheManager and skeleton CompressedStorage --- src/LibHac/FsSystem/AsynchronousAccess.cs | 76 ++++ src/LibHac/FsSystem/CompressedStorage.cs | 389 ++++++++++++++++++ src/LibHac/FsSystem/CompressionCommon.cs | 32 ++ src/LibHac/FsSystem/Impl/BlockCacheManager.cs | 276 +++++++++++++ src/LibHac/Util/Alignment.cs | 3 +- 5 files changed, 775 insertions(+), 1 deletion(-) create mode 100644 src/LibHac/FsSystem/AsynchronousAccess.cs create mode 100644 src/LibHac/FsSystem/CompressedStorage.cs create mode 100644 src/LibHac/FsSystem/CompressionCommon.cs create mode 100644 src/LibHac/FsSystem/Impl/BlockCacheManager.cs diff --git a/src/LibHac/FsSystem/AsynchronousAccess.cs b/src/LibHac/FsSystem/AsynchronousAccess.cs new file mode 100644 index 00000000..b85e513c --- /dev/null +++ b/src/LibHac/FsSystem/AsynchronousAccess.cs @@ -0,0 +1,76 @@ +using System; +using LibHac.Common; +using LibHac.Diag; +using LibHac.Util; + +namespace LibHac.FsSystem; + +public interface IAsynchronousAccessSplitter : IDisposable +{ + private static readonly DefaultAsynchronousAccessSplitter DefaultAccessSplitter = new(); + + public static IAsynchronousAccessSplitter GetDefaultAsynchronousAccessSplitter() + { + return DefaultAccessSplitter; + } + + Result QueryNextOffset(out long nextOffset, long startOffset, long endOffset, long accessSize, long alignmentSize) + { + UnsafeHelpers.SkipParamInit(out nextOffset); + + Assert.SdkRequiresLess(0, accessSize); + Assert.SdkRequiresLess(0, alignmentSize); + + if (endOffset - startOffset <= accessSize) + { + nextOffset = endOffset; + return Result.Success; + } + + Result rc = QueryAppropriateOffset(out long offsetAppropriate, startOffset, accessSize, alignmentSize); + if (rc.IsFailure()) return rc.Miss(); + Assert.SdkNotEqual(startOffset, offsetAppropriate); + + nextOffset = Math.Min(startOffset, offsetAppropriate); + return Result.Success; + } + + Result QueryInvocationCount(out long count, long startOffset, long endOffset, long accessSize, long alignmentSize) + { + UnsafeHelpers.SkipParamInit(out count); + + long invocationCount = 0; + long currentOffset = startOffset; + + while (currentOffset < endOffset) + { + Result rc = QueryNextOffset(out currentOffset, currentOffset, endOffset, accessSize, alignmentSize); + if (rc.IsFailure()) return rc.Miss(); + + invocationCount++; + } + + count = invocationCount; + return Result.Success; + } + + Result QueryAppropriateOffset(out long offsetAppropriate, long startOffset, long accessSize, long alignmentSize); +} + +public class DefaultAsynchronousAccessSplitter : IAsynchronousAccessSplitter +{ + public void Dispose() { } + + public Result QueryAppropriateOffset(out long offsetAppropriate, long startOffset, long accessSize, long alignmentSize) + { + offsetAppropriate = Alignment.AlignDownPow2(startOffset + accessSize, alignmentSize); + return Result.Success; + } + + public Result QueryInvocationCount(out long count, long startOffset, long endOffset, long accessSize, long alignmentSize) + { + long alignedStartOffset = Alignment.AlignDownPow2(startOffset, alignmentSize); + count = BitUtil.DivideUp(endOffset - alignedStartOffset, accessSize); + return Result.Success; + } +} \ No newline at end of file diff --git a/src/LibHac/FsSystem/CompressedStorage.cs b/src/LibHac/FsSystem/CompressedStorage.cs new file mode 100644 index 00000000..8c64f0fd --- /dev/null +++ b/src/LibHac/FsSystem/CompressedStorage.cs @@ -0,0 +1,389 @@ +using System; +using System.Runtime.CompilerServices; +using LibHac.Diag; +using LibHac.Fs; +using LibHac.FsSystem.Impl; +using LibHac.Os; +using Buffer = LibHac.Mem.Buffer; + +namespace LibHac.FsSystem; + +public class CompressedStorage : IStorage, IAsynchronousAccessSplitter +{ + public delegate Result DecompressorFunction(Span destination, ReadOnlySpan source); + public delegate DecompressorFunction GetDecompressorFunction(CompressionType type); + + public class CompressedStorageCore : IDisposable + { + private long _blockSizeMax; + private long _continuousReadingSizeMax; + private readonly BucketTree _bucketTree; + private ValueSubStorage _dataStorage; + private GetDecompressorFunction _getDecompressorFunction; + + public CompressedStorageCore() + { + _bucketTree = new BucketTree(); + _dataStorage = new ValueSubStorage(); + } + + public void Dispose() + { + FinalizeObject(); + _dataStorage.Dispose(); + _bucketTree.Dispose(); + } + + private bool IsInitialized() + { + return _bucketTree.IsInitialized(); + } + + public Result GetSize(out long size) + { + Assert.SdkRequiresNotNullOut(out size); + + Result rc = _bucketTree.GetOffsets(out BucketTree.Offsets offsets); + if (rc.IsFailure()) return rc.Miss(); + + size = offsets.EndOffset; + return Result.Success; + } + + public delegate Result OperatePerEntryFunc(out bool isContinuous, in Entry entry, long virtualDataSize, + long offsetInEntry, long readSize); + + public Result OperatePerEntry(long offset, long size, OperatePerEntryFunc func) + { + throw new NotImplementedException(); + } + + public delegate Result OperateEntryFunc(long offset, long size); + + public Result OperateEntry(long offset, long size, OperatePerEntryFunc func) + { + throw new NotImplementedException(); + } + + public Result Initialize(MemoryResource allocatorForBucketTree, in ValueSubStorage dataStorage, + in ValueSubStorage nodeStorage, in ValueSubStorage entryStorage, int bucketTreeEntryCount, + long blockSizeMax, long continuousReadingSizeMax, GetDecompressorFunction getDecompressorFunc) + { + Assert.SdkRequiresNotNull(allocatorForBucketTree); + Assert.SdkRequiresLess(0, blockSizeMax); + Assert.SdkRequiresLessEqual(blockSizeMax, continuousReadingSizeMax); + Assert.SdkRequiresNotNull(getDecompressorFunc); + + Result rc = _bucketTree.Initialize(allocatorForBucketTree, in nodeStorage, in entryStorage, NodeSize, + Unsafe.SizeOf(), bucketTreeEntryCount); + if (rc.IsFailure()) return rc.Miss(); + + _blockSizeMax = blockSizeMax; + _continuousReadingSizeMax = continuousReadingSizeMax; + _dataStorage.Set(in dataStorage); + _getDecompressorFunction = getDecompressorFunc; + + return Result.Success; + } + + public void FinalizeObject() + { + if (IsInitialized()) + { + _bucketTree.FinalizeObject(); + + using var temp = new ValueSubStorage(); + _dataStorage.Set(in temp); + } + } + + public delegate Result ReadImplFunc(Span buffer); + public delegate Result ReadFunc(long sizeBufferRequired, ReadImplFunc readImplFunc); + + public Result Read(long offset, long size, ReadFunc func) + { + throw new NotImplementedException(); + } + + public Result Invalidate() + { + throw new NotImplementedException(); + } + + public Result QueryRange(Span buffer, long offset, long size) + { + throw new NotImplementedException(); + } + + public Result QueryAppropriateOffsetForAsynchronousAccess(out long offsetAppropriate, long offset, + long accessSize, long alignmentSize) + { + throw new NotImplementedException(); + } + + private DecompressorFunction GetDecompressor(CompressionType type) + { + if (CompressionTypeUtility.IsUnknownType(type)) + return null; + + return _getDecompressorFunction(type); + } + } + + public class CacheManager : IDisposable + { + public struct CacheEntry : IBlockCacheManagerEntry + { + public Range Range { get; set; } + public long Handle { get; set; } + public Buffer Buffer { get; set; } + public bool IsValid { get; set; } + public bool IsCached { get; set; } + public short Age { get; set; } + + public void Invalidate() { /* empty */ } + public readonly bool IsAllocated() => IsValid && Handle != 0; + + + public bool IsWriteBack + { + get => false; + set { } + } + + public bool IsFlushing + { + set { } + } + } + + public struct Range : IBlockCacheManagerRange + { + public long Offset { get; set; } + public uint Size { get; set; } + + public long GetEndOffset() => Offset + Size; + public bool IsIncluded(long offset) => Offset <= offset && offset < GetEndOffset(); + } + + public struct AccessRange + { + public long VirtualOffset; + public long VirtualSize; + public uint PhysicalSize; + public bool IsBlockAlignmentRequired; + + public readonly long GetEndVirtualOffset() => VirtualOffset + VirtualSize; + } + + private long _cacheSize0; + private long _cacheSize1; + private SdkMutexType _mutex; + private BlockCacheManager _cacheManager; + private long _storageSize; + + public CacheManager() + { + _mutex = new SdkMutexType(); + _cacheManager = new BlockCacheManager(); + _storageSize = 0; + } + + public void Dispose() + { + throw new NotImplementedException(); + } + + public Result Initialize(IBufferManager allocator, long storageSize, long cacheSize0, long cacheSize1, + int maxCacheEntries) + { + Result rc = _cacheManager.Initialize(allocator, maxCacheEntries); + if (rc.IsFailure()) return rc.Miss(); + + _storageSize = storageSize; + _cacheSize0 = cacheSize0; + _cacheSize1 = cacheSize1; + + return Result.Success; + } + + public void FinalizeObject() + { + throw new NotImplementedException(); + } + + public void Invalidate() + { + throw new NotImplementedException(); + } + + public Result Read(CompressedStorageCore core, long offset, Span buffer) + { + throw new NotImplementedException(); + } + + private Result FindBufferImpl(out Buffer buffer, out CacheEntry entry, long offset) + { + throw new NotImplementedException(); + } + + private Result FindBuffer(out Buffer buffer, out CacheEntry entry, long offset) + { + throw new NotImplementedException(); + } + + private Result FindOrAllocateBuffer(out Buffer buffer, out CacheEntry entry, long offset, ulong size) + { + throw new NotImplementedException(); + } + + private void StoreAssociateBuffer(Buffer buffer, in CacheEntry entry) + { + throw new NotImplementedException(); + } + + private Result ReadHeadCache(CompressedStorageCore core, ref long offset, ref Span buffer, + ref AccessRange headRange, in AccessRange endRange) + { + throw new NotImplementedException(); + } + + private Result ReadTailCache(CompressedStorageCore core, long offset, Span buffer, + in AccessRange headRange, ref AccessRange endRange) + { + throw new NotImplementedException(); + } + } + + public struct Entry + { + public long VirtualOffset; + public long PhysicalOffset; + public CompressionType CompressionType; + public uint PhysicalSize; + + public readonly long GetPhysicalSize() => PhysicalSize; + } + + public static readonly int NodeSize = 0x4000; + + private CompressedStorageCore _core; + private CacheManager _cacheManager; + + public static long QueryEntryStorageSize(int entryCount) + { + return BucketTree.QueryEntryStorageSize(NodeSize, Unsafe.SizeOf(), entryCount); + } + + public static long QueryNodeStorageSize(int entryCount) + { + return BucketTree.QueryNodeStorageSize(NodeSize, Unsafe.SizeOf(), entryCount); + } + + public CompressedStorage() + { + _core = new CompressedStorageCore(); + _cacheManager = new CacheManager(); + } + + public override void Dispose() + { + FinalizeObject(); + _cacheManager.Dispose(); + _core.Dispose(); + } + + public Result Initialize(MemoryResource allocatorForBucketTree, IBufferManager allocatorForCacheManager, + in ValueSubStorage dataStorage, in ValueSubStorage nodeStorage, in ValueSubStorage entryStorage, + int bucketTreeEntryCount, long blockSizeMax, long continuousReadingSizeMax, + GetDecompressorFunction getDecompressorFunc, long cacheSize0, long cacheSize1, int maxCacheEntries) + { + Result rc = _core.Initialize(allocatorForBucketTree, in dataStorage, in nodeStorage, in entryStorage, + bucketTreeEntryCount, blockSizeMax, continuousReadingSizeMax, getDecompressorFunc); + if (rc.IsFailure()) return rc.Miss(); + + rc = _core.GetSize(out long size); + if (rc.IsFailure()) return rc.Miss(); + + rc = _cacheManager.Initialize(allocatorForCacheManager, size, cacheSize0, cacheSize1, maxCacheEntries); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + + } + + public void FinalizeObject() + { + _cacheManager.FinalizeObject(); + _core.FinalizeObject(); + } + + protected override Result DoRead(long offset, Span destination) + { + Result rc = _cacheManager.Read(_core, offset, destination); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + protected override Result DoWrite(long offset, ReadOnlySpan source) + { + return ResultFs.UnsupportedWriteForCompressedStorage.Log(); + } + + protected override Result DoFlush() + { + return Result.Success; + } + + protected override Result DoSetSize(long size) + { + return ResultFs.UnsupportedSetSizeForIndirectStorage.Log(); + } + + protected override Result DoGetSize(out long size) + { + Result rc = _core.GetSize(out size); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + Assert.SdkRequiresLessEqual(0, offset); + Assert.SdkRequiresLessEqual(0, size); + + switch (operationId) + { + case OperationId.InvalidateCache: + { + _cacheManager.Invalidate(); + Result rc = _core.Invalidate(); + if (rc.IsFailure()) return rc.Miss(); + break; + } + case OperationId.QueryRange: + { + Result rc = _core.QueryRange(outBuffer, offset, size); + if (rc.IsFailure()) return rc.Miss(); + break; + } + default: + return ResultFs.UnsupportedOperateRangeForCompressedStorage.Log(); + } + + return Result.Success; + } + + public Result QueryAppropriateOffset(out long offsetAppropriate, long startOffset, long accessSize, + long alignmentSize) + { + Result rc = _core.QueryAppropriateOffsetForAsynchronousAccess(out offsetAppropriate, startOffset, accessSize, + alignmentSize); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } +} \ No newline at end of file diff --git a/src/LibHac/FsSystem/CompressionCommon.cs b/src/LibHac/FsSystem/CompressionCommon.cs new file mode 100644 index 00000000..cbd59512 --- /dev/null +++ b/src/LibHac/FsSystem/CompressionCommon.cs @@ -0,0 +1,32 @@ +namespace LibHac.FsSystem; + +public enum CompressionType +{ + None = 0, + Zeroed = 1, + Lz4 = 3, + Unknown = 4 +} + +public static class CompressionTypeUtility +{ + public static bool IsBlockAlignmentRequired(CompressionType type) + { + return type != CompressionType.None && type != CompressionType.Zeroed; + } + + public static bool IsDataStorageAccessRequired(CompressionType type) + { + return type != CompressionType.Zeroed; + } + + public static bool IsRandomAccessible(CompressionType type) + { + return type == CompressionType.None; + } + + public static bool IsUnknownType(CompressionType type) + { + return type >= CompressionType.Unknown; + } +} \ No newline at end of file diff --git a/src/LibHac/FsSystem/Impl/BlockCacheManager.cs b/src/LibHac/FsSystem/Impl/BlockCacheManager.cs new file mode 100644 index 00000000..1f2bf195 --- /dev/null +++ b/src/LibHac/FsSystem/Impl/BlockCacheManager.cs @@ -0,0 +1,276 @@ +using System; +using LibHac.Diag; +using LibHac.Fs; + +using Buffer = LibHac.Mem.Buffer; + +namespace LibHac.FsSystem.Impl; + +public interface IBlockCacheManagerEntry where TRange : struct, IBlockCacheManagerRange +{ + TRange Range { get; } + bool IsValid { get; set; } + bool IsWriteBack { get; set; } + bool IsCached { get; set; } + bool IsFlushing { set; } + long Handle { get; set; } + Buffer Buffer { get; set; } + short Age { get; set; } + + void Invalidate(); + bool IsAllocated(); +} + +public interface IBlockCacheManagerRange +{ + long Offset { get; } + long GetEndOffset(); +} + +public class BlockCacheManager : IDisposable + where TEntry : struct, IBlockCacheManagerEntry + where TRange : struct, IBlockCacheManagerRange +{ + private IBufferManager _allocator; + private TEntry[] _cacheEntries; + private int _cacheEntriesCount; + + public void Dispose() + { + } + + public Result Initialize(IBufferManager allocator, int maxCacheEntries) + { + Assert.SdkRequiresNull(_allocator); + Assert.SdkRequiresNull(_cacheEntries); + Assert.SdkRequiresNotNull(allocator); + + if (maxCacheEntries > 0) + { + _cacheEntries = new TEntry[maxCacheEntries]; + } + + _allocator = allocator; + _cacheEntriesCount = maxCacheEntries; + + return Result.Success; + } + + public void FinalizeObject() + { + _allocator = null; + _cacheEntries = null; + _cacheEntriesCount = 0; + } + + public ref readonly TEntry this[int index] + { + get + { + Assert.SdkRequires(IsInitialized()); + Assert.SdkRequiresInRange(index, 0, _cacheEntriesCount); + + return ref _cacheEntries[index]; + } + } + + public bool IsInitialized() => _allocator is not null; + + public IBufferManager GetAllocator() => _allocator; + + public int GetCount() => _cacheEntriesCount; + + public void GetEmptyCacheIndex(out int emptyIndex, out int leastRecentlyUsedIndex) + { + int empty = -1; + int leastRecentlyUsed = -1; + + for (int i = 0; i < GetCount(); i++) + { + if (!_cacheEntries[i].IsValid) + { + // Get the first empty cache index + if (empty < 0) + empty = i; + } + else + { + // Protect against overflow + if (_cacheEntries[i].Age != short.MaxValue) + { + _cacheEntries[i].Age++; + } + + // Get the cache index that was least recently used + if (leastRecentlyUsed < 0 || _cacheEntries[leastRecentlyUsed].Age < _cacheEntries[i].Age) + { + leastRecentlyUsed = i; + } + } + } + + emptyIndex = empty; + leastRecentlyUsedIndex = leastRecentlyUsed; + } + + public void InvalidateCacheEntry(int index) + { + Assert.SdkRequires(IsInitialized()); + Assert.SdkRequiresLess(index, GetCount()); + + ref TEntry entry = ref _cacheEntries[index]; + + Assert.SdkAssert(entry.IsValid); + + if (entry.IsWriteBack) + { + Assert.SdkAssert(!entry.Buffer.IsNull && entry.Handle == 0); + _allocator.DeallocateBuffer(entry.Buffer); + entry.Buffer = new Buffer(); + } + else + { + Assert.SdkAssert(entry.Buffer.IsNull && entry.Handle != 0); + Buffer buffer = _allocator.AcquireCache(entry.Handle); + + if (!buffer.IsNull) + _allocator.DeallocateBuffer(buffer); + } + + entry.IsValid = false; + entry.Invalidate(); + } + + public void Invalidate() + { + Assert.SdkRequires(IsInitialized()); + + int count = _cacheEntriesCount; + for (int i = 0; i < count; i++) + { + if (_cacheEntries[i].IsValid) + InvalidateCacheEntry(i); + } + } + + public void ReleaseCacheEntry(int index, Buffer buffer) + { + ReleaseCacheEntry(ref _cacheEntries[index], buffer); + } + + public void ReleaseCacheEntry(ref TEntry entry, Buffer buffer) + { + Assert.SdkRequires(IsInitialized()); + + _allocator.DeallocateBuffer(buffer); + entry.IsValid = false; + entry.IsCached = false; + } + + public void RegisterCacheEntry(int index, Buffer buffer, IBufferManager.BufferAttribute attribute) + { + Assert.SdkRequires(IsInitialized()); + + ref TEntry entry = ref _cacheEntries[index]; + + if (entry.IsWriteBack) + { + entry.Handle = 0; + entry.Buffer = buffer; + } + else + { + entry.Handle = _allocator.RegisterCache(buffer, attribute); + entry.Buffer = new Buffer(); + } + } + + public void AcquireCacheEntry(out TEntry outEntry, out Buffer outBuffer, int index) + { + Assert.SdkRequires(IsInitialized()); + Assert.SdkRequiresLess(index, GetCount()); + + ref TEntry entry = ref _cacheEntries[index]; + + if (entry.IsWriteBack) + { + outBuffer = entry.Buffer; + } + else + { + outBuffer = _allocator.AcquireCache(entry.Handle); + } + + outEntry = entry; + + Assert.SdkAssert(outEntry.IsValid); + Assert.SdkAssert(outEntry.IsCached); + + entry.IsValid = false; + entry.Handle = 0; + entry.Buffer = new Buffer(); + entry.Age = 0; + + outEntry.IsValid = true; + outEntry.Handle = 0; + outEntry.Buffer = new Buffer(); + outEntry.Age = 0; + } + + private bool ExistsRedundantCacheEntry(in TEntry entry) + { + Assert.SdkRequires(IsInitialized()); + + for (int i = 0; i < GetCount(); i++) + { + ref TEntry currentEntry = ref _cacheEntries[i]; + + if (currentEntry.IsAllocated() && + currentEntry.Range.Offset < entry.Range.GetEndOffset() && + entry.Range.Offset < currentEntry.Range.GetEndOffset()) + { + return true; + } + } + + return false; + } + + public bool SetCacheEntry(int index, in TEntry entry, Buffer buffer) + { + return SetCacheEntry(index, in entry, buffer, new IBufferManager.BufferAttribute()); + } + + public bool SetCacheEntry(int index, in TEntry entry, Buffer buffer, IBufferManager.BufferAttribute attribute) + { + Assert.SdkRequires(IsInitialized()); + Assert.SdkRequiresInRange(index, 0, _cacheEntriesCount); + + _cacheEntries[index] = entry; + + Assert.SdkAssert(entry.IsValid); + Assert.SdkAssert(entry.IsCached); + Assert.SdkAssert(entry.Handle == 0); + Assert.SdkAssert(entry.Buffer.IsNull); + + // Get rid of the input entry if it overlaps with anything currently in the cache + if (ExistsRedundantCacheEntry(in entry)) + { + ReleaseCacheEntry(index, buffer); + return false; + } + + RegisterCacheEntry(index, buffer, attribute); + return true; + } + + public void SetFlushing(int index, bool isFlushing) + { + _cacheEntries[index].IsFlushing = isFlushing; + } + + public void SetWriteBack(int index, bool isWriteBack) + { + _cacheEntries[index].IsWriteBack = isWriteBack; + } +} \ No newline at end of file diff --git a/src/LibHac/Util/Alignment.cs b/src/LibHac/Util/Alignment.cs index 2096f76e..16eb451b 100644 --- a/src/LibHac/Util/Alignment.cs +++ b/src/LibHac/Util/Alignment.cs @@ -47,6 +47,7 @@ public static class Alignment public static long AlignUpPow2(long value, uint alignment) => (long)AlignUpPow2((ulong)value, alignment); public static int AlignDownPow2(int value, uint alignment) => (int)AlignDownPow2((ulong)value, alignment); public static long AlignDownPow2(long value, uint alignment) => (long)AlignDownPow2((ulong)value, alignment); + public static long AlignDownPow2(long value, long alignment) => (long)AlignDownPow2((ulong)value, (uint)alignment); public static bool IsAlignedPow2(int value, uint alignment) => IsAlignedPow2((ulong)value, alignment); public static bool IsAlignedPow2(long value, uint alignment) => IsAlignedPow2((ulong)value, alignment); @@ -60,4 +61,4 @@ public static class Alignment public static long AlignDown(long value, uint alignment) => (long)AlignDown((ulong)value, alignment); public static bool IsAligned(int value, uint alignment) => IsAligned((ulong)value, alignment); public static bool IsAligned(long value, uint alignment) => IsAligned((ulong)value, alignment); -} +} \ No newline at end of file From c4783f458941d8b8019a731e7ad9e3865a580a94 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sun, 19 Dec 2021 14:07:20 -0700 Subject: [PATCH 03/34] Update FileStorage for 13.1.0 --- src/LibHac/Fs/Common/FileStorage.cs | 62 ++++++++++--- src/LibHac/Fs/FileHandleStorage.cs | 98 --------------------- src/LibHac/Fs/FileStorageBasedFileSystem.cs | 41 --------- 3 files changed, 52 insertions(+), 149 deletions(-) delete mode 100644 src/LibHac/Fs/FileHandleStorage.cs delete mode 100644 src/LibHac/Fs/FileStorageBasedFileSystem.cs diff --git a/src/LibHac/Fs/Common/FileStorage.cs b/src/LibHac/Fs/Common/FileStorage.cs index 8317065a..93fb1144 100644 --- a/src/LibHac/Fs/Common/FileStorage.cs +++ b/src/LibHac/Fs/Common/FileStorage.cs @@ -8,6 +8,10 @@ using LibHac.Os; // ReSharper disable once CheckNamespace namespace LibHac.Fs; +/// +/// Allows interacting with an via an interface. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) public class FileStorage : IStorage { private const long InvalidSize = -1; @@ -100,18 +104,22 @@ public class FileStorage : IStorage protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { - if (operationId == OperationId.InvalidateCache || operationId == OperationId.QueryRange) + if (operationId == OperationId.InvalidateCache) + { + Result rc = _baseFile.OperateRange(OperationId.InvalidateCache, offset, size); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + if (operationId == OperationId.QueryRange) { if (size == 0) { - if (operationId == OperationId.QueryRange) - { - if (outBuffer.Length != Unsafe.SizeOf()) - return ResultFs.InvalidSize.Log(); - - SpanHelpers.AsStruct(outBuffer).Clear(); - } + if (outBuffer.Length != Unsafe.SizeOf()) + return ResultFs.InvalidSize.Log(); + SpanHelpers.AsStruct(outBuffer).Clear(); return Result.Success; } @@ -140,6 +148,12 @@ public class FileStorage : IStorage } } +/// +/// Opens a file from an and allows interacting with it through an +/// interface. The opened file will automatically be closed when the +/// is disposed. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) public class FileStorageBasedFileSystem : FileStorage { private SharedRef _baseFileSystem; @@ -153,6 +167,16 @@ public class FileStorageBasedFileSystem : FileStorage base.Dispose(); } + /// + /// Initializes this with the file at the specified path. + /// + /// The containing the file to open. + /// The full path of the file to open. + /// Specifies the access permissions of the opened file. + /// : The operation was successful.
+ /// : The specified path does not exist or is a directory.
+ /// : When opening as , + /// the file is already opened as .
public Result Initialize(ref SharedRef baseFileSystem, in Path path, OpenMode mode) { using var baseFile = new UniqueRef(); @@ -168,6 +192,11 @@ public class FileStorageBasedFileSystem : FileStorage } } +/// +/// Provides an interface for interacting with an opened file from a mounted file system. +/// The caller may choose whether or not the file will be closed when the is disposed. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) public class FileHandleStorage : IStorage { private const long InvalidSize = -1; @@ -177,11 +206,24 @@ public class FileHandleStorage : IStorage private long _size; private SdkMutexType _mutex; - // LibHac addition + // LibHac addition because we don't use global state for the FS client private FileSystemClient _fsClient; + /// + /// Initializes a new with the provided . + /// The file will not be closed when this is disposed. + /// + /// The of the provided . + /// The handle of the file to use. public FileHandleStorage(FileSystemClient fsClient, FileHandle handle) : this(fsClient, handle, false) { } + /// + /// Initializes a new with the provided . + /// + /// The of the provided . + /// The handle of the file to use. + /// Should be closed when this + /// is disposed? public FileHandleStorage(FileSystemClient fsClient, FileHandle handle, bool closeFile) { _fsClient = fsClient; @@ -282,4 +324,4 @@ public class FileHandleStorage : IStorage _size = size; return Result.Success; } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/FileHandleStorage.cs b/src/LibHac/Fs/FileHandleStorage.cs deleted file mode 100644 index 6b484513..00000000 --- a/src/LibHac/Fs/FileHandleStorage.cs +++ /dev/null @@ -1,98 +0,0 @@ -//using System; -//using LibHac.Common; -//using LibHac.Fs.Fsa; - -//namespace LibHac.Fs -//{ -// public class FileHandleStorage : IStorage -// { -// private const long InvalidSize = -1; -// private readonly object _locker = new object(); - -// private FileSystemClient FsClient { get; } -// private FileHandle Handle { get; } -// private long FileSize { get; set; } = InvalidSize; -// private bool CloseHandle { get; } - -// public FileHandleStorage(FileSystemClient fsClient, FileHandle handle) : this(fsClient, handle, false) { } - -// public FileHandleStorage(FileSystemClient fsClient, FileHandle handle, bool closeHandleOnDispose) -// { -// FsClient = fsClient; -// Handle = handle; -// CloseHandle = closeHandleOnDispose; -// } - -// protected override Result DoRead(long offset, Span destination) -// { -// lock (_locker) -// { -// if (destination.Length == 0) return Result.Success; - -// Result rc = UpdateSize(); -// if (rc.IsFailure()) return rc; - -// if (!CheckAccessRange(offset, destination.Length, FileSize)) return ResultFs.OutOfRange.Log(); - -// return FsClient.ReadFile(Handle, offset, destination); -// } -// } - -// protected override Result DoWrite(long offset, ReadOnlySpan source) -// { -// lock (_locker) -// { -// if (source.Length == 0) return Result.Success; - -// Result rc = UpdateSize(); -// if (rc.IsFailure()) return rc; - -// if (!CheckAccessRange(offset, source.Length, FileSize)) return ResultFs.OutOfRange.Log(); - -// return FsClient.WriteFile(Handle, offset, source, WriteOption.None); -// } -// } - -// protected override Result DoFlush() -// { -// return FsClient.FlushFile(Handle); -// } - -// protected override Result DoSetSize(long size) -// { -// FileSize = InvalidSize; - -// return FsClient.SetFileSize(Handle, size); -// } - -// protected override Result DoGetSize(out long size) -// { -// UnsafeHelpers.SkipParamInit(out size); - -// Result rc = UpdateSize(); -// if (rc.IsFailure()) return rc; - -// size = FileSize; -// return Result.Success; -// } - -// private Result UpdateSize() -// { -// if (FileSize != InvalidSize) return Result.Success; - -// Result rc = FsClient.GetFileSize(out long fileSize, Handle); -// if (rc.IsFailure()) return rc; - -// FileSize = fileSize; -// return Result.Success; -// } - -// protected override void Dispose(bool disposing) -// { -// if (CloseHandle) -// { -// FsClient.CloseFile(Handle); -// } -// } -// } -//} diff --git a/src/LibHac/Fs/FileStorageBasedFileSystem.cs b/src/LibHac/Fs/FileStorageBasedFileSystem.cs deleted file mode 100644 index 27f340a5..00000000 --- a/src/LibHac/Fs/FileStorageBasedFileSystem.cs +++ /dev/null @@ -1,41 +0,0 @@ -//using LibHac.Common; -//using LibHac.Fs.Fsa; - -//namespace LibHac.Fs -//{ -// public class FileStorageBasedFileSystem : FileStorage2 -// { -// private ReferenceCountedDisposable _baseFileSystem; -// private UniqueRef _baseFile; - -// public FileStorageBasedFileSystem() -// { -// FileSize = SizeNotInitialized; -// } - -// public Result Initialize(ref ReferenceCountedDisposable baseFileSystem, in Path path, -// OpenMode mode) -// { -// using var baseFile = new UniqueRef(); -// Result rc = baseFileSystem.Get.OpenFile(ref baseFile.Ref(), in path, mode); -// if (rc.IsFailure()) return rc; - -// SetFile(baseFile.Get); -// _baseFileSystem = Shared.Move(ref baseFileSystem); -// _baseFile.Set(ref _baseFile.Ref()); - -// return Result.Success; -// } - -// protected override void Dispose(bool disposing) -// { -// if (disposing) -// { -// _baseFile.Destroy(); -// _baseFileSystem?.Dispose(); -// } - -// base.Dispose(disposing); -// } -// } -//} From 2a658e273334f1a2183971e9b75213d918d209a7 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sun, 19 Dec 2021 14:36:32 -0700 Subject: [PATCH 04/34] Update FileSystemInterfaceAdapter for 13.1.0 --- .../FsSrv/Impl/FileSystemInterfaceAdapter.cs | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/LibHac/FsSrv/Impl/FileSystemInterfaceAdapter.cs b/src/LibHac/FsSrv/Impl/FileSystemInterfaceAdapter.cs index 7b0552de..cd5dfe6e 100644 --- a/src/LibHac/FsSrv/Impl/FileSystemInterfaceAdapter.cs +++ b/src/LibHac/FsSrv/Impl/FileSystemInterfaceAdapter.cs @@ -20,7 +20,7 @@ namespace LibHac.FsSrv.Impl; /// /// Wraps an to allow interfacing with it via the interface over IPC. /// -/// Based on FS 12.1.0 (nnSdk 12.3.1) +/// Based on FS 13.1.0 (nnSdk 13.4.0) public class FileInterfaceAdapter : IFileSf { private SharedRef _parentFs; @@ -49,15 +49,18 @@ public class FileInterfaceAdapter : IFileSf if (offset < 0) return ResultFs.InvalidOffset.Log(); - if (destination.Size < 0 || destination.Size < (int)size) + if (destination.Size < 0) + return ResultFs.InvalidSize.Log(); + + if (destination.Size < (int)size) return ResultFs.InvalidSize.Log(); Result rc = Result.Success; - long tmpBytesRead = 0; + long readSize = 0; for (int tryNum = 0; tryNum < maxTryCount; tryNum++) { - rc = _baseFile.Get.Read(out tmpBytesRead, offset, destination.Buffer.Slice(0, (int)size), option); + rc = _baseFile.Get.Read(out readSize, offset, destination.Buffer.Slice(0, (int)size), option); // Retry on ResultDataCorrupted if (!ResultFs.DataCorrupted.Includes(rc)) @@ -66,7 +69,7 @@ public class FileInterfaceAdapter : IFileSf if (rc.IsFailure()) return rc; - bytesRead = tmpBytesRead; + bytesRead = readSize; return Result.Success; } @@ -75,7 +78,10 @@ public class FileInterfaceAdapter : IFileSf if (offset < 0) return ResultFs.InvalidOffset.Log(); - if (source.Size < 0 || source.Size < (int)size) + if (source.Size < 0) + return ResultFs.InvalidSize.Log(); + + if (source.Size < (int)size) return ResultFs.InvalidSize.Log(); using var scopedPriorityChanger = @@ -175,7 +181,7 @@ public class FileInterfaceAdapter : IFileSf /// /// Wraps an to allow interfacing with it via the interface over IPC. /// -/// Based on FS 12.1.0 (nnSdk 12.3.1) +/// Based on FS 13.1.0 (nnSdk 13.4.0) public class DirectoryInterfaceAdapter : IDirectorySf { private SharedRef _parentFs; @@ -235,13 +241,11 @@ public class DirectoryInterfaceAdapter : IDirectorySf /// Wraps an to allow interfacing with it via the interface over IPC. /// All incoming paths are normalized before they are passed to the base . /// -/// Based on FS 12.1.0 (nnSdk 12.3.1) +/// Based on FS 13.1.0 (nnSdk 13.4.0) public class FileSystemInterfaceAdapter : IFileSystemSf { private SharedRef _baseFileSystem; private PathFlags _pathFlags; - - // This field is always false in FS 12.0.0. Not sure what it's actually used for. private bool _allowAllOperations; // In FS, FileSystemInterfaceAdapter is derived from ISharedObject, so that's used for ref-counting when @@ -593,4 +597,4 @@ public class FileSystemInterfaceAdapter : IFileSystemSf fileSystem.SetByCopy(in _baseFileSystem); return Result.Success; } -} +} \ No newline at end of file From a17605b292e2a56a92312fde6c26ad78be741352 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Mon, 20 Dec 2021 15:12:34 -0700 Subject: [PATCH 05/34] Fixup FileSystemBufferManager --- src/LibHac/Fs/Buffers/IBufferManager.cs | 13 +- src/LibHac/FsSystem/BufferedStorage.cs | 1 + .../Buffers/FileSystemBufferManager.cs | 320 ++++++++++-------- src/LibHac/FsSystem/CompressedStorage.cs | 4 +- src/LibHac/FsSystem/Impl/BlockCacheManager.cs | 3 +- .../FsSystem/FileSystemBufferManagerTests.cs | 16 +- 6 files changed, 190 insertions(+), 167 deletions(-) diff --git a/src/LibHac/Fs/Buffers/IBufferManager.cs b/src/LibHac/Fs/Buffers/IBufferManager.cs index 27c94fc8..63e03365 100644 --- a/src/LibHac/Fs/Buffers/IBufferManager.cs +++ b/src/LibHac/Fs/Buffers/IBufferManager.cs @@ -1,4 +1,5 @@ using System; + using Buffer = LibHac.Mem.Buffer; using CacheHandle = System.Int64; @@ -28,6 +29,8 @@ public abstract class IBufferManager : IDisposable public const int BufferLevelMin = 0; + public virtual void Dispose() { } + public Buffer AllocateBuffer(int size, BufferAttribute attribute) => DoAllocateBuffer(size, attribute); @@ -118,12 +121,4 @@ public abstract class IBufferManager : IDisposable protected abstract int DoGetTotalAllocatableSizePeak(); protected abstract int DoGetRetriedCount(); protected abstract void DoClearPeak(); - - protected virtual void Dispose(bool disposing) { } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } -} +} \ No newline at end of file diff --git a/src/LibHac/FsSystem/BufferedStorage.cs b/src/LibHac/FsSystem/BufferedStorage.cs index e7db9bbd..153b91dd 100644 --- a/src/LibHac/FsSystem/BufferedStorage.cs +++ b/src/LibHac/FsSystem/BufferedStorage.cs @@ -7,6 +7,7 @@ using LibHac.Diag; using LibHac.Fs; using LibHac.FsSystem.Buffers; using LibHac.Util; + using Buffer = LibHac.Mem.Buffer; using CacheHandle = System.Int64; diff --git a/src/LibHac/FsSystem/Buffers/FileSystemBufferManager.cs b/src/LibHac/FsSystem/Buffers/FileSystemBufferManager.cs index b405c64f..4c1b851a 100644 --- a/src/LibHac/FsSystem/Buffers/FileSystemBufferManager.cs +++ b/src/LibHac/FsSystem/Buffers/FileSystemBufferManager.cs @@ -5,16 +5,21 @@ using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Diag; using LibHac.Fs; +using LibHac.Os; using LibHac.Util; + using Buffer = LibHac.Mem.Buffer; using CacheHandle = System.Int64; // ReSharper disable once CheckNamespace namespace LibHac.FsSystem; +/// +/// An that uses a as an allocator. +/// public class FileSystemBufferManager : IBufferManager { - private class CacheHandleTable + private class CacheHandleTable : IDisposable { private struct Entry { @@ -62,14 +67,24 @@ public class FileSystemBufferManager : IBufferManager } } - private Entry[] Entries { get; set; } - private int EntryCount { get; set; } - private int EntryCountMax { get; set; } - private LinkedList AttrList { get; set; } = new(); - private int CacheCountMin { get; set; } - private int CacheSizeMin { get; set; } - private int TotalCacheSize { get; set; } - private CacheHandle CurrentHandle { get; set; } + private Entry[] _entries; + private int _entryCount; + private int _entryCountMax; + private LinkedList _attrList; + private int _cacheCountMin; + private int _cacheSizeMin; + private int _totalCacheSize; + private CacheHandle _currentHandle; + + public CacheHandleTable() + { + _attrList = new LinkedList(); + } + + public void Dispose() + { + FinalizeObject(); + } // ReSharper disable once UnusedMember.Local // We can't use an external buffer in C# without ensuring all allocated buffers are pinned. @@ -90,41 +105,53 @@ public class FileSystemBufferManager : IBufferManager public Result Initialize(int maxCacheCount) { // Validate pre-conditions. - Assert.SdkRequiresNull(Entries); + Assert.SdkRequiresNull(_entries); // Note: We don't have the option of using an external Entry buffer like the original C++ code // because Entry includes managed references so we can't cast a byte* to Entry* without pinning. // If we don't have an external buffer, try to allocate an internal one. - Entries = new Entry[maxCacheCount]; + _entries = new Entry[maxCacheCount]; - if (Entries == null) + if (_entries == null) { return ResultFs.AllocationMemoryFailedInFileSystemBufferManagerA.Log(); } // Set entries. - EntryCount = 0; - EntryCountMax = maxCacheCount; + _entryCount = 0; + _entryCountMax = maxCacheCount; - Assert.SdkNotNull(Entries); + Assert.SdkNotNull(_entries); - CacheCountMin = maxCacheCount / 16; - CacheSizeMin = CacheCountMin * 0x100; + _cacheCountMin = maxCacheCount / 16; + _cacheSizeMin = _cacheCountMin * 0x100; return Result.Success; } + public void FinalizeObject() + { + if (_entries is null) + return; + + Assert.SdkAssert(_entryCount == 0); + + _attrList.Clear(); + _entries = null; + _totalCacheSize = 0; + } + // ReSharper disable once UnusedParameter.Local private int GetCacheCountMin(BufferAttribute attr) { - return CacheCountMin; + return _cacheCountMin; } // ReSharper disable once UnusedParameter.Local private int GetCacheSizeMin(BufferAttribute attr) { - return CacheSizeMin; + return _cacheSizeMin; } public bool Register(out CacheHandle handle, Buffer buffer, BufferAttribute attr) @@ -132,7 +159,7 @@ public class FileSystemBufferManager : IBufferManager UnsafeHelpers.SkipParamInit(out handle); // Validate pre-conditions. - Assert.SdkRequiresNotNull(Entries); + Assert.SdkRequiresNotNull(_entries); Assert.SdkRequiresNotNull(ref handle); // Get the entry. @@ -154,10 +181,10 @@ public class FileSystemBufferManager : IBufferManager // Make a new attr info and add it to the list. // Note: Not using attr info buffer var newInfo = new AttrInfo(attr.Level, 1, buffer.Length); - AttrList.AddLast(newInfo); + _attrList.AddLast(newInfo); } - TotalCacheSize += buffer.Length; + _totalCacheSize += buffer.Length; handle = entry.GetHandle(); return true; } @@ -166,17 +193,17 @@ public class FileSystemBufferManager : IBufferManager { // Validate pre-conditions. Unsafe.SkipInit(out buffer); - Assert.SdkRequiresNotNull(Entries); + Assert.SdkRequiresNotNull(_entries); Assert.SdkRequiresNotNull(ref buffer); UnsafeHelpers.SkipParamInit(out buffer); // Find the lower bound for the entry. - for (int i = 0; i < EntryCount; i++) + for (int i = 0; i < _entryCount; i++) { - if (Entries[i].GetHandle() == handle) + if (_entries[i].GetHandle() == handle) { - UnregisterCore(out buffer, ref Entries[i]); + UnregisterCore(out buffer, ref _entries[i]); return true; } } @@ -190,13 +217,13 @@ public class FileSystemBufferManager : IBufferManager { // Validate pre-conditions. Unsafe.SkipInit(out buffer); - Assert.SdkRequiresNotNull(Entries); + Assert.SdkRequiresNotNull(_entries); Assert.SdkRequiresNotNull(ref buffer); UnsafeHelpers.SkipParamInit(out buffer); // If we have no entries, we can't unregister any. - if (EntryCount == 0) + if (_entryCount == 0) { return false; } @@ -214,18 +241,18 @@ public class FileSystemBufferManager : IBufferManager // Find an entry, falling back to the first entry. ref Entry entry = ref Unsafe.NullRef(); - for (int i = 0; i < EntryCount; i++) + for (int i = 0; i < _entryCount; i++) { - if (CanUnregister(this, ref Entries[i])) + if (CanUnregister(this, ref _entries[i])) { - entry = ref Entries[i]; + entry = ref _entries[i]; break; } } if (Unsafe.IsNullRef(ref entry)) { - entry = ref Entries[0]; + entry = ref _entries[0]; } Assert.SdkNotNull(ref entry); @@ -237,7 +264,7 @@ public class FileSystemBufferManager : IBufferManager { // Validate pre-conditions. Unsafe.SkipInit(out buffer); - Assert.SdkRequiresNotNull(Entries); + Assert.SdkRequiresNotNull(_entries); Assert.SdkRequiresNotNull(ref buffer); Assert.SdkRequiresNotNull(ref entry); @@ -254,8 +281,8 @@ public class FileSystemBufferManager : IBufferManager attrInfo.SubtractCacheSize(entry.GetSize()); // Release from cached size. - Assert.SdkGreaterEqual(TotalCacheSize, entry.GetSize()); - TotalCacheSize -= entry.GetSize(); + Assert.SdkGreaterEqual(_totalCacheSize, entry.GetSize()); + _totalCacheSize -= entry.GetSize(); // Release the entry. buffer = entry.GetBuffer(); @@ -264,27 +291,27 @@ public class FileSystemBufferManager : IBufferManager public CacheHandle PublishCacheHandle() { - Assert.SdkRequires(Entries != null); - return ++CurrentHandle; + Assert.SdkRequires(_entries != null); + return ++_currentHandle; } public int GetTotalCacheSize() { - return TotalCacheSize; + return _totalCacheSize; } private ref Entry AcquireEntry(Buffer buffer, BufferAttribute attr) { // Validate pre-conditions. - Assert.SdkRequiresNotNull(Entries); + Assert.SdkRequiresNotNull(_entries); ref Entry entry = ref Unsafe.NullRef(); - if (EntryCount < EntryCountMax) + if (_entryCount < _entryCountMax) { - entry = ref Entries[EntryCount]; + entry = ref _entries[_entryCount]; entry.Initialize(PublishCacheHandle(), buffer, attr); - EntryCount++; - Assert.SdkAssert(EntryCount == 1 || Entries[EntryCount - 2].GetHandle() < entry.GetHandle()); + _entryCount++; + Assert.SdkAssert(_entryCount == 1 || _entries[_entryCount - 2].GetHandle() < entry.GetHandle()); } return ref entry; @@ -293,11 +320,11 @@ public class FileSystemBufferManager : IBufferManager private void ReleaseEntry(ref Entry entry) { // Validate pre-conditions. - Assert.SdkRequiresNotNull(Entries); + Assert.SdkRequiresNotNull(_entries); Assert.SdkRequiresNotNull(ref entry); // Ensure the entry is valid. - Span entryBuffer = Entries; + Span entryBuffer = _entries; Assert.SdkAssert(!Unsafe.IsAddressLessThan(ref entry, ref MemoryMarshal.GetReference(entryBuffer))); Assert.SdkAssert(Unsafe.IsAddressLessThan(ref entry, ref Unsafe.Add(ref MemoryMarshal.GetReference(entryBuffer), entryBuffer.Length))); @@ -307,17 +334,17 @@ public class FileSystemBufferManager : IBufferManager Unsafe.SizeOf(); // Copy the entries back by one. - Span source = entryBuffer.Slice(index + 1, EntryCount - (index + 1)); + Span source = entryBuffer.Slice(index + 1, _entryCount - (index + 1)); Span dest = entryBuffer.Slice(index); source.CopyTo(dest); // Decrement our entry count. - EntryCount--; + _entryCount--; } private ref AttrInfo FindAttrInfo(BufferAttribute attr) { - LinkedListNode curNode = AttrList.First; + LinkedListNode curNode = _attrList.First; while (curNode != null) { @@ -333,50 +360,54 @@ public class FileSystemBufferManager : IBufferManager } } - private FileSystemBuddyHeap BuddyHeap { get; } = new(); - private CacheHandleTable CacheTable { get; } = new(); - private int TotalSize { get; set; } - private int PeakFreeSize { get; set; } - private int PeakTotalAllocatableSize { get; set; } - private int RetriedCount { get; set; } - private object Locker { get; } = new(); + private FileSystemBuddyHeap _buddyHeap; + private CacheHandleTable _cacheTable; + private int _totalSize; + private int _peakFreeSize; + private int _peakTotalAllocatableSize; + private int _retriedCount; + private SdkMutexType _mutex; - protected override void Dispose(bool disposing) + public FileSystemBufferManager() { - if (disposing) - { - BuddyHeap.Dispose(); - } + _buddyHeap = new FileSystemBuddyHeap(); + _cacheTable = new CacheHandleTable(); + _mutex = new SdkMutexType(); + } - base.Dispose(disposing); + public override void Dispose() + { + _cacheTable.Dispose(); + _buddyHeap.Dispose(); + base.Dispose(); } public Result Initialize(int maxCacheCount, Memory heapBuffer, int blockSize) { - Result rc = CacheTable.Initialize(maxCacheCount); + Result rc = _cacheTable.Initialize(maxCacheCount); if (rc.IsFailure()) return rc; - rc = BuddyHeap.Initialize(heapBuffer, blockSize); + rc = _buddyHeap.Initialize(heapBuffer, blockSize); if (rc.IsFailure()) return rc; - TotalSize = (int)BuddyHeap.GetTotalFreeSize(); - PeakFreeSize = TotalSize; - PeakTotalAllocatableSize = TotalSize; + _totalSize = (int)_buddyHeap.GetTotalFreeSize(); + _peakFreeSize = _totalSize; + _peakTotalAllocatableSize = _totalSize; return Result.Success; } public Result Initialize(int maxCacheCount, Memory heapBuffer, int blockSize, int maxOrder) { - Result rc = CacheTable.Initialize(maxCacheCount); + Result rc = _cacheTable.Initialize(maxCacheCount); if (rc.IsFailure()) return rc; - rc = BuddyHeap.Initialize(heapBuffer, blockSize, maxOrder); + rc = _buddyHeap.Initialize(heapBuffer, blockSize, maxOrder); if (rc.IsFailure()) return rc; - TotalSize = (int)BuddyHeap.GetTotalFreeSize(); - PeakFreeSize = TotalSize; - PeakTotalAllocatableSize = TotalSize; + _totalSize = (int)_buddyHeap.GetTotalFreeSize(); + _peakFreeSize = _totalSize; + _peakTotalAllocatableSize = _totalSize; return Result.Success; } @@ -386,15 +417,15 @@ public class FileSystemBufferManager : IBufferManager // Note: We can't use an external buffer for the cache handle table since it contains managed pointers, // so pass the work buffer directly to the buddy heap. - Result rc = CacheTable.Initialize(maxCacheCount); + Result rc = _cacheTable.Initialize(maxCacheCount); if (rc.IsFailure()) return rc; - rc = BuddyHeap.Initialize(heapBuffer, blockSize, workBuffer); + rc = _buddyHeap.Initialize(heapBuffer, blockSize, workBuffer); if (rc.IsFailure()) return rc; - TotalSize = (int)BuddyHeap.GetTotalFreeSize(); - PeakFreeSize = TotalSize; - PeakTotalAllocatableSize = TotalSize; + _totalSize = (int)_buddyHeap.GetTotalFreeSize(); + _peakFreeSize = _totalSize; + _peakTotalAllocatableSize = _totalSize; return Result.Success; } @@ -405,40 +436,39 @@ public class FileSystemBufferManager : IBufferManager // Note: We can't use an external buffer for the cache handle table since it contains managed pointers, // so pass the work buffer directly to the buddy heap. - Result rc = CacheTable.Initialize(maxCacheCount); + Result rc = _cacheTable.Initialize(maxCacheCount); if (rc.IsFailure()) return rc; - rc = BuddyHeap.Initialize(heapBuffer, blockSize, maxOrder, workBuffer); + rc = _buddyHeap.Initialize(heapBuffer, blockSize, maxOrder, workBuffer); if (rc.IsFailure()) return rc; - TotalSize = (int)BuddyHeap.GetTotalFreeSize(); - PeakFreeSize = TotalSize; - PeakTotalAllocatableSize = TotalSize; + _totalSize = (int)_buddyHeap.GetTotalFreeSize(); + _peakFreeSize = _totalSize; + _peakTotalAllocatableSize = _totalSize; return Result.Success; } protected override Buffer DoAllocateBuffer(int size, BufferAttribute attribute) { - lock (Locker) - { - return AllocateBufferImpl(size, attribute); - } + using var lk = new ScopedLock(ref _mutex); + + return AllocateBufferImpl(size, attribute); } private Buffer AllocateBufferImpl(int size, BufferAttribute attribute) { - int order = BuddyHeap.GetOrderFromBytes((nuint)size); + int order = _buddyHeap.GetOrderFromBytes((nuint)size); Assert.SdkAssert(order >= 0); // Allocate space on the heap Buffer buffer; - while ((buffer = BuddyHeap.AllocateBufferByOrder(order)).IsNull) + while ((buffer = _buddyHeap.AllocateBufferByOrder(order)).IsNull) { // Not enough space in heap. Deallocate cached buffer and try again. - RetriedCount++; + _retriedCount++; - if (!CacheTable.UnregisterOldest(out Buffer deallocateBuffer, attribute, size)) + if (!_cacheTable.UnregisterOldest(out Buffer deallocateBuffer, attribute, size)) { // No cached buffers left to deallocate. return Buffer.Empty; @@ -448,57 +478,56 @@ public class FileSystemBufferManager : IBufferManager } // Successfully allocated a buffer. - int allocatedSize = (int)BuddyHeap.GetBytesFromOrder(order); + int allocatedSize = (int)_buddyHeap.GetBytesFromOrder(order); Assert.SdkAssert(size <= allocatedSize); // Update heap stats - int freeSize = (int)BuddyHeap.GetTotalFreeSize(); - PeakFreeSize = Math.Min(PeakFreeSize, freeSize); + int freeSize = (int)_buddyHeap.GetTotalFreeSize(); + _peakFreeSize = Math.Min(_peakFreeSize, freeSize); - int totalAllocatableSize = freeSize + CacheTable.GetTotalCacheSize(); - PeakTotalAllocatableSize = Math.Min(PeakTotalAllocatableSize, totalAllocatableSize); + int totalAllocatableSize = freeSize + _cacheTable.GetTotalCacheSize(); + _peakTotalAllocatableSize = Math.Min(_peakTotalAllocatableSize, totalAllocatableSize); return buffer; } protected override void DoDeallocateBuffer(Buffer buffer) { - lock (Locker) - { - DeallocateBufferImpl(buffer); - } + using var lk = new ScopedLock(ref _mutex); + + DeallocateBufferImpl(buffer); } private void DeallocateBufferImpl(Buffer buffer) { Assert.SdkRequires(BitUtil.IsPowerOfTwo(buffer.Length)); - BuddyHeap.Free(buffer); + _buddyHeap.Free(buffer); } protected override CacheHandle DoRegisterCache(Buffer buffer, BufferAttribute attribute) { - lock (Locker) - { - return RegisterCacheImpl(buffer, attribute); - } + using var lk = new ScopedLock(ref _mutex); + + return RegisterCacheImpl(buffer, attribute); } private CacheHandle RegisterCacheImpl(Buffer buffer, BufferAttribute attribute) { - CacheHandle handle; + // ReSharper disable once RedundantAssignment + CacheHandle handle = 0; // Try to register the handle. - while (!CacheTable.Register(out handle, buffer, attribute)) + while (!_cacheTable.Register(out handle, buffer, attribute)) { // Unregister a buffer and try registering again. - RetriedCount++; - if (!CacheTable.UnregisterOldest(out Buffer deallocateBuffer, attribute)) + _retriedCount++; + if (!_cacheTable.UnregisterOldest(out Buffer deallocateBuffer, attribute)) { // Can't unregister any existing buffers. // Register the input buffer to /dev/null. DeallocateBufferImpl(buffer); - return CacheTable.PublishCacheHandle(); + return _cacheTable.PublishCacheHandle(); } // Deallocate the unregistered buffer. @@ -510,18 +539,17 @@ public class FileSystemBufferManager : IBufferManager protected override Buffer DoAcquireCache(CacheHandle handle) { - lock (Locker) - { - return AcquireCacheImpl(handle); - } + using var lk = new ScopedLock(ref _mutex); + + return AcquireCacheImpl(handle); } private Buffer AcquireCacheImpl(CacheHandle handle) { - if (CacheTable.Unregister(out Buffer range, handle)) + if (_cacheTable.Unregister(out Buffer range, handle)) { - int totalAllocatableSize = (int)BuddyHeap.GetTotalFreeSize() + CacheTable.GetTotalCacheSize(); - PeakTotalAllocatableSize = Math.Min(PeakTotalAllocatableSize, totalAllocatableSize); + int totalAllocatableSize = (int)_buddyHeap.GetTotalFreeSize() + _cacheTable.GetTotalCacheSize(); + _peakTotalAllocatableSize = Math.Min(_peakTotalAllocatableSize, totalAllocatableSize); } else { @@ -533,86 +561,80 @@ public class FileSystemBufferManager : IBufferManager protected override int DoGetTotalSize() { - return TotalSize; + return _totalSize; } protected override int DoGetFreeSize() { - lock (Locker) - { - return GetFreeSizeImpl(); - } + using var lk = new ScopedLock(ref _mutex); + + return GetFreeSizeImpl(); } private int GetFreeSizeImpl() { - return (int)BuddyHeap.GetTotalFreeSize(); + return (int)_buddyHeap.GetTotalFreeSize(); } protected override int DoGetTotalAllocatableSize() { - lock (Locker) - { - return GetTotalAllocatableSizeImpl(); - } + using var lk = new ScopedLock(ref _mutex); + + return GetTotalAllocatableSizeImpl(); } private int GetTotalAllocatableSizeImpl() { - return GetFreeSizeImpl() + CacheTable.GetTotalCacheSize(); + return GetFreeSizeImpl() + _cacheTable.GetTotalCacheSize(); } protected override int DoGetFreeSizePeak() { - lock (Locker) - { - return GetFreeSizePeakImpl(); - } + using var lk = new ScopedLock(ref _mutex); + + return GetFreeSizePeakImpl(); } private int GetFreeSizePeakImpl() { - return PeakFreeSize; + return _peakFreeSize; } protected override int DoGetTotalAllocatableSizePeak() { - lock (Locker) - { - return GetTotalAllocatableSizePeakImpl(); - } + using var lk = new ScopedLock(ref _mutex); + + return GetTotalAllocatableSizePeakImpl(); } private int GetTotalAllocatableSizePeakImpl() { - return PeakTotalAllocatableSize; + return _peakTotalAllocatableSize; } protected override int DoGetRetriedCount() { - lock (Locker) - { - return GetRetriedCountImpl(); - } + using var lk = new ScopedLock(ref _mutex); + + return GetRetriedCountImpl(); } private int GetRetriedCountImpl() { - return RetriedCount; + return _retriedCount; } protected override void DoClearPeak() { - lock (Locker) - { - ClearPeakImpl(); - } + using var lk = new ScopedLock(ref _mutex); + + ClearPeakImpl(); } private void ClearPeakImpl() { - PeakFreeSize = GetFreeSizeImpl(); - PeakTotalAllocatableSize = GetTotalAllocatableSizeImpl(); - RetriedCount = 0; + _peakFreeSize = GetFreeSizeImpl(); + _peakTotalAllocatableSize = GetTotalAllocatableSizeImpl(); + _retriedCount = 0; } -} +} \ No newline at end of file diff --git a/src/LibHac/FsSystem/CompressedStorage.cs b/src/LibHac/FsSystem/CompressedStorage.cs index 8c64f0fd..51392f20 100644 --- a/src/LibHac/FsSystem/CompressedStorage.cs +++ b/src/LibHac/FsSystem/CompressedStorage.cs @@ -4,7 +4,9 @@ using LibHac.Diag; using LibHac.Fs; using LibHac.FsSystem.Impl; using LibHac.Os; + using Buffer = LibHac.Mem.Buffer; +using CacheHandle = System.Int64; namespace LibHac.FsSystem; @@ -135,7 +137,7 @@ public class CompressedStorage : IStorage, IAsynchronousAccessSplitter public struct CacheEntry : IBlockCacheManagerEntry { public Range Range { get; set; } - public long Handle { get; set; } + public CacheHandle Handle { get; set; } public Buffer Buffer { get; set; } public bool IsValid { get; set; } public bool IsCached { get; set; } diff --git a/src/LibHac/FsSystem/Impl/BlockCacheManager.cs b/src/LibHac/FsSystem/Impl/BlockCacheManager.cs index 1f2bf195..237f07ef 100644 --- a/src/LibHac/FsSystem/Impl/BlockCacheManager.cs +++ b/src/LibHac/FsSystem/Impl/BlockCacheManager.cs @@ -3,6 +3,7 @@ using LibHac.Diag; using LibHac.Fs; using Buffer = LibHac.Mem.Buffer; +using CacheHandle = System.Int64; namespace LibHac.FsSystem.Impl; @@ -13,7 +14,7 @@ public interface IBlockCacheManagerEntry where TRange : struct, IBlockCa bool IsWriteBack { get; set; } bool IsCached { get; set; } bool IsFlushing { set; } - long Handle { get; set; } + CacheHandle Handle { get; set; } Buffer Buffer { get; set; } short Age { get; set; } diff --git a/tests/LibHac.Tests/FsSystem/FileSystemBufferManagerTests.cs b/tests/LibHac.Tests/FsSystem/FileSystemBufferManagerTests.cs index e98150f6..55df93e7 100644 --- a/tests/LibHac.Tests/FsSystem/FileSystemBufferManagerTests.cs +++ b/tests/LibHac.Tests/FsSystem/FileSystemBufferManagerTests.cs @@ -3,6 +3,8 @@ using LibHac.FsSystem; using LibHac.Mem; using Xunit; +using CacheHandle = System.Int64; + namespace LibHac.Tests.FsSystem; public class FileSystemBufferManagerTests @@ -38,7 +40,7 @@ public class FileSystemBufferManagerTests FileSystemBufferManager manager = CreateManager(0x20000); Buffer buffer1 = manager.AllocateBuffer(0x10000); - long handle = manager.RegisterCache(buffer1, new IBufferManager.BufferAttribute()); + CacheHandle handle = manager.RegisterCache(buffer1, new IBufferManager.BufferAttribute()); manager.AllocateBuffer(0x10000); Buffer buffer3 = manager.AcquireCache(handle); @@ -52,7 +54,7 @@ public class FileSystemBufferManagerTests FileSystemBufferManager manager = CreateManager(0x20000); Buffer buffer1 = manager.AllocateBuffer(0x10000); - long handle = manager.RegisterCache(buffer1, new IBufferManager.BufferAttribute()); + CacheHandle handle = manager.RegisterCache(buffer1, new IBufferManager.BufferAttribute()); manager.AllocateBuffer(0x20000); Buffer buffer3 = manager.AcquireCache(handle); @@ -69,10 +71,10 @@ public class FileSystemBufferManagerTests Buffer buffer3 = manager.AllocateBuffer(0x8000); Buffer buffer4 = manager.AllocateBuffer(0x8000); - long handle1 = manager.RegisterCache(buffer1, new IBufferManager.BufferAttribute()); - long handle2 = manager.RegisterCache(buffer2, new IBufferManager.BufferAttribute()); - long handle3 = manager.RegisterCache(buffer3, new IBufferManager.BufferAttribute()); - long handle4 = manager.RegisterCache(buffer4, new IBufferManager.BufferAttribute()); + CacheHandle handle1 = manager.RegisterCache(buffer1, new IBufferManager.BufferAttribute()); + CacheHandle handle2 = manager.RegisterCache(buffer2, new IBufferManager.BufferAttribute()); + CacheHandle handle3 = manager.RegisterCache(buffer3, new IBufferManager.BufferAttribute()); + CacheHandle handle4 = manager.RegisterCache(buffer4, new IBufferManager.BufferAttribute()); manager.AllocateBuffer(0x10000); @@ -86,4 +88,4 @@ public class FileSystemBufferManagerTests Assert.Equal(buffer3, buffer3B); Assert.Equal(buffer4, buffer4B); } -} +} \ No newline at end of file From f1105da2ccbf1299a6e184fe5714ca69acf31d17 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 21 Dec 2021 12:45:09 -0700 Subject: [PATCH 06/34] Update FileSystemBufferManager for 13.1.0 --- src/LibHac/Fs/Buffers/IBufferManager.cs | 2 +- src/LibHac/FsSystem/BufferedStorage.cs | 2 +- src/LibHac/FsSystem/Buffers/FileSystemBufferManager.cs | 3 ++- src/LibHac/FsSystem/CompressedStorage.cs | 2 +- src/LibHac/FsSystem/Impl/BlockCacheManager.cs | 2 +- tests/LibHac.Tests/FsSystem/FileSystemBufferManagerTests.cs | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/LibHac/Fs/Buffers/IBufferManager.cs b/src/LibHac/Fs/Buffers/IBufferManager.cs index 63e03365..83788f48 100644 --- a/src/LibHac/Fs/Buffers/IBufferManager.cs +++ b/src/LibHac/Fs/Buffers/IBufferManager.cs @@ -1,7 +1,7 @@ using System; using Buffer = LibHac.Mem.Buffer; -using CacheHandle = System.Int64; +using CacheHandle = System.UInt64; // ReSharper disable once CheckNamespace namespace LibHac.Fs; diff --git a/src/LibHac/FsSystem/BufferedStorage.cs b/src/LibHac/FsSystem/BufferedStorage.cs index 153b91dd..09c7d97b 100644 --- a/src/LibHac/FsSystem/BufferedStorage.cs +++ b/src/LibHac/FsSystem/BufferedStorage.cs @@ -9,7 +9,7 @@ using LibHac.FsSystem.Buffers; using LibHac.Util; using Buffer = LibHac.Mem.Buffer; -using CacheHandle = System.Int64; +using CacheHandle = System.UInt64; namespace LibHac.FsSystem; diff --git a/src/LibHac/FsSystem/Buffers/FileSystemBufferManager.cs b/src/LibHac/FsSystem/Buffers/FileSystemBufferManager.cs index 4c1b851a..f3acf25c 100644 --- a/src/LibHac/FsSystem/Buffers/FileSystemBufferManager.cs +++ b/src/LibHac/FsSystem/Buffers/FileSystemBufferManager.cs @@ -9,7 +9,7 @@ using LibHac.Os; using LibHac.Util; using Buffer = LibHac.Mem.Buffer; -using CacheHandle = System.Int64; +using CacheHandle = System.UInt64; // ReSharper disable once CheckNamespace namespace LibHac.FsSystem; @@ -17,6 +17,7 @@ namespace LibHac.FsSystem; /// /// An that uses a as an allocator. /// +/// Based on FS 13.1.0 (nnSdk 13.4.0) public class FileSystemBufferManager : IBufferManager { private class CacheHandleTable : IDisposable diff --git a/src/LibHac/FsSystem/CompressedStorage.cs b/src/LibHac/FsSystem/CompressedStorage.cs index 51392f20..13443ad9 100644 --- a/src/LibHac/FsSystem/CompressedStorage.cs +++ b/src/LibHac/FsSystem/CompressedStorage.cs @@ -6,7 +6,7 @@ using LibHac.FsSystem.Impl; using LibHac.Os; using Buffer = LibHac.Mem.Buffer; -using CacheHandle = System.Int64; +using CacheHandle = System.UInt64; namespace LibHac.FsSystem; diff --git a/src/LibHac/FsSystem/Impl/BlockCacheManager.cs b/src/LibHac/FsSystem/Impl/BlockCacheManager.cs index 237f07ef..51a24273 100644 --- a/src/LibHac/FsSystem/Impl/BlockCacheManager.cs +++ b/src/LibHac/FsSystem/Impl/BlockCacheManager.cs @@ -3,7 +3,7 @@ using LibHac.Diag; using LibHac.Fs; using Buffer = LibHac.Mem.Buffer; -using CacheHandle = System.Int64; +using CacheHandle = System.UInt64; namespace LibHac.FsSystem.Impl; diff --git a/tests/LibHac.Tests/FsSystem/FileSystemBufferManagerTests.cs b/tests/LibHac.Tests/FsSystem/FileSystemBufferManagerTests.cs index 55df93e7..cad8dad0 100644 --- a/tests/LibHac.Tests/FsSystem/FileSystemBufferManagerTests.cs +++ b/tests/LibHac.Tests/FsSystem/FileSystemBufferManagerTests.cs @@ -3,7 +3,7 @@ using LibHac.FsSystem; using LibHac.Mem; using Xunit; -using CacheHandle = System.Int64; +using CacheHandle = System.UInt64; namespace LibHac.Tests.FsSystem; From d2d73827bac71c03e47cd885e83a35681586138a Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 21 Dec 2021 12:26:42 -0700 Subject: [PATCH 07/34] Fixup BufferedStorage --- src/LibHac/FsSystem/BufferedStorage.cs | 772 +++++++++--------- .../FsSystem/BufferedStorageTests.cs | 8 +- 2 files changed, 397 insertions(+), 383 deletions(-) diff --git a/src/LibHac/FsSystem/BufferedStorage.cs b/src/LibHac/FsSystem/BufferedStorage.cs index 09c7d97b..2bc7adf3 100644 --- a/src/LibHac/FsSystem/BufferedStorage.cs +++ b/src/LibHac/FsSystem/BufferedStorage.cs @@ -1,11 +1,11 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Threading; using LibHac.Common; using LibHac.Diag; using LibHac.Fs; using LibHac.FsSystem.Buffers; +using LibHac.Os; using LibHac.Util; using Buffer = LibHac.Mem.Buffer; @@ -32,73 +32,82 @@ public class BufferedStorage : IStorage public Span Buffer; } - private BufferedStorage BufferedStorage { get; set; } - private Buffer MemoryRange { get; set; } - private CacheHandle CacheHandle { get; set; } - private long Offset { get; set; } + private BufferedStorage _bufferedStorage; + private Buffer _memoryRange; + private CacheHandle _cacheHandle; + private long _offset; + + // Todo: Atomic type for these two bools? private bool _isValid; private bool _isDirty; - private int ReferenceCount { get; set; } - private int Index { get; set; } - private int NextIndex { get; set; } - private int PrevIndex { get; set; } + private int _referenceCount; - private ref Cache Next => ref BufferedStorage.Caches[NextIndex]; - private ref Cache Prev => ref BufferedStorage.Caches[PrevIndex]; + // Instead of storing pointers to the next Cache we store indexes + private int _index; + private int _nextIndex; + private int _prevIndex; + + private ref Cache Next => ref _bufferedStorage._caches[_nextIndex]; + private ref Cache Prev => ref _bufferedStorage._caches[_prevIndex]; + + public Cache(int index) + { + _bufferedStorage = null; + _memoryRange = Buffer.Empty; + _cacheHandle = 0; + _offset = InvalidOffset; + _isValid = false; + _isDirty = false; + _referenceCount = 1; + _index = index; + _nextIndex = InvalidIndex; + _prevIndex = InvalidIndex; + } public void Dispose() { FinalizeObject(); } - public void Initialize(BufferedStorage bufferedStorage, int index) + public void Initialize(BufferedStorage bufferedStorage) { - // Note: C# can't have default constructors on structs, so the default constructor code was - // moved into Initialize since Initialize is always called right after the constructor. - Offset = InvalidOffset; - ReferenceCount = 1; - Index = index; - NextIndex = InvalidIndex; - PrevIndex = InvalidIndex; - // End default constructor code - Assert.SdkRequiresNotNull(bufferedStorage); - Assert.SdkRequires(BufferedStorage == null); + Assert.SdkRequires(_bufferedStorage == null); - BufferedStorage = bufferedStorage; + _bufferedStorage = bufferedStorage; Link(); } public void FinalizeObject() { - Assert.SdkRequiresNotNull(BufferedStorage); - Assert.SdkRequiresNotNull(BufferedStorage.BufferManager); - Assert.SdkRequiresEqual(0, ReferenceCount); + Assert.SdkRequiresNotNull(_bufferedStorage); + Assert.SdkRequiresNotNull(_bufferedStorage._bufferManager); + Assert.SdkRequiresEqual(0, _referenceCount); // If we're valid, acquire our cache handle and free our buffer. if (IsValid()) { - IBufferManager bufferManager = BufferedStorage.BufferManager; + IBufferManager bufferManager = _bufferedStorage._bufferManager; if (!_isDirty) { - Assert.SdkAssert(MemoryRange.IsNull); - MemoryRange = bufferManager.AcquireCache(CacheHandle); + Assert.SdkAssert(_memoryRange.IsNull); + _memoryRange = bufferManager.AcquireCache(_cacheHandle); } - if (!MemoryRange.IsNull) + if (!_memoryRange.IsNull) { - bufferManager.DeallocateBuffer(MemoryRange); - MemoryRange = Buffer.Empty; + bufferManager.DeallocateBuffer(_memoryRange); + _memoryRange = Buffer.Empty; } } // Clear all our members. - BufferedStorage = null; - Offset = InvalidOffset; + _bufferedStorage = null; + _offset = InvalidOffset; _isValid = false; _isDirty = false; - NextIndex = InvalidIndex; - PrevIndex = InvalidIndex; + _nextIndex = InvalidIndex; + _prevIndex = InvalidIndex; } /// @@ -108,79 +117,79 @@ public class BufferedStorage : IStorage /// public void Link() { - Assert.SdkRequiresNotNull(BufferedStorage); - Assert.SdkRequiresNotNull(BufferedStorage.BufferManager); - Assert.SdkRequiresLess(0, ReferenceCount); + Assert.SdkRequiresNotNull(_bufferedStorage); + Assert.SdkRequiresNotNull(_bufferedStorage._bufferManager); + Assert.SdkRequiresLess(0, _referenceCount); - ReferenceCount--; - if (ReferenceCount == 0) + _referenceCount--; + if (_referenceCount == 0) { - Assert.SdkAssert(NextIndex == InvalidIndex); - Assert.SdkAssert(PrevIndex == InvalidIndex); + Assert.SdkAssert(_nextIndex == InvalidIndex); + Assert.SdkAssert(_prevIndex == InvalidIndex); // If the fetch list is empty we can simply add it as the only cache in the list. - if (BufferedStorage.NextFetchCacheIndex == InvalidIndex) + if (_bufferedStorage._nextFetchCacheIndex == InvalidIndex) { - BufferedStorage.NextFetchCacheIndex = Index; - NextIndex = Index; - PrevIndex = Index; + _bufferedStorage._nextFetchCacheIndex = _index; + _nextIndex = _index; + _prevIndex = _index; } else { // Check against a cache being registered twice. - ref Cache cache = ref BufferedStorage.NextFetchCache; + ref Cache cache = ref _bufferedStorage.NextFetchCache; do { - if (cache.IsValid() && Hits(cache.Offset, BufferedStorage.BlockSize)) + if (cache.IsValid() && Hits(cache._offset, _bufferedStorage._blockSize)) { _isValid = false; break; } cache = ref cache.Next; - } while (cache.Index != BufferedStorage.NextFetchCacheIndex); + } while (cache._index != _bufferedStorage._nextFetchCacheIndex); // Verify the end of the fetch list loops back to the start. - Assert.SdkAssert(BufferedStorage.NextFetchCache.PrevIndex != InvalidIndex); - Assert.SdkEqual(BufferedStorage.NextFetchCache.Prev.NextIndex, - BufferedStorage.NextFetchCacheIndex); + Assert.SdkAssert(_bufferedStorage.NextFetchCache._prevIndex != InvalidIndex); + Assert.SdkEqual(_bufferedStorage.NextFetchCache.Prev._nextIndex, + _bufferedStorage._nextFetchCacheIndex); // Link into the fetch list. - NextIndex = BufferedStorage.NextFetchCacheIndex; - PrevIndex = BufferedStorage.NextFetchCache.PrevIndex; - Next.PrevIndex = Index; - Prev.NextIndex = Index; + _nextIndex = _bufferedStorage._nextFetchCacheIndex; + _prevIndex = _bufferedStorage.NextFetchCache._prevIndex; + Next._prevIndex = _index; + Prev._nextIndex = _index; // Insert invalid caches at the start of the list so they'll // be used first when a fetch cache is needed. if (!IsValid()) - BufferedStorage.NextFetchCacheIndex = Index; + _bufferedStorage._nextFetchCacheIndex = _index; } // If we're not valid, clear our offset. if (!IsValid()) { - Offset = InvalidOffset; + _offset = InvalidOffset; _isDirty = false; } // Ensure our buffer state is coherent. // We can let go of our buffer if it's not dirty, allowing the buffer to be used elsewhere if needed. - if (!MemoryRange.IsNull && !IsDirty()) + if (!_memoryRange.IsNull && !IsDirty()) { // If we're valid, register the buffer with the buffer manager for possible later retrieval. // Otherwise the the data in the buffer isn't needed, so deallocate it. if (IsValid()) { - CacheHandle = BufferedStorage.BufferManager.RegisterCache(MemoryRange, + _cacheHandle = _bufferedStorage._bufferManager.RegisterCache(_memoryRange, new IBufferManager.BufferAttribute()); } else { - BufferedStorage.BufferManager.DeallocateBuffer(MemoryRange); + _bufferedStorage._bufferManager.DeallocateBuffer(_memoryRange); } - MemoryRange = default; + _memoryRange = default; } } } @@ -191,42 +200,42 @@ public class BufferedStorage : IStorage /// public void Unlink() { - Assert.SdkRequiresNotNull(BufferedStorage); - Assert.SdkRequiresGreaterEqual(ReferenceCount, 0); + Assert.SdkRequiresNotNull(_bufferedStorage); + Assert.SdkRequiresGreaterEqual(_referenceCount, 0); - ReferenceCount++; - if (ReferenceCount == 1) + _referenceCount++; + if (_referenceCount == 1) { // If we're the first to grab this Cache, the Cache should be in the BufferedStorage's fetch list. - Assert.SdkNotEqual(NextIndex, InvalidIndex); - Assert.SdkNotEqual(PrevIndex, InvalidIndex); - Assert.SdkEqual(Next.PrevIndex, Index); - Assert.SdkEqual(Prev.NextIndex, Index); + Assert.SdkNotEqual(_nextIndex, InvalidIndex); + Assert.SdkNotEqual(_prevIndex, InvalidIndex); + Assert.SdkEqual(Next._prevIndex, _index); + Assert.SdkEqual(Prev._nextIndex, _index); // Set the new fetch list head if this Cache is the current head - if (BufferedStorage.NextFetchCacheIndex == Index) + if (_bufferedStorage._nextFetchCacheIndex == _index) { - if (NextIndex != Index) + if (_nextIndex != _index) { - BufferedStorage.NextFetchCacheIndex = NextIndex; + _bufferedStorage._nextFetchCacheIndex = _nextIndex; } else { - BufferedStorage.NextFetchCacheIndex = InvalidIndex; + _bufferedStorage._nextFetchCacheIndex = InvalidIndex; } } - BufferedStorage.NextAcquireCacheIndex = Index; + _bufferedStorage._nextAcquireCacheIndex = _index; - Next.PrevIndex = PrevIndex; - Prev.NextIndex = NextIndex; - NextIndex = InvalidIndex; - PrevIndex = InvalidIndex; + Next._prevIndex = _prevIndex; + Prev._nextIndex = _nextIndex; + _nextIndex = InvalidIndex; + _prevIndex = InvalidIndex; } else { - Assert.SdkEqual(NextIndex, InvalidIndex); - Assert.SdkEqual(PrevIndex, InvalidIndex); + Assert.SdkEqual(_nextIndex, InvalidIndex); + Assert.SdkEqual(_prevIndex, InvalidIndex); } } @@ -237,22 +246,22 @@ public class BufferedStorage : IStorage /// /// The offset in the base to be read from. /// The buffer in which to place the read data. - public void Read(long offset, Span buffer) + public readonly void Read(long offset, Span buffer) { - Assert.SdkRequiresNotNull(BufferedStorage); - Assert.SdkRequiresEqual(NextIndex, InvalidIndex); - Assert.SdkRequiresEqual(PrevIndex, InvalidIndex); + Assert.SdkRequiresNotNull(_bufferedStorage); + Assert.SdkRequiresEqual(_nextIndex, InvalidIndex); + Assert.SdkRequiresEqual(_prevIndex, InvalidIndex); Assert.SdkRequires(IsValid()); Assert.SdkRequires(Hits(offset, 1)); - Assert.SdkRequires(!MemoryRange.IsNull); + Assert.SdkRequires(!_memoryRange.IsNull); - long readOffset = offset - Offset; - long readableOffsetMax = BufferedStorage.BlockSize - buffer.Length; + long readOffset = offset - _offset; + long readableOffsetMax = _bufferedStorage._blockSize - buffer.Length; Assert.SdkLessEqual(0, readOffset); Assert.SdkLessEqual(readOffset, readableOffsetMax); - Span cacheBuffer = MemoryRange.Span.Slice((int)readOffset, buffer.Length); + Span cacheBuffer = _memoryRange.Span.Slice((int)readOffset, buffer.Length); cacheBuffer.CopyTo(buffer); } @@ -265,20 +274,20 @@ public class BufferedStorage : IStorage /// The buffer containing the data to be written. public void Write(long offset, ReadOnlySpan buffer) { - Assert.SdkRequiresNotNull(BufferedStorage); - Assert.SdkRequiresEqual(NextIndex, InvalidIndex); - Assert.SdkRequiresEqual(PrevIndex, InvalidIndex); + Assert.SdkRequiresNotNull(_bufferedStorage); + Assert.SdkRequiresEqual(_nextIndex, InvalidIndex); + Assert.SdkRequiresEqual(_prevIndex, InvalidIndex); Assert.SdkRequires(IsValid()); Assert.SdkRequires(Hits(offset, 1)); - Assert.SdkRequires(!MemoryRange.IsNull); + Assert.SdkRequires(!_memoryRange.IsNull); - long writeOffset = offset - Offset; - long writableOffsetMax = BufferedStorage.BlockSize - buffer.Length; + long writeOffset = offset - _offset; + long writableOffsetMax = _bufferedStorage._blockSize - buffer.Length; Assert.SdkLessEqual(0, writeOffset); Assert.SdkLessEqual(writeOffset, writableOffsetMax); - Span cacheBuffer = MemoryRange.Span.Slice((int)writeOffset, buffer.Length); + Span cacheBuffer = _memoryRange.Span.Slice((int)writeOffset, buffer.Length); buffer.CopyTo(cacheBuffer); _isDirty = true; } @@ -290,25 +299,25 @@ public class BufferedStorage : IStorage /// The of the operation. public Result Flush() { - Assert.SdkRequiresNotNull(BufferedStorage); - Assert.SdkRequiresEqual(NextIndex, InvalidIndex); - Assert.SdkRequiresEqual(PrevIndex, InvalidIndex); + Assert.SdkRequiresNotNull(_bufferedStorage); + Assert.SdkRequiresEqual(_nextIndex, InvalidIndex); + Assert.SdkRequiresEqual(_prevIndex, InvalidIndex); Assert.SdkRequires(IsValid()); if (_isDirty) { - Assert.SdkRequires(!MemoryRange.IsNull); + Assert.SdkRequires(!_memoryRange.IsNull); - long baseSize = BufferedStorage.BaseStorageSize; - long blockSize = BufferedStorage.BlockSize; - long flushSize = Math.Min(blockSize, baseSize - Offset); + long baseSize = _bufferedStorage._baseStorageSize; + long blockSize = _bufferedStorage._blockSize; + long flushSize = Math.Min(blockSize, baseSize - _offset); - SubStorage baseStorage = BufferedStorage.BaseStorage; - Span cacheBuffer = MemoryRange.Span; + ref ValueSubStorage baseStorage = ref _bufferedStorage._baseStorage; + Span cacheBuffer = _memoryRange.Span; Assert.SdkEqual(flushSize, cacheBuffer.Length); - Result rc = baseStorage.Write(Offset, cacheBuffer); - if (rc.IsFailure()) return rc; + Result rc = baseStorage.Write(_offset, cacheBuffer); + if (rc.IsFailure()) return rc.Miss(); _isDirty = false; @@ -328,23 +337,23 @@ public class BufferedStorage : IStorage /// is prepared to fetch; if not. public (Result Result, bool IsPrepared) PrepareFetch() { - Assert.SdkRequiresNotNull(BufferedStorage); - Assert.SdkRequiresNotNull(BufferedStorage.BufferManager); - Assert.SdkRequiresEqual(NextIndex, InvalidIndex); - Assert.SdkRequiresEqual(PrevIndex, InvalidIndex); + Assert.SdkRequiresNotNull(_bufferedStorage); + Assert.SdkRequiresNotNull(_bufferedStorage._bufferManager); + Assert.SdkRequiresEqual(_nextIndex, InvalidIndex); + Assert.SdkRequiresEqual(_prevIndex, InvalidIndex); Assert.SdkRequires(IsValid()); - Assert.SdkRequires(Monitor.IsEntered(BufferedStorage.Locker)); + Assert.SdkRequires(_bufferedStorage._mutex.IsLockedByCurrentThread()); (Result Result, bool IsPrepared) result = (Result.Success, false); - if (ReferenceCount == 1) + if (_referenceCount == 1) { result.Result = Flush(); if (result.Result.IsSuccess()) { _isValid = false; - ReferenceCount = 0; + _referenceCount = 0; result.IsPrepared = true; } } @@ -358,16 +367,16 @@ public class BufferedStorage : IStorage /// public void UnprepareFetch() { - Assert.SdkRequiresNotNull(BufferedStorage); - Assert.SdkRequiresNotNull(BufferedStorage.BufferManager); - Assert.SdkRequiresEqual(NextIndex, InvalidIndex); - Assert.SdkRequiresEqual(PrevIndex, InvalidIndex); + Assert.SdkRequiresNotNull(_bufferedStorage); + Assert.SdkRequiresNotNull(_bufferedStorage._bufferManager); + Assert.SdkRequiresEqual(_nextIndex, InvalidIndex); + Assert.SdkRequiresEqual(_prevIndex, InvalidIndex); Assert.SdkRequires(!IsValid()); Assert.SdkRequires(!_isDirty); - Assert.SdkRequires(Monitor.IsEntered(BufferedStorage.Locker)); + Assert.SdkRequires(_bufferedStorage._mutex.IsLockedByCurrentThread()); _isValid = true; - ReferenceCount = 1; + _referenceCount = 1; } /// @@ -378,28 +387,28 @@ public class BufferedStorage : IStorage /// : A buffer could not be allocated. public Result Fetch(long offset) { - Assert.SdkRequiresNotNull(BufferedStorage); - Assert.SdkRequiresNotNull(BufferedStorage.BufferManager); - Assert.SdkRequiresEqual(NextIndex, InvalidIndex); - Assert.SdkRequiresEqual(PrevIndex, InvalidIndex); + Assert.SdkRequiresNotNull(_bufferedStorage); + Assert.SdkRequiresNotNull(_bufferedStorage._bufferManager); + Assert.SdkRequiresEqual(_nextIndex, InvalidIndex); + Assert.SdkRequiresEqual(_prevIndex, InvalidIndex); Assert.SdkRequires(!IsValid()); Assert.SdkRequires(!_isDirty); Result rc; // Make sure this Cache has an allocated buffer - if (MemoryRange.IsNull) + if (_memoryRange.IsNull) { rc = AllocateFetchBuffer(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); } CalcFetchParameter(out FetchParameter fetchParam, offset); - rc = BufferedStorage.BaseStorage.Read(fetchParam.Offset, fetchParam.Buffer); - if (rc.IsFailure()) return rc; + rc = _bufferedStorage._baseStorage.Read(fetchParam.Offset, fetchParam.Buffer); + if (rc.IsFailure()) return rc.Miss(); - Offset = fetchParam.Offset; + _offset = fetchParam.Offset; Assert.SdkAssert(Hits(offset, 1)); return Result.Success; @@ -416,19 +425,19 @@ public class BufferedStorage : IStorage /// : A buffer could not be allocated. public Result FetchFromBuffer(long offset, ReadOnlySpan buffer) { - Assert.SdkRequiresNotNull(BufferedStorage); - Assert.SdkRequiresNotNull(BufferedStorage.BufferManager); - Assert.SdkRequiresEqual(NextIndex, InvalidIndex); - Assert.SdkRequiresEqual(PrevIndex, InvalidIndex); + Assert.SdkRequiresNotNull(_bufferedStorage); + Assert.SdkRequiresNotNull(_bufferedStorage._bufferManager); + Assert.SdkRequiresEqual(_nextIndex, InvalidIndex); + Assert.SdkRequiresEqual(_prevIndex, InvalidIndex); Assert.SdkRequires(!IsValid()); Assert.SdkRequires(!_isDirty); - Assert.SdkRequiresAligned((ulong)offset, (int)BufferedStorage.BlockSize); + Assert.SdkRequiresAligned((ulong)offset, (int)_bufferedStorage._blockSize); // Make sure this Cache has an allocated buffer - if (MemoryRange.IsNull) + if (_memoryRange.IsNull) { Result rc = AllocateFetchBuffer(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); } CalcFetchParameter(out FetchParameter fetchParam, offset); @@ -436,7 +445,7 @@ public class BufferedStorage : IStorage Assert.SdkLessEqual(fetchParam.Buffer.Length, buffer.Length); buffer.Slice(0, fetchParam.Buffer.Length).CopyTo(fetchParam.Buffer); - Offset = fetchParam.Offset; + _offset = fetchParam.Offset; Assert.SdkAssert(Hits(offset, 1)); return Result.Success; @@ -449,15 +458,15 @@ public class BufferedStorage : IStorage /// if the buffer has been evicted from the cache. public bool TryAcquireCache() { - Assert.SdkRequiresNotNull(BufferedStorage); - Assert.SdkRequiresNotNull(BufferedStorage.BufferManager); + Assert.SdkRequiresNotNull(_bufferedStorage); + Assert.SdkRequiresNotNull(_bufferedStorage._bufferManager); Assert.SdkRequires(IsValid()); - if (!MemoryRange.IsNull) + if (!_memoryRange.IsNull) return true; - MemoryRange = BufferedStorage.BufferManager.AcquireCache(CacheHandle); - _isValid = !MemoryRange.IsNull; + _memoryRange = _bufferedStorage._bufferManager.AcquireCache(_cacheHandle); + _isValid = !_memoryRange.IsNull; return _isValid; } @@ -466,7 +475,7 @@ public class BufferedStorage : IStorage /// public void Invalidate() { - Assert.SdkRequiresNotNull(BufferedStorage); + Assert.SdkRequiresNotNull(_bufferedStorage); _isValid = false; } @@ -475,11 +484,11 @@ public class BufferedStorage : IStorage /// /// if this has a valid buffer /// or if anybody currently has a reference to this Cache. Otherwise, . - public bool IsValid() + public readonly bool IsValid() { - Assert.SdkRequiresNotNull(BufferedStorage); + Assert.SdkRequiresNotNull(_bufferedStorage); - return _isValid || ReferenceCount > 0; + return _isValid || _referenceCount > 0; } /// @@ -488,9 +497,9 @@ public class BufferedStorage : IStorage /// /// if this has unflushed data. /// Otherwise, . - public bool IsDirty() + public readonly bool IsDirty() { - Assert.SdkRequiresNotNull(BufferedStorage); + Assert.SdkRequiresNotNull(_bufferedStorage); return _isDirty; } @@ -502,12 +511,12 @@ public class BufferedStorage : IStorage /// The size of the range to check. /// if this 's range covers any of the input range. /// Otherwise, . - public bool Hits(long offset, long size) + public readonly bool Hits(long offset, long size) { - Assert.SdkRequiresNotNull(BufferedStorage); + Assert.SdkRequiresNotNull(_bufferedStorage); - long blockSize = BufferedStorage.BlockSize; - return (offset < Offset + blockSize) && (Offset < offset + size); + long blockSize = _bufferedStorage._blockSize; + return (offset < _offset + blockSize) && (_offset < offset + size); } /// @@ -518,15 +527,20 @@ public class BufferedStorage : IStorage /// : A buffer could not be allocated. private Result AllocateFetchBuffer() { - IBufferManager bufferManager = BufferedStorage.BufferManager; - Assert.SdkAssert(bufferManager.AcquireCache(CacheHandle).IsNull); + IBufferManager bufferManager = _bufferedStorage._bufferManager; + Assert.SdkAssert(bufferManager.AcquireCache(_cacheHandle).IsNull); - Result rc = BufferManagerUtility.AllocateBufferUsingBufferManagerContext(out Buffer bufferTemp, - bufferManager, (int)BufferedStorage.BlockSize, new IBufferManager.BufferAttribute(), + Result rc = BufferManagerUtility.AllocateBufferUsingBufferManagerContext(out _memoryRange, + bufferManager, (int)_bufferedStorage._blockSize, new IBufferManager.BufferAttribute(), static (in Buffer buffer) => !buffer.IsNull); // Clear the current MemoryRange if allocation failed. - MemoryRange = rc.IsSuccess() ? bufferTemp : default; + if (rc.IsFailure()) + { + _memoryRange = Buffer.Empty; + return rc.Log(); + } + return Result.Success; } @@ -537,14 +551,14 @@ public class BufferedStorage : IStorage /// When this function returns, contains /// the parameters that can be used to fetch the block. /// The offset to be fetched. - private void CalcFetchParameter(out FetchParameter fetchParam, long offset) + private readonly void CalcFetchParameter(out FetchParameter fetchParam, long offset) { - long blockSize = BufferedStorage.BlockSize; - long storageOffset = Alignment.AlignDownPow2(offset, (uint)BufferedStorage.BlockSize); - long baseSize = BufferedStorage.BaseStorageSize; + long blockSize = _bufferedStorage._blockSize; + long storageOffset = Alignment.AlignDownPow2(offset, (uint)_bufferedStorage._blockSize); + long baseSize = _bufferedStorage._baseStorageSize; long remainingSize = baseSize - storageOffset; long cacheSize = Math.Min(blockSize, remainingSize); - Span cacheBuffer = MemoryRange.Span.Slice(0, (int)cacheSize); + Span cacheBuffer = _memoryRange.Span.Slice(0, (int)cacheSize); Assert.SdkLessEqual(0, offset); Assert.SdkLess(offset, baseSize); @@ -570,18 +584,18 @@ public class BufferedStorage : IStorage public SharedCache(BufferedStorage bufferedStorage) { - Assert.SdkRequiresNotNull(bufferedStorage); Cache = default; StartCache = new Ref(ref bufferedStorage.NextAcquireCache); BufferedStorage = bufferedStorage; + + Assert.SdkRequiresNotNull(BufferedStorage); } public void Dispose() { - lock (BufferedStorage.Locker) - { - Release(); - } + using var lk = new ScopedLock(ref BufferedStorage._mutex); + + Release(); } /// @@ -601,46 +615,45 @@ public class BufferedStorage : IStorage // Make sure the Cache instance is in-range. Assert.SdkAssert(!Unsafe.IsAddressLessThan(ref start, - ref MemoryMarshal.GetArrayDataReference(BufferedStorage.Caches))); + ref MemoryMarshal.GetArrayDataReference(BufferedStorage._caches))); Assert.SdkAssert(!Unsafe.IsAddressGreaterThan(ref start, - ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(BufferedStorage.Caches), - BufferedStorage.CacheCount))); + ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(BufferedStorage._caches), + BufferedStorage._cacheCount))); - lock (BufferedStorage.Locker) + using var lk = new ScopedLock(ref BufferedStorage._mutex); + + Release(); + Assert.SdkAssert(Cache.IsNull); + + for (ref Cache cache = ref start; ; cache = ref Unsafe.Add(ref cache, 1)) { - Release(); - Assert.SdkAssert(Cache.IsNull); - - for (ref Cache cache = ref start; ; cache = ref Unsafe.Add(ref cache, 1)) + // Wrap to the front of the list if we've reached the end. + ref Cache end = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(BufferedStorage._caches), + BufferedStorage._cacheCount); + if (!Unsafe.IsAddressLessThan(ref cache, ref end)) { - // Wrap to the front of the list if we've reached the end. - ref Cache end = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(BufferedStorage.Caches), - BufferedStorage.CacheCount); - if (!Unsafe.IsAddressLessThan(ref cache, ref end)) - { - cache = ref MemoryMarshal.GetArrayDataReference(BufferedStorage.Caches); - } - - // Break if we've iterated all the Caches - if (!isFirst && Unsafe.AreSame(ref cache, ref StartCache.Value)) - { - break; - } - - if (cache.IsValid() && cache.Hits(offset, size) && cache.TryAcquireCache()) - { - cache.Unlink(); - Cache = new Ref(ref cache); - return true; - } - - isFirst = false; + cache = ref MemoryMarshal.GetArrayDataReference(BufferedStorage._caches); } - Cache = default; - return false; + // Break if we've iterated all the Caches + if (!isFirst && Unsafe.AreSame(ref cache, ref StartCache.Value)) + { + break; + } + + if (cache.IsValid() && cache.Hits(offset, size) && cache.TryAcquireCache()) + { + cache.Unlink(); + Cache = new Ref(ref cache); + return true; + } + + isFirst = false; } + + Cache = default; + return false; } /// @@ -654,15 +667,15 @@ public class BufferedStorage : IStorage Assert.SdkRequiresNotNull(BufferedStorage); ref Cache start = ref Cache.IsNull - ? ref MemoryMarshal.GetArrayDataReference(BufferedStorage.Caches) + ? ref MemoryMarshal.GetArrayDataReference(BufferedStorage._caches) : ref Unsafe.Add(ref Cache.Value, 1); - ref Cache end = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(BufferedStorage.Caches), - BufferedStorage.CacheCount); + ref Cache end = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(BufferedStorage._caches), + BufferedStorage._cacheCount); // Validate the range. Assert.SdkAssert(!Unsafe.IsAddressLessThan(ref start, - ref MemoryMarshal.GetArrayDataReference(BufferedStorage.Caches))); + ref MemoryMarshal.GetArrayDataReference(BufferedStorage._caches))); Assert.SdkAssert(!Unsafe.IsAddressGreaterThan(ref start, ref end)); @@ -671,8 +684,8 @@ public class BufferedStorage : IStorage // Find the next dirty Cache for (ref Cache cache = ref start; - Unsafe.IsAddressLessThan(ref cache, ref end); - cache = ref Unsafe.Add(ref cache, 1)) + Unsafe.IsAddressLessThan(ref cache, ref end); + cache = ref Unsafe.Add(ref cache, 1)) { if (cache.IsValid() && cache.IsDirty() && cache.TryAcquireCache()) { @@ -697,15 +710,15 @@ public class BufferedStorage : IStorage Assert.SdkRequiresNotNull(BufferedStorage); ref Cache start = ref Cache.IsNull - ? ref MemoryMarshal.GetArrayDataReference(BufferedStorage.Caches) + ? ref MemoryMarshal.GetArrayDataReference(BufferedStorage._caches) : ref Unsafe.Add(ref Cache.Value, 1); - ref Cache end = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(BufferedStorage.Caches), - BufferedStorage.CacheCount); + ref Cache end = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(BufferedStorage._caches), + BufferedStorage._cacheCount); // Validate the range. Assert.SdkAssert(!Unsafe.IsAddressLessThan(ref start, - ref MemoryMarshal.GetArrayDataReference(BufferedStorage.Caches))); + ref MemoryMarshal.GetArrayDataReference(BufferedStorage._caches))); Assert.SdkAssert(!Unsafe.IsAddressGreaterThan(ref start, ref end)); @@ -739,23 +752,22 @@ public class BufferedStorage : IStorage { Assert.SdkRequiresNotNull(BufferedStorage); - lock (BufferedStorage.Locker) + using var lk = new ScopedLock(ref BufferedStorage._mutex); + + Release(); + Assert.SdkAssert(Cache.IsNull); + + Cache = new Ref(ref BufferedStorage.NextFetchCache); + + if (!Cache.IsNull) { - Release(); - Assert.SdkAssert(Cache.IsNull); + if (Cache.Value.IsValid()) + Cache.Value.TryAcquireCache(); - Cache = new Ref(ref BufferedStorage.NextFetchCache); - - if (!Cache.IsNull) - { - if (Cache.Value.IsValid()) - Cache.Value.TryAcquireCache(); - - Cache.Value.Unlink(); - } - - return !Cache.IsNull; + Cache.Value.Unlink(); } + + return !Cache.IsNull; } /// @@ -827,11 +839,11 @@ public class BufferedStorage : IStorage { // Make sure the Cache instance is in-range. Assert.SdkAssert(!Unsafe.IsAddressLessThan(ref Cache.Value, - ref MemoryMarshal.GetArrayDataReference(BufferedStorage.Caches))); + ref MemoryMarshal.GetArrayDataReference(BufferedStorage._caches))); Assert.SdkAssert(!Unsafe.IsAddressGreaterThan(ref Cache.Value, - ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(BufferedStorage.Caches), - BufferedStorage.CacheCount))); + ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(BufferedStorage._caches), + BufferedStorage._cacheCount))); Cache.Value.Link(); Cache = default; @@ -845,15 +857,14 @@ public class BufferedStorage : IStorage /// private ref struct UniqueCache { - // ReSharper disable once MemberHidesStaticFromOuterClass - private Ref Cache { get; set; } - private BufferedStorage BufferedStorage { get; } + private Ref _cache; + private BufferedStorage _bufferedStorage; public UniqueCache(BufferedStorage bufferedStorage) { - Assert.SdkRequiresNotNull(bufferedStorage); - Cache = default; - BufferedStorage = bufferedStorage; + _cache = default; + _bufferedStorage = bufferedStorage; + Assert.SdkRequiresNotNull(_bufferedStorage); } /// @@ -861,12 +872,12 @@ public class BufferedStorage : IStorage /// public void Dispose() { - if (!Cache.IsNull) + if (!_cache.IsNull) { - lock (BufferedStorage.Locker) - { - Cache.Value.UnprepareFetch(); - } + using var lk = new ScopedLock(ref _bufferedStorage._mutex); + + _cache.Value.UnprepareFetch(); + } } @@ -876,21 +887,20 @@ public class BufferedStorage : IStorage /// /// The to gain exclusive access to. /// The of the operation, and if exclusive - /// access to the was gained; if not. + /// access to the was gained; if not. public (Result Result, bool wasUpgradeSuccessful) Upgrade(in SharedCache sharedCache) { - Assert.SdkRequires(BufferedStorage == sharedCache.BufferedStorage); + Assert.SdkRequires(_bufferedStorage == sharedCache.BufferedStorage); Assert.SdkRequires(!sharedCache.Cache.IsNull); - lock (BufferedStorage.Locker) - { - (Result Result, bool wasUpgradeSuccessful) result = sharedCache.Cache.Value.PrepareFetch(); + using var lk = new ScopedLock(ref _bufferedStorage._mutex); - if (result.Result.IsSuccess() && result.wasUpgradeSuccessful) - Cache = sharedCache.Cache; + (Result Result, bool wasUpgradeSuccessful) result = sharedCache.Cache.Value.PrepareFetch(); - return result; - } + if (result.Result.IsSuccess() && result.wasUpgradeSuccessful) + _cache = sharedCache.Cache; + + return result; } /// @@ -902,9 +912,9 @@ public class BufferedStorage : IStorage /// : A buffer could not be allocated. public Result Fetch(long offset) { - Assert.SdkRequires(!Cache.IsNull); + Assert.SdkRequires(!_cache.IsNull); - return Cache.Value.Fetch(offset); + return _cache.Value.Fetch(offset); } /// @@ -918,48 +928,42 @@ public class BufferedStorage : IStorage /// : A buffer could not be allocated. public Result FetchFromBuffer(long offset, ReadOnlySpan buffer) { - Assert.SdkRequires(!Cache.IsNull); + Assert.SdkRequires(!_cache.IsNull); - return Cache.Value.FetchFromBuffer(offset, buffer); + return _cache.Value.FetchFromBuffer(offset, buffer).Miss(); } } - private SubStorage BaseStorage { get; set; } - private IBufferManager BufferManager { get; set; } - private long BlockSize { get; set; } - + private ValueSubStorage _baseStorage; + private IBufferManager _bufferManager; + private long _blockSize; private long _baseStorageSize; - private long BaseStorageSize - { - get => _baseStorageSize; - set => _baseStorageSize = value; - } - - private Cache[] Caches { get; set; } - private int CacheCount { get; set; } - private int NextAcquireCacheIndex { get; set; } - private int NextFetchCacheIndex { get; set; } - private object Locker { get; } = new(); - private bool BulkReadEnabled { get; set; } + private Cache[] _caches; + private int _cacheCount; + private int _nextAcquireCacheIndex; + private int _nextFetchCacheIndex; + private SdkMutexType _mutex; + private bool _bulkReadEnabled; /// /// The at which new s will begin iterating. /// - private ref Cache NextAcquireCache => ref Caches[NextAcquireCacheIndex]; + private ref Cache NextAcquireCache => ref _caches[_nextAcquireCacheIndex]; /// /// A list of s that can be used for fetching /// new blocks of data from the base . /// - private ref Cache NextFetchCache => ref Caches[NextFetchCacheIndex]; + private ref Cache NextFetchCache => ref _caches[_nextFetchCacheIndex]; /// /// Creates an uninitialized . /// public BufferedStorage() { - NextAcquireCacheIndex = InvalidIndex; - NextFetchCacheIndex = InvalidIndex; + _nextAcquireCacheIndex = InvalidIndex; + _nextFetchCacheIndex = InvalidIndex; + _mutex = new SdkMutexType(); } /// @@ -968,6 +972,7 @@ public class BufferedStorage : IStorage public override void Dispose() { FinalizeObject(); + _baseStorage.Dispose(); base.Dispose(); } @@ -979,48 +984,53 @@ public class BufferedStorage : IStorage /// The base storage to use. /// The buffer manager used to allocate and cache memory. /// The size of each cached block. Must be a power of 2. - /// The maximum number of blocks that can be cached at one time. + /// The maximum number of blocks that can be cached at one time. /// - public Result Initialize(SubStorage baseStorage, IBufferManager bufferManager, int blockSize, int cacheCount) + public Result Initialize(in ValueSubStorage baseStorage, IBufferManager bufferManager, int blockSize, int bufferCount) { - Assert.SdkRequiresNotNull(baseStorage); Assert.SdkRequiresNotNull(bufferManager); Assert.SdkRequiresLess(0, blockSize); Assert.SdkRequires(BitUtil.IsPowerOfTwo(blockSize)); - Assert.SdkRequiresLess(0, cacheCount); + Assert.SdkRequiresLess(0, bufferCount); // Get the base storage size. Result rc = baseStorage.GetSize(out _baseStorageSize); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); // Set members. - BaseStorage = baseStorage; - BufferManager = bufferManager; - BlockSize = blockSize; - CacheCount = cacheCount; + _baseStorage.Set(in baseStorage); + _bufferManager = bufferManager; + _blockSize = blockSize; + _cacheCount = bufferCount; // Allocate the caches. - if (Caches != null) + if (_caches != null) { - for (int i = 0; i < Caches.Length; i++) + for (int i = 0; i < _caches.Length; i++) { - Caches[i].FinalizeObject(); + _caches[i].FinalizeObject(); } } - Caches = new Cache[cacheCount]; - if (Caches == null) + _caches = new Cache[bufferCount]; + if (_caches == null) { return ResultFs.AllocationMemoryFailedInBufferedStorageA.Log(); } - // Initialize the caches. - for (int i = 0; i < Caches.Length; i++) + for (int i = 0; i < _caches.Length; i++) { - Caches[i].Initialize(this, i); + _caches[i] = new Cache(i); } - NextAcquireCacheIndex = 0; + // Initialize the caches. + for (int i = 0; i < _caches.Length; i++) + { + _caches[i].Initialize(this); + } + + _nextFetchCacheIndex = 0; + _nextAcquireCacheIndex = 0; return Result.Success; } @@ -1029,17 +1039,21 @@ public class BufferedStorage : IStorage /// public void FinalizeObject() { - BaseStorage = null; - BaseStorageSize = 0; + using (var emptyStorage = new ValueSubStorage()) + { + _baseStorage.Set(in emptyStorage); + } - foreach (Cache cache in Caches) + _baseStorageSize = 0; + + foreach (Cache cache in _caches) { cache.Dispose(); } - Caches = null; - CacheCount = 0; - NextFetchCacheIndex = InvalidIndex; + _caches = null; + _cacheCount = 0; + _nextFetchCacheIndex = InvalidIndex; } /// @@ -1047,7 +1061,7 @@ public class BufferedStorage : IStorage /// /// if this is initialized. /// Otherwise, . - public bool IsInitialized() => Caches != null; + public bool IsInitialized() => _caches != null; protected override Result DoRead(long offset, Span destination) { @@ -1058,7 +1072,7 @@ public class BufferedStorage : IStorage return Result.Success; // Do the read. - return ReadCore(offset, destination); + return ReadCore(offset, destination).Miss(); } protected override Result DoWrite(long offset, ReadOnlySpan source) @@ -1070,14 +1084,14 @@ public class BufferedStorage : IStorage return Result.Success; // Do the read. - return WriteCore(offset, source); + return WriteCore(offset, source).Miss(); } protected override Result DoGetSize(out long size) { Assert.SdkRequires(IsInitialized()); - size = BaseStorageSize; + size = _baseStorageSize; return Result.Success; } @@ -1086,11 +1100,11 @@ public class BufferedStorage : IStorage Assert.SdkRequires(IsInitialized()); Result rc; - long prevSize = BaseStorageSize; + long prevSize = _baseStorageSize; if (prevSize < size) { // Prepare to expand. - if (!Alignment.IsAlignedPow2(prevSize, (uint)BlockSize)) + if (!Alignment.IsAlignedPow2(prevSize, (uint)_blockSize)) { using var cache = new SharedCache(this); long invalidateOffset = prevSize; @@ -1099,7 +1113,7 @@ public class BufferedStorage : IStorage if (cache.AcquireNextOverlappedCache(invalidateOffset, invalidateSize)) { rc = cache.Flush(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); cache.Invalidate(); } @@ -1113,14 +1127,14 @@ public class BufferedStorage : IStorage using var cache = new SharedCache(this); long invalidateOffset = prevSize; long invalidateSize = size - prevSize; - bool isFragment = Alignment.IsAlignedPow2(size, (uint)BlockSize); + bool isFragment = Alignment.IsAlignedPow2(size, (uint)_blockSize); while (cache.AcquireNextOverlappedCache(invalidateOffset, invalidateSize)) { if (isFragment && cache.Hits(invalidateOffset, 1)) { rc = cache.Flush(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); } cache.Invalidate(); @@ -1128,14 +1142,14 @@ public class BufferedStorage : IStorage } // Set the size. - rc = BaseStorage.SetSize(size); - if (rc.IsFailure()) return rc; + rc = _baseStorage.SetSize(size); + if (rc.IsFailure()) return rc.Miss(); // Get our new size. - rc = BaseStorage.GetSize(out long newSize); - if (rc.IsFailure()) return rc; + rc = _baseStorage.GetSize(out long newSize); + if (rc.IsFailure()) return rc.Miss(); - BaseStorageSize = newSize; + _baseStorageSize = newSize; return Result.Success; } @@ -1153,7 +1167,7 @@ public class BufferedStorage : IStorage cache.Invalidate(); } - return BaseStorage.OperateRange(outBuffer, operationId, offset, size, inBuffer); + return _baseStorage.OperateRange(outBuffer, operationId, offset, size, inBuffer); } protected override Result DoFlush() @@ -1164,12 +1178,12 @@ public class BufferedStorage : IStorage using var cache = new SharedCache(this); while (cache.AcquireNextDirtyCache()) { - Result flushResult = cache.Flush(); - if (flushResult.IsFailure()) return flushResult; + Result rc = cache.Flush(); + if (rc.IsFailure()) return rc.Miss(); } // Flush the base storage. - return BaseStorage.Flush(); + return _baseStorage.Flush().Miss(); } /// @@ -1188,9 +1202,9 @@ public class BufferedStorage : IStorage /// Gets the used by this . /// /// The buffer manager. - public IBufferManager GetBufferManager() => BufferManager; + public IBufferManager GetBufferManager() => _bufferManager; - public void EnableBulkRead() => BulkReadEnabled = true; + public void EnableBulkRead() => _bulkReadEnabled = true; /// /// Flushes the cache to the base if less than 1/8 of the @@ -1199,12 +1213,12 @@ public class BufferedStorage : IStorage /// The of the operation. private Result PrepareAllocation() { - uint flushThreshold = (uint)BufferManager.GetTotalSize() / 8; + uint flushThreshold = (uint)_bufferManager.GetTotalSize() / 8; - if (BufferManager.GetTotalAllocatableSize() < flushThreshold) + if (_bufferManager.GetTotalAllocatableSize() < flushThreshold) { Result rc = Flush(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); } return Result.Success; @@ -1217,9 +1231,9 @@ public class BufferedStorage : IStorage /// private Result ControlDirtiness() { - uint flushThreshold = (uint)BufferManager.GetTotalSize() / 4; + uint flushThreshold = (uint)_bufferManager.GetTotalSize() / 4; - if (BufferManager.GetTotalAllocatableSize() < flushThreshold) + if (_bufferManager.GetTotalAllocatableSize() < flushThreshold) { using var cache = new SharedCache(this); int dirtyCount = 0; @@ -1229,7 +1243,7 @@ public class BufferedStorage : IStorage if (++dirtyCount > 1) { Result rc = cache.Flush(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); cache.Invalidate(); } @@ -1248,11 +1262,11 @@ public class BufferedStorage : IStorage /// The of the operation. private Result ReadCore(long offset, Span destination) { - Assert.SdkRequiresNotNull(Caches); + Assert.SdkRequiresNotNull(_caches); Assert.SdkRequiresNotNull(destination); // Validate the offset. - long baseStorageSize = BaseStorageSize; + long baseStorageSize = _baseStorageSize; if (offset < 0 || offset > baseStorageSize) return ResultFs.InvalidOffset.Log(); @@ -1269,7 +1283,7 @@ public class BufferedStorage : IStorage // // This is imitated during bulk reads by tracking if there are any partial head or tail blocks that aren't // already in the cache. After the bulk read is complete these partial blocks will be added to the cache. - if (BulkReadEnabled) + if (_bulkReadEnabled) { // Read any blocks at the head of the range that are cached. bool headCacheNeeded = @@ -1283,16 +1297,16 @@ public class BufferedStorage : IStorage // Perform bulk reads. const long bulkReadSizeMax = 1024 * 1024 * 2; // 2 MB - if (remainingSize < bulkReadSizeMax) + if (remainingSize <= bulkReadSizeMax) { // Try to do a bulk read. Result rc = BulkRead(currentOffset, destination.Slice((int)bufferOffset, (int)remainingSize), headCacheNeeded, tailCacheNeeded); - // If the read fails due to insufficient pooled buffer size, + // If the read fails due to insufficient pooled buffer size // then we want to fall back to the normal read path. if (!ResultFs.AllocationPooledBufferNotEnoughSize.Includes(rc)) - return rc; + return rc.Miss(); } } @@ -1302,27 +1316,27 @@ public class BufferedStorage : IStorage // Determine how much to read this iteration. int currentSize; - // If the offset is in the middle of a block. Read the remaining part of that block. - if (!Alignment.IsAlignedPow2(currentOffset, (uint)BlockSize)) + // If the offset is in the middle of a block, read the remaining part of that block. + if (!Alignment.IsAlignedPow2(currentOffset, (uint)_blockSize)) { - long alignedSize = BlockSize - (currentOffset & (BlockSize - 1)); + long alignedSize = _blockSize - (currentOffset & (_blockSize - 1)); currentSize = (int)Math.Min(alignedSize, remainingSize); } // If we only have a partial block left to read, read that partial block. - else if (remainingSize < BlockSize) + else if (remainingSize < _blockSize) { currentSize = (int)remainingSize; } // We have at least one full block to read. Read all the remaining full blocks at once. else { - currentSize = (int)Alignment.AlignDownPow2(remainingSize, (uint)BlockSize); + currentSize = (int)Alignment.AlignDownPow2(remainingSize, (uint)_blockSize); } Span currentDestination = destination.Slice((int)bufferOffset, currentSize); // If reading a single block or less, read it using the cache - if (currentSize <= BlockSize) + if (currentSize <= _blockSize) { using var cache = new SharedCache(this); @@ -1331,13 +1345,13 @@ public class BufferedStorage : IStorage { // The block wasn't in the cache. Read the block from the base storage Result rc = PrepareAllocation(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); // Loop until we can get exclusive access to the cache block while (true) { if (!cache.AcquireFetchableCache()) - return ResultFs.OutOfResource.Log(); + return ResultFs.OutOfResource.Value; // Try to upgrade out SharedCache to a UniqueCache using var fetchCache = new UniqueCache(this); @@ -1349,14 +1363,14 @@ public class BufferedStorage : IStorage if (upgradeResult.wasUpgradeSuccessful) { rc = fetchCache.Fetch(currentOffset); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); break; } } rc = ControlDirtiness(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); } // Copy the data from the cache buffer to the destination buffer @@ -1372,15 +1386,15 @@ public class BufferedStorage : IStorage while (cache.AcquireNextOverlappedCache(currentOffset, currentSize)) { Result rc = cache.Flush(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); cache.Invalidate(); } } // Read directly from the base storage to the destination buffer - Result rcRead = BaseStorage.Read(currentOffset, currentDestination); - if (rcRead.IsFailure()) return rcRead; + Result rcRead = _baseStorage.Read(currentOffset, currentDestination); + if (rcRead.IsFailure()) return rcRead.Miss(); } remainingSize -= currentSize; @@ -1407,24 +1421,24 @@ public class BufferedStorage : IStorage /// Otherwise, . private bool ReadHeadCache(ref long offset, Span buffer, ref long size, ref long bufferOffset) { - bool isCacheNeeded = !Alignment.IsAlignedPow2(offset, (uint)BlockSize); + bool isCacheNeeded = !Alignment.IsAlignedPow2(offset, (uint)_blockSize); while (size > 0) { long currentSize; - if (!Alignment.IsAlignedPow2(offset, (uint)BlockSize)) + if (!Alignment.IsAlignedPow2(offset, (uint)_blockSize)) { - long alignedSize = Alignment.AlignUpPow2(offset, (uint)BlockSize) - offset; + long alignedSize = Alignment.AlignUpPow2(offset, (uint)_blockSize) - offset; currentSize = Math.Min(alignedSize, size); } - else if (size < BlockSize) + else if (size < _blockSize) { currentSize = size; } else { - currentSize = BlockSize; + currentSize = _blockSize; } using var cache = new SharedCache(this); @@ -1444,25 +1458,25 @@ public class BufferedStorage : IStorage private bool ReadTailCache(long offset, Span buffer, ref long size, long bufferOffset) { - bool isCacheNeeded = !Alignment.IsAlignedPow2(offset + size, (uint)BlockSize); + bool isCacheNeeded = !Alignment.IsAlignedPow2(offset + size, (uint)_blockSize); while (size > 0) { long currentOffsetEnd = offset + size; long currentSize; - if (!Alignment.IsAlignedPow2(currentOffsetEnd, (uint)BlockSize)) + if (!Alignment.IsAlignedPow2(currentOffsetEnd, (uint)_blockSize)) { - long alignedSize = currentOffsetEnd - Alignment.AlignDownPow2(currentOffsetEnd, (uint)BlockSize); + long alignedSize = currentOffsetEnd - Alignment.AlignDownPow2(currentOffsetEnd, (uint)_blockSize); currentSize = Math.Min(alignedSize, size); } - else if (size < BlockSize) + else if (size < _blockSize) { currentSize = size; } else { - currentSize = BlockSize; + currentSize = _blockSize; } long currentOffset = currentOffsetEnd - currentSize; @@ -1497,9 +1511,9 @@ public class BufferedStorage : IStorage Result rc; // Determine aligned extents. - long alignedOffset = Alignment.AlignDownPow2(offset, (uint)BlockSize); - long alignedOffsetEnd = Math.Min(Alignment.AlignUpPow2(offset + buffer.Length, (uint)BlockSize), - BaseStorageSize); + long alignedOffset = Alignment.AlignDownPow2(offset, (uint)_blockSize); + long alignedOffsetEnd = Math.Min(Alignment.AlignUpPow2(offset + buffer.Length, (uint)_blockSize), + _baseStorageSize); long alignedSize = alignedOffsetEnd - alignedOffset; // Allocate a work buffer if either the head or tail of the range isn't aligned. @@ -1526,15 +1540,15 @@ public class BufferedStorage : IStorage while (cache.AcquireNextOverlappedCache(alignedOffset, alignedSize)) { rc = cache.Flush(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); cache.Invalidate(); } } // Read from the base storage. - rc = BaseStorage.Read(alignedOffset, workBuffer.Slice(0, (int)alignedSize)); - if (rc.IsFailure()) return rc; + rc = _baseStorage.Read(alignedOffset, workBuffer.Slice(0, (int)alignedSize)); + if (rc.IsFailure()) return rc.Miss(); if (workBuffer != buffer) { workBuffer.Slice((int)(offset - alignedOffset), buffer.Length).CopyTo(buffer); @@ -1546,7 +1560,7 @@ public class BufferedStorage : IStorage if (isHeadCacheNeeded) { rc = PrepareAllocation(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); using var cache = new SharedCache(this); while (true) @@ -1557,12 +1571,12 @@ public class BufferedStorage : IStorage using var fetchCache = new UniqueCache(this); (Result Result, bool wasUpgradeSuccessful) upgradeResult = fetchCache.Upgrade(in cache); if (upgradeResult.Result.IsFailure()) - return upgradeResult.Result; + return upgradeResult.Result.Miss(); if (upgradeResult.wasUpgradeSuccessful) { rc = fetchCache.FetchFromBuffer(alignedOffset, workBuffer.Slice(0, (int)alignedSize)); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); break; } } @@ -1571,12 +1585,12 @@ public class BufferedStorage : IStorage } // Cache the tail block if needed. - if (isTailCacheNeeded && (!isHeadCacheNeeded || alignedSize > BlockSize)) + if (isTailCacheNeeded && (!isHeadCacheNeeded || alignedSize > _blockSize)) { if (!cached) { rc = PrepareAllocation(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); } using var cache = new SharedCache(this); @@ -1588,16 +1602,16 @@ public class BufferedStorage : IStorage using var fetchCache = new UniqueCache(this); (Result Result, bool wasUpgradeSuccessful) upgradeResult = fetchCache.Upgrade(in cache); if (upgradeResult.Result.IsFailure()) - return upgradeResult.Result; + return upgradeResult.Result.Miss(); if (upgradeResult.wasUpgradeSuccessful) { - long tailCacheOffset = Alignment.AlignDownPow2(offset + buffer.Length, (uint)BlockSize); + long tailCacheOffset = Alignment.AlignDownPow2(offset + buffer.Length, (uint)_blockSize); long tailCacheSize = alignedSize - tailCacheOffset + alignedOffset; rc = fetchCache.FetchFromBuffer(tailCacheOffset, workBuffer.Slice((int)(tailCacheOffset - alignedOffset), (int)tailCacheSize)); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); break; } } @@ -1606,7 +1620,7 @@ public class BufferedStorage : IStorage if (cached) { rc = ControlDirtiness(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); } return Result.Success; @@ -1614,11 +1628,11 @@ public class BufferedStorage : IStorage private Result WriteCore(long offset, ReadOnlySpan source) { - Assert.SdkRequiresNotNull(Caches); + Assert.SdkRequiresNotNull(_caches); Assert.SdkRequiresNotNull(source); // Validate the offset. - long baseStorageSize = BaseStorageSize; + long baseStorageSize = _baseStorageSize; if (offset < 0 || baseStorageSize < offset) return ResultFs.InvalidOffset.Log(); @@ -1635,34 +1649,34 @@ public class BufferedStorage : IStorage ReadOnlySpan currentSource = source.Slice(bufferOffset); int currentSize; - if (!Alignment.IsAlignedPow2(currentOffset, (uint)BlockSize)) + if (!Alignment.IsAlignedPow2(currentOffset, (uint)_blockSize)) { - int alignedSize = (int)(BlockSize - (currentOffset & (BlockSize - 1))); + int alignedSize = (int)(_blockSize - (currentOffset & (_blockSize - 1))); currentSize = Math.Min(alignedSize, remainingSize); } - else if (remainingSize < BlockSize) + else if (remainingSize < _blockSize) { currentSize = remainingSize; } else { - currentSize = Alignment.AlignDownPow2(remainingSize, (uint)BlockSize); + currentSize = Alignment.AlignDownPow2(remainingSize, (uint)_blockSize); } Result rc; - if (currentSize < BlockSize) + if (currentSize <= _blockSize) { using var cache = new SharedCache(this); if (!cache.AcquireNextOverlappedCache(currentOffset, currentSize)) { rc = PrepareAllocation(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); while (true) { if (!cache.AcquireFetchableCache()) - return ResultFs.OutOfResource.Log(); + return ResultFs.OutOfResource.Value; using var fetchCache = new UniqueCache(this); (Result Result, bool wasUpgradeSuccessful) upgradeResult = fetchCache.Upgrade(in cache); @@ -1672,7 +1686,7 @@ public class BufferedStorage : IStorage if (upgradeResult.wasUpgradeSuccessful) { rc = fetchCache.Fetch(currentOffset); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); break; } } @@ -1682,7 +1696,7 @@ public class BufferedStorage : IStorage BufferManagerUtility.EnableBlockingBufferManagerAllocation(); rc = ControlDirtiness(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); } else { @@ -1691,14 +1705,14 @@ public class BufferedStorage : IStorage while (cache.AcquireNextOverlappedCache(currentOffset, currentSize)) { rc = cache.Flush(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); cache.Invalidate(); } } - rc = BaseStorage.Write(currentOffset, currentSource.Slice(0, currentSize)); - if (rc.IsFailure()) return rc; + rc = _baseStorage.Write(currentOffset, currentSource.Slice(0, currentSize)); + if (rc.IsFailure()) return rc.Miss(); BufferManagerUtility.EnableBlockingBufferManagerAllocation(); } diff --git a/tests/LibHac.Tests/FsSystem/BufferedStorageTests.cs b/tests/LibHac.Tests/FsSystem/BufferedStorageTests.cs index 487b6a47..12822877 100644 --- a/tests/LibHac.Tests/FsSystem/BufferedStorageTests.cs +++ b/tests/LibHac.Tests/FsSystem/BufferedStorageTests.cs @@ -18,10 +18,10 @@ public class BufferedStorageTests Assert.Success(bufferManager.Initialize(5, buffer, 0x4000, workBuffer)); byte[] storageBuffer = new byte[0x80000]; - var baseStorage = new SubStorage(new MemoryStorage(storageBuffer), 0, storageBuffer.Length); + using var baseStorage = new ValueSubStorage(new MemoryStorage(storageBuffer), 0, storageBuffer.Length); var bufferedStorage = new BufferedStorage(); - Assert.Success(bufferedStorage.Initialize(baseStorage, bufferManager, 0x4000, 4)); + Assert.Success(bufferedStorage.Initialize(in baseStorage, bufferManager, 0x4000, 4)); byte[] writeBuffer = new byte[0x400]; byte[] readBuffer = new byte[0x400]; @@ -196,10 +196,10 @@ public class BufferedStorageTests byte[] bufferedStorageArray = new byte[config.StorageSize]; var memoryStorage = new MemoryStorage(memoryStorageArray); - var baseBufferedStorage = new SubStorage(new MemoryStorage(bufferedStorageArray), 0, bufferedStorageArray.Length); + using var baseBufferedStorage = new ValueSubStorage(new MemoryStorage(bufferedStorageArray), 0, bufferedStorageArray.Length); var bufferedStorage = new BufferedStorage(); - Assert.Success(bufferedStorage.Initialize(baseBufferedStorage, bufferManager, config.BlockSize, config.StorageCacheCount)); + Assert.Success(bufferedStorage.Initialize(in baseBufferedStorage, bufferManager, config.BlockSize, config.StorageCacheCount)); if (config.EnableBulkRead) { From c9a205684462609e43b34395d5ce680bc8376bfc Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 21 Dec 2021 12:34:42 -0700 Subject: [PATCH 08/34] Update BufferedStorage for 13.1.0 --- src/LibHac/FsSystem/BufferedStorage.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/LibHac/FsSystem/BufferedStorage.cs b/src/LibHac/FsSystem/BufferedStorage.cs index 2bc7adf3..07d2803d 100644 --- a/src/LibHac/FsSystem/BufferedStorage.cs +++ b/src/LibHac/FsSystem/BufferedStorage.cs @@ -16,6 +16,7 @@ namespace LibHac.FsSystem; /// /// An that provides buffered access to a base . /// +/// Based on FS 13.1.0 (nnSdk 13.4.0) public class BufferedStorage : IStorage { private const long InvalidOffset = long.MaxValue; @@ -673,6 +674,8 @@ public class BufferedStorage : IStorage ref Cache end = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(BufferedStorage._caches), BufferedStorage._cacheCount); + using var lk = new ScopedLock(ref BufferedStorage._mutex); + // Validate the range. Assert.SdkAssert(!Unsafe.IsAddressLessThan(ref start, ref MemoryMarshal.GetArrayDataReference(BufferedStorage._caches))); @@ -716,6 +719,8 @@ public class BufferedStorage : IStorage ref Cache end = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(BufferedStorage._caches), BufferedStorage._cacheCount); + using var lk = new ScopedLock(ref BufferedStorage._mutex); + // Validate the range. Assert.SdkAssert(!Unsafe.IsAddressLessThan(ref start, ref MemoryMarshal.GetArrayDataReference(BufferedStorage._caches))); @@ -1161,10 +1166,7 @@ public class BufferedStorage : IStorage // Invalidate caches if needed. if (operationId == OperationId.InvalidateCache) { - using var cache = new SharedCache(this); - - while (cache.AcquireNextOverlappedCache(offset, size)) - cache.Invalidate(); + InvalidateCaches(); } return _baseStorage.OperateRange(outBuffer, operationId, offset, size, inBuffer); @@ -1351,13 +1353,13 @@ public class BufferedStorage : IStorage while (true) { if (!cache.AcquireFetchableCache()) - return ResultFs.OutOfResource.Value; + return ResultFs.OutOfResource.Log(); // Try to upgrade out SharedCache to a UniqueCache using var fetchCache = new UniqueCache(this); (Result Result, bool wasUpgradeSuccessful) upgradeResult = fetchCache.Upgrade(in cache); if (upgradeResult.Result.IsFailure()) - return upgradeResult.Result; + return upgradeResult.Result.Miss(); // Fetch the data from the base storage into the cache buffer if successful if (upgradeResult.wasUpgradeSuccessful) @@ -1676,12 +1678,12 @@ public class BufferedStorage : IStorage while (true) { if (!cache.AcquireFetchableCache()) - return ResultFs.OutOfResource.Value; + return ResultFs.OutOfResource.Log(); using var fetchCache = new UniqueCache(this); (Result Result, bool wasUpgradeSuccessful) upgradeResult = fetchCache.Upgrade(in cache); if (upgradeResult.Result.IsFailure()) - return upgradeResult.Result; + return upgradeResult.Result.Miss(); if (upgradeResult.wasUpgradeSuccessful) { From feef0ff63f541f95c8f73ade2547b54236881ab5 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 21 Dec 2021 17:45:27 -0700 Subject: [PATCH 09/34] Fixup ConcatenationFileSystem - Experiment with using Catch/Handle/Rethrow for logging Results - Try adding a new Ret function for logging results - Misc tweaks --- .../FsSystem/ConcatenationFileSystem.cs | 401 ++++++------ src/LibHac/Result.cs | 7 +- src/LibHac/Tools/Fs/SwitchFs.cs | 10 +- src/hactoolnet/ProcessSwitchFs.cs | 8 +- .../IFileSystemTests.OpenDirectory.cs | 13 +- .../IFileSystemTests.OpenFile.cs | 13 +- .../FsSystem/ConcatenationFileSystemTests.cs | 594 +++++++++++++++++- 7 files changed, 831 insertions(+), 215 deletions(-) diff --git a/src/LibHac/FsSystem/ConcatenationFileSystem.cs b/src/LibHac/FsSystem/ConcatenationFileSystem.cs index 2212404c..2ded58e4 100644 --- a/src/LibHac/FsSystem/ConcatenationFileSystem.cs +++ b/src/LibHac/FsSystem/ConcatenationFileSystem.cs @@ -4,6 +4,7 @@ using System.Buffers.Text; using System.Collections.Generic; using System.Runtime.CompilerServices; using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Diag; using LibHac.Fs; using LibHac.Fs.Fsa; @@ -16,23 +17,24 @@ namespace LibHac.FsSystem; /// An that stores large files as smaller, separate sub-files. /// /// -/// This filesystem is mainly used to allow storing large files on filesystems that have low +/// This filesystem is mainly used to allow storing large files on filesystems that have low /// limits on file size such as FAT filesystems. The underlying base filesystem must have -/// support for the "Archive" file attribute found in FAT or NTFS filesystems.
-///
-/// A may contain both standard files or Concatenation files. +/// support for the "Archive" file attribute found in FAT or NTFS filesystems. +///
+/// A may contain both standard files or Concatenation files. /// If a directory has the archive attribute set, its contents will be concatenated and treated -/// as a single file. These sub-files must follow the naming scheme "00", "01", "02", ... -/// Each sub-file except the final one must have the size that was specified +/// as a single file. These internal files must follow the naming scheme "00", "01", "02", ... +/// Each internal file except the final one must have the internal file size that was specified /// at the creation of the . -///
Based on FS 12.1.0 (nnSdk 12.3.1) +///
+/// Based on FS 12.1.0 (nnSdk 12.3.1) ///
public class ConcatenationFileSystem : IFileSystem { private class ConcatenationFile : IFile { private OpenMode _mode; - private List _files; + private List _fileArray; private long _internalFileSize; private IFileSystem _baseFileSystem; private Path.Stored _path; @@ -40,7 +42,7 @@ public class ConcatenationFileSystem : IFileSystem public ConcatenationFile(OpenMode mode, ref List internalFiles, long internalFileSize, IFileSystem baseFileSystem) { _mode = mode; - _files = Shared.Move(ref internalFiles); + _fileArray = Shared.Move(ref internalFiles); _internalFileSize = internalFileSize; _baseFileSystem = baseFileSystem; _path = new Path.Stored(); @@ -50,19 +52,19 @@ public class ConcatenationFileSystem : IFileSystem { _path.Dispose(); - foreach (IFile file in _files) + foreach (IFile file in _fileArray) { file?.Dispose(); } - _files.Clear(); + _fileArray.Clear(); base.Dispose(); } public Result Initialize(in Path path) { - return _path.Initialize(in path); + return _path.Initialize(in path).Ret(); } private int GetInternalFileIndex(long offset) @@ -96,10 +98,11 @@ public class ConcatenationFileSystem : IFileSystem UnsafeHelpers.SkipParamInit(out bytesRead); long fileOffset = offset; - int bufferOffset = 0; Result rc = DryRead(out long remaining, offset, destination.Length, in option, _mode); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); + + int bufferOffset = 0; while (remaining > 0) { @@ -109,11 +112,11 @@ public class ConcatenationFileSystem : IFileSystem int bytesToRead = (int)Math.Min(remaining, internalFileRemaining); - Assert.SdkAssert(fileIndex < _files.Count); + Assert.SdkAssert(fileIndex < _fileArray.Count); - rc = _files[fileIndex].Read(out long internalFileBytesRead, internalFileOffset, + rc = _fileArray[fileIndex].Read(out long internalFileBytesRead, internalFileOffset, destination.Slice(bufferOffset, bytesToRead), in option); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); remaining -= internalFileBytesRead; bufferOffset += (int)internalFileBytesRead; @@ -130,12 +133,12 @@ public class ConcatenationFileSystem : IFileSystem protected override Result DoWrite(long offset, ReadOnlySpan source, in WriteOption option) { Result rc = DryWrite(out bool needsAppend, offset, source.Length, in option, _mode); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); if (source.Length > 0 && needsAppend) { rc = SetSize(offset + source.Length); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); } int remaining = source.Length; @@ -153,11 +156,11 @@ public class ConcatenationFileSystem : IFileSystem int bytesToWrite = (int)Math.Min(remaining, internalFileRemaining); - Assert.SdkAssert(fileIndex < _files.Count); + Assert.SdkAssert(fileIndex < _fileArray.Count); - rc = _files[fileIndex].Write(internalFileOffset, source.Slice(bufferOffset, bytesToWrite), + rc = _fileArray[fileIndex].Write(internalFileOffset, source.Slice(bufferOffset, bytesToWrite), in internalFileOption); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); remaining -= bytesToWrite; bufferOffset += bytesToWrite; @@ -167,7 +170,7 @@ public class ConcatenationFileSystem : IFileSystem if (option.HasFlushFlag()) { rc = Flush(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); } return Result.Success; @@ -178,10 +181,12 @@ public class ConcatenationFileSystem : IFileSystem if (!_mode.HasFlag(OpenMode.Write)) return Result.Success; - foreach (IFile file in _files) + for (int index = 0; index < _fileArray.Count; index++) { - Result rc = file.Flush(); - if (rc.IsFailure()) return rc; + Assert.SdkNotNull(_fileArray[index]); + + Result rc = _fileArray[index].Flush(); + if (rc.IsFailure()) return rc.Miss(); } return Result.Success; @@ -190,10 +195,10 @@ public class ConcatenationFileSystem : IFileSystem protected override Result DoSetSize(long size) { Result rc = DrySetSize(size, _mode); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); rc = GetSize(out long currentSize); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); if (currentSize == size) return Result.Success; @@ -202,51 +207,51 @@ public class ConcatenationFileSystem : IFileSystem using var internalFilePath = new Path(); rc = internalFilePath.Initialize(in _path); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); if (size > currentSize) { - rc = _files[currentTailIndex].SetSize(GetInternalFileSize(size, currentTailIndex)); - if (rc.IsFailure()) return rc; + rc = _fileArray[currentTailIndex].SetSize(GetInternalFileSize(size, currentTailIndex)); + if (rc.IsFailure()) return rc.Miss(); - for (int i = currentTailIndex + 1; i < newTailIndex; i++) + for (int i = currentTailIndex + 1; i <= newTailIndex; i++) { rc = AppendInternalFilePath(ref internalFilePath.Ref(), i); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); rc = _baseFileSystem.CreateFile(in internalFilePath, GetInternalFileSize(size, i), CreateFileOptions.None); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); using var newInternalFile = new UniqueRef(); rc = _baseFileSystem.OpenFile(ref newInternalFile.Ref(), in internalFilePath, _mode); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); - _files.Add(newInternalFile.Release()); + _fileArray.Add(newInternalFile.Release()); rc = internalFilePath.RemoveChild(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); } } else { - for (int i = currentTailIndex - 1; i > newTailIndex; i--) + for (int i = currentTailIndex; i > newTailIndex; i--) { - _files[i].Dispose(); - _files.RemoveAt(i); + _fileArray[i].Dispose(); + _fileArray.RemoveAt(i); rc = AppendInternalFilePath(ref internalFilePath.Ref(), i); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); rc = _baseFileSystem.DeleteFile(in internalFilePath); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); rc = internalFilePath.RemoveChild(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); } - rc = _files[newTailIndex].SetSize(GetInternalFileSize(size, newTailIndex)); - if (rc.IsFailure()) return rc; + rc = _fileArray[newTailIndex].SetSize(GetInternalFileSize(size, newTailIndex)); + if (rc.IsFailure()) return rc.Miss(); } return Result.Success; @@ -258,10 +263,10 @@ public class ConcatenationFileSystem : IFileSystem long totalSize = 0; - foreach (IFile file in _files) + foreach (IFile file in _fileArray) { Result rc = file.GetSize(out long internalFileSize); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); totalSize += internalFileSize; } @@ -273,44 +278,43 @@ public class ConcatenationFileSystem : IFileSystem protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { - if (operationId == OperationId.InvalidateCache) + switch (operationId) { - if (!_mode.HasFlag(OpenMode.Read)) - return ResultFs.ReadUnpermitted.Log(); + case OperationId.InvalidateCache: + { + if (!_mode.HasFlag(OpenMode.Read)) + return ResultFs.ReadUnpermitted.Log(); - var closure = new OperateRangeClosure(); - closure.OutBuffer = outBuffer; - closure.InBuffer = inBuffer; - closure.OperationId = operationId; + var closure = new OperateRangeClosure(); + closure.OutBuffer = outBuffer; + closure.InBuffer = inBuffer; + closure.OperationId = operationId; - Result rc = DoOperateRangeImpl(offset, size, InvalidateCacheImpl, ref closure); - if (rc.IsFailure()) return rc; + return DoOperateRangeImpl(offset, size, InvalidateCacheImpl, ref closure).Ret(); + } + case OperationId.QueryRange: + { + if (outBuffer.Length != Unsafe.SizeOf()) + return ResultFs.InvalidSize.Log(); + + var closure = new OperateRangeClosure(); + closure.InBuffer = inBuffer; + closure.OperationId = operationId; + closure.InfoMerged.Clear(); + + Result rc = DoOperateRangeImpl(offset, size, QueryRangeImpl, ref closure); + if (rc.IsFailure()) return rc.Miss(); + + SpanHelpers.AsByteSpan(ref closure.InfoMerged).CopyTo(outBuffer); + return Result.Success; + } + default: + return ResultFs.UnsupportedOperateRangeForConcatenationFile.Log(); } - else if (operationId == OperationId.QueryRange) - { - if (outBuffer.Length != Unsafe.SizeOf()) - return ResultFs.InvalidSize.Log(); - - var closure = new OperateRangeClosure(); - closure.InBuffer = inBuffer; - closure.OperationId = operationId; - closure.InfoMerged.Clear(); - - Result rc = DoOperateRangeImpl(offset, size, QueryRangeImpl, ref closure); - if (rc.IsFailure()) return rc; - - SpanHelpers.AsByteSpan(ref closure.InfoMerged).CopyTo(outBuffer); - } - else - { - return ResultFs.UnsupportedOperateRangeForConcatenationFile.Log(); - } - - return Result.Success; static Result InvalidateCacheImpl(IFile file, long offset, long size, ref OperateRangeClosure closure) { - return file.OperateRange(closure.OutBuffer, closure.OperationId, offset, size, closure.InBuffer); + return file.OperateRange(closure.OutBuffer, closure.OperationId, offset, size, closure.InBuffer).Ret(); } static Result QueryRangeImpl(IFile file, long offset, long size, ref OperateRangeClosure closure) @@ -319,7 +323,7 @@ public class ConcatenationFileSystem : IFileSystem Result rc = file.OperateRange(SpanHelpers.AsByteSpan(ref infoEntry), closure.OperationId, offset, size, closure.InBuffer); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); closure.InfoMerged.Merge(in infoEntry); return Result.Success; @@ -333,7 +337,7 @@ public class ConcatenationFileSystem : IFileSystem return ResultFs.OutOfRange.Log(); Result rc = GetSize(out long currentSize); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); if (offset > currentSize) return ResultFs.OutOfRange.Log(); @@ -350,10 +354,10 @@ public class ConcatenationFileSystem : IFileSystem long sizeToOperate = Math.Min(remaining, internalFileRemaining); - Assert.SdkAssert(fileIndex < _files.Count); + Assert.SdkAssert(fileIndex < _fileArray.Count); - rc = func(_files[fileIndex], internalFileOffset, sizeToOperate, ref closure); - if (rc.IsFailure()) return rc; + rc = func(_fileArray[fileIndex], internalFileOffset, sizeToOperate, ref closure); + if (rc.IsFailure()) return rc.Miss(); remaining -= sizeToOperate; currentOffset += sizeToOperate; @@ -386,6 +390,7 @@ public class ConcatenationFileSystem : IFileSystem { _mode = mode; _baseDirectory = new UniqueRef(ref baseDirectory); + _path = new Path.Stored(); _baseFileSystem = baseFileSystem; _concatenationFileSystem = concatFileSystem; } @@ -400,10 +405,7 @@ public class ConcatenationFileSystem : IFileSystem public Result Initialize(in Path path) { - Result rc = _path.Initialize(in path); - if (rc.IsFailure()) return rc; - - return Result.Success; + return _path.Initialize(in path).Ret(); } protected override Result DoRead(out long entriesRead, Span entryBuffer) @@ -416,7 +418,7 @@ public class ConcatenationFileSystem : IFileSystem while (readCountTotal < entryBuffer.Length) { Result rc = _baseDirectory.Get.Read(out long readCount, SpanHelpers.AsSpan(ref entry)); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); if (readCount == 0) break; @@ -432,13 +434,13 @@ public class ConcatenationFileSystem : IFileSystem { using var internalFilePath = new Path(); rc = internalFilePath.Initialize(in _path); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); rc = internalFilePath.AppendChild(entry.Name); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); rc = _concatenationFileSystem.GetFileSize(out entry.Size, in internalFilePath); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); } } @@ -461,14 +463,14 @@ public class ConcatenationFileSystem : IFileSystem Result rc = _baseFileSystem.OpenDirectory(ref directory.Ref(), in path, OpenDirectoryMode.All | OpenDirectoryMode.NoFileSize); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); long entryCountTotal = 0; while (true) { directory.Get.Read(out long readCount, SpanHelpers.AsSpan(ref entry)); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); if (readCount == 0) break; @@ -492,15 +494,17 @@ public class ConcatenationFileSystem : IFileSystem public static readonly long DefaultInternalFileSize = 0xFFFF0000; // Hard-coded value used by FS - private IAttributeFileSystem _baseFileSystem; - private long _InternalFileSize; + private UniqueRef _baseFileSystem; + private long _internalFileSize; /// /// Initializes a new with an internal file size of . /// /// The base for the /// new . - public ConcatenationFileSystem(IAttributeFileSystem baseFileSystem) : this(baseFileSystem, DefaultInternalFileSize) { } + public ConcatenationFileSystem(ref UniqueRef baseFileSystem) : this(ref baseFileSystem, + DefaultInternalFileSize) + { } /// /// Initializes a new . @@ -508,41 +512,41 @@ public class ConcatenationFileSystem : IFileSystem /// The base for the /// new . /// The size of each internal file. Once a file exceeds this size, a new internal file will be created - public ConcatenationFileSystem(IAttributeFileSystem baseFileSystem, long internalFileSize) + public ConcatenationFileSystem(ref UniqueRef baseFileSystem, long internalFileSize) { - _baseFileSystem = baseFileSystem; - _InternalFileSize = internalFileSize; + _baseFileSystem = new UniqueRef(ref baseFileSystem); + _internalFileSize = internalFileSize; } public override void Dispose() { - _baseFileSystem?.Dispose(); - _baseFileSystem = null; + _baseFileSystem.Destroy(); base.Dispose(); } private static ReadOnlySpan RootPath => new[] { (byte)'/' }; + /// + /// Appends the two-digit-padded to the given . + /// + /// The to be modified. + /// The index to append to the . + /// : The operation was successful. private static Result AppendInternalFilePath(ref Path path, int index) { - // Use an int as the buffer instead of a stackalloc byte[3] to workaround CS8350. - // Path.AppendChild will not save the span passed to it so this should be safe. - int bufferInt = 0; - Utf8Formatter.TryFormat(index, SpanHelpers.AsByteSpan(ref bufferInt), out _, new StandardFormat('d', 2)); + var buffer = new Array3(); + Utf8Formatter.TryFormat(index, buffer.Items, out _, new StandardFormat('d', 2)); - return path.AppendChild(SpanHelpers.AsByteSpan(ref bufferInt)); + return path.AppendChild(buffer.ItemsRo).Ret(); } private static Result GenerateInternalFilePath(ref Path outPath, int index, in Path basePath) { Result rc = outPath.Initialize(in basePath); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); - rc = AppendInternalFilePath(ref outPath, index); - if (rc.IsFailure()) return rc; - - return Result.Success; + return AppendInternalFilePath(ref outPath, index).Ret(); } private static Result GenerateParentPath(ref Path outParentPath, in Path path) @@ -551,12 +555,9 @@ public class ConcatenationFileSystem : IFileSystem return ResultFs.PathNotFound.Log(); Result rc = outParentPath.Initialize(in path); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); - rc = outParentPath.RemoveChild(); - if (rc.IsFailure()) return rc; - - return Result.Success; + return outParentPath.RemoveChild().Ret(); } private static bool IsConcatenationFileAttribute(NxFileAttributes attribute) @@ -566,7 +567,7 @@ public class ConcatenationFileSystem : IFileSystem private bool IsConcatenationFile(in Path path) { - Result rc = _baseFileSystem.GetFileAttributes(out NxFileAttributes attribute, in path); + Result rc = _baseFileSystem.Get.GetFileAttributes(out NxFileAttributes attribute, in path); if (rc.IsFailure()) return false; @@ -579,29 +580,32 @@ public class ConcatenationFileSystem : IFileSystem using var internalFilePath = new Path(); Result rc = internalFilePath.Initialize(in path); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); for (int i = 0; ; i++) { rc = AppendInternalFilePath(ref internalFilePath.Ref(), i); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); - rc = _baseFileSystem.GetEntryType(out _, in internalFilePath); + rc = _baseFileSystem.Get.GetEntryType(out _, in internalFilePath); if (rc.IsFailure()) { // We've passed the last internal file of the concatenation file // once the next internal file doesn't exist. if (ResultFs.PathNotFound.Includes(rc)) { + rc.Catch(); count = i; + rc.Handle(); + return Result.Success; } - return rc; + return rc.Miss(); } rc = internalFilePath.RemoveChild(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); } } @@ -613,67 +617,70 @@ public class ConcatenationFileSystem : IFileSystem return Result.Success; } - return _baseFileSystem.GetEntryType(out entryType, path); + return _baseFileSystem.Get.GetEntryType(out entryType, path).Ret(); } protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path) { - return _baseFileSystem.GetFreeSpaceSize(out freeSpace, path); + return _baseFileSystem.Get.GetFreeSpaceSize(out freeSpace, path).Ret(); } protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path) { - return _baseFileSystem.GetTotalSpaceSize(out totalSpace, path); + return _baseFileSystem.Get.GetTotalSpaceSize(out totalSpace, path).Ret(); } protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path) { - return _baseFileSystem.GetFileTimeStampRaw(out timeStamp, path); + return _baseFileSystem.Get.GetFileTimeStampRaw(out timeStamp, path).Ret(); } protected override Result DoFlush() { - return _baseFileSystem.Flush(); + return _baseFileSystem.Get.Flush().Ret(); } protected override Result DoOpenFile(ref UniqueRef outFile, in Path path, OpenMode mode) { if (!IsConcatenationFile(in path)) { - return _baseFileSystem.OpenFile(ref outFile, in path, mode); + return _baseFileSystem.Get.OpenFile(ref outFile, in path, mode).Ret(); } Result rc = GetInternalFileCount(out int fileCount, in path); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); + + if (fileCount <= 0) + return ResultFs.ConcatenationFsInvalidInternalFileCount.Log(); var internalFiles = new List(fileCount); using var filePath = new Path(); filePath.Initialize(in path); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); try { for (int i = 0; i < fileCount; i++) { rc = AppendInternalFilePath(ref filePath.Ref(), i); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); using var internalFile = new UniqueRef(); - rc = _baseFileSystem.OpenFile(ref internalFile.Ref(), in filePath, mode); - if (rc.IsFailure()) return rc; + rc = _baseFileSystem.Get.OpenFile(ref internalFile.Ref(), in filePath, mode); + if (rc.IsFailure()) return rc.Miss(); internalFiles.Add(internalFile.Release()); rc = filePath.RemoveChild(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); } using var concatFile = new UniqueRef( - new ConcatenationFile(mode, ref internalFiles, _InternalFileSize, _baseFileSystem)); + new ConcatenationFile(mode, ref internalFiles, _internalFileSize, _baseFileSystem.Get)); rc = concatFile.Get.Initialize(in path); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); outFile.Set(ref concatFile.Ref()); return Result.Success; @@ -699,13 +706,13 @@ public class ConcatenationFileSystem : IFileSystem } using var baseDirectory = new UniqueRef(); - Result rc = _baseFileSystem.OpenDirectory(ref baseDirectory.Ref(), path, OpenDirectoryMode.All); - if (rc.IsFailure()) return rc; + Result rc = _baseFileSystem.Get.OpenDirectory(ref baseDirectory.Ref(), path, OpenDirectoryMode.All); + if (rc.IsFailure()) return rc.Miss(); using var concatDirectory = new UniqueRef( - new ConcatenationDirectory(mode, ref baseDirectory.Ref(), this, _baseFileSystem)); + new ConcatenationDirectory(mode, ref baseDirectory.Ref(), this, _baseFileSystem.Get)); rc = concatDirectory.Get.Initialize(in path); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); outDirectory.Set(ref concatDirectory.Ref()); return Result.Success; @@ -718,12 +725,12 @@ public class ConcatenationFileSystem : IFileSystem // Create a normal file if the concatenation file flag isn't set if (!option.HasFlag(CreateFileOptions.CreateConcatenationFile)) { - return _baseFileSystem.CreateFile(path, size, newOption); + return _baseFileSystem.Get.CreateFile(path, size, newOption).Ret(); } using var parentPath = new Path(); Result rc = GenerateParentPath(ref parentPath.Ref(), in path); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); if (IsConcatenationFile(in parentPath)) { @@ -731,18 +738,18 @@ public class ConcatenationFileSystem : IFileSystem return ResultFs.PathNotFound.Log(); } - rc = _baseFileSystem.CreateDirectory(in path, NxFileAttributes.Archive); - if (rc.IsFailure()) return rc; + rc = _baseFileSystem.Get.CreateDirectory(in path, NxFileAttributes.Archive); + if (rc.IsFailure()) return rc.Miss(); // Handle the empty file case by manually creating a single empty internal file if (size == 0) { using var emptyFilePath = new Path(); rc = GenerateInternalFilePath(ref emptyFilePath.Ref(), 0, in path); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); - rc = _baseFileSystem.CreateFile(in emptyFilePath, 0, newOption); - if (rc.IsFailure()) return rc; + rc = _baseFileSystem.Get.CreateFile(in emptyFilePath, 0, newOption); + if (rc.IsFailure()) return rc.Miss(); return Result.Success; } @@ -750,38 +757,42 @@ public class ConcatenationFileSystem : IFileSystem long remaining = size; using var filePath = new Path(); filePath.Initialize(in path); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); for (int i = 0; remaining > 0; i++) { rc = AppendInternalFilePath(ref filePath.Ref(), i); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); - long fileSize = Math.Min(remaining, _InternalFileSize); - Result createInternalFileResult = _baseFileSystem.CreateFile(in filePath, fileSize, newOption); + long fileSize = Math.Min(remaining, _internalFileSize); + Result createInternalFileResult = _baseFileSystem.Get.CreateFile(in filePath, fileSize, newOption); // If something goes wrong when creating an internal file, delete all the // internal files we've created so far and delete the directory. // This will allow results like insufficient space results to be returned properly. if (createInternalFileResult.IsFailure()) { + createInternalFileResult.Catch(); + for (int index = i - 1; index >= 0; index--) { rc = GenerateInternalFilePath(ref filePath.Ref(), index, in path); - if (rc.IsFailure()) return rc; - - rc = _baseFileSystem.DeleteFile(in filePath); - if (rc.IsFailure()) + { + createInternalFileResult.Handle(); + return rc.Miss(); + } + + if (_baseFileSystem.Get.DeleteFile(in filePath).IsFailure()) break; } - _baseFileSystem.DeleteDirectoryRecursively(in path).IgnoreResult(); - return createInternalFileResult; + _baseFileSystem.Get.DeleteDirectoryRecursively(in path).IgnoreResult(); + return createInternalFileResult.Rethrow(); } rc = filePath.RemoveChild(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); remaining -= fileSize; } @@ -793,30 +804,30 @@ public class ConcatenationFileSystem : IFileSystem { if (!IsConcatenationFile(in path)) { - return _baseFileSystem.DeleteFile(in path); + return _baseFileSystem.Get.DeleteFile(in path).Ret(); } Result rc = GetInternalFileCount(out int count, path); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); using var filePath = new Path(); rc = filePath.Initialize(in path); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); for (int i = count - 1; i >= 0; i--) { rc = AppendInternalFilePath(ref filePath.Ref(), i); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); - rc = _baseFileSystem.DeleteFile(in filePath); - if (rc.IsFailure()) return rc; + rc = _baseFileSystem.Get.DeleteFile(in filePath); + if (rc.IsFailure()) return rc.Miss(); rc = filePath.RemoveChild(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); } - rc = _baseFileSystem.DeleteDirectoryRecursively(in path); - if (rc.IsFailure()) return rc; + rc = _baseFileSystem.Get.DeleteDirectoryRecursively(in path); + if (rc.IsFailure()) return rc.Miss(); return Result.Success; } @@ -826,15 +837,12 @@ public class ConcatenationFileSystem : IFileSystem // Check if the parent path is a concatenation file because we can't create a directory inside one. using var parentPath = new Path(); Result rc = GenerateParentPath(ref parentPath.Ref(), in path); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); if (IsConcatenationFile(in parentPath)) return ResultFs.PathNotFound.Log(); - rc = _baseFileSystem.CreateDirectory(in path); - if (rc.IsFailure()) return rc; - - return Result.Success; + return _baseFileSystem.Get.CreateDirectory(in path).Ret(); } protected override Result DoDeleteDirectory(in Path path) @@ -843,7 +851,7 @@ public class ConcatenationFileSystem : IFileSystem if (IsConcatenationFile(path)) return ResultFs.PathNotFound.Log(); - return _baseFileSystem.DeleteDirectory(path); + return _baseFileSystem.Get.DeleteDirectory(path).Ret(); } private Result CleanDirectoryRecursivelyImpl(in Path path) @@ -852,17 +860,17 @@ public class ConcatenationFileSystem : IFileSystem Result.Success; static Result OnExitDir(in Path path, in DirectoryEntry entry, ref FsIterationTaskClosure closure) => - closure.SourceFileSystem.DeleteDirectory(in path); + closure.SourceFileSystem.DeleteDirectory(in path).Ret(); static Result OnFile(in Path path, in DirectoryEntry entry, ref FsIterationTaskClosure closure) => - closure.SourceFileSystem.DeleteFile(in path); + closure.SourceFileSystem.DeleteFile(in path).Ret(); var closure = new FsIterationTaskClosure(); closure.SourceFileSystem = this; var directoryEntry = new DirectoryEntry(); return CleanupDirectoryRecursively(this, in path, ref directoryEntry, OnEnterDir, OnExitDir, OnFile, - ref closure); + ref closure).Ret(); } protected override Result DoDeleteDirectoryRecursively(in Path path) @@ -871,12 +879,9 @@ public class ConcatenationFileSystem : IFileSystem return ResultFs.PathNotFound.Log(); Result rc = CleanDirectoryRecursivelyImpl(in path); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); - rc = _baseFileSystem.DeleteDirectory(in path); - if (rc.IsFailure()) return rc; - - return Result.Success; + return _baseFileSystem.Get.DeleteDirectory(in path).Ret(); } protected override Result DoCleanDirectoryRecursively(in Path path) @@ -884,20 +889,15 @@ public class ConcatenationFileSystem : IFileSystem if (IsConcatenationFile(in path)) return ResultFs.PathNotFound.Log(); - Result rc = CleanDirectoryRecursivelyImpl(in path); - if (rc.IsFailure()) return rc; - - return Result.Success; + return CleanDirectoryRecursivelyImpl(in path).Ret(); } protected override Result DoRenameFile(in Path currentPath, in Path newPath) { if (IsConcatenationFile(in currentPath)) - { - return _baseFileSystem.RenameDirectory(in currentPath, in newPath); - } + return _baseFileSystem.Get.RenameDirectory(in currentPath, in newPath).Ret(); - return _baseFileSystem.RenameFile(in currentPath, in newPath); + return _baseFileSystem.Get.RenameFile(in currentPath, in newPath).Ret(); } protected override Result DoRenameDirectory(in Path currentPath, in Path newPath) @@ -905,7 +905,7 @@ public class ConcatenationFileSystem : IFileSystem if (IsConcatenationFile(in currentPath)) return ResultFs.PathNotFound.Log(); - return _baseFileSystem.RenameDirectory(in currentPath, in newPath); + return _baseFileSystem.Get.RenameDirectory(in currentPath, in newPath).Ret(); } public Result GetFileSize(out long size, in Path path) @@ -914,31 +914,34 @@ public class ConcatenationFileSystem : IFileSystem using var internalFilePath = new Path(); Result rc = internalFilePath.Initialize(in path); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); long sizeTotal = 0; for (int i = 0; ; i++) { rc = AppendInternalFilePath(ref internalFilePath.Ref(), i); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); - rc = _baseFileSystem.GetFileSize(out long internalFileSize, in internalFilePath); + rc = _baseFileSystem.Get.GetFileSize(out long internalFileSize, in internalFilePath); if (rc.IsFailure()) { // We've passed the last internal file of the concatenation file // once the next internal file doesn't exist. if (ResultFs.PathNotFound.Includes(rc)) { + rc.Catch(); size = sizeTotal; + rc.Handle(); + return Result.Success; } - return rc; + return rc.Miss(); } rc = internalFilePath.RemoveChild(); - if (rc.IsFailure()) return rc; + if (rc.IsFailure()) return rc.Miss(); sizeTotal += internalFileSize; } @@ -950,16 +953,16 @@ public class ConcatenationFileSystem : IFileSystem if (queryId != QueryId.SetConcatenationFileAttribute) return ResultFs.UnsupportedQueryEntryForConcatenationFileSystem.Log(); - return _baseFileSystem.SetFileAttributes(in path, NxFileAttributes.Archive); + return _baseFileSystem.Get.SetFileAttributes(in path, NxFileAttributes.Archive).Ret(); } protected override Result DoCommit() { - return _baseFileSystem.Commit(); + return _baseFileSystem.Get.Commit().Ret(); } protected override Result DoCommitProvisionally(long counter) { - return _baseFileSystem.CommitProvisionally(counter); + return _baseFileSystem.Get.CommitProvisionally(counter).Ret(); } -} +} \ No newline at end of file diff --git a/src/LibHac/Result.cs b/src/LibHac/Result.cs index fc12ec98..c096ed4b 100644 --- a/src/LibHac/Result.cs +++ b/src/LibHac/Result.cs @@ -132,6 +132,11 @@ public readonly struct Result : IEquatable return this; } + public Result Ret() + { + return this; + } + public bool TryGetResultName(out string name) { IResultNameResolver resolver = NameResolver; @@ -419,4 +424,4 @@ public readonly struct Result : IEquatable { public bool TryResolveName(Result result, out string name); } -} +} \ No newline at end of file diff --git a/src/LibHac/Tools/Fs/SwitchFs.cs b/src/LibHac/Tools/Fs/SwitchFs.cs index 3713029d..0eaf2d74 100644 --- a/src/LibHac/Tools/Fs/SwitchFs.cs +++ b/src/LibHac/Tools/Fs/SwitchFs.cs @@ -42,9 +42,9 @@ public class SwitchFs : IDisposable CreateApplications(); } - public static SwitchFs OpenSdCard(KeySet keySet, IAttributeFileSystem fileSystem) + public static SwitchFs OpenSdCard(KeySet keySet, ref UniqueRef fileSystem) { - var concatFs = new ConcatenationFileSystem(fileSystem); + var concatFs = new ConcatenationFileSystem(ref fileSystem); using var contentDirPath = new LibHac.Fs.Path(); PathFunctions.SetUpFixedPath(ref contentDirPath.Ref(), "/Nintendo/Contents".ToU8String()).ThrowIfFailure(); @@ -56,7 +56,7 @@ public class SwitchFs : IDisposable contentDirFs.Initialize(in contentDirPath).ThrowIfFailure(); AesXtsFileSystem encSaveFs = null; - if (fileSystem.DirectoryExists("/Nintendo/save")) + if (concatFs.DirectoryExists("/Nintendo/save")) { var saveDirFs = new SubdirectoryFileSystem(concatFs); saveDirFs.Initialize(in saveDirPath).ThrowIfFailure(); @@ -69,9 +69,9 @@ public class SwitchFs : IDisposable return new SwitchFs(keySet, encContentFs, encSaveFs); } - public static SwitchFs OpenNandPartition(KeySet keySet, IAttributeFileSystem fileSystem) + public static SwitchFs OpenNandPartition(KeySet keySet, ref UniqueRef fileSystem) { - var concatFs = new ConcatenationFileSystem(fileSystem); + var concatFs = new ConcatenationFileSystem(ref fileSystem); SubdirectoryFileSystem saveDirFs = null; SubdirectoryFileSystem contentDirFs; diff --git a/src/hactoolnet/ProcessSwitchFs.cs b/src/hactoolnet/ProcessSwitchFs.cs index 2eb5a186..5ffe7a51 100644 --- a/src/hactoolnet/ProcessSwitchFs.cs +++ b/src/hactoolnet/ProcessSwitchFs.cs @@ -21,26 +21,26 @@ internal static class ProcessSwitchFs public static void Process(Context ctx) { SwitchFs switchFs; - var baseFs = new LocalFileSystem(ctx.Options.InFile); + using var baseFs = new UniqueRef(new LocalFileSystem(ctx.Options.InFile)); if (Directory.Exists(Path.Combine(ctx.Options.InFile, "Nintendo", "Contents", "registered"))) { ctx.Logger.LogMessage("Treating path as SD card storage"); - switchFs = SwitchFs.OpenSdCard(ctx.KeySet, baseFs); + switchFs = SwitchFs.OpenSdCard(ctx.KeySet, ref baseFs.Ref()); CheckForNcaFolders(ctx, switchFs); } else if (Directory.Exists(Path.Combine(ctx.Options.InFile, "Contents", "registered"))) { ctx.Logger.LogMessage("Treating path as NAND storage"); - switchFs = SwitchFs.OpenNandPartition(ctx.KeySet, baseFs); + switchFs = SwitchFs.OpenNandPartition(ctx.KeySet, ref baseFs.Ref()); CheckForNcaFolders(ctx, switchFs); } else { ctx.Logger.LogMessage("Treating path as a directory of loose NCAs"); - switchFs = SwitchFs.OpenNcaDirectory(ctx.KeySet, baseFs); + switchFs = SwitchFs.OpenNcaDirectory(ctx.KeySet, baseFs.Get); } if (ctx.Options.ListNcas) diff --git a/tests/LibHac.Tests/Fs/IFileSystemTestBase/IFileSystemTests.OpenDirectory.cs b/tests/LibHac.Tests/Fs/IFileSystemTestBase/IFileSystemTests.OpenDirectory.cs index 8d2aff1c..e6147867 100644 --- a/tests/LibHac.Tests/Fs/IFileSystemTestBase/IFileSystemTests.OpenDirectory.cs +++ b/tests/LibHac.Tests/Fs/IFileSystemTestBase/IFileSystemTests.OpenDirectory.cs @@ -19,4 +19,15 @@ public abstract partial class IFileSystemTests Assert.Result(ResultFs.PathNotFound, rc); } -} + + [Fact] + public void OpenDirectory_PathDoesNotExist_ReturnsPathNotFound() + { + IFileSystem fs = CreateFileSystem(); + + using var directory = new UniqueRef(); + Result rc = fs.OpenDirectory(ref directory.Ref(), "/dir", OpenDirectoryMode.All); + + Assert.Result(ResultFs.PathNotFound, rc); + } +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Fs/IFileSystemTestBase/IFileSystemTests.OpenFile.cs b/tests/LibHac.Tests/Fs/IFileSystemTestBase/IFileSystemTests.OpenFile.cs index 35f5f2c5..a50e1d71 100644 --- a/tests/LibHac.Tests/Fs/IFileSystemTestBase/IFileSystemTests.OpenFile.cs +++ b/tests/LibHac.Tests/Fs/IFileSystemTestBase/IFileSystemTests.OpenFile.cs @@ -19,4 +19,15 @@ public abstract partial class IFileSystemTests Assert.Result(ResultFs.PathNotFound, rc); } -} + + [Fact] + public void OpenFile_PathDoesNotExist_ReturnsPathNotFound() + { + IFileSystem fs = CreateFileSystem(); + + using var file = new UniqueRef(); + Result rc = fs.OpenFile(ref file.Ref(), "/file", OpenMode.All); + + Assert.Result(ResultFs.PathNotFound, rc); + } +} \ No newline at end of file diff --git a/tests/LibHac.Tests/FsSystem/ConcatenationFileSystemTests.cs b/tests/LibHac.Tests/FsSystem/ConcatenationFileSystemTests.cs index 0222ef6d..9a4ce758 100644 --- a/tests/LibHac.Tests/FsSystem/ConcatenationFileSystemTests.cs +++ b/tests/LibHac.Tests/FsSystem/ConcatenationFileSystemTests.cs @@ -1,6 +1,592 @@ -namespace LibHac.Tests.FsSystem; +using System; +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.FsSystem; +using LibHac.Tests.Fs; +using LibHac.Tests.Fs.IFileSystemTestBase; +using LibHac.Tools.Fs; +using Xunit; -public class ConcatenationFileSystemTests +namespace LibHac.Tests.FsSystem; + +public class ConcatenationFileSystemTests : IFileSystemTests { - //asdf -} + private const int InternalFileSize = 0x10000; + protected override IFileSystem CreateFileSystem() + { + return CreateFileSystemInternal().concatFs; + } + + private (InMemoryFileSystem baseFs, ConcatenationFileSystem concatFs) CreateFileSystemInternal() + { + var baseFs = new InMemoryFileSystem(); + + using var uniqueBaseFs = new UniqueRef(baseFs); + var concatFs = new ConcatenationFileSystem(ref uniqueBaseFs.Ref(), InternalFileSize); + + return (baseFs, concatFs); + } + + [Fact] + public void OpenFile_OpenInternalFile_OpensSuccessfully() + { + IFileSystem fs = CreateFileSystem(); + + Assert.Success(fs.CreateFile("/file", InternalFileSize * 3, CreateFileOptions.CreateConcatenationFile)); + + using var file = new UniqueRef(); + Assert.Success(fs.OpenFile(ref file.Ref(), "/file/01", OpenMode.All)); + } + + [Fact] + public void OpenFile_ConcatFileWithNoInternalFiles_ReturnsConcatenationFsInvalidInternalFileCount() + { + (IAttributeFileSystem baseFs, IFileSystem concatFs) = CreateFileSystemInternal(); + + Assert.Success(concatFs.CreateFile("/file", InternalFileSize * 3, CreateFileOptions.CreateConcatenationFile)); + Assert.Success(baseFs.DeleteFile("/file/00")); + + using var file = new UniqueRef(); + Assert.Result(ResultFs.ConcatenationFsInvalidInternalFileCount, concatFs.OpenFile(ref file.Ref(), "/file", OpenMode.All)); + } + + [Fact] + public void OpenDirectory_OpenConcatFile_ReturnsPathNotFound() + { + IFileSystem fs = CreateFileSystem(); + + Assert.Success(fs.CreateFile("/file", InternalFileSize * 3, CreateFileOptions.CreateConcatenationFile)); + + using var dir = new UniqueRef(); + Assert.Result(ResultFs.PathNotFound, fs.OpenDirectory(ref dir.Ref(), "/file", OpenDirectoryMode.All)); + } + + [Fact] + public void CreateFile_ConcatenationFile_GetEntryTypeReturnsFile() + { + IFileSystem concatFs = CreateFileSystem(); + + Assert.Success(concatFs.CreateFile("/file", 0, CreateFileOptions.CreateConcatenationFile)); + + Assert.Success(concatFs.GetEntryType(out DirectoryEntryType type, "/file")); + Assert.Equal(DirectoryEntryType.File, type); + } + + [Fact] + public void CreateFile_EmptyConcatenationFile_BaseDirHasCorrectStructure() + { + (IAttributeFileSystem baseFs, IFileSystem concatFs) = CreateFileSystemInternal(); + + Assert.Success(concatFs.CreateFile("/file", 0, CreateFileOptions.CreateConcatenationFile)); + + // Ensure the directory exists with the archive bit set + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType dirType, "/file")); + Assert.Success(baseFs.GetFileAttributes(out NxFileAttributes dirAttributes, "/file")); + Assert.Equal(DirectoryEntryType.Directory, dirType); + Assert.Equal(NxFileAttributes.Directory | NxFileAttributes.Archive, dirAttributes); + + // Ensure the internal files exist + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType internalFile00Type, "/file/00")); + Assert.Equal(DirectoryEntryType.File, internalFile00Type); + + // Ensure no additional internal files exist + Assert.Result(ResultFs.PathNotFound, baseFs.GetEntryType(out _, "/file/01")); + + // Ensure the internal file sizes are correct + Assert.Success(baseFs.GetFileSize(out long internalFile00Size, "/file/00")); + Assert.Equal(0, internalFile00Size); + } + + [Fact] + public void CreateFile_SizeIsMultipleOfInternalFile_BaseDirHasCorrectStructure() + { + (IAttributeFileSystem baseFs, IFileSystem concatFs) = CreateFileSystemInternal(); + + Assert.Success(concatFs.CreateFile("/file", InternalFileSize * 2, CreateFileOptions.CreateConcatenationFile)); + + // Ensure the directory exists with the archive bit set + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType dirType, "/file")); + Assert.Success(baseFs.GetFileAttributes(out NxFileAttributes dirAttributes, "/file")); + Assert.Equal(DirectoryEntryType.Directory, dirType); + Assert.Equal(NxFileAttributes.Directory | NxFileAttributes.Archive, dirAttributes); + + // Ensure no additional internal files exist + Assert.Result(ResultFs.PathNotFound, baseFs.GetEntryType(out _, "/file/02")); + + // Ensure the internal file sizes are correct + Assert.Success(baseFs.GetFileSize(out long internalFile00Size, "/file/00")); + Assert.Success(baseFs.GetFileSize(out long internalFile01Size, "/file/01")); + Assert.Equal(InternalFileSize, internalFile00Size); + Assert.Equal(InternalFileSize, internalFile01Size); + } + + [Fact] + public void CreateFile_NormalFileInsideConcatFile_CreatesSuccessfully() + { + IFileSystem fs = CreateFileSystem(); + + Assert.Success(fs.CreateFile("/file", 0, CreateFileOptions.CreateConcatenationFile)); + Assert.Success(fs.CreateFile("/file/file", 0)); + + Assert.Success(fs.GetEntryType(out DirectoryEntryType type, "/file/file")); + Assert.Equal(DirectoryEntryType.File, type); + } + + [Fact] + public void CreateFile_ConcatFileInsideConcatFile_ReturnsPathNotFound() + { + IFileSystem fs = CreateFileSystem(); + + Assert.Success(fs.CreateFile("/file", 0, CreateFileOptions.CreateConcatenationFile)); + + Assert.Result(ResultFs.PathNotFound, fs.CreateFile("/file/file", 0, CreateFileOptions.CreateConcatenationFile)); + } + + [Fact] + public void DeleteFile_DeleteConcatFile_DeletesSuccessfully() + { + (IAttributeFileSystem baseFs, IFileSystem concatFs) = CreateFileSystemInternal(); + + Assert.Success(concatFs.CreateFile("/file", 0, CreateFileOptions.CreateConcatenationFile)); + Assert.Success(concatFs.DeleteFile("/file")); + + Assert.Result(ResultFs.PathNotFound, baseFs.GetEntryType(out _, "/file")); + } + + [Fact] + public void CreateDirectory_InsideConcatFile_ReturnsPathNotFound() + { + IFileSystem fs = CreateFileSystem(); + + Assert.Success(fs.CreateFile("/file", 0, CreateFileOptions.CreateConcatenationFile)); + + Assert.Result(ResultFs.PathNotFound, fs.CreateDirectory("/file/dir")); + } + + [Fact] + public void DeleteDirectory_DeleteConcatFile_ReturnsPathNotFound() + { + IFileSystem fs = CreateFileSystem(); + + Assert.Success(fs.CreateFile("/file", 0, CreateFileOptions.CreateConcatenationFile)); + + Assert.Result(ResultFs.PathNotFound, fs.DeleteDirectory("/file")); + } + + [Fact] + public void CleanDirectoryRecursively_CleanConcatFile_ReturnsPathNotFound() + { + IFileSystem fs = CreateFileSystem(); + + Assert.Success(fs.CreateFile("/file", 0, CreateFileOptions.CreateConcatenationFile)); + + Assert.Result(ResultFs.PathNotFound, fs.CleanDirectoryRecursively("/file")); + } + + [Fact] + public void RenameFile_RenameConcatFile_RenamesSuccessfully() + { + (IAttributeFileSystem baseFs, IFileSystem concatFs) = CreateFileSystemInternal(); + + Assert.Success(concatFs.CreateFile("/file", 0, CreateFileOptions.CreateConcatenationFile)); + Assert.Success(concatFs.RenameFile("/file", "/file2")); + + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType type, "/file2")); + Assert.Equal(DirectoryEntryType.Directory, type); + } + + [Fact] + public void RenameDirectory_RenameConcatFile_ReturnsPathNotFound() + { + IFileSystem fs = CreateFileSystem(); + + Assert.Success(fs.CreateFile("/file", 0, CreateFileOptions.CreateConcatenationFile)); + + Assert.Result(ResultFs.PathNotFound, fs.RenameDirectory("/file", "/file2")); + } + + [Fact] + public void Write_ConcatFileWithMultipleInternalFiles_CanReadBackWrittenData() + { + const long fileSize = InternalFileSize * 5 - 5; + + byte[] data = new byte[fileSize]; + new Random(1234).NextBytes(data); + + IFileSystem fs = CreateFileSystem(); + + fs.CreateFile("/file", fileSize, CreateFileOptions.CreateConcatenationFile); + + using var file = new UniqueRef(); + fs.OpenFile(ref file.Ref(), "/file", OpenMode.Write); + file.Get.Write(0, data, WriteOption.None); + file.Reset(); + + byte[] readData = new byte[data.Length]; + fs.OpenFile(ref file.Ref(), "/file", OpenMode.Read); + + Assert.Success(file.Get.Read(out long bytesRead, 0, readData, ReadOption.None)); + Assert.Equal(data.Length, bytesRead); + + Assert.Equal(data, readData); + } + + [Fact] + public void SetSize_ResizeToHigherMultipleOfInternalFile_BaseDirHasCorrectStructure() + { + const long originalSize = InternalFileSize; + const long newSize = InternalFileSize * 2; + + (IAttributeFileSystem baseFs, IFileSystem concatFs) = CreateFileSystemInternal(); + + using var file = new UniqueRef(); + + // Create the file and then resize it + Assert.Success(concatFs.CreateFile("/file", originalSize, CreateFileOptions.CreateConcatenationFile)); + Assert.Success(concatFs.OpenFile(ref file.Ref(), "/file", OpenMode.All)); + Assert.Success(file.Get.SetSize(newSize)); + + Assert.Success(file.Get.GetSize(out long concatFileSize)); + Assert.Equal(newSize, concatFileSize); + + // Ensure the directory exists with the archive bit set + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType dirType, "/file")); + Assert.Success(baseFs.GetFileAttributes(out NxFileAttributes dirAttributes, "/file")); + Assert.Equal(DirectoryEntryType.Directory, dirType); + Assert.Equal(NxFileAttributes.Directory | NxFileAttributes.Archive, dirAttributes); + + // Ensure the internal files exist + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType internalFile00Type, "/file/00")); + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType internalFile01Type, "/file/01")); + Assert.Equal(DirectoryEntryType.File, internalFile00Type); + Assert.Equal(DirectoryEntryType.File, internalFile01Type); + + // Ensure no additional internal files exist + Assert.Result(ResultFs.PathNotFound, baseFs.GetEntryType(out _, "/file/02")); + + // Ensure the internal file sizes are correct + Assert.Success(baseFs.GetFileSize(out long internalFile00Size, "/file/00")); + Assert.Success(baseFs.GetFileSize(out long internalFile01Size, "/file/01")); + Assert.Equal(InternalFileSize, internalFile00Size); + Assert.Equal(InternalFileSize, internalFile01Size); + } + + [Fact] + public void SetSize_ResizeToLowerMultipleOfInternalFile_BaseDirHasCorrectStructure() + { + const long originalSize = InternalFileSize * 5; + const long newSize = InternalFileSize * 2; + + (IAttributeFileSystem baseFs, IFileSystem concatFs) = CreateFileSystemInternal(); + + using var file = new UniqueRef(); + + // Create the file and then resize it + Assert.Success(concatFs.CreateFile("/file", originalSize, CreateFileOptions.CreateConcatenationFile)); + Assert.Success(concatFs.OpenFile(ref file.Ref(), "/file", OpenMode.All)); + Assert.Success(file.Get.SetSize(newSize)); + + Assert.Success(file.Get.GetSize(out long concatFileSize)); + Assert.Equal(newSize, concatFileSize); + + // Ensure the directory exists with the archive bit set + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType dirType, "/file")); + Assert.Success(baseFs.GetFileAttributes(out NxFileAttributes dirAttributes, "/file")); + Assert.Equal(DirectoryEntryType.Directory, dirType); + Assert.Equal(NxFileAttributes.Directory | NxFileAttributes.Archive, dirAttributes); + + // Ensure the internal files exist + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType internalFile00Type, "/file/00")); + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType internalFile01Type, "/file/01")); + Assert.Equal(DirectoryEntryType.File, internalFile00Type); + Assert.Equal(DirectoryEntryType.File, internalFile01Type); + + // Ensure no additional internal files exist + Assert.Result(ResultFs.PathNotFound, baseFs.GetEntryType(out _, "/file/02")); + Assert.Result(ResultFs.PathNotFound, baseFs.GetEntryType(out _, "/file/03")); + Assert.Result(ResultFs.PathNotFound, baseFs.GetEntryType(out _, "/file/04")); + Assert.Result(ResultFs.PathNotFound, baseFs.GetEntryType(out _, "/file/05")); + + // Ensure the internal file sizes are correct + Assert.Success(baseFs.GetFileSize(out long internalFile00Size, "/file/00")); + Assert.Success(baseFs.GetFileSize(out long internalFile01Size, "/file/01")); + Assert.Equal(InternalFileSize, internalFile00Size); + Assert.Equal(InternalFileSize, internalFile01Size); + } + + [Fact] + public void SetSize_ResizeSmallerWithoutChangingInternalFileCount_BaseDirHasCorrectStructure() + { + const long originalSize = InternalFileSize * 2; + const long newSize = InternalFileSize * 2 - 5; + + (IAttributeFileSystem baseFs, IFileSystem concatFs) = CreateFileSystemInternal(); + + using var file = new UniqueRef(); + + // Create the file and then resize it + Assert.Success(concatFs.CreateFile("/file", originalSize, CreateFileOptions.CreateConcatenationFile)); + Assert.Success(concatFs.OpenFile(ref file.Ref(), "/file", OpenMode.All)); + Assert.Success(file.Get.SetSize(newSize)); + + Assert.Success(file.Get.GetSize(out long concatFileSize)); + Assert.Equal(newSize, concatFileSize); + + // Ensure the directory exists with the archive bit set + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType dirType, "/file")); + Assert.Success(baseFs.GetFileAttributes(out NxFileAttributes dirAttributes, "/file")); + Assert.Equal(DirectoryEntryType.Directory, dirType); + Assert.Equal(NxFileAttributes.Directory | NxFileAttributes.Archive, dirAttributes); + + // Ensure the internal files exist + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType internalFile00Type, "/file/00")); + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType internalFile01Type, "/file/01")); + Assert.Equal(DirectoryEntryType.File, internalFile00Type); + Assert.Equal(DirectoryEntryType.File, internalFile01Type); + + // Ensure no additional internal files exist + Assert.Result(ResultFs.PathNotFound, baseFs.GetEntryType(out _, "/file/02")); + + // Ensure the internal file sizes are correct + Assert.Success(baseFs.GetFileSize(out long internalFile00Size, "/file/00")); + Assert.Success(baseFs.GetFileSize(out long internalFile01Size, "/file/01")); + Assert.Equal(InternalFileSize, internalFile00Size); + Assert.Equal(InternalFileSize - 5, internalFile01Size); + } + + [Fact] + public void SetSize_ResizeLargerWithoutChangingInternalFileCount_BaseDirHasCorrectStructure() + { + const long originalSize = InternalFileSize + 5; + const long newSize = InternalFileSize * 2 - 5; + + (IAttributeFileSystem baseFs, IFileSystem concatFs) = CreateFileSystemInternal(); + + using var file = new UniqueRef(); + + // Create the file and then resize it + Assert.Success(concatFs.CreateFile("/file", originalSize, CreateFileOptions.CreateConcatenationFile)); + Assert.Success(concatFs.OpenFile(ref file.Ref(), "/file", OpenMode.All)); + Assert.Success(file.Get.SetSize(newSize)); + + Assert.Success(file.Get.GetSize(out long concatFileSize)); + Assert.Equal(newSize, concatFileSize); + + // Ensure the directory exists with the archive bit set + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType dirType, "/file")); + Assert.Success(baseFs.GetFileAttributes(out NxFileAttributes dirAttributes, "/file")); + Assert.Equal(DirectoryEntryType.Directory, dirType); + Assert.Equal(NxFileAttributes.Directory | NxFileAttributes.Archive, dirAttributes); + + // Ensure the internal files exist + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType internalFile00Type, "/file/00")); + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType internalFile01Type, "/file/01")); + Assert.Equal(DirectoryEntryType.File, internalFile00Type); + Assert.Equal(DirectoryEntryType.File, internalFile01Type); + + // Ensure no additional internal files exist + Assert.Result(ResultFs.PathNotFound, baseFs.GetEntryType(out _, "/file/02")); + + // Ensure the internal file sizes are correct + Assert.Success(baseFs.GetFileSize(out long internalFile00Size, "/file/00")); + Assert.Success(baseFs.GetFileSize(out long internalFile01Size, "/file/01")); + Assert.Equal(InternalFileSize, internalFile00Size); + Assert.Equal(InternalFileSize - 5, internalFile01Size); + } + + [Fact] + public void SetSize_ResizeSmallerChangingInternalFileCount_BaseDirHasCorrectStructure() + { + const long originalSize = InternalFileSize * 4 + 5; + const long newSize = InternalFileSize * 2 - 5; + + (IAttributeFileSystem baseFs, IFileSystem concatFs) = CreateFileSystemInternal(); + + using var file = new UniqueRef(); + + // Create the file and then resize it + Assert.Success(concatFs.CreateFile("/file", originalSize, CreateFileOptions.CreateConcatenationFile)); + Assert.Success(concatFs.OpenFile(ref file.Ref(), "/file", OpenMode.All)); + Assert.Success(file.Get.SetSize(newSize)); + + Assert.Success(file.Get.GetSize(out long concatFileSize)); + Assert.Equal(newSize, concatFileSize); + + // Ensure the directory exists with the archive bit set + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType dirType, "/file")); + Assert.Success(baseFs.GetFileAttributes(out NxFileAttributes dirAttributes, "/file")); + Assert.Equal(DirectoryEntryType.Directory, dirType); + Assert.Equal(NxFileAttributes.Directory | NxFileAttributes.Archive, dirAttributes); + + // Ensure the internal files exist + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType internalFile00Type, "/file/00")); + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType internalFile01Type, "/file/01")); + Assert.Equal(DirectoryEntryType.File, internalFile00Type); + Assert.Equal(DirectoryEntryType.File, internalFile01Type); + + // Ensure no additional internal files exist + Assert.Result(ResultFs.PathNotFound, baseFs.GetEntryType(out _, "/file/02")); + Assert.Result(ResultFs.PathNotFound, baseFs.GetEntryType(out _, "/file/03")); + Assert.Result(ResultFs.PathNotFound, baseFs.GetEntryType(out _, "/file/04")); + + // Ensure the internal file sizes are correct + Assert.Success(baseFs.GetFileSize(out long internalFile00Size, "/file/00")); + Assert.Success(baseFs.GetFileSize(out long internalFile01Size, "/file/01")); + Assert.Equal(InternalFileSize, internalFile00Size); + Assert.Equal(InternalFileSize - 5, internalFile01Size); + } + + [Fact] + public void SetSize_ResizeLargerChangingInternalFileCount_BaseDirHasCorrectStructure() + { + const long originalSize = InternalFileSize - 5; + const long newSize = InternalFileSize * 2 - 5; + + (IAttributeFileSystem baseFs, IFileSystem concatFs) = CreateFileSystemInternal(); + + using var file = new UniqueRef(); + + // Create the file and then resize it + Assert.Success(concatFs.CreateFile("/file", originalSize, CreateFileOptions.CreateConcatenationFile)); + Assert.Success(concatFs.OpenFile(ref file.Ref(), "/file", OpenMode.All)); + Assert.Success(file.Get.SetSize(newSize)); + + Assert.Success(file.Get.GetSize(out long concatFileSize)); + Assert.Equal(newSize, concatFileSize); + + // Ensure the directory exists with the archive bit set + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType dirType, "/file")); + Assert.Success(baseFs.GetFileAttributes(out NxFileAttributes dirAttributes, "/file")); + Assert.Equal(DirectoryEntryType.Directory, dirType); + Assert.Equal(NxFileAttributes.Directory | NxFileAttributes.Archive, dirAttributes); + + // Ensure the internal files exist + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType internalFile00Type, "/file/00")); + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType internalFile01Type, "/file/01")); + Assert.Equal(DirectoryEntryType.File, internalFile00Type); + Assert.Equal(DirectoryEntryType.File, internalFile01Type); + + // Ensure no additional internal files exist + Assert.Result(ResultFs.PathNotFound, baseFs.GetEntryType(out _, "/file/02")); + + // Ensure the internal file sizes are correct + Assert.Success(baseFs.GetFileSize(out long internalFile00Size, "/file/00")); + Assert.Success(baseFs.GetFileSize(out long internalFile01Size, "/file/01")); + Assert.Equal(InternalFileSize, internalFile00Size); + Assert.Equal(InternalFileSize - 5, internalFile01Size); + } + + [Fact] + public void SetSize_ResizeToEmpty_BaseDirHasCorrectStructure() + { + const long originalSize = InternalFileSize * 2 + 5; + const long newSize = 0; + + (IAttributeFileSystem baseFs, IFileSystem concatFs) = CreateFileSystemInternal(); + + using var file = new UniqueRef(); + + // Create the file and then resize it + Assert.Success(concatFs.CreateFile("/file", originalSize, CreateFileOptions.CreateConcatenationFile)); + Assert.Success(concatFs.OpenFile(ref file.Ref(), "/file", OpenMode.All)); + Assert.Success(file.Get.SetSize(newSize)); + + Assert.Success(file.Get.GetSize(out long concatFileSize)); + Assert.Equal(newSize, concatFileSize); + + // Ensure the directory exists with the archive bit set + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType dirType, "/file")); + Assert.Success(baseFs.GetFileAttributes(out NxFileAttributes dirAttributes, "/file")); + Assert.Equal(DirectoryEntryType.Directory, dirType); + Assert.Equal(NxFileAttributes.Directory | NxFileAttributes.Archive, dirAttributes); + + // Ensure the internal files exist + Assert.Success(baseFs.GetEntryType(out DirectoryEntryType internalFile00Type, "/file/00")); + Assert.Equal(DirectoryEntryType.File, internalFile00Type); + + // Ensure no additional internal files exist + Assert.Result(ResultFs.PathNotFound, baseFs.GetEntryType(out _, "/file/01")); + Assert.Result(ResultFs.PathNotFound, baseFs.GetEntryType(out _, "/file/02")); + + // Ensure the internal file sizes are correct + Assert.Success(baseFs.GetFileSize(out long internalFile00Size, "/file/00")); + Assert.Equal(0, internalFile00Size); + } + + [Fact] + public void SetSize_ResizeToHigherMultipleOfInternalFile_FileContentsAreRetained() + { + EnsureContentsAreRetainedOnResize(InternalFileSize, InternalFileSize * 2); + } + + [Fact] + public void SetSize_ResizeToLowerMultipleOfInternalFile_FileContentsAreRetained() + { + EnsureContentsAreRetainedOnResize(InternalFileSize * 5, InternalFileSize * 2); + } + + [Fact] + public void SetSize__ResizeSmallerWithoutChangingInternalFileCount_FileContentsAreRetained() + { + EnsureContentsAreRetainedOnResize(InternalFileSize * 2, InternalFileSize * 2 - 5); + } + + [Fact] + public void SetSize_ResizeLargerWithoutChangingInternalFileCount_FileContentsAreRetained() + { + EnsureContentsAreRetainedOnResize(InternalFileSize + 5, InternalFileSize * 2 - 5); + } + + [Fact] + public void SetSize_ResizeSmallerChangingInternalFileCount_FileContentsAreRetained() + { + EnsureContentsAreRetainedOnResize(InternalFileSize * 4 + 5, InternalFileSize * 2 - 5); + } + + [Fact] + public void SetSize_ResizeLargerChangingInternalFileCount_FileContentsAreRetained() + { + EnsureContentsAreRetainedOnResize(InternalFileSize - 5, InternalFileSize * 2 - 5); + } + + private void EnsureContentsAreRetainedOnResize(int originalSize, int newSize) + { + const string fileName = "/file"; + + byte[] originalData = new byte[originalSize]; + new Random(1234).NextBytes(originalData); + + byte[] newData = new byte[newSize]; + byte[] actualNewData = new byte[newSize]; + originalData.AsSpan(0, Math.Min(originalSize, newSize)).CopyTo(newData); + + IFileSystem fs = CreateFileSystem(); + + // Create the file and write the data to it + using (var file = new UniqueRef()) + { + // Create the file and then write the data to it + Assert.Success(fs.CreateFile(fileName, originalSize, CreateFileOptions.CreateConcatenationFile)); + Assert.Success(fs.OpenFile(ref file.Ref(), fileName, OpenMode.Write)); + Assert.Success(file.Get.Write(0, originalData, WriteOption.None)); + } + + // Resize the file + using (var file = new UniqueRef()) + { + Assert.Success(fs.OpenFile(ref file.Ref(), fileName, OpenMode.Write)); + Assert.Success(file.Get.SetSize(newSize)); + } + + // Read back the entire resized file and ensure the contents are as expected + using (var file = new UniqueRef()) + { + Assert.Success(fs.OpenFile(ref file.Ref(), fileName, OpenMode.Read)); + Assert.Success(file.Get.Read(out long bytesRead, 0, actualNewData)); + Assert.Equal(newSize, bytesRead); + } + + Assert.Equal(newData, actualNewData); + } +} \ No newline at end of file From 375b5b922052a5969ffe596502432d9c51186f71 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 21 Dec 2021 19:47:42 -0700 Subject: [PATCH 10/34] Update ConcatenationFileSystem from 12.1.0 to 13.1.0 --- .../FsSystem/ConcatenationFileSystem.cs | 74 +++++++++++++++---- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/src/LibHac/FsSystem/ConcatenationFileSystem.cs b/src/LibHac/FsSystem/ConcatenationFileSystem.cs index 2ded58e4..d20119ed 100644 --- a/src/LibHac/FsSystem/ConcatenationFileSystem.cs +++ b/src/LibHac/FsSystem/ConcatenationFileSystem.cs @@ -8,6 +8,7 @@ using LibHac.Common.FixedArrays; using LibHac.Diag; using LibHac.Fs; using LibHac.Fs.Fsa; +using LibHac.Os; using LibHac.Util; using static LibHac.FsSystem.Utility; @@ -27,7 +28,7 @@ namespace LibHac.FsSystem; /// Each internal file except the final one must have the internal file size that was specified /// at the creation of the . /// -/// Based on FS 12.1.0 (nnSdk 12.3.1) +/// Based on FS 13.1.0 (nnSdk 13.4.0) /// public class ConcatenationFileSystem : IFileSystem { @@ -211,6 +212,8 @@ public class ConcatenationFileSystem : IFileSystem if (size > currentSize) { + Assert.SdkAssert(_fileArray.Count > currentTailIndex); + rc = _fileArray[currentTailIndex].SetSize(GetInternalFileSize(size, currentTailIndex)); if (rc.IsFailure()) return rc.Miss(); @@ -285,12 +288,13 @@ public class ConcatenationFileSystem : IFileSystem if (!_mode.HasFlag(OpenMode.Read)) return ResultFs.ReadUnpermitted.Log(); - var closure = new OperateRangeClosure(); - closure.OutBuffer = outBuffer; - closure.InBuffer = inBuffer; - closure.OperationId = operationId; + foreach (IFile file in _fileArray) + { + Result rc = file.OperateRange(operationId, 0, long.MaxValue); + if (rc.IsFailure()) return rc.Miss(); + } - return DoOperateRangeImpl(offset, size, InvalidateCacheImpl, ref closure).Ret(); + return Result.Success; } case OperationId.QueryRange: { @@ -312,11 +316,6 @@ public class ConcatenationFileSystem : IFileSystem return ResultFs.UnsupportedOperateRangeForConcatenationFile.Log(); } - static Result InvalidateCacheImpl(IFile file, long offset, long size, ref OperateRangeClosure closure) - { - return file.OperateRange(closure.OutBuffer, closure.OperationId, offset, size, closure.InBuffer).Ret(); - } - static Result QueryRangeImpl(IFile file, long offset, long size, ref OperateRangeClosure closure) { Unsafe.SkipInit(out QueryRangeInfo infoEntry); @@ -370,7 +369,6 @@ public class ConcatenationFileSystem : IFileSystem private ref struct OperateRangeClosure { - public Span OutBuffer; public ReadOnlySpan InBuffer; public OperationId OperationId; public QueryRangeInfo InfoMerged; @@ -496,6 +494,7 @@ public class ConcatenationFileSystem : IFileSystem private UniqueRef _baseFileSystem; private long _internalFileSize; + private SdkMutexType _mutex; /// /// Initializes a new with an internal file size of . @@ -516,6 +515,7 @@ public class ConcatenationFileSystem : IFileSystem { _baseFileSystem = new UniqueRef(ref baseFileSystem); _internalFileSize = internalFileSize; + _mutex = new SdkMutexType(); } public override void Dispose() @@ -611,6 +611,8 @@ public class ConcatenationFileSystem : IFileSystem protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path) { + using var scopedLock = new ScopedLock(ref _mutex); + if (IsConcatenationFile(in path)) { entryType = DirectoryEntryType.File; @@ -622,26 +624,36 @@ public class ConcatenationFileSystem : IFileSystem protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path) { + using var scopedLock = new ScopedLock(ref _mutex); + return _baseFileSystem.Get.GetFreeSpaceSize(out freeSpace, path).Ret(); } protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path) { + using var scopedLock = new ScopedLock(ref _mutex); + return _baseFileSystem.Get.GetTotalSpaceSize(out totalSpace, path).Ret(); } protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path) { + using var scopedLock = new ScopedLock(ref _mutex); + return _baseFileSystem.Get.GetFileTimeStampRaw(out timeStamp, path).Ret(); } protected override Result DoFlush() { + using var scopedLock = new ScopedLock(ref _mutex); + return _baseFileSystem.Get.Flush().Ret(); } protected override Result DoOpenFile(ref UniqueRef outFile, in Path path, OpenMode mode) { + using var scopedLock = new ScopedLock(ref _mutex); + if (!IsConcatenationFile(in path)) { return _baseFileSystem.Get.OpenFile(ref outFile, in path, mode).Ret(); @@ -700,6 +712,8 @@ public class ConcatenationFileSystem : IFileSystem protected override Result DoOpenDirectory(ref UniqueRef outDirectory, in Path path, OpenDirectoryMode mode) { + using var scopedLock = new ScopedLock(ref _mutex); + if (IsConcatenationFile(path)) { return ResultFs.PathNotFound.Log(); @@ -720,6 +734,8 @@ public class ConcatenationFileSystem : IFileSystem protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option) { + using var scopedLock = new ScopedLock(ref _mutex); + CreateFileOptions newOption = option & ~CreateFileOptions.CreateConcatenationFile; // Create a normal file if the concatenation file flag isn't set @@ -802,6 +818,8 @@ public class ConcatenationFileSystem : IFileSystem protected override Result DoDeleteFile(in Path path) { + using var scopedLock = new ScopedLock(ref _mutex); + if (!IsConcatenationFile(in path)) { return _baseFileSystem.Get.DeleteFile(in path).Ret(); @@ -834,6 +852,8 @@ public class ConcatenationFileSystem : IFileSystem protected override Result DoCreateDirectory(in Path path) { + using var scopedLock = new ScopedLock(ref _mutex); + // Check if the parent path is a concatenation file because we can't create a directory inside one. using var parentPath = new Path(); Result rc = GenerateParentPath(ref parentPath.Ref(), in path); @@ -847,6 +867,8 @@ public class ConcatenationFileSystem : IFileSystem protected override Result DoDeleteDirectory(in Path path) { + using var scopedLock = new ScopedLock(ref _mutex); + // Make sure the directory isn't a concatenation file. if (IsConcatenationFile(path)) return ResultFs.PathNotFound.Log(); @@ -875,7 +897,13 @@ public class ConcatenationFileSystem : IFileSystem protected override Result DoDeleteDirectoryRecursively(in Path path) { - if (IsConcatenationFile(in path)) + bool isConcatenationFile; + using (new ScopedLock(ref _mutex)) + { + isConcatenationFile = IsConcatenationFile(in path); + } + + if (isConcatenationFile) return ResultFs.PathNotFound.Log(); Result rc = CleanDirectoryRecursivelyImpl(in path); @@ -886,7 +914,13 @@ public class ConcatenationFileSystem : IFileSystem protected override Result DoCleanDirectoryRecursively(in Path path) { - if (IsConcatenationFile(in path)) + bool isConcatenationFile; + using (new ScopedLock(ref _mutex)) + { + isConcatenationFile = IsConcatenationFile(in path); + } + + if (isConcatenationFile) return ResultFs.PathNotFound.Log(); return CleanDirectoryRecursivelyImpl(in path).Ret(); @@ -894,6 +928,8 @@ public class ConcatenationFileSystem : IFileSystem protected override Result DoRenameFile(in Path currentPath, in Path newPath) { + using var scopedLock = new ScopedLock(ref _mutex); + if (IsConcatenationFile(in currentPath)) return _baseFileSystem.Get.RenameDirectory(in currentPath, in newPath).Ret(); @@ -902,6 +938,8 @@ public class ConcatenationFileSystem : IFileSystem protected override Result DoRenameDirectory(in Path currentPath, in Path newPath) { + using var scopedLock = new ScopedLock(ref _mutex); + if (IsConcatenationFile(in currentPath)) return ResultFs.PathNotFound.Log(); @@ -912,6 +950,8 @@ public class ConcatenationFileSystem : IFileSystem { UnsafeHelpers.SkipParamInit(out size); + using var scopedLock = new ScopedLock(ref _mutex); + using var internalFilePath = new Path(); Result rc = internalFilePath.Initialize(in path); if (rc.IsFailure()) return rc.Miss(); @@ -950,6 +990,8 @@ public class ConcatenationFileSystem : IFileSystem protected override Result DoQueryEntry(Span outBuffer, ReadOnlySpan inBuffer, QueryId queryId, in Path path) { + using var scopedLock = new ScopedLock(ref _mutex); + if (queryId != QueryId.SetConcatenationFileAttribute) return ResultFs.UnsupportedQueryEntryForConcatenationFileSystem.Log(); @@ -958,11 +1000,15 @@ public class ConcatenationFileSystem : IFileSystem protected override Result DoCommit() { + using var scopedLock = new ScopedLock(ref _mutex); + return _baseFileSystem.Get.Commit().Ret(); } protected override Result DoCommitProvisionally(long counter) { + using var scopedLock = new ScopedLock(ref _mutex); + return _baseFileSystem.Get.CommitProvisionally(counter).Ret(); } } \ No newline at end of file From cf7062788f96b7c5657c4140d01540964bba35bb Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 22 Dec 2021 16:43:04 -0700 Subject: [PATCH 11/34] Fixup some mounted-FS-related classes and update them to 13.1.0 - FileSystemAccessor - FileAccessor - DirectoryAccessor - MountTable - UserMountTable - FileDataCacheAccessor - IFileDataCache - GlobalFileDataCacheAccessorReadableScopedPointer - FileDataCache shim functions - PathBasedFileDataCache shim functions --- src/LibHac/Common/Box.cs | 13 ++ src/LibHac/Fs/FileSystemClient.cs | 3 +- src/LibHac/Fs/Fsa/DirectoryAccessor.cs | 6 +- src/LibHac/Fs/Fsa/FileAccessor.cs | 100 +++++----- src/LibHac/Fs/Fsa/FileSystemAccessor.cs | 76 ++++++-- src/LibHac/Fs/Fsa/MountTable.cs | 51 +----- src/LibHac/Fs/Fsa/Registrar.cs | 6 +- src/LibHac/Fs/Fsa/UserMountTable.cs | 19 +- src/LibHac/Fs/Impl/FileDataCacheAccessor.cs | 12 +- src/LibHac/Fs/Impl/FilePathHash.cs | 8 +- src/LibHac/Fs/Impl/IFileDataCache.cs | 8 +- src/LibHac/Fs/Shim/FileDataCache.cs | 183 +++++++++++++++++++ src/LibHac/Fs/Shim/PathBasedFileDataCache.cs | 62 +++++++ src/LibHac/Os/ReaderWriterLock.cs | 9 +- 14 files changed, 414 insertions(+), 142 deletions(-) create mode 100644 src/LibHac/Common/Box.cs create mode 100644 src/LibHac/Fs/Shim/FileDataCache.cs create mode 100644 src/LibHac/Fs/Shim/PathBasedFileDataCache.cs diff --git a/src/LibHac/Common/Box.cs b/src/LibHac/Common/Box.cs new file mode 100644 index 00000000..6d31ac62 --- /dev/null +++ b/src/LibHac/Common/Box.cs @@ -0,0 +1,13 @@ +namespace LibHac.Common; + +public class Box where T : struct +{ + private T _value; + + public ref T Value => ref _value; + + public Box() + { + _value = new T(); + } +} \ No newline at end of file diff --git a/src/LibHac/Fs/FileSystemClient.cs b/src/LibHac/Fs/FileSystemClient.cs index 898a1702..f6e285a2 100644 --- a/src/LibHac/Fs/FileSystemClient.cs +++ b/src/LibHac/Fs/FileSystemClient.cs @@ -35,6 +35,7 @@ internal struct FileSystemClientGlobals : IDisposable public FsContextHandlerGlobals FsContextHandler; public ResultHandlingUtilityGlobals ResultHandlingUtility; public DirectorySaveDataFileSystemGlobals DirectorySaveDataFileSystem; + public FileDataCacheShim.Globals FileDataCache; public void Initialize(FileSystemClient fsClient, HorizonClient horizonClient) { @@ -60,4 +61,4 @@ public readonly struct FileSystemClientImpl internal ref FileSystemClientGlobals Globals => ref Fs.Globals; internal FileSystemClientImpl(FileSystemClient parentClient) => Fs = parentClient; -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/DirectoryAccessor.cs b/src/LibHac/Fs/Fsa/DirectoryAccessor.cs index bf8962e0..b08a98aa 100644 --- a/src/LibHac/Fs/Fsa/DirectoryAccessor.cs +++ b/src/LibHac/Fs/Fsa/DirectoryAccessor.cs @@ -5,6 +5,10 @@ using LibHac.Fs.Fsa; // ReSharper disable once CheckNamespace namespace LibHac.Fs.Impl; +/// +/// Provides access to a directory in a mounted file system and handles closing the directory. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) internal class DirectoryAccessor : IDisposable { private UniqueRef _directory; @@ -35,4 +39,4 @@ internal class DirectoryAccessor : IDisposable { return _directory.Get.GetEntryCount(out entryCount); } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/FileAccessor.cs b/src/LibHac/Fs/Fsa/FileAccessor.cs index 6f61137c..5352accb 100644 --- a/src/LibHac/Fs/Fsa/FileAccessor.cs +++ b/src/LibHac/Fs/Fsa/FileAccessor.cs @@ -2,6 +2,7 @@ using LibHac.Common; using LibHac.Diag; using LibHac.Fs.Fsa; +using LibHac.Fs.Shim; using LibHac.Os; using static LibHac.Fs.Impl.AccessLogStrings; @@ -15,6 +16,10 @@ internal enum WriteState Failed, } +/// +/// Provides access to a mount and handles caching it. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) internal class FileAccessor : IDisposable { private const string NeedFlushMessage = "Error: nn::fs::CloseFile() failed because the file was not flushed.\n"; @@ -24,8 +29,7 @@ internal class FileAccessor : IDisposable private WriteState _writeState; private Result _lastResult; private OpenMode _openMode; - private FilePathHash _filePathHash; - // ReSharper disable once NotAccessedField.Local + private Box _filePathHash; private int _pathHashIndex; internal HorizonClient Hos { get; } @@ -59,7 +63,7 @@ internal class FileAccessor : IDisposable public WriteState GetWriteState() => _writeState; public FileSystemAccessor GetParent() => _parentFileSystem; - public void SetFilePathHash(FilePathHash filePathHash, int index) + public void SetFilePathHash(Box filePathHash, int index) { _filePathHash = filePathHash; _pathHashIndex = index; @@ -73,18 +77,18 @@ internal class FileAccessor : IDisposable return result; } - public Result ReadWithoutCacheAccessLog(out long bytesRead, long offset, Span destination, - in ReadOption option) - { - return _file.Get.Read(out bytesRead, offset, destination, in option); - } - private Result ReadWithCacheAccessLog(out long bytesRead, long offset, Span destination, in ReadOption option, bool usePathCache, bool useDataCache) { throw new NotImplementedException(); } + public Result ReadWithoutCacheAccessLog(out long bytesRead, long offset, Span destination, + in ReadOption option) + { + return _file.Get.Read(out bytesRead, offset, destination, in option); + } + public Result Read(out long bytesRead, long offset, Span destination, in ReadOption option) { UnsafeHelpers.SkipParamInit(out bytesRead); @@ -113,42 +117,36 @@ internal class FileAccessor : IDisposable return _lastResult; } - // ReSharper disable ConditionIsAlwaysTrueOrFalse - bool usePathCache = _parentFileSystem is not null && _filePathHash.Data != 0; + bool usePathCache = _parentFileSystem is not null && _filePathHash is not null; + bool useDataCache = Hos.Fs.Impl.IsGlobalFileDataCacheEnabled() && _parentFileSystem is not null && + _parentFileSystem.IsFileDataCacheAttachable(); - // Todo: Call IsGlobalFileDataCacheEnabled -#pragma warning disable 162 - bool useDataCache = false && _parentFileSystem is not null && _parentFileSystem.IsFileDataCacheAttachable(); -#pragma warning restore 162 if (usePathCache || useDataCache) { return ReadWithCacheAccessLog(out bytesRead, offset, destination, in option, usePathCache, useDataCache); } + + if (Hos.Fs.Impl.IsEnabledAccessLog() && Hos.Fs.Impl.IsEnabledHandleAccessLog(handle)) + { + Tick start = Hos.Os.GetSystemTick(); + rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option); + Tick end = Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogOffset).AppendFormat(offset) + .Append(LogSize).AppendFormat(destination.Length) + .Append(LogReadSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in bytesRead, rc)); + + Hos.Fs.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(logBuffer), + nameof(UserFile.ReadFile)); + } else { - if (Hos.Fs.Impl.IsEnabledAccessLog() && Hos.Fs.Impl.IsEnabledHandleAccessLog(handle)) - { - Tick start = Hos.Os.GetSystemTick(); - rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option); - Tick end = Hos.Os.GetSystemTick(); - - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogOffset).AppendFormat(offset) - .Append(LogSize).AppendFormat(destination.Length) - .Append(LogReadSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in bytesRead, rc)); - - Hos.Fs.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(logBuffer), - nameof(UserFile.ReadFile)); - } - else - { - rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option); - } - - return rc; + rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option); } - // ReSharper restore ConditionIsAlwaysTrueOrFalse + + return rc; } public Result Write(long offset, ReadOnlySpan source, in WriteOption option) @@ -159,9 +157,11 @@ internal class FileAccessor : IDisposable using ScopedSetter setter = ScopedSetter.MakeScopedSetter(ref _writeState, WriteState.Failed); - if (_filePathHash.Data != 0) + if (_filePathHash is not null) { - throw new NotImplementedException(); + Result rc = UpdateLastResult(Hos.Fs.Impl.WriteViaPathBasedFileDataCache(_file.Get, (int)GetOpenMode(), + _parentFileSystem, in _filePathHash.Value, _pathHashIndex, offset, source, in option)); + if (rc.IsFailure()) return rc.Miss(); } else { @@ -193,19 +193,27 @@ internal class FileAccessor : IDisposable if (_lastResult.IsFailure()) return _lastResult; - WriteState oldWriteState = _writeState; + WriteState originalWriteState = _writeState; using ScopedSetter setter = ScopedSetter.MakeScopedSetter(ref _writeState, WriteState.Failed); - Result rc = UpdateLastResult(_file.Get.SetSize(size)); - if (rc.IsFailure()) return rc; - - if (_filePathHash.Data != 0) + if (_filePathHash is not null) { - throw new NotImplementedException(); + using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries(); + + Result rc = UpdateLastResult(_file.Get.SetSize(size)); + if (rc.IsFailure()) return rc.Miss(); + + Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntry(_parentFileSystem, in _filePathHash.Value, _pathHashIndex); + if (rc.IsFailure()) return rc.Miss(); + } + else + { + Result rc = UpdateLastResult(_file.Get.SetSize(size)); + if (rc.IsFailure()) return rc; } - setter.Set(oldWriteState); + setter.Set(originalWriteState); return Result.Success; } @@ -224,4 +232,4 @@ internal class FileAccessor : IDisposable { return _file.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer); } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/FileSystemAccessor.cs b/src/LibHac/Fs/Fsa/FileSystemAccessor.cs index bd113e67..d24c7b91 100644 --- a/src/LibHac/Fs/Fsa/FileSystemAccessor.cs +++ b/src/LibHac/Fs/Fsa/FileSystemAccessor.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using LibHac.Common; using LibHac.Diag; using LibHac.Fs.Fsa; +using LibHac.Fs.Shim; using LibHac.Os; using LibHac.Util; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; @@ -10,6 +11,13 @@ using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; // ReSharper disable once CheckNamespace namespace LibHac.Fs.Impl; +/// +/// Provides access to a mounted and contains metadata and objects related to it. +/// This data includes the mount name, open files and directories, whether the access log is enabled for this file +/// system, whether caching is being used, how to get a save file system's and +/// the target used to include a save file system in a multi-commit operation. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) internal class FileSystemAccessor : IDisposable { private const string EmptyMountNameMessage = "Error: Mount failed because the mount name was empty.\n"; @@ -31,7 +39,7 @@ internal class FileSystemAccessor : IDisposable private bool _isPathCacheAttached; private IMultiCommitTarget _multiCommitTarget; private PathFlags _pathFlags; - private Optional _dataId; + private IStorage _storageForPurgeFileDataCache; internal HorizonClient Hos { get; } @@ -44,7 +52,7 @@ internal class FileSystemAccessor : IDisposable _fileSystem = new UniqueRef(ref fileSystem); _openFiles = new LinkedList(); _openDirectories = new LinkedList(); - _openListLock.Initialize(); + _openListLock = new SdkMutexType(); _mountNameGenerator = new UniqueRef(ref mountNameGenerator); _saveDataAttributeGetter = new UniqueRef(ref saveAttributeGetter); _multiCommitTarget = multiCommitTarget; @@ -90,7 +98,8 @@ internal class FileSystemAccessor : IDisposable if (_isPathCacheAttached) { - throw new NotImplementedException(); + using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries(); + Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntries(this); } } @@ -113,7 +122,16 @@ internal class FileSystemAccessor : IDisposable } public void SetAccessLog(bool isEnabled) => _isAccessLogEnabled = isEnabled; - public void SetFileDataCacheAttachable(bool isAttachable) => _isDataCacheAttachable = isAttachable; + + public void SetFileDataCacheAttachable(bool isAttachable, IStorage storageForPurgeFileDataCache) + { + if (isAttachable) + Assert.SdkAssert(storageForPurgeFileDataCache is not null); + + _isDataCacheAttachable = isAttachable; + _storageForPurgeFileDataCache = storageForPurgeFileDataCache; + } + public void SetPathBasedFileDataCacheAttachable(bool isAttachable) => _isPathCacheAttachable = isAttachable; public bool IsEnabledAccessLog() => _isAccessLogEnabled; @@ -126,10 +144,7 @@ internal class FileSystemAccessor : IDisposable _isPathCacheAttached = true; } - public Optional GetDataId() => _dataId; - public void SetDataId(Optional dataId) => _dataId = dataId; - - public Result SetUpPath(ref Path path, U8Span pathBuffer) + private Result SetUpPath(ref Path path, U8Span pathBuffer) { Result rc = PathFormatter.IsNormalized(out bool isNormalized, out _, pathBuffer, _pathFlags); @@ -168,7 +183,12 @@ internal class FileSystemAccessor : IDisposable if (_isPathCacheAttached) { - throw new NotImplementedException(); + using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries(); + + rc = _fileSystem.Get.CreateFile(in pathNormalized, size, option); + if (rc.IsFailure()) return rc.Miss(); + + Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntry(this, in pathNormalized); } else { @@ -251,7 +271,12 @@ internal class FileSystemAccessor : IDisposable if (_isPathCacheAttached) { - throw new NotImplementedException(); + using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries(); + + rc = _fileSystem.Get.RenameFile(in currentPathNormalized, in newPathNormalized); + if (rc.IsFailure()) return rc.Miss(); + + Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntry(this, in newPathNormalized); } else { @@ -274,7 +299,12 @@ internal class FileSystemAccessor : IDisposable if (_isPathCacheAttached) { - throw new NotImplementedException(); + using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries(); + + rc = _fileSystem.Get.RenameDirectory(in currentPathNormalized, in newPathNormalized); + if (rc.IsFailure()) return rc.Miss(); + + Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntries(this); } else { @@ -347,11 +377,17 @@ internal class FileSystemAccessor : IDisposable { if (mode.HasFlag(OpenMode.AllowAppend)) { - throw new NotImplementedException(); + using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries(); + Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntry(this, in pathNormalized); } else { - throw new NotImplementedException(); + var hash = new Box(); + + if (Hos.Fs.Impl.FindPathBasedFileDataCacheEntry(out hash.Value, out int hashIndex, this, in pathNormalized)) + { + accessor.SetFilePathHash(hash, hashIndex); + } } } @@ -432,6 +468,13 @@ internal class FileSystemAccessor : IDisposable return Result.Success; } + public void PurgeFileDataCache(FileDataCacheAccessor accessor) + { + Assert.SdkAssert(_storageForPurgeFileDataCache is not null); + + accessor.Purge(_storageForPurgeFileDataCache); + } + public U8Span GetName() { return new U8Span(_mountName.Name); @@ -470,11 +513,6 @@ internal class FileSystemAccessor : IDisposable } } - public void PurgeFileDataCache(FileDataCacheAccessor cacheAccessor) - { - cacheAccessor.Purge(_fileSystem.Get); - } - internal void NotifyCloseFile(FileAccessor file) { using ScopedLock scopedLock = ScopedLock.Lock(ref _openListLock); @@ -673,4 +711,4 @@ internal class FileSystemAccessor : IDisposable } } } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/MountTable.cs b/src/LibHac/Fs/Fsa/MountTable.cs index 2c2609ad..07fcc727 100644 --- a/src/LibHac/Fs/Fsa/MountTable.cs +++ b/src/LibHac/Fs/Fsa/MountTable.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Diag; -using LibHac.Ncm; using LibHac.Os; using LibHac.Util; @@ -14,7 +13,7 @@ namespace LibHac.Fs.Impl; /// Holds a list of s that are indexed by their name. /// These may be retrieved or removed using their name as a key. /// -/// Based on FS 12.1.0 (nnSdk 12.3.1) +/// Based on FS 13.1.0 (nnSdk 13.4.0) internal class MountTable : IDisposable { private LinkedList _fileSystemList; @@ -76,9 +75,11 @@ internal class MountTable : IDisposable currentNode is not null; currentNode = currentNode.Next) { - if (!Matches(currentNode.Value, name)) continue; - accessor = currentNode.Value; - return Result.Success; + if (Matches(currentNode.Value, name)) + { + accessor = currentNode.Value; + return Result.Success; + } } return ResultFs.NotMounted.Log(); @@ -121,44 +122,4 @@ internal class MountTable : IDisposable return true; } - - public int GetDataIdCount() - { - using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex); - - int count = 0; - - for (LinkedListNode currentNode = _fileSystemList.First; - currentNode is not null; - currentNode = currentNode.Next) - { - if (currentNode.Value.GetDataId().HasValue) - count++; - } - - return count; - } - - public Result ListDataId(out int dataIdCount, Span dataIdBuffer) - { - using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex); - - int count = 0; - - for (LinkedListNode currentNode = _fileSystemList.First; - currentNode is not null && count < dataIdBuffer.Length; - currentNode = currentNode.Next) - { - Optional dataId = currentNode.Value.GetDataId(); - - if (dataId.HasValue) - { - dataIdBuffer[count] = dataId.Value; - count++; - } - } - - dataIdCount = count; - return Result.Success; - } } \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/Registrar.cs b/src/LibHac/Fs/Fsa/Registrar.cs index c523477c..1a2e06f0 100644 --- a/src/LibHac/Fs/Fsa/Registrar.cs +++ b/src/LibHac/Fs/Fsa/Registrar.cs @@ -97,9 +97,9 @@ public static class Registrar if (!accessor.HasValue) return ResultFs.AllocationMemoryFailedInRegisterB.Log(); - accessor.Get.SetFileDataCacheAttachable(useDataCache); + // accessor.Get.SetFileDataCacheAttachable(useDataCache); accessor.Get.SetPathBasedFileDataCacheAttachable(usePathCache); - accessor.Get.SetDataId(dataId); + // accessor.Get.SetDataId(dataId); Result rc = fs.Impl.Register(ref accessor.Ref()); if (rc.IsFailure()) return rc.Miss(); @@ -111,4 +111,4 @@ public static class Registrar { fs.Impl.Unregister(name); } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/UserMountTable.cs b/src/LibHac/Fs/Fsa/UserMountTable.cs index 177e4382..dd860cc2 100644 --- a/src/LibHac/Fs/Fsa/UserMountTable.cs +++ b/src/LibHac/Fs/Fsa/UserMountTable.cs @@ -1,7 +1,5 @@ -using System; -using LibHac.Common; +using LibHac.Common; using LibHac.Fs.Impl; -using LibHac.Ncm; namespace LibHac.Fs.Fsa; @@ -18,7 +16,7 @@ internal struct UserMountTableGlobals /// /// Contains functions for adding, removing and retrieving s from the mount table. /// -/// Based on FS 12.1.0 (nnSdk 12.3.1) +/// Based on FS 13.1.0 (nnSdk 13.4.0) internal static class UserMountTable { public static Result Register(this FileSystemClientImpl fs, ref UniqueRef fileSystem) @@ -35,15 +33,4 @@ internal static class UserMountTable { fs.Globals.UserMountTable.MountTable.Unmount(name); } - - public static int GetMountedDataIdCount(this FileSystemClientImpl fs) - { - return fs.Globals.UserMountTable.MountTable.GetDataIdCount(); - } - - public static Result ListMountedDataId(this FileSystemClientImpl fs, out int dataIdCount, - Span dataIdBuffer) - { - return fs.Globals.UserMountTable.MountTable.ListDataId(out dataIdCount, dataIdBuffer); - } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Impl/FileDataCacheAccessor.cs b/src/LibHac/Fs/Impl/FileDataCacheAccessor.cs index 7c715c2b..1bab0250 100644 --- a/src/LibHac/Fs/Impl/FileDataCacheAccessor.cs +++ b/src/LibHac/Fs/Impl/FileDataCacheAccessor.cs @@ -3,9 +3,13 @@ using LibHac.Fs.Fsa; namespace LibHac.Fs.Impl; +/// +/// Provides access to an . +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) internal class FileDataCacheAccessor { - private IFileDataCache _cache; + private readonly IFileDataCache _cache; public FileDataCacheAccessor(IFileDataCache cache) { @@ -18,8 +22,8 @@ internal class FileDataCacheAccessor return _cache.Read(file, out bytesRead, offset, destination, in option, ref cacheAccessResult); } - public void Purge(IFileSystem fileSystem) + public void Purge(IStorage storage) { - _cache.Purge(fileSystem); + _cache.Purge(storage); } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Impl/FilePathHash.cs b/src/LibHac/Fs/Impl/FilePathHash.cs index 3f1531f8..80caf8ce 100644 --- a/src/LibHac/Fs/Impl/FilePathHash.cs +++ b/src/LibHac/Fs/Impl/FilePathHash.cs @@ -1,6 +1,8 @@ -namespace LibHac.Fs.Impl; +using LibHac.Common.FixedArrays; + +namespace LibHac.Fs.Impl; public struct FilePathHash { - public int Data; -} + public Array4 Data; +} \ No newline at end of file diff --git a/src/LibHac/Fs/Impl/IFileDataCache.cs b/src/LibHac/Fs/Impl/IFileDataCache.cs index c2051423..bfdd01ea 100644 --- a/src/LibHac/Fs/Impl/IFileDataCache.cs +++ b/src/LibHac/Fs/Impl/IFileDataCache.cs @@ -7,11 +7,15 @@ using LibHac.Fs.Fsa; namespace LibHac.Fs.Impl; // ReSharper disable once InconsistentNaming +/// +/// Provides a system for caching reads to one or more file systems. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) internal abstract class IFileDataCache : IDisposable { public abstract void Dispose(); - public abstract void Purge(IFileSystem fileSystem); + public abstract void Purge(IStorage storage); protected abstract Result DoRead(IFile file, out long bytesRead, long offset, Span destination, in ReadOption option, ref FileDataCacheAccessResult cacheAccessResult); @@ -88,4 +92,4 @@ internal struct FileDataCacheAccessResult _regionCount++; } } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Shim/FileDataCache.cs b/src/LibHac/Fs/Shim/FileDataCache.cs new file mode 100644 index 00000000..35138910 --- /dev/null +++ b/src/LibHac/Fs/Shim/FileDataCache.cs @@ -0,0 +1,183 @@ +// ReSharper disable UnusedMember.Local +using System; +using LibHac.Common; +using LibHac.Diag; +using LibHac.Fs.Impl; +using LibHac.Os; + +namespace LibHac.Fs.Impl +{ + /// + /// Handles getting scoped read access to the global file data cache. + /// + /// Based on FS 13.1.0 (nnSdk 13.4.0) + internal struct GlobalFileDataCacheAccessorReadableScopedPointer : IDisposable + { + private FileDataCacheAccessor _accessor; + private ReaderWriterLock _lock; + + public void Dispose() + { + _lock?.ReleaseReadLock(); + } + + public readonly FileDataCacheAccessor Get() => _accessor; + + public void Set(FileDataCacheAccessor accessor, ReaderWriterLock rwLock) + { + if (_lock is not null && _lock != rwLock) + _lock.ReleaseReadLock(); + + _accessor = accessor; + _lock = rwLock; + } + } +} + +namespace LibHac.Fs.Shim +{ + public static class FileDataCacheShim + { + internal struct Globals : IDisposable + { + public nint FileSystemProxyServiceObjectInitGuard; + public GlobalFileDataCacheAccessorHolder GlobalFileDataCacheAccessorHolder; + + public void Dispose() + { + GlobalFileDataCacheAccessorHolder.Dispose(); + } + } + + internal class GlobalFileDataCacheAccessorHolder + { + private FileDataCacheAccessor _accessor; + private ReaderWriterLock _accessorLock; + private long _cacheSize; + private bool _isDefault; + + public GlobalFileDataCacheAccessorHolder(HorizonClient hos) + { + _accessorLock = new ReaderWriterLock(hos.Os); + } + + public void Dispose() + { + _accessorLock?.Dispose(); + } + + public ReaderWriterLock GetLock() => _accessorLock; + + public bool HasAccessor() + { + Assert.SdkAssert(_accessorLock.IsReadLockHeld() || _accessorLock.IsWriteLockHeldByCurrentThread()); + + return _accessor is not null; + } + + public void SetAccessor() + { + Assert.SdkAssert(_accessorLock.IsWriteLockHeldByCurrentThread()); + + _accessor = null; + } + + public void SetAccessor(FileDataCacheAccessor accessor, long cacheSize, bool isDefault) + { + Assert.SdkAssert(_accessorLock.IsWriteLockHeldByCurrentThread()); + + _accessor = accessor; + _cacheSize = cacheSize; + _isDefault = isDefault; + } + + public FileDataCacheAccessor GetAccessor() + { + Assert.SdkAssert(_accessorLock.IsReadLockHeld() || _accessorLock.IsWriteLockHeldByCurrentThread()); + + return _accessor; + } + + public long GetCacheSize() + { + Assert.SdkAssert(_accessorLock.IsReadLockHeld() || _accessorLock.IsWriteLockHeldByCurrentThread()); + + return _cacheSize; + } + + public bool IsDefaultGlobalFileDataCache() + { + Assert.SdkAssert(_accessorLock.IsReadLockHeld() || _accessorLock.IsWriteLockHeldByCurrentThread()); + Assert.SdkNotNull(_accessor); + + return _isDefault; + } + } + + private static GlobalFileDataCacheAccessorHolder GetGlobalFileDataCacheAccessorHolder(FileSystemClient fs) + { + ref Globals g = ref fs.Globals.FileDataCache; + using var guard = new InitializationGuard(ref g.FileSystemProxyServiceObjectInitGuard, + fs.Globals.InitMutex); + + if (!guard.IsInitialized) + { + g.GlobalFileDataCacheAccessorHolder = new GlobalFileDataCacheAccessorHolder(fs.Hos); + } + + return g.GlobalFileDataCacheAccessorHolder; + } + + private static Result EnableGlobalFileDataCacheImpl(FileSystemClient fs, Memory buffer, bool isDefault) + { + throw new NotImplementedException(); + } + + private static Result DisableGlobalFileDataCacheImpl(FileSystemClient fs) + { + throw new NotImplementedException(); + } + + private static int PrintDefaultGlobalFileDataCacheAccessLog(Span textBuffer) + { + throw new NotImplementedException(); + } + + internal static bool IsGlobalFileDataCacheEnabled(this FileSystemClientImpl fs) + { + return false; + } + + internal static bool TryGetGlobalFileDataCacheAccessor(this FileSystemClientImpl fs, + ref GlobalFileDataCacheAccessorReadableScopedPointer scopedPointer) + { + throw new NotImplementedException(); + } + + internal static void SetGlobalFileDataCacheAccessorForDebug(this FileSystemClientImpl fs, + FileDataCacheAccessor accessor) + { + throw new NotImplementedException(); + } + + public static void EnableGlobalFileDataCache(this FileSystemClient fs, Memory buffer) + { + throw new NotImplementedException(); + } + + public static void DisableGlobalFileDataCache(this FileSystemClient fs) + { + throw new NotImplementedException(); + } + + public static void EnableDefaultGlobalFileDataCache(this FileSystemClient fs, Memory buffer) + { + throw new NotImplementedException(); + } + + public static bool IsDefaultGlobalFileDataCacheEnabled(this FileSystemClient fs) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/LibHac/Fs/Shim/PathBasedFileDataCache.cs b/src/LibHac/Fs/Shim/PathBasedFileDataCache.cs new file mode 100644 index 00000000..30499df5 --- /dev/null +++ b/src/LibHac/Fs/Shim/PathBasedFileDataCache.cs @@ -0,0 +1,62 @@ +using System; +using LibHac.Common; +using LibHac.Fs.Fsa; +using LibHac.Fs.Impl; + +namespace LibHac.Fs.Shim; + +public static class PathBasedFileDataCacheShim +{ + internal static UniqueLock LockPathBasedFileDataCacheEntries(this FileSystemClientImpl fs) + { + throw new NotImplementedException(); + } + + internal static void InvalidatePathBasedFileDataCacheEntries(this FileSystemClientImpl fs, + FileSystemAccessor fsAccessor) + { + throw new NotImplementedException(); + } + + internal static void InvalidatePathBasedFileDataCacheEntry(this FileSystemClientImpl fs, + FileSystemAccessor fsAccessor, in Path path) + { + throw new NotImplementedException(); + } + + internal static void InvalidatePathBasedFileDataCacheEntry(this FileSystemClientImpl fs, + FileSystemAccessor fsAccessor, in FilePathHash hash, int hashIndex) + { + throw new NotImplementedException(); + } + + internal static bool FindPathBasedFileDataCacheEntry(this FileSystemClientImpl fs, out FilePathHash outHash, + out int outHashIndex, FileSystemAccessor fsAccessor, in Path path) + { + throw new NotImplementedException(); + } + + internal static Result ReadViaPathBasedFileDataCache(this FileSystemClientImpl fs, IFile file, int openMode, + FileSystemAccessor fileSystem, in FilePathHash hash, int hashIndex, out long bytesRead, long offset, + Span buffer, in ReadOption option, ref FileDataCacheAccessResult outCacheAccessResult) + { + throw new NotImplementedException(); + } + + internal static Result WriteViaPathBasedFileDataCache(this FileSystemClientImpl fs, IFile file, int openMode, + FileSystemAccessor fileSystem, in FilePathHash hash, int hashIndex, long offset, ReadOnlySpan buffer, + in WriteOption option) + { + throw new NotImplementedException(); + } + + public static Result EnableIndividualFileDataCache(this FileSystemClient fs, U8Span path, Memory buffer) + { + throw new NotImplementedException(); + } + + public static void DisableIndividualFileDataCache(this FileSystemClient fs, U8Span path) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/LibHac/Os/ReaderWriterLock.cs b/src/LibHac/Os/ReaderWriterLock.cs index 3a2eb19b..45dfda8b 100644 --- a/src/LibHac/Os/ReaderWriterLock.cs +++ b/src/LibHac/Os/ReaderWriterLock.cs @@ -98,7 +98,7 @@ public static class ReaderWriterLockApi } } -public class ReaderWriterLock : ISharedMutex +public class ReaderWriterLock : ISharedMutex, IDisposable { public const int ReaderWriterLockCountMax = (1 << 15) - 1; public const int ReadWriteLockWaiterCountMax = (1 << 8) - 1; @@ -112,6 +112,11 @@ public class ReaderWriterLock : ISharedMutex _os.InitializeReaderWriterLock(ref _rwLock); } + public void Dispose() + { + _os.FinalizeReaderWriterLock(ref _rwLock); + } + public void AcquireReadLock() { _os.AcquireReadLock(ref _rwLock); @@ -191,4 +196,4 @@ public class ReaderWriterLock : ISharedMutex { return ref _rwLock; } -} +} \ No newline at end of file From 03ba6b01aa2b17e9bf33316237c772331b60136e Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 22 Dec 2021 18:59:01 -0700 Subject: [PATCH 12/34] Update Registrar from 12.1.0 to 13.1.0 --- src/LibHac/Fs/Fsa/Registrar.cs | 139 +++++++++++++++++++++++---- src/LibHac/Fs/Shim/SaveData.cs | 4 +- src/LibHac/Fs/Shim/SystemSaveData.cs | 4 +- 3 files changed, 123 insertions(+), 24 deletions(-) diff --git a/src/LibHac/Fs/Fsa/Registrar.cs b/src/LibHac/Fs/Fsa/Registrar.cs index 1a2e06f0..5ac0b76a 100644 --- a/src/LibHac/Fs/Fsa/Registrar.cs +++ b/src/LibHac/Fs/Fsa/Registrar.cs @@ -1,7 +1,7 @@ using System; using LibHac.Common; +using LibHac.Diag; using LibHac.Fs.Impl; -using LibHac.Util; namespace LibHac.Fs.Fsa; @@ -15,12 +15,93 @@ public interface ISaveDataAttributeGetter : IDisposable Result GetSaveDataAttribute(out SaveDataAttribute attribute); } +public interface IUnmountHookInvoker : IDisposable +{ + void Invoke(); +} + /// /// Contains functions for registering and unregistering mounted s. /// -/// Based on FS 12.1.0 (nnSdk 12.3.1) +/// Based on FS 13.1.0 (nnSdk 13.4.0) public static class Registrar { + private class UnmountHookFileSystem : IFileSystem + { + private UniqueRef _fileSystem; + private UniqueRef _unmountHookInvoker; + + public UnmountHookFileSystem(ref UniqueRef fileSystem, + ref UniqueRef unmountHookInvoker) + { + _fileSystem = new UniqueRef(ref fileSystem); + _unmountHookInvoker = new UniqueRef(ref unmountHookInvoker); + } + + public override void Dispose() + { + if (_unmountHookInvoker.HasValue) + _unmountHookInvoker.Get.Invoke(); + + _unmountHookInvoker.Destroy(); + _fileSystem.Destroy(); + + base.Dispose(); + } + + protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option) => + _fileSystem.Get.CreateFile(in path, size, option); + + protected override Result DoDeleteFile(in Path path) => _fileSystem.Get.DeleteFile(in path); + + protected override Result DoCreateDirectory(in Path path) => _fileSystem.Get.CreateDirectory(in path); + + protected override Result DoDeleteDirectory(in Path path) => _fileSystem.Get.DeleteDirectory(in path); + + protected override Result DoDeleteDirectoryRecursively(in Path path) => + _fileSystem.Get.DeleteDirectoryRecursively(in path); + + protected override Result DoCleanDirectoryRecursively(in Path path) => + _fileSystem.Get.CleanDirectoryRecursively(in path); + + protected override Result DoRenameFile(in Path currentPath, in Path newPath) => + _fileSystem.Get.RenameFile(in currentPath, in newPath); + + protected override Result DoRenameDirectory(in Path currentPath, in Path newPath) => + _fileSystem.Get.RenameDirectory(in currentPath, in newPath); + + protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path) => + _fileSystem.Get.GetEntryType(out entryType, in path); + + protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path) => + _fileSystem.Get.GetFreeSpaceSize(out freeSpace, in path); + + protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path) => + _fileSystem.Get.GetTotalSpaceSize(out totalSpace, in path); + + protected override Result DoOpenFile(ref UniqueRef outFile, in Path path, OpenMode mode) => + _fileSystem.Get.OpenFile(ref outFile, in path, mode); + + protected override Result DoOpenDirectory(ref UniqueRef outDirectory, in Path path, + OpenDirectoryMode mode) => + _fileSystem.Get.OpenDirectory(ref outDirectory, in path, mode); + + protected override Result DoCommit() => _fileSystem.Get.Commit(); + + protected override Result DoCommitProvisionally(long counter) => + _fileSystem.Get.CommitProvisionally(counter); + + protected override Result DoRollback() => _fileSystem.Get.Rollback(); + + protected override Result DoFlush() => _fileSystem.Get.Flush(); + + protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path) => + _fileSystem.Get.GetFileTimeStampRaw(out timeStamp, in path); + + protected override Result DoQueryEntry(Span outBuffer, ReadOnlySpan inBuffer, QueryId queryId, + in Path path) => _fileSystem.Get.QueryEntry(outBuffer, inBuffer, queryId, in path); + } + public static Result Register(this FileSystemClient fs, U8Span name, ref UniqueRef fileSystem) { using var attributeGetter = new UniqueRef(); @@ -51,12 +132,28 @@ public static class Registrar public static Result Register(this FileSystemClient fs, U8Span name, IMultiCommitTarget multiCommitTarget, ref UniqueRef fileSystem, ref UniqueRef mountNameGenerator, - bool useDataCache, bool usePathCache) + bool useDataCache, IStorage storageForPurgeFileDataCache, bool usePathCache) + { + using var unmountHookInvoker = new UniqueRef(); + using var attributeGetter = new UniqueRef(); + + Result rc = Register(fs, name, multiCommitTarget, ref fileSystem, ref mountNameGenerator, + ref attributeGetter.Ref(), useDataCache, storageForPurgeFileDataCache, usePathCache, + ref unmountHookInvoker.Ref()); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result Register(this FileSystemClient fs, U8Span name, IMultiCommitTarget multiCommitTarget, + ref UniqueRef fileSystem, ref UniqueRef mountNameGenerator, + bool useDataCache, IStorage storageForPurgeFileDataCache, bool usePathCache, + ref UniqueRef unmountHook) { using var attributeGetter = new UniqueRef(); Result rc = Register(fs, name, multiCommitTarget, ref fileSystem, ref mountNameGenerator, - ref attributeGetter.Ref(), useDataCache, usePathCache, new Optional()); + ref attributeGetter.Ref(), useDataCache, storageForPurgeFileDataCache, usePathCache, ref unmountHook); if (rc.IsFailure()) return rc.Miss(); return Result.Success; @@ -64,12 +161,14 @@ public static class Registrar public static Result Register(this FileSystemClient fs, U8Span name, IMultiCommitTarget multiCommitTarget, ref UniqueRef fileSystem, ref UniqueRef mountNameGenerator, - bool useDataCache, bool usePathCache, Optional dataId) + ref UniqueRef saveAttributeGetter, bool useDataCache, + IStorage storageForPurgeFileDataCache, bool usePathCache) { - using var attributeGetter = new UniqueRef(); + using var unmountHookInvoker = new UniqueRef(); Result rc = Register(fs, name, multiCommitTarget, ref fileSystem, ref mountNameGenerator, - ref attributeGetter.Ref(), useDataCache, usePathCache, dataId); + ref saveAttributeGetter, useDataCache, storageForPurgeFileDataCache, usePathCache, + ref unmountHookInvoker.Ref()); if (rc.IsFailure()) return rc.Miss(); return Result.Success; @@ -77,29 +176,29 @@ public static class Registrar public static Result Register(this FileSystemClient fs, U8Span name, IMultiCommitTarget multiCommitTarget, ref UniqueRef fileSystem, ref UniqueRef mountNameGenerator, - ref UniqueRef saveAttributeGetter, bool useDataCache, bool usePathCache) + ref UniqueRef saveAttributeGetter, bool useDataCache, + IStorage storageForPurgeFileDataCache, bool usePathCache, ref UniqueRef unmountHook) { - Result rc = Register(fs, name, multiCommitTarget, ref fileSystem, ref mountNameGenerator, - ref saveAttributeGetter, useDataCache, usePathCache, new Optional()); - if (rc.IsFailure()) return rc.Miss(); + if (useDataCache) + Assert.SdkAssert(storageForPurgeFileDataCache is not null); - return Result.Success; - } + using (var unmountHookFileSystem = + new UniqueRef(new UnmountHookFileSystem(ref fileSystem, ref unmountHook))) + { + fileSystem.Set(ref unmountHookFileSystem.Ref()); + } + + if (!fileSystem.HasValue) + return ResultFs.AllocationMemoryFailedInRegisterB.Log(); - public static Result Register(this FileSystemClient fs, U8Span name, IMultiCommitTarget multiCommitTarget, - ref UniqueRef fileSystem, ref UniqueRef mountNameGenerator, - ref UniqueRef saveAttributeGetter, bool useDataCache, bool usePathCache, - Optional dataId) - { using var accessor = new UniqueRef(new FileSystemAccessor(fs.Hos, name, multiCommitTarget, ref fileSystem, ref mountNameGenerator, ref saveAttributeGetter)); if (!accessor.HasValue) return ResultFs.AllocationMemoryFailedInRegisterB.Log(); - // accessor.Get.SetFileDataCacheAttachable(useDataCache); + accessor.Get.SetFileDataCacheAttachable(useDataCache, storageForPurgeFileDataCache); accessor.Get.SetPathBasedFileDataCacheAttachable(usePathCache); - // accessor.Get.SetDataId(dataId); Result rc = fs.Impl.Register(ref accessor.Ref()); if (rc.IsFailure()) return rc.Miss(); diff --git a/src/LibHac/Fs/Shim/SaveData.cs b/src/LibHac/Fs/Shim/SaveData.cs index 704f58d6..9a11a4cd 100644 --- a/src/LibHac/Fs/Shim/SaveData.cs +++ b/src/LibHac/Fs/Shim/SaveData.cs @@ -50,7 +50,7 @@ public static class SaveData using var mountNameGenerator = new UniqueRef(); rc = fs.Fs.Register(mountName, fileSystemAdapterRaw, ref fileSystemAdapter.Ref(), - ref mountNameGenerator.Ref(), false, true); + ref mountNameGenerator.Ref(), false, null, true); if (rc.IsFailure()) return rc.Miss(); return Result.Success; @@ -291,4 +291,4 @@ public static class SaveData return rc; } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Shim/SystemSaveData.cs b/src/LibHac/Fs/Shim/SystemSaveData.cs index c55c85c0..3de0cab7 100644 --- a/src/LibHac/Fs/Shim/SystemSaveData.cs +++ b/src/LibHac/Fs/Shim/SystemSaveData.cs @@ -87,7 +87,7 @@ public static class SystemSaveData { using var mountNameGenerator = new UniqueRef(); return fs.Register(mountName, fileSystemAdapterRaw, ref fileSystemAdapter.Ref(), - ref mountNameGenerator.Ref(), false, false); + ref mountNameGenerator.Ref(), false, null, false); } else { @@ -95,4 +95,4 @@ public static class SystemSaveData } } } -} +} \ No newline at end of file From 221e2fa608de39cc02b2cb65b6cd3774a1a7327e Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Thu, 23 Dec 2021 15:56:50 -0700 Subject: [PATCH 13/34] Add Fs.Range --- src/LibHac/Fs/Common/Range.cs | 66 +++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/LibHac/Fs/Common/Range.cs diff --git a/src/LibHac/Fs/Common/Range.cs b/src/LibHac/Fs/Common/Range.cs new file mode 100644 index 00000000..41a55d9a --- /dev/null +++ b/src/LibHac/Fs/Common/Range.cs @@ -0,0 +1,66 @@ +// ReSharper disable InconsistentNaming CheckNamespace + +using System; +using LibHac.Diag; + +namespace LibHac.Fs; + +/// +/// Represents a contiguous range of offsets in a piece of data. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) +public readonly struct Range : IEquatable, IComparable +{ + public readonly long Offset; + public readonly long Size; + + public Range(long offset, long size) + { + Offset = offset; + Size = size; + } + + public bool Contains(Range range) + { + return Offset <= range.Offset && + range.Offset + range.Size <= Offset + Size; + } + + public bool HasIntersection(Range range) + { + return Offset + Size > range.Offset && + range.Offset + range.Size > Offset; + } + + public bool IsAdjacent(Range range) + { + return Offset + Size == range.Offset || + range.Offset + range.Size == Offset; + } + + public Range MakeMerge(Range range) + { + Assert.SdkAssert(HasIntersection(range) || IsAdjacent(range)); + + long endOffsetThis = Offset + Size; + long endOffsetOther = range.Offset + range.Size; + + long startOffsetMerged = Math.Min(Offset, range.Offset); + long endOffsetMerged = Math.Max(endOffsetThis, endOffsetOther); + + return new Range(startOffsetMerged, endOffsetMerged - startOffsetMerged); + } + + // Equality is done with only the offset because this type is mainly used with RangeSet + // which will never contain multiple ranges with the same offset. + public static bool operator <(Range lhs, Range rhs) => lhs.Offset < rhs.Offset; + public static bool operator >(Range lhs, Range rhs) => lhs.Offset > rhs.Offset; + public static bool operator ==(Range lhs, Range rhs) => lhs.Offset == rhs.Offset; + public static bool operator !=(Range lhs, Range rhs) => lhs.Offset != rhs.Offset; + + public bool Equals(Range other) => Offset == other.Offset; + + public override bool Equals(object obj) => obj is Range other && Equals(other); + public int CompareTo(Range other) => Offset.CompareTo(other.Offset); + public override int GetHashCode() => Offset.GetHashCode(); +} \ No newline at end of file From 52d502a793f9e5494c87412c3771cc82160f9bbe Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Fri, 24 Dec 2021 13:29:35 -0700 Subject: [PATCH 14/34] Update UserFile functions for 13.1.0 --- src/LibHac/Common/FixedArrays/Array20.cs | 31 +++++++++ src/LibHac/Common/FixedArrays/Array60.cs | 32 ++++++++++ src/LibHac/Fs/Fsa/UserFile.cs | 73 +++++++++++++++++++++- src/LibHac/Fs/LazyLoadTypes.cs | 19 ++++++ tests/LibHac.Tests/Fs/LazyLoadTypeTests.cs | 22 +++++++ 5 files changed, 174 insertions(+), 3 deletions(-) create mode 100644 src/LibHac/Common/FixedArrays/Array20.cs create mode 100644 src/LibHac/Common/FixedArrays/Array60.cs create mode 100644 src/LibHac/Fs/LazyLoadTypes.cs create mode 100644 tests/LibHac.Tests/Fs/LazyLoadTypeTests.cs diff --git a/src/LibHac/Common/FixedArrays/Array20.cs b/src/LibHac/Common/FixedArrays/Array20.cs new file mode 100644 index 00000000..5edeba12 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array20.cs @@ -0,0 +1,31 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +[StructLayout(LayoutKind.Sequential)] +public struct Array20 +{ + public const int Length = 20; + + private Array16 _0; + private Array4 _16; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array20 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array60.cs b/src/LibHac/Common/FixedArrays/Array60.cs new file mode 100644 index 00000000..f7a04c95 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array60.cs @@ -0,0 +1,32 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +[StructLayout(LayoutKind.Sequential)] +public struct Array60 +{ + public const int Length = 60; + + private Array32 _0; + private Array16 _32; + private Array12 _48; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array60 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/UserFile.cs b/src/LibHac/Fs/Fsa/UserFile.cs index 60b6598e..89a6a630 100644 --- a/src/LibHac/Fs/Fsa/UserFile.cs +++ b/src/LibHac/Fs/Fsa/UserFile.cs @@ -7,6 +7,10 @@ using static LibHac.Fs.Impl.AccessLogStrings; namespace LibHac.Fs.Fsa; +/// +/// Contains functions for interacting with opened files. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) [SkipLocalsInit] public static class UserFile { @@ -215,12 +219,75 @@ public static class UserFile return rc; } - public static Result InvalidateCache(this FileSystemClient fs, FileHandle handle, long offset, long size) + public static Result InvalidateCache(this FileSystemClient fs, FileHandle handle) { - Result rc = Get(handle).OperateRange(Span.Empty, OperationId.InvalidateCache, offset, size, + Result rc = Get(handle).OperateRange(Span.Empty, OperationId.InvalidateCache, 0, long.MaxValue, ReadOnlySpan.Empty); fs.Impl.AbortIfNeeded(rc); return rc; } -} + + public static Result QueryUnpreparedRange(this FileSystemClient fs, out Range unpreparedRange, FileHandle handle) + { + UnsafeHelpers.SkipParamInit(out unpreparedRange); + Unsafe.SkipInit(out UnpreparedRangeInfo info); + + Result rc = Get(handle).OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.QueryUnpreparedRange, 0, 0, + ReadOnlySpan.Empty); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + unpreparedRange = info.Range; + return Result.Success; + } + + public static Result QueryUnpreparedRangeDetail(this FileSystemClient fs, + out UnpreparedRangeInfo unpreparedRangeInfo, FileHandle handle) + { + UnsafeHelpers.SkipParamInit(out unpreparedRangeInfo); + Unsafe.SkipInit(out UnpreparedRangeInfo info); + + Result rc = Get(handle).OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.QueryUnpreparedRange, 0, 0, + ReadOnlySpan.Empty); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + unpreparedRangeInfo = info; + return Result.Success; + } + + public static Result QueryLazyLoadCompletionRate(this FileSystemClient fs, out int completionRate, + FileHandle handle, int guideIndex) + { + UnsafeHelpers.SkipParamInit(out completionRate); + + Unsafe.SkipInit(out UnpreparedRangeInfo info); + var args = new LazyLoadArguments { GuideIndex = guideIndex }; + + Result rc = Get(handle).OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.QueryLazyLoadCompletionRate, + 0, 0, SpanHelpers.AsReadOnlyByteSpan(in args)); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + completionRate = info.CompletionRate; + return Result.Success; + } + + public static Result ReadyLazyLoadFileForciblyForDebug(this FileSystemClient fs, FileHandle handle, long offset, + long size) + { + Unsafe.SkipInit(out UnpreparedRangeInfo info); + + Result rc = Get(handle).OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.ReadyLazyLoadFile, + offset, size, ReadOnlySpan.Empty); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } +} \ No newline at end of file diff --git a/src/LibHac/Fs/LazyLoadTypes.cs b/src/LibHac/Fs/LazyLoadTypes.cs new file mode 100644 index 00000000..f5b2f025 --- /dev/null +++ b/src/LibHac/Fs/LazyLoadTypes.cs @@ -0,0 +1,19 @@ +using LibHac.Common.FixedArrays; + +namespace LibHac.Fs; + +public struct UnpreparedRangeInfo +{ + public Range Range; + public long FileSize; + public long PreparedRangeSize; + public long TotalReadSize; + public int CompletionRate; + public Array20 Reserved; +} + +public struct LazyLoadArguments +{ + public int GuideIndex; + public Array60 Reserved; +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Fs/LazyLoadTypeTests.cs b/tests/LibHac.Tests/Fs/LazyLoadTypeTests.cs new file mode 100644 index 00000000..4cbf9562 --- /dev/null +++ b/tests/LibHac.Tests/Fs/LazyLoadTypeTests.cs @@ -0,0 +1,22 @@ +// ReSharper disable InconsistentNaming + +using System.Runtime.CompilerServices; +using LibHac.Fs; +using Xunit; + +namespace LibHac.Tests.Fs; + +public class LazyLoadTypeTests +{ + [Fact] + public static void UnpreparedRangeInfoSizeIs0x40() + { + Assert.Equal(0x40, Unsafe.SizeOf()); + } + + [Fact] + public static void LazyLoadArgumentsSizeIs0x40() + { + Assert.Equal(0x40, Unsafe.SizeOf()); + } +} \ No newline at end of file From 5670f2fd485c1843896416cc7a88e345d84dc0f8 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Fri, 24 Dec 2021 16:25:06 -0700 Subject: [PATCH 15/34] Add summary docs to ReadOnlySpan strings --- src/LibHac/Fs/AccessLog.cs | 58 +++++++++++++++++++-- src/LibHac/Fs/Fsa/FileSystemAccessor.cs | 14 +++++ src/LibHac/FsSrv/AccessLogService.cs | 3 +- src/LibHac/FsSrv/Impl/MultiCommitManager.cs | 4 +- 4 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/LibHac/Fs/AccessLog.cs b/src/LibHac/Fs/AccessLog.cs index 48dcf385..b1a68511 100644 --- a/src/LibHac/Fs/AccessLog.cs +++ b/src/LibHac/Fs/AccessLog.cs @@ -730,23 +730,29 @@ namespace LibHac.Fs.Impl internal static class AccessLogStrings { + /// "$fs" public static ReadOnlySpan FsModuleName => // "$fs" new[] { (byte)'$', (byte)'f', (byte)'s' }; - public static ReadOnlySpan LogLibHacVersion => // "0.13.0" + /// "0.15.0" + public static ReadOnlySpan LogLibHacVersion => // "0.15.0" new[] { - (byte)'0', (byte)'.', (byte)'1', (byte)'3', (byte)'.', (byte)'0' + (byte)'0', (byte)'.', (byte)'1', (byte)'5', (byte)'.', (byte)'0' }; + /// """ public static byte LogQuote => (byte)'"'; + /// "true" public static ReadOnlySpan LogTrue => // "true" new[] { (byte)'t', (byte)'r', (byte)'u', (byte)'e' }; + /// "false" public static ReadOnlySpan LogFalse => // "false" new[] { (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e' }; + /// ", entry_buffer_count: " public static ReadOnlySpan LogEntryBufferCount => // ", entry_buffer_count: " new[] { @@ -755,6 +761,7 @@ namespace LibHac.Fs.Impl (byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)':', (byte)' ' }; + /// ", entry_count: " public static ReadOnlySpan LogEntryCount => // ", entry_count: " new[] { @@ -762,6 +769,7 @@ namespace LibHac.Fs.Impl (byte)'c', (byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)':', (byte)' ' }; + /// ", offset: " public static ReadOnlySpan LogOffset => // ", offset: " new[] { @@ -769,12 +777,14 @@ namespace LibHac.Fs.Impl (byte)':', (byte)' ' }; + /// ", size: " public static ReadOnlySpan LogSize => // ", size: " new[] { (byte)',', (byte)' ', (byte)'s', (byte)'i', (byte)'z', (byte)'e', (byte)':', (byte)' ' }; + /// ", read_size: " public static ReadOnlySpan LogReadSize => // ", read_size: " new[] { @@ -782,6 +792,7 @@ namespace LibHac.Fs.Impl (byte)'i', (byte)'z', (byte)'e', (byte)':', (byte)' ' }; + /// ", write_option: Flush" public static ReadOnlySpan LogWriteOptionFlush => // ", write_option: Flush" new[] { @@ -790,6 +801,7 @@ namespace LibHac.Fs.Impl (byte)'F', (byte)'l', (byte)'u', (byte)'s', (byte)'h' }; + /// ", open_mode: 0x" public static ReadOnlySpan LogOpenMode => // ", open_mode: 0x" new[] { @@ -797,6 +809,7 @@ namespace LibHac.Fs.Impl (byte)'o', (byte)'d', (byte)'e', (byte)':', (byte)' ', (byte)'0', (byte)'x' }; + /// ", path: "" public static ReadOnlySpan LogPath => // ", path: "" new[] { @@ -804,6 +817,7 @@ namespace LibHac.Fs.Impl (byte)'"' }; + /// "", new_path: "" public static ReadOnlySpan LogNewPath => // "", new_path: "" new[] { @@ -811,6 +825,7 @@ namespace LibHac.Fs.Impl (byte)'a', (byte)'t', (byte)'h', (byte)':', (byte)' ', (byte)'"' }; + /// "", entry_type: " public static ReadOnlySpan LogEntryType => // "", entry_type: " new[] { @@ -818,6 +833,7 @@ namespace LibHac.Fs.Impl (byte)'_', (byte)'t', (byte)'y', (byte)'p', (byte)'e', (byte)':', (byte)' ' }; + /// ", name: "" public static ReadOnlySpan LogName => // ", name: "" new[] { @@ -825,6 +841,7 @@ namespace LibHac.Fs.Impl (byte)'"' }; + /// "", commit_option: 0x" public static ReadOnlySpan LogCommitOption => // "", commit_option: 0x" new[] { @@ -833,6 +850,7 @@ namespace LibHac.Fs.Impl (byte)':', (byte)' ', (byte)'0', (byte)'x' }; + /// "", is_mounted: "" public static ReadOnlySpan LogIsMounted => // "", is_mounted: "" new[] { @@ -840,6 +858,7 @@ namespace LibHac.Fs.Impl (byte)'u', (byte)'n', (byte)'t', (byte)'e', (byte)'d', (byte)':', (byte)' ', (byte)'"' }; + /// ", applicationid: 0x" public static ReadOnlySpan LogApplicationId => // ", applicationid: 0x" new[] { @@ -848,6 +867,7 @@ namespace LibHac.Fs.Impl (byte)' ', (byte)'0', (byte)'x' }; + /// ", programid: 0x" public static ReadOnlySpan LogProgramId => // ", programid: 0x" new[] { @@ -855,6 +875,7 @@ namespace LibHac.Fs.Impl (byte)'m', (byte)'i', (byte)'d', (byte)':', (byte)' ', (byte)'0', (byte)'x' }; + /// ", dataid: 0x" public static ReadOnlySpan LogDataId => // ", dataid: 0x" new[] { @@ -862,6 +883,7 @@ namespace LibHac.Fs.Impl (byte)':', (byte)' ', (byte)'0', (byte)'x' }; + /// ", bispartitionid: " public static ReadOnlySpan LogBisPartitionId => // ", bispartitionid: " new[] { @@ -870,6 +892,7 @@ namespace LibHac.Fs.Impl (byte)':', (byte)' ' }; + /// ", content_type: " public static ReadOnlySpan LogContentType => // ", content_type: " new[] { @@ -877,6 +900,7 @@ namespace LibHac.Fs.Impl (byte)'t', (byte)'_', (byte)'t', (byte)'y', (byte)'p', (byte)'e', (byte)':', (byte)' ' }; + /// ", contentstorageid: " public static ReadOnlySpan LogContentStorageId => // ", contentstorageid: " new[] { @@ -885,6 +909,7 @@ namespace LibHac.Fs.Impl (byte)'i', (byte)'d', (byte)':', (byte)' ' }; + /// ", gamecard_handle: 0x" public static ReadOnlySpan LogGameCardHandle => // ", gamecard_handle: 0x" new[] { @@ -893,6 +918,7 @@ namespace LibHac.Fs.Impl (byte)'e', (byte)':', (byte)' ', (byte)'0', (byte)'x' }; + /// ", gamecard_partition: " public static ReadOnlySpan LogGameCardPartition => // ", gamecard_partition: " new[] { @@ -901,6 +927,7 @@ namespace LibHac.Fs.Impl (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)':', (byte)' ' }; + /// ", mount_host_option: " public static ReadOnlySpan LogMountHostOption => // ", mount_host_option: " new[] { @@ -909,6 +936,7 @@ namespace LibHac.Fs.Impl (byte)'i', (byte)'o', (byte)'n', (byte)':', (byte)' ' }; + /// ", root_path: "" public static ReadOnlySpan LogRootPath => // ", root_path: "" new[] { @@ -916,6 +944,7 @@ namespace LibHac.Fs.Impl (byte)'a', (byte)'t', (byte)'h', (byte)':', (byte)' ', (byte)'"' }; + /// ", userid: 0x" public static ReadOnlySpan LogUserId => // ", userid: 0x" new[] { @@ -923,6 +952,7 @@ namespace LibHac.Fs.Impl (byte)':', (byte)' ', (byte)'0', (byte)'x' }; + /// ", index: " public static ReadOnlySpan LogIndex => // ", index: " new[] { @@ -930,6 +960,7 @@ namespace LibHac.Fs.Impl (byte)' ' }; + /// ", save_data_owner_id: 0x" public static ReadOnlySpan LogSaveDataOwnerId => // ", save_data_owner_id: 0x" new[] { @@ -938,6 +969,7 @@ namespace LibHac.Fs.Impl (byte)'r', (byte)'_', (byte)'i', (byte)'d', (byte)':', (byte)' ', (byte)'0', (byte)'x' }; + /// ", save_data_size: " public static ReadOnlySpan LogSaveDataSize => // ", save_data_size: " new[] { @@ -946,6 +978,7 @@ namespace LibHac.Fs.Impl (byte)':', (byte)' ' }; + /// ", save_data_journal_size: " public static ReadOnlySpan LogSaveDataJournalSize => // ", save_data_journal_size: " new[] { @@ -955,6 +988,7 @@ namespace LibHac.Fs.Impl (byte)':', (byte)' ' }; + /// ", save_data_flags: 0x" public static ReadOnlySpan LogSaveDataFlags => // ", save_data_flags: 0x" new[] { @@ -963,6 +997,7 @@ namespace LibHac.Fs.Impl (byte)'s', (byte)':', (byte)' ', (byte)'0', (byte)'x' }; + /// ", savedataid: 0x" public static ReadOnlySpan LogSaveDataId => // ", savedataid: 0x" new[] { @@ -970,6 +1005,7 @@ namespace LibHac.Fs.Impl (byte)'t', (byte)'a', (byte)'i', (byte)'d', (byte)':', (byte)' ', (byte)'0', (byte)'x' }; + /// ", savedataspaceid: " public static ReadOnlySpan LogSaveDataSpaceId => // ", savedataspaceid: " new[] { @@ -978,6 +1014,7 @@ namespace LibHac.Fs.Impl (byte)'d', (byte)':', (byte)' ' }; + /// ", save_data_time_stamp: " public static ReadOnlySpan LogSaveDataTimeStamp => // ", save_data_time_stamp: " new[] { @@ -986,6 +1023,7 @@ namespace LibHac.Fs.Impl (byte)'_', (byte)'s', (byte)'t', (byte)'a', (byte)'m', (byte)'p', (byte)':', (byte)' ' }; + /// ", save_data_commit_id: 0x" public static ReadOnlySpan LogSaveDataCommitId => // ", save_data_commit_id: 0x" new[] { @@ -995,6 +1033,7 @@ namespace LibHac.Fs.Impl (byte)'x' }; + /// ", restore_flag: " public static ReadOnlySpan LogRestoreFlag => // ", restore_flag: " new[] { @@ -1002,6 +1041,7 @@ namespace LibHac.Fs.Impl (byte)'e', (byte)'_', (byte)'f', (byte)'l', (byte)'a', (byte)'g', (byte)':', (byte)' ' }; + /// "sdk_version: " public static ReadOnlySpan LogSdkVersion => // "sdk_version: " new[] { @@ -1009,15 +1049,18 @@ namespace LibHac.Fs.Impl (byte)'i', (byte)'o', (byte)'n', (byte)':', (byte)' ' }; + /// ", spec: " public static ReadOnlySpan LogSpec => // ", spec: " new[] { (byte)',', (byte)' ', (byte)'s', (byte)'p', (byte)'e', (byte)'c', (byte)':', (byte)' ' }; + /// "NX" public static ReadOnlySpan LogNx => // "NX" new[] { (byte)'N', (byte)'X' }; + /// ", program_index: " public static ReadOnlySpan LogProgramIndex => // ", program_index: " new[] { @@ -1026,6 +1069,7 @@ namespace LibHac.Fs.Impl (byte)' ' }; + /// ", for_system: true" public static ReadOnlySpan LogForSystem => // ", for_system: true" new[] { @@ -1034,6 +1078,7 @@ namespace LibHac.Fs.Impl (byte)'u', (byte)'e' }; + /// ""FS_ACCESS: { " public static ReadOnlySpan LogLineStart => // "FS_ACCESS: { " new[] { @@ -1041,21 +1086,25 @@ namespace LibHac.Fs.Impl (byte)'S', (byte)':', (byte)' ', (byte)'{', (byte)' ' }; + /// " }\n" public static ReadOnlySpan LogLineEnd => // " }\n" new[] { (byte)' ', (byte)'}', (byte)'\n' }; + /// "start: " public static ReadOnlySpan LogStart => // "start: " new[] { (byte)'s', (byte)'t', (byte)'a', (byte)'r', (byte)'t', (byte)':', (byte)' ' }; + /// ", end: " public static ReadOnlySpan LogEnd => // ", end: " new[] { (byte)',', (byte)' ', (byte)'e', (byte)'n', (byte)'d', (byte)':', (byte)' ' }; + /// ", result: 0x" public static ReadOnlySpan LogResult => // ", result: 0x" new[] { @@ -1063,6 +1112,7 @@ namespace LibHac.Fs.Impl (byte)':', (byte)' ', (byte)'0', (byte)'x' }; + /// ", handle: 0x" public static ReadOnlySpan LogHandle => // ", handle: 0x" new[] { @@ -1070,6 +1120,7 @@ namespace LibHac.Fs.Impl (byte)':', (byte)' ', (byte)'0', (byte)'x' }; + /// ", priority: " public static ReadOnlySpan LogPriority => // ", priority: " new[] { @@ -1077,6 +1128,7 @@ namespace LibHac.Fs.Impl (byte)'t', (byte)'y', (byte)':', (byte)' ' }; + /// ", function: "" public static ReadOnlySpan LogFunction => // ", function: "" new[] { @@ -1084,4 +1136,4 @@ namespace LibHac.Fs.Impl (byte)'o', (byte)'n', (byte)':', (byte)' ', (byte)'"' }; } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/FileSystemAccessor.cs b/src/LibHac/Fs/Fsa/FileSystemAccessor.cs index d24c7b91..5f13dbfe 100644 --- a/src/LibHac/Fs/Fsa/FileSystemAccessor.cs +++ b/src/LibHac/Fs/Fsa/FileSystemAccessor.cs @@ -525,8 +525,10 @@ internal class FileSystemAccessor : IDisposable Remove(_openDirectories, directory); } + /// "$fs" private static ReadOnlySpan LogFsModuleName => new[] { (byte)'$', (byte)'f', (byte)'s' }; // "$fs" + /// "------ FS ERROR INFORMATION ------\n" private static ReadOnlySpan LogFsErrorInfo => // "------ FS ERROR INFORMATION ------\n" new[] { @@ -537,6 +539,7 @@ internal class FileSystemAccessor : IDisposable (byte)'-', (byte)'-', (byte)'\\', (byte)'n' }; + /// "Error: File not closed" private static ReadOnlySpan LogFileNotClosed => // "Error: File not closed" new[] { @@ -545,6 +548,7 @@ internal class FileSystemAccessor : IDisposable (byte)'c', (byte)'l', (byte)'o', (byte)'s', (byte)'e', (byte)'d' }; + /// "Error: Directory not closed" private static ReadOnlySpan LogDirectoryNotClosed => // "Error: Directory not closed" new[] { @@ -554,6 +558,7 @@ internal class FileSystemAccessor : IDisposable (byte)'s', (byte)'e', (byte)'d' }; + /// " (mount_name: "" private static ReadOnlySpan LogMountName => // " (mount_name: "" new[] { @@ -561,6 +566,7 @@ internal class FileSystemAccessor : IDisposable (byte)'n', (byte)'a', (byte)'m', (byte)'e', (byte)':', (byte)' ', (byte)'"' }; + /// "", count: " private static ReadOnlySpan LogCount => // "", count: " new[] { @@ -568,10 +574,13 @@ internal class FileSystemAccessor : IDisposable (byte)':', (byte)' ' }; + /// ")\n" public static ReadOnlySpan LogLineEnd => new[] { (byte)')', (byte)'\\', (byte)'n' }; // ")\n" + /// " | " public static ReadOnlySpan LogOrOperator => new[] { (byte)' ', (byte)'|', (byte)' ' }; // " | " + /// "OpenMode_Read" private static ReadOnlySpan LogOpenModeRead => // "OpenMode_Read" new[] { @@ -579,6 +588,7 @@ internal class FileSystemAccessor : IDisposable (byte)'_', (byte)'R', (byte)'e', (byte)'a', (byte)'d' }; + /// "OpenMode_Write" private static ReadOnlySpan LogOpenModeWrite => // "OpenMode_Write" new[] { @@ -586,6 +596,7 @@ internal class FileSystemAccessor : IDisposable (byte)'_', (byte)'W', (byte)'r', (byte)'i', (byte)'t', (byte)'e' }; + /// "OpenMode_AllowAppend" private static ReadOnlySpan LogOpenModeAppend => // "OpenMode_AllowAppend" new[] { @@ -594,6 +605,7 @@ internal class FileSystemAccessor : IDisposable (byte)'p', (byte)'e', (byte)'n', (byte)'d' }; + /// " handle: 0x" private static ReadOnlySpan LogHandle => // " handle: 0x" new[] { @@ -601,6 +613,7 @@ internal class FileSystemAccessor : IDisposable (byte)'d', (byte)'l', (byte)'e', (byte)':', (byte)' ', (byte)'0', (byte)'x' }; + /// ", open_mode: " private static ReadOnlySpan LogOpenMode => // ", open_mode: " new[] { @@ -608,6 +621,7 @@ internal class FileSystemAccessor : IDisposable (byte)'o', (byte)'d', (byte)'e', (byte)':', (byte)' ' }; + /// ", size:" private static ReadOnlySpan LogSize => // ", size: " new[] { diff --git a/src/LibHac/FsSrv/AccessLogService.cs b/src/LibHac/FsSrv/AccessLogService.cs index 549bebd4..4808ab15 100644 --- a/src/LibHac/FsSrv/AccessLogService.cs +++ b/src/LibHac/FsSrv/AccessLogService.cs @@ -63,6 +63,7 @@ internal readonly struct AccessLogService return _serviceImpl.GetProgramInfo(out programInfo, _processId); } + /// "FS_ACCESS: { multi_program_tag: true }\n" private static ReadOnlySpan MultiProgramTag => // FS_ACCESS: { multi_program_tag: true }\n new[] { @@ -72,4 +73,4 @@ internal readonly struct AccessLogService (byte) 'a', (byte) 'm', (byte) '_', (byte) 't', (byte) 'a', (byte) 'g', (byte) ':', (byte) ' ', (byte) 't', (byte) 'r', (byte) 'u', (byte) 'e', (byte) ' ', (byte) '}', (byte) '\n' }; -} +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/Impl/MultiCommitManager.cs b/src/LibHac/FsSrv/Impl/MultiCommitManager.cs index d3d849c8..7c847132 100644 --- a/src/LibHac/FsSrv/Impl/MultiCommitManager.cs +++ b/src/LibHac/FsSrv/Impl/MultiCommitManager.cs @@ -55,7 +55,7 @@ internal class MultiCommitManager : IMultiCommitManager private const int CurrentCommitContextVersion = 0x10000; private const long CommitContextFileSize = 0x200; - // /commitinfo + /// "/commitinfo" private static ReadOnlySpan CommitContextFileName => new[] { (byte)'/', (byte)'c', (byte)'o', (byte)'m', (byte)'m', (byte)'i', (byte)'t', (byte)'i', (byte)'n', (byte)'f', (byte)'o' }; @@ -640,4 +640,4 @@ internal class MultiCommitManager : IMultiCommitManager return Result.Success; } } -} +} \ No newline at end of file From ac378020dc71e0d44ca3b0949a8ef46f1a99622f Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Fri, 24 Dec 2021 16:43:34 -0700 Subject: [PATCH 16/34] Ensure UserDirectory and UserFileSystem are updated to 13.1.0 --- src/LibHac/Fs/Fsa/UserDirectory.cs | 6 ++- src/LibHac/Fs/Fsa/UserFileSystem.cs | 70 ++++++++++++++++------------- 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/src/LibHac/Fs/Fsa/UserDirectory.cs b/src/LibHac/Fs/Fsa/UserDirectory.cs index 2b50045d..cb24cdc6 100644 --- a/src/LibHac/Fs/Fsa/UserDirectory.cs +++ b/src/LibHac/Fs/Fsa/UserDirectory.cs @@ -7,6 +7,10 @@ using static LibHac.Fs.Impl.AccessLogStrings; namespace LibHac.Fs.Fsa; +/// +/// Contains functions for interacting with opened directories. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) [SkipLocalsInit] public static class UserDirectory { @@ -82,4 +86,4 @@ public static class UserDirectory Get(handle).Dispose(); } } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/UserFileSystem.cs b/src/LibHac/Fs/Fsa/UserFileSystem.cs index c99c1fab..8654ab08 100644 --- a/src/LibHac/Fs/Fsa/UserFileSystem.cs +++ b/src/LibHac/Fs/Fsa/UserFileSystem.cs @@ -5,11 +5,16 @@ using LibHac.Fs.Impl; using LibHac.Fs.Shim; using LibHac.FsSrv.Sf; using LibHac.Os; +using LibHac.Util; using static LibHac.Fs.Impl.AccessLogStrings; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; namespace LibHac.Fs.Fsa; +/// +/// Contains functions for interacting with mounted file systems. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) [SkipLocalsInit] public static class UserFileSystem { @@ -228,7 +233,7 @@ public static class UserFileSystem return rc; } - public static Result RenameFile(this FileSystemClient fs, U8Span oldPath, U8Span newPath) + public static Result RenameFile(this FileSystemClient fs, U8Span currentPath, U8Span newPath) { Result rc; U8Span currentSubPath, newSubPath; @@ -239,18 +244,18 @@ public static class UserFileSystem if (fs.Impl.IsEnabledAccessLog()) { Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.FindFileSystem(out currentFileSystem, out currentSubPath, oldPath); + rc = fs.Impl.FindFileSystem(out currentFileSystem, out currentSubPath, currentPath); Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogPath).Append(oldPath).Append(LogNewPath).Append(newPath).Append((byte)'"'); + sb.Append(LogPath).Append(currentPath).Append(LogNewPath).Append(newPath).Append((byte)'"'); logBuffer = sb.Buffer; fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer)); } else { - rc = fs.Impl.FindFileSystem(out currentFileSystem, out currentSubPath, oldPath); + rc = fs.Impl.FindFileSystem(out currentFileSystem, out currentSubPath, currentPath); } fs.Impl.AbortIfNeeded(rc); if (rc.IsFailure()) return rc; @@ -294,7 +299,7 @@ public static class UserFileSystem return rc; } - public static Result RenameDirectory(this FileSystemClient fs, U8Span oldPath, U8Span newPath) + public static Result RenameDirectory(this FileSystemClient fs, U8Span currentPath, U8Span newPath) { Result rc; U8Span currentSubPath, newSubPath; @@ -305,18 +310,18 @@ public static class UserFileSystem if (fs.Impl.IsEnabledAccessLog()) { Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.FindFileSystem(out currentFileSystem, out currentSubPath, oldPath); + rc = fs.Impl.FindFileSystem(out currentFileSystem, out currentSubPath, currentPath); Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogPath).Append(oldPath).Append(LogNewPath).Append(newPath).Append((byte)'"'); + sb.Append(LogPath).Append(currentPath).Append(LogNewPath).Append(newPath).Append((byte)'"'); logBuffer = sb.Buffer; fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer)); } else { - rc = fs.Impl.FindFileSystem(out currentFileSystem, out currentSubPath, oldPath); + rc = fs.Impl.FindFileSystem(out currentFileSystem, out currentSubPath, currentPath); } fs.Impl.AbortIfNeeded(rc); if (rc.IsFailure()) return rc; @@ -421,19 +426,18 @@ public static class UserFileSystem FileSystemAccessor fileSystem; Span logBuffer = stackalloc byte[0x300]; + static Result FindImpl(FileSystemClient fs, U8Span path, out FileSystemAccessor fileSystem, ref U8Span subPath) + { + if (fs.Impl.IsValidMountName(path)) + return fs.Impl.Find(out fileSystem, path); + else + return fs.Impl.FindFileSystem(out fileSystem, out subPath, path); + } + if (fs.Impl.IsEnabledAccessLog()) { Tick start = fs.Hos.Os.GetSystemTick(); - if (fs.Impl.IsValidMountName(path)) - { - rc = fs.Impl.Find(out fileSystem, path); - if (rc.IsFailure()) return rc; - } - else - { - rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path); - if (rc.IsFailure()) return rc; - } + rc = FindImpl(fs, path, out fileSystem, ref subPath); Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); @@ -445,24 +449,25 @@ public static class UserFileSystem } else { - if (fs.Impl.IsValidMountName(path)) - { - rc = fs.Impl.Find(out fileSystem, path); - if (rc.IsFailure()) return rc; - } - else - { - rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path); - if (rc.IsFailure()) return rc; - } + rc = FindImpl(fs, path, out fileSystem, ref subPath); } fs.Impl.AbortIfNeeded(rc); if (rc.IsFailure()) return rc; + static Result GetImpl(out long freeSpace, FileSystemAccessor fileSystem, U8Span subPath) + { + UnsafeHelpers.SkipParamInit(out freeSpace); + + if (subPath.IsEmpty() && StringUtils.Compare(subPath, new[] { (byte)'/' }) != 0) + return ResultFs.InvalidMountName.Log(); + + return fileSystem.GetFreeSpaceSize(out freeSpace, new U8Span(new[] { (byte)'/' })); + } + if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog()) { Tick start = fs.Hos.Os.GetSystemTick(); - rc = fileSystem.GetFreeSpaceSize(out freeSpace, subPath); + rc = GetImpl(out freeSpace, fileSystem, subPath); Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); @@ -474,7 +479,7 @@ public static class UserFileSystem } else { - rc = fileSystem.GetFreeSpaceSize(out freeSpace, subPath); + rc = GetImpl(out freeSpace, fileSystem, subPath); } fs.Impl.AbortIfNeeded(rc); return rc; @@ -635,6 +640,9 @@ public static class UserFileSystem if (mountNames.Length > 10) return ResultFs.InvalidCommitNameCount.Log(); + if (mountNames.Length < 10) + return ResultFs.InvalidCommitNameCount.Log(); + if (mountNames.Length == 0) return Result.Success; @@ -744,4 +752,4 @@ public static class UserFileSystem { return CommitImpl(fs, mountName); } -} +} \ No newline at end of file From 3940ca9d766c945c6f3582a1f58a695784d2a61d Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sat, 25 Dec 2021 01:26:52 -0700 Subject: [PATCH 17/34] Ensure UserFileSystemForDebug/Private are updated to 13.1.0 --- src/LibHac/Common/FixedArrays/Array512.cs | 31 ++++++++++++++++++++ src/LibHac/Common/FixedArrays/Array769.cs | 32 +++++++++++++++++++++ src/LibHac/Fs/Fsa/UserFileSystemForDebug.cs | 30 +++++++++++++++++++ src/LibHac/Fs/Fsa/UserFileSystemPrivate.cs | 32 +++++++++++++++++---- src/LibHac/Fs/LazyLoadTypes.cs | 5 ++++ tests/LibHac.Tests/Fs/LazyLoadTypeTests.cs | 6 ++++ 6 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 src/LibHac/Common/FixedArrays/Array512.cs create mode 100644 src/LibHac/Common/FixedArrays/Array769.cs create mode 100644 src/LibHac/Fs/Fsa/UserFileSystemForDebug.cs diff --git a/src/LibHac/Common/FixedArrays/Array512.cs b/src/LibHac/Common/FixedArrays/Array512.cs new file mode 100644 index 00000000..64417923 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array512.cs @@ -0,0 +1,31 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +[StructLayout(LayoutKind.Sequential)] +public struct Array512 +{ + public const int Length = 512; + + private Array256 _0; + private Array256 _256; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array512 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array769.cs b/src/LibHac/Common/FixedArrays/Array769.cs new file mode 100644 index 00000000..2e0e3167 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array769.cs @@ -0,0 +1,32 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +[StructLayout(LayoutKind.Sequential)] +public struct Array769 +{ + public const int Length = 769; + + private Array512 _0; + private Array256 _512; + private Array1 _768; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array769 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/UserFileSystemForDebug.cs b/src/LibHac/Fs/Fsa/UserFileSystemForDebug.cs new file mode 100644 index 00000000..08899a91 --- /dev/null +++ b/src/LibHac/Fs/Fsa/UserFileSystemForDebug.cs @@ -0,0 +1,30 @@ +using LibHac.Common; +using LibHac.Fs.Impl; + +namespace LibHac.Fs.Fsa; + +/// +/// Contains functions meant for debug use for interacting with mounted file systems. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) +public static class UserFileSystemForDebug +{ + internal static Result GetFileTimeStampRawForDebug(this FileSystemClientImpl fs, out FileTimeStampRaw timeStamp, + U8Span path) + { + UnsafeHelpers.SkipParamInit(out timeStamp); + + Result rc = fs.FindFileSystem(out FileSystemAccessor fileSystem, out U8Span subPath, path); + if (rc.IsFailure()) return rc.Miss(); + + return fileSystem.GetFileTimeStampRaw(out timeStamp, subPath); + } + + public static Result GetFileTimeStampRawForDebug(this FileSystemClient fs, out FileTimeStampRaw timeStamp, + U8Span path) + { + Result rc = fs.Impl.GetFileTimeStampRawForDebug(out timeStamp, path); + fs.Impl.AbortIfNeeded(rc); + return rc; + } +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/UserFileSystemPrivate.cs b/src/LibHac/Fs/Fsa/UserFileSystemPrivate.cs index 5ae24068..dde11262 100644 --- a/src/LibHac/Fs/Fsa/UserFileSystemPrivate.cs +++ b/src/LibHac/Fs/Fsa/UserFileSystemPrivate.cs @@ -6,9 +6,13 @@ using static LibHac.Fs.Impl.AccessLogStrings; namespace LibHac.Fs.Fsa; +/// +/// Contains functions meant for internal use for interacting with mounted file systems. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) public static class UserFileSystemPrivate { - public static Result CreateFile(this FileSystemClient fs, U8Span path, long size, CreateFileOptions options) + public static Result CreateFile(this FileSystemClient fs, U8Span path, long size, CreateFileOptions option) { Result rc; U8Span subPath; @@ -37,7 +41,7 @@ public static class UserFileSystemPrivate if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog()) { Tick start = fs.Hos.Os.GetSystemTick(); - rc = fileSystem.CreateFile(subPath, size, options); + rc = fileSystem.CreateFile(subPath, size, option); Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); @@ -48,7 +52,7 @@ public static class UserFileSystemPrivate } else { - rc = fileSystem.CreateFile(subPath, size, options); + rc = fileSystem.CreateFile(subPath, size, option); } fs.Impl.AbortIfNeeded(rc); return rc; @@ -62,7 +66,7 @@ public static class UserFileSystemPrivate fs.Impl.AbortIfNeeded(rc); if (rc.IsFailure()) return rc; - rc = fileSystem.GetFreeSpaceSize(out totalSpace, subPath); + rc = fileSystem.GetTotalSpaceSize(out totalSpace, subPath); fs.Impl.AbortIfNeeded(rc); return rc; } @@ -73,8 +77,24 @@ public static class UserFileSystemPrivate fs.Impl.AbortIfNeeded(rc); if (rc.IsFailure()) return rc; - rc = fileSystem.QueryEntry(Span.Empty, ReadOnlySpan.Empty, QueryId.SetConcatenationFileAttribute, subPath); + rc = fileSystem.QueryEntry(Span.Empty, ReadOnlySpan.Empty, QueryId.SetConcatenationFileAttribute, + subPath); fs.Impl.AbortIfNeeded(rc); return rc; } -} + + public static Result QueryUnpreparedFileInformation(this FileSystemClient fs, out UnpreparedFileInformation info, + U8Span path) + { + UnsafeHelpers.SkipParamInit(out info); + + Result rc = fs.Impl.FindFileSystem(out FileSystemAccessor fileSystem, out _, path); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + rc = fileSystem.QueryEntry(SpanHelpers.AsByteSpan(ref info), ReadOnlySpan.Empty, + QueryId.QueryUnpreparedFileInformation, new U8Span(new[] { (byte)'/' })); + fs.Impl.AbortIfNeeded(rc); + return rc; + } +} \ No newline at end of file diff --git a/src/LibHac/Fs/LazyLoadTypes.cs b/src/LibHac/Fs/LazyLoadTypes.cs index f5b2f025..50551b64 100644 --- a/src/LibHac/Fs/LazyLoadTypes.cs +++ b/src/LibHac/Fs/LazyLoadTypes.cs @@ -12,6 +12,11 @@ public struct UnpreparedRangeInfo public Array20 Reserved; } +public struct UnpreparedFileInformation +{ + public Array769 Path; +} + public struct LazyLoadArguments { public int GuideIndex; diff --git a/tests/LibHac.Tests/Fs/LazyLoadTypeTests.cs b/tests/LibHac.Tests/Fs/LazyLoadTypeTests.cs index 4cbf9562..45fe957f 100644 --- a/tests/LibHac.Tests/Fs/LazyLoadTypeTests.cs +++ b/tests/LibHac.Tests/Fs/LazyLoadTypeTests.cs @@ -14,6 +14,12 @@ public class LazyLoadTypeTests Assert.Equal(0x40, Unsafe.SizeOf()); } + [Fact] + public static void UnpreparedFileInformationSizeIs0x301() + { + Assert.Equal(0x301, Unsafe.SizeOf()); + } + [Fact] public static void LazyLoadArgumentsSizeIs0x40() { From f1d704b8f8951e528080a43bb969d36e49452b7a Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Mon, 27 Dec 2021 18:12:43 -0700 Subject: [PATCH 18/34] Mark the versions of IFileSystem interfaces --- src/LibHac/Fs/Fsa/IDirectory.cs | 7 +-- src/LibHac/Fs/Fsa/IFile.cs | 43 +++++++++--------- src/LibHac/Fs/Fsa/IFileSystem.cs | 77 ++++++++++++++++---------------- 3 files changed, 64 insertions(+), 63 deletions(-) diff --git a/src/LibHac/Fs/Fsa/IDirectory.cs b/src/LibHac/Fs/Fsa/IDirectory.cs index 4207cd41..389a7109 100644 --- a/src/LibHac/Fs/Fsa/IDirectory.cs +++ b/src/LibHac/Fs/Fsa/IDirectory.cs @@ -6,8 +6,11 @@ namespace LibHac.Fs.Fsa; /// /// Provides an interface for enumerating the child entries of a directory. /// +/// Based on FS 13.1.0 (nnSdk 13.4.0) public abstract class IDirectory : IDisposable { + public virtual void Dispose() { } + /// /// Retrieves the next entries that this directory contains. Does not search subdirectories. /// @@ -43,6 +46,4 @@ public abstract class IDirectory : IDisposable protected abstract Result DoRead(out long entriesRead, Span entryBuffer); protected abstract Result DoGetEntryCount(out long entryCount); - - public virtual void Dispose() { } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/IFile.cs b/src/LibHac/Fs/Fsa/IFile.cs index c9417e07..dac96ba5 100644 --- a/src/LibHac/Fs/Fsa/IFile.cs +++ b/src/LibHac/Fs/Fsa/IFile.cs @@ -10,17 +10,20 @@ namespace LibHac.Fs.Fsa; /// /// is similar to , and has a few main differences: /// -/// - allows an to be set that controls read, write -/// and append permissions for the file. +/// - allows an to be set that controls read, write +/// and append permissions for the file. /// -/// - If the cannot read or write as many bytes as requested, it will read -/// or write as many bytes as it can and return that number of bytes to the caller. +/// - If the cannot read or write as many bytes as requested, it will read +/// or write as many bytes as it can and return that number of bytes to the caller. /// -/// - If is called on an offset past the end of the , +/// - If is called on an offset past the end of the , /// the mode is set and the file supports expansion, -/// the file will be expanded so that it is large enough to contain the written data. +/// the file will be expanded so that it is large enough to contain the written data. +/// Based on FS 13.1.0 (nnSdk 13.4.0) public abstract class IFile : IDisposable { + public virtual void Dispose() { } + /// /// Reads a sequence of bytes from the current . /// @@ -174,15 +177,6 @@ public abstract class IFile : IDisposable return Result.Success; } - protected Result DrySetSize(long size, OpenMode openMode) - { - // Check that we can write. - if (!openMode.HasFlag(OpenMode.Write)) - return ResultFs.WriteUnpermitted.Log(); - - return Result.Success; - } - protected Result DryWrite(out bool needsAppend, long offset, long size, in WriteOption option, OpenMode openMode) { @@ -196,6 +190,8 @@ public abstract class IFile : IDisposable Result rc = GetSize(out long fileSize); if (rc.IsFailure()) return rc; + needsAppend = false; + if (fileSize < offset + size) { if (!openMode.HasFlag(OpenMode.AllowAppend)) @@ -203,10 +199,15 @@ public abstract class IFile : IDisposable needsAppend = true; } - else - { - needsAppend = false; - } + + return Result.Success; + } + + protected Result DrySetSize(long size, OpenMode openMode) + { + // Check that we can write. + if (!openMode.HasFlag(OpenMode.Write)) + return ResultFs.WriteUnpermitted.Log(); return Result.Success; } @@ -218,6 +219,4 @@ public abstract class IFile : IDisposable protected abstract Result DoGetSize(out long size); protected abstract Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer); - - public virtual void Dispose() { } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/IFileSystem.cs b/src/LibHac/Fs/Fsa/IFileSystem.cs index 913655b3..b405de69 100644 --- a/src/LibHac/Fs/Fsa/IFileSystem.cs +++ b/src/LibHac/Fs/Fsa/IFileSystem.cs @@ -8,8 +8,11 @@ namespace LibHac.Fs.Fsa; /// /// Provides an interface for accessing a file system. / is used as the path delimiter. /// +/// Based on FS 13.1.0 (nnSdk 13.4.0) public abstract class IFileSystem : IDisposable { + public virtual void Dispose() { } + /// /// Creates or overwrites a file at the specified path. /// @@ -171,25 +174,22 @@ public abstract class IFileSystem : IDisposable } /// - /// Gets the amount of available free space on a drive, in bytes. + /// Opens an instance for the specified path. /// - /// If the operation returns successfully, the amount of free space available on the drive, in bytes. - /// The path of the drive to query. Unused in almost all cases. - /// The of the requested operation. - public Result GetFreeSpaceSize(out long freeSpace, in Path path) + /// If the operation returns successfully, + /// An instance for the specified path. + /// The full path of the file to open. + /// Specifies the access permissions of the created . + /// : The operation was successful.
+ /// : The specified path does not exist or is a directory.
+ /// : When opening as , + /// the file is already opened as .
+ public Result OpenFile(ref UniqueRef file, in Path path, OpenMode mode) { - return DoGetFreeSpaceSize(out freeSpace, in path); - } + if ((mode & OpenMode.ReadWrite) == 0 || (mode & ~OpenMode.All) != 0) + return ResultFs.InvalidModeForFileOpen.Log(); - /// - /// Gets the total size of storage space on a drive, in bytes. - /// - /// If the operation returns successfully, the total size of the drive, in bytes. - /// The path of the drive to query. Unused in almost all cases. - /// The of the requested operation. - public Result GetTotalSpaceSize(out long totalSpace, in Path path) - { - return DoGetTotalSpaceSize(out totalSpace, in path); + return DoOpenFile(ref file, in path, mode); } /// @@ -215,25 +215,6 @@ public abstract class IFileSystem : IDisposable return DoOpenFile(ref file, in pathNormalized, mode); } - /// - /// Opens an instance for the specified path. - /// - /// If the operation returns successfully, - /// An instance for the specified path. - /// The full path of the file to open. - /// Specifies the access permissions of the created . - /// : The operation was successful.
- /// : The specified path does not exist or is a directory.
- /// : When opening as , - /// the file is already opened as .
- public Result OpenFile(ref UniqueRef file, in Path path, OpenMode mode) - { - if ((mode & OpenMode.ReadWrite) == 0 || (mode & ~OpenMode.All) != 0) - return ResultFs.InvalidModeForFileOpen.Log(); - - return DoOpenFile(ref file, in path, mode); - } - /// /// Creates an instance for enumerating the specified directory. /// @@ -264,6 +245,28 @@ public abstract class IFileSystem : IDisposable public Result Flush() => DoFlush(); + /// + /// Gets the amount of available free space on a drive, in bytes. + /// + /// If the operation returns successfully, the amount of free space available on the drive, in bytes. + /// The path of the drive to query. Unused in almost all cases. + /// The of the requested operation. + public Result GetFreeSpaceSize(out long freeSpace, in Path path) + { + return DoGetFreeSpaceSize(out freeSpace, in path); + } + + /// + /// Gets the total size of storage space on a drive, in bytes. + /// + /// If the operation returns successfully, the total size of the drive, in bytes. + /// The path of the drive to query. Unused in almost all cases. + /// The of the requested operation. + public Result GetTotalSpaceSize(out long totalSpace, in Path path) + { + return DoGetTotalSpaceSize(out totalSpace, in path); + } + /// /// Gets the creation, last accessed, and last modified timestamps of a file or directory. /// @@ -333,8 +336,6 @@ public abstract class IFileSystem : IDisposable protected virtual Result DoQueryEntry(Span outBuffer, ReadOnlySpan inBuffer, QueryId queryId, in Path path) => ResultFs.NotImplemented.Log(); - - public virtual void Dispose() { } } /// @@ -372,4 +373,4 @@ public enum QueryId UpdateMac = 1, IsSignedSystemPartitionOnSdCardValid = 2, QueryUnpreparedFileInformation = 3 -} +} \ No newline at end of file From f0df8c2d9b98f4e2a373ed714cdf1160465f9bbd Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 28 Dec 2021 17:00:37 -0700 Subject: [PATCH 19/34] Remove Do* methods from IStorage --- src/LibHac/Fs/Common/FileStorage.cs | 24 +++--- src/LibHac/Fs/Fsa/UserFileSystem.cs | 4 +- src/LibHac/Fs/IStorage.cs | 80 +++++-------------- .../Fs/Impl/StorageServiceObjectAdapter.cs | 14 ++-- src/LibHac/Fs/MemoryStorage.cs | 18 +++-- src/LibHac/Fs/SubStorage.cs | 14 ++-- .../EmulatedGameCardStorageCreator.cs | 18 +++-- .../Impl/DeviceEventSimulationStorage.cs | 14 ++-- src/LibHac/FsSrv/SaveDataSharedFileStorage.cs | 14 ++-- src/LibHac/FsSrv/SpeedEmulationStorage.cs | 14 ++-- src/LibHac/FsSystem/BufferedStorage.cs | 12 +-- src/LibHac/FsSystem/CompressedStorage.cs | 12 +-- src/LibHac/FsSystem/IndirectStorage.cs | 14 ++-- src/LibHac/FsSystem/LocalStorage.cs | 57 +++++++------ src/LibHac/FsSystem/SparseStorage.cs | 14 ++-- .../FsSystem/StorageLayoutTypeSetter.cs | 14 ++-- src/LibHac/FsSystem/TruncatedSubStorage.cs | 10 +-- .../Tools/FsSystem/Aes128CtrExStorage.cs | 8 +- src/LibHac/Tools/FsSystem/Aes128CtrStorage.cs | 8 +- src/LibHac/Tools/FsSystem/Aes128XtsStorage.cs | 10 +-- src/LibHac/Tools/FsSystem/AesCbcStorage.cs | 10 +-- src/LibHac/Tools/FsSystem/CachedStorage.cs | 16 ++-- .../Tools/FsSystem/ConcatenationStorage.cs | 16 ++-- ...ierarchicalIntegrityVerificationStorage.cs | 16 ++-- .../FsSystem/IntegrityVerificationStorage.cs | 8 +- src/LibHac/Tools/FsSystem/NullStorage.cs | 16 ++-- .../FsSystem/Save/AllocationTableStorage.cs | 16 ++-- .../Tools/FsSystem/Save/DuplexStorage.cs | 16 ++-- .../Save/HierarchicalDuplexStorage.cs | 16 ++-- .../Tools/FsSystem/Save/JournalStorage.cs | 16 ++-- .../Tools/FsSystem/Save/RemapStorage.cs | 16 ++-- src/LibHac/Tools/FsSystem/SectorStorage.cs | 16 ++-- src/LibHac/Tools/FsSystem/StreamStorage.cs | 16 ++-- 33 files changed, 308 insertions(+), 259 deletions(-) diff --git a/src/LibHac/Fs/Common/FileStorage.cs b/src/LibHac/Fs/Common/FileStorage.cs index 93fb1144..a12fce38 100644 --- a/src/LibHac/Fs/Common/FileStorage.cs +++ b/src/LibHac/Fs/Common/FileStorage.cs @@ -52,7 +52,7 @@ public class FileStorage : IStorage _baseFile = file; } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { if (destination.Length == 0) return Result.Success; @@ -66,7 +66,7 @@ public class FileStorage : IStorage return _baseFile.Read(out _, offset, destination, ReadOption.None); } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { if (source.Length == 0) return Result.Success; @@ -80,12 +80,12 @@ public class FileStorage : IStorage return _baseFile.Write(offset, source, WriteOption.None); } - protected override Result DoFlush() + public override Result Flush() { return _baseFile.Flush(); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { UnsafeHelpers.SkipParamInit(out size); @@ -96,13 +96,13 @@ public class FileStorage : IStorage return Result.Success; } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { _fileSize = InvalidSize; return _baseFile.SetSize(size); } - protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { if (operationId == OperationId.InvalidateCache) { @@ -245,7 +245,7 @@ public class FileHandleStorage : IStorage base.Dispose(); } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex); @@ -261,7 +261,7 @@ public class FileHandleStorage : IStorage return _fsClient.ReadFile(_handle, offset, destination, ReadOption.None); } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex); @@ -277,12 +277,12 @@ public class FileHandleStorage : IStorage return _fsClient.WriteFile(_handle, offset, source, WriteOption.None); } - protected override Result DoFlush() + public override Result Flush() { return _fsClient.FlushFile(_handle); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { UnsafeHelpers.SkipParamInit(out size); @@ -293,13 +293,13 @@ public class FileHandleStorage : IStorage return Result.Success; } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { _size = InvalidSize; return _fsClient.SetFileSize(_handle, size); } - protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { if (operationId != OperationId.QueryRange) return ResultFs.UnsupportedOperateRangeForFileHandleStorage.Log(); diff --git a/src/LibHac/Fs/Fsa/UserFileSystem.cs b/src/LibHac/Fs/Fsa/UserFileSystem.cs index 8654ab08..053db768 100644 --- a/src/LibHac/Fs/Fsa/UserFileSystem.cs +++ b/src/LibHac/Fs/Fsa/UserFileSystem.cs @@ -637,10 +637,10 @@ public static class UserFileSystem { // Todo: Add access log - if (mountNames.Length > 10) + if (mountNames.Length < 0) return ResultFs.InvalidCommitNameCount.Log(); - if (mountNames.Length < 10) + if (mountNames.Length > 10) return ResultFs.InvalidCommitNameCount.Log(); if (mountNames.Length == 0) diff --git a/src/LibHac/Fs/IStorage.cs b/src/LibHac/Fs/IStorage.cs index f556e0cf..8dd6c702 100644 --- a/src/LibHac/Fs/IStorage.cs +++ b/src/LibHac/Fs/IStorage.cs @@ -12,8 +12,11 @@ namespace LibHac.Fs; /// have DoRead etc. methods. We're using them here so we can make sure /// the object isn't disposed before calling the method implementation. /// +/// Based on FS 13.1.0 (nnSdk 13.4.0) public abstract class IStorage : IDisposable { + public virtual void Dispose() { } + /// /// Reads a sequence of bytes from the current . /// @@ -21,11 +24,7 @@ public abstract class IStorage : IDisposable /// The buffer where the read bytes will be stored. /// The number of bytes read will be equal to the length of the buffer. /// The of the operation. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Result Read(long offset, Span destination) - { - return DoRead(offset, destination); - } + public abstract Result Read(long offset, Span destination); /// /// Writes a sequence of bytes to the current . @@ -33,42 +32,38 @@ public abstract class IStorage : IDisposable /// The offset in the at which to begin writing. /// The buffer containing the bytes to be written. /// The of the operation. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Result Write(long offset, ReadOnlySpan source) - { - return DoWrite(offset, source); - } + public abstract Result Write(long offset, ReadOnlySpan source); /// /// Causes any buffered data to be written to the underlying device. /// /// The of the operation. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Result Flush() - { - return DoFlush(); - } + public abstract Result Flush(); /// /// Sets the size of the current . /// /// The desired size of the in bytes. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Result SetSize(long size) - { - return DoSetSize(size); - } + public abstract Result SetSize(long size); /// /// Gets the number of bytes in the . /// /// If the operation returns successfully, the length of the file in bytes. /// The of the operation. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Result GetSize(out long size) - { - return DoGetSize(out size); - } + public abstract Result GetSize(out long size); + + /// + /// Performs various operations on the storage. Used to extend the functionality of the interface. + /// + /// A buffer that will contain the response from the operation. + /// The operation to be performed. + /// The offset of the range to operate on. + /// The size of the range to operate on. + /// An input buffer. Size may vary depending on the operation performed. + /// The of the operation. + public abstract Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer); /// /// Performs various operations on the storage. Used to extend the functionality of the interface. @@ -80,23 +75,7 @@ public abstract class IStorage : IDisposable [MethodImpl(MethodImplOptions.AggressiveInlining)] public Result OperateRange(OperationId operationId, long offset, long size) { - return DoOperateRange(Span.Empty, operationId, offset, size, ReadOnlySpan.Empty); - } - - /// - /// Performs various operations on the storage. Used to extend the functionality of the interface. - /// - /// A buffer that will contain the response from the operation. - /// The operation to be performed. - /// The offset of the range to operate on. - /// The size of the range to operate on. - /// An input buffer. Size may vary depending on the operation performed. - /// The of the operation. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, - ReadOnlySpan inBuffer) - { - return DoOperateRange(outBuffer, operationId, offset, size, inBuffer); + return OperateRange(Span.Empty, operationId, offset, size, ReadOnlySpan.Empty); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -115,19 +94,4 @@ public abstract class IStorage : IDisposable size >= 0 && offset <= offset + size; } - - // Todo: Remove Do* methods - protected abstract Result DoRead(long offset, Span destination); - protected abstract Result DoWrite(long offset, ReadOnlySpan source); - protected abstract Result DoFlush(); - protected abstract Result DoSetSize(long size); - protected abstract Result DoGetSize(out long size); - - protected virtual Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, - ReadOnlySpan inBuffer) - { - return ResultFs.NotImplemented.Log(); - } - - public virtual void Dispose() { } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Impl/StorageServiceObjectAdapter.cs b/src/LibHac/Fs/Impl/StorageServiceObjectAdapter.cs index 7cb138d4..29285d2b 100644 --- a/src/LibHac/Fs/Impl/StorageServiceObjectAdapter.cs +++ b/src/LibHac/Fs/Impl/StorageServiceObjectAdapter.cs @@ -20,32 +20,32 @@ internal class StorageServiceObjectAdapter : IStorage _baseStorage = SharedRef.CreateMove(ref baseStorage); } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { return _baseStorage.Get.Read(offset, new OutBuffer(destination), destination.Length); } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { return _baseStorage.Get.Write(offset, new InBuffer(source), source.Length); } - protected override Result DoFlush() + public override Result Flush() { return _baseStorage.Get.Flush(); } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return _baseStorage.Get.SetSize(size); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { return _baseStorage.Get.GetSize(out size); } - protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { switch (operationId) @@ -69,4 +69,4 @@ internal class StorageServiceObjectAdapter : IStorage _baseStorage.Destroy(); base.Dispose(); } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/MemoryStorage.cs b/src/LibHac/Fs/MemoryStorage.cs index 196e685e..9a9dde6d 100644 --- a/src/LibHac/Fs/MemoryStorage.cs +++ b/src/LibHac/Fs/MemoryStorage.cs @@ -11,7 +11,7 @@ public class MemoryStorage : IStorage StorageBuffer = buffer; } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { if (destination.Length == 0) return Result.Success; @@ -24,7 +24,7 @@ public class MemoryStorage : IStorage return Result.Success; } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { if (source.Length == 0) return Result.Success; @@ -37,20 +37,26 @@ public class MemoryStorage : IStorage return Result.Success; } - protected override Result DoFlush() + public override Result Flush() { return Result.Success; } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return ResultFs.UnsupportedSetSizeForMemoryStorage.Log(); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { size = StorageBuffer.Length; return Result.Success; } -} + + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/LibHac/Fs/SubStorage.cs b/src/LibHac/Fs/SubStorage.cs index 7cd86327..72394195 100644 --- a/src/LibHac/Fs/SubStorage.cs +++ b/src/LibHac/Fs/SubStorage.cs @@ -153,7 +153,7 @@ public class SubStorage : IStorage _isResizable = isResizable; } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { if (!IsValid()) return ResultFs.NotInitialized.Log(); if (destination.Length == 0) return Result.Success; @@ -164,7 +164,7 @@ public class SubStorage : IStorage return BaseStorage.Read(_offset + offset, destination); } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { if (!IsValid()) return ResultFs.NotInitialized.Log(); if (source.Length == 0) return Result.Success; @@ -175,14 +175,14 @@ public class SubStorage : IStorage return BaseStorage.Write(_offset + offset, source); } - protected override Result DoFlush() + public override Result Flush() { if (!IsValid()) return ResultFs.NotInitialized.Log(); return BaseStorage.Flush(); } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { if (!IsValid()) return ResultFs.NotInitialized.Log(); if (!_isResizable) return ResultFs.UnsupportedSetSizeForNotResizableSubStorage.Log(); @@ -205,7 +205,7 @@ public class SubStorage : IStorage return Result.Success; } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { UnsafeHelpers.SkipParamInit(out size); @@ -215,7 +215,7 @@ public class SubStorage : IStorage return Result.Success; } - protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { if (!IsValid()) return ResultFs.NotInitialized.Log(); @@ -227,4 +227,4 @@ public class SubStorage : IStorage return BaseStorage.OperateRange(outBuffer, operationId, _offset + offset, size, inBuffer); } -} +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/FsCreator/EmulatedGameCardStorageCreator.cs b/src/LibHac/FsSrv/FsCreator/EmulatedGameCardStorageCreator.cs index 1d962fee..df9c37b8 100644 --- a/src/LibHac/FsSrv/FsCreator/EmulatedGameCardStorageCreator.cs +++ b/src/LibHac/FsSrv/FsCreator/EmulatedGameCardStorageCreator.cs @@ -86,7 +86,7 @@ public class EmulatedGameCardStorageCreator : IGameCardStorageCreator imageHash.CopyTo(ImageHash); } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { // In secure mode, if Handle is old and the card's device ID and // header hash are still the same, Handle is updated to the new handle @@ -94,22 +94,22 @@ public class EmulatedGameCardStorageCreator : IGameCardStorageCreator return GameCard.Read(Handle, offset, destination); } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { return ResultFs.UnsupportedWriteForReadOnlyGameCardStorage.Log(); } - protected override Result DoFlush() + public override Result Flush() { return Result.Success; } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return ResultFs.UnsupportedSetSizeForReadOnlyGameCardStorage.Log(); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { UnsafeHelpers.SkipParamInit(out size); @@ -119,5 +119,11 @@ public class EmulatedGameCardStorageCreator : IGameCardStorageCreator size = info.Size; return Result.Success; } + + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + throw new NotImplementedException(); + } } -} +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/Impl/DeviceEventSimulationStorage.cs b/src/LibHac/FsSrv/Impl/DeviceEventSimulationStorage.cs index 34d5e886..f25c67eb 100644 --- a/src/LibHac/FsSrv/Impl/DeviceEventSimulationStorage.cs +++ b/src/LibHac/FsSrv/Impl/DeviceEventSimulationStorage.cs @@ -25,7 +25,7 @@ internal class DeviceEventSimulationStorage : IStorage base.Dispose(); } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { Result rc = _eventSimulator.CheckSimulatedAccessFailureEvent(SimulatingDeviceTargetOperation.Read); if (rc.IsFailure()) return rc; @@ -33,7 +33,7 @@ internal class DeviceEventSimulationStorage : IStorage return _baseStorage.Get.Read(offset, destination); } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { Result rc = _eventSimulator.CheckSimulatedAccessFailureEvent(SimulatingDeviceTargetOperation.Write); if (rc.IsFailure()) return rc; @@ -41,24 +41,24 @@ internal class DeviceEventSimulationStorage : IStorage return _baseStorage.Get.Write(offset, source); } - protected override Result DoFlush() + public override Result Flush() { return _baseStorage.Get.Flush(); } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return _baseStorage.Get.SetSize(size); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { return _baseStorage.Get.GetSize(out size); } - protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { return _baseStorage.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer); } -} +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/SaveDataSharedFileStorage.cs b/src/LibHac/FsSrv/SaveDataSharedFileStorage.cs index d8497c6b..db1c8333 100644 --- a/src/LibHac/FsSrv/SaveDataSharedFileStorage.cs +++ b/src/LibHac/FsSrv/SaveDataSharedFileStorage.cs @@ -201,7 +201,7 @@ public class SaveDataSharedFileStorage : IStorage return Result.Success; } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { using UniqueLockRef scopedLock = _baseStorage.Get.GetLock(); @@ -211,7 +211,7 @@ public class SaveDataSharedFileStorage : IStorage return _baseStorage.Get.Read(offset, destination); } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { using UniqueLockRef scopedLock = _baseStorage.Get.GetLock(); @@ -221,7 +221,7 @@ public class SaveDataSharedFileStorage : IStorage return _baseStorage.Get.Write(offset, source); } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { using UniqueLockRef scopedLock = _baseStorage.Get.GetLock(); @@ -231,7 +231,7 @@ public class SaveDataSharedFileStorage : IStorage return _baseStorage.Get.SetSize(size); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { Unsafe.SkipInit(out size); @@ -243,7 +243,7 @@ public class SaveDataSharedFileStorage : IStorage return _baseStorage.Get.GetSize(out size); } - protected override Result DoFlush() + public override Result Flush() { using UniqueLockRef scopedLock = _baseStorage.Get.GetLock(); @@ -253,7 +253,7 @@ public class SaveDataSharedFileStorage : IStorage return _baseStorage.Get.Flush(); } - protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { using UniqueLockRef scopedLock = _baseStorage.Get.GetLock(); @@ -430,4 +430,4 @@ public class SaveDataFileStorageHolder currentEntry = currentEntry.Next; } } -} +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/SpeedEmulationStorage.cs b/src/LibHac/FsSrv/SpeedEmulationStorage.cs index 25f926e9..0013eee5 100644 --- a/src/LibHac/FsSrv/SpeedEmulationStorage.cs +++ b/src/LibHac/FsSrv/SpeedEmulationStorage.cs @@ -20,34 +20,34 @@ public class SpeedEmulationStorage : IStorage base.Dispose(); } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { return _baseStorage.Get.Read(offset, destination); } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { return _baseStorage.Get.Write(offset, source); } - protected override Result DoFlush() + public override Result Flush() { return _baseStorage.Get.Flush(); } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return _baseStorage.Get.SetSize(size); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { return _baseStorage.Get.GetSize(out size); } - protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { return _baseStorage.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer); } -} +} \ No newline at end of file diff --git a/src/LibHac/FsSystem/BufferedStorage.cs b/src/LibHac/FsSystem/BufferedStorage.cs index 07d2803d..08842f5a 100644 --- a/src/LibHac/FsSystem/BufferedStorage.cs +++ b/src/LibHac/FsSystem/BufferedStorage.cs @@ -1068,7 +1068,7 @@ public class BufferedStorage : IStorage /// Otherwise, . public bool IsInitialized() => _caches != null; - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { Assert.SdkRequires(IsInitialized()); @@ -1080,7 +1080,7 @@ public class BufferedStorage : IStorage return ReadCore(offset, destination).Miss(); } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { Assert.SdkRequires(IsInitialized()); @@ -1092,7 +1092,7 @@ public class BufferedStorage : IStorage return WriteCore(offset, source).Miss(); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { Assert.SdkRequires(IsInitialized()); @@ -1100,7 +1100,7 @@ public class BufferedStorage : IStorage return Result.Success; } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { Assert.SdkRequires(IsInitialized()); @@ -1158,7 +1158,7 @@ public class BufferedStorage : IStorage return Result.Success; } - protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { Assert.SdkRequires(IsInitialized()); @@ -1172,7 +1172,7 @@ public class BufferedStorage : IStorage return _baseStorage.OperateRange(outBuffer, operationId, offset, size, inBuffer); } - protected override Result DoFlush() + public override Result Flush() { Assert.SdkRequires(IsInitialized()); diff --git a/src/LibHac/FsSystem/CompressedStorage.cs b/src/LibHac/FsSystem/CompressedStorage.cs index 13443ad9..e89a2857 100644 --- a/src/LibHac/FsSystem/CompressedStorage.cs +++ b/src/LibHac/FsSystem/CompressedStorage.cs @@ -320,7 +320,7 @@ public class CompressedStorage : IStorage, IAsynchronousAccessSplitter _core.FinalizeObject(); } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { Result rc = _cacheManager.Read(_core, offset, destination); if (rc.IsFailure()) return rc.Miss(); @@ -328,22 +328,22 @@ public class CompressedStorage : IStorage, IAsynchronousAccessSplitter return Result.Success; } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { return ResultFs.UnsupportedWriteForCompressedStorage.Log(); } - protected override Result DoFlush() + public override Result Flush() { return Result.Success; } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return ResultFs.UnsupportedSetSizeForIndirectStorage.Log(); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { Result rc = _core.GetSize(out size); if (rc.IsFailure()) return rc.Miss(); @@ -351,7 +351,7 @@ public class CompressedStorage : IStorage, IAsynchronousAccessSplitter return Result.Success; } - protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { Assert.SdkRequiresLessEqual(0, offset); diff --git a/src/LibHac/FsSystem/IndirectStorage.cs b/src/LibHac/FsSystem/IndirectStorage.cs index f7746d40..d5dcc966 100644 --- a/src/LibHac/FsSystem/IndirectStorage.cs +++ b/src/LibHac/FsSystem/IndirectStorage.cs @@ -169,7 +169,7 @@ public class IndirectStorage : IStorage } } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { // Validate pre-conditions Assert.SdkRequiresLessEqual(0, offset); @@ -196,17 +196,17 @@ public class IndirectStorage : IStorage return Result.Success; } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { return ResultFs.UnsupportedWriteForIndirectStorage.Log(); } - protected override Result DoFlush() + public override Result Flush() { return Result.Success; } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { UnsafeHelpers.SkipParamInit(out size); @@ -217,7 +217,7 @@ public class IndirectStorage : IStorage return Result.Success; } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return ResultFs.UnsupportedSetSizeForIndirectStorage.Log(); } @@ -288,7 +288,7 @@ public class IndirectStorage : IStorage return Result.Success; } - protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { Assert.SdkRequiresLessEqual(0, offset); @@ -518,4 +518,4 @@ public class IndirectStorage : IStorage return Result.Success; } -} +} \ No newline at end of file diff --git a/src/LibHac/FsSystem/LocalStorage.cs b/src/LibHac/FsSystem/LocalStorage.cs index 75ab1ce1..65cbf364 100644 --- a/src/LibHac/FsSystem/LocalStorage.cs +++ b/src/LibHac/FsSystem/LocalStorage.cs @@ -20,34 +20,41 @@ public class LocalStorage : IStorage Storage = new StreamStorage(Stream, false); } - protected override Result DoRead(long offset, Span destination) - { - return Storage.Read(offset, destination); - } - - protected override Result DoWrite(long offset, ReadOnlySpan source) - { - return Storage.Write(offset, source); - } - - protected override Result DoFlush() - { - return Storage.Flush(); - } - - protected override Result DoSetSize(long size) - { - return ResultFs.NotImplemented.Log(); - } - - protected override Result DoGetSize(out long size) - { - return Storage.GetSize(out size); - } - public override void Dispose() { Storage?.Dispose(); Stream?.Dispose(); + base.Dispose(); + } + + public override Result Read(long offset, Span destination) + { + return Storage.Read(offset, destination); + } + + public override Result Write(long offset, ReadOnlySpan source) + { + return Storage.Write(offset, source); + } + + public override Result Flush() + { + return Storage.Flush(); + } + + public override Result SetSize(long size) + { + return ResultFs.NotImplemented.Log(); + } + + public override Result GetSize(out long size) + { + return Storage.GetSize(out size); + } + + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + throw new NotImplementedException(); } } \ No newline at end of file diff --git a/src/LibHac/FsSystem/SparseStorage.cs b/src/LibHac/FsSystem/SparseStorage.cs index c230267c..1e7359e0 100644 --- a/src/LibHac/FsSystem/SparseStorage.cs +++ b/src/LibHac/FsSystem/SparseStorage.cs @@ -16,7 +16,7 @@ public class SparseStorage : IndirectStorage { private class ZeroStorage : IStorage { - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { Assert.SdkRequiresGreaterEqual(offset, 0); @@ -26,28 +26,28 @@ public class SparseStorage : IndirectStorage return Result.Success; } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { return ResultFs.UnsupportedWriteForZeroStorage.Log(); } - protected override Result DoFlush() + public override Result Flush() { return Result.Success; } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { size = long.MaxValue; return Result.Success; } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return ResultFs.UnsupportedSetSizeForZeroStorage.Log(); } - protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { return Result.Success; @@ -86,7 +86,7 @@ public class SparseStorage : IndirectStorage SetStorage(1, _zeroStorage, 0, long.MaxValue); } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { // Validate pre-conditions Assert.SdkRequiresLessEqual(0, offset); diff --git a/src/LibHac/FsSystem/StorageLayoutTypeSetter.cs b/src/LibHac/FsSystem/StorageLayoutTypeSetter.cs index 324a8e50..36fbe6b9 100644 --- a/src/LibHac/FsSystem/StorageLayoutTypeSetter.cs +++ b/src/LibHac/FsSystem/StorageLayoutTypeSetter.cs @@ -61,37 +61,37 @@ internal class StorageLayoutTypeSetStorage : IStorage base.Dispose(); } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { using var scopedContext = new ScopedStorageLayoutTypeSetter(_storageFlag); return _baseStorage.Get.Read(offset, destination); } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { using var scopedContext = new ScopedStorageLayoutTypeSetter(_storageFlag); return _baseStorage.Get.Write(offset, source); } - protected override Result DoFlush() + public override Result Flush() { using var scopedContext = new ScopedStorageLayoutTypeSetter(_storageFlag); return _baseStorage.Get.Flush(); } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { using var scopedContext = new ScopedStorageLayoutTypeSetter(_storageFlag); return _baseStorage.Get.SetSize(size); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { using var scopedContext = new ScopedStorageLayoutTypeSetter(_storageFlag); return _baseStorage.Get.GetSize(out size); } - protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) { using var scopedContext = new ScopedStorageLayoutTypeSetter(_storageFlag); @@ -352,4 +352,4 @@ internal class StorageLayoutTypeSetFileSystem : IFileSystem using var scopedContext = new ScopedStorageLayoutTypeSetter(_storageFlag); return _baseFileSystem.Get.QueryEntry(outBuffer, inBuffer, queryId, path); } -} +} \ No newline at end of file diff --git a/src/LibHac/FsSystem/TruncatedSubStorage.cs b/src/LibHac/FsSystem/TruncatedSubStorage.cs index 5f25bb88..d9b65863 100644 --- a/src/LibHac/FsSystem/TruncatedSubStorage.cs +++ b/src/LibHac/FsSystem/TruncatedSubStorage.cs @@ -16,7 +16,7 @@ public class TruncatedSubStorage : SubStorage public TruncatedSubStorage() { } public TruncatedSubStorage(SubStorage other) : base(other) { } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { if (destination.Length == 0) return Result.Success; @@ -27,10 +27,10 @@ public class TruncatedSubStorage : SubStorage long availableSize = baseStorageSize - offset; long sizeToRead = Math.Min(destination.Length, availableSize); - return base.DoRead(offset, destination.Slice(0, (int)sizeToRead)); + return base.Read(offset, destination.Slice(0, (int)sizeToRead)); } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { if (source.Length == 0) return Result.Success; @@ -41,6 +41,6 @@ public class TruncatedSubStorage : SubStorage long availableSize = baseStorageSize - offset; long sizeToWrite = Math.Min(source.Length, availableSize); - return base.DoWrite(offset, source.Slice(0, (int)sizeToWrite)); + return base.Write(offset, source.Slice(0, (int)sizeToWrite)); } -} +} \ No newline at end of file diff --git a/src/LibHac/Tools/FsSystem/Aes128CtrExStorage.cs b/src/LibHac/Tools/FsSystem/Aes128CtrExStorage.cs index 8a9c095c..a96643ad 100644 --- a/src/LibHac/Tools/FsSystem/Aes128CtrExStorage.cs +++ b/src/LibHac/Tools/FsSystem/Aes128CtrExStorage.cs @@ -37,7 +37,7 @@ public class Aes128CtrExStorage : Aes128CtrStorage rc.ThrowIfFailure(); } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { if (destination.Length == 0) return Result.Success; @@ -83,7 +83,7 @@ public class Aes128CtrExStorage : Aes128CtrStorage { UpdateCounterSubsection((uint)currentEntry.Generation); - rc = base.DoRead(inPos, destination.Slice(outPos, bytesToRead)); + rc = base.Read(inPos, destination.Slice(outPos, bytesToRead)); if (rc.IsFailure()) return rc; } @@ -95,12 +95,12 @@ public class Aes128CtrExStorage : Aes128CtrStorage return Result.Success; } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { return ResultFs.UnsupportedWriteForAesCtrCounterExtendedStorage.Log(); } - protected override Result DoFlush() + public override Result Flush() { return Result.Success; } diff --git a/src/LibHac/Tools/FsSystem/Aes128CtrStorage.cs b/src/LibHac/Tools/FsSystem/Aes128CtrStorage.cs index 3442976c..b3932bee 100644 --- a/src/LibHac/Tools/FsSystem/Aes128CtrStorage.cs +++ b/src/LibHac/Tools/FsSystem/Aes128CtrStorage.cs @@ -59,9 +59,9 @@ public class Aes128CtrStorage : SectorStorage Counter = _decryptor.Counter; } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { - Result rc = base.DoRead(offset, destination); + Result rc = base.Read(offset, destination); if (rc.IsFailure()) return rc; lock (_locker) @@ -73,7 +73,7 @@ public class Aes128CtrStorage : SectorStorage return Result.Success; } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { byte[] encrypted = ArrayPool.Shared.Rent(source.Length); try @@ -87,7 +87,7 @@ public class Aes128CtrStorage : SectorStorage _decryptor.TransformBlock(encryptedSpan); } - Result rc = base.DoWrite(offset, encryptedSpan); + Result rc = base.Write(offset, encryptedSpan); if (rc.IsFailure()) return rc; } finally diff --git a/src/LibHac/Tools/FsSystem/Aes128XtsStorage.cs b/src/LibHac/Tools/FsSystem/Aes128XtsStorage.cs index 5c9d949f..f5c968c4 100644 --- a/src/LibHac/Tools/FsSystem/Aes128XtsStorage.cs +++ b/src/LibHac/Tools/FsSystem/Aes128XtsStorage.cs @@ -42,14 +42,14 @@ public class Aes128XtsStorage : SectorStorage _key2 = key2.ToArray(); } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { int size = destination.Length; long sectorIndex = offset / SectorSize; if (_readTransform == null) _readTransform = new Aes128XtsTransform(_key1, _key2, _decryptRead); - Result rc = base.DoRead(offset, _tempBuffer.AsSpan(0, size)); + Result rc = base.Read(offset, _tempBuffer.AsSpan(0, size)); if (rc.IsFailure()) return rc; _readTransform.TransformBlock(_tempBuffer, 0, size, (ulong)sectorIndex); @@ -58,7 +58,7 @@ public class Aes128XtsStorage : SectorStorage return Result.Success; } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { int size = source.Length; long sectorIndex = offset / SectorSize; @@ -68,10 +68,10 @@ public class Aes128XtsStorage : SectorStorage source.CopyTo(_tempBuffer); _writeTransform.TransformBlock(_tempBuffer, 0, size, (ulong)sectorIndex); - return base.DoWrite(offset, _tempBuffer.AsSpan(0, size)); + return base.Write(offset, _tempBuffer.AsSpan(0, size)); } - protected override Result DoFlush() + public override Result Flush() { return BaseStorage.Flush(); } diff --git a/src/LibHac/Tools/FsSystem/AesCbcStorage.cs b/src/LibHac/Tools/FsSystem/AesCbcStorage.cs index 5696fa0e..8045d2f6 100644 --- a/src/LibHac/Tools/FsSystem/AesCbcStorage.cs +++ b/src/LibHac/Tools/FsSystem/AesCbcStorage.cs @@ -26,12 +26,12 @@ public class AesCbcStorage : SectorStorage baseStorage.GetSize(out _size).ThrowIfFailure(); } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { if (!CheckAccessRange(offset, destination.Length, _size)) return ResultFs.OutOfRange.Log(); - Result rc = base.DoRead(offset, destination); + Result rc = base.Read(offset, destination); if (rc.IsFailure()) return rc; rc = GetDecryptor(out ICipher cipher, offset); @@ -42,17 +42,17 @@ public class AesCbcStorage : SectorStorage return Result.Success; } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { return ResultFs.UnsupportedOperation.Log(); } - protected override Result DoFlush() + public override Result Flush() { return Result.Success; } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return ResultFs.UnsupportedOperation.Log(); } diff --git a/src/LibHac/Tools/FsSystem/CachedStorage.cs b/src/LibHac/Tools/FsSystem/CachedStorage.cs index 59cc92fc..89de720d 100644 --- a/src/LibHac/Tools/FsSystem/CachedStorage.cs +++ b/src/LibHac/Tools/FsSystem/CachedStorage.cs @@ -33,7 +33,7 @@ public class CachedStorage : IStorage public CachedStorage(SectorStorage baseStorage, int cacheSize, bool leaveOpen) : this(baseStorage, baseStorage.SectorSize, cacheSize, leaveOpen) { } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { long remaining = destination.Length; long inOffset = offset; @@ -63,7 +63,7 @@ public class CachedStorage : IStorage return Result.Success; } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { long remaining = source.Length; long inOffset = offset; @@ -95,7 +95,7 @@ public class CachedStorage : IStorage return Result.Success; } - protected override Result DoFlush() + public override Result Flush() { lock (Blocks) { @@ -108,13 +108,13 @@ public class CachedStorage : IStorage return BaseStorage.Flush(); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { size = Length; return Result.Success; } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { Result rc = BaseStorage.SetSize(size); if (rc.IsFailure()) return rc; @@ -127,6 +127,12 @@ public class CachedStorage : IStorage return Result.Success; } + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + throw new NotImplementedException(); + } + public override void Dispose() { if (!LeaveOpen) diff --git a/src/LibHac/Tools/FsSystem/ConcatenationStorage.cs b/src/LibHac/Tools/FsSystem/ConcatenationStorage.cs index bb293323..cbb8102d 100644 --- a/src/LibHac/Tools/FsSystem/ConcatenationStorage.cs +++ b/src/LibHac/Tools/FsSystem/ConcatenationStorage.cs @@ -28,7 +28,7 @@ public class ConcatenationStorage : IStorage Length = length; } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { long inPos = offset; int outPos = 0; @@ -59,7 +59,7 @@ public class ConcatenationStorage : IStorage return Result.Success; } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { long inPos = offset; int outPos = 0; @@ -90,7 +90,7 @@ public class ConcatenationStorage : IStorage return Result.Success; } - protected override Result DoFlush() + public override Result Flush() { foreach (ConcatSource source in Sources) { @@ -101,17 +101,23 @@ public class ConcatenationStorage : IStorage return Result.Success; } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return ResultFs.NotImplemented.Log(); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { size = Length; return Result.Success; } + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + throw new NotImplementedException(); + } + public override void Dispose() { if (!LeaveOpen && Sources != null) diff --git a/src/LibHac/Tools/FsSystem/HierarchicalIntegrityVerificationStorage.cs b/src/LibHac/Tools/FsSystem/HierarchicalIntegrityVerificationStorage.cs index 7a7bb652..570a4a92 100644 --- a/src/LibHac/Tools/FsSystem/HierarchicalIntegrityVerificationStorage.cs +++ b/src/LibHac/Tools/FsSystem/HierarchicalIntegrityVerificationStorage.cs @@ -98,32 +98,38 @@ public class HierarchicalIntegrityVerificationStorage : IStorage return initInfo; } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { return DataLevel.Read(offset, destination); } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { return DataLevel.Write(offset, source); } - protected override Result DoFlush() + public override Result Flush() { return DataLevel.Flush(); } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return ResultFs.UnsupportedSetSizeForHierarchicalIntegrityVerificationStorage.Log(); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { size = Length; return Result.Success; } + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + throw new NotImplementedException(); + } + public override void Dispose() { if (!LeaveOpen) diff --git a/src/LibHac/Tools/FsSystem/IntegrityVerificationStorage.cs b/src/LibHac/Tools/FsSystem/IntegrityVerificationStorage.cs index f95009ec..08be66b2 100644 --- a/src/LibHac/Tools/FsSystem/IntegrityVerificationStorage.cs +++ b/src/LibHac/Tools/FsSystem/IntegrityVerificationStorage.cs @@ -118,7 +118,7 @@ public class IntegrityVerificationStorage : SectorStorage return Result.Success; } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { return ReadImpl(offset, destination, IntegrityCheckLevel); } @@ -129,7 +129,7 @@ public class IntegrityVerificationStorage : SectorStorage return ReadImpl(offset, destination, integrityCheckLevel); } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { long blockIndex = offset / SectorSize; long hashPos = blockIndex * DigestSize; @@ -189,12 +189,12 @@ public class IntegrityVerificationStorage : SectorStorage } } - protected override Result DoFlush() + public override Result Flush() { Result rc = HashStorage.Flush(); if (rc.IsFailure()) return rc; - return base.DoFlush(); + return base.Flush(); } public void FsTrim() diff --git a/src/LibHac/Tools/FsSystem/NullStorage.cs b/src/LibHac/Tools/FsSystem/NullStorage.cs index 452b7153..6f0704dd 100644 --- a/src/LibHac/Tools/FsSystem/NullStorage.cs +++ b/src/LibHac/Tools/FsSystem/NullStorage.cs @@ -14,30 +14,36 @@ public class NullStorage : IStorage public NullStorage(long length) => Length = length; - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { destination.Clear(); return Result.Success; } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { return Result.Success; } - protected override Result DoFlush() + public override Result Flush() { return Result.Success; } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return ResultFs.NotImplemented.Log(); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { size = Length; return Result.Success; } + + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + return Result.Success; + } } \ No newline at end of file diff --git a/src/LibHac/Tools/FsSystem/Save/AllocationTableStorage.cs b/src/LibHac/Tools/FsSystem/Save/AllocationTableStorage.cs index 76cf371a..0458d32d 100644 --- a/src/LibHac/Tools/FsSystem/Save/AllocationTableStorage.cs +++ b/src/LibHac/Tools/FsSystem/Save/AllocationTableStorage.cs @@ -24,7 +24,7 @@ public class AllocationTableStorage : IStorage _length = initialBlock == -1 ? 0 : table.GetListLength(initialBlock) * blockSize; } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { var iterator = new AllocationTableIterator(Fat, InitialBlock); @@ -58,7 +58,7 @@ public class AllocationTableStorage : IStorage return Result.Success; } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { var iterator = new AllocationTableIterator(Fat, InitialBlock); @@ -92,18 +92,18 @@ public class AllocationTableStorage : IStorage return Result.Success; } - protected override Result DoFlush() + public override Result Flush() { return BaseStorage.Flush(); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { size = _length; return Result.Success; } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { int oldBlockCount = (int)BitUtil.DivideUp(_length, BlockSize); int newBlockCount = (int)BitUtil.DivideUp(size, BlockSize); @@ -147,4 +147,10 @@ public class AllocationTableStorage : IStorage return Result.Success; } + + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + throw new NotImplementedException(); + } } \ No newline at end of file diff --git a/src/LibHac/Tools/FsSystem/Save/DuplexStorage.cs b/src/LibHac/Tools/FsSystem/Save/DuplexStorage.cs index d9e2c042..af0e4d32 100644 --- a/src/LibHac/Tools/FsSystem/Save/DuplexStorage.cs +++ b/src/LibHac/Tools/FsSystem/Save/DuplexStorage.cs @@ -27,7 +27,7 @@ public class DuplexStorage : IStorage Length = dataSize; } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { long inPos = offset; int outPos = 0; @@ -56,7 +56,7 @@ public class DuplexStorage : IStorage return Result.Success; } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { long inPos = offset; int outPos = 0; @@ -85,7 +85,7 @@ public class DuplexStorage : IStorage return Result.Success; } - protected override Result DoFlush() + public override Result Flush() { Result rc = BitmapStorage.Flush(); if (rc.IsFailure()) return rc; @@ -99,17 +99,23 @@ public class DuplexStorage : IStorage return Result.Success; } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return ResultFs.NotImplemented.Log(); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { size = Length; return Result.Success; } + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + throw new NotImplementedException(); + } + public void FsTrim() { DataA.GetSize(out long dataSize).ThrowIfFailure(); diff --git a/src/LibHac/Tools/FsSystem/Save/HierarchicalDuplexStorage.cs b/src/LibHac/Tools/FsSystem/Save/HierarchicalDuplexStorage.cs index 1a7850d2..2683bf01 100644 --- a/src/LibHac/Tools/FsSystem/Save/HierarchicalDuplexStorage.cs +++ b/src/LibHac/Tools/FsSystem/Save/HierarchicalDuplexStorage.cs @@ -34,32 +34,38 @@ public class HierarchicalDuplexStorage : IStorage Length = dataSize; } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { return DataLayer.Read(offset, destination); } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { return DataLayer.Write(offset, source); } - protected override Result DoFlush() + public override Result Flush() { return DataLayer.Flush(); } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return ResultFs.NotImplemented.Log(); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { size = Length; return Result.Success; } + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + throw new NotImplementedException(); + } + public void FsTrim() { foreach (DuplexStorage layer in Layers) diff --git a/src/LibHac/Tools/FsSystem/Save/JournalStorage.cs b/src/LibHac/Tools/FsSystem/Save/JournalStorage.cs index 1ffa15ff..7d681963 100644 --- a/src/LibHac/Tools/FsSystem/Save/JournalStorage.cs +++ b/src/LibHac/Tools/FsSystem/Save/JournalStorage.cs @@ -34,7 +34,7 @@ public class JournalStorage : IStorage LeaveOpen = leaveOpen; } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { long inPos = offset; int outPos = 0; @@ -63,7 +63,7 @@ public class JournalStorage : IStorage return Result.Success; } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { long inPos = offset; int outPos = 0; @@ -92,22 +92,28 @@ public class JournalStorage : IStorage return Result.Success; } - protected override Result DoFlush() + public override Result Flush() { return BaseStorage.Flush(); } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return ResultFs.NotImplemented.Log(); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { size = Length; return Result.Success; } + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + throw new NotImplementedException(); + } + public override void Dispose() { if (!LeaveOpen) diff --git a/src/LibHac/Tools/FsSystem/Save/RemapStorage.cs b/src/LibHac/Tools/FsSystem/Save/RemapStorage.cs index a7a35107..b2867b31 100644 --- a/src/LibHac/Tools/FsSystem/Save/RemapStorage.cs +++ b/src/LibHac/Tools/FsSystem/Save/RemapStorage.cs @@ -49,7 +49,7 @@ public class RemapStorage : IStorage Segments = InitSegments(Header, MapEntries); } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { if (destination.Length == 0) return Result.Success; @@ -79,7 +79,7 @@ public class RemapStorage : IStorage return Result.Success; } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { if (source.Length == 0) return Result.Success; @@ -111,23 +111,29 @@ public class RemapStorage : IStorage return Result.Success; } - protected override Result DoFlush() + public override Result Flush() { return BaseStorage.Flush(); } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return ResultFs.UnsupportedSetSizeForHierarchicalIntegrityVerificationStorage.Log(); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { // todo: Different result code size = -1; return Result.Success; } + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + throw new NotImplementedException(); + } + public override void Dispose() { if (!LeaveOpen) diff --git a/src/LibHac/Tools/FsSystem/SectorStorage.cs b/src/LibHac/Tools/FsSystem/SectorStorage.cs index e8fdc2b4..c3051cbc 100644 --- a/src/LibHac/Tools/FsSystem/SectorStorage.cs +++ b/src/LibHac/Tools/FsSystem/SectorStorage.cs @@ -27,30 +27,30 @@ public class SectorStorage : IStorage LeaveOpen = leaveOpen; } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { ValidateSize(destination.Length, offset); return BaseStorage.Read(offset, destination); } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { ValidateSize(source.Length, offset); return BaseStorage.Write(offset, source); } - protected override Result DoFlush() + public override Result Flush() { return BaseStorage.Flush(); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { size = Length; return Result.Success; } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { Result rc = BaseStorage.SetSize(size); if (rc.IsFailure()) return rc; @@ -64,6 +64,12 @@ public class SectorStorage : IStorage return Result.Success; } + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + throw new NotImplementedException(); + } + public override void Dispose() { if (!LeaveOpen) diff --git a/src/LibHac/Tools/FsSystem/StreamStorage.cs b/src/LibHac/Tools/FsSystem/StreamStorage.cs index 281eea5f..b869bae3 100644 --- a/src/LibHac/Tools/FsSystem/StreamStorage.cs +++ b/src/LibHac/Tools/FsSystem/StreamStorage.cs @@ -20,7 +20,7 @@ public class StreamStorage : IStorage LeaveOpen = leaveOpen; } - protected override Result DoRead(long offset, Span destination) + public override Result Read(long offset, Span destination) { lock (Locker) { @@ -35,7 +35,7 @@ public class StreamStorage : IStorage return Result.Success; } - protected override Result DoWrite(long offset, ReadOnlySpan source) + public override Result Write(long offset, ReadOnlySpan source) { lock (Locker) { @@ -50,7 +50,7 @@ public class StreamStorage : IStorage return Result.Success; } - protected override Result DoFlush() + public override Result Flush() { lock (Locker) { @@ -60,17 +60,23 @@ public class StreamStorage : IStorage } } - protected override Result DoSetSize(long size) + public override Result SetSize(long size) { return ResultFs.NotImplemented.Log(); } - protected override Result DoGetSize(out long size) + public override Result GetSize(out long size) { size = Length; return Result.Success; } + public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size, + ReadOnlySpan inBuffer) + { + throw new NotImplementedException(); + } + public override void Dispose() { if (!LeaveOpen) From 7c3b8df56460601ec134d7a5eb85dbb3b3ff0f2e Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 28 Dec 2021 22:47:37 -0700 Subject: [PATCH 20/34] Remove unnecessary StructLayoutAttributes in FixedArrays --- src/LibHac/Common/FixedArrays/Array1.cs | 7 +++---- src/LibHac/Common/FixedArrays/Array12.cs | 7 +++---- src/LibHac/Common/FixedArrays/Array128.cs | 6 +++--- src/LibHac/Common/FixedArrays/Array16.cs | 6 +++--- src/LibHac/Common/FixedArrays/Array2.cs | 7 +++---- src/LibHac/Common/FixedArrays/Array20.cs | 4 ++-- src/LibHac/Common/FixedArrays/Array256.cs | 6 +++--- src/LibHac/Common/FixedArrays/Array3.cs | 7 +++---- src/LibHac/Common/FixedArrays/Array32.cs | 6 +++--- src/LibHac/Common/FixedArrays/Array4.cs | 7 +++---- src/LibHac/Common/FixedArrays/Array5.cs | 7 +++---- src/LibHac/Common/FixedArrays/Array512.cs | 4 ++-- src/LibHac/Common/FixedArrays/Array6.cs | 7 +++---- src/LibHac/Common/FixedArrays/Array60.cs | 4 ++-- src/LibHac/Common/FixedArrays/Array64.cs | 6 +++--- src/LibHac/Common/FixedArrays/Array769.cs | 4 ++-- src/LibHac/Common/FixedArrays/Array8.cs | 7 +++---- 17 files changed, 47 insertions(+), 55 deletions(-) diff --git a/src/LibHac/Common/FixedArrays/Array1.cs b/src/LibHac/Common/FixedArrays/Array1.cs index 34d094ba..f468055f 100644 --- a/src/LibHac/Common/FixedArrays/Array1.cs +++ b/src/LibHac/Common/FixedArrays/Array1.cs @@ -1,10 +1,9 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array1 { public const int Length = 1; @@ -18,4 +17,4 @@ public struct Array1 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array1 value) => value.ItemsRo; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array12.cs b/src/LibHac/Common/FixedArrays/Array12.cs index 102178bd..fc3d71ce 100644 --- a/src/LibHac/Common/FixedArrays/Array12.cs +++ b/src/LibHac/Common/FixedArrays/Array12.cs @@ -1,10 +1,9 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array12 { public const int Length = 12; @@ -29,4 +28,4 @@ public struct Array12 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array12 value) => value.ItemsRo; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array128.cs b/src/LibHac/Common/FixedArrays/Array128.cs index 9ad3d4ce..ce253129 100644 --- a/src/LibHac/Common/FixedArrays/Array128.cs +++ b/src/LibHac/Common/FixedArrays/Array128.cs @@ -1,10 +1,10 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array128 { public const int Length = 128; @@ -28,4 +28,4 @@ public struct Array128 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array128 value) => value.ItemsRo; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array16.cs b/src/LibHac/Common/FixedArrays/Array16.cs index b8622a94..15ad2ad6 100644 --- a/src/LibHac/Common/FixedArrays/Array16.cs +++ b/src/LibHac/Common/FixedArrays/Array16.cs @@ -1,10 +1,10 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array16 { public const int Length = 16; @@ -28,4 +28,4 @@ public struct Array16 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array16 value) => value.ItemsRo; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array2.cs b/src/LibHac/Common/FixedArrays/Array2.cs index f5b59cdb..397c1ed3 100644 --- a/src/LibHac/Common/FixedArrays/Array2.cs +++ b/src/LibHac/Common/FixedArrays/Array2.cs @@ -1,10 +1,9 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array2 { public const int Length = 2; @@ -19,4 +18,4 @@ public struct Array2 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array2 value) => value.ItemsRo; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array20.cs b/src/LibHac/Common/FixedArrays/Array20.cs index 5edeba12..6d0f12c1 100644 --- a/src/LibHac/Common/FixedArrays/Array20.cs +++ b/src/LibHac/Common/FixedArrays/Array20.cs @@ -1,10 +1,10 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array20 { public const int Length = 20; diff --git a/src/LibHac/Common/FixedArrays/Array256.cs b/src/LibHac/Common/FixedArrays/Array256.cs index 2c9a42e7..e6b7b923 100644 --- a/src/LibHac/Common/FixedArrays/Array256.cs +++ b/src/LibHac/Common/FixedArrays/Array256.cs @@ -1,10 +1,10 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array256 { public const int Length = 256; @@ -28,4 +28,4 @@ public struct Array256 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array256 value) => value.ItemsRo; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array3.cs b/src/LibHac/Common/FixedArrays/Array3.cs index c075228f..8a953a33 100644 --- a/src/LibHac/Common/FixedArrays/Array3.cs +++ b/src/LibHac/Common/FixedArrays/Array3.cs @@ -1,10 +1,9 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array3 { public const int Length = 3; @@ -20,4 +19,4 @@ public struct Array3 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array3 value) => value.ItemsRo; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array32.cs b/src/LibHac/Common/FixedArrays/Array32.cs index ae73b597..1004dc18 100644 --- a/src/LibHac/Common/FixedArrays/Array32.cs +++ b/src/LibHac/Common/FixedArrays/Array32.cs @@ -1,10 +1,10 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array32 { public const int Length = 32; @@ -28,4 +28,4 @@ public struct Array32 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array32 value) => value.ItemsRo; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array4.cs b/src/LibHac/Common/FixedArrays/Array4.cs index 22a9aad6..ea867bec 100644 --- a/src/LibHac/Common/FixedArrays/Array4.cs +++ b/src/LibHac/Common/FixedArrays/Array4.cs @@ -1,10 +1,9 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array4 { public const int Length = 4; @@ -21,4 +20,4 @@ public struct Array4 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array4 value) => value.ItemsRo; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array5.cs b/src/LibHac/Common/FixedArrays/Array5.cs index 0fa75f3c..b15e3470 100644 --- a/src/LibHac/Common/FixedArrays/Array5.cs +++ b/src/LibHac/Common/FixedArrays/Array5.cs @@ -1,10 +1,9 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array5 { public const int Length = 5; @@ -22,4 +21,4 @@ public struct Array5 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array5 value) => value.ItemsRo; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array512.cs b/src/LibHac/Common/FixedArrays/Array512.cs index 64417923..1a617bb2 100644 --- a/src/LibHac/Common/FixedArrays/Array512.cs +++ b/src/LibHac/Common/FixedArrays/Array512.cs @@ -1,10 +1,10 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array512 { public const int Length = 512; diff --git a/src/LibHac/Common/FixedArrays/Array6.cs b/src/LibHac/Common/FixedArrays/Array6.cs index 0fe6087c..ac181645 100644 --- a/src/LibHac/Common/FixedArrays/Array6.cs +++ b/src/LibHac/Common/FixedArrays/Array6.cs @@ -1,10 +1,9 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array6 { public const int Length = 6; @@ -23,4 +22,4 @@ public struct Array6 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array6 value) => value.ItemsRo; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array60.cs b/src/LibHac/Common/FixedArrays/Array60.cs index f7a04c95..3cab17e7 100644 --- a/src/LibHac/Common/FixedArrays/Array60.cs +++ b/src/LibHac/Common/FixedArrays/Array60.cs @@ -1,10 +1,10 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array60 { public const int Length = 60; diff --git a/src/LibHac/Common/FixedArrays/Array64.cs b/src/LibHac/Common/FixedArrays/Array64.cs index 9c49f837..7f63f75a 100644 --- a/src/LibHac/Common/FixedArrays/Array64.cs +++ b/src/LibHac/Common/FixedArrays/Array64.cs @@ -1,10 +1,10 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array64 { public const int Length = 64; @@ -28,4 +28,4 @@ public struct Array64 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array64 value) => value.ItemsRo; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array769.cs b/src/LibHac/Common/FixedArrays/Array769.cs index 2e0e3167..78fbad09 100644 --- a/src/LibHac/Common/FixedArrays/Array769.cs +++ b/src/LibHac/Common/FixedArrays/Array769.cs @@ -1,10 +1,10 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array769 { public const int Length = 769; diff --git a/src/LibHac/Common/FixedArrays/Array8.cs b/src/LibHac/Common/FixedArrays/Array8.cs index 60e39447..ac664470 100644 --- a/src/LibHac/Common/FixedArrays/Array8.cs +++ b/src/LibHac/Common/FixedArrays/Array8.cs @@ -1,10 +1,9 @@ -using System; +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace LibHac.Common.FixedArrays; -[StructLayout(LayoutKind.Sequential)] public struct Array8 { public const int Length = 8; @@ -25,4 +24,4 @@ public struct Array8 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array8 value) => value.ItemsRo; -} +} \ No newline at end of file From 53e5029ef4894e101aa88eaf564f343df6037f72 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 28 Dec 2021 22:53:09 -0700 Subject: [PATCH 21/34] Remove StructLayout and fix naming in KeySet --- build/CodeGen/Stage2/KeysCodeGen.cs | 22 ++--- src/LibHac/Common/Keys/DefaultKeySet.cs | 24 ++--- src/LibHac/Common/Keys/KeySet.cs | 117 +++++++++++------------- 3 files changed, 77 insertions(+), 86 deletions(-) diff --git a/build/CodeGen/Stage2/KeysCodeGen.cs b/build/CodeGen/Stage2/KeysCodeGen.cs index 06557425..21c60f98 100644 --- a/build/CodeGen/Stage2/KeysCodeGen.cs +++ b/build/CodeGen/Stage2/KeysCodeGen.cs @@ -37,17 +37,17 @@ public static class KeysCodeGen sb.AppendLine("internal static partial class DefaultKeySet"); sb.AppendLineAndIncrease("{"); - BuildArray(sb, "RootKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._rootKeysDev)); - BuildArray(sb, "RootKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._rootKeysProd)); - BuildArray(sb, "KeySeeds", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._keySeeds)); - BuildArray(sb, "StoredKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._storedKeysDev)); - BuildArray(sb, "StoredKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._storedKeysProd)); - BuildArray(sb, "DerivedKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._derivedKeysDev)); - BuildArray(sb, "DerivedKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._derivedKeysProd)); - BuildArray(sb, "DeviceKeys", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._deviceKeys)); - BuildArray(sb, "RsaSigningKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._rsaSigningKeysDev)); - BuildArray(sb, "RsaSigningKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._rsaSigningKeysProd)); - BuildArray(sb, "RsaKeys", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._rsaKeys)); + BuildArray(sb, "RootKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.RootKeysDev)); + BuildArray(sb, "RootKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.RootKeysProd)); + BuildArray(sb, "KeySeeds", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.KeySeeds)); + BuildArray(sb, "StoredKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.StoredKeysDev)); + BuildArray(sb, "StoredKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.StoredKeysProd)); + BuildArray(sb, "DerivedKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.DerivedKeysDev)); + BuildArray(sb, "DerivedKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.DerivedKeysProd)); + BuildArray(sb, "DeviceKeys", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.DeviceKeys)); + BuildArray(sb, "RsaSigningKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.RsaSigningKeysDev)); + BuildArray(sb, "RsaSigningKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.RsaSigningKeysProd)); + BuildArray(sb, "RsaKeys", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.RsaKeys)); sb.DecreaseAndAppendLine("}"); diff --git a/src/LibHac/Common/Keys/DefaultKeySet.cs b/src/LibHac/Common/Keys/DefaultKeySet.cs index 25cbd7d9..eaa95a3f 100644 --- a/src/LibHac/Common/Keys/DefaultKeySet.cs +++ b/src/LibHac/Common/Keys/DefaultKeySet.cs @@ -19,57 +19,57 @@ internal static partial class DefaultKeySet // Fill the key set with any key structs included in the library. if (RootKeysDev.Length == Unsafe.SizeOf()) { - keySet.KeyStruct._rootKeysDev = MemoryMarshal.Cast(RootKeysDev)[0]; + keySet.KeyStruct.RootKeysDev = MemoryMarshal.Cast(RootKeysDev)[0]; } if (RootKeysProd.Length == Unsafe.SizeOf()) { - keySet.KeyStruct._rootKeysProd = MemoryMarshal.Cast(RootKeysProd)[0]; + keySet.KeyStruct.RootKeysProd = MemoryMarshal.Cast(RootKeysProd)[0]; } if (KeySeeds.Length == Unsafe.SizeOf()) { - keySet.KeyStruct._keySeeds = MemoryMarshal.Cast(KeySeeds)[0]; + keySet.KeyStruct.KeySeeds = MemoryMarshal.Cast(KeySeeds)[0]; } if (StoredKeysDev.Length == Unsafe.SizeOf()) { - keySet.KeyStruct._storedKeysDev = MemoryMarshal.Cast(StoredKeysDev)[0]; + keySet.KeyStruct.StoredKeysDev = MemoryMarshal.Cast(StoredKeysDev)[0]; } if (StoredKeysProd.Length == Unsafe.SizeOf()) { - keySet.KeyStruct._storedKeysProd = MemoryMarshal.Cast(StoredKeysProd)[0]; + keySet.KeyStruct.StoredKeysProd = MemoryMarshal.Cast(StoredKeysProd)[0]; } if (DerivedKeysDev.Length == Unsafe.SizeOf()) { - keySet.KeyStruct._derivedKeysDev = MemoryMarshal.Cast(DerivedKeysDev)[0]; + keySet.KeyStruct.DerivedKeysDev = MemoryMarshal.Cast(DerivedKeysDev)[0]; } if (DerivedKeysProd.Length == Unsafe.SizeOf()) { - keySet.KeyStruct._derivedKeysProd = MemoryMarshal.Cast(DerivedKeysProd)[0]; + keySet.KeyStruct.DerivedKeysProd = MemoryMarshal.Cast(DerivedKeysProd)[0]; } if (DeviceKeys.Length == Unsafe.SizeOf()) { - keySet.KeyStruct._deviceKeys = MemoryMarshal.Cast(DeviceKeys)[0]; + keySet.KeyStruct.DeviceKeys = MemoryMarshal.Cast(DeviceKeys)[0]; } if (RsaSigningKeysDev.Length == Unsafe.SizeOf()) { - keySet.KeyStruct._rsaSigningKeysDev = MemoryMarshal.Cast(RsaSigningKeysDev)[0]; + keySet.KeyStruct.RsaSigningKeysDev = MemoryMarshal.Cast(RsaSigningKeysDev)[0]; } if (RsaSigningKeysProd.Length == Unsafe.SizeOf()) { - keySet.KeyStruct._rsaSigningKeysProd = MemoryMarshal.Cast(RsaSigningKeysProd)[0]; + keySet.KeyStruct.RsaSigningKeysProd = MemoryMarshal.Cast(RsaSigningKeysProd)[0]; } if (RsaKeys.Length == Unsafe.SizeOf()) { - keySet.KeyStruct._rsaKeys = MemoryMarshal.Cast(RsaKeys)[0]; + keySet.KeyStruct.RsaKeys = MemoryMarshal.Cast(RsaKeys)[0]; } return keySet; @@ -177,4 +177,4 @@ internal static partial class DefaultKeySet return keys; } -} +} \ No newline at end of file diff --git a/src/LibHac/Common/Keys/KeySet.cs b/src/LibHac/Common/Keys/KeySet.cs index 532ecc3b..32ec535a 100644 --- a/src/LibHac/Common/Keys/KeySet.cs +++ b/src/LibHac/Common/Keys/KeySet.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Runtime.InteropServices; using System.Security.Cryptography; using LibHac.Boot; using LibHac.Common.FixedArrays; @@ -31,11 +30,11 @@ public class KeySet public ref AllKeys KeyStruct => ref _keys; public Mode CurrentMode => _mode; - private ref RootKeys RootKeys => ref _mode == Mode.Dev ? ref _keys._rootKeysDev : ref _keys._rootKeysProd; - private ref StoredKeys StoredKeys => ref _mode == Mode.Dev ? ref _keys._storedKeysDev : ref _keys._storedKeysProd; - private ref DerivedKeys DerivedKeys => ref _mode == Mode.Dev ? ref _keys._derivedKeysDev : ref _keys._derivedKeysProd; - private ref RsaSigningKeys RsaSigningKeys => ref _mode == Mode.Dev ? ref _keys._rsaSigningKeysDev : ref _keys._rsaSigningKeysProd; - private ref RsaKeys RsaKeys => ref _keys._rsaKeys; + private ref RootKeys RootKeys => ref _mode == Mode.Dev ? ref _keys.RootKeysDev : ref _keys.RootKeysProd; + private ref StoredKeys StoredKeys => ref _mode == Mode.Dev ? ref _keys.StoredKeysDev : ref _keys.StoredKeysProd; + private ref DerivedKeys DerivedKeys => ref _mode == Mode.Dev ? ref _keys.DerivedKeysDev : ref _keys.DerivedKeysProd; + private ref RsaSigningKeys RsaSigningKeys => ref _mode == Mode.Dev ? ref _keys.RsaSigningKeysDev : ref _keys.RsaSigningKeysProd; + private ref RsaKeys RsaKeys => ref _keys.RsaKeys; private ref RsaSigningKeyParameters RsaSigningKeyParams => ref _mode == Mode.Dev ? ref _rsaSigningKeyParamsDev @@ -47,44 +46,44 @@ public class KeySet public ref AesKey MarikoKek => ref RootKeys.MarikoKek; public ref AesKey MarikoBek => ref RootKeys.MarikoBek; public Span KeyBlobs => RootKeys.KeyBlobs.Items; - public Span KeyBlobKeySources => _keys._keySeeds.KeyBlobKeySources.Items; - public ref AesKey KeyBlobMacKeySource => ref _keys._keySeeds.KeyBlobMacKeySource; + public Span KeyBlobKeySources => _keys.KeySeeds.KeyBlobKeySources.Items; + public ref AesKey KeyBlobMacKeySource => ref _keys.KeySeeds.KeyBlobMacKeySource; public ref AesKey TsecRootKek => ref RootKeys.TsecRootKek; public ref AesKey Package1MacKek => ref RootKeys.Package1MacKek; public ref AesKey Package1Kek => ref RootKeys.Package1Kek; public Span TsecAuthSignatures => RootKeys.TsecAuthSignatures.Items; public Span TsecRootKeys => RootKeys.TsecRootKeys.Items; - public Span MasterKekSources => _keys._keySeeds.MasterKekSources.Items; + public Span MasterKekSources => _keys.KeySeeds.MasterKekSources.Items; public Span MarikoMasterKekSources => _mode == Mode.Dev - ? _keys._keySeeds.MarikoMasterKekSources_dev.Items - : _keys._keySeeds.MarikoMasterKekSources.Items; + ? _keys.KeySeeds.MarikoMasterKekSourcesDev.Items + : _keys.KeySeeds.MarikoMasterKekSources.Items; public Span MasterKeks => DerivedKeys.MasterKeks.Items; - public ref AesKey MasterKeySource => ref _keys._keySeeds.MasterKeySource; + public ref AesKey MasterKeySource => ref _keys.KeySeeds.MasterKeySource; public Span MasterKeys => DerivedKeys.MasterKeys.Items; public Span Package1MacKeys => DerivedKeys.Package1MacKeys.Items; public Span Package1Keys => DerivedKeys.Package1Keys.Items; public Span Package2Keys => DerivedKeys.Package2Keys.Items; - public ref AesKey Package2KeySource => ref _keys._keySeeds.Package2KeySource; - public ref AesKey PerConsoleKeySource => ref _keys._keySeeds.PerConsoleKeySource; - public ref AesKey RetailSpecificAesKeySource => ref _keys._keySeeds.RetailSpecificAesKeySource; - public ref AesKey BisKekSource => ref _keys._keySeeds.BisKekSource; - public Span BisKeySources => _keys._keySeeds.BisKeySources.Items; - public ref AesKey AesKekGenerationSource => ref _keys._keySeeds.AesKekGenerationSource; - public ref AesKey AesKeyGenerationSource => ref _keys._keySeeds.AesKeyGenerationSource; - public ref AesKey KeyAreaKeyApplicationSource => ref _keys._keySeeds.KeyAreaKeyApplicationSource; - public ref AesKey KeyAreaKeyOceanSource => ref _keys._keySeeds.KeyAreaKeyOceanSource; - public ref AesKey KeyAreaKeySystemSource => ref _keys._keySeeds.KeyAreaKeySystemSource; - public ref AesKey TitleKekSource => ref _keys._keySeeds.TitleKekSource; - public ref AesKey HeaderKekSource => ref _keys._keySeeds.HeaderKekSource; - public ref AesKey SdCardKekSource => ref _keys._keySeeds.SdCardKekSource; - public Span SdCardKeySources => _keys._keySeeds.SdCardKeySources.Items; - public ref AesKey DeviceUniqueSaveMacKekSource => ref _keys._keySeeds.DeviceUniqueSaveMacKekSource; - public Span DeviceUniqueSaveMacKeySources => _keys._keySeeds.DeviceUniqueSaveMacKeySources.Items; - public ref AesKey SeedUniqueSaveMacKekSource => ref _keys._keySeeds.SeedUniqueSaveMacKekSource; - public ref AesKey SeedUniqueSaveMacKeySource => ref _keys._keySeeds.SeedUniqueSaveMacKeySource; - public ref AesXtsKey HeaderKeySource => ref _keys._keySeeds.HeaderKeySource; + public ref AesKey Package2KeySource => ref _keys.KeySeeds.Package2KeySource; + public ref AesKey PerConsoleKeySource => ref _keys.KeySeeds.PerConsoleKeySource; + public ref AesKey RetailSpecificAesKeySource => ref _keys.KeySeeds.RetailSpecificAesKeySource; + public ref AesKey BisKekSource => ref _keys.KeySeeds.BisKekSource; + public Span BisKeySources => _keys.KeySeeds.BisKeySources.Items; + public ref AesKey AesKekGenerationSource => ref _keys.KeySeeds.AesKekGenerationSource; + public ref AesKey AesKeyGenerationSource => ref _keys.KeySeeds.AesKeyGenerationSource; + public ref AesKey KeyAreaKeyApplicationSource => ref _keys.KeySeeds.KeyAreaKeyApplicationSource; + public ref AesKey KeyAreaKeyOceanSource => ref _keys.KeySeeds.KeyAreaKeyOceanSource; + public ref AesKey KeyAreaKeySystemSource => ref _keys.KeySeeds.KeyAreaKeySystemSource; + public ref AesKey TitleKekSource => ref _keys.KeySeeds.TitleKekSource; + public ref AesKey HeaderKekSource => ref _keys.KeySeeds.HeaderKekSource; + public ref AesKey SdCardKekSource => ref _keys.KeySeeds.SdCardKekSource; + public Span SdCardKeySources => _keys.KeySeeds.SdCardKeySources.Items; + public ref AesKey DeviceUniqueSaveMacKekSource => ref _keys.KeySeeds.DeviceUniqueSaveMacKekSource; + public Span DeviceUniqueSaveMacKeySources => _keys.KeySeeds.DeviceUniqueSaveMacKeySources.Items; + public ref AesKey SeedUniqueSaveMacKekSource => ref _keys.KeySeeds.SeedUniqueSaveMacKekSource; + public ref AesKey SeedUniqueSaveMacKeySource => ref _keys.KeySeeds.SeedUniqueSaveMacKeySource; + public ref AesXtsKey HeaderKeySource => ref _keys.KeySeeds.HeaderKeySource; public ref AesXtsKey HeaderKey => ref DerivedKeys.HeaderKey; public Span TitleKeks => DerivedKeys.TitleKeks.Items; public Span> KeyAreaKeys => DerivedKeys.KeyAreaKeys.Items; @@ -92,19 +91,19 @@ public class KeySet public ref AesKey ETicketRsaKek => ref DerivedKeys.ETicketRsaKek; public ref AesKey SslRsaKek => ref DerivedKeys.SslRsaKek; - public ref AesKey SecureBootKey => ref _keys._deviceKeys.SecureBootKey; - public ref AesKey TsecKey => ref _keys._deviceKeys.TsecKey; - public Span KeyBlobKeys => _keys._deviceKeys.KeyBlobKeys.Items; - public Span KeyBlobMacKeys => _keys._deviceKeys.KeyBlobMacKeys.Items; - public Span EncryptedKeyBlobs => _keys._deviceKeys.EncryptedKeyBlobs.Items; - public ref AesKey DeviceKey => ref _keys._deviceKeys.DeviceKey; - public Span BisKeys => _keys._deviceKeys.BisKeys.Items; - public Span DeviceUniqueSaveMacKeys => _keys._deviceKeys.DeviceUniqueSaveMacKeys.Items; - public ref AesKey SeedUniqueSaveMacKey => ref _keys._deviceKeys.SeedUniqueSaveMacKey; - public ref AesKey SdCardEncryptionSeed => ref _keys._deviceKeys.SdCardEncryptionSeed; + public ref AesKey SecureBootKey => ref _keys.DeviceKeys.SecureBootKey; + public ref AesKey TsecKey => ref _keys.DeviceKeys.TsecKey; + public Span KeyBlobKeys => _keys.DeviceKeys.KeyBlobKeys.Items; + public Span KeyBlobMacKeys => _keys.DeviceKeys.KeyBlobMacKeys.Items; + public Span EncryptedKeyBlobs => _keys.DeviceKeys.EncryptedKeyBlobs.Items; + public ref AesKey DeviceKey => ref _keys.DeviceKeys.DeviceKey; + public Span BisKeys => _keys.DeviceKeys.BisKeys.Items; + public Span DeviceUniqueSaveMacKeys => _keys.DeviceKeys.DeviceUniqueSaveMacKeys.Items; + public ref AesKey SeedUniqueSaveMacKey => ref _keys.DeviceKeys.SeedUniqueSaveMacKey; + public ref AesKey SdCardEncryptionSeed => ref _keys.DeviceKeys.SdCardEncryptionSeed; // Todo: Make a separate type? Not actually an AES-XTS key, but it's still the same shape. - public Span SdCardEncryptionKeys => _keys._deviceKeys.SdCardEncryptionKeys.Items; + public Span SdCardEncryptionKeys => _keys.DeviceKeys.SdCardEncryptionKeys.Items; public Span NcaHeaderSigningKeys => RsaSigningKeys.NcaHeaderSigningKeys.Items; public Span AcidSigningKeys => RsaSigningKeys.AcidSigningKeys.Items; @@ -259,23 +258,21 @@ public class KeySet } } -[StructLayout(LayoutKind.Sequential)] public struct AllKeys { - public RootKeys _rootKeysDev; - public RootKeys _rootKeysProd; - public KeySeeds _keySeeds; - public StoredKeys _storedKeysDev; - public StoredKeys _storedKeysProd; - public DerivedKeys _derivedKeysDev; - public DerivedKeys _derivedKeysProd; - public DeviceKeys _deviceKeys; - public RsaSigningKeys _rsaSigningKeysDev; - public RsaSigningKeys _rsaSigningKeysProd; - public RsaKeys _rsaKeys; + public RootKeys RootKeysDev; + public RootKeys RootKeysProd; + public KeySeeds KeySeeds; + public StoredKeys StoredKeysDev; + public StoredKeys StoredKeysProd; + public DerivedKeys DerivedKeysDev; + public DerivedKeys DerivedKeysProd; + public DeviceKeys DeviceKeys; + public RsaSigningKeys RsaSigningKeysDev; + public RsaSigningKeys RsaSigningKeysProd; + public RsaKeys RsaKeys; } -[StructLayout(LayoutKind.Sequential)] public struct RootKeys { // Mariko keys. The AES class keys are currently unused. @@ -298,14 +295,13 @@ public struct RootKeys public Array32 TsecRootKeys; } -[StructLayout(LayoutKind.Sequential)] public struct KeySeeds { public Array32 KeyBlobKeySources; public AesKey KeyBlobMacKeySource; public Array32 MasterKekSources; public Array32 MarikoMasterKekSources; - public Array32 MarikoMasterKekSources_dev; + public Array32 MarikoMasterKekSourcesDev; public AesKey MasterKeySource; public AesKey Package2KeySource; public AesKey PerConsoleKeySource; @@ -331,13 +327,11 @@ public struct KeySeeds /// /// Holds keys that are stored directly in Horizon programs. /// -[StructLayout(LayoutKind.Sequential)] public struct StoredKeys { public AesKey XciHeaderKey; } -[StructLayout(LayoutKind.Sequential)] public struct DerivedKeys { public Array32 MasterKeks; @@ -352,7 +346,6 @@ public struct DerivedKeys public AesKey SslRsaKek; } -[StructLayout(LayoutKind.Sequential)] public struct DeviceKeys { public AesKey SecureBootKey; @@ -368,7 +361,6 @@ public struct DeviceKeys public Array3 SdCardEncryptionKeys; } -[StructLayout(LayoutKind.Sequential)] public struct RsaSigningKeys { public Array2 NcaHeaderSigningKeys; @@ -376,8 +368,7 @@ public struct RsaSigningKeys public RsaKey Package2SigningKey; } -[StructLayout(LayoutKind.Sequential)] public struct RsaKeys { public RsaFullKey BetaNca0KeyAreaKey; -} +} \ No newline at end of file From 5013e7d2ec9f03ba807fd15c811857d65f6e313e Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 29 Dec 2021 10:06:02 -0700 Subject: [PATCH 22/34] Use fixed array structs instead of Data* structs --- build/CodeGen/Stage2/KeysCodeGen.cs | 56 +++++++++---------- src/LibHac/Common/Keys/KeySet.cs | 20 +++---- src/LibHac/Crypto/KeyTypes.cs | 51 ++++------------- src/LibHac/Fat/FatError.cs | 20 +++---- src/LibHac/Fs/Fsa/MountUtility.cs | 2 +- src/LibHac/Fs/MountName.cs | 8 +-- .../SaveDataTransferCryptoConfiguration.cs | 20 +++---- tests/LibHac.Tests/Common/Layout.cs | 19 +++++++ tests/LibHac.Tests/Fat/TypeLayoutTests.cs | 23 ++++++++ 9 files changed, 114 insertions(+), 105 deletions(-) create mode 100644 tests/LibHac.Tests/Common/Layout.cs create mode 100644 tests/LibHac.Tests/Fat/TypeLayoutTests.cs diff --git a/build/CodeGen/Stage2/KeysCodeGen.cs b/build/CodeGen/Stage2/KeysCodeGen.cs index 21c60f98..9e5152f8 100644 --- a/build/CodeGen/Stage2/KeysCodeGen.cs +++ b/build/CodeGen/Stage2/KeysCodeGen.cs @@ -99,46 +99,46 @@ public static class KeysCodeGen RSAParameters betaNca0Params = Rsa.RecoverParameters(BetaNca0Modulus, StandardPublicExponent, BetaNca0Exponent); - betaNca0Params.D.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.PrivateExponent.Data); - betaNca0Params.DP.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Dp.Data); - betaNca0Params.DQ.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Dq.Data); - betaNca0Params.Exponent.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.PublicExponent.Data); - betaNca0Params.InverseQ.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.InverseQ.Data); - betaNca0Params.Modulus.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Modulus.Data); - betaNca0Params.P.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.P.Data); - betaNca0Params.Q.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Q.Data); + betaNca0Params.D.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.PrivateExponent.Items); + betaNca0Params.DP.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Dp.Items); + betaNca0Params.DQ.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Dq.Items); + betaNca0Params.Exponent.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.PublicExponent.Items); + betaNca0Params.InverseQ.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.InverseQ.Items); + betaNca0Params.Modulus.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Modulus.Items); + betaNca0Params.P.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.P.Items); + betaNca0Params.Q.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Q.Items); // First populate the prod RSA keys keySet.SetMode(KeySet.Mode.Prod); - StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[0].PublicExponent.Data); - StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[1].PublicExponent.Data); - NcaHdrFixedKeyModulus0Prod.CopyTo(keySet.NcaHeaderSigningKeys[0].Modulus.Data); - NcaHdrFixedKeyModulus1Prod.CopyTo(keySet.NcaHeaderSigningKeys[1].Modulus.Data); + StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[0].PublicExponent.Items); + StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[1].PublicExponent.Items); + NcaHdrFixedKeyModulus0Prod.CopyTo(keySet.NcaHeaderSigningKeys[0].Modulus.Items); + NcaHdrFixedKeyModulus1Prod.CopyTo(keySet.NcaHeaderSigningKeys[1].Modulus.Items); - StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[0].PublicExponent.Data); - StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[1].PublicExponent.Data); - AcidFixedKeyModulus0Prod.CopyTo(keySet.AcidSigningKeys[0].Modulus.Data); - AcidFixedKeyModulus1Prod.CopyTo(keySet.AcidSigningKeys[1].Modulus.Data); + StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[0].PublicExponent.Items); + StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[1].PublicExponent.Items); + AcidFixedKeyModulus0Prod.CopyTo(keySet.AcidSigningKeys[0].Modulus.Items); + AcidFixedKeyModulus1Prod.CopyTo(keySet.AcidSigningKeys[1].Modulus.Items); - StandardPublicExponent.CopyTo(keySet.Package2SigningKey.PublicExponent.Data); - Package2FixedKeyModulusProd.CopyTo(keySet.Package2SigningKey.Modulus.Data); + StandardPublicExponent.CopyTo(keySet.Package2SigningKey.PublicExponent.Items); + Package2FixedKeyModulusProd.CopyTo(keySet.Package2SigningKey.Modulus.Items); // Populate the dev RSA keys keySet.SetMode(KeySet.Mode.Dev); - StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[0].PublicExponent.Data); - StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[1].PublicExponent.Data); - NcaHdrFixedKeyModulus0Dev.CopyTo(keySet.NcaHeaderSigningKeys[0].Modulus.Data); - NcaHdrFixedKeyModulus1Dev.CopyTo(keySet.NcaHeaderSigningKeys[1].Modulus.Data); + StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[0].PublicExponent.Items); + StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[1].PublicExponent.Items); + NcaHdrFixedKeyModulus0Dev.CopyTo(keySet.NcaHeaderSigningKeys[0].Modulus.Items); + NcaHdrFixedKeyModulus1Dev.CopyTo(keySet.NcaHeaderSigningKeys[1].Modulus.Items); - StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[0].PublicExponent.Data); - StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[1].PublicExponent.Data); - AcidFixedKeyModulus0Dev.CopyTo(keySet.AcidSigningKeys[0].Modulus.Data); - AcidFixedKeyModulus1Dev.CopyTo(keySet.AcidSigningKeys[1].Modulus.Data); + StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[0].PublicExponent.Items); + StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[1].PublicExponent.Items); + AcidFixedKeyModulus0Dev.CopyTo(keySet.AcidSigningKeys[0].Modulus.Items); + AcidFixedKeyModulus1Dev.CopyTo(keySet.AcidSigningKeys[1].Modulus.Items); - StandardPublicExponent.CopyTo(keySet.Package2SigningKey.PublicExponent.Data); - Package2FixedKeyModulusDev.CopyTo(keySet.Package2SigningKey.Modulus.Data); + StandardPublicExponent.CopyTo(keySet.Package2SigningKey.PublicExponent.Items); + Package2FixedKeyModulusDev.CopyTo(keySet.Package2SigningKey.Modulus.Items); return keySet; } diff --git a/src/LibHac/Common/Keys/KeySet.cs b/src/LibHac/Common/Keys/KeySet.cs index 32ec535a..95974f29 100644 --- a/src/LibHac/Common/Keys/KeySet.cs +++ b/src/LibHac/Common/Keys/KeySet.cs @@ -225,8 +225,8 @@ public class KeySet { return new RSAParameters { - Exponent = key.PublicExponent.DataRo.ToArray(), - Modulus = key.Modulus.DataRo.ToArray() + Exponent = key.PublicExponent.ItemsRo.ToArray(), + Modulus = key.Modulus.ItemsRo.ToArray() }; } @@ -234,14 +234,14 @@ public class KeySet { return new RSAParameters { - D = key.PrivateExponent.DataRo.ToArray(), - DP = key.Dp.DataRo.ToArray(), - DQ = key.Dq.DataRo.ToArray(), - Exponent = key.PublicExponent.DataRo.ToArray(), - InverseQ = key.InverseQ.DataRo.ToArray(), - Modulus = key.Modulus.DataRo.ToArray(), - P = key.P.DataRo.ToArray(), - Q = key.Q.DataRo.ToArray() + D = key.PrivateExponent.ItemsRo.ToArray(), + DP = key.Dp.ItemsRo.ToArray(), + DQ = key.Dq.ItemsRo.ToArray(), + Exponent = key.PublicExponent.ItemsRo.ToArray(), + InverseQ = key.InverseQ.ItemsRo.ToArray(), + Modulus = key.Modulus.ItemsRo.ToArray(), + P = key.P.ItemsRo.ToArray(), + Q = key.Q.ItemsRo.ToArray() }; } diff --git a/src/LibHac/Crypto/KeyTypes.cs b/src/LibHac/Crypto/KeyTypes.cs index 083d53e8..01f38993 100644 --- a/src/LibHac/Crypto/KeyTypes.cs +++ b/src/LibHac/Crypto/KeyTypes.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Util; namespace LibHac.Crypto; @@ -117,49 +118,21 @@ public struct AesCmac #endif } -[StructLayout(LayoutKind.Sequential)] public struct RsaFullKey { - public Data100 PrivateExponent; - public Data80 Dp; - public Data80 Dq; - public Data3 PublicExponent; - public Data80 InverseQ; - public Data100 Modulus; - public Data80 P; - public Data80 Q; + // ReSharper disable once UnassignedField.Global + public Array256 PrivateExponent; + public Array128 Dp; + public Array128 Dq; + public Array3 PublicExponent; + public Array128 InverseQ; + public Array256 Modulus; + public Array128 P; + public Array128 Q; } -[StructLayout(LayoutKind.Sequential)] public struct RsaKey { - public Data100 Modulus; - public Data3 PublicExponent; -} - -[StructLayout(LayoutKind.Explicit, Size = 0x100)] -public struct Data100 -{ - [FieldOffset(0)] private byte _byte; - - public Span Data => SpanHelpers.CreateSpan(ref _byte, 0x100); - public readonly ReadOnlySpan DataRo => SpanHelpers.CreateReadOnlySpan(in _byte, 0x100); -} - -[StructLayout(LayoutKind.Explicit, Size = 0x80)] -public struct Data80 -{ - [FieldOffset(0)] private byte _byte; - - public Span Data => SpanHelpers.CreateSpan(ref _byte, 0x80); - public readonly ReadOnlySpan DataRo => SpanHelpers.CreateReadOnlySpan(in _byte, 0x80); -} - -[StructLayout(LayoutKind.Explicit, Size = 3)] -public struct Data3 -{ - [FieldOffset(0)] private byte _byte; - - public Span Data => SpanHelpers.CreateSpan(ref _byte, 3); - public readonly ReadOnlySpan DataRo => SpanHelpers.CreateReadOnlySpan(in _byte, 3); + public Array256 Modulus; + public Array3 PublicExponent; } \ No newline at end of file diff --git a/src/LibHac/Fat/FatError.cs b/src/LibHac/Fat/FatError.cs index bef2cf1a..f558cd22 100644 --- a/src/LibHac/Fat/FatError.cs +++ b/src/LibHac/Fat/FatError.cs @@ -1,18 +1,12 @@ -using System.Runtime.InteropServices; -using LibHac.Common; +using LibHac.Common.FixedArrays; namespace LibHac.Fat; -[StructLayout(LayoutKind.Explicit, Size = 0x20)] public struct FatError { - private const int FunctionNameLength = 0x10; - - [FieldOffset(0x00)] public int Error; - [FieldOffset(0x04)] public int ExtraError; - [FieldOffset(0x08)] public int DriveId; - [FieldOffset(0x0C)] private byte _functionName; - - public U8SpanMutable ErrorName => - new U8SpanMutable(SpanHelpers.CreateSpan(ref _functionName, FunctionNameLength)); -} + public int Error; + public int ExtraError; + public int DriveId; + public Array16 ErrorName; + public int Reserved; +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/MountUtility.cs b/src/LibHac/Fs/Fsa/MountUtility.cs index 09182350..38096e77 100644 --- a/src/LibHac/Fs/Fsa/MountUtility.cs +++ b/src/LibHac/Fs/Fsa/MountUtility.cs @@ -11,7 +11,7 @@ namespace LibHac.Fs.Fsa; public static class MountUtility { - internal static Result GetMountNameAndSubPath(out MountName mountName, out U8Span subPath, U8Span path) + private static Result GetMountNameAndSubPath(out MountName mountName, out U8Span subPath, U8Span path) { UnsafeHelpers.SkipParamInit(out mountName); subPath = default; diff --git a/src/LibHac/Fs/MountName.cs b/src/LibHac/Fs/MountName.cs index 8f6c8d35..52ca771c 100644 --- a/src/LibHac/Fs/MountName.cs +++ b/src/LibHac/Fs/MountName.cs @@ -1,15 +1,15 @@ using System; using System.Diagnostics; -using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Common.FixedArrays; namespace LibHac.Fs; -[StructLayout(LayoutKind.Sequential, Size = 16)] [DebuggerDisplay("{ToString()}")] internal struct MountName { - public Span Name => SpanHelpers.AsByteSpan(ref this); + private Array16 _nameArray; + public Span Name => _nameArray.Items; public override string ToString() => new U8Span(Name).ToString(); -} +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/SaveDataTransferCryptoConfiguration.cs b/src/LibHac/FsSrv/SaveDataTransferCryptoConfiguration.cs index e3c9b86a..d6b6b15d 100644 --- a/src/LibHac/FsSrv/SaveDataTransferCryptoConfiguration.cs +++ b/src/LibHac/FsSrv/SaveDataTransferCryptoConfiguration.cs @@ -1,19 +1,19 @@ using System; -using LibHac.Crypto; +using LibHac.Common.FixedArrays; namespace LibHac.FsSrv; public class SaveDataTransferCryptoConfiguration { - private Data100 _tokenSigningKeyModulus; - private Data100 _keySeedPackageSigningKeyModulus; - private Data100 _kekEncryptionKeyModulus; - private Data100 _keyPackageSigningModulus; + private Array256 _tokenSigningKeyModulus; + private Array256 _keySeedPackageSigningKeyModulus; + private Array256 _kekEncryptionKeyModulus; + private Array256 _keyPackageSigningModulus; - public Span TokenSigningKeyModulus => _tokenSigningKeyModulus.Data; - public Span KeySeedPackageSigningKeyModulus => _keySeedPackageSigningKeyModulus.Data; - public Span KekEncryptionKeyModulus => _kekEncryptionKeyModulus.Data; - public Span KeyPackageSigningModulus => _keyPackageSigningModulus.Data; + public Span TokenSigningKeyModulus => _tokenSigningKeyModulus.Items; + public Span KeySeedPackageSigningKeyModulus => _keySeedPackageSigningKeyModulus.Items; + public Span KekEncryptionKeyModulus => _kekEncryptionKeyModulus.Items; + public Span KeyPackageSigningModulus => _keyPackageSigningModulus.Items; public SaveTransferAesKeyGenerator GenerateAesKey { get; set; } public RandomDataGenerator GenerateRandomData { get; set; } @@ -31,4 +31,4 @@ public class SaveDataTransferCryptoConfiguration SaveDataRepairInitialDataMacBeforeRepair, SaveDataRepairInitialDataMacAfterRepair } -} +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Common/Layout.cs b/tests/LibHac.Tests/Common/Layout.cs new file mode 100644 index 00000000..1a0cb743 --- /dev/null +++ b/tests/LibHac.Tests/Common/Layout.cs @@ -0,0 +1,19 @@ +using System; +using System.Runtime.CompilerServices; + +namespace LibHac.Tests.Common; + +public class Layout +{ + public static int GetOffset(in TStruct structRef, in TField fieldRef) + { + ref TField structOffset = ref Unsafe.As(ref Unsafe.AsRef(in structRef)); + ref TField fieldOffset = ref Unsafe.AsRef(in fieldRef); + int offset = Unsafe.ByteOffset(ref structOffset, ref fieldOffset).ToInt32(); + + if (offset >= Unsafe.SizeOf()) + throw new ArgumentException($"Error getting field offset. {nameof(structRef)} and {nameof(fieldRef)} must be from the same struct instance."); + + return offset; + } +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Fat/TypeLayoutTests.cs b/tests/LibHac.Tests/Fat/TypeLayoutTests.cs new file mode 100644 index 00000000..30c82b21 --- /dev/null +++ b/tests/LibHac.Tests/Fat/TypeLayoutTests.cs @@ -0,0 +1,23 @@ +using System.Runtime.CompilerServices; +using LibHac.Fat; +using Xunit; +using static LibHac.Tests.Common.Layout; + +namespace LibHac.Tests.Fat; + +public class TypeLayoutTests +{ + [Fact] + public static void FatError_Layout() + { + var s = new FatError(); + + Assert.Equal(0x20, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Error)); + Assert.Equal(0x04, GetOffset(in s, in s.ExtraError)); + Assert.Equal(0x08, GetOffset(in s, in s.DriveId)); + Assert.Equal(0x0C, GetOffset(in s, in s.ErrorName)); + Assert.Equal(0x1C, GetOffset(in s, in s.Reserved)); + } +} \ No newline at end of file From 53809023763135e2fe45082d0e7f2a46674a296a Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 29 Dec 2021 10:13:42 -0700 Subject: [PATCH 23/34] Rename some save enum/struct fields --- .../Fs/ApplicationSaveDataManagement.cs | 8 +-- src/LibHac/Fs/FsEnums.cs | 6 +-- src/LibHac/Fs/SaveDataStructs.cs | 6 +-- src/LibHac/FsSrv/SaveDataFileSystemService.cs | 12 ++--- .../FsSrv/SaveDataFileSystemServiceImpl.cs | 54 +++++++++---------- src/LibHac/FsSrv/SaveDataIndexerManager.cs | 6 +-- .../ApplicationSaveDataManagementTests.cs | 4 +- .../ShimTests/SaveData.cs | 6 +-- .../ShimTests/SaveDataManagement.cs | 8 +-- 9 files changed, 55 insertions(+), 55 deletions(-) diff --git a/src/LibHac/Fs/ApplicationSaveDataManagement.cs b/src/LibHac/Fs/ApplicationSaveDataManagement.cs index 549e3e08..84c67d03 100644 --- a/src/LibHac/Fs/ApplicationSaveDataManagement.cs +++ b/src/LibHac/Fs/ApplicationSaveDataManagement.cs @@ -253,7 +253,7 @@ public static class ApplicationSaveDataManagement } else if (targetMedia == CacheStorageTargetMedia.SdCard) { - rc = TryCreateCacheStorage(fs, out requiredSizeLocal, SaveDataSpaceId.SdCache, applicationId, + rc = TryCreateCacheStorage(fs, out requiredSizeLocal, SaveDataSpaceId.SdUser, applicationId, saveDataOwnerId, index, dataSize, journalSize, allowExisting); if (rc.IsFailure()) return rc; } @@ -265,7 +265,7 @@ public static class ApplicationSaveDataManagement { target = CacheStorageTargetMedia.SdCard; - Result CreateFuncSdCard() => fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdCache, + Result CreateFuncSdCard() => fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdUser, saveDataOwnerId, index, dataSize, journalSize, SaveDataFlags.None); rc = CreateSaveData(fs, CreateFuncSdCard, ref requiredSizeLocal, 0x4000, dataSize, journalSize); @@ -420,7 +420,7 @@ public static class ApplicationSaveDataManagement if (fs.IsSdCardAccessible()) { - Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.SdCache, in filter); + Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.SdUser, in filter); if (rc.IsFailure() && !ResultFs.TargetNotFound.Includes(rc)) return rc; if (rc.IsSuccess()) @@ -471,4 +471,4 @@ public static class ApplicationSaveDataManagement { return new UserId(uid.Id.High, uid.Id.Low); } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/FsEnums.cs b/src/LibHac/Fs/FsEnums.cs index cac7c8bf..48f82d31 100644 --- a/src/LibHac/Fs/FsEnums.cs +++ b/src/LibHac/Fs/FsEnums.cs @@ -52,7 +52,7 @@ public enum SaveDataSpaceId : byte User = 1, SdSystem = 2, Temporary = 3, - SdCache = 4, + SdUser = 4, ProperSystem = 100, SafeMode = 101, BisAuto = 127 @@ -260,5 +260,5 @@ public enum SdCardSpeedMode Sdr50 = 5, Sdr104 = 6, Ddr50 = 7, - Unknown = 8, -} + Unknown = 8 +} \ No newline at end of file diff --git a/src/LibHac/Fs/SaveDataStructs.cs b/src/LibHac/Fs/SaveDataStructs.cs index 09f39c14..6a691612 100644 --- a/src/LibHac/Fs/SaveDataStructs.cs +++ b/src/LibHac/Fs/SaveDataStructs.cs @@ -118,7 +118,7 @@ public struct SaveDataCreationInfo [FieldOffset(0x18)] public ulong OwnerId; [FieldOffset(0x20)] public SaveDataFlags Flags; [FieldOffset(0x24)] public SaveDataSpaceId SpaceId; - [FieldOffset(0x25)] public bool Field25; + [FieldOffset(0x25)] public bool IsPseudoSaveData; public static Result Make(out SaveDataCreationInfo creationInfo, long size, long journalSize, ulong ownerId, SaveDataFlags flags, SaveDataSpaceId spaceId) @@ -132,7 +132,7 @@ public struct SaveDataCreationInfo tempCreationInfo.OwnerId = ownerId; tempCreationInfo.Flags = flags; tempCreationInfo.SpaceId = spaceId; - tempCreationInfo.Field25 = false; + tempCreationInfo.IsPseudoSaveData = false; if (!SaveDataTypesValidity.IsValid(in tempCreationInfo)) return ResultFs.InvalidArgument.Log(); @@ -331,7 +331,7 @@ internal static class SaveDataTypesValidity public static bool IsValid(in SaveDataSpaceId spaceId) { - return (uint)spaceId <= (uint)SaveDataSpaceId.SdCache || spaceId == SaveDataSpaceId.ProperSystem || + return (uint)spaceId <= (uint)SaveDataSpaceId.SdUser || spaceId == SaveDataSpaceId.ProperSystem || spaceId == SaveDataSpaceId.SafeMode; } diff --git a/src/LibHac/FsSrv/SaveDataFileSystemService.cs b/src/LibHac/FsSrv/SaveDataFileSystemService.cs index 46e0b8e6..bf148599 100644 --- a/src/LibHac/FsSrv/SaveDataFileSystemService.cs +++ b/src/LibHac/FsSrv/SaveDataFileSystemService.cs @@ -102,7 +102,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave break; case SaveDataSpaceId.User: case SaveDataSpaceId.Temporary: - case SaveDataSpaceId.SdCache: + case SaveDataSpaceId.SdUser: if (!programInfo.AccessControl.CanCall(OperationType.OpenSaveDataInfoReader)) return ResultFs.PermissionDenied.Log(); break; @@ -1751,7 +1751,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave Result rc = GetProgramInfo(out ProgramInfo programInfo); if (rc.IsFailure()) return rc; - if (spaceId != SaveDataSpaceId.SdCache && spaceId != SaveDataSpaceId.User) + if (spaceId != SaveDataSpaceId.SdUser && spaceId != SaveDataSpaceId.User) return ResultFs.InvalidSaveDataSpaceId.Log(); using var filterReader = new UniqueRef(); @@ -1810,12 +1810,12 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave // Cache storage on the SD card will always take priority over case storage in NAND if (_serviceImpl.IsSdCardAccessible()) { - rc = SaveExists(out bool existsOnSdCard, SaveDataSpaceId.SdCache); + rc = SaveExists(out bool existsOnSdCard, SaveDataSpaceId.SdUser); if (rc.IsFailure()) return rc; if (existsOnSdCard) { - spaceId = SaveDataSpaceId.SdCache; + spaceId = SaveDataSpaceId.SdUser; return Result.Success; } } @@ -2207,7 +2207,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave return StorageType.Bis | StorageType.SdCard | StorageType.Usb; if (type == SaveDataType.System || - spaceId != SaveDataSpaceId.SdSystem && spaceId != SaveDataSpaceId.SdCache) + spaceId != SaveDataSpaceId.SdSystem && spaceId != SaveDataSpaceId.SdUser) return StorageType.Bis; return StorageType.SdCard | StorageType.Usb; @@ -2257,4 +2257,4 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave _openEntryCountSemaphore.Dispose(); _saveDataMountCountSemaphore.Dispose(); } -} +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs index 8db8ea91..5a6811da 100644 --- a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs +++ b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs @@ -659,37 +659,37 @@ public class SaveDataFileSystemServiceImpl return Result.Success; case SaveDataSpaceId.SdSystem: - case SaveDataSpaceId.SdCache: - { - rc = _config.BaseFsService.OpenSdCardProxyFileSystem(ref baseFileSystem.Ref(), true); - if (rc.IsFailure()) return rc.Miss(); + case SaveDataSpaceId.SdUser: + { + rc = _config.BaseFsService.OpenSdCardProxyFileSystem(ref baseFileSystem.Ref(), true); + if (rc.IsFailure()) return rc.Miss(); - // Hack around error CS8350. - const int bufferLength = 0x40; - Span buffer = stackalloc byte[bufferLength]; - ref byte bufferRef = ref MemoryMarshal.GetReference(buffer); - Span pathParentBuffer = MemoryMarshal.CreateSpan(ref bufferRef, bufferLength); + // Hack around error CS8350. + const int bufferLength = 0x40; + Span buffer = stackalloc byte[bufferLength]; + ref byte bufferRef = ref MemoryMarshal.GetReference(buffer); + Span pathParentBuffer = MemoryMarshal.CreateSpan(ref bufferRef, bufferLength); - using var pathParent = new Path(); - rc = PathFunctions.SetUpFixedPathSingleEntry(ref pathParent.Ref(), pathParentBuffer, - CommonPaths.SdCardNintendoRootDirectoryName); - if (rc.IsFailure()) return rc.Miss(); + using var pathParent = new Path(); + rc = PathFunctions.SetUpFixedPathSingleEntry(ref pathParent.Ref(), pathParentBuffer, + CommonPaths.SdCardNintendoRootDirectoryName); + if (rc.IsFailure()) return rc.Miss(); - using var pathSdRoot = new Path(); - rc = pathSdRoot.Combine(in pathParent, in basePath); - if (rc.IsFailure()) return rc.Miss(); + using var pathSdRoot = new Path(); + rc = pathSdRoot.Combine(in pathParent, in basePath); + if (rc.IsFailure()) return rc.Miss(); - using SharedRef tempFileSystem = - SharedRef.CreateMove(ref baseFileSystem.Ref()); - rc = Utility.WrapSubDirectory(ref baseFileSystem.Ref(), ref tempFileSystem.Ref(), in pathSdRoot, createIfMissing); - if (rc.IsFailure()) return rc.Miss(); + using SharedRef tempFileSystem = + SharedRef.CreateMove(ref baseFileSystem.Ref()); + rc = Utility.WrapSubDirectory(ref baseFileSystem.Ref(), ref tempFileSystem.Ref(), in pathSdRoot, createIfMissing); + if (rc.IsFailure()) return rc.Miss(); - rc = _config.EncryptedFsCreator.Create(ref outFileSystem, ref baseFileSystem.Ref(), - IEncryptedFileSystemCreator.KeyId.Save, in _encryptionSeed); - if (rc.IsFailure()) return rc.Miss(); + rc = _config.EncryptedFsCreator.Create(ref outFileSystem, ref baseFileSystem.Ref(), + IEncryptedFileSystemCreator.KeyId.Save, in _encryptionSeed); + if (rc.IsFailure()) return rc.Miss(); - return Result.Success; - } + return Result.Success; + } case SaveDataSpaceId.ProperSystem: rc = _config.BaseFsService.OpenBisFileSystem(ref baseFileSystem.Ref(), @@ -721,7 +721,7 @@ public class SaveDataFileSystemServiceImpl _config.SaveFsCreator.SetSdCardEncryptionSeed(seed.Value); _config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdSystem); - _config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdCache); + _config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdUser); return Result.Success; } @@ -817,4 +817,4 @@ public class SaveDataFileSystemServiceImpl { _config.SaveIndexerManager.ResetIndexer(SaveDataSpaceId.Temporary); } -} +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/SaveDataIndexerManager.cs b/src/LibHac/FsSrv/SaveDataIndexerManager.cs index 45fd089c..3803fae8 100644 --- a/src/LibHac/FsSrv/SaveDataIndexerManager.cs +++ b/src/LibHac/FsSrv/SaveDataIndexerManager.cs @@ -85,7 +85,7 @@ internal class SaveDataIndexerManager : ISaveDataIndexerManager indexer = _bisIndexer.Indexer; break; case SaveDataSpaceId.SdSystem: - case SaveDataSpaceId.SdCache: + case SaveDataSpaceId.SdUser: // ReSharper doesn't realize that UniqueLock locks the indexer's lock object // ReSharper disable InconsistentlySynchronizedField indexerLock = new UniqueLock(_sdCardIndexer.Locker); @@ -176,7 +176,7 @@ internal class SaveDataIndexerManager : ISaveDataIndexerManager // Note: Nintendo doesn't lock when doing this operation lock (_sdCardIndexer.Locker) { - if (spaceId != SaveDataSpaceId.SdCache && spaceId != SaveDataSpaceId.SdSystem) + if (spaceId != SaveDataSpaceId.SdUser && spaceId != SaveDataSpaceId.SdSystem) { Abort.UnexpectedDefault(); } @@ -252,4 +252,4 @@ public class SaveDataIndexerAccessor : IDisposable { _locker.Dispose(); } -} +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Fs/FileSystemClientTests/ApplicationSaveDataManagementTests.cs b/tests/LibHac.Tests/Fs/FileSystemClientTests/ApplicationSaveDataManagementTests.cs index 37eca4fb..560d8dae 100644 --- a/tests/LibHac.Tests/Fs/FileSystemClientTests/ApplicationSaveDataManagementTests.cs +++ b/tests/LibHac.Tests/Fs/FileSystemClientTests/ApplicationSaveDataManagementTests.cs @@ -140,7 +140,7 @@ public class ApplicationSaveDataManagementTests Assert.Equal(CacheStorageTargetMedia.SdCard, target); using var iterator = new UniqueRef(); - fs.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.SdCache); + fs.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.SdUser); var info = new SaveDataInfo[2]; Assert.Success(iterator.Get.ReadSaveDataInfo(out long entriesRead, info)); @@ -208,4 +208,4 @@ public class ApplicationSaveDataManagementTests Assert.Success(fs.GetCacheStorageTargetMedia(out CacheStorageTargetMedia target, new Ncm.ApplicationId(11))); Assert.Equal(CacheStorageTargetMedia.None, target); } -} +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveData.cs b/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveData.cs index 01709c9f..a54043f9 100644 --- a/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveData.cs +++ b/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveData.cs @@ -25,7 +25,7 @@ public class SaveData var applicationId = new Ncm.ApplicationId(1); FileSystemClient fs = FileSystemServerFactory.CreateClient(true); - fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdCache, applicationId.Value, 0, 0, SaveDataFlags.None); + fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdUser, applicationId.Value, 0, 0, SaveDataFlags.None); fs.MountCacheStorage("cache".ToU8Span(), applicationId); fs.CreateFile("cache:/file".ToU8Span(), 0); @@ -43,7 +43,7 @@ public class SaveData var applicationId = new Ncm.ApplicationId(1); FileSystemClient fs = FileSystemServerFactory.CreateClient(true); - Assert.Success(fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdCache, applicationId.Value, 0, 0, SaveDataFlags.None)); + Assert.Success(fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdUser, applicationId.Value, 0, 0, SaveDataFlags.None)); Assert.Success(fs.MountCacheStorage("cache".ToU8Span(), applicationId)); fs.CreateFile("cache:/sd".ToU8Span(), 0); fs.Commit("cache".ToU8Span()); @@ -64,4 +64,4 @@ public class SaveData Assert.Success(fs.GetEntryType(out _, "cache:/sd".ToU8Span())); Assert.Failure(fs.GetEntryType(out _, "cache:/bis".ToU8Span())); } -} +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveDataManagement.cs b/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveDataManagement.cs index 4ab80cfb..5bf97d8c 100644 --- a/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveDataManagement.cs +++ b/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveDataManagement.cs @@ -36,10 +36,10 @@ public class SaveDataManagement var applicationId = new Ncm.ApplicationId(1); FileSystemClient fs = FileSystemServerFactory.CreateClient(true); - Assert.Success(fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdCache, applicationId.Value, 0, 0, SaveDataFlags.None)); + Assert.Success(fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdUser, applicationId.Value, 0, 0, SaveDataFlags.None)); using var iterator = new UniqueRef(); - fs.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.SdCache); + fs.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.SdUser); var info = new SaveDataInfo[2]; iterator.Get.ReadSaveDataInfo(out long entriesRead, info); @@ -54,7 +54,7 @@ public class SaveDataManagement var applicationId = new Ncm.ApplicationId(1); FileSystemClient fs = FileSystemServerFactory.CreateClient(false); - Assert.Result(ResultFs.PortSdCardNoDevice, fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdCache, applicationId.Value, 0, 0, SaveDataFlags.None)); + Assert.Result(ResultFs.PortSdCardNoDevice, fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdUser, applicationId.Value, 0, 0, SaveDataFlags.None)); } [Fact] @@ -602,4 +602,4 @@ public class SaveDataManagement return Result.Success; } -} +} \ No newline at end of file From 57750b896dbfcda21b49c2c0701d51b368c8fe10 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 29 Dec 2021 15:47:20 -0700 Subject: [PATCH 24/34] Update layout of Arp, Bcat, and some Fs structs Stop using LayoutKind.Explicit and the Size parameter of StructLayout --- src/LibHac/Arp/ApplicationLaunchProperty.cs | 20 ++- .../Bcat/DeliveryCacheDirectoryEntry.cs | 15 +- src/LibHac/Bcat/Digest.cs | 24 +-- src/LibHac/Bcat/DirectoryName.cs | 27 +--- src/LibHac/Bcat/FileName.cs | 27 +--- .../Core/DeliveryCacheDirectoryMetaEntry.cs | 10 +- .../Core/DeliveryCacheFileMetaAccessor.cs | 4 +- .../Core/DeliveryCacheFileMetaEntry.cs | 14 +- .../Core/DeliveryCacheStorageManager.cs | 10 +- .../Service/DeliveryCacheDirectoryService.cs | 4 +- .../Service/DeliveryCacheStorageService.cs | 4 +- src/LibHac/Common/FixedArrays/Array11.cs | 30 ++++ src/LibHac/Common/FixedArrays/Array24.cs | 31 ++++ src/LibHac/Common/FixedArrays/Array26.cs | 32 ++++ src/LibHac/Common/FixedArrays/Array36.cs | 31 ++++ src/LibHac/Common/FixedArrays/Array400.cs | 32 ++++ src/LibHac/Fs/MemoryReportInfo.cs | 11 +- src/LibHac/Fs/SaveDataStructs.cs | 101 ++++++------ src/LibHac/FsSrv/StatusReportService.cs | 6 +- tests/LibHac.Tests/Arp/TypeLayoutTests.cs | 23 +++ tests/LibHac.Tests/Bcat/TypeLayoutTests.cs | 78 +++++++++ tests/LibHac.Tests/Fs/TypeLayoutTests.cs | 149 ++++++++++++++++++ 22 files changed, 525 insertions(+), 158 deletions(-) create mode 100644 src/LibHac/Common/FixedArrays/Array11.cs create mode 100644 src/LibHac/Common/FixedArrays/Array24.cs create mode 100644 src/LibHac/Common/FixedArrays/Array26.cs create mode 100644 src/LibHac/Common/FixedArrays/Array36.cs create mode 100644 src/LibHac/Common/FixedArrays/Array400.cs create mode 100644 tests/LibHac.Tests/Arp/TypeLayoutTests.cs create mode 100644 tests/LibHac.Tests/Bcat/TypeLayoutTests.cs create mode 100644 tests/LibHac.Tests/Fs/TypeLayoutTests.cs diff --git a/src/LibHac/Arp/ApplicationLaunchProperty.cs b/src/LibHac/Arp/ApplicationLaunchProperty.cs index 4d73db27..3477555b 100644 --- a/src/LibHac/Arp/ApplicationLaunchProperty.cs +++ b/src/LibHac/Arp/ApplicationLaunchProperty.cs @@ -1,12 +1,16 @@ -using System.Runtime.InteropServices; +namespace LibHac.Arp; -namespace LibHac.Arp; - -[StructLayout(LayoutKind.Explicit, Size = 0x10)] public struct ApplicationLaunchProperty { - [FieldOffset(0x0)] public ApplicationId ApplicationId; - [FieldOffset(0x8)] public uint Version; - [FieldOffset(0xC)] public Ncm.StorageId BaseStorageId; - [FieldOffset(0xD)] public Ncm.StorageId UpdateStorageId; + public ApplicationId ApplicationId; + public uint Version; + public Ncm.StorageId StorageId; + public Ncm.StorageId PatchStorageId; + public ApplicationKind ApplicationKind; } + +public enum ApplicationKind : byte +{ + Application = 0, + MicroApplication = 1 +} \ No newline at end of file diff --git a/src/LibHac/Bcat/DeliveryCacheDirectoryEntry.cs b/src/LibHac/Bcat/DeliveryCacheDirectoryEntry.cs index ea81f9ef..2b18afbd 100644 --- a/src/LibHac/Bcat/DeliveryCacheDirectoryEntry.cs +++ b/src/LibHac/Bcat/DeliveryCacheDirectoryEntry.cs @@ -1,18 +1,15 @@ -using System.Runtime.InteropServices; +namespace LibHac.Bcat; -namespace LibHac.Bcat; - -[StructLayout(LayoutKind.Explicit, Size = 0x38)] public struct DeliveryCacheDirectoryEntry { - [FieldOffset(0x00)] public FileName Name; - [FieldOffset(0x20)] public long Size; - [FieldOffset(0x28)] public Digest Digest; + public FileName Name; + public long Size; + public Digest Digest; - public DeliveryCacheDirectoryEntry(ref FileName name, long size, ref Digest digest) + public DeliveryCacheDirectoryEntry(in FileName name, long size, in Digest digest) { Name = name; Size = size; Digest = digest; } -} +} \ No newline at end of file diff --git a/src/LibHac/Bcat/Digest.cs b/src/LibHac/Bcat/Digest.cs index 241e970e..108ef0aa 100644 --- a/src/LibHac/Bcat/Digest.cs +++ b/src/LibHac/Bcat/Digest.cs @@ -1,28 +1,16 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using LibHac.Common; +using System.Diagnostics; +using LibHac.Common.FixedArrays; using LibHac.Util; namespace LibHac.Bcat; [DebuggerDisplay("{ToString()}")] -[StructLayout(LayoutKind.Sequential, Size = 16)] public struct Digest { - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1; + public Array16 Value; - public byte this[int i] + public readonly override string ToString() { - get => Bytes[i]; - set => Bytes[i] = value; + return Value.ItemsRo.ToHexString(); } - - public Span Bytes => SpanHelpers.AsByteSpan(ref this); - - public override string ToString() - { - return Bytes.ToHexString(); - } -} +} \ No newline at end of file diff --git a/src/LibHac/Bcat/DirectoryName.cs b/src/LibHac/Bcat/DirectoryName.cs index fb0497cd..990daeeb 100644 --- a/src/LibHac/Bcat/DirectoryName.cs +++ b/src/LibHac/Bcat/DirectoryName.cs @@ -1,33 +1,20 @@ using System; using System.Diagnostics; -using System.Runtime.InteropServices; -using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Util; namespace LibHac.Bcat; [DebuggerDisplay("{ToString()}")] -[StructLayout(LayoutKind.Sequential, Size = MaxSize)] public struct DirectoryName { private const int MaxSize = 0x20; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy2; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy3; + public Array32 Value; - public byte this[int i] + public readonly bool IsValid() { - get => Bytes[i]; - set => Bytes[i] = value; - } - - public Span Bytes => SpanHelpers.AsByteSpan(ref this); - - public bool IsValid() - { - Span name = Bytes; + ReadOnlySpan name = Value.ItemsRo; int i; for (i = 0; i < name.Length; i++) @@ -45,8 +32,8 @@ public struct DirectoryName return name[i] == 0; } - public override string ToString() + public readonly override string ToString() { - return StringUtils.Utf8ZToString(Bytes); + return StringUtils.Utf8ZToString(Value.ItemsRo); } -} +} \ No newline at end of file diff --git a/src/LibHac/Bcat/FileName.cs b/src/LibHac/Bcat/FileName.cs index 91c2d3a3..3c35004f 100644 --- a/src/LibHac/Bcat/FileName.cs +++ b/src/LibHac/Bcat/FileName.cs @@ -1,33 +1,20 @@ using System; using System.Diagnostics; -using System.Runtime.InteropServices; -using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Util; namespace LibHac.Bcat; [DebuggerDisplay("{ToString()}")] -[StructLayout(LayoutKind.Sequential, Size = MaxSize)] public struct FileName { private const int MaxSize = 0x20; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy2; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy3; + public Array32 Value; - public byte this[int i] + public readonly bool IsValid() { - get => Bytes[i]; - set => Bytes[i] = value; - } - - public Span Bytes => SpanHelpers.AsByteSpan(ref this); - - public bool IsValid() - { - Span name = Bytes; + ReadOnlySpan name = Value.ItemsRo; int i; for (i = 0; i < name.Length; i++) @@ -48,8 +35,8 @@ public struct FileName return name[i - 1] != '.'; } - public override string ToString() + public readonly override string ToString() { - return StringUtils.Utf8ZToString(Bytes); + return StringUtils.Utf8ZToString(Value.ItemsRo); } -} +} \ No newline at end of file diff --git a/src/LibHac/Bcat/Impl/Service/Core/DeliveryCacheDirectoryMetaEntry.cs b/src/LibHac/Bcat/Impl/Service/Core/DeliveryCacheDirectoryMetaEntry.cs index 837650bd..117cafb9 100644 --- a/src/LibHac/Bcat/Impl/Service/Core/DeliveryCacheDirectoryMetaEntry.cs +++ b/src/LibHac/Bcat/Impl/Service/Core/DeliveryCacheDirectoryMetaEntry.cs @@ -1,10 +1,10 @@ -using System.Runtime.InteropServices; +using LibHac.Common.FixedArrays; namespace LibHac.Bcat.Impl.Service.Core; -[StructLayout(LayoutKind.Explicit, Size = 0x40)] internal struct DeliveryCacheDirectoryMetaEntry { - [FieldOffset(0x00)] public DirectoryName Name; - [FieldOffset(0x20)] public Digest Digest; -} + public DirectoryName Name; + public Digest Digest; + public Array16 Reserved; +} \ No newline at end of file diff --git a/src/LibHac/Bcat/Impl/Service/Core/DeliveryCacheFileMetaAccessor.cs b/src/LibHac/Bcat/Impl/Service/Core/DeliveryCacheFileMetaAccessor.cs index 32efa93c..14520c29 100644 --- a/src/LibHac/Bcat/Impl/Service/Core/DeliveryCacheFileMetaAccessor.cs +++ b/src/LibHac/Bcat/Impl/Service/Core/DeliveryCacheFileMetaAccessor.cs @@ -56,7 +56,7 @@ internal class DeliveryCacheFileMetaAccessor { for (int i = 0; i < Count; i++) { - if (StringUtils.CompareCaseInsensitive(Entries[i].Name.Bytes, fileName.Bytes) == 0) + if (StringUtils.CompareCaseInsensitive(Entries[i].Name.Value, fileName.Value) == 0) { entry = Entries[i]; return Result.Success; @@ -118,4 +118,4 @@ internal class DeliveryCacheFileMetaAccessor } } } -} +} \ No newline at end of file diff --git a/src/LibHac/Bcat/Impl/Service/Core/DeliveryCacheFileMetaEntry.cs b/src/LibHac/Bcat/Impl/Service/Core/DeliveryCacheFileMetaEntry.cs index d049f426..13fe72ce 100644 --- a/src/LibHac/Bcat/Impl/Service/Core/DeliveryCacheFileMetaEntry.cs +++ b/src/LibHac/Bcat/Impl/Service/Core/DeliveryCacheFileMetaEntry.cs @@ -1,12 +1,12 @@ -using System.Runtime.InteropServices; +using LibHac.Common.FixedArrays; namespace LibHac.Bcat.Impl.Service.Core; -[StructLayout(LayoutKind.Explicit, Size = 0x80)] internal struct DeliveryCacheFileMetaEntry { - [FieldOffset(0x00)] public FileName Name; - [FieldOffset(0x20)] public long Id; - [FieldOffset(0x28)] public long Size; - [FieldOffset(0x30)] public Digest Digest; -} + public FileName Name; + public long Id; + public long Size; + public Digest Digest; + public Array64 Reserved; +} \ No newline at end of file diff --git a/src/LibHac/Bcat/Impl/Service/Core/DeliveryCacheStorageManager.cs b/src/LibHac/Bcat/Impl/Service/Core/DeliveryCacheStorageManager.cs index c2d085d6..c10712b3 100644 --- a/src/LibHac/Bcat/Impl/Service/Core/DeliveryCacheStorageManager.cs +++ b/src/LibHac/Bcat/Impl/Service/Core/DeliveryCacheStorageManager.cs @@ -222,9 +222,9 @@ internal class DeliveryCacheStorageManager AppendMountName(ref sb, applicationId); sb.Append(DirectoriesPath) - .Append(DirectorySeparator).Append(directoryName.Bytes) + .Append(DirectorySeparator).Append(directoryName.Value) .Append(DirectorySeparator).Append(FilesDirectoryName) - .Append(DirectorySeparator).Append(fileName.Bytes); + .Append(DirectorySeparator).Append(fileName.Value); } } @@ -237,7 +237,7 @@ internal class DeliveryCacheStorageManager AppendMountName(ref sb, applicationId); sb.Append(DirectoriesPath) - .Append(DirectorySeparator).Append(directoryName.Bytes) + .Append(DirectorySeparator).Append(directoryName.Value) .Append(DirectorySeparator).Append(FilesMetaFileName); } } @@ -262,7 +262,7 @@ internal class DeliveryCacheStorageManager AppendMountName(ref sb, applicationId); sb.Append(DirectoriesPath) - .Append(DirectorySeparator).Append(directoryName.Bytes); + .Append(DirectorySeparator).Append(directoryName.Value); } } @@ -395,4 +395,4 @@ internal class DeliveryCacheStorageManager private static ReadOnlySpan FilesDirectoryName => // files new[] { (byte)'f', (byte)'i', (byte)'l', (byte)'e', (byte)'s' }; -} +} \ No newline at end of file diff --git a/src/LibHac/Bcat/Impl/Service/DeliveryCacheDirectoryService.cs b/src/LibHac/Bcat/Impl/Service/DeliveryCacheDirectoryService.cs index 59e10aa5..586905a8 100644 --- a/src/LibHac/Bcat/Impl/Service/DeliveryCacheDirectoryService.cs +++ b/src/LibHac/Bcat/Impl/Service/DeliveryCacheDirectoryService.cs @@ -75,7 +75,7 @@ internal class DeliveryCacheDirectoryService : IDeliveryCacheDirectoryService break; } - entryBuffer[i] = new DeliveryCacheDirectoryEntry(ref entry.Name, entry.Size, ref entry.Digest); + entryBuffer[i] = new DeliveryCacheDirectoryEntry(in entry.Name, entry.Size, in entry.Digest); } entriesRead = i; @@ -103,4 +103,4 @@ internal class DeliveryCacheDirectoryService : IDeliveryCacheDirectoryService { Parent.NotifyCloseDirectory(); } -} +} \ No newline at end of file diff --git a/src/LibHac/Bcat/Impl/Service/DeliveryCacheStorageService.cs b/src/LibHac/Bcat/Impl/Service/DeliveryCacheStorageService.cs index 09fe2fb6..bfd95f59 100644 --- a/src/LibHac/Bcat/Impl/Service/DeliveryCacheStorageService.cs +++ b/src/LibHac/Bcat/Impl/Service/DeliveryCacheStorageService.cs @@ -76,7 +76,7 @@ internal class DeliveryCacheStorageService : IDeliveryCacheStorageService break; } - StringUtils.Copy(nameBuffer[i].Bytes, entry.Name.Bytes); + StringUtils.Copy(nameBuffer[i].Value.Items, entry.Name.Value); } namesRead = i; @@ -108,4 +108,4 @@ internal class DeliveryCacheStorageService : IDeliveryCacheStorageService { Server.GetStorageManager().Release(ApplicationId); } -} +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array11.cs b/src/LibHac/Common/FixedArrays/Array11.cs new file mode 100644 index 00000000..251d6c68 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array11.cs @@ -0,0 +1,30 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array11 +{ + public const int Length = 11; + + private T _1; + private T _2; + private T _3; + private T _4; + private T _5; + private T _6; + private T _7; + private T _8; + private T _9; + private T _10; + private T _11; + + public ref T this[int i] => ref Items[i]; + + public Span Items => SpanHelpers.CreateSpan(ref _1, Length); + public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array11 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array24.cs b/src/LibHac/Common/FixedArrays/Array24.cs new file mode 100644 index 00000000..d4d833b4 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array24.cs @@ -0,0 +1,31 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array24 +{ + public const int Length = 24; + + private Array16 _0; + private Array8 _16; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array24 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array26.cs b/src/LibHac/Common/FixedArrays/Array26.cs new file mode 100644 index 00000000..d447ae69 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array26.cs @@ -0,0 +1,32 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array26 +{ + public const int Length = 26; + + private Array16 _0; + private Array8 _16; + private Array2 _24; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array26 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array36.cs b/src/LibHac/Common/FixedArrays/Array36.cs new file mode 100644 index 00000000..dae2d49f --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array36.cs @@ -0,0 +1,31 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array36 +{ + public const int Length = 36; + + private Array32 _0; + private Array4 _32; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array36 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array400.cs b/src/LibHac/Common/FixedArrays/Array400.cs new file mode 100644 index 00000000..4287386a --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array400.cs @@ -0,0 +1,32 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array400 +{ + public const int Length = 400; + + private Array256 _0; + private Array128 _256; + private Array16 _384; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array400 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Fs/MemoryReportInfo.cs b/src/LibHac/Fs/MemoryReportInfo.cs index dd199c74..1f69acf3 100644 --- a/src/LibHac/Fs/MemoryReportInfo.cs +++ b/src/LibHac/Fs/MemoryReportInfo.cs @@ -1,8 +1,7 @@ -using System.Runtime.InteropServices; +using LibHac.Common.FixedArrays; namespace LibHac.Fs; -[StructLayout(LayoutKind.Sequential, Size = 0x80)] public struct MemoryReportInfo { public long PooledBufferFreeSizePeak; @@ -12,8 +11,10 @@ public struct MemoryReportInfo public long BufferManagerRetriedCount; public long ExpHeapFreeSizePeak; public long BufferPoolFreeSizePeak; - public long PatrolAllocateSuccessCount; - public long PatrolAllocateFailureCount; + public long PatrolReadAllocateBufferSuccessCount; + public long PatrolReadAllocateBufferFailureCount; public long BufferManagerTotalAllocatableSizePeak; public long BufferPoolAllocateSizeMax; -} + public long PooledBufferFailedIdealAllocationCountOnAsyncAccess; + public Array32 Reserved; +} \ No newline at end of file diff --git a/src/LibHac/Fs/SaveDataStructs.cs b/src/LibHac/Fs/SaveDataStructs.cs index 6a691612..6aa64c51 100644 --- a/src/LibHac/Fs/SaveDataStructs.cs +++ b/src/LibHac/Fs/SaveDataStructs.cs @@ -1,21 +1,21 @@ using System; -using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.FsSrv.Impl; using LibHac.Ncm; using LibHac.Util; namespace LibHac.Fs; -[StructLayout(LayoutKind.Explicit, Size = 0x40)] public struct SaveDataAttribute : IEquatable, IComparable { - [FieldOffset(0x00)] public ProgramId ProgramId; - [FieldOffset(0x08)] public UserId UserId; - [FieldOffset(0x18)] public ulong StaticSaveDataId; - [FieldOffset(0x20)] public SaveDataType Type; - [FieldOffset(0x21)] public SaveDataRank Rank; - [FieldOffset(0x22)] public ushort Index; + public ProgramId ProgramId; + public UserId UserId; + public ulong StaticSaveDataId; + public SaveDataType Type; + public SaveDataRank Rank; + public ushort Index; + public Array24 Reserved; public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId) : this( programId, type, userId, saveDataId, 0, SaveDataRank.Primary) @@ -34,6 +34,7 @@ public struct SaveDataAttribute : IEquatable, IComparable(); } public static Result Make(out SaveDataAttribute attribute, ProgramId programId, SaveDataType type, @@ -109,16 +110,16 @@ public struct SaveDataAttribute : IEquatable, IComparable Reserved; public static Result Make(out SaveDataCreationInfo creationInfo, long size, long journalSize, ulong ownerId, SaveDataFlags flags, SaveDataSpaceId spaceId) @@ -142,17 +143,16 @@ public struct SaveDataCreationInfo } } -[StructLayout(LayoutKind.Explicit, Size = 0x48)] public struct SaveDataFilter { - [FieldOffset(0x00)] public bool FilterByProgramId; - [FieldOffset(0x01)] public bool FilterBySaveDataType; - [FieldOffset(0x02)] public bool FilterByUserId; - [FieldOffset(0x03)] public bool FilterBySaveDataId; - [FieldOffset(0x04)] public bool FilterByIndex; - [FieldOffset(0x05)] public SaveDataRank Rank; + public bool FilterByProgramId; + public bool FilterBySaveDataType; + public bool FilterByUserId; + public bool FilterBySaveDataId; + public bool FilterByIndex; + public SaveDataRank Rank; - [FieldOffset(0x08)] public SaveDataAttribute Attribute; + public SaveDataAttribute Attribute; public void SetProgramId(ProgramId value) { @@ -245,49 +245,46 @@ public struct SaveDataFilter } } -[StructLayout(LayoutKind.Explicit, Size = HashLength)] public struct HashSalt { - private const int HashLength = 0x20; + private Array32 _value; - [FieldOffset(0x00)] private byte _hashStart; - - public Span Hash => SpanHelpers.CreateSpan(ref _hashStart, HashLength); - public ReadOnlySpan HashRo => SpanHelpers.CreateReadOnlySpan(in _hashStart, HashLength); + public Span Hash => _value.Items; + public readonly ReadOnlySpan HashRo => _value.ItemsRo; } -[StructLayout(LayoutKind.Explicit, Size = 0x10)] public struct SaveDataMetaInfo { - [FieldOffset(0)] public int Size; - [FieldOffset(4)] public SaveDataMetaType Type; + public int Size; + public SaveDataMetaType Type; + public Array11 Reserved; } -[StructLayout(LayoutKind.Explicit, Size = 0x60)] public struct SaveDataInfo { - [FieldOffset(0x00)] public ulong SaveDataId; - [FieldOffset(0x08)] public SaveDataSpaceId SpaceId; - [FieldOffset(0x09)] public SaveDataType Type; - [FieldOffset(0x10)] public UserId UserId; - [FieldOffset(0x20)] public ulong StaticSaveDataId; - [FieldOffset(0x28)] public ProgramId ProgramId; - [FieldOffset(0x30)] public long Size; - [FieldOffset(0x38)] public ushort Index; - [FieldOffset(0x3A)] public SaveDataRank Rank; - [FieldOffset(0x3B)] public SaveDataState State; + public ulong SaveDataId; + public SaveDataSpaceId SpaceId; + public SaveDataType Type; + public UserId UserId; + public ulong StaticSaveDataId; + public ProgramId ProgramId; + public long Size; + public ushort Index; + public SaveDataRank Rank; + public SaveDataState State; + public Array36 Reserved; } -[StructLayout(LayoutKind.Explicit, Size = 0x200)] public struct SaveDataExtraData { - [FieldOffset(0x00)] public SaveDataAttribute Attribute; - [FieldOffset(0x40)] public ulong OwnerId; - [FieldOffset(0x48)] public long TimeStamp; - [FieldOffset(0x50)] public SaveDataFlags Flags; - [FieldOffset(0x58)] public long DataSize; - [FieldOffset(0x60)] public long JournalSize; - [FieldOffset(0x68)] public long CommitId; + public SaveDataAttribute Attribute; + public ulong OwnerId; + public long TimeStamp; + public SaveDataFlags Flags; + public long DataSize; + public long JournalSize; + public long CommitId; + public Array400 Reserved; } public struct CommitOption diff --git a/src/LibHac/FsSrv/StatusReportService.cs b/src/LibHac/FsSrv/StatusReportService.cs index 08d56406..774f1072 100644 --- a/src/LibHac/FsSrv/StatusReportService.cs +++ b/src/LibHac/FsSrv/StatusReportService.cs @@ -112,8 +112,8 @@ public class StatusReportServiceImpl if (_config.GetPatrolAllocateCounts != null) { - _config.GetPatrolAllocateCounts(out reportInfo.PatrolAllocateSuccessCount, - out reportInfo.PatrolAllocateFailureCount); + _config.GetPatrolAllocateCounts(out reportInfo.PatrolReadAllocateBufferSuccessCount, + out reportInfo.PatrolReadAllocateBufferFailureCount); } return Result.Success; @@ -139,4 +139,4 @@ public class StatusReportServiceImpl return 0; } } -} +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Arp/TypeLayoutTests.cs b/tests/LibHac.Tests/Arp/TypeLayoutTests.cs new file mode 100644 index 00000000..73accf72 --- /dev/null +++ b/tests/LibHac.Tests/Arp/TypeLayoutTests.cs @@ -0,0 +1,23 @@ +using System.Runtime.CompilerServices; +using LibHac.Arp; +using Xunit; +using static LibHac.Tests.Common.Layout; + +namespace LibHac.Tests.Arp; + +public class TypeLayoutTests +{ + [Fact] + public static void ApplicationLaunchProperty_Layout() + { + var s = new ApplicationLaunchProperty(); + + Assert.Equal(0x10, Unsafe.SizeOf()); + + Assert.Equal(0x0, GetOffset(in s, in s.ApplicationId)); + Assert.Equal(0x8, GetOffset(in s, in s.Version)); + Assert.Equal(0xC, GetOffset(in s, in s.StorageId)); + Assert.Equal(0xD, GetOffset(in s, in s.PatchStorageId)); + Assert.Equal(0xE, GetOffset(in s, in s.ApplicationKind)); + } +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Bcat/TypeLayoutTests.cs b/tests/LibHac.Tests/Bcat/TypeLayoutTests.cs new file mode 100644 index 00000000..b601bac0 --- /dev/null +++ b/tests/LibHac.Tests/Bcat/TypeLayoutTests.cs @@ -0,0 +1,78 @@ +using System.Runtime.CompilerServices; +using LibHac.Bcat; +using LibHac.Bcat.Impl.Service.Core; +using Xunit; +using static LibHac.Tests.Common.Layout; + +namespace LibHac.Tests.Bcat; + +public class TypeLayoutTests +{ + [Fact] + public static void Digest_Layout() + { + var s = new Digest(); + + Assert.Equal(0x10, Unsafe.SizeOf()); + + Assert.Equal(0x0, GetOffset(in s, in s.Value)); + } + + [Fact] + public static void DirectoryName_Layout() + { + var s = new DirectoryName(); + + Assert.Equal(0x20, Unsafe.SizeOf()); + + Assert.Equal(0x0, GetOffset(in s, in s.Value)); + } + + [Fact] + public static void FileName_Layout() + { + var s = new FileName(); + + Assert.Equal(0x20, Unsafe.SizeOf()); + + Assert.Equal(0x0, GetOffset(in s, in s.Value)); + } + + [Fact] + public static void DeliveryCacheDirectoryEntry_Layout() + { + var s = new DeliveryCacheDirectoryEntry(); + + Assert.Equal(0x38, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Name)); + Assert.Equal(0x20, GetOffset(in s, in s.Size)); + Assert.Equal(0x28, GetOffset(in s, in s.Digest)); + } + + [Fact] + public static void DeliveryCacheDirectoryMetaEntry_Layout() + { + var s = new DeliveryCacheDirectoryMetaEntry(); + + Assert.Equal(0x40, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Name)); + Assert.Equal(0x20, GetOffset(in s, in s.Digest)); + Assert.Equal(0x30, GetOffset(in s, in s.Reserved)); + } + + [Fact] + public static void DeliveryCacheFileMetaEntry_Layout() + { + var s = new DeliveryCacheFileMetaEntry(); + + Assert.Equal(0x80, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Name)); + Assert.Equal(0x20, GetOffset(in s, in s.Id)); + Assert.Equal(0x28, GetOffset(in s, in s.Size)); + Assert.Equal(0x30, GetOffset(in s, in s.Digest)); + Assert.Equal(0x40, GetOffset(in s, in s.Reserved)); + } +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs new file mode 100644 index 00000000..e0746b30 --- /dev/null +++ b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs @@ -0,0 +1,149 @@ +using System.Runtime.CompilerServices; +using LibHac.Fs; +using Xunit; +using static LibHac.Tests.Common.Layout; + +namespace LibHac.Tests.Fs; + +public class TypeLayoutTests +{ + [Fact] + public static void SaveDataAttribute_Layout() + { + var s = new SaveDataAttribute(); + + Assert.Equal(0x40, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.ProgramId)); + Assert.Equal(0x08, GetOffset(in s, in s.UserId)); + Assert.Equal(0x18, GetOffset(in s, in s.StaticSaveDataId)); + Assert.Equal(0x20, GetOffset(in s, in s.Type)); + Assert.Equal(0x21, GetOffset(in s, in s.Rank)); + Assert.Equal(0x22, GetOffset(in s, in s.Index)); + Assert.Equal(0x24, GetOffset(in s, in s.Reserved)); + } + + [Fact] + public static void SaveDataCreationInfo_Layout() + { + var s = new SaveDataCreationInfo(); + + Assert.Equal(0x40, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Size)); + Assert.Equal(0x08, GetOffset(in s, in s.JournalSize)); + Assert.Equal(0x10, GetOffset(in s, in s.BlockSize)); + Assert.Equal(0x18, GetOffset(in s, in s.OwnerId)); + Assert.Equal(0x20, GetOffset(in s, in s.Flags)); + Assert.Equal(0x24, GetOffset(in s, in s.SpaceId)); + Assert.Equal(0x25, GetOffset(in s, in s.IsPseudoSaveData)); + Assert.Equal(0x26, GetOffset(in s, in s.Reserved)); + } + + [Fact] + public static void SaveDataFilter_Layout() + { + var s = new SaveDataFilter(); + + Assert.Equal(0x48, Unsafe.SizeOf()); + + Assert.Equal(0, GetOffset(in s, in s.FilterByProgramId)); + Assert.Equal(1, GetOffset(in s, in s.FilterBySaveDataType)); + Assert.Equal(2, GetOffset(in s, in s.FilterByUserId)); + Assert.Equal(3, GetOffset(in s, in s.FilterBySaveDataId)); + Assert.Equal(4, GetOffset(in s, in s.FilterByIndex)); + Assert.Equal(5, GetOffset(in s, in s.Rank)); + Assert.Equal(8, GetOffset(in s, in s.Attribute)); + } + + [Fact] + public static void SaveDataMetaInfo_Layout() + { + var s = new SaveDataMetaInfo(); + + Assert.Equal(0x10, Unsafe.SizeOf()); + + Assert.Equal(0, GetOffset(in s, in s.Size)); + Assert.Equal(4, GetOffset(in s, in s.Type)); + Assert.Equal(5, GetOffset(in s, in s.Reserved)); + } + + [Fact] + public static void HashSalt_Layout() + { + var s = new HashSalt(); + + Assert.Equal(0x20, Unsafe.SizeOf()); + + Assert.Equal(0, GetOffset(in s, in s.Hash[0])); + } + + [Fact] + public static void SaveDataInfo_Layout() + { + var s = new SaveDataInfo(); + + Assert.Equal(0x60, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.SaveDataId)); + Assert.Equal(0x08, GetOffset(in s, in s.SpaceId)); + Assert.Equal(0x09, GetOffset(in s, in s.Type)); + Assert.Equal(0x10, GetOffset(in s, in s.UserId)); + Assert.Equal(0x20, GetOffset(in s, in s.StaticSaveDataId)); + Assert.Equal(0x28, GetOffset(in s, in s.ProgramId)); + Assert.Equal(0x30, GetOffset(in s, in s.Size)); + Assert.Equal(0x38, GetOffset(in s, in s.Index)); + Assert.Equal(0x3A, GetOffset(in s, in s.Rank)); + Assert.Equal(0x3B, GetOffset(in s, in s.State)); + Assert.Equal(0x3C, GetOffset(in s, in s.Reserved)); + } + + [Fact] + public static void SaveDataExtraData_Layout() + { + var s = new SaveDataExtraData(); + + Assert.Equal(0x200, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Attribute)); + Assert.Equal(0x40, GetOffset(in s, in s.OwnerId)); + Assert.Equal(0x48, GetOffset(in s, in s.TimeStamp)); + Assert.Equal(0x50, GetOffset(in s, in s.Flags)); + Assert.Equal(0x58, GetOffset(in s, in s.DataSize)); + Assert.Equal(0x60, GetOffset(in s, in s.JournalSize)); + Assert.Equal(0x68, GetOffset(in s, in s.CommitId)); + Assert.Equal(0x70, GetOffset(in s, in s.Reserved)); + } + + [Fact] + public static void CommitOption_Layout() + { + var s = new CommitOption(); + + Assert.Equal(4, Unsafe.SizeOf()); + + Assert.Equal(0, GetOffset(in s, in s.Flags)); + } + + [Fact] + public static void MemoryReportInfo_Layout() + { + var s = new MemoryReportInfo(); + + Assert.Equal(0x80, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.PooledBufferFreeSizePeak)); + Assert.Equal(0x08, GetOffset(in s, in s.PooledBufferRetriedCount)); + Assert.Equal(0x10, GetOffset(in s, in s.PooledBufferReduceAllocationCount)); + Assert.Equal(0x18, GetOffset(in s, in s.BufferManagerFreeSizePeak)); + Assert.Equal(0x20, GetOffset(in s, in s.BufferManagerRetriedCount)); + Assert.Equal(0x28, GetOffset(in s, in s.ExpHeapFreeSizePeak)); + Assert.Equal(0x30, GetOffset(in s, in s.BufferPoolFreeSizePeak)); + Assert.Equal(0x38, GetOffset(in s, in s.PatrolReadAllocateBufferSuccessCount)); + Assert.Equal(0x40, GetOffset(in s, in s.PatrolReadAllocateBufferFailureCount)); + Assert.Equal(0x48, GetOffset(in s, in s.BufferManagerTotalAllocatableSizePeak)); + Assert.Equal(0x50, GetOffset(in s, in s.BufferPoolAllocateSizeMax)); + Assert.Equal(0x58, GetOffset(in s, in s.PooledBufferFailedIdealAllocationCountOnAsyncAccess)); + Assert.Equal(0x60, GetOffset(in s, in s.Reserved)); + } +} \ No newline at end of file From ec38f800666e7d2d2c685a3db50ff3d3cc907f07 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Thu, 30 Dec 2021 17:12:29 -0700 Subject: [PATCH 25/34] Update layout of Boot and some Fs structs Boot: - EncryptedKeyBlob - KeyBlob - Package1MarikoOemHeader - Package1MetaData - Package1Stage1Footer - Package1Pk11Header - Package2Header - Package2Meta Fs: - ApplicationInfo - CodeVerificationData - DirectoryEntry - EncryptionSeed - FileSystemProxyErrorInfo - StorageErrorInfo - FileTimeStamp - FileTimeStampRaw - ProgramIndexMapInfo - QueryRangeInfo - RightsId --- src/LibHac/Boot/KeyBlob.cs | 67 +++----- src/LibHac/Boot/Package1.cs | 89 +++++------ src/LibHac/Boot/Package2.cs | 89 ++++------- src/LibHac/Boot/Package2StorageReader.cs | 15 +- src/LibHac/Common/FixedArrays/Array112.cs | 31 ++++ src/LibHac/Common/FixedArrays/Array14.cs | 31 ++++ src/LibHac/Common/FixedArrays/Array144.cs | 31 ++++ src/LibHac/Common/FixedArrays/Array15.cs | 31 ++++ src/LibHac/Common/FixedArrays/Array18.cs | 31 ++++ src/LibHac/Common/FixedArrays/Array56.cs | 31 ++++ src/LibHac/Common/FixedArrays/Array7.cs | 26 ++++ src/LibHac/Common/FixedArrays/Array80.cs | 31 ++++ src/LibHac/Common/Utilities.cs | 3 +- src/LibHac/Fs/AccessLog.cs | 4 +- src/LibHac/Fs/CodeVerificationData.cs | 18 +-- src/LibHac/Fs/DirectoryEntry.cs | 17 +-- src/LibHac/Fs/EncryptionSeed.cs | 29 +--- src/LibHac/Fs/ErrorInfo.cs | 25 ++- src/LibHac/Fs/FileTimeStamp.cs | 16 +- src/LibHac/Fs/ProgramIndexMapInfo.cs | 12 +- src/LibHac/Fs/QueryRangeInfo.cs | 6 +- src/LibHac/Fs/RightsId.cs | 62 ++++---- src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs | 6 +- src/LibHac/FsSrv/StatusReportService.cs | 6 +- src/LibHac/FsSystem/LocalDirectory.cs | 2 +- src/LibHac/FsSystem/PartitionDirectory.cs | 2 +- .../FsSystem/PartitionFileSystemCore.cs | 4 +- src/LibHac/Tools/Fs/InMemoryFileSystem.cs | 4 +- .../Tools/FsSystem/RomFs/RomFsDirectory.cs | 4 +- .../Tools/FsSystem/Save/SaveDataDirectory.cs | 4 +- src/hactoolnet/ProcessPackage.cs | 16 +- tests/LibHac.Tests/Boot/TypeLayoutTests.cs | 132 ++++++++++++++++ tests/LibHac.Tests/Boot/TypeSizeTests.cs | 21 --- tests/LibHac.Tests/Fs/TypeLayoutTests.cs | 143 ++++++++++++++++++ 34 files changed, 726 insertions(+), 313 deletions(-) create mode 100644 src/LibHac/Common/FixedArrays/Array112.cs create mode 100644 src/LibHac/Common/FixedArrays/Array14.cs create mode 100644 src/LibHac/Common/FixedArrays/Array144.cs create mode 100644 src/LibHac/Common/FixedArrays/Array15.cs create mode 100644 src/LibHac/Common/FixedArrays/Array18.cs create mode 100644 src/LibHac/Common/FixedArrays/Array56.cs create mode 100644 src/LibHac/Common/FixedArrays/Array7.cs create mode 100644 src/LibHac/Common/FixedArrays/Array80.cs create mode 100644 tests/LibHac.Tests/Boot/TypeLayoutTests.cs delete mode 100644 tests/LibHac.Tests/Boot/TypeSizeTests.cs diff --git a/src/LibHac/Boot/KeyBlob.cs b/src/LibHac/Boot/KeyBlob.cs index dffa95e9..517a38a0 100644 --- a/src/LibHac/Boot/KeyBlob.cs +++ b/src/LibHac/Boot/KeyBlob.cs @@ -1,42 +1,27 @@ using System; -using System.Diagnostics; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Crypto; using LibHac.Util; namespace LibHac.Boot; -[DebuggerDisplay("{ToString()}")] -[StructLayout(LayoutKind.Explicit, Size = 0xB0)] public struct EncryptedKeyBlob { -#if DEBUG - [FieldOffset(0x00)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy1; - [FieldOffset(0x20)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy2; - [FieldOffset(0x40)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy3; - [FieldOffset(0x60)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy4; - [FieldOffset(0x80)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy5; - [FieldOffset(0xA0)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer16 _dummy6; -#endif - - [FieldOffset(0x00)] public AesCmac Cmac; - [FieldOffset(0x10)] public AesIv Counter; - - public Span Payload => Bytes.Slice(0x20, Unsafe.SizeOf()); + public AesCmac Cmac; + public AesIv Counter; + public Array144 Payload; public Span Bytes => SpanHelpers.AsByteSpan(ref this); - public readonly ReadOnlySpan ReadOnlyBytes => SpanHelpers.AsReadOnlyByteSpan(in this); + public readonly ReadOnlySpan BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this); [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool IsZeros() { - ReadOnlySpan ulongSpan = MemoryMarshal.Cast(ReadOnlyBytes); - - for (int i = 0; i < ulongSpan.Length; i++) + foreach (ulong val in SpanHelpers.AsReadOnlySpan(in this)) { - if (ulongSpan[i] != 0) + if (val != 0) return false; } @@ -44,39 +29,27 @@ public struct EncryptedKeyBlob } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ReadOnlySpan(in EncryptedKeyBlob value) - { - return SpanHelpers.AsReadOnlyByteSpan(in value); - } + public static implicit operator ReadOnlySpan(in EncryptedKeyBlob value) => + SpanHelpers.AsReadOnlyByteSpan(in value); - public readonly override string ToString() => ReadOnlyBytes.ToHexString(); + public readonly override string ToString() => BytesRo.ToHexString(); } -[DebuggerDisplay("{ToString()}")] -[StructLayout(LayoutKind.Explicit, Size = 0x90)] public struct KeyBlob { -#if DEBUG - [FieldOffset(0x00)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy1; - [FieldOffset(0x20)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy2; - [FieldOffset(0x40)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy3; - [FieldOffset(0x60)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy4; -#endif - - [FieldOffset(0x00)] public AesKey MasterKek; - [FieldOffset(0x80)] public AesKey Package1Key; + public AesKey MasterKek; + public Array112 Unused; + public AesKey Package1Key; public Span Bytes => SpanHelpers.AsByteSpan(ref this); - public readonly ReadOnlySpan ReadOnlyBytes => SpanHelpers.AsReadOnlyByteSpan(in this); + public readonly ReadOnlySpan BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this); [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool IsZeros() { - ReadOnlySpan ulongSpan = MemoryMarshal.Cast(ReadOnlyBytes); - - for (int i = 0; i < ulongSpan.Length; i++) + foreach (ulong val in SpanHelpers.AsReadOnlySpan(in this)) { - if (ulongSpan[i] != 0) + if (val != 0) return false; } @@ -84,10 +57,6 @@ public struct KeyBlob } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ReadOnlySpan(in KeyBlob value) - { - return SpanHelpers.AsReadOnlyByteSpan(in value); - } - - public readonly override string ToString() => ReadOnlyBytes.ToHexString(); + public static implicit operator ReadOnlySpan(in KeyBlob value) => SpanHelpers.AsReadOnlyByteSpan(in value); + public readonly override string ToString() => BytesRo.ToHexString(); } \ No newline at end of file diff --git a/src/LibHac/Boot/Package1.cs b/src/LibHac/Boot/Package1.cs index 70407b41..03b16e05 100644 --- a/src/LibHac/Boot/Package1.cs +++ b/src/LibHac/Boot/Package1.cs @@ -1,71 +1,63 @@ -using LibHac.Common; -using LibHac.Fs; -using System; +using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Common.Keys; using LibHac.Diag; +using LibHac.Fs; using LibHac.Tools.FsSystem; using LibHac.Util; namespace LibHac.Boot; -[StructLayout(LayoutKind.Explicit, Size = 0x170)] public struct Package1MarikoOemHeader { - [FieldOffset(0x000)] private byte _aesMac; - [FieldOffset(0x010)] private byte _rsaSig; - [FieldOffset(0x110)] private byte _salt; - [FieldOffset(0x130)] private byte _hash; - [FieldOffset(0x150)] public int Version; - [FieldOffset(0x154)] public int Size; - [FieldOffset(0x158)] public int LoadAddress; - [FieldOffset(0x15C)] public int EntryPoint; - [FieldOffset(0x160)] private byte _reserved; - - public ReadOnlySpan AesMac => SpanHelpers.CreateSpan(ref _aesMac, 0x10); - public ReadOnlySpan RsaSig => SpanHelpers.CreateSpan(ref _rsaSig, 0x100); - public ReadOnlySpan Salt => SpanHelpers.CreateSpan(ref _salt, 0x20); - public ReadOnlySpan Hash => SpanHelpers.CreateSpan(ref _hash, 0x20); - public ReadOnlySpan Reserved => SpanHelpers.CreateSpan(ref _reserved, 0x10); + public Array16 AesMac; + public Array256 RsaSig; + public Array32 Salt; + public Array32 Hash; + public int Version; + public int Size; + public int LoadAddress; + public int EntryPoint; + public Array16 Reserved; } -[StructLayout(LayoutKind.Explicit, Size = 0x20)] public struct Package1MetaData { - [FieldOffset(0x00)] public uint LoaderHash; - [FieldOffset(0x04)] public uint SecureMonitorHash; - [FieldOffset(0x08)] public uint BootloaderHash; - [FieldOffset(0x10)] private byte _buildDate; - [FieldOffset(0x1E)] public byte KeyGeneration; - [FieldOffset(0x1F)] public byte Version; + public uint LoaderHash; + public uint SecureMonitorHash; + public uint BootloaderHash; + public uint Reserved; + private Array14 _buildDate; + public byte KeyGeneration; + public byte Version; - public U8Span BuildDate => new U8Span(SpanHelpers.CreateSpan(ref _buildDate, 0xE)); - public ReadOnlySpan Iv => SpanHelpers.CreateSpan(ref _buildDate, 0x10); + public U8Span BuildDate => new U8Span(_buildDate); + public ReadOnlySpan Iv => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_buildDate.Items), 0x10); } -[StructLayout(LayoutKind.Explicit, Size = 0x20)] public struct Package1Stage1Footer { - [FieldOffset(0x00)] public int Pk11Size; - [FieldOffset(0x10)] private byte _iv; - - public ReadOnlySpan Iv => SpanHelpers.CreateSpan(ref _iv, 0x10); + public int Pk11Size; + public Array12 Reserved; + public Array16 Iv; } -[StructLayout(LayoutKind.Explicit, Size = 0x20)] public struct Package1Pk11Header { - public const uint ExpectedMagic = 0x31314B50; // PK11 + public static readonly uint ExpectedMagic = 0x31314B50; // PK11 - [FieldOffset(0x00)] public uint Magic; - [FieldOffset(0x04)] public int WarmBootSize; - [FieldOffset(0x08)] public int WarmBootOffset; - [FieldOffset(0x10)] public int BootloaderSize; - [FieldOffset(0x14)] public int BootloaderOffset; - [FieldOffset(0x18)] public int SecureMonitorSize; - [FieldOffset(0x1C)] public int SecureMonitorOffset; + public uint Magic; + public int WarmBootSize; + public int WarmBootOffset; + public int Reserved; + public int BootloaderSize; + public int BootloaderOffset; + public int SecureMonitorSize; + public int SecureMonitorOffset; } public enum Package1Section @@ -102,13 +94,13 @@ public class Package1 private Package1MetaData _metaData; private Package1Stage1Footer _stage1Footer; private Package1Pk11Header _pk11Header; - private Buffer16 _pk11Mac; + private Array16 _pk11Mac; public ref readonly Package1MarikoOemHeader MarikoOemHeader => ref _marikoOemHeader; public ref readonly Package1MetaData MetaData => ref _metaData; public ref readonly Package1Stage1Footer Stage1Footer => ref _stage1Footer; public ref readonly Package1Pk11Header Pk11Header => ref _pk11Header; - public ref readonly Buffer16 Pk11Mac => ref _pk11Mac; + public ref readonly Array16 Pk11Mac => ref _pk11Mac; public Result Initialize(KeySet keySet, in SharedRef storage) { @@ -259,7 +251,7 @@ public class Package1 private Result ReadModernEristaMac() { - return _baseStorage.Get.Read(ModernStage1Size + Pk11Size, _pk11Mac.Bytes); + return _baseStorage.Get.Read(ModernStage1Size + Pk11Size, _pk11Mac.Items); } private Result SetPk11Storage() @@ -295,7 +287,7 @@ public class Package1 else { decPk11Storage = new Aes128CtrStorage(encPk11Storage, - KeySet.Package1Keys[KeyRevision].DataRo.ToArray(), _stage1Footer.Iv.ToArray(), true); + KeySet.Package1Keys[KeyRevision].DataRo.ToArray(), _stage1Footer.Iv.ItemsRo.ToArray(), true); } _pk11Storage = new SubStorage(new CachedStorage(decPk11Storage, 0x4000, 1, true), 0, Pk11Size); @@ -359,7 +351,7 @@ public class Package1 // MarikoOemHeader must be read first private bool IsMarikoImpl() { - return MarikoOemHeader.AesMac.IsZeros() && MarikoOemHeader.Reserved.IsZeros(); + return MarikoOemHeader.AesMac.ItemsRo.IsZeros() && MarikoOemHeader.Reserved.ItemsRo.IsZeros(); } /// @@ -392,7 +384,7 @@ public class Package1 if (IsModern) { - storages.Add(new MemoryStorage(_pk11Mac.Bytes.ToArray())); + storages.Add(new MemoryStorage(_pk11Mac.ItemsRo.ToArray())); } } @@ -437,7 +429,6 @@ public class Package1 return new SubStorage(_pk11Storage, offset, size); } - public IStorage OpenDecryptedWarmBootStorage() { if (!IsDecrypted) diff --git a/src/LibHac/Boot/Package2.cs b/src/LibHac/Boot/Package2.cs index 7e91c81c..2529cac3 100644 --- a/src/LibHac/Boot/Package2.cs +++ b/src/LibHac/Boot/Package2.cs @@ -1,16 +1,12 @@ using System; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Crypto; using LibHac.Util; -#if DEBUG -using System.Diagnostics; -#endif namespace LibHac.Boot; -[StructLayout(LayoutKind.Explicit, Size = 0x200)] public struct Package2Header { internal const int Package2SizeMax = (1024 * 1024 * 8) - (1024 * 16); // 8MB - 16KB @@ -18,14 +14,12 @@ public struct Package2Header internal const int PayloadCount = 3; internal const int SignatureSize = 0x100; - private ReadOnlySpan RsaPublicKeyExponent => new byte[] { 0x00, 0x01, 0x00, 0x01 }; + private static ReadOnlySpan RsaPublicKeyExponent => new byte[] { 0x00, 0x01, 0x00, 0x01 }; - [FieldOffset(0x00)] private byte _signature; - [FieldOffset(0x100)] public Package2Meta Meta; + public Array256 Signature; + public Package2Meta Meta; - public ReadOnlySpan Signature => SpanHelpers.CreateSpan(ref _signature, SignatureSize); - - public Result VerifySignature(ReadOnlySpan modulus, ReadOnlySpan data) + public readonly Result VerifySignature(ReadOnlySpan modulus, ReadOnlySpan data) { if (!Rsa.VerifyRsa2048PssSha256(Signature, modulus, RsaPublicKeyExponent, data)) { @@ -34,53 +28,38 @@ public struct Package2Header return Result.Success; } - -#if DEBUG - [DebuggerBrowsable(DebuggerBrowsableState.Never)] [FieldOffset(0x00)] private readonly Padding100 PaddingForVsDebugging; -#endif } -[StructLayout(LayoutKind.Explicit, Size = 0x100)] public struct Package2Meta { - public const uint ExpectedMagicValue = 0x31324B50; // PK21 + public static readonly uint ExpectedMagicValue = 0x31324B50; // PK21 - [FieldOffset(0x00)] private Buffer16 _headerIv; + public Array16 HeaderIv; + public Array3> PayloadIvs; + public Array16 Padding40; - [FieldOffset(0x00)] private uint _package2Size; - [FieldOffset(0x04)] private byte _keyGeneration; + public uint Magic; + public uint EntryPoint; + public Array4 Padding58; + public byte Package2Version; + public byte BootloaderVersion; - [FieldOffset(0x06)] private byte _keyGenerationXor1; - [FieldOffset(0x07)] private byte _keyGenerationXor2; - [FieldOffset(0x08)] private uint _sizeXor1; - [FieldOffset(0x0C)] private uint _sizeXor2; + public Array3 PayloadSizes; + public Array4 Padding6C; + public Array3 PayloadOffsets; + public Array4 Padding7C; + public Array3> PayloadHashes; + public Array32 PaddingE0; - [FieldOffset(0x10)] private Buffer16 _payloadIvs; + public readonly uint GetSize() + { + ReadOnlySpan ints = SpanHelpers.AsReadOnlySpan, uint>(in HeaderIv); + return ints[0] ^ ints[2] ^ ints[3]; + } - [FieldOffset(0x50)] private readonly uint _magic; - [FieldOffset(0x54)] private readonly uint _entryPoint; - [FieldOffset(0x5C)] private readonly byte _package2Version; - [FieldOffset(0x5D)] private readonly byte _bootloaderVersion; + public readonly byte GetKeyGeneration() => (byte)Math.Max(0, (HeaderIv[4] ^ HeaderIv[6] ^ HeaderIv[7]) - 1); - [FieldOffset(0x60)] private uint _payloadSizes; - [FieldOffset(0x70)] private uint _payloadOffsets; - [FieldOffset(0x80)] private Buffer32 _payloadHashes; - - public uint Magic => _magic; - public uint EntryPoint => _entryPoint; - public byte Package2Version => _package2Version; - public byte BootloaderVersion => _bootloaderVersion; - - public Buffer16 HeaderIv => _headerIv; - public readonly uint Size => _package2Size ^ _sizeXor1 ^ _sizeXor2; - public byte KeyGeneration => (byte)Math.Max(0, (_keyGeneration ^ _keyGenerationXor1 ^ _keyGenerationXor2) - 1); - - public ReadOnlySpan PayloadIvs => SpanHelpers.CreateSpan(ref _payloadIvs, Package2Header.PayloadCount); - public ReadOnlySpan PayloadSizes => SpanHelpers.CreateSpan(ref _payloadSizes, Package2Header.PayloadCount); - public ReadOnlySpan PayloadOffsets => SpanHelpers.CreateSpan(ref _payloadOffsets, Package2Header.PayloadCount); - public ReadOnlySpan PayloadHashes => SpanHelpers.CreateSpan(ref _payloadHashes, Package2Header.PayloadCount); - - public int GetPayloadFileOffset(int index) + public readonly int GetPayloadFileOffset(int index) { if ((uint)index >= Package2Header.PayloadCount) throw new IndexOutOfRangeException("Invalid payload index."); @@ -95,11 +74,11 @@ public struct Package2Meta return offset; } - public Result Verify() + public readonly Result Verify() { // Get the obfuscated metadata. - uint size = Size; - byte keyGeneration = KeyGeneration; + uint size = GetSize(); + byte keyGeneration = GetKeyGeneration(); // Check that size is big enough for the header. if (size < Unsafe.SizeOf()) @@ -128,7 +107,7 @@ public struct Package2Meta } // Check that the sizes sum to the total. - if (Size != Unsafe.SizeOf() + PayloadSizes[0] + PayloadSizes[1] + PayloadSizes[2]) + if (GetSize() != Unsafe.SizeOf() + PayloadSizes[0] + PayloadSizes[1] + PayloadSizes[2]) return ResultLibHac.InvalidPackage2MetaTotalSize.Log(); // Check that the payloads do not overflow. @@ -156,8 +135,4 @@ public struct Package2Meta // No payload contains the entrypoint, so we're not valid. return ResultLibHac.InvalidPackage2MetaEntryPointNotFound.Log(); } - -#if DEBUG - [DebuggerBrowsable(DebuggerBrowsableState.Never)] [FieldOffset(0x00)] private readonly Padding100 PaddingForVsDebugging; -#endif -} +} \ No newline at end of file diff --git a/src/LibHac/Boot/Package2StorageReader.cs b/src/LibHac/Boot/Package2StorageReader.cs index e19dd186..66258236 100644 --- a/src/LibHac/Boot/Package2StorageReader.cs +++ b/src/LibHac/Boot/Package2StorageReader.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Common.Keys; using LibHac.Crypto; using LibHac.Fs; @@ -41,7 +42,7 @@ public class Package2StorageReader : IDisposable Result rc = storage.Get.Read(0, SpanHelpers.AsByteSpan(ref _header)); if (rc.IsFailure()) return rc; - _key = keySet.Package2Keys[_header.Meta.KeyGeneration]; + _key = keySet.Package2Keys[_header.Meta.GetKeyGeneration()]; DecryptHeader(_key, ref _header.Meta, ref _header.Meta); _storage.SetByCopy(in storage); @@ -54,7 +55,7 @@ public class Package2StorageReader : IDisposable /// /// If the method returns successfully, contains an /// of the specified payload. - /// The index of the payload to get. Must me less than + /// The index of the payload to get. Must be less than /// The of the operation. public Result OpenPayload(ref UniqueRef outPayloadStorage, int index) { @@ -72,7 +73,7 @@ public class Package2StorageReader : IDisposable return Result.Success; } - byte[] iv = _header.Meta.PayloadIvs[index].Bytes.ToArray(); + byte[] iv = _header.Meta.PayloadIvs[index].ItemsRo.ToArray(); outPayloadStorage.Reset(new CachedStorage(new Aes128CtrStorage(payloadSubStorage, _key.DataRo.ToArray(), iv, true), 0x4000, 1, true)); return Result.Success; } @@ -219,7 +220,7 @@ public class Package2StorageReader : IDisposable var storages = new List(4); // The signature and IV are unencrypted - int unencryptedHeaderSize = Package2Header.SignatureSize + Unsafe.SizeOf(); + int unencryptedHeaderSize = Package2Header.SignatureSize + Unsafe.SizeOf>(); int encryptedHeaderSize = Unsafe.SizeOf() - unencryptedHeaderSize; // Get signature and IV @@ -230,7 +231,7 @@ public class Package2StorageReader : IDisposable // The counter starts counting at the beginning of the meta struct, but the first block in // the struct isn't encrypted. Increase the counter by one to skip that block. - byte[] iv = _header.Meta.HeaderIv.Bytes.ToArray(); + byte[] iv = _header.Meta.HeaderIv.ItemsRo.ToArray(); Utilities.IncrementByteArray(iv); storages.Add(new CachedStorage(new Aes128CtrStorage(encMetaStorage, _key.DataRo.ToArray(), iv, true), 0x100, 1, true)); @@ -254,12 +255,12 @@ public class Package2StorageReader : IDisposable private void DecryptHeader(ReadOnlySpan key, ref Package2Meta source, ref Package2Meta dest) { - Buffer16 iv = source.HeaderIv; + Array16 iv = source.HeaderIv; Aes.DecryptCtr128(SpanHelpers.AsByteSpan(ref source), SpanHelpers.AsByteSpan(ref dest), key, iv); // Copy the IV to the output because the IV field will be garbage after "decrypting" it - Unsafe.As(ref dest) = iv; + dest.HeaderIv = iv; } private bool HasIniPayload() diff --git a/src/LibHac/Common/FixedArrays/Array112.cs b/src/LibHac/Common/FixedArrays/Array112.cs new file mode 100644 index 00000000..a25e7f98 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array112.cs @@ -0,0 +1,31 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array112 +{ + public const int Length = 112; + + private Array80 _0; + private Array32 _80; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array112 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array14.cs b/src/LibHac/Common/FixedArrays/Array14.cs new file mode 100644 index 00000000..8d309393 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array14.cs @@ -0,0 +1,31 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array14 +{ + public const int Length = 14; + + private Array8 _0; + private Array6 _8; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array14 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array144.cs b/src/LibHac/Common/FixedArrays/Array144.cs new file mode 100644 index 00000000..b727dcae --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array144.cs @@ -0,0 +1,31 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array144 +{ + public const int Length = 144; + + private Array128 _0; + private Array16 _128; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array144 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array15.cs b/src/LibHac/Common/FixedArrays/Array15.cs new file mode 100644 index 00000000..6c7d6d93 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array15.cs @@ -0,0 +1,31 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array15 +{ + public const int Length = 15; + + private Array8 _0; + private Array7 _8; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array15 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array18.cs b/src/LibHac/Common/FixedArrays/Array18.cs new file mode 100644 index 00000000..c69b5990 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array18.cs @@ -0,0 +1,31 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array18 +{ + public const int Length = 18; + + private Array16 _0; + private Array2 _16; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array18 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array56.cs b/src/LibHac/Common/FixedArrays/Array56.cs new file mode 100644 index 00000000..b03d02cb --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array56.cs @@ -0,0 +1,31 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array56 +{ + public const int Length = 56; + + private Array32 _0; + private Array24 _32; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array56 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array7.cs b/src/LibHac/Common/FixedArrays/Array7.cs new file mode 100644 index 00000000..a64b7207 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array7.cs @@ -0,0 +1,26 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array7 +{ + public const int Length = 7; + + private T _1; + private T _2; + private T _3; + private T _4; + private T _5; + private T _6; + private T _7; + + public ref T this[int i] => ref Items[i]; + + public Span Items => SpanHelpers.CreateSpan(ref _1, Length); + public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array7 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array80.cs b/src/LibHac/Common/FixedArrays/Array80.cs new file mode 100644 index 00000000..186efd72 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array80.cs @@ -0,0 +1,31 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array80 +{ + public const int Length = 80; + + private Array64 _0; + private Array16 _64; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array80 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/Utilities.cs b/src/LibHac/Common/Utilities.cs index 47a4f336..98530ca5 100644 --- a/src/LibHac/Common/Utilities.cs +++ b/src/LibHac/Common/Utilities.cs @@ -289,7 +289,8 @@ public static class Utilities 8 => "8.1.0-8.1.1", 9 => "9.0.0-9.0.1", 0xA => "9.1.0-12.0.3", - 0xB => "12.1.0-", + 0xB => "12.1.0", + 0xC => "13.0.0-", _ => "Unknown" }; diff --git a/src/LibHac/Fs/AccessLog.cs b/src/LibHac/Fs/AccessLog.cs index b1a68511..693766dc 100644 --- a/src/LibHac/Fs/AccessLog.cs +++ b/src/LibHac/Fs/AccessLog.cs @@ -2,10 +2,10 @@ using System.Buffers; using System.Buffers.Text; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Text; using System.Text.Unicode; using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Diag; using LibHac.Fs.Fsa; using LibHac.Fs.Impl; @@ -34,13 +34,13 @@ namespace LibHac.Fs } } - [StructLayout(LayoutKind.Sequential, Size = 0x20)] public struct ApplicationInfo { public Ncm.ApplicationId ApplicationId; public uint Version; public byte LaunchType; public bool IsMultiProgram; + public Array18 Reserved; } [Flags] diff --git a/src/LibHac/Fs/CodeVerificationData.cs b/src/LibHac/Fs/CodeVerificationData.cs index 829a3114..361df014 100644 --- a/src/LibHac/Fs/CodeVerificationData.cs +++ b/src/LibHac/Fs/CodeVerificationData.cs @@ -1,17 +1,11 @@ -using System; -using System.Runtime.InteropServices; -using LibHac.Common; +using LibHac.Common.FixedArrays; namespace LibHac.Fs; -[StructLayout(LayoutKind.Explicit, Size = 0x124)] public struct CodeVerificationData { - private const int Signature2Size = 0x100; - - [FieldOffset(0x000)] private byte _signature2; - [FieldOffset(0x100)] public Buffer32 NcaHeaderHash; - [FieldOffset(0x120)] public bool IsValid; - - public Span NcaSignature2 => SpanHelpers.CreateSpan(ref _signature2, Signature2Size); -} + public Array256 Signature; + public Array32 Hash; + public bool HasData; + public Array3 Reserved; +} \ No newline at end of file diff --git a/src/LibHac/Fs/DirectoryEntry.cs b/src/LibHac/Fs/DirectoryEntry.cs index 2a0569ed..6a222d57 100644 --- a/src/LibHac/Fs/DirectoryEntry.cs +++ b/src/LibHac/Fs/DirectoryEntry.cs @@ -1,18 +1,15 @@ using System; -using System.Runtime.InteropServices; -using LibHac.Common; +using LibHac.Common.FixedArrays; namespace LibHac.Fs; - -[StructLayout(LayoutKind.Explicit)] public struct DirectoryEntry { - [FieldOffset(0)] private byte _name; - [FieldOffset(0x301)] public NxFileAttributes Attributes; - [FieldOffset(0x304)] public DirectoryEntryType Type; - [FieldOffset(0x308)] public long Size; - - public Span Name => SpanHelpers.CreateSpan(ref _name, PathTool.EntryNameLengthMax + 1); + public Array769 Name; + public NxFileAttributes Attributes; + public Array2 Reserved302; + public DirectoryEntryType Type; + public Array3 Reserved305; + public long Size; } public enum DirectoryEntryType : byte diff --git a/src/LibHac/Fs/EncryptionSeed.cs b/src/LibHac/Fs/EncryptionSeed.cs index add168b4..619abda7 100644 --- a/src/LibHac/Fs/EncryptionSeed.cs +++ b/src/LibHac/Fs/EncryptionSeed.cs @@ -1,28 +1,13 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using LibHac.Common; +using System.Diagnostics; +using LibHac.Common.FixedArrays; +using LibHac.Util; namespace LibHac.Fs; [DebuggerDisplay("{ToString()}")] -[StructLayout(LayoutKind.Sequential, Size = 0x10)] -public struct EncryptionSeed : IEquatable +public struct EncryptionSeed { - private readonly Key128 Key; + public Array16 Value; - public readonly ReadOnlySpan Value => SpanHelpers.AsReadOnlyByteSpan(in this); - - public EncryptionSeed(ReadOnlySpan bytes) - { - Key = new Key128(bytes); - } - - public override string ToString() => Key.ToString(); - - public override bool Equals(object obj) => obj is EncryptionSeed key && Equals(key); - public bool Equals(EncryptionSeed other) => Key.Equals(other.Key); - public override int GetHashCode() => Key.GetHashCode(); - public static bool operator ==(EncryptionSeed left, EncryptionSeed right) => left.Equals(right); - public static bool operator !=(EncryptionSeed left, EncryptionSeed right) => !(left == right); -} + public readonly override string ToString() => Value.ItemsRo.ToHexString(); +} \ No newline at end of file diff --git a/src/LibHac/Fs/ErrorInfo.cs b/src/LibHac/Fs/ErrorInfo.cs index e6ad9679..b3ef2385 100644 --- a/src/LibHac/Fs/ErrorInfo.cs +++ b/src/LibHac/Fs/ErrorInfo.cs @@ -1,23 +1,22 @@ -using System.Runtime.InteropServices; +using LibHac.Common.FixedArrays; using LibHac.Fat; namespace LibHac.Fs; -[StructLayout(LayoutKind.Explicit, Size = 0x80)] public struct FileSystemProxyErrorInfo { - [FieldOffset(0x00)] public int RomFsRemountForDataCorruptionCount; - [FieldOffset(0x04)] public int RomFsUnrecoverableDataCorruptionByRemountCount; - [FieldOffset(0x08)] public FatError FatError; - [FieldOffset(0x28)] public int RomFsRecoveredByInvalidateCacheCount; - [FieldOffset(0x2C)] public int SaveDataIndexCount; + public int RemountForDataCorruptionCount; + public int UnrecoverableDataCorruptionByRemountCount; + public FatError FatFsError; + public int RecoveredByInvalidateCacheCount; + public int SaveDataIndexCount; + public Array80 Reserved; } -[StructLayout(LayoutKind.Explicit, Size = 0x10)] public struct StorageErrorInfo { - [FieldOffset(0x00)] public int NumActivationFailures; - [FieldOffset(0x04)] public int NumActivationErrorCorrections; - [FieldOffset(0x08)] public int NumReadWriteFailures; - [FieldOffset(0x0C)] public int NumReadWriteErrorCorrections; -} + public int NumActivationFailures; + public int NumActivationErrorCorrections; + public int NumReadWriteFailures; + public int NumReadWriteErrorCorrections; +} \ No newline at end of file diff --git a/src/LibHac/Fs/FileTimeStamp.cs b/src/LibHac/Fs/FileTimeStamp.cs index afeccd6f..63d4ba84 100644 --- a/src/LibHac/Fs/FileTimeStamp.cs +++ b/src/LibHac/Fs/FileTimeStamp.cs @@ -1,12 +1,22 @@ -using System.Runtime.InteropServices; +using LibHac.Common.FixedArrays; +using LibHac.Time; namespace LibHac.Fs; -[StructLayout(LayoutKind.Sequential, Size = 0x20)] +public struct FileTimeStamp +{ + public PosixTime Created; + public PosixTime Accessed; + public PosixTime Modified; + public bool IsLocalTime; + public Array7 Reserved; +} + public struct FileTimeStampRaw { public long Created; public long Accessed; public long Modified; public bool IsLocalTime; -} + public Array7 Reserved; +} \ No newline at end of file diff --git a/src/LibHac/Fs/ProgramIndexMapInfo.cs b/src/LibHac/Fs/ProgramIndexMapInfo.cs index a56ba198..5d326151 100644 --- a/src/LibHac/Fs/ProgramIndexMapInfo.cs +++ b/src/LibHac/Fs/ProgramIndexMapInfo.cs @@ -1,12 +1,12 @@ -using System.Runtime.InteropServices; +using LibHac.Common.FixedArrays; using LibHac.Ncm; namespace LibHac.Fs; -[StructLayout(LayoutKind.Explicit, Size = 0x20)] public struct ProgramIndexMapInfo { - [FieldOffset(0x00)] public ProgramId ProgramId; - [FieldOffset(0x08)] public ProgramId MainProgramId; - [FieldOffset(0x10)] public byte ProgramIndex; -} + public ProgramId ProgramId; + public ProgramId MainProgramId; + public byte ProgramIndex; + public Array15 Reserved; +} \ No newline at end of file diff --git a/src/LibHac/Fs/QueryRangeInfo.cs b/src/LibHac/Fs/QueryRangeInfo.cs index 2a496a90..d44cfbf8 100644 --- a/src/LibHac/Fs/QueryRangeInfo.cs +++ b/src/LibHac/Fs/QueryRangeInfo.cs @@ -1,13 +1,13 @@ using System; -using System.Runtime.InteropServices; +using LibHac.Common.FixedArrays; namespace LibHac.Fs; -[StructLayout(LayoutKind.Sequential, Size = 0x40)] public struct QueryRangeInfo { public int AesCtrKeyType; public int SpeedEmulationType; + public Array56 Reserved; public void Clear() { @@ -27,4 +27,4 @@ public struct QueryRangeInfo InternalKeyForHardwareAes = 1 << 1, ExternalKeyForHardwareAes = 1 << 2 } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/RightsId.cs b/src/LibHac/Fs/RightsId.cs index 1653c1c0..a81b5cf5 100644 --- a/src/LibHac/Fs/RightsId.cs +++ b/src/LibHac/Fs/RightsId.cs @@ -1,62 +1,56 @@ using System; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using LibHac.Common; +using System.Runtime.Intrinsics; +using LibHac.Common.FixedArrays; +using LibHac.Diag; using LibHac.Util; namespace LibHac.Fs; [DebuggerDisplay("{DebugDisplay(),nq}")] -[StructLayout(LayoutKind.Sequential, Size = 0x10)] -public struct RightsId : IEquatable, IComparable, IComparable +public struct RightsId : IEquatable { - public readonly Id128 Id; + public Array16 Value; - public RightsId(ulong high, ulong low) + public RightsId(ReadOnlySpan value) { - Id = new Id128(high, low); + Assert.Equal(0x10, value.Length); + + Unsafe.SkipInit(out Value); + + Span longsThis = MemoryMarshal.Cast(Value.Items); + ReadOnlySpan longsValue = MemoryMarshal.Cast(value); + + longsThis[1] = longsValue[1]; + longsThis[0] = longsValue[0]; } - public RightsId(ReadOnlySpan uid) - { - Id = new Id128(uid); - } + public readonly override string ToString() => Value.ItemsRo.ToHexString(); - public override string ToString() => Id.ToString(); - - public string DebugDisplay() + public readonly string DebugDisplay() { - ReadOnlySpan highBytes = AsBytes().Slice(0, 8); - ReadOnlySpan lowBytes = AsBytes().Slice(8, 8); + ReadOnlySpan highBytes = Value.ItemsRo.Slice(0, 8); + ReadOnlySpan lowBytes = Value.ItemsRo.Slice(8, 8); return $"{highBytes.ToHexString()} {lowBytes.ToHexString()}"; } - public bool Equals(RightsId other) => Id == other.Id; - public override bool Equals(object obj) => obj is RightsId other && Equals(other); - - public override int GetHashCode() => Id.GetHashCode(); - - public int CompareTo(RightsId other) => Id.CompareTo(other.Id); - - public int CompareTo(object obj) + public readonly bool Equals(RightsId other) { - if (obj is null) return 1; - return obj is RightsId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(RightsId)}"); + return Unsafe.As, Vector128>(ref Unsafe.AsRef(in Value)) + .Equals(Unsafe.As, Vector128>(ref other.Value)); } - public void ToBytes(Span output) => Id.ToBytes(output); + public readonly override bool Equals(object obj) => obj is RightsId other && Equals(other); - public ReadOnlySpan AsBytes() + public readonly override int GetHashCode() { - return SpanHelpers.AsByteSpan(ref this); + ReadOnlySpan longSpan = MemoryMarshal.Cast(Value.ItemsRo); + return HashCode.Combine(longSpan[0], longSpan[1]); } public static bool operator ==(RightsId left, RightsId right) => left.Equals(right); public static bool operator !=(RightsId left, RightsId right) => !left.Equals(right); - - public static bool operator <(RightsId left, RightsId right) => left.CompareTo(right) < 0; - public static bool operator >(RightsId left, RightsId right) => left.CompareTo(right) > 0; - public static bool operator <=(RightsId left, RightsId right) => left.CompareTo(right) <= 0; - public static bool operator >=(RightsId left, RightsId right) => left.CompareTo(right) >= 0; -} +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs b/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs index cc19675a..3a694bf7 100644 --- a/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs +++ b/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs @@ -86,7 +86,7 @@ public class NcaFileSystemServiceImpl UnsafeHelpers.SkipParamInit(out verificationData); if (!Unsafe.IsNullRef(ref verificationData)) - verificationData.IsValid = false; + verificationData.HasData = false; // Get a reference to the path that will be advanced as each part of the path is parsed var currentPath = new U8Span(path.GetString()); @@ -691,9 +691,9 @@ public class NcaFileSystemServiceImpl private Result SetExternalKeyForRightsId(Nca nca) { var rightsId = new RightsId(nca.Header.RightsId); - var zero = new RightsId(0, 0); + var zero = new RightsId(); - if (Crypto.CryptoUtil.IsSameBytes(rightsId.AsBytes(), zero.AsBytes(), Unsafe.SizeOf())) + if (Crypto.CryptoUtil.IsSameBytes(rightsId.Value, zero.Value, Unsafe.SizeOf())) return Result.Success; // ReSharper disable once UnusedVariable diff --git a/src/LibHac/FsSrv/StatusReportService.cs b/src/LibHac/FsSrv/StatusReportService.cs index 774f1072..6ff8be12 100644 --- a/src/LibHac/FsSrv/StatusReportService.cs +++ b/src/LibHac/FsSrv/StatusReportService.cs @@ -62,9 +62,9 @@ public class StatusReportServiceImpl { errorInfo = new FileSystemProxyErrorInfo(); - _config.NcaFsServiceImpl.GetAndClearRomFsErrorInfo(out errorInfo.RomFsRemountForDataCorruptionCount, - out errorInfo.RomFsUnrecoverableDataCorruptionByRemountCount, - out errorInfo.RomFsRecoveredByInvalidateCacheCount); + _config.NcaFsServiceImpl.GetAndClearRomFsErrorInfo(out errorInfo.RemountForDataCorruptionCount, + out errorInfo.UnrecoverableDataCorruptionByRemountCount, + out errorInfo.RecoveredByInvalidateCacheCount); // Missing: GetFatInfo diff --git a/src/LibHac/FsSystem/LocalDirectory.cs b/src/LibHac/FsSystem/LocalDirectory.cs index 2b6c1a06..67c5e2ef 100644 --- a/src/LibHac/FsSystem/LocalDirectory.cs +++ b/src/LibHac/FsSystem/LocalDirectory.cs @@ -40,7 +40,7 @@ public class LocalDirectory : IDirectory DirectoryEntryType type = isDir ? DirectoryEntryType.Directory : DirectoryEntryType.File; long length = isDir ? 0 : ((FileInfo)localEntry).Length; - StringUtils.Copy(entryBuffer[i].Name, name); + StringUtils.Copy(entryBuffer[i].Name.Items, name); entryBuffer[i].Name[PathTool.EntryNameLengthMax] = 0; entryBuffer[i].Attributes = localEntry.Attributes.ToNxAttributes(); diff --git a/src/LibHac/FsSystem/PartitionDirectory.cs b/src/LibHac/FsSystem/PartitionDirectory.cs index a21ec0d8..7ac91655 100644 --- a/src/LibHac/FsSystem/PartitionDirectory.cs +++ b/src/LibHac/FsSystem/PartitionDirectory.cs @@ -47,7 +47,7 @@ public class PartitionDirectory : IDirectory entry.Type = DirectoryEntryType.File; entry.Size = fileEntry.Size; - StringUtils.Copy(entry.Name, nameUtf8); + StringUtils.Copy(entry.Name.Items, nameUtf8); entry.Name[PathTool.EntryNameLengthMax] = 0; CurrentIndex++; diff --git a/src/LibHac/FsSystem/PartitionFileSystemCore.cs b/src/LibHac/FsSystem/PartitionFileSystemCore.cs index db133b75..0ebac724 100644 --- a/src/LibHac/FsSystem/PartitionFileSystemCore.cs +++ b/src/LibHac/FsSystem/PartitionFileSystemCore.cs @@ -358,7 +358,7 @@ public class PartitionFileSystemCore : IFileSystem where T : unmanaged, IPart entryBuffer[i].Size = ParentFs._metaData.GetEntry(CurrentIndex).Size; U8Span name = ParentFs._metaData.GetName(CurrentIndex); - StringUtils.Copy(entryBuffer[i].Name, name); + StringUtils.Copy(entryBuffer[i].Name.Items, name); entryBuffer[i].Name[FsPath.MaxLength] = 0; CurrentIndex++; @@ -388,4 +388,4 @@ public class PartitionFileSystemCore : IFileSystem where T : unmanaged, IPart return Result.Success; } } -} +} \ No newline at end of file diff --git a/src/LibHac/Tools/Fs/InMemoryFileSystem.cs b/src/LibHac/Tools/Fs/InMemoryFileSystem.cs index e799804f..0efca041 100644 --- a/src/LibHac/Tools/Fs/InMemoryFileSystem.cs +++ b/src/LibHac/Tools/Fs/InMemoryFileSystem.cs @@ -252,7 +252,7 @@ public class InMemoryFileSystem : IAttributeFileSystem { ref DirectoryEntry entry = ref entryBuffer[i]; - StringUtils.Copy(entry.Name, CurrentDir.Name); + StringUtils.Copy(entry.Name.Items, CurrentDir.Name); entry.Name[PathTool.EntryNameLengthMax] = 0; entry.Type = DirectoryEntryType.Directory; @@ -270,7 +270,7 @@ public class InMemoryFileSystem : IAttributeFileSystem { ref DirectoryEntry entry = ref entryBuffer[i]; - StringUtils.Copy(entry.Name, CurrentFile.Name); + StringUtils.Copy(entry.Name.Items, CurrentFile.Name); entry.Name[PathTool.EntryNameLengthMax] = 0; entry.Type = DirectoryEntryType.File; diff --git a/src/LibHac/Tools/FsSystem/RomFs/RomFsDirectory.cs b/src/LibHac/Tools/FsSystem/RomFs/RomFsDirectory.cs index ed2e593c..d7b25092 100644 --- a/src/LibHac/Tools/FsSystem/RomFs/RomFsDirectory.cs +++ b/src/LibHac/Tools/FsSystem/RomFs/RomFsDirectory.cs @@ -50,7 +50,7 @@ public class RomFsDirectory : IDirectory ref DirectoryEntry entry = ref entryBuffer[i]; Span nameUtf8 = Encoding.UTF8.GetBytes(name); - StringUtils.Copy(entry.Name, nameUtf8); + StringUtils.Copy(entry.Name.Items, nameUtf8); entry.Name[PathTool.EntryNameLengthMax] = 0; entry.Type = DirectoryEntryType.Directory; @@ -70,7 +70,7 @@ public class RomFsDirectory : IDirectory ref DirectoryEntry entry = ref entryBuffer[i]; Span nameUtf8 = Encoding.UTF8.GetBytes(name); - StringUtils.Copy(entry.Name, nameUtf8); + StringUtils.Copy(entry.Name.Items, nameUtf8); entry.Name[PathTool.EntryNameLengthMax] = 0; entry.Type = DirectoryEntryType.File; diff --git a/src/LibHac/Tools/FsSystem/Save/SaveDataDirectory.cs b/src/LibHac/Tools/FsSystem/Save/SaveDataDirectory.cs index adf8f9dd..d1133b8a 100644 --- a/src/LibHac/Tools/FsSystem/Save/SaveDataDirectory.cs +++ b/src/LibHac/Tools/FsSystem/Save/SaveDataDirectory.cs @@ -50,7 +50,7 @@ public class SaveDataDirectory : IDirectory ref DirectoryEntry entry = ref entryBuffer[i]; Span nameUtf8 = Encoding.UTF8.GetBytes(name); - StringUtils.Copy(entry.Name, nameUtf8); + StringUtils.Copy(entry.Name.Items, nameUtf8); entry.Name[64] = 0; entry.Type = DirectoryEntryType.Directory; @@ -70,7 +70,7 @@ public class SaveDataDirectory : IDirectory ref DirectoryEntry entry = ref entryBuffer[i]; Span nameUtf8 = Encoding.UTF8.GetBytes(name); - StringUtils.Copy(entry.Name, nameUtf8); + StringUtils.Copy(entry.Name.Items, nameUtf8); entry.Name[64] = 0; entry.Type = DirectoryEntryType.File; diff --git a/src/hactoolnet/ProcessPackage.cs b/src/hactoolnet/ProcessPackage.cs index 1ab47203..b62670af 100644 --- a/src/hactoolnet/ProcessPackage.cs +++ b/src/hactoolnet/ProcessPackage.cs @@ -64,9 +64,9 @@ internal static class ProcessPackage if (package1.IsMariko) { sb.AppendLine("Mariko OEM Header:"); - PrintItem(sb, colLen, " Signature:", package1.MarikoOemHeader.RsaSig.ToArray()); - PrintItem(sb, colLen, " Random Salt:", package1.MarikoOemHeader.Salt.ToArray()); - PrintItem(sb, colLen, " OEM Bootloader Hash:", package1.MarikoOemHeader.Hash.ToArray()); + PrintItem(sb, colLen, " Signature:", package1.MarikoOemHeader.RsaSig.ItemsRo.ToArray()); + PrintItem(sb, colLen, " Random Salt:", package1.MarikoOemHeader.Salt.ItemsRo.ToArray()); + PrintItem(sb, colLen, " OEM Bootloader Hash:", package1.MarikoOemHeader.Hash.ItemsRo.ToArray()); PrintItem(sb, colLen, " OEM Bootloader Version:", $"{package1.MarikoOemHeader.Version:x2}"); PrintItem(sb, colLen, " OEM Bootloader Size:", $"{package1.MarikoOemHeader.Size:x8}"); PrintItem(sb, colLen, " OEM Bootloader Load Address:", $"{package1.MarikoOemHeader.LoadAddress:x8}"); @@ -82,7 +82,7 @@ internal static class ProcessPackage if (!package1.IsMariko && package1.IsModern) { - PrintItem(sb, colLen, " PK11 MAC:", package1.Pk11Mac); + PrintItem(sb, colLen, " PK11 MAC:", package1.Pk11Mac.ItemsRo.ToArray()); } if (package1.IsDecrypted) @@ -162,16 +162,16 @@ internal static class ProcessPackage sb.AppendLine(); sb.AppendLine("PK21:"); - PrintItem(sb, colLen, $"Signature{signatureValidity.GetValidityString()}:", package2.Header.Signature.ToArray()); - PrintItem(sb, colLen, "Header Version:", $"{package2.Header.Meta.KeyGeneration:x2}"); + PrintItem(sb, colLen, $"Signature{signatureValidity.GetValidityString()}:", package2.Header.Signature.ItemsRo.ToArray()); + PrintItem(sb, colLen, "Header Version:", $"{package2.Header.Meta.GetKeyGeneration():x2}"); for (int i = 0; i < 3; i++) { string name = package2.Header.Meta.PayloadSizes[i] != 0 ? Package2SectionNames[i] : "Empty"; sb.AppendLine($"Section {i} ({name}):"); - PrintItem(sb, colLen, " Hash:", package2.Header.Meta.PayloadHashes[i]); - PrintItem(sb, colLen, " CTR:", package2.Header.Meta.PayloadIvs[i]); + PrintItem(sb, colLen, " Hash:", package2.Header.Meta.PayloadHashes[i].ItemsRo.ToArray()); + PrintItem(sb, colLen, " CTR:", package2.Header.Meta.PayloadIvs[i].ItemsRo.ToArray()); PrintItem(sb, colLen, " Load Address:", $"{package2.Header.Meta.PayloadOffsets[i] + 0x80000000:x8}"); PrintItem(sb, colLen, " Size:", $"{package2.Header.Meta.PayloadSizes[i]:x8}"); } diff --git a/tests/LibHac.Tests/Boot/TypeLayoutTests.cs b/tests/LibHac.Tests/Boot/TypeLayoutTests.cs new file mode 100644 index 00000000..4effb74a --- /dev/null +++ b/tests/LibHac.Tests/Boot/TypeLayoutTests.cs @@ -0,0 +1,132 @@ +using System.Runtime.CompilerServices; +using LibHac.Boot; +using Xunit; +using static LibHac.Tests.Common.Layout; + +namespace LibHac.Tests.Boot; + +public class TypeLayoutTests +{ + [Fact] + public static void EncryptedKeyBlob_Layout() + { + var s = new EncryptedKeyBlob(); + + Assert.Equal(0xB0, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Cmac)); + Assert.Equal(0x10, GetOffset(in s, in s.Counter)); + Assert.Equal(0x20, GetOffset(in s, in s.Payload)); + } + + [Fact] + public static void KeyBlob_Layout() + { + var s = new KeyBlob(); + + Assert.Equal(0x90, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.MasterKek)); + Assert.Equal(0x10, GetOffset(in s, in s.Unused)); + Assert.Equal(0x80, GetOffset(in s, in s.Package1Key)); + } + + [Fact] + public static void Package1MarikoOemHeader_Layout() + { + var s = new Package1MarikoOemHeader(); + + Assert.Equal(0x170, Unsafe.SizeOf()); + + Assert.Equal(0x000, GetOffset(in s, in s.AesMac)); + Assert.Equal(0x010, GetOffset(in s, in s.RsaSig)); + Assert.Equal(0x110, GetOffset(in s, in s.Salt)); + Assert.Equal(0x130, GetOffset(in s, in s.Hash)); + Assert.Equal(0x150, GetOffset(in s, in s.Version)); + Assert.Equal(0x154, GetOffset(in s, in s.Size)); + Assert.Equal(0x158, GetOffset(in s, in s.LoadAddress)); + Assert.Equal(0x15C, GetOffset(in s, in s.EntryPoint)); + Assert.Equal(0x160, GetOffset(in s, in s.Reserved)); + } + + [Fact] + public static void Package1MetaData_Layout() + { + var s = new Package1MetaData(); + + Assert.Equal(0x20, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.LoaderHash)); + Assert.Equal(0x04, GetOffset(in s, in s.SecureMonitorHash)); + Assert.Equal(0x08, GetOffset(in s, in s.BootloaderHash)); + Assert.Equal(0x0C, GetOffset(in s, in s.Reserved)); + Assert.Equal(0x10, GetOffset(in s, in s.BuildDate.Value[0])); + Assert.Equal(0x1E, GetOffset(in s, in s.KeyGeneration)); + Assert.Equal(0x1F, GetOffset(in s, in s.Version)); + + Assert.Equal(0x10, GetOffset(in s, in s.Iv[0])); + } + + [Fact] + public static void Package1Stage1Footer_Layout() + { + var s = new Package1Stage1Footer(); + + Assert.Equal(0x20, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Pk11Size)); + Assert.Equal(0x04, GetOffset(in s, in s.Reserved)); + Assert.Equal(0x10, GetOffset(in s, in s.Iv)); + } + + [Fact] + public static void Package1Pk11Header_Layout() + { + var s = new Package1Pk11Header(); + + Assert.Equal(0x20, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Magic)); + Assert.Equal(0x04, GetOffset(in s, in s.WarmBootSize)); + Assert.Equal(0x08, GetOffset(in s, in s.WarmBootOffset)); + Assert.Equal(0x0C, GetOffset(in s, in s.Reserved)); + Assert.Equal(0x10, GetOffset(in s, in s.BootloaderSize)); + Assert.Equal(0x14, GetOffset(in s, in s.BootloaderOffset)); + Assert.Equal(0x18, GetOffset(in s, in s.SecureMonitorSize)); + Assert.Equal(0x1C, GetOffset(in s, in s.SecureMonitorOffset)); + } + + [Fact] + public static void Package2Header_Layout() + { + var s = new Package2Header(); + + Assert.Equal(0x200, Unsafe.SizeOf()); + + Assert.Equal(0x000, GetOffset(in s, in s.Signature)); + Assert.Equal(0x100, GetOffset(in s, in s.Meta)); + } + + [Fact] + public static void Package2Meta_Layout() + { + var s = new Package2Meta(); + + Assert.Equal(0x100, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.HeaderIv)); + Assert.Equal(0x10, GetOffset(in s, in s.PayloadIvs)); + Assert.Equal(0x40, GetOffset(in s, in s.Padding40)); + Assert.Equal(0x50, GetOffset(in s, in s.Magic)); + Assert.Equal(0x54, GetOffset(in s, in s.EntryPoint)); + Assert.Equal(0x5C, GetOffset(in s, in s.Package2Version)); + Assert.Equal(0x58, GetOffset(in s, in s.Padding58)); + Assert.Equal(0x5D, GetOffset(in s, in s.BootloaderVersion)); + Assert.Equal(0x60, GetOffset(in s, in s.PayloadSizes)); + Assert.Equal(0x6C, GetOffset(in s, in s.Padding6C)); + Assert.Equal(0x70, GetOffset(in s, in s.PayloadOffsets)); + Assert.Equal(0x7C, GetOffset(in s, in s.Padding7C)); + Assert.Equal(0x80, GetOffset(in s, in s.PayloadHashes)); + Assert.Equal(0xE0, GetOffset(in s, in s.PaddingE0)); + } +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Boot/TypeSizeTests.cs b/tests/LibHac.Tests/Boot/TypeSizeTests.cs deleted file mode 100644 index 7b62c316..00000000 --- a/tests/LibHac.Tests/Boot/TypeSizeTests.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ReSharper disable InconsistentNaming -using System.Runtime.CompilerServices; -using LibHac.Boot; -using Xunit; - -namespace LibHac.Tests.Boot; - -public class TypeSizeTests -{ - [Fact] - public static void EncryptedKeyBlobSizeIs0xB0() - { - Assert.Equal(0xB0, Unsafe.SizeOf()); - } - - [Fact] - public static void KeyBlobSizeIs0x90() - { - Assert.Equal(0x90, Unsafe.SizeOf()); - } -} diff --git a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs index e0746b30..057d9818 100644 --- a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs +++ b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs @@ -146,4 +146,147 @@ public class TypeLayoutTests Assert.Equal(0x58, GetOffset(in s, in s.PooledBufferFailedIdealAllocationCountOnAsyncAccess)); Assert.Equal(0x60, GetOffset(in s, in s.Reserved)); } + + [Fact] + public static void ApplicationInfo_Layout() + { + var s = new ApplicationInfo(); + + Assert.Equal(0x20, Unsafe.SizeOf()); + + Assert.Equal(0x0, GetOffset(in s, in s.ApplicationId)); + Assert.Equal(0x8, GetOffset(in s, in s.Version)); + Assert.Equal(0xC, GetOffset(in s, in s.LaunchType)); + Assert.Equal(0xD, GetOffset(in s, in s.IsMultiProgram)); + Assert.Equal(0xE, GetOffset(in s, in s.Reserved)); + } + + [Fact] + public static void CodeVerificationData_Layout() + { + var s = new CodeVerificationData(); + + Assert.Equal(0x124, Unsafe.SizeOf()); + + Assert.Equal(0x000, GetOffset(in s, in s.Signature)); + Assert.Equal(0x100, GetOffset(in s, in s.Hash)); + Assert.Equal(0x120, GetOffset(in s, in s.HasData)); + Assert.Equal(0x121, GetOffset(in s, in s.Reserved)); + } + + [Fact] + public static void DirectoryEntry_Layout() + { + var s = new DirectoryEntry(); + + Assert.Equal(0x310, Unsafe.SizeOf()); + + Assert.Equal(0x000, GetOffset(in s, in s.Name)); + Assert.Equal(0x301, GetOffset(in s, in s.Attributes)); + Assert.Equal(0x302, GetOffset(in s, in s.Reserved302)); + Assert.Equal(0x304, GetOffset(in s, in s.Type)); + Assert.Equal(0x305, GetOffset(in s, in s.Reserved305)); + Assert.Equal(0x308, GetOffset(in s, in s.Size)); + } + + [Fact] + public static void EncryptionSeed_Layout() + { + var s = new EncryptionSeed(); + + Assert.Equal(0x10, Unsafe.SizeOf()); + + Assert.Equal(0, GetOffset(in s, in s.Value)); + } + + [Fact] + public static void FileSystemProxyErrorInfo_Layout() + { + var s = new FileSystemProxyErrorInfo(); + + Assert.Equal(0x80, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.RemountForDataCorruptionCount)); + Assert.Equal(0x04, GetOffset(in s, in s.UnrecoverableDataCorruptionByRemountCount)); + Assert.Equal(0x08, GetOffset(in s, in s.FatFsError)); + Assert.Equal(0x28, GetOffset(in s, in s.RecoveredByInvalidateCacheCount)); + Assert.Equal(0x2C, GetOffset(in s, in s.SaveDataIndexCount)); + Assert.Equal(0x30, GetOffset(in s, in s.Reserved)); + } + + [Fact] + public static void StorageErrorInfo_Layout() + { + var s = new StorageErrorInfo(); + + Assert.Equal(0x10, Unsafe.SizeOf()); + + Assert.Equal(0x0, GetOffset(in s, in s.NumActivationFailures)); + Assert.Equal(0x4, GetOffset(in s, in s.NumActivationErrorCorrections)); + Assert.Equal(0x8, GetOffset(in s, in s.NumReadWriteFailures)); + Assert.Equal(0xC, GetOffset(in s, in s.NumReadWriteErrorCorrections)); + } + + [Fact] + public static void FileTimeStamp_Layout() + { + var s = new FileTimeStamp(); + + Assert.Equal(0x20, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Created)); + Assert.Equal(0x08, GetOffset(in s, in s.Accessed)); + Assert.Equal(0x10, GetOffset(in s, in s.Modified)); + Assert.Equal(0x18, GetOffset(in s, in s.IsLocalTime)); + Assert.Equal(0x19, GetOffset(in s, in s.Reserved)); + } + + [Fact] + public static void FileTimeStampRaw_Layout() + { + var s = new FileTimeStampRaw(); + + Assert.Equal(0x20, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Created)); + Assert.Equal(0x08, GetOffset(in s, in s.Accessed)); + Assert.Equal(0x10, GetOffset(in s, in s.Modified)); + Assert.Equal(0x18, GetOffset(in s, in s.IsLocalTime)); + Assert.Equal(0x19, GetOffset(in s, in s.Reserved)); + } + + [Fact] + public static void ProgramIndexMapInfo_Layout() + { + var s = new ProgramIndexMapInfo(); + + Assert.Equal(0x20, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.ProgramId)); + Assert.Equal(0x08, GetOffset(in s, in s.MainProgramId)); + Assert.Equal(0x10, GetOffset(in s, in s.ProgramIndex)); + Assert.Equal(0x11, GetOffset(in s, in s.Reserved)); + } + + [Fact] + public static void QueryRangeInfo_Layout() + { + var s = new QueryRangeInfo(); + + Assert.Equal(0x40, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.AesCtrKeyType)); + Assert.Equal(0x04, GetOffset(in s, in s.SpeedEmulationType)); + Assert.Equal(0x08, GetOffset(in s, in s.Reserved)); + } + + [Fact] + public static void RightsId_Layout() + { + var s = new RightsId(); + + Assert.Equal(0x10, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Value)); + } } \ No newline at end of file From 9a358a4e30a5ea1ef7b3ec4a55814a801e5b4b78 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Thu, 30 Dec 2021 20:49:36 -0700 Subject: [PATCH 26/34] Remove unnecessary DebuggerDisplayAttributes --- src/LibHac/Bcat/Digest.cs | 4 +--- src/LibHac/Bcat/DirectoryName.cs | 2 -- src/LibHac/Bcat/FileName.cs | 2 -- src/LibHac/Common/Buffer.cs | 4 +--- src/LibHac/Common/Id128.cs | 4 +--- src/LibHac/Common/Key128.cs | 4 +--- src/LibHac/Common/U8Span.cs | 6 ++---- src/LibHac/Common/U8SpanMutable.cs | 6 ++---- src/LibHac/Common/U8String.cs | 4 +--- src/LibHac/Common/U8StringBuilder.cs | 2 -- src/LibHac/Common/U8StringMutable.cs | 4 +--- src/LibHac/Crypto/KeyTypes.cs | 4 ---- src/LibHac/Fs/EncryptionSeed.cs | 4 +--- src/LibHac/Fs/MountName.cs | 2 -- src/LibHac/FsSrv/Sf/FspPath.cs | 3 +-- src/LibHac/FsSystem/FsPath.cs | 3 +-- src/LibHac/Lr/Path.cs | 1 - src/LibHac/Ncm/ContentId.cs | 4 +--- src/LibHac/Ncm/PlaceHolderId.cs | 4 +--- src/LibHac/Sm/ServiceName.cs | 4 +--- src/LibHac/Spl/AccessKey.cs | 4 +--- 21 files changed, 17 insertions(+), 58 deletions(-) diff --git a/src/LibHac/Bcat/Digest.cs b/src/LibHac/Bcat/Digest.cs index 108ef0aa..b4f23f23 100644 --- a/src/LibHac/Bcat/Digest.cs +++ b/src/LibHac/Bcat/Digest.cs @@ -1,10 +1,8 @@ -using System.Diagnostics; -using LibHac.Common.FixedArrays; +using LibHac.Common.FixedArrays; using LibHac.Util; namespace LibHac.Bcat; -[DebuggerDisplay("{ToString()}")] public struct Digest { public Array16 Value; diff --git a/src/LibHac/Bcat/DirectoryName.cs b/src/LibHac/Bcat/DirectoryName.cs index 990daeeb..5d0ad5c9 100644 --- a/src/LibHac/Bcat/DirectoryName.cs +++ b/src/LibHac/Bcat/DirectoryName.cs @@ -1,11 +1,9 @@ using System; -using System.Diagnostics; using LibHac.Common.FixedArrays; using LibHac.Util; namespace LibHac.Bcat; -[DebuggerDisplay("{ToString()}")] public struct DirectoryName { private const int MaxSize = 0x20; diff --git a/src/LibHac/Bcat/FileName.cs b/src/LibHac/Bcat/FileName.cs index 3c35004f..0fbfac18 100644 --- a/src/LibHac/Bcat/FileName.cs +++ b/src/LibHac/Bcat/FileName.cs @@ -1,11 +1,9 @@ using System; -using System.Diagnostics; using LibHac.Common.FixedArrays; using LibHac.Util; namespace LibHac.Bcat; -[DebuggerDisplay("{ToString()}")] public struct FileName { private const int MaxSize = 0x20; diff --git a/src/LibHac/Common/Buffer.cs b/src/LibHac/Common/Buffer.cs index 7f3846f4..cfaf233a 100644 --- a/src/LibHac/Common/Buffer.cs +++ b/src/LibHac/Common/Buffer.cs @@ -10,7 +10,6 @@ namespace LibHac.Common; /// Represents a buffer of 16 bytes. /// Contains functions that assist with common operations on small buffers. /// -[DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Sequential, Size = 16)] public struct Buffer16 { @@ -67,7 +66,6 @@ public struct Buffer16 } } -[DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Sequential, Size = 32)] public struct Buffer32 { @@ -124,4 +122,4 @@ public struct Buffer32 { return Bytes.ToHexString(); } -} +} \ No newline at end of file diff --git a/src/LibHac/Common/Id128.cs b/src/LibHac/Common/Id128.cs index e470d6b0..75a36e0f 100644 --- a/src/LibHac/Common/Id128.cs +++ b/src/LibHac/Common/Id128.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.Runtime.InteropServices; using LibHac.Util; @@ -8,7 +7,6 @@ namespace LibHac.Common; /// /// A generic 128-bit ID value. /// -[DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct Id128 : IEquatable, IComparable, IComparable { @@ -81,4 +79,4 @@ public struct Id128 : IEquatable, IComparable, IComparable public static bool operator >(Id128 left, Id128 right) => left.CompareTo(right) > 0; public static bool operator <=(Id128 left, Id128 right) => left.CompareTo(right) <= 0; public static bool operator >=(Id128 left, Id128 right) => left.CompareTo(right) >= 0; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/Key128.cs b/src/LibHac/Common/Key128.cs index ebc6682e..f1171c2c 100644 --- a/src/LibHac/Common/Key128.cs +++ b/src/LibHac/Common/Key128.cs @@ -1,11 +1,9 @@ using System; -using System.Diagnostics; using System.Runtime.InteropServices; using LibHac.Util; namespace LibHac.Common; -[DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct Key128 : IEquatable { @@ -42,4 +40,4 @@ public struct Key128 : IEquatable public static bool operator ==(Key128 left, Key128 right) => left.Equals(right); public static bool operator !=(Key128 left, Key128 right) => !(left == right); -} +} \ No newline at end of file diff --git a/src/LibHac/Common/U8Span.cs b/src/LibHac/Common/U8Span.cs index dac9b2c5..c5758105 100644 --- a/src/LibHac/Common/U8Span.cs +++ b/src/LibHac/Common/U8Span.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -7,7 +6,6 @@ using LibHac.Util; namespace LibHac.Common; -[DebuggerDisplay("{ToString()}")] public readonly ref struct U8Span { private readonly ReadOnlySpan _buffer; @@ -51,7 +49,7 @@ public readonly ref struct U8Span #if DEBUG return _buffer[i]; #else - return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i); + return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i); #endif } @@ -99,4 +97,4 @@ public readonly ref struct U8Span /// if the span has no buffer or begins with a null terminator. /// Otherwise, . public bool IsEmpty() => _buffer.IsEmpty || MemoryMarshal.GetReference(_buffer) == 0; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/U8SpanMutable.cs b/src/LibHac/Common/U8SpanMutable.cs index 1dd1629e..e3ad7938 100644 --- a/src/LibHac/Common/U8SpanMutable.cs +++ b/src/LibHac/Common/U8SpanMutable.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -7,7 +6,6 @@ using LibHac.Util; namespace LibHac.Common; -[DebuggerDisplay("{ToString()}")] public readonly ref struct U8SpanMutable { private readonly Span _buffer; @@ -50,7 +48,7 @@ public readonly ref struct U8SpanMutable #if DEBUG return _buffer[i]; #else - return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i); + return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i); #endif } @@ -95,4 +93,4 @@ public readonly ref struct U8SpanMutable /// if the span has no buffer or begins with a null terminator. /// Otherwise, . public bool IsEmpty() => _buffer.IsEmpty || MemoryMarshal.GetReference(_buffer) == 0; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/U8String.cs b/src/LibHac/Common/U8String.cs index dbdbae0b..10fc22a1 100644 --- a/src/LibHac/Common/U8String.cs +++ b/src/LibHac/Common/U8String.cs @@ -1,11 +1,9 @@ using System; -using System.Diagnostics; using System.Text; using LibHac.Util; namespace LibHac.Common; -[DebuggerDisplay("{ToString()}")] public readonly struct U8String { private readonly byte[] _buffer; @@ -60,4 +58,4 @@ public readonly struct U8String /// if the string has no buffer or begins with a null terminator. /// Otherwise, . public bool IsEmpty() => _buffer == null || _buffer.Length < 1 || _buffer[0] == 0; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/U8StringBuilder.cs b/src/LibHac/Common/U8StringBuilder.cs index 300afa94..73b8bd23 100644 --- a/src/LibHac/Common/U8StringBuilder.cs +++ b/src/LibHac/Common/U8StringBuilder.cs @@ -1,14 +1,12 @@ using System; using System.Buffers; using System.Buffers.Text; -using System.Diagnostics; using System.Runtime.CompilerServices; using LibHac.Diag; using LibHac.Util; namespace LibHac.Common; -[DebuggerDisplay("{ToString()}")] public ref struct U8StringBuilder { private const int NullTerminatorLength = 1; diff --git a/src/LibHac/Common/U8StringMutable.cs b/src/LibHac/Common/U8StringMutable.cs index dd76847e..38f2abca 100644 --- a/src/LibHac/Common/U8StringMutable.cs +++ b/src/LibHac/Common/U8StringMutable.cs @@ -1,11 +1,9 @@ using System; -using System.Diagnostics; using System.Text; using LibHac.Util; namespace LibHac.Common; -[DebuggerDisplay("{ToString()}")] public readonly struct U8StringMutable { private readonly byte[] _buffer; @@ -67,4 +65,4 @@ public readonly struct U8StringMutable /// if the string has no buffer or begins with a null terminator. /// Otherwise, . public bool IsEmpty() => _buffer == null || _buffer.Length < 1 || _buffer[0] == 0; -} +} \ No newline at end of file diff --git a/src/LibHac/Crypto/KeyTypes.cs b/src/LibHac/Crypto/KeyTypes.cs index 01f38993..32c9817f 100644 --- a/src/LibHac/Crypto/KeyTypes.cs +++ b/src/LibHac/Crypto/KeyTypes.cs @@ -8,7 +8,6 @@ using LibHac.Util; namespace LibHac.Crypto; -[DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Explicit, Size = Size)] public struct AesKey { @@ -36,7 +35,6 @@ public struct AesKey #endif } -[DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Explicit, Size = Size)] public struct AesXtsKey { @@ -64,7 +62,6 @@ public struct AesXtsKey public readonly override string ToString() => DataRo.ToHexString(); } -[DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Explicit, Size = Size)] public struct AesIv { @@ -91,7 +88,6 @@ public struct AesIv #endif } -[DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Explicit, Size = Size)] public struct AesCmac { diff --git a/src/LibHac/Fs/EncryptionSeed.cs b/src/LibHac/Fs/EncryptionSeed.cs index 619abda7..8d5601e1 100644 --- a/src/LibHac/Fs/EncryptionSeed.cs +++ b/src/LibHac/Fs/EncryptionSeed.cs @@ -1,10 +1,8 @@ -using System.Diagnostics; -using LibHac.Common.FixedArrays; +using LibHac.Common.FixedArrays; using LibHac.Util; namespace LibHac.Fs; -[DebuggerDisplay("{ToString()}")] public struct EncryptionSeed { public Array16 Value; diff --git a/src/LibHac/Fs/MountName.cs b/src/LibHac/Fs/MountName.cs index 52ca771c..152dfb41 100644 --- a/src/LibHac/Fs/MountName.cs +++ b/src/LibHac/Fs/MountName.cs @@ -1,11 +1,9 @@ using System; -using System.Diagnostics; using LibHac.Common; using LibHac.Common.FixedArrays; namespace LibHac.Fs; -[DebuggerDisplay("{ToString()}")] internal struct MountName { private Array16 _nameArray; diff --git a/src/LibHac/FsSrv/Sf/FspPath.cs b/src/LibHac/FsSrv/Sf/FspPath.cs index 862b2620..9fdc4fd4 100644 --- a/src/LibHac/FsSrv/Sf/FspPath.cs +++ b/src/LibHac/FsSrv/Sf/FspPath.cs @@ -8,7 +8,6 @@ using LibHac.Util; namespace LibHac.FsSrv.Sf; -[DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Sequential, Size = MaxLength + 1)] public readonly struct FspPath { @@ -48,4 +47,4 @@ public readonly struct FspPath public static implicit operator U8Span(in FspPath value) => new U8Span(SpanHelpers.AsReadOnlyByteSpan(in value)); public override string ToString() => StringUtils.Utf8ZToString(Str); -} +} \ No newline at end of file diff --git a/src/LibHac/FsSystem/FsPath.cs b/src/LibHac/FsSystem/FsPath.cs index 3d6434a6..8bc24c03 100644 --- a/src/LibHac/FsSystem/FsPath.cs +++ b/src/LibHac/FsSystem/FsPath.cs @@ -8,7 +8,6 @@ using LibHac.Util; namespace LibHac.FsSystem; -[DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Sequential, Size = MaxLength + 1)] public struct FsPath { @@ -40,4 +39,4 @@ public struct FsPath public static implicit operator U8Span(in FsPath value) => new U8Span(SpanHelpers.AsReadOnlyByteSpan(in value)); public override string ToString() => StringUtils.Utf8ZToString(Str); -} +} \ No newline at end of file diff --git a/src/LibHac/Lr/Path.cs b/src/LibHac/Lr/Path.cs index 495f8be3..444b6056 100644 --- a/src/LibHac/Lr/Path.cs +++ b/src/LibHac/Lr/Path.cs @@ -8,7 +8,6 @@ using LibHac.Util; namespace LibHac.Lr; -[DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Sequential, Size = PathTool.EntryNameLengthMax)] public struct Path { diff --git a/src/LibHac/Ncm/ContentId.cs b/src/LibHac/Ncm/ContentId.cs index 1cfb93b4..f2a03564 100644 --- a/src/LibHac/Ncm/ContentId.cs +++ b/src/LibHac/Ncm/ContentId.cs @@ -1,11 +1,9 @@ using System; -using System.Diagnostics; using System.Runtime.InteropServices; using LibHac.Common; namespace LibHac.Ncm; -[DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct ContentId : IEquatable, IComparable, IComparable { @@ -50,4 +48,4 @@ public struct ContentId : IEquatable, IComparable, ICompar public static bool operator >(ContentId left, ContentId right) => left.CompareTo(right) > 0; public static bool operator <=(ContentId left, ContentId right) => left.CompareTo(right) <= 0; public static bool operator >=(ContentId left, ContentId right) => left.CompareTo(right) >= 0; -} +} \ No newline at end of file diff --git a/src/LibHac/Ncm/PlaceHolderId.cs b/src/LibHac/Ncm/PlaceHolderId.cs index f0aac726..0a49f4c6 100644 --- a/src/LibHac/Ncm/PlaceHolderId.cs +++ b/src/LibHac/Ncm/PlaceHolderId.cs @@ -1,11 +1,9 @@ using System; -using System.Diagnostics; using System.Runtime.InteropServices; using LibHac.Common; namespace LibHac.Ncm; -[DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct PlaceHolderId : IEquatable, IComparable, IComparable { @@ -50,4 +48,4 @@ public struct PlaceHolderId : IEquatable, IComparable(PlaceHolderId left, PlaceHolderId right) => left.CompareTo(right) > 0; public static bool operator <=(PlaceHolderId left, PlaceHolderId right) => left.CompareTo(right) <= 0; public static bool operator >=(PlaceHolderId left, PlaceHolderId right) => left.CompareTo(right) >= 0; -} +} \ No newline at end of file diff --git a/src/LibHac/Sm/ServiceName.cs b/src/LibHac/Sm/ServiceName.cs index a5baf17f..5b0c99a0 100644 --- a/src/LibHac/Sm/ServiceName.cs +++ b/src/LibHac/Sm/ServiceName.cs @@ -1,11 +1,9 @@ using System; -using System.Diagnostics; using LibHac.Common; using LibHac.Util; namespace LibHac.Sm; -[DebuggerDisplay("{ToString()}")] public readonly struct ServiceName : IEquatable { private const int MaxLength = 8; @@ -40,4 +38,4 @@ public readonly struct ServiceName : IEquatable ulong name = Name; return StringUtils.Utf8ZToString(SpanHelpers.AsReadOnlyByteSpan(in name)); } -} +} \ No newline at end of file diff --git a/src/LibHac/Spl/AccessKey.cs b/src/LibHac/Spl/AccessKey.cs index e8cb0772..33586fd1 100644 --- a/src/LibHac/Spl/AccessKey.cs +++ b/src/LibHac/Spl/AccessKey.cs @@ -1,11 +1,9 @@ using System; -using System.Diagnostics; using System.Runtime.InteropServices; using LibHac.Common; namespace LibHac.Spl; -[DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct AccessKey : IEquatable { @@ -25,4 +23,4 @@ public struct AccessKey : IEquatable public override int GetHashCode() => Key.GetHashCode(); public static bool operator ==(AccessKey left, AccessKey right) => left.Equals(right); public static bool operator !=(AccessKey left, AccessKey right) => !(left == right); -} +} \ No newline at end of file From b7e8ea8249d9d773ce87cfe9284b795db6fad87d Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sun, 2 Jan 2022 14:14:37 -0700 Subject: [PATCH 27/34] Update struct layout of most remaining Fs structs --- src/LibHac/Common/FixedArrays/Array28.cs | 32 +++++++ src/LibHac/Common/FixedArrays/Array38.cs | 31 +++++++ src/LibHac/Fs/Impl/SaveDataTransferTypes.cs | 38 ++------ src/LibHac/Fs/SaveDataTransferTypes.cs | 27 +----- src/LibHac/FsSrv/Impl/AccessControl.cs | 34 +++---- src/LibHac/FsSrv/Impl/MultiCommitManager.cs | 9 +- src/LibHac/FsSrv/SaveDataIndexerValue.cs | 16 ++-- src/LibHac/FsSrv/Sf/FspPath.cs | 15 +-- src/LibHac/FsSrv/Sf/Path.cs | 19 +--- .../FsSrv/Storage/StorageDeviceHandle.cs | 7 +- tests/LibHac.Tests/Fs/TypeLayoutTests.cs | 65 +++++++++++++ tests/LibHac.Tests/FsSrv/TypeLayoutTests.cs | 91 +++++++++++++++++++ 12 files changed, 272 insertions(+), 112 deletions(-) create mode 100644 src/LibHac/Common/FixedArrays/Array28.cs create mode 100644 src/LibHac/Common/FixedArrays/Array38.cs create mode 100644 tests/LibHac.Tests/FsSrv/TypeLayoutTests.cs diff --git a/src/LibHac/Common/FixedArrays/Array28.cs b/src/LibHac/Common/FixedArrays/Array28.cs new file mode 100644 index 00000000..24b8beb7 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array28.cs @@ -0,0 +1,32 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array28 +{ + public const int Length = 28; + + private Array16 _0; + private Array8 _16; + private Array4 _24; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array28 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array38.cs b/src/LibHac/Common/FixedArrays/Array38.cs new file mode 100644 index 00000000..2c6b8e90 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array38.cs @@ -0,0 +1,31 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array38 +{ + public const int Length = 38; + + private Array32 _0; + private Array6 _32; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array38 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Fs/Impl/SaveDataTransferTypes.cs b/src/LibHac/Fs/Impl/SaveDataTransferTypes.cs index 5c239659..4c7090a3 100644 --- a/src/LibHac/Fs/Impl/SaveDataTransferTypes.cs +++ b/src/LibHac/Fs/Impl/SaveDataTransferTypes.cs @@ -1,53 +1,27 @@ -using System; -using System.Runtime.InteropServices; -using LibHac.Common; +using LibHac.Common.FixedArrays; namespace LibHac.Fs.Impl; -[StructLayout(LayoutKind.Sequential, Size = 0x20)] public struct InitialDataAad { - public byte this[int i] - { - readonly get => BytesRo[i]; - set => Bytes[i] = value; - } - - public Span Bytes => SpanHelpers.AsByteSpan(ref this); - public readonly ReadOnlySpan BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this); + public Array32 Value; } -[StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct KeySeed { - public byte this[int i] - { - readonly get => BytesRo[i]; - set => Bytes[i] = value; - } - - public Span Bytes => SpanHelpers.AsByteSpan(ref this); - public readonly ReadOnlySpan BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this); + public Array16 Value; } -[StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct InitialDataMac { - public byte this[int i] - { - readonly get => BytesRo[i]; - set => Bytes[i] = value; - } - - public Span Bytes => SpanHelpers.AsByteSpan(ref this); - public readonly ReadOnlySpan BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this); + public Array16 Value; } -[StructLayout(LayoutKind.Sequential, Size = 0x20)] public struct ImportReportInfo { public byte DiffChunkCount; public byte DoubleDivisionDiffChunkCount; public byte HalfDivisionDiffChunkCount; public byte CompressionRate; -} + public Array28 Reserved; +} \ No newline at end of file diff --git a/src/LibHac/Fs/SaveDataTransferTypes.cs b/src/LibHac/Fs/SaveDataTransferTypes.cs index 18871a89..d3777e0a 100644 --- a/src/LibHac/Fs/SaveDataTransferTypes.cs +++ b/src/LibHac/Fs/SaveDataTransferTypes.cs @@ -1,32 +1,13 @@ -using System; -using System.Runtime.InteropServices; -using LibHac.Common; +using LibHac.Common.FixedArrays; namespace LibHac.Fs; -[StructLayout(LayoutKind.Sequential, Size = 0x100)] public struct RsaEncryptedKey { - public byte this[int i] - { - readonly get => BytesRo[i]; - set => Bytes[i] = value; - } - - public Span Bytes => SpanHelpers.AsByteSpan(ref this); - public readonly ReadOnlySpan BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this); - + public Array256 Value; } -[StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct AesKey { - public byte this[int i] - { - readonly get => BytesRo[i]; - set => Bytes[i] = value; - } - - public Span Bytes => SpanHelpers.AsByteSpan(ref this); - public readonly ReadOnlySpan BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this); -} + public Array16 Value; +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/Impl/AccessControl.cs b/src/LibHac/FsSrv/Impl/AccessControl.cs index a40758f7..6d63c096 100644 --- a/src/LibHac/FsSrv/Impl/AccessControl.cs +++ b/src/LibHac/FsSrv/Impl/AccessControl.cs @@ -752,30 +752,30 @@ public readonly struct AccessControlBits public bool CanWriteSaveDataFileSystemExtraDataTimeStamp() => Has(Bits.SaveDataBackUp); } -[StructLayout(LayoutKind.Explicit, Size = 0x2C)] +[StructLayout(LayoutKind.Sequential, Pack = 4)] internal struct AccessControlDescriptor { - [FieldOffset(0x00)] public byte Version; - [FieldOffset(0x01)] public byte ContentOwnerIdCount; - [FieldOffset(0x02)] public byte SaveDataOwnerIdCount; - [FieldOffset(0x04)] public ulong AccessFlags; - [FieldOffset(0x0C)] public ulong ContentOwnerIdMin; - [FieldOffset(0x14)] public ulong ContentOwnerIdMax; - [FieldOffset(0x1C)] public ulong SaveDataOwnerIdMin; - [FieldOffset(0x24)] public ulong SaveDataOwnerIdMax; + public byte Version; + public byte ContentOwnerIdCount; + public byte SaveDataOwnerIdCount; + public ulong AccessFlags; + public ulong ContentOwnerIdMin; + public ulong ContentOwnerIdMax; + public ulong SaveDataOwnerIdMin; + public ulong SaveDataOwnerIdMax; // public ulong ContentOwnerIds[ContentOwnerIdCount]; // public ulong SaveDataOwnerIds[SaveDataOwnerIdCount]; } -[StructLayout(LayoutKind.Explicit, Size = 0x1C)] +[StructLayout(LayoutKind.Sequential, Pack = 4)] internal struct AccessControlDataHeader { - [FieldOffset(0x00)] public byte Version; - [FieldOffset(0x04)] public ulong AccessFlags; - [FieldOffset(0x0C)] public int ContentOwnerInfoOffset; - [FieldOffset(0x10)] public int ContentOwnerInfoSize; - [FieldOffset(0x14)] public int SaveDataOwnerInfoOffset; - [FieldOffset(0x18)] public int SaveDataOwnerInfoSize; + public byte Version; + public ulong AccessFlags; + public int ContentOwnerInfoOffset; + public int ContentOwnerInfoSize; + public int SaveDataOwnerInfoOffset; + public int SaveDataOwnerInfoSize; // [FieldOffset(ContentOwnerInfoOffset)] // public int ContentOwnerInfoCount; @@ -911,4 +911,4 @@ public enum AccessibilityType MountTemporaryDirectory, MountAllBaseFileSystem, NotMount -} +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/Impl/MultiCommitManager.cs b/src/LibHac/FsSrv/Impl/MultiCommitManager.cs index 7c847132..a147c4d0 100644 --- a/src/LibHac/FsSrv/Impl/MultiCommitManager.cs +++ b/src/LibHac/FsSrv/Impl/MultiCommitManager.cs @@ -498,13 +498,12 @@ internal class MultiCommitManager : IMultiCommitManager return Recover(multiCommitInterface, fileSystem.Get, saveService); } - [StructLayout(LayoutKind.Explicit, Size = 0x18)] private struct Context { - [FieldOffset(0x00)] public int Version; - [FieldOffset(0x04)] public CommitState State; - [FieldOffset(0x08)] public int FileSystemCount; - [FieldOffset(0x10)] public long Counter; + public int Version; + public CommitState State; + public int FileSystemCount; + public long Counter; } private enum CommitState diff --git a/src/LibHac/FsSrv/SaveDataIndexerValue.cs b/src/LibHac/FsSrv/SaveDataIndexerValue.cs index 34b09a5c..b1a2e9a5 100644 --- a/src/LibHac/FsSrv/SaveDataIndexerValue.cs +++ b/src/LibHac/FsSrv/SaveDataIndexerValue.cs @@ -1,14 +1,14 @@ -using System.Runtime.InteropServices; +using LibHac.Common.FixedArrays; using LibHac.Fs; namespace LibHac.FsSrv; -[StructLayout(LayoutKind.Explicit, Size = 0x40)] public struct SaveDataIndexerValue { - [FieldOffset(0x00)] public ulong SaveDataId; - [FieldOffset(0x08)] public long Size; - [FieldOffset(0x10)] public ulong Field10; - [FieldOffset(0x18)] public SaveDataSpaceId SpaceId; - [FieldOffset(0x19)] public SaveDataState State; -} + public ulong SaveDataId; + public long Size; + public ulong Field10; + public SaveDataSpaceId SpaceId; + public SaveDataState State; + public Array38 Reserved; +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/Sf/FspPath.cs b/src/LibHac/FsSrv/Sf/FspPath.cs index 9fdc4fd4..37e1c8ff 100644 --- a/src/LibHac/FsSrv/Sf/FspPath.cs +++ b/src/LibHac/FsSrv/Sf/FspPath.cs @@ -1,26 +1,21 @@ using System; -using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Fs; using LibHac.Util; namespace LibHac.FsSrv.Sf; -[StructLayout(LayoutKind.Sequential, Size = MaxLength + 1)] +[StructLayout(LayoutKind.Sequential)] public readonly struct FspPath { internal const int MaxLength = 0x300; -#if DEBUG - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding200; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly byte Padding300; -#endif + private readonly Array769 _value; - public ReadOnlySpan Str => SpanHelpers.AsReadOnlyByteSpan(in this); + public ReadOnlySpan Str => SpanHelpers.AsReadOnlyByteSpan(in _value); public static Result FromSpan(out FspPath fspPath, ReadOnlySpan path) { @@ -28,7 +23,7 @@ public readonly struct FspPath Span str = SpanHelpers.AsByteSpan(ref fspPath); - // Ensure null terminator even if the creation fails for safety + // Ensure null terminator even if the creation fails str[MaxLength] = 0; var sb = new U8StringBuilder(str); diff --git a/src/LibHac/FsSrv/Sf/Path.cs b/src/LibHac/FsSrv/Sf/Path.cs index 79996102..111ed9e4 100644 --- a/src/LibHac/FsSrv/Sf/Path.cs +++ b/src/LibHac/FsSrv/Sf/Path.cs @@ -1,23 +1,14 @@ using System; using System.Runtime.InteropServices; using LibHac.Common; -using LibHac.Fs; - -#if DEBUG -using System.Diagnostics; -#endif +using LibHac.Common.FixedArrays; namespace LibHac.FsSrv.Sf; -[StructLayout(LayoutKind.Sequential, Size = PathTool.EntryNameLengthMax + 1)] +[StructLayout(LayoutKind.Sequential)] public readonly struct Path { -#if DEBUG - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding200; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly byte Padding300; -#endif + private readonly Array769 _value; - public ReadOnlySpan Str => SpanHelpers.AsReadOnlyByteSpan(in this); -} + public ReadOnlySpan Str => SpanHelpers.AsReadOnlyByteSpan(in _value); +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/Storage/StorageDeviceHandle.cs b/src/LibHac/FsSrv/Storage/StorageDeviceHandle.cs index 1210d9ff..bde985c7 100644 --- a/src/LibHac/FsSrv/Storage/StorageDeviceHandle.cs +++ b/src/LibHac/FsSrv/Storage/StorageDeviceHandle.cs @@ -1,18 +1,19 @@ using System; -using System.Runtime.InteropServices; +using LibHac.Common.FixedArrays; namespace LibHac.FsSrv.Storage; -[StructLayout(LayoutKind.Sequential, Size = 0x10)] public readonly struct StorageDeviceHandle : IEquatable { public readonly uint Value; public readonly StorageDevicePortId PortId; + public readonly Array11 Reserved; public StorageDeviceHandle(uint value, StorageDevicePortId portId) { Value = value; PortId = portId; + Reserved = default; } public override bool Equals(object obj) => obj is StorageDeviceHandle other && Equals(other); @@ -22,4 +23,4 @@ public readonly struct StorageDeviceHandle : IEquatable public static bool operator !=(StorageDeviceHandle left, StorageDeviceHandle right) => !(left == right); public override int GetHashCode() => HashCode.Combine(Value, (int)PortId); -} +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs index 057d9818..bbef6c80 100644 --- a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs +++ b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs @@ -1,5 +1,6 @@ using System.Runtime.CompilerServices; using LibHac.Fs; +using LibHac.Fs.Impl; using Xunit; using static LibHac.Tests.Common.Layout; @@ -289,4 +290,68 @@ public class TypeLayoutTests Assert.Equal(0x00, GetOffset(in s, in s.Value)); } + + [Fact] + public static void RsaEncryptedKey_Layout() + { + var s = new RsaEncryptedKey(); + + Assert.Equal(0x100, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Value)); + } + + [Fact] + public static void AesKey_Layout() + { + var s = new AesKey(); + + Assert.Equal(0x10, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Value)); + } + + [Fact] + public static void InitialDataAad_Layout() + { + var s = new InitialDataAad(); + + Assert.Equal(0x20, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Value)); + } + + [Fact] + public static void KeySeed_Layout() + { + var s = new KeySeed(); + + Assert.Equal(0x10, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Value)); + } + + [Fact] + public static void InitialDataMac_Layout() + { + var s = new InitialDataMac(); + + Assert.Equal(0x10, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Value)); + } + + [Fact] + public static void ImportReportInfo_Layout() + { + var s = new ImportReportInfo(); + + Assert.Equal(0x20, Unsafe.SizeOf()); + + Assert.Equal(0, GetOffset(in s, in s.DiffChunkCount)); + Assert.Equal(1, GetOffset(in s, in s.DoubleDivisionDiffChunkCount)); + Assert.Equal(2, GetOffset(in s, in s.HalfDivisionDiffChunkCount)); + Assert.Equal(3, GetOffset(in s, in s.CompressionRate)); + Assert.Equal(4, GetOffset(in s, in s.Reserved)); + } } \ No newline at end of file diff --git a/tests/LibHac.Tests/FsSrv/TypeLayoutTests.cs b/tests/LibHac.Tests/FsSrv/TypeLayoutTests.cs new file mode 100644 index 00000000..ed20395c --- /dev/null +++ b/tests/LibHac.Tests/FsSrv/TypeLayoutTests.cs @@ -0,0 +1,91 @@ +using System.Runtime.CompilerServices; +using LibHac.FsSrv; +using LibHac.FsSrv.Impl; +using LibHac.FsSrv.Sf; +using LibHac.FsSrv.Storage; +using Xunit; +using static LibHac.Tests.Common.Layout; + +namespace LibHac.Tests.FsSrv; + +public class TypeLayoutTests +{ + [Fact] + public static void AccessControlDescriptor_Layout() + { + var s = new AccessControlDescriptor(); + + Assert.Equal(0x2C, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Version)); + Assert.Equal(0x01, GetOffset(in s, in s.ContentOwnerIdCount)); + Assert.Equal(0x02, GetOffset(in s, in s.SaveDataOwnerIdCount)); + Assert.Equal(0x04, GetOffset(in s, in s.AccessFlags)); + Assert.Equal(0x0C, GetOffset(in s, in s.ContentOwnerIdMin)); + Assert.Equal(0x14, GetOffset(in s, in s.ContentOwnerIdMax)); + Assert.Equal(0x1C, GetOffset(in s, in s.SaveDataOwnerIdMin)); + Assert.Equal(0x24, GetOffset(in s, in s.SaveDataOwnerIdMax)); + } + + [Fact] + public static void AccessControlDataHeader_Layout() + { + var s = new AccessControlDataHeader(); + + Assert.Equal(0x1C, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Version)); + Assert.Equal(0x04, GetOffset(in s, in s.AccessFlags)); + Assert.Equal(0x0C, GetOffset(in s, in s.ContentOwnerInfoOffset)); + Assert.Equal(0x10, GetOffset(in s, in s.ContentOwnerInfoSize)); + Assert.Equal(0x14, GetOffset(in s, in s.SaveDataOwnerInfoOffset)); + Assert.Equal(0x18, GetOffset(in s, in s.SaveDataOwnerInfoSize)); + } + + [Fact] + public static void FspPath_Layout() + { + var s = new FspPath(); + + Assert.Equal(0x301, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Str[0])); + } + + [Fact] + public static void Path_Layout() + { + var s = new Path(); + + Assert.Equal(0x301, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Str[0])); + } + + [Fact] + public static void StorageDeviceHandle_Layout() + { + var s = new StorageDeviceHandle(); + + Assert.Equal(0x10, Unsafe.SizeOf()); + + Assert.Equal(0x0, GetOffset(in s, in s.Value)); + Assert.Equal(0x4, GetOffset(in s, in s.PortId)); + Assert.Equal(0x5, GetOffset(in s, in s.Reserved)); + } + + [Fact] + public static void SaveDataIndexerValue_Layout() + { + var s = new SaveDataIndexerValue(); + + Assert.Equal(0x40, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.SaveDataId)); + Assert.Equal(0x08, GetOffset(in s, in s.Size)); + Assert.Equal(0x10, GetOffset(in s, in s.Field10)); + Assert.Equal(0x18, GetOffset(in s, in s.SpaceId)); + Assert.Equal(0x19, GetOffset(in s, in s.State)); + Assert.Equal(0x1A, GetOffset(in s, in s.Reserved)); + } +} \ No newline at end of file From b0e679d00043d347b0f560c78b8de4210255691b Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sun, 2 Jan 2022 16:52:57 -0700 Subject: [PATCH 28/34] Update struct layout of Ns structs --- src/LibHac/Common/FixedArrays/Array1024.cs | 31 ++ src/LibHac/Common/FixedArrays/Array2048.cs | 31 ++ src/LibHac/Common/FixedArrays/Array3000.cs | 34 ++ src/LibHac/Common/FixedArrays/Array37.cs | 31 ++ src/LibHac/Common/FixedArrays/Array65.cs | 31 ++ src/LibHac/Common/FixedArrays/Array768.cs | 31 ++ src/LibHac/Common/PaddingStructs.cs | 51 --- .../Fs/ApplicationSaveDataManagement.cs | 21 +- src/LibHac/Fs/Shim/Host.cs | 12 +- src/LibHac/FsSrv/Impl/LocationResolverSet.cs | 12 +- src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs | 2 +- src/LibHac/FsSystem/FsPath.cs | 42 -- src/LibHac/FsSystem/Hash.cs | 14 +- .../FsSystem/PartitionFileSystemCore.cs | 2 +- .../Kernel/InitialProcessBinaryReader.cs | 5 +- src/LibHac/Kernel/KipHeader.cs | 64 +-- src/LibHac/Lr/Path.cs | 19 +- src/LibHac/Ns/ApplicationControlProperty.cs | 428 +++++++++++------- src/LibHac/Tools/Fs/SwitchFs.cs | 2 +- .../LibHac.Tests/FsSystem/TypeLayoutTests.cs | 19 + tests/LibHac.Tests/Kernel/TypeLayoutTests.cs | 69 +++ tests/LibHac.Tests/Lr/TypeLayoutTests.cs | 19 + tests/LibHac.Tests/Ns/TypeLayoutTests.cs | 126 ++++++ 23 files changed, 759 insertions(+), 337 deletions(-) create mode 100644 src/LibHac/Common/FixedArrays/Array1024.cs create mode 100644 src/LibHac/Common/FixedArrays/Array2048.cs create mode 100644 src/LibHac/Common/FixedArrays/Array3000.cs create mode 100644 src/LibHac/Common/FixedArrays/Array37.cs create mode 100644 src/LibHac/Common/FixedArrays/Array65.cs create mode 100644 src/LibHac/Common/FixedArrays/Array768.cs delete mode 100644 src/LibHac/Common/PaddingStructs.cs delete mode 100644 src/LibHac/FsSystem/FsPath.cs create mode 100644 tests/LibHac.Tests/FsSystem/TypeLayoutTests.cs create mode 100644 tests/LibHac.Tests/Kernel/TypeLayoutTests.cs create mode 100644 tests/LibHac.Tests/Lr/TypeLayoutTests.cs create mode 100644 tests/LibHac.Tests/Ns/TypeLayoutTests.cs diff --git a/src/LibHac/Common/FixedArrays/Array1024.cs b/src/LibHac/Common/FixedArrays/Array1024.cs new file mode 100644 index 00000000..a1ef6dab --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array1024.cs @@ -0,0 +1,31 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array1024 +{ + public const int Length = 1024; + + private Array512 _0; + private Array512 _512; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array1024 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array2048.cs b/src/LibHac/Common/FixedArrays/Array2048.cs new file mode 100644 index 00000000..bfe46d18 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array2048.cs @@ -0,0 +1,31 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array2048 +{ + public const int Length = 2048; + + private Array1024 _0; + private Array1024 _1024; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array2048 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array3000.cs b/src/LibHac/Common/FixedArrays/Array3000.cs new file mode 100644 index 00000000..bc1fd263 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array3000.cs @@ -0,0 +1,34 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array3000 +{ + public const int Length = 3000; + + private Array2048 _0; + private Array512 _2048; + private Array256 _2560; + private Array128 _2816; + private Array56 _2944; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array3000 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array37.cs b/src/LibHac/Common/FixedArrays/Array37.cs new file mode 100644 index 00000000..4006cfb4 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array37.cs @@ -0,0 +1,31 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array37 +{ + public const int Length = 37; + + private Array32 _0; + private Array5 _32; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array37 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array65.cs b/src/LibHac/Common/FixedArrays/Array65.cs new file mode 100644 index 00000000..08882816 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array65.cs @@ -0,0 +1,31 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array65 +{ + public const int Length = 65; + + private Array64 _0; + private T _64; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array65 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array768.cs b/src/LibHac/Common/FixedArrays/Array768.cs new file mode 100644 index 00000000..1fb566c2 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array768.cs @@ -0,0 +1,31 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array768 +{ + public const int Length = 768; + + private Array512 _0; + private Array256 _512; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array768 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/PaddingStructs.cs b/src/LibHac/Common/PaddingStructs.cs deleted file mode 100644 index d50c6435..00000000 --- a/src/LibHac/Common/PaddingStructs.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace LibHac.Common; - -// In order for the Visual Studio debugger to accurately display a struct, every offset -// in the struct that is used for the debugger display must be part of a field. -// These padding structs make it easier to accomplish that. -[StructLayout(LayoutKind.Sequential, Size = 0x10)] -internal struct Padding10 -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding00; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding08; -} - -[StructLayout(LayoutKind.Sequential, Size = 0x20)] -internal struct Padding20 -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding00; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding08; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding10; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding18; -} - -[StructLayout(LayoutKind.Sequential, Size = 0x40)] -internal struct Padding40 -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding20 Padding00; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding20 Padding20; -} - -[StructLayout(LayoutKind.Sequential, Size = 0x80)] -internal struct Padding80 -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding40 Padding00; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding40 Padding40; -} - -[StructLayout(LayoutKind.Sequential, Size = 0x100)] -internal struct Padding100 -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding00; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding80; -} - -[StructLayout(LayoutKind.Sequential, Size = 0x200)] -internal struct Padding200 -{ - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100; -} diff --git a/src/LibHac/Fs/ApplicationSaveDataManagement.cs b/src/LibHac/Fs/ApplicationSaveDataManagement.cs index 84c67d03..2e13c0d0 100644 --- a/src/LibHac/Fs/ApplicationSaveDataManagement.cs +++ b/src/LibHac/Fs/ApplicationSaveDataManagement.cs @@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis; using LibHac.Account; using LibHac.Common; using LibHac.Fs.Shim; -using LibHac.Ncm; using LibHac.Ns; using LibHac.Util; @@ -18,7 +17,7 @@ public static class ApplicationSaveDataManagement long requiredSizeSum = 0; // Create local variable for use in closures - ProgramId saveDataOwnerId = nacp.SaveDataOwnerId; + ulong saveDataOwnerId = nacp.SaveDataOwnerId; // Ensure the user account save exists if (uid != Uid.Zero && nacp.UserAccountSaveDataSize > 0) @@ -31,7 +30,7 @@ public static class ApplicationSaveDataManagement Result CreateAccountSaveFunc() { UserId userId = ConvertAccountUidToFsUserId(uidLocal); - return fs.CreateSaveData(applicationId, userId, saveDataOwnerId.Value, accountSaveDataSize, + return fs.CreateSaveData(applicationId, userId, saveDataOwnerId, accountSaveDataSize, accountSaveJournalSize, SaveDataFlags.None); } @@ -53,8 +52,8 @@ public static class ApplicationSaveDataManagement long deviceSaveDataSize = nacp.DeviceSaveDataSize; long deviceSaveJournalSize = nacp.DeviceSaveDataJournalSize; - Result CreateDeviceSaveFunc() => fs.CreateDeviceSaveData(applicationId, saveDataOwnerId.Value, - deviceSaveDataSize, deviceSaveJournalSize, 0); + Result CreateDeviceSaveFunc() => fs.CreateDeviceSaveData(applicationId, saveDataOwnerId, deviceSaveDataSize, + deviceSaveJournalSize, 0); var filter = new SaveDataFilter(); filter.SetProgramId(applicationId); @@ -108,7 +107,7 @@ public static class ApplicationSaveDataManagement } else { - Result createRc = fs.CreateTemporaryStorage(applicationId, nacp.SaveDataOwnerId.Value, + Result createRc = fs.CreateTemporaryStorage(applicationId, nacp.SaveDataOwnerId, nacp.TemporaryStorageSize, 0); if (createRc.IsFailure()) @@ -311,7 +310,7 @@ public static class ApplicationSaveDataManagement public static Result EnsureApplicationCacheStorage(this FileSystemClient fs, out long requiredSize, Ncm.ApplicationId applicationId, ref ApplicationControlProperty nacp) { - return EnsureApplicationCacheStorageImpl(fs, out requiredSize, out _, applicationId, nacp.SaveDataOwnerId.Value, + return EnsureApplicationCacheStorageImpl(fs, out requiredSize, out _, applicationId, nacp.SaveDataOwnerId, 0, nacp.CacheStorageSize, nacp.CacheStorageJournalSize, true); } @@ -324,7 +323,7 @@ public static class ApplicationSaveDataManagement return Result.Success; return EnsureApplicationCacheStorageImpl(fs, out requiredSize, out target, applicationId, - nacp.SaveDataOwnerId.Value, 0, nacp.CacheStorageSize, nacp.CacheStorageJournalSize, true); + nacp.SaveDataOwnerId, 0, nacp.CacheStorageSize, nacp.CacheStorageJournalSize, true); } @@ -334,14 +333,14 @@ public static class ApplicationSaveDataManagement { UnsafeHelpers.SkipParamInit(out requiredSize, out target); - if (index > nacp.CacheStorageMaxIndex) + if (index > nacp.CacheStorageIndexMax) return ResultFs.CacheStorageIndexTooLarge.Log(); - if (dataSize + journalSize > nacp.CacheStorageMaxSizeAndMaxJournalSize) + if (dataSize + journalSize > nacp.CacheStorageDataAndJournalSizeMax) return ResultFs.CacheStorageSizeTooLarge.Log(); Result rc = fs.EnsureApplicationCacheStorage(out requiredSize, out target, applicationId, - nacp.SaveDataOwnerId.Value, index, dataSize, journalSize, false); + nacp.SaveDataOwnerId, index, dataSize, journalSize, false); fs.Impl.AbortIfNeeded(rc); return rc; diff --git a/src/LibHac/Fs/Shim/Host.cs b/src/LibHac/Fs/Shim/Host.cs index fabebedd..fe0d78b5 100644 --- a/src/LibHac/Fs/Shim/Host.cs +++ b/src/LibHac/Fs/Shim/Host.cs @@ -1,11 +1,11 @@ using System; using System.Runtime.CompilerServices; using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Diag; using LibHac.Fs.Fsa; using LibHac.Fs.Impl; using LibHac.FsSrv.Sf; -using LibHac.FsSystem; using LibHac.Os; using LibHac.Util; using static LibHac.Fs.StringTraits; @@ -65,11 +65,11 @@ public static class Host private class HostCommonMountNameGenerator : ICommonMountNameGenerator { - private FsPath _path; + private Array769 _path; public HostCommonMountNameGenerator(U8Span path) { - StringUtils.Strlcpy(_path.Str, path, FsPath.MaxLength + 1); + StringUtils.Strlcpy(_path.Items, path, PathTool.EntryNameLengthMax + 1); } public void Dispose() { } @@ -77,13 +77,13 @@ public static class Host public Result GenerateCommonMountName(Span nameBuffer) { int requiredNameBufferSize = - StringUtils.GetLength(_path.Str, FsPath.MaxLength + 1) + HostRootFileSystemPathLength; + StringUtils.GetLength(_path, PathTool.EntryNameLengthMax + 1) + HostRootFileSystemPathLength; if (nameBuffer.Length < requiredNameBufferSize) return ResultFs.TooLongPath.Log(); var sb = new U8StringBuilder(nameBuffer); - sb.Append(HostRootFileSystemPath).Append(_path.Str); + sb.Append(HostRootFileSystemPath).Append(_path); Assert.SdkEqual(sb.Length, requiredNameBufferSize - 1); @@ -533,4 +533,4 @@ public static class Host fs.Impl.LogResultErrorMessage(rc); Abort.DoAbortUnless(rc.IsSuccess()); } -} +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/Impl/LocationResolverSet.cs b/src/LibHac/FsSrv/Impl/LocationResolverSet.cs index ca8b88f2..bf7ac043 100644 --- a/src/LibHac/FsSrv/Impl/LocationResolverSet.cs +++ b/src/LibHac/FsSrv/Impl/LocationResolverSet.cs @@ -79,10 +79,10 @@ internal class LocationResolverSet : IDisposable var pathFlags = new PathFlags(); pathFlags.AllowMountName(); - if (Utility.IsHostFsMountName(lrPath.Str)) + if (Utility.IsHostFsMountName(lrPath.Value)) pathFlags.AllowWindowsPath(); - Result rc = outPath.InitializeWithReplaceUnc(lrPath.Str); + Result rc = outPath.InitializeWithReplaceUnc(lrPath.Value); if (rc.IsFailure()) return rc; rc = outPath.Normalize(pathFlags); @@ -170,7 +170,7 @@ internal class LocationResolverSet : IDisposable rc = resolver.ResolveApplicationHtmlDocumentPath(out Lr.Path path, applicationId); if (rc.IsFailure()) return rc; - isDirectory = PathUtility.IsDirectoryPath(path.Str); + isDirectory = PathUtility.IsDirectoryPath(path.Value); return SetUpFsPath(ref outPath, in path); } @@ -185,7 +185,7 @@ internal class LocationResolverSet : IDisposable rc = resolver.ResolveProgramPath(out Lr.Path path, programId); if (rc.IsFailure()) return rc; - isDirectory = PathUtility.IsDirectoryPath(path.Str); + isDirectory = PathUtility.IsDirectoryPath(path.Value); return SetUpFsPath(ref outPath, in path); } @@ -200,7 +200,7 @@ internal class LocationResolverSet : IDisposable rc = resolver.ResolveProgramPathForDebug(out Lr.Path path, programId); if (rc.IsFailure()) return rc; - isDirectory = PathUtility.IsDirectoryPath(path.Str); + isDirectory = PathUtility.IsDirectoryPath(path.Value); return SetUpFsPath(ref outPath, in path); } @@ -291,4 +291,4 @@ internal class LocationResolverSet : IDisposable _ => -1 }; } -} +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs b/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs index 3a694bf7..85d4c7e8 100644 --- a/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs +++ b/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs @@ -474,7 +474,7 @@ public class NcaFileSystemServiceImpl return ResultFs.PathNotFound.Log(); } - if (StringUtils.GetLength(path, FsPath.MaxLength) == 0) + if (StringUtils.GetLength(path, PathTool.EntryNameLengthMax) == 0) { shouldContinue = false; } diff --git a/src/LibHac/FsSystem/FsPath.cs b/src/LibHac/FsSystem/FsPath.cs deleted file mode 100644 index 8bc24c03..00000000 --- a/src/LibHac/FsSystem/FsPath.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Util; - -namespace LibHac.FsSystem; - -[StructLayout(LayoutKind.Sequential, Size = MaxLength + 1)] -public struct FsPath -{ - internal const int MaxLength = 0x300; - -#if DEBUG - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding200; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly byte Padding300; -#endif - - public Span Str => SpanHelpers.AsByteSpan(ref this); - - public static Result FromSpan(out FsPath fsPath, ReadOnlySpan path) - { - UnsafeHelpers.SkipParamInit(out fsPath); - - // Ensure null terminator even if the creation fails for safety - fsPath.Str[MaxLength] = 0; - - var sb = new U8StringBuilder(fsPath.Str); - bool overflowed = sb.Append(path).Overflowed; - - return overflowed ? ResultFs.TooLongPath.Log() : Result.Success; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator U8Span(in FsPath value) => new U8Span(SpanHelpers.AsReadOnlyByteSpan(in value)); - - public override string ToString() => StringUtils.Utf8ZToString(Str); -} \ No newline at end of file diff --git a/src/LibHac/FsSystem/Hash.cs b/src/LibHac/FsSystem/Hash.cs index 2cd0a907..3d9a2129 100644 --- a/src/LibHac/FsSystem/Hash.cs +++ b/src/LibHac/FsSystem/Hash.cs @@ -1,16 +1,8 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using LibHac.Common; +using LibHac.Common.FixedArrays; namespace LibHac.FsSystem; -[StructLayout(LayoutKind.Sequential, Size = 0x20)] public struct Hash { - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1; - - public readonly ReadOnlySpan Bytes => SpanHelpers.AsReadOnlyByteSpan(in this); - public Span BytesMutable => SpanHelpers.AsByteSpan(ref this); -} + public Array32 Value; +} \ No newline at end of file diff --git a/src/LibHac/FsSystem/PartitionFileSystemCore.cs b/src/LibHac/FsSystem/PartitionFileSystemCore.cs index 0ebac724..fe6aaa23 100644 --- a/src/LibHac/FsSystem/PartitionFileSystemCore.cs +++ b/src/LibHac/FsSystem/PartitionFileSystemCore.cs @@ -359,7 +359,7 @@ public class PartitionFileSystemCore : IFileSystem where T : unmanaged, IPart U8Span name = ParentFs._metaData.GetName(CurrentIndex); StringUtils.Copy(entryBuffer[i].Name.Items, name); - entryBuffer[i].Name[FsPath.MaxLength] = 0; + entryBuffer[i].Name[PathTool.EntryNameLengthMax] = 0; CurrentIndex++; } diff --git a/src/LibHac/Kernel/InitialProcessBinaryReader.cs b/src/LibHac/Kernel/InitialProcessBinaryReader.cs index c015c89b..f677d1b2 100644 --- a/src/LibHac/Kernel/InitialProcessBinaryReader.cs +++ b/src/LibHac/Kernel/InitialProcessBinaryReader.cs @@ -1,6 +1,5 @@ using System; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Diag; using LibHac.Fs; @@ -96,8 +95,6 @@ public class InitialProcessBinaryReader : IDisposable return Result.Success; } - - [StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct IniHeader { public uint Magic; @@ -105,4 +102,4 @@ public class InitialProcessBinaryReader : IDisposable public int ProcessCount; public uint Reserved; } -} +} \ No newline at end of file diff --git a/src/LibHac/Kernel/KipHeader.cs b/src/LibHac/Kernel/KipHeader.cs index 649e7cb3..a2a413b9 100644 --- a/src/LibHac/Kernel/KipHeader.cs +++ b/src/LibHac/Kernel/KipHeader.cs @@ -2,56 +2,57 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Common.FixedArrays; namespace LibHac.Kernel; -[StructLayout(LayoutKind.Explicit, Size = 0x100)] +[StructLayout(LayoutKind.Sequential)] public struct KipHeader { - public const uint Kip1Magic = 0x3150494B; // KIP1 - public const int NameSize = 12; - public const int SegmentCount = 6; + public static readonly uint Kip1Magic = 0x3150494B; // KIP1 + public static readonly int SegmentCount = 6; - [FieldOffset(0x00)] public uint Magic; + public uint Magic; - [FieldOffset(0x04)] private byte _name; + public Array12 Name; - [FieldOffset(0x10)] public ulong ProgramId; - [FieldOffset(0x18)] public int Version; + public ulong ProgramId; + public int Version; - [FieldOffset(0x1C)] public byte Priority; - [FieldOffset(0x1D)] public byte IdealCoreId; - [FieldOffset(0x1F)] public Flag Flags; + public byte Priority; + public byte IdealCoreId; + private byte _reserved1E; + public Flag Flags; - [FieldOffset(0x20)] public int TextMemoryOffset; - [FieldOffset(0x24)] public int TextSize; - [FieldOffset(0x28)] public int TextFileSize; + public int TextMemoryOffset; + public int TextSize; + public int TextFileSize; - [FieldOffset(0x2C)] public int AffinityMask; + public int AffinityMask; - [FieldOffset(0x30)] public int RoMemoryOffset; - [FieldOffset(0x34)] public int RoSize; - [FieldOffset(0x38)] public int RoFileSize; + public int RoMemoryOffset; + public int RoSize; + public int RoFileSize; - [FieldOffset(0x3C)] public int StackSize; + public int StackSize; - [FieldOffset(0x40)] public int DataMemoryOffset; - [FieldOffset(0x44)] public int DataSize; - [FieldOffset(0x48)] public int DataFileSize; + public int DataMemoryOffset; + public int DataSize; + public int DataFileSize; + private byte _reserved4C; - [FieldOffset(0x50)] public int BssMemoryOffset; - [FieldOffset(0x54)] public int BssSize; - [FieldOffset(0x58)] public int BssFileSize; + public int BssMemoryOffset; + public int BssSize; + public int BssFileSize; + private byte _reserved5C; - [FieldOffset(0x80)] private uint _capabilities; + private Array2 _unusedSegmentHeaders; - public Span Name => SpanHelpers.CreateSpan(ref _name, NameSize); + public Array32 Capabilities; public Span Segments => SpanHelpers.CreateSpan(ref Unsafe.As(ref TextMemoryOffset), SegmentCount); - public Span Capabilities => SpanHelpers.CreateSpan(ref _capabilities, 0x80 / sizeof(uint)); - public bool IsValid => Magic == Kip1Magic; [Flags] @@ -65,11 +66,12 @@ public struct KipHeader UseSecureMemory = 1 << 5 } - [StructLayout(LayoutKind.Sequential, Size = 0x10)] + [StructLayout(LayoutKind.Sequential)] public struct SegmentHeader { public int MemoryOffset; public int Size; public int FileSize; + private int _unused; } -} +} \ No newline at end of file diff --git a/src/LibHac/Lr/Path.cs b/src/LibHac/Lr/Path.cs index 444b6056..7b3ea32d 100644 --- a/src/LibHac/Lr/Path.cs +++ b/src/LibHac/Lr/Path.cs @@ -1,24 +1,13 @@ -using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; using LibHac.Common; -using LibHac.Fs; +using LibHac.Common.FixedArrays; using LibHac.Util; namespace LibHac.Lr; -[StructLayout(LayoutKind.Sequential, Size = PathTool.EntryNameLengthMax)] public struct Path { -#if DEBUG - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding200; -#endif - - public readonly ReadOnlySpan Str => SpanHelpers.AsReadOnlyByteSpan(in this); - public Span StrMutable => SpanHelpers.AsByteSpan(ref this); + public Array768 Value; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void InitEmpty(out Path path) @@ -30,5 +19,5 @@ public struct Path [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator U8Span(in Path value) => new U8Span(SpanHelpers.AsReadOnlyByteSpan(in value)); - public readonly override string ToString() => StringUtils.Utf8ZToString(Str); + public readonly override string ToString() => StringUtils.Utf8ZToString(Value); } \ No newline at end of file diff --git a/src/LibHac/Ns/ApplicationControlProperty.cs b/src/LibHac/Ns/ApplicationControlProperty.cs index a0bc6eb7..4d856046 100644 --- a/src/LibHac/Ns/ApplicationControlProperty.cs +++ b/src/LibHac/Ns/ApplicationControlProperty.cs @@ -1,188 +1,302 @@ using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +using System.Diagnostics.CodeAnalysis; using LibHac.Common; -using LibHac.Ncm; +using LibHac.Common.FixedArrays; namespace LibHac.Ns; -[StructLayout(LayoutKind.Explicit, Size = 0x4000)] public struct ApplicationControlProperty { - private const int TitleCount = 0x10; - private const int IsbnSize = 0x25; - private const int RatingAgeCount = 0x20; - private const int DisplayVersionSize = 0x10; - private const int ApplicationErrorCodeCategorySize = 8; - private const int LocalCommunicationIdCount = 8; - private const int Reserved30F3Size = 3; - private const int BcatPassphraseSize = 0x41; - private const int ReservedForUserAccountSaveDataOperationSize = 6; - private const int PlayLogQueryableApplicationIdCount = 0x10; - private const int ReceivableDataConfigurationCount = 0x10; + public Array16 Title; + public Array37 Isbn; + public StartupUserAccountValue StartupUserAccount; + public UserAccountSwitchLockValue UserAccountSwitchLock; + public AddOnContentRegistrationTypeValue AddOnContentRegistrationType; + public AttributeFlagValue AttributeFlag; + public uint SupportedLanguageFlag; + public ParentalControlFlagValue ParentalControlFlag; + public ScreenshotValue Screenshot; + public VideoCaptureValue VideoCapture; + public DataLossConfirmationValue DataLossConfirmation; + public PlayLogPolicyValue PlayLogPolicy; + public ulong PresenceGroupId; + public Array32 RatingAge; + public Array16 DisplayVersion; + public ulong AddOnContentBaseId; + public ulong SaveDataOwnerId; + public long UserAccountSaveDataSize; + public long UserAccountSaveDataJournalSize; + public long DeviceSaveDataSize; + public long DeviceSaveDataJournalSize; + public long BcatDeliveryCacheStorageSize; + public Array8 ApplicationErrorCodeCategory; + public Array8 LocalCommunicationId; + public LogoTypeValue LogoType; + public LogoHandlingValue LogoHandling; + public RuntimeAddOnContentInstallValue RuntimeAddOnContentInstall; + public RuntimeParameterDeliveryValue RuntimeParameterDelivery; + public Array2 Reserved30F4; + public CrashReportValue CrashReport; + public HdcpValue Hdcp; + public ulong SeedForPseudoDeviceId; + public Array65 BcatPassphrase; + public StartupUserAccountOptionFlagValue StartupUserAccountOption; + public Array6 ReservedForUserAccountSaveDataOperation; + public long UserAccountSaveDataSizeMax; + public long UserAccountSaveDataJournalSizeMax; + public long DeviceSaveDataSizeMax; + public long DeviceSaveDataJournalSizeMax; + public long TemporaryStorageSize; + public long CacheStorageSize; + public long CacheStorageJournalSize; + public long CacheStorageDataAndJournalSizeMax; + public ushort CacheStorageIndexMax; + public byte Reserved318A; + public byte RuntimeUpgrade; + public uint SupportingLimitedLicenses; + public Array16 PlayLogQueryableApplicationId; + public PlayLogQueryCapabilityValue PlayLogQueryCapability; + public RepairFlagValue RepairFlag; + public byte ProgramIndex; + public RequiredNetworkServiceLicenseOnLaunchValue RequiredNetworkServiceLicenseOnLaunchFlag; + public Array4 Reserved3214; + public ApplicationNeighborDetectionClientConfiguration NeighborDetectionClientConfiguration; + public ApplicationJitConfiguration JitConfiguration; + public RequiredAddOnContentsSetBinaryDescriptor RequiredAddOnContentsSetBinaryDescriptors; + public PlayReportPermissionValue PlayReportPermission; + public CrashScreenshotForProdValue CrashScreenshotForProd; + public CrashScreenshotForDevValue CrashScreenshotForDev; + public byte ContentsAvailabilityTransitionPolicy; + public Array4 Reserved3404; + public AccessibleLaunchRequiredVersionValue AccessibleLaunchRequiredVersion; + public Array3000 Reserved3448; - [FieldOffset(0x0000)] private byte _titles; - [FieldOffset(0x3000)] private byte _isbn; - [FieldOffset(0x3025)] public StartupUserAccount StartupUserAccount; - [FieldOffset(0x3026)] public byte UserAccountSwitchLock; - [FieldOffset(0x3027)] public byte AddOnContentRegistrationType; - [FieldOffset(0x3028)] public ApplicationAttribute ApplicationAttribute; - [FieldOffset(0x302C)] public uint SupportedLanguages; - [FieldOffset(0x3030)] public ParentalControlFlagValue ParentalControl; - [FieldOffset(0x3034)] public ScreenshotValue Screenshot; - [FieldOffset(0x3035)] public VideoCaptureValue VideoCaptureMode; - [FieldOffset(0x3036)] public byte DataLossConfirmation; - [FieldOffset(0x3037)] public byte PlayLogPolicy; - [FieldOffset(0x3038)] public ulong PresenceGroupId; - [FieldOffset(0x3040)] private sbyte _ratingAge; - [FieldOffset(0x3060)] private byte _displayVersion; - [FieldOffset(0x3070)] public ulong AddOnContentBaseId; - [FieldOffset(0x3078)] public ProgramId SaveDataOwnerId; - [FieldOffset(0x3080)] public long UserAccountSaveDataSize; - [FieldOffset(0x3088)] public long UserAccountSaveDataJournalSize; - [FieldOffset(0x3090)] public long DeviceSaveDataSize; - [FieldOffset(0x3098)] public long DeviceSaveDataJournalSize; - [FieldOffset(0x30A0)] public long BcatDeliveryCacheStorageSize; - [FieldOffset(0x30A8)] private byte _applicationErrorCodeCategory; - [FieldOffset(0x30B0)] private ulong _localCommunicationIds; - [FieldOffset(0x30F0)] public LogoType LogoType; - [FieldOffset(0x30F1)] public LogoHandling LogoHandling; - [FieldOffset(0x30F2)] public byte RuntimeAddOnContentInstall; - [FieldOffset(0x30F3)] public byte _reserved30F3; - [FieldOffset(0x30F6)] public byte CrashReport; - [FieldOffset(0x30F7)] public byte Hdcp; - [FieldOffset(0x30F8)] public ulong SeedForPseudoDeviceId; - [FieldOffset(0x3100)] private byte _bcatPassphrase; - [FieldOffset(0x3141)] public byte StartupUserAccountOption; - [FieldOffset(0x3142)] private byte _reservedForUserAccountSaveDataOperation; - [FieldOffset(0x3148)] public long UserAccountSaveDataMaxSize; - [FieldOffset(0x3150)] public long UserAccountSaveDataMaxJournalSize; - [FieldOffset(0x3158)] public long DeviceSaveDataMaxSize; - [FieldOffset(0x3160)] public long DeviceSaveDataMaxJournalSize; - [FieldOffset(0x3168)] public long TemporaryStorageSize; - [FieldOffset(0x3170)] public long CacheStorageSize; - [FieldOffset(0x3178)] public long CacheStorageJournalSize; - [FieldOffset(0x3180)] public long CacheStorageMaxSizeAndMaxJournalSize; - [FieldOffset(0x3188)] public long CacheStorageMaxIndex; - [FieldOffset(0x3190)] private ulong _playLogQueryableApplicationId; - [FieldOffset(0x3210)] public PlayLogQueryCapability PlayLogQueryCapability; - [FieldOffset(0x3211)] public byte RepairFlag; - [FieldOffset(0x3212)] public byte ProgramIndex; - [FieldOffset(0x3213)] public byte RequiredNetworkServiceLicenseOnLaunchFlag; - [FieldOffset(0x3214)] public uint Reserved3214; - [FieldOffset(0x3218)] public ApplicationControlDataConfiguration SendDataConfiguration; - [FieldOffset(0x3230)] private ApplicationControlDataConfiguration _receivableDataConfigurations; - [FieldOffset(0x32B0)] public ulong JitConfigurationFlag; - [FieldOffset(0x32B8)] public long MemorySize; + public struct ApplicationTitle + { + private Array512 _name; + private Array256 _publisher; - [FieldOffset(0x3000), DebuggerBrowsable(DebuggerBrowsableState.Never)] private Padding200 _padding1; - [FieldOffset(0x3200), DebuggerBrowsable(DebuggerBrowsableState.Never)] private Padding100 _padding2; + public U8SpanMutable Name => new U8SpanMutable(_name.Items); + public U8SpanMutable Publisher => new U8SpanMutable(_publisher.Items); + } - public Span Titles => SpanHelpers.CreateSpan(ref Unsafe.As(ref _titles), TitleCount); - public U8SpanMutable Isbn => new U8SpanMutable(SpanHelpers.CreateSpan(ref _isbn, IsbnSize)); - public Span RatingAge => SpanHelpers.CreateSpan(ref _ratingAge, RatingAgeCount); - public U8SpanMutable DisplayVersion => new U8SpanMutable(SpanHelpers.CreateSpan(ref _displayVersion, DisplayVersionSize)); + public struct ApplicationNeighborDetectionClientConfiguration + { + public ApplicationNeighborDetectionGroupConfiguration SendGroupConfiguration; + public Array16 ReceivableGroupConfigurations; + } - public U8SpanMutable ApplicationErrorCodeCategory => - new U8SpanMutable(SpanHelpers.CreateSpan(ref _applicationErrorCodeCategory, - ApplicationErrorCodeCategorySize)); + public struct ApplicationNeighborDetectionGroupConfiguration + { + public ulong GroupId; + public Array16 Key; + } - public Span LocalCommunicationIds => SpanHelpers.CreateSpan(ref _localCommunicationIds, LocalCommunicationIdCount); - public Span Reserved30F3 => SpanHelpers.CreateSpan(ref _reserved30F3, Reserved30F3Size); - public U8SpanMutable BcatPassphrase => new U8SpanMutable(SpanHelpers.CreateSpan(ref _bcatPassphrase, BcatPassphraseSize)); + public struct ApplicationJitConfiguration + { + public JitConfigurationFlag Flags; + public long MemorySize; + } - public Span ReservedForUserAccountSaveDataOperation => - SpanHelpers.CreateSpan(ref _reservedForUserAccountSaveDataOperation, - ReservedForUserAccountSaveDataOperationSize); + public struct RequiredAddOnContentsSetBinaryDescriptor + { + public Array32 Descriptors; + } - public Span PlayLogQueryableApplicationId => - SpanHelpers.CreateSpan(ref _playLogQueryableApplicationId, PlayLogQueryableApplicationIdCount); + public struct AccessibleLaunchRequiredVersionValue + { + public Array8 ApplicationId; + } - public Span ReceivableDataConfigurations => - SpanHelpers.CreateSpan(ref _receivableDataConfigurations, ReceivableDataConfigurationCount); -} + public enum Language + { + AmericanEnglish = 0, + BritishEnglish = 1, + Japanese = 2, + French = 3, + German = 4, + LatinAmericanSpanish = 5, + Spanish = 6, + Italian = 7, + Dutch = 8, + CanadianFrench = 9, + Portuguese = 10, + Russian = 11, + Korean = 12, + TraditionalChinese = 13, + SimplifiedChinese = 14, + BrazilianPortuguese = 15 + } -[StructLayout(LayoutKind.Explicit, Size = 0x300)] -public struct ApplicationControlTitle -{ - private const int NameLength = 0x200; - private const int PublisherLength = 0x100; + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum Organization + { + CERO = 0, + GRACGCRB = 1, + GSRMR = 2, + ESRB = 3, + ClassInd = 4, + USK = 5, + PEGI = 6, + PEGIPortugal = 7, + PEGIBBFC = 8, + Russian = 9, + ACB = 10, + OFLC = 11, + IARCGeneric = 12 + } - [FieldOffset(0x000)] private byte _name; - [FieldOffset(0x200)] private byte _publisher; + public enum StartupUserAccountValue : byte + { + None = 0, + Required = 1, + RequiredWithNetworkServiceAccountAvailable = 2 + } - [FieldOffset(0x000), DebuggerBrowsable(DebuggerBrowsableState.Never)] - private Padding200 _padding0; + public enum UserAccountSwitchLockValue : byte + { + Disable = 0, + Enable = 1 + } - [FieldOffset(0x200), DebuggerBrowsable(DebuggerBrowsableState.Never)] - private Padding100 _padding200; + public enum AddOnContentRegistrationTypeValue : byte + { + AllOnLaunch = 0, + OnDemand = 1 + } - public U8SpanMutable Name => new U8SpanMutable(SpanHelpers.CreateSpan(ref _name, NameLength)); - public U8SpanMutable Publisher => new U8SpanMutable(SpanHelpers.CreateSpan(ref _publisher, PublisherLength)); -} + [Flags] + public enum AttributeFlagValue + { + None = 0, + Demo = 1 << 0, + RetailInteractiveDisplay = 1 << 1, + } -[StructLayout(LayoutKind.Explicit, Size = 0x18)] -public struct ApplicationControlDataConfiguration -{ - [FieldOffset(0)] public ulong Id; - [FieldOffset(8)] private byte _key; + public enum ParentalControlFlagValue + { + None = 0, + FreeCommunication = 1 + } - [FieldOffset(8), DebuggerBrowsable(DebuggerBrowsableState.Never)] - private Padding10 _keyPadding; + public enum ScreenshotValue : byte + { + Allow = 0, + Deny = 1 + } - public Span Key => SpanHelpers.CreateSpan(ref _key, 0x10); -} + public enum VideoCaptureValue : byte + { + Disable = 0, + Manual = 1, + Enable = 2 + } -public enum StartupUserAccount : byte -{ - None = 0, - Required = 1, - RequiredWithNetworkServiceAccountAvailable = 2 -} + public enum DataLossConfirmationValue : byte + { + None = 0, + Required = 1 + } -public enum LogoHandling : byte -{ - Auto = 0, - Manual = 1 -} + public enum PlayLogPolicyValue : byte + { + Open = 0, + LogOnly = 1, + None = 2, + Closed = 3, + All = 0 + } -public enum LogoType : byte -{ - LicensedByNintendo = 0, - DistributedByNintendo = 1, - Nintendo = 2 -} + public enum LogoTypeValue : byte + { + LicensedByNintendo = 0, + DistributedByNintendo = 1, + Nintendo = 2 + } -[Flags] -public enum ApplicationAttribute -{ - None = 0, - Demo = 1 -} + public enum LogoHandlingValue : byte + { + Auto = 0, + Manual = 1 + } -public enum PlayLogQueryCapability : byte -{ - None = 0, - WhiteList = 1, - All = 2 -} + public enum RuntimeAddOnContentInstallValue : byte + { + Deny = 0, + AllowAppend = 1, + AllowAppendButDontDownloadWhenUsingNetwork = 2 + } -public enum ParentalControlFlagValue -{ - None = 0, - FreeCommunication = 1 -} + public enum RuntimeParameterDeliveryValue : byte + { + Always = 0, + AlwaysIfUserStateMatched = 1, + OnRestart = 2 + } -public enum ScreenshotValue : byte -{ - Allow = 0, - Deny = 1 -} + public enum CrashReportValue : byte + { + Deny = 0, + Allow = 1 + } -public enum VideoCaptureValue : byte -{ - Deny = 0, - Allow = 1, - Automatic = 2 -} + public enum HdcpValue : byte + { + None = 0, + Required = 1 + } + + [Flags] + public enum StartupUserAccountOptionFlagValue : byte + { + None = 0, + IsOptional = 1 << 0 + } + + public enum PlayLogQueryCapabilityValue : byte + { + None = 0, + WhiteList = 1, + All = 2 + } + + [Flags] + public enum RepairFlagValue : byte + { + None = 0, + SuppressGameCardAccess = 1 << 0 + } + + [Flags] + public enum RequiredNetworkServiceLicenseOnLaunchValue : byte + { + None = 0, + Common = 1 << 0 + } + + [Flags] + public enum JitConfigurationFlag : ulong + { + None = 0, + Enabled = 1 << 0 + } + + [Flags] + public enum PlayReportPermissionValue : byte + { + None = 0, + TargetMarketing = 1 << 0 + } + + public enum CrashScreenshotForProdValue : byte + { + Deny = 0, + Allow = 1 + } + + public enum CrashScreenshotForDevValue : byte + { + Deny = 0, + Allow = 1 + } +} \ No newline at end of file diff --git a/src/LibHac/Tools/Fs/SwitchFs.cs b/src/LibHac/Tools/Fs/SwitchFs.cs index 0eaf2d74..5f109d6a 100644 --- a/src/LibHac/Tools/Fs/SwitchFs.cs +++ b/src/LibHac/Tools/Fs/SwitchFs.cs @@ -229,7 +229,7 @@ public class SwitchFs : IDisposable control.Get.Read(out _, 0, title.Control.ByteSpan).ThrowIfFailure(); } - foreach (ref ApplicationControlTitle desc in title.Control.Value.Titles) + foreach (ref readonly ApplicationControlProperty.ApplicationTitle desc in title.Control.Value.Title.ItemsRo) { if (!desc.Name.IsEmpty()) { diff --git a/tests/LibHac.Tests/FsSystem/TypeLayoutTests.cs b/tests/LibHac.Tests/FsSystem/TypeLayoutTests.cs new file mode 100644 index 00000000..2a31de87 --- /dev/null +++ b/tests/LibHac.Tests/FsSystem/TypeLayoutTests.cs @@ -0,0 +1,19 @@ +using System.Runtime.CompilerServices; +using LibHac.FsSystem; +using Xunit; +using static LibHac.Tests.Common.Layout; + +namespace LibHac.Tests.FsSystem; + +public class TypeLayoutTests +{ + [Fact] + public static void Hash_Layout() + { + var s = new Hash(); + + Assert.Equal(0x20, Unsafe.SizeOf()); + + Assert.Equal(0x0, GetOffset(in s, in s.Value)); + } +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Kernel/TypeLayoutTests.cs b/tests/LibHac.Tests/Kernel/TypeLayoutTests.cs new file mode 100644 index 00000000..4341fd75 --- /dev/null +++ b/tests/LibHac.Tests/Kernel/TypeLayoutTests.cs @@ -0,0 +1,69 @@ +using System.Runtime.CompilerServices; +using LibHac.Kernel; +using Xunit; +using static LibHac.Kernel.InitialProcessBinaryReader; +using static LibHac.Kernel.KipHeader; +using static LibHac.Tests.Common.Layout; + +namespace LibHac.Tests.Kernel; + +public class TypeLayoutTests +{ + [Fact] + public static void IniHeader_Layout() + { + var s = new IniHeader(); + + Assert.Equal(0x10, Unsafe.SizeOf()); + + Assert.Equal(0x0, GetOffset(in s, in s.Magic)); + Assert.Equal(0x4, GetOffset(in s, in s.Size)); + Assert.Equal(0x8, GetOffset(in s, in s.ProcessCount)); + Assert.Equal(0xC, GetOffset(in s, in s.Reserved)); + } + + [Fact] + public static void KipHeader_Layout() + { + var s = new KipHeader(); + + Assert.Equal(0x100, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Magic)); + Assert.Equal(0x04, GetOffset(in s, in s.Name)); + Assert.Equal(0x10, GetOffset(in s, in s.ProgramId)); + Assert.Equal(0x18, GetOffset(in s, in s.Version)); + Assert.Equal(0x1C, GetOffset(in s, in s.Priority)); + Assert.Equal(0x1D, GetOffset(in s, in s.IdealCoreId)); + Assert.Equal(0x1F, GetOffset(in s, in s.Flags)); + Assert.Equal(0x20, GetOffset(in s, in s.TextMemoryOffset)); + Assert.Equal(0x24, GetOffset(in s, in s.TextSize)); + Assert.Equal(0x28, GetOffset(in s, in s.TextFileSize)); + Assert.Equal(0x2C, GetOffset(in s, in s.AffinityMask)); + Assert.Equal(0x30, GetOffset(in s, in s.RoMemoryOffset)); + Assert.Equal(0x34, GetOffset(in s, in s.RoSize)); + Assert.Equal(0x38, GetOffset(in s, in s.RoFileSize)); + Assert.Equal(0x3C, GetOffset(in s, in s.StackSize)); + Assert.Equal(0x40, GetOffset(in s, in s.DataMemoryOffset)); + Assert.Equal(0x44, GetOffset(in s, in s.DataSize)); + Assert.Equal(0x48, GetOffset(in s, in s.DataFileSize)); + Assert.Equal(0x50, GetOffset(in s, in s.BssMemoryOffset)); + Assert.Equal(0x54, GetOffset(in s, in s.BssSize)); + Assert.Equal(0x58, GetOffset(in s, in s.BssFileSize)); + Assert.Equal(0x80, GetOffset(in s, in s.Capabilities)); + + Assert.Equal(0x20, GetOffset(in s, in s.Segments[0])); + } + + [Fact] + public static void KipSegmentHeader_Layout() + { + var s = new SegmentHeader(); + + Assert.Equal(0x10, Unsafe.SizeOf()); + + Assert.Equal(0x0, GetOffset(in s, in s.MemoryOffset)); + Assert.Equal(0x4, GetOffset(in s, in s.Size)); + Assert.Equal(0x8, GetOffset(in s, in s.FileSize)); + } +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Lr/TypeLayoutTests.cs b/tests/LibHac.Tests/Lr/TypeLayoutTests.cs new file mode 100644 index 00000000..6b8da1b0 --- /dev/null +++ b/tests/LibHac.Tests/Lr/TypeLayoutTests.cs @@ -0,0 +1,19 @@ +using System.Runtime.CompilerServices; +using LibHac.Lr; +using Xunit; +using static LibHac.Tests.Common.Layout; + +namespace LibHac.Tests.Lr; + +public class TypeLayoutTests +{ + [Fact] + public static void IniHeader_Layout() + { + var s = new Path(); + + Assert.Equal(0x300, Unsafe.SizeOf()); + + Assert.Equal(0x0, GetOffset(in s, in s.Value)); + } +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Ns/TypeLayoutTests.cs b/tests/LibHac.Tests/Ns/TypeLayoutTests.cs new file mode 100644 index 00000000..88fc2df1 --- /dev/null +++ b/tests/LibHac.Tests/Ns/TypeLayoutTests.cs @@ -0,0 +1,126 @@ +using System.Runtime.CompilerServices; +using LibHac.Ns; +using Xunit; +using static LibHac.Ns.ApplicationControlProperty; +using static LibHac.Tests.Common.Layout; + +namespace LibHac.Tests.Ns; + +public class TypeLayoutTests +{ + [Fact] + public static void ApplicationTitle_Layout() + { + var s = new ApplicationTitle(); + + Assert.Equal(0x300, Unsafe.SizeOf()); + + Assert.Equal(0x000, GetOffset(in s, in s.Name.Value[0])); + Assert.Equal(0x200, GetOffset(in s, in s.Publisher.Value[0])); + } + + [Fact] + public static void ApplicationNeighborDetectionGroupConfiguration_Layout() + { + var s = new ApplicationNeighborDetectionGroupConfiguration(); + + Assert.Equal(0x18, Unsafe.SizeOf()); + + Assert.Equal(0x0, GetOffset(in s, in s.GroupId)); + Assert.Equal(0x8, GetOffset(in s, in s.Key)); + } + + [Fact] + public static void ApplicationNeighborDetectionClientConfiguration_Layout() + { + var s = new ApplicationNeighborDetectionClientConfiguration(); + + Assert.Equal(0x198, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.SendGroupConfiguration)); + Assert.Equal(0x18, GetOffset(in s, in s.ReceivableGroupConfigurations)); + } + + [Fact] + public static void ApplicationJitConfiguration_Layout() + { + var s = new ApplicationJitConfiguration(); + + Assert.Equal(0x10, Unsafe.SizeOf()); + + Assert.Equal(0x0, GetOffset(in s, in s.Flags)); + Assert.Equal(0x8, GetOffset(in s, in s.MemorySize)); + } + + [Fact] + public static void ApplicationControlProperty_Layout() + { + var s = new ApplicationControlProperty(); + + Assert.Equal(0x4000, Unsafe.SizeOf()); + + Assert.Equal(0x0000, GetOffset(in s, in s.Title)); + Assert.Equal(0x3000, GetOffset(in s, in s.Isbn)); + Assert.Equal(0x3025, GetOffset(in s, in s.StartupUserAccount)); + Assert.Equal(0x3026, GetOffset(in s, in s.UserAccountSwitchLock)); + Assert.Equal(0x3027, GetOffset(in s, in s.AddOnContentRegistrationType)); + Assert.Equal(0x3028, GetOffset(in s, in s.AttributeFlag)); + Assert.Equal(0x302C, GetOffset(in s, in s.SupportedLanguageFlag)); + Assert.Equal(0x3030, GetOffset(in s, in s.ParentalControlFlag)); + Assert.Equal(0x3034, GetOffset(in s, in s.Screenshot)); + Assert.Equal(0x3035, GetOffset(in s, in s.VideoCapture)); + Assert.Equal(0x3036, GetOffset(in s, in s.DataLossConfirmation)); + Assert.Equal(0x3037, GetOffset(in s, in s.PlayLogPolicy)); + Assert.Equal(0x3038, GetOffset(in s, in s.PresenceGroupId)); + Assert.Equal(0x3040, GetOffset(in s, in s.RatingAge)); + Assert.Equal(0x3060, GetOffset(in s, in s.DisplayVersion)); + Assert.Equal(0x3070, GetOffset(in s, in s.AddOnContentBaseId)); + Assert.Equal(0x3078, GetOffset(in s, in s.SaveDataOwnerId)); + Assert.Equal(0x3080, GetOffset(in s, in s.UserAccountSaveDataSize)); + Assert.Equal(0x3088, GetOffset(in s, in s.UserAccountSaveDataJournalSize)); + Assert.Equal(0x3090, GetOffset(in s, in s.DeviceSaveDataSize)); + Assert.Equal(0x3098, GetOffset(in s, in s.DeviceSaveDataJournalSize)); + Assert.Equal(0x30A0, GetOffset(in s, in s.BcatDeliveryCacheStorageSize)); + Assert.Equal(0x30A8, GetOffset(in s, in s.ApplicationErrorCodeCategory)); + Assert.Equal(0x30B0, GetOffset(in s, in s.LocalCommunicationId)); + Assert.Equal(0x30F0, GetOffset(in s, in s.LogoType)); + Assert.Equal(0x30F1, GetOffset(in s, in s.LogoHandling)); + Assert.Equal(0x30F2, GetOffset(in s, in s.RuntimeAddOnContentInstall)); + Assert.Equal(0x30F3, GetOffset(in s, in s.RuntimeParameterDelivery)); + Assert.Equal(0x30F4, GetOffset(in s, in s.Reserved30F4)); + Assert.Equal(0x30F6, GetOffset(in s, in s.CrashReport)); + Assert.Equal(0x30F7, GetOffset(in s, in s.Hdcp)); + Assert.Equal(0x30F8, GetOffset(in s, in s.SeedForPseudoDeviceId)); + Assert.Equal(0x3100, GetOffset(in s, in s.BcatPassphrase)); + Assert.Equal(0x3141, GetOffset(in s, in s.StartupUserAccountOption)); + Assert.Equal(0x3142, GetOffset(in s, in s.ReservedForUserAccountSaveDataOperation)); + Assert.Equal(0x3148, GetOffset(in s, in s.UserAccountSaveDataSizeMax)); + Assert.Equal(0x3150, GetOffset(in s, in s.UserAccountSaveDataJournalSizeMax)); + Assert.Equal(0x3158, GetOffset(in s, in s.DeviceSaveDataSizeMax)); + Assert.Equal(0x3160, GetOffset(in s, in s.DeviceSaveDataJournalSizeMax)); + Assert.Equal(0x3168, GetOffset(in s, in s.TemporaryStorageSize)); + Assert.Equal(0x3170, GetOffset(in s, in s.CacheStorageSize)); + Assert.Equal(0x3178, GetOffset(in s, in s.CacheStorageJournalSize)); + Assert.Equal(0x3180, GetOffset(in s, in s.CacheStorageDataAndJournalSizeMax)); + Assert.Equal(0x3188, GetOffset(in s, in s.CacheStorageIndexMax)); + Assert.Equal(0x318A, GetOffset(in s, in s.Reserved318A)); + Assert.Equal(0x318B, GetOffset(in s, in s.RuntimeUpgrade)); + Assert.Equal(0x318C, GetOffset(in s, in s.SupportingLimitedLicenses)); + Assert.Equal(0x3190, GetOffset(in s, in s.PlayLogQueryableApplicationId)); + Assert.Equal(0x3210, GetOffset(in s, in s.PlayLogQueryCapability)); + Assert.Equal(0x3211, GetOffset(in s, in s.RepairFlag)); + Assert.Equal(0x3212, GetOffset(in s, in s.ProgramIndex)); + Assert.Equal(0x3213, GetOffset(in s, in s.RequiredNetworkServiceLicenseOnLaunchFlag)); + Assert.Equal(0x3214, GetOffset(in s, in s.Reserved3214)); + Assert.Equal(0x3218, GetOffset(in s, in s.NeighborDetectionClientConfiguration)); + Assert.Equal(0x33B0, GetOffset(in s, in s.JitConfiguration)); + Assert.Equal(0x33C0, GetOffset(in s, in s.RequiredAddOnContentsSetBinaryDescriptors)); + Assert.Equal(0x3400, GetOffset(in s, in s.PlayReportPermission)); + Assert.Equal(0x3401, GetOffset(in s, in s.CrashScreenshotForProd)); + Assert.Equal(0x3402, GetOffset(in s, in s.CrashScreenshotForDev)); + Assert.Equal(0x3403, GetOffset(in s, in s.ContentsAvailabilityTransitionPolicy)); + Assert.Equal(0x3404, GetOffset(in s, in s.Reserved3404)); + Assert.Equal(0x3408, GetOffset(in s, in s.AccessibleLaunchRequiredVersion)); + Assert.Equal(0x3448, GetOffset(in s, in s.Reserved3448)); + } +} \ No newline at end of file From e1fd31c1ff3b259b90a38c188e78806f1615c3e1 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sun, 2 Jan 2022 22:58:11 -0700 Subject: [PATCH 29/34] Update layout of Loader and Kvdb structs --- src/LibHac/Common/FixedArrays/Array48.cs | 31 +++++ src/LibHac/Kvdb/KeyValueArchive.cs | 9 +- src/LibHac/Loader/NsoHeader.cs | 73 +++++------ src/LibHac/Loader/NsoReader.cs | 9 +- src/LibHac/Loader/Types.cs | 33 ++--- tests/LibHac.Tests/Kvdb/TypeLayoutTests.cs | 33 +++++ tests/LibHac.Tests/Loader/TypeLayoutTests.cs | 130 +++++++++++++++++++ tests/LibHac.Tests/Loader/TypeSizeTests.cs | 26 ---- 8 files changed, 251 insertions(+), 93 deletions(-) create mode 100644 src/LibHac/Common/FixedArrays/Array48.cs create mode 100644 tests/LibHac.Tests/Kvdb/TypeLayoutTests.cs create mode 100644 tests/LibHac.Tests/Loader/TypeLayoutTests.cs delete mode 100644 tests/LibHac.Tests/Loader/TypeSizeTests.cs diff --git a/src/LibHac/Common/FixedArrays/Array48.cs b/src/LibHac/Common/FixedArrays/Array48.cs new file mode 100644 index 00000000..ce3268c4 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array48.cs @@ -0,0 +1,31 @@ +#pragma warning disable CS0169, IDE0051 // Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array48 +{ + public const int Length = 48; + + private Array32 _0; + private Array16 _32; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array48 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Kvdb/KeyValueArchive.cs b/src/LibHac/Kvdb/KeyValueArchive.cs index 314a5757..b27ff680 100644 --- a/src/LibHac/Kvdb/KeyValueArchive.cs +++ b/src/LibHac/Kvdb/KeyValueArchive.cs @@ -1,15 +1,13 @@ using System; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Diag; namespace LibHac.Kvdb; -[StructLayout(LayoutKind.Sequential, Size = 0xC)] public struct KeyValueArchiveHeader { - public const uint ExpectedMagic = 0x564B4D49; // IMKV + public static readonly uint ExpectedMagic = 0x564B4D49; // IMKV public uint Magic; public int Reserved; @@ -25,10 +23,9 @@ public struct KeyValueArchiveHeader } } -[StructLayout(LayoutKind.Sequential, Size = 0xC)] internal struct KeyValueArchiveEntryHeader { - public const uint ExpectedMagic = 0x4E454D49; // IMEN + public static readonly uint ExpectedMagic = 0x4E454D49; // IMEN public uint Magic; public int KeySize; @@ -202,4 +199,4 @@ internal ref struct KeyValueArchiveBufferWriter Write(key); Write(value); } -} +} \ No newline at end of file diff --git a/src/LibHac/Loader/NsoHeader.cs b/src/LibHac/Loader/NsoHeader.cs index 259de222..4529b86b 100644 --- a/src/LibHac/Loader/NsoHeader.cs +++ b/src/LibHac/Loader/NsoHeader.cs @@ -2,65 +2,63 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Common.FixedArrays; namespace LibHac.Loader; -[StructLayout(LayoutKind.Explicit, Size = 0x100)] public struct NsoHeader { - public const int SegmentCount = 3; + public static readonly int SegmentCount = 3; - [FieldOffset(0x00)] public uint Magic; - [FieldOffset(0x04)] public uint Version; - [FieldOffset(0x08)] public uint Reserved08; - [FieldOffset(0x0C)] public Flag Flags; + public uint Magic; + public uint Version; + public uint Reserved08; + public Flag Flags; - [FieldOffset(0x10)] public uint TextFileOffset; - [FieldOffset(0x14)] public uint TextMemoryOffset; - [FieldOffset(0x18)] public uint TextSize; + public uint TextFileOffset; + public uint TextMemoryOffset; + public uint TextSize; - [FieldOffset(0x1C)] public uint ModuleNameOffset; + public uint ModuleNameOffset; - [FieldOffset(0x20)] public uint RoFileOffset; - [FieldOffset(0x24)] public uint RoMemoryOffset; - [FieldOffset(0x28)] public uint RoSize; + public uint RoFileOffset; + public uint RoMemoryOffset; + public uint RoSize; - [FieldOffset(0x2C)] public uint ModuleNameSize; + public uint ModuleNameSize; - [FieldOffset(0x30)] public uint DataFileOffset; - [FieldOffset(0x34)] public uint DataMemoryOffset; - [FieldOffset(0x38)] public uint DataSize; + public uint DataFileOffset; + public uint DataMemoryOffset; + public uint DataSize; - [FieldOffset(0x3C)] public uint BssSize; + public uint BssSize; - [FieldOffset(0x40)] public Buffer32 ModuleId; + public Array32 ModuleId; // Size of the sections in the NSO file - [FieldOffset(0x60)] public uint TextFileSize; - [FieldOffset(0x64)] public uint RoFileSize; - [FieldOffset(0x68)] public uint DataFileSize; + public uint TextFileSize; + public uint RoFileSize; + public uint DataFileSize; - [FieldOffset(0x6C)] private byte _reserved6C; + public Array28 Reserved6C; - [FieldOffset(0x88)] public uint ApiInfoOffset; - [FieldOffset(0x8C)] public uint ApiInfoSize; - [FieldOffset(0x90)] public uint DynStrOffset; - [FieldOffset(0x94)] public uint DynStrSize; - [FieldOffset(0x98)] public uint DynSymOffset; - [FieldOffset(0x9C)] public uint DynSymSize; + public uint ApiInfoOffset; + public uint ApiInfoSize; + public uint DynStrOffset; + public uint DynStrSize; + public uint DynSymOffset; + public uint DynSymSize; - [FieldOffset(0xA0)] public Buffer32 TextHash; - [FieldOffset(0xC0)] public Buffer32 RoHash; - [FieldOffset(0xE0)] public Buffer32 DataHash; + public Array32 TextHash; + public Array32 RoHash; + public Array32 DataHash; public Span Segments => SpanHelpers.CreateSpan(ref Unsafe.As(ref TextFileOffset), SegmentCount); public Span CompressedSizes => SpanHelpers.CreateSpan(ref TextFileSize, SegmentCount); - public Span SegmentHashes => SpanHelpers.CreateSpan(ref TextHash, SegmentCount); - - public Span Reserved6C => SpanHelpers.CreateSpan(ref _reserved6C, 0x1C); + public Span> SegmentHashes => SpanHelpers.CreateSpan(ref TextHash, SegmentCount); [Flags] public enum Flag @@ -73,11 +71,12 @@ public struct NsoHeader DataHash = 1 << 5 } - [StructLayout(LayoutKind.Sequential, Size = 0x10)] + [StructLayout(LayoutKind.Sequential)] public struct SegmentHeader { public uint FileOffset; public uint MemoryOffset; public uint Size; + private int _unused; } -} +} \ No newline at end of file diff --git a/src/LibHac/Loader/NsoReader.cs b/src/LibHac/Loader/NsoReader.cs index 35554ba4..4538de88 100644 --- a/src/LibHac/Loader/NsoReader.cs +++ b/src/LibHac/Loader/NsoReader.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.CompilerServices; using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.Util; @@ -56,7 +57,7 @@ public class NsoReader Header.SegmentHashes[(int)segment], isCompressed, checkHash, buffer); } - private Result ReadSegmentImpl(ref NsoHeader.SegmentHeader segment, uint fileSize, Buffer32 fileHash, + private Result ReadSegmentImpl(ref NsoHeader.SegmentHeader segment, uint fileSize, Array32 fileHash, bool isCompressed, bool checkHash, Span buffer) { // Select read size based on compression. @@ -90,10 +91,10 @@ public class NsoReader // Check hash if necessary. if (checkHash) { - var hash = new Buffer32(); - Crypto.Sha256.GenerateSha256Hash(buffer.Slice(0, (int)segment.Size), hash.Bytes); + var hash = new Array32(); + Crypto.Sha256.GenerateSha256Hash(buffer.Slice(0, (int)segment.Size), hash.Items); - if (hash.Bytes.SequenceCompareTo(fileHash.Bytes) != 0) + if (hash.ItemsRo.SequenceCompareTo(fileHash) != 0) return ResultLoader.InvalidNso.Log(); } diff --git a/src/LibHac/Loader/Types.cs b/src/LibHac/Loader/Types.cs index 123cd5ef..b8c7fd03 100644 --- a/src/LibHac/Loader/Types.cs +++ b/src/LibHac/Loader/Types.cs @@ -27,26 +27,22 @@ public struct Meta public uint Magic; public int SignatureKeyGeneration; - private Array4 _reserved08; + public Array4 Reserved08; public byte Flags; - private byte _reserved0D; + public byte Reserved0D; public byte MainThreadPriority; public byte DefaultCpuId; - private Array4 _reserved10; + public Array4 Reserved10; public uint SystemResourceSize; public uint Version; public uint MainThreadStackSize; - private Array16 _programName; - private Array16 _productCode; - private Array32 _reserved40; - private Array16 _reserved60; + public Array16 ProgramName; + public Array16 ProductCode; + public Array48 Reserved40; public int AciOffset; public int AciSize; public int AcidOffset; public int AcidSize; - - public readonly ReadOnlySpan ProgramName => _programName.ItemsRo; - public readonly ReadOnlySpan ProductCode => _productCode.ItemsRo; } public struct AciHeader @@ -54,24 +50,24 @@ public struct AciHeader public static readonly uint MagicValue = 0x30494341; // ACI0 public uint Magic; - private Array12 _reserved04; + public Array12 Reserved04; public ProgramId ProgramId; - private Array8 _reserved18; + public Array8 Reserved18; public int FsAccessControlOffset; public int FsAccessControlSize; public int ServiceAccessControlOffset; public int ServiceAccessControlSize; public int KernelCapabilityOffset; public int KernelCapabilitySize; - private Array4 _reserved38; + public Array8 Reserved38; } public struct AcidHeaderData { public static readonly uint MagicValue = 0x44494341; // ACID - private Array256 _signature; - private Array256 _modulus; + public Array256 Signature; + public Array256 Modulus; public uint Magic; public int Size; public byte Version; @@ -84,8 +80,5 @@ public struct AcidHeaderData public int ServiceAccessControlSize; public int KernelCapabilityOffset; public int KernelCapabilitySize; - private Array4 _reserved238; - - public readonly ReadOnlySpan Signature => _signature.ItemsRo; - public readonly ReadOnlySpan Modulus => _modulus.ItemsRo; -} + public Array4 Reserved238; +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Kvdb/TypeLayoutTests.cs b/tests/LibHac.Tests/Kvdb/TypeLayoutTests.cs new file mode 100644 index 00000000..9f0f57c9 --- /dev/null +++ b/tests/LibHac.Tests/Kvdb/TypeLayoutTests.cs @@ -0,0 +1,33 @@ +using System.Runtime.CompilerServices; +using LibHac.Kvdb; +using Xunit; +using static LibHac.Tests.Common.Layout; + +namespace LibHac.Tests.Kvdb; + +public class TypeLayoutTests +{ + [Fact] + public static void KeyValueArchiveHeader_Layout() + { + var s = new KeyValueArchiveHeader(); + + Assert.Equal(0xC, Unsafe.SizeOf()); + + Assert.Equal(0x0, GetOffset(in s, in s.Magic)); + Assert.Equal(0x4, GetOffset(in s, in s.Reserved)); + Assert.Equal(0x8, GetOffset(in s, in s.EntryCount)); + } + + [Fact] + public static void KeyValueArchiveEntryHeader_Layout() + { + var s = new KeyValueArchiveEntryHeader(); + + Assert.Equal(0xC, Unsafe.SizeOf()); + + Assert.Equal(0x0, GetOffset(in s, in s.Magic)); + Assert.Equal(0x4, GetOffset(in s, in s.KeySize)); + Assert.Equal(0x8, GetOffset(in s, in s.ValueSize)); + } +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Loader/TypeLayoutTests.cs b/tests/LibHac.Tests/Loader/TypeLayoutTests.cs new file mode 100644 index 00000000..6d4763f0 --- /dev/null +++ b/tests/LibHac.Tests/Loader/TypeLayoutTests.cs @@ -0,0 +1,130 @@ +using System.Runtime.CompilerServices; +using LibHac.Loader; +using Xunit; +using static LibHac.Tests.Common.Layout; + +namespace LibHac.Tests.Loader; + +public class TypeLayoutTests +{ + [Fact] + public static void NsoHeader_Layout() + { + var s = new NsoHeader(); + + Assert.Equal(0x100, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Magic)); + Assert.Equal(0x04, GetOffset(in s, in s.Version)); + Assert.Equal(0x08, GetOffset(in s, in s.Reserved08)); + Assert.Equal(0x0C, GetOffset(in s, in s.Flags)); + + Assert.Equal(0x10, GetOffset(in s, in s.TextFileOffset)); + Assert.Equal(0x14, GetOffset(in s, in s.TextMemoryOffset)); + Assert.Equal(0x18, GetOffset(in s, in s.TextSize)); + + Assert.Equal(0x1C, GetOffset(in s, in s.ModuleNameOffset)); + + Assert.Equal(0x20, GetOffset(in s, in s.RoFileOffset)); + Assert.Equal(0x24, GetOffset(in s, in s.RoMemoryOffset)); + Assert.Equal(0x28, GetOffset(in s, in s.RoSize)); + + Assert.Equal(0x2C, GetOffset(in s, in s.ModuleNameSize)); + + Assert.Equal(0x30, GetOffset(in s, in s.DataFileOffset)); + Assert.Equal(0x34, GetOffset(in s, in s.DataMemoryOffset)); + Assert.Equal(0x38, GetOffset(in s, in s.DataSize)); + + Assert.Equal(0x3C, GetOffset(in s, in s.BssSize)); + + Assert.Equal(0x40, GetOffset(in s, in s.ModuleId)); + + Assert.Equal(0x60, GetOffset(in s, in s.TextFileSize)); + Assert.Equal(0x64, GetOffset(in s, in s.RoFileSize)); + Assert.Equal(0x68, GetOffset(in s, in s.DataFileSize)); + + Assert.Equal(0x6C, GetOffset(in s, in s.Reserved6C)); + + Assert.Equal(0x88, GetOffset(in s, in s.ApiInfoOffset)); + Assert.Equal(0x8C, GetOffset(in s, in s.ApiInfoSize)); + Assert.Equal(0x90, GetOffset(in s, in s.DynStrOffset)); + Assert.Equal(0x94, GetOffset(in s, in s.DynStrSize)); + Assert.Equal(0x98, GetOffset(in s, in s.DynSymOffset)); + Assert.Equal(0x9C, GetOffset(in s, in s.DynSymSize)); + + Assert.Equal(0xA0, GetOffset(in s, in s.TextHash)); + Assert.Equal(0xC0, GetOffset(in s, in s.RoHash)); + Assert.Equal(0xE0, GetOffset(in s, in s.DataHash)); + } + + [Fact] + public static void Meta_layout() + { + var s = new Meta(); + + Assert.Equal(0x80, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Magic)); + Assert.Equal(0x04, GetOffset(in s, in s.SignatureKeyGeneration)); + Assert.Equal(0x08, GetOffset(in s, in s.Reserved08)); + Assert.Equal(0x0C, GetOffset(in s, in s.Flags)); + Assert.Equal(0x0D, GetOffset(in s, in s.Reserved0D)); + Assert.Equal(0x0E, GetOffset(in s, in s.MainThreadPriority)); + Assert.Equal(0x0F, GetOffset(in s, in s.DefaultCpuId)); + Assert.Equal(0x10, GetOffset(in s, in s.Reserved10)); + Assert.Equal(0x14, GetOffset(in s, in s.SystemResourceSize)); + Assert.Equal(0x18, GetOffset(in s, in s.Version)); + Assert.Equal(0x1C, GetOffset(in s, in s.MainThreadStackSize)); + Assert.Equal(0x20, GetOffset(in s, in s.ProgramName)); + Assert.Equal(0x30, GetOffset(in s, in s.ProductCode)); + Assert.Equal(0x40, GetOffset(in s, in s.Reserved40)); + Assert.Equal(0x70, GetOffset(in s, in s.AciOffset)); + Assert.Equal(0x74, GetOffset(in s, in s.AciSize)); + Assert.Equal(0x78, GetOffset(in s, in s.AcidOffset)); + Assert.Equal(0x7C, GetOffset(in s, in s.AcidSize)); + } + + [Fact] + public static void AciHeader_Layout() + { + var s = new AciHeader(); + + Assert.Equal(0x40, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Magic)); + Assert.Equal(0x04, GetOffset(in s, in s.Reserved04)); + Assert.Equal(0x10, GetOffset(in s, in s.ProgramId)); + Assert.Equal(0x18, GetOffset(in s, in s.Reserved18)); + Assert.Equal(0x20, GetOffset(in s, in s.FsAccessControlOffset)); + Assert.Equal(0x24, GetOffset(in s, in s.FsAccessControlSize)); + Assert.Equal(0x28, GetOffset(in s, in s.ServiceAccessControlOffset)); + Assert.Equal(0x2C, GetOffset(in s, in s.ServiceAccessControlSize)); + Assert.Equal(0x30, GetOffset(in s, in s.KernelCapabilityOffset)); + Assert.Equal(0x34, GetOffset(in s, in s.KernelCapabilitySize)); + Assert.Equal(0x38, GetOffset(in s, in s.Reserved38)); + } + + [Fact] + public static void AcidHeaderData_Layout() + { + var s = new AcidHeaderData(); + + Assert.Equal(0x240, Unsafe.SizeOf()); + + Assert.Equal(0x000, GetOffset(in s, in s.Signature)); + Assert.Equal(0x100, GetOffset(in s, in s.Modulus)); + Assert.Equal(0x200, GetOffset(in s, in s.Magic)); + Assert.Equal(0x204, GetOffset(in s, in s.Size)); + Assert.Equal(0x208, GetOffset(in s, in s.Version)); + Assert.Equal(0x20C, GetOffset(in s, in s.Flags)); + Assert.Equal(0x210, GetOffset(in s, in s.ProgramIdMin)); + Assert.Equal(0x218, GetOffset(in s, in s.ProgramIdMax)); + Assert.Equal(0x220, GetOffset(in s, in s.FsAccessControlOffset)); + Assert.Equal(0x224, GetOffset(in s, in s.FsAccessControlSize)); + Assert.Equal(0x228, GetOffset(in s, in s.ServiceAccessControlOffset)); + Assert.Equal(0x22C, GetOffset(in s, in s.ServiceAccessControlSize)); + Assert.Equal(0x230, GetOffset(in s, in s.KernelCapabilityOffset)); + Assert.Equal(0x234, GetOffset(in s, in s.KernelCapabilitySize)); + Assert.Equal(0x238, GetOffset(in s, in s.Reserved238)); + } +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Loader/TypeSizeTests.cs b/tests/LibHac.Tests/Loader/TypeSizeTests.cs deleted file mode 100644 index 27f4f2e3..00000000 --- a/tests/LibHac.Tests/Loader/TypeSizeTests.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Runtime.CompilerServices; -using LibHac.Loader; -using Xunit; - -namespace LibHac.Tests.Loader; - -public class TypeSizeTests -{ - [Fact] - public static void MetaSizeIsCorrect() - { - Assert.Equal(0x80, Unsafe.SizeOf()); - } - - [Fact] - public static void AciSizeIsCorrect() - { - Assert.Equal(0x40, Unsafe.SizeOf()); - } - - [Fact] - public static void AcidSizeIsCorrect() - { - Assert.Equal(0x240, Unsafe.SizeOf()); - } -} From 61b29e57a35c179b0d623c4222ecabbf802d6db3 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 4 Jan 2022 22:17:35 -0700 Subject: [PATCH 30/34] Update PathFormatter and PathNormalizer for 13.1.0 --- src/LibHac/Fs/Common/Path.cs | 2 + src/LibHac/Fs/Common/PathFormatter.cs | 51 +++- src/LibHac/Fs/Common/PathNormalizer.cs | 44 +++- tests/LibHac.Tests/Fs/PathFormatterTests.cs | 121 +++++++-- .../Fs/PathNormalizationTestGenerator.cpp | 248 ++++++++++++++---- tests/LibHac.Tests/Fs/PathNormalizerTests.cs | 160 +++++------ 6 files changed, 448 insertions(+), 178 deletions(-) diff --git a/src/LibHac/Fs/Common/Path.cs b/src/LibHac/Fs/Common/Path.cs index 3fa70880..49af516f 100644 --- a/src/LibHac/Fs/Common/Path.cs +++ b/src/LibHac/Fs/Common/Path.cs @@ -19,12 +19,14 @@ public struct PathFlags public void AllowEmptyPath() => _value |= 1 << 2; public void AllowMountName() => _value |= 1 << 3; public void AllowBackslash() => _value |= 1 << 4; + public void AllowAllCharacters() => _value |= 1 << 5; public bool IsWindowsPathAllowed() => (_value & (1 << 0)) != 0; public bool IsRelativePathAllowed() => (_value & (1 << 1)) != 0; public bool IsEmptyPathAllowed() => (_value & (1 << 2)) != 0; public bool IsMountNameAllowed() => (_value & (1 << 3)) != 0; public bool IsBackslashAllowed() => (_value & (1 << 4)) != 0; + public bool AreAllCharactersAllowed() => (_value & (1 << 5)) != 0; } /// diff --git a/src/LibHac/Fs/Common/PathFormatter.cs b/src/LibHac/Fs/Common/PathFormatter.cs index 24b961f8..08e4ddc2 100644 --- a/src/LibHac/Fs/Common/PathFormatter.cs +++ b/src/LibHac/Fs/Common/PathFormatter.cs @@ -13,9 +13,19 @@ namespace LibHac.Fs; /// /// Contains functions for working with path formatting and normalization. /// -/// Based on FS 12.1.0 (nnSdk 12.3.1) +/// Based on FS 13.1.0 (nnSdk 13.4.0) public static class PathFormatter { + private static ReadOnlySpan InvalidCharacter => + new[] { (byte)':', (byte)'*', (byte)'?', (byte)'<', (byte)'>', (byte)'|' }; + + private static ReadOnlySpan InvalidCharacterForHostName => + new[] { (byte)':', (byte)'*', (byte)'<', (byte)'>', (byte)'|', (byte)'$' }; + + private static ReadOnlySpan InvalidCharacterForMountName => + new[] { (byte)'*', (byte)'?', (byte)'<', (byte)'>', (byte)'|' }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Result CheckHostName(ReadOnlySpan name) { @@ -24,8 +34,11 @@ public static class PathFormatter for (int i = 0; i < name.Length; i++) { - if (name[i] == ':' || name[i] == '$') - return ResultFs.InvalidPathFormat.Log(); + foreach (byte c in InvalidCharacterForHostName) + { + if (name[i] == c) + return ResultFs.InvalidCharacter.Log(); + } } return Result.Success; @@ -41,8 +54,11 @@ public static class PathFormatter for (int i = 0; i < name.Length; i++) { - if (name[i] == ':') - return ResultFs.InvalidPathFormat.Log(); + foreach (byte c in InvalidCharacter) + { + if (name[i] == c) + return ResultFs.InvalidCharacter.Log(); + } } return Result.Success; @@ -90,8 +106,11 @@ public static class PathFormatter for (int i = 0; i < mountLength; i++) { - if (path.At(i) is (byte)'*' or (byte)'?' or (byte)'<' or (byte)'>' or (byte)'|') - return ResultFs.InvalidCharacter.Log(); + foreach (byte c in InvalidCharacterForMountName) + { + if (path.At(i) == c) + return ResultFs.InvalidCharacter.Log(); + } } if (!outMountNameBuffer.IsEmpty) @@ -150,6 +169,12 @@ public static class PathFormatter int winPathLength; for (winPathLength = 2; currentPath.At(winPathLength) != NullTerminator; winPathLength++) { + foreach (byte c in InvalidCharacter) + { + if (currentPath[winPathLength] == c) + return ResultFs.InvalidCharacter.Log(); + } + if (currentPath[winPathLength] == DirectorySeparator || currentPath[winPathLength] == AltDirectorySeparator) { @@ -478,8 +503,11 @@ public static class PathFormatter } } - if (PathNormalizer.IsParentDirectoryPathReplacementNeeded(buffer)) - return ResultFs.DirectoryUnobtainable.Log(); + if (flags.IsBackslashAllowed() && PathNormalizer.IsParentDirectoryPathReplacementNeeded(buffer)) + { + isNormalized = false; + return Result.Success; + } rc = PathUtility.CheckInvalidBackslash(out bool isBackslashContained, buffer, flags.IsWindowsPathAllowed() || flags.IsBackslashAllowed()); @@ -491,7 +519,7 @@ public static class PathFormatter return Result.Success; } - rc = PathNormalizer.IsNormalized(out isNormalized, out int length, buffer); + rc = PathNormalizer.IsNormalized(out isNormalized, out int length, buffer, flags.AreAllCharactersAllowed()); if (rc.IsFailure()) return rc; totalLength += length; @@ -612,7 +640,8 @@ public static class PathFormatter src = srcBufferSlashReplaced.AsSpan(srcOffset); } - rc = PathNormalizer.Normalize(outputBuffer.Slice(currentPos), out _, src, isWindowsPath, isDriveRelative); + rc = PathNormalizer.Normalize(outputBuffer.Slice(currentPos), out _, src, isWindowsPath, isDriveRelative, + flags.AreAllCharactersAllowed()); if (rc.IsFailure()) return rc; return Result.Success; diff --git a/src/LibHac/Fs/Common/PathNormalizer.cs b/src/LibHac/Fs/Common/PathNormalizer.cs index 197728dd..91251b29 100644 --- a/src/LibHac/Fs/Common/PathNormalizer.cs +++ b/src/LibHac/Fs/Common/PathNormalizer.cs @@ -10,7 +10,7 @@ namespace LibHac.Fs; /// /// Contains functions for doing with basic path normalization. /// -/// Based on FS 12.1.0 (nnSdk 12.3.1) +/// Based on FS 13.1.0 (nnSdk 13.4.0) public static class PathNormalizer { private enum PathState @@ -25,6 +25,12 @@ public static class PathNormalizer public static Result Normalize(Span outputBuffer, out int length, ReadOnlySpan path, bool isWindowsPath, bool isDriveRelativePath) + { + return Normalize(outputBuffer, out length, path, isWindowsPath, isDriveRelativePath, false); + } + + public static Result Normalize(Span outputBuffer, out int length, ReadOnlySpan path, bool isWindowsPath, + bool isDriveRelativePath, bool allowAllCharacters) { UnsafeHelpers.SkipParamInit(out length); @@ -32,7 +38,7 @@ public static class PathNormalizer int totalLength = 0; int i = 0; - if (!IsSeparator(path.At(0))) + if (path.At(0) != DirectorySeparator) { if (!isDriveRelativePath) return ResultFs.InvalidPathFormat.Log(); @@ -43,6 +49,7 @@ public static class PathNormalizer var convertedPath = new RentedArray(); try { + Result rc; // Check if parent directory path replacement is needed. if (IsParentDirectoryPathReplacementNeeded(currentPath)) { @@ -58,20 +65,22 @@ public static class PathNormalizer bool skipNextSeparator = false; - while (!IsNul(currentPath.At(i))) + while (currentPath.At(i) != NullTerminator) { - if (IsSeparator(currentPath[i])) + if (currentPath[i] == DirectorySeparator) { do { i++; - } while (IsSeparator(currentPath.At(i))); + } while (currentPath.At(i) == DirectorySeparator); - if (IsNul(currentPath.At(i))) + if (currentPath.At(i) == NullTerminator) break; if (!skipNextSeparator) { + // Note: Nintendo returns TooLongPath in some cases where the output buffer is actually long + // enough to hold the normalized path. e.g. "/aa/bb/." with an output buffer length of 7 if (totalLength + 1 == outputBuffer.Length) { outputBuffer[totalLength] = NullTerminator; @@ -87,8 +96,14 @@ public static class PathNormalizer } int dirLen = 0; - while (!IsSeparator(currentPath.At(i + dirLen)) && !IsNul(currentPath.At(i + dirLen))) + while (currentPath.At(i + dirLen) != DirectorySeparator && currentPath.At(i + dirLen) != NullTerminator) { + if (!allowAllCharacters) + { + rc = CheckInvalidCharacter(currentPath[i + dirLen]); + if (rc.IsFailure()) return rc.Miss(); + } + dirLen++; } @@ -163,12 +178,14 @@ public static class PathNormalizer } // Note: This bug is in the original code. They probably meant to put "totalLength + 1" - if (totalLength - 1 > outputBuffer.Length) + // The buffer needs to be able to contain the total length of the normalized string plus + // one for the null terminator + if (outputBuffer.Length < totalLength - 1) return ResultFs.TooLongPath.Log(); outputBuffer[totalLength] = NullTerminator; - Result rc = IsNormalized(out bool isNormalized, out _, outputBuffer); + rc = IsNormalized(out bool isNormalized, out _, outputBuffer, allowAllCharacters); if (rc.IsFailure()) return rc; Assert.SdkAssert(isNormalized); @@ -200,6 +217,12 @@ public static class PathNormalizer /// : The path contains an invalid character.
/// : The path is not in a valid format. public static Result IsNormalized(out bool isNormalized, out int length, ReadOnlySpan path) + { + return IsNormalized(out isNormalized, out length, path, false); + } + + public static Result IsNormalized(out bool isNormalized, out int length, ReadOnlySpan path, + bool allowAllCharacters) { UnsafeHelpers.SkipParamInit(out isNormalized, out length); @@ -213,7 +236,7 @@ public static class PathNormalizer pathLength++; - if (state != PathState.Initial) + if (!allowAllCharacters && state != PathState.Initial) { Result rc = CheckInvalidCharacter(c); if (rc.IsFailure()) return rc; @@ -292,7 +315,6 @@ public static class PathNormalizer return Result.Success; } - /// /// Checks if a path begins with / or \ and contains any of these patterns: /// "/..\", "\..\", "\../", "\..0" where '0' is the null terminator. diff --git a/tests/LibHac.Tests/Fs/PathFormatterTests.cs b/tests/LibHac.Tests/Fs/PathFormatterTests.cs index 0cb382d8..c5d0201c 100644 --- a/tests/LibHac.Tests/Fs/PathFormatterTests.cs +++ b/tests/LibHac.Tests/Fs/PathFormatterTests.cs @@ -54,17 +54,18 @@ public class PathFormatterTests { @"\\?\c:\", "", @"", ResultFs.InvalidCharacter.Value }, { @"mount:\\host\share\aa\bb", "M", @"mount:", ResultFs.InvalidCharacter.Value }, { @"mount:\\host/share\aa\bb", "M", @"mount:", ResultFs.InvalidCharacter.Value }, + { @"c:\aa\..\..\..\bb", "W", @"c:/bb", Result.Success }, { @"mount:/\\aa\..\bb", "MW", @"mount:", ResultFs.InvalidPathFormat.Value }, { @"mount:/c:\aa\..\bb", "MW", @"mount:c:/bb", Result.Success }, { @"mount:/aa/bb", "MW", @"mount:/aa/bb", Result.Success }, - { @"/mount:/aa/bb", "MW", @"/mount:/aa/bb", ResultFs.InvalidCharacter.Value }, - { @"/mount:/aa/bb", "W", @"/mount:/aa/bb", ResultFs.InvalidCharacter.Value }, + { @"/mount:/aa/bb", "MW", @"/", ResultFs.InvalidCharacter.Value }, + { @"/mount:/aa/bb", "W", @"/", ResultFs.InvalidCharacter.Value }, { @"a:aa/../bb", "MW", @"a:aa/bb", Result.Success }, { @"a:aa\..\bb", "MW", @"a:aa/bb", Result.Success }, - { @"/a:aa\..\bb", "W", @"/bb", Result.Success }, + { @"/a:aa\..\bb", "W", @"/", ResultFs.InvalidCharacter.Value }, { @"\\?\c:\.\aa", "W", @"\\?\c:/aa", Result.Success }, { @"\\.\c:\.\aa", "W", @"\\.\c:/aa", Result.Success }, - { @"\\.\mount:\.\aa", "W", @"\\./mount:/aa", ResultFs.InvalidCharacter.Value }, + { @"\\.\mount:\.\aa", "W", @"\\./", ResultFs.InvalidCharacter.Value }, { @"\\./.\aa", "W", @"\\./aa", Result.Success }, { @"\\/aa", "W", @"", ResultFs.InvalidPathFormat.Value }, { @"\\\aa", "W", @"", ResultFs.InvalidPathFormat.Value }, @@ -73,13 +74,13 @@ public class PathFormatterTests { @"\\host\share\path", "W", @"\\host\share/path", Result.Success }, { @"\\host\share\path\aa\bb\..\cc\.", "W", @"\\host\share/path/aa/cc", Result.Success }, { @"\\host\", "W", @"", ResultFs.InvalidPathFormat.Value }, - { @"\\ho$st\share\path", "W", @"", ResultFs.InvalidPathFormat.Value }, - { @"\\host:\share\path", "W", @"", ResultFs.InvalidPathFormat.Value }, + { @"\\ho$st\share\path", "W", @"", ResultFs.InvalidCharacter.Value }, + { @"\\host:\share\path", "W", @"", ResultFs.InvalidCharacter.Value }, { @"\\..\share\path", "W", @"", ResultFs.InvalidPathFormat.Value }, - { @"\\host\s:hare\path", "W", @"", ResultFs.InvalidPathFormat.Value }, + { @"\\host\s:hare\path", "W", @"", ResultFs.InvalidCharacter.Value }, { @"\\host\.\path", "W", @"", ResultFs.InvalidPathFormat.Value }, { @"\\host\..\path", "W", @"", ResultFs.InvalidPathFormat.Value }, - { @"\\host\sha:re", "W", @"", ResultFs.InvalidPathFormat.Value }, + { @"\\host\sha:re", "W", @"", ResultFs.InvalidCharacter.Value }, { @".\\host\share", "RW", @"..\\host\share/", Result.Success } }; @@ -128,14 +129,46 @@ public class PathFormatterTests NormalizeImpl(path, pathFlags, 0x301, expectedNormalized, expectedResult); } + public static TheoryData TestData_Normalize_AllowAllChars => new() + { + { @"/aa/b:b/cc", "", @"/aa/", ResultFs.InvalidCharacter.Value }, + { @"/aa/b*b/cc", "", @"/aa/", ResultFs.InvalidCharacter.Value }, + { @"/aa/b?b/cc", "", @"/aa/", ResultFs.InvalidCharacter.Value }, + { @"/aa/bb/cc", "", @"/aa/", ResultFs.InvalidCharacter.Value }, + { @"/aa/b|b/cc", "", @"/aa/", ResultFs.InvalidCharacter.Value }, + { @"/aa/b:b/cc", "C", @"/aa/b:b/cc", Result.Success }, + { @"/aa/b*b/cc", "C", @"/aa/b*b/cc", Result.Success }, + { @"/aa/b?b/cc", "C", @"/aa/b?b/cc", Result.Success }, + { @"/aa/bb/cc", "C", @"/aa/b>b/cc", Result.Success }, + { @"/aa/b|b/cc", "C", @"/aa/b|b/cc", Result.Success }, + { @"/aa/b'b/cc", "", @"/aa/b'b/cc", Result.Success }, + { @"/aa/b""b/cc", "", @"/aa/b""b/cc", Result.Success }, + { @"/aa/b(b/cc", "", @"/aa/b(b/cc", Result.Success }, + { @"/aa/b)b/cc", "", @"/aa/b)b/cc", Result.Success }, + { @"/aa/b'b/cc", "C", @"/aa/b'b/cc", Result.Success }, + { @"/aa/b""b/cc", "C", @"/aa/b""b/cc", Result.Success }, + { @"/aa/b(b/cc", "C", @"/aa/b(b/cc", Result.Success }, + { @"/aa/b)b/cc", "C", @"/aa/b)b/cc", Result.Success }, + { @"mount:/aa/bunt:/aa/bb/cc", "MC", @"", ResultFs.InvalidCharacter.Value } + }; + + [Theory, MemberData(nameof(TestData_Normalize_AllowAllChars))] + public static void Normalize_AllowAllChars(string path, string pathFlags, string expectedNormalized, Result expectedResult) + { + NormalizeImpl(path, pathFlags, 0x301, expectedNormalized, expectedResult); + } + public static TheoryData TestData_Normalize_All => new() { { @"mount:./aa/bb", "WRM", @"mount:./aa/bb", Result.Success }, { @"mount:./aa/bb\cc/dd", "WRM", @"mount:./aa/bb/cc/dd", Result.Success }, { @"mount:./aa/bb\cc/dd", "WRMB", @"mount:./aa/bb/cc/dd", Result.Success }, - { @"mount:./.c:/aa/bb", "RM", @"mount:./.c:/aa/bb", ResultFs.InvalidCharacter.Value }, - { @"mount:.c:/aa/bb", "WRM", @"mount:./.c:/aa/bb", ResultFs.InvalidCharacter.Value }, - { @"mount:./cc:/aa/bb", "WRM", @"mount:./cc:/aa/bb", ResultFs.InvalidCharacter.Value }, + { @"mount:./.c:/aa/bb", "RM", @"mount:./", ResultFs.InvalidCharacter.Value }, + { @"mount:.c:/aa/bb", "WRM", @"mount:./", ResultFs.InvalidCharacter.Value }, + { @"mount:./cc:/aa/bb", "WRM", @"mount:./", ResultFs.InvalidCharacter.Value }, { @"mount:./\\host\share/aa/bb", "MW", @"mount:", ResultFs.InvalidPathFormat.Value }, { @"mount:./\\host\share/aa/bb", "WRM", @"mount:.\\host\share/aa/bb", Result.Success }, { @"mount:.\\host\share/aa/bb", "WRM", @"mount:..\\host\share/aa/bb", Result.Success }, @@ -147,7 +180,8 @@ public class PathFormatterTests { @"mount:/aa\bb", "BM", @"mount:/aa\bb", Result.Success }, { @".//aa/bb", "RW", @"./aa/bb", Result.Success }, { @"./aa/bb", "R", @"./aa/bb", Result.Success }, - { @"./c:/aa/bb", "RW", @"./c:/aa/bb", ResultFs.InvalidCharacter.Value } + { @"./c:/aa/bb", "RW", @"./", ResultFs.InvalidCharacter.Value }, + { @"mount:./aa/b:b\cc/dd", "WRMBC", @"mount:./aa/b:b/cc/dd", Result.Success } }; [Theory, MemberData(nameof(TestData_Normalize_All))] @@ -219,7 +253,6 @@ public class PathFormatterTests public static TheoryData TestData_IsNormalized_WindowsPath => new() { - { @"c:/aa/bb", "", false, 0, ResultFs.InvalidPathFormat.Value }, { @"c:/aa/bb", "", false, 0, ResultFs.InvalidPathFormat.Value }, { @"c:\aa\bb", "", false, 0, ResultFs.InvalidPathFormat.Value }, { @"\\host\share", "", false, 0, ResultFs.InvalidPathFormat.Value }, @@ -228,6 +261,7 @@ public class PathFormatterTests { @"\\?\c:\", "", false, 0, ResultFs.InvalidPathFormat.Value }, { @"mount:\\host\share\aa\bb", "M", false, 0, ResultFs.InvalidPathFormat.Value }, { @"mount:\\host/share\aa\bb", "M", false, 0, ResultFs.InvalidPathFormat.Value }, + { @"c:\aa\..\..\..\bb", "W", false, 0, Result.Success }, { @"mount:/\\aa\..\bb", "MW", false, 0, Result.Success }, { @"mount:/c:\aa\..\bb", "MW", false, 0, Result.Success }, { @"mount:/aa/bb", "MW", true, 12, Result.Success }, @@ -235,7 +269,7 @@ public class PathFormatterTests { @"/mount:/aa/bb", "W", false, 0, ResultFs.InvalidCharacter.Value }, { @"a:aa/../bb", "MW", false, 8, Result.Success }, { @"a:aa\..\bb", "MW", false, 0, Result.Success }, - { @"/a:aa\..\bb", "W", false, 0, ResultFs.DirectoryUnobtainable.Value }, + { @"/a:aa\..\bb", "W", false, 0, Result.Success }, { @"\\?\c:\.\aa", "W", false, 0, Result.Success }, { @"\\.\c:\.\aa", "W", false, 0, Result.Success }, { @"\\.\mount:\.\aa", "W", false, 0, Result.Success }, @@ -247,13 +281,13 @@ public class PathFormatterTests { @"\\host\share\path", "W", false, 0, Result.Success }, { @"\\host\share\path\aa\bb\..\cc\.", "W", false, 0, Result.Success }, { @"\\host\", "W", false, 0, ResultFs.InvalidPathFormat.Value }, - { @"\\ho$st\share\path", "W", false, 0, ResultFs.InvalidPathFormat.Value }, - { @"\\host:\share\path", "W", false, 0, ResultFs.InvalidPathFormat.Value }, + { @"\\ho$st\share\path", "W", false, 0, ResultFs.InvalidCharacter.Value }, + { @"\\host:\share\path", "W", false, 0, ResultFs.InvalidCharacter.Value }, { @"\\..\share\path", "W", false, 0, ResultFs.InvalidPathFormat.Value }, - { @"\\host\s:hare\path", "W", false, 0, ResultFs.InvalidPathFormat.Value }, + { @"\\host\s:hare\path", "W", false, 0, ResultFs.InvalidCharacter.Value }, { @"\\host\.\path", "W", false, 0, ResultFs.InvalidPathFormat.Value }, { @"\\host\..\path", "W", false, 0, ResultFs.InvalidPathFormat.Value }, - { @"\\host\sha:re", "W", false, 0, ResultFs.InvalidPathFormat.Value }, + { @"\\host\sha:re", "W", false, 0, ResultFs.InvalidCharacter.Value }, { @".\\host\share", "RW", false, 0, Result.Success } }; @@ -288,14 +322,14 @@ public class PathFormatterTests { { @"\aa\bb\..\cc", "", false, 0, ResultFs.InvalidPathFormat.Value }, { @"\aa\bb\..\cc", "B", false, 0, ResultFs.InvalidPathFormat.Value }, - { @"/aa\bb\..\cc", "", false, 0, ResultFs.DirectoryUnobtainable.Value }, - { @"/aa\bb\..\cc", "B", false, 0, ResultFs.DirectoryUnobtainable.Value }, + { @"/aa\bb\..\cc", "", false, 0, ResultFs.InvalidCharacter.Value }, + { @"/aa\bb\..\cc", "B", false, 0, Result.Success }, { @"/aa\bb\cc", "", false, 0, ResultFs.InvalidCharacter.Value }, { @"/aa\bb\cc", "B", true, 9, Result.Success }, { @"\\host\share\path\aa\bb\cc", "W", false, 0, Result.Success }, { @"\\host\share\path\aa\bb\cc", "WB", false, 0, Result.Success }, - { @"/aa/bb\../cc/..\dd\..\ee/..", "", false, 0, ResultFs.DirectoryUnobtainable.Value }, - { @"/aa/bb\../cc/..\dd\..\ee/..", "B", false, 0, ResultFs.DirectoryUnobtainable.Value } + { @"/aa/bb\../cc/..\dd\..\ee/..", "", false, 0, ResultFs.InvalidCharacter.Value }, + { @"/aa/bb\../cc/..\dd\..\ee/..", "B", false, 0, Result.Success } }; [Theory, MemberData(nameof(TestData_IsNormalized_Backslash))] @@ -305,6 +339,39 @@ public class PathFormatterTests IsNormalizedImpl(path, pathFlags, expectedIsNormalized, expectedLength, expectedResult); } + public static TheoryData TestData_IsNormalized_AllowAllChars => new() + { + { @"/aa/b:b/cc", "", false, 0, ResultFs.InvalidCharacter.Value }, + { @"/aa/b*b/cc", "", false, 0, ResultFs.InvalidCharacter.Value }, + { @"/aa/b?b/cc", "", false, 0, ResultFs.InvalidCharacter.Value }, + { @"/aa/bb/cc", "", false, 0, ResultFs.InvalidCharacter.Value }, + { @"/aa/b|b/cc", "", false, 0, ResultFs.InvalidCharacter.Value }, + { @"/aa/b:b/cc", "C", true, 10, Result.Success }, + { @"/aa/b*b/cc", "C", true, 10, Result.Success }, + { @"/aa/b?b/cc", "C", true, 10, Result.Success }, + { @"/aa/bb/cc", "C", true, 10, Result.Success }, + { @"/aa/b|b/cc", "C", true, 10, Result.Success }, + { @"/aa/b'b/cc", "", true, 10, Result.Success }, + { @"/aa/b""b/cc", "", true, 10, Result.Success }, + { @"/aa/b(b/cc", "", true, 10, Result.Success }, + { @"/aa/b)b/cc", "", true, 10, Result.Success }, + { @"/aa/b'b/cc", "C", true, 10, Result.Success }, + { @"/aa/b""b/cc", "C", true, 10, Result.Success }, + { @"/aa/b(b/cc", "C", true, 10, Result.Success }, + { @"/aa/b)b/cc", "C", true, 10, Result.Success }, + { @"mount:/aa/bunt:/aa/bb/cc", "MC", false, 0, ResultFs.InvalidCharacter.Value } + }; + + [Theory, MemberData(nameof(TestData_IsNormalized_AllowAllChars))] + public static void IsNormalized_AllowAllChars(string path, string pathFlags, bool expectedIsNormalized, long expectedLength, + Result expectedResult) + { + IsNormalizedImpl(path, pathFlags, expectedIsNormalized, expectedLength, expectedResult); + } + public static TheoryData TestData_IsNormalized_All => new() { { @"mount:./aa/bb", "WRM", true, 13, Result.Success }, @@ -324,7 +391,8 @@ public class PathFormatterTests { @"mount:/aa\bb", "BM", true, 12, Result.Success }, { @".//aa/bb", "RW", false, 1, Result.Success }, { @"./aa/bb", "R", true, 7, Result.Success }, - { @"./c:/aa/bb", "RW", false, 0, ResultFs.InvalidCharacter.Value } + { @"./c:/aa/bb", "RW", false, 0, ResultFs.InvalidCharacter.Value }, + { @"mount:./aa/b:b\cc/dd", "WRMBC", true, 20, Result.Success } }; [Theory, MemberData(nameof(TestData_IsNormalized_All))] @@ -386,9 +454,14 @@ public class PathFormatterTests case 'W': flags.AllowWindowsPath(); break; + case 'C': + flags.AllowAllCharacters(); + break; + default: + throw new NotSupportedException(); } } return flags; } -} +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Fs/PathNormalizationTestGenerator.cpp b/tests/LibHac.Tests/Fs/PathNormalizationTestGenerator.cpp index 5c5e41cc..0e875b40 100644 --- a/tests/LibHac.Tests/Fs/PathNormalizationTestGenerator.cpp +++ b/tests/LibHac.Tests/Fs/PathNormalizationTestGenerator.cpp @@ -14,7 +14,7 @@ namespace nn::fs::detail { bool IsEnabledAccessLog(); } -// SDK 12 +// SDK 13 namespace nn::fs { bool IsSubPath(const char* path1, const char* path2); @@ -29,12 +29,32 @@ namespace nn::fs { void AllowEmptyPath() { value |= (1 << 2); } void AllowMountName() { value |= (1 << 3); } void AllowBackslash() { value |= (1 << 4); } + void AllowAllCharacters() { value |= (1 << 5); } - const bool IsWindowsPathAllowed() { return (value & (1 << 0)) != 0; } - const bool IsRelativePathAllowed() { return (value & (1 << 1)) != 0; } - const bool IsEmptyPathAllowed() { return (value & (1 << 2)) != 0; } - const bool IsMountNameAllowed() { return (value & (1 << 3)) != 0; } - const bool IsBackslashAllowed() { return (value & (1 << 4)) != 0; } + bool IsWindowsPathAllowed()const { return (value & (1 << 0)) != 0; } + bool IsRelativePathAllowed()const { return (value & (1 << 1)) != 0; } + bool IsEmptyPathAllowed()const { return (value & (1 << 2)) != 0; } + bool IsMountNameAllowed()const { return (value & (1 << 3)) != 0; } + bool IsBackslashAllowed()const { return (value & (1 << 4)) != 0; } + bool AreAllCharactersAllowed()const { return (value & (1 << 5)) != 0; } + }; + + class Path { + public: + char* m_String; + char* m_WriteBuffer; + uint64_t m_UniquePtrLength; + uint64_t m_WriteBufferLength; + bool m_IsNormalized; + + Path(); + nn::Result Initialize(char const* path); + nn::Result InitializeWithNormalization(char const* path); + nn::Result InitializeWithReplaceUnc(char const* path); + nn::Result Initialize(char const* path, uint64_t pathLength); + nn::Result InsertParent(char const* path); + nn::Result RemoveChild(); + nn::Result Normalize(const nn::fs::PathFlags&); }; class PathFormatter { @@ -48,7 +68,9 @@ namespace nn::fs { class PathNormalizer { public: static nn::Result Normalize(char* outBuffer, uint64_t* outLength, const char* path, uint64_t outBufferLength, bool isWindowsPath, bool isDriveRelative); + static nn::Result Normalize(char* outBuffer, uint64_t* outLength, const char* path, uint64_t outBufferLength, bool isWindowsPath, bool isDriveRelative, bool allowAllCharacters); static nn::Result IsNormalized(bool* outIsNormalized, uint64_t* outNormalizedPathLength, const char* path); + static nn::Result IsNormalized(bool* outIsNormalized, uint64_t* outNormalizedPathLength, const char* path, bool allowAllCharacters); }; } @@ -75,6 +97,7 @@ void CreateTest(const char* name, void (*func)(Ts...), const std::arrayb/cc)", ""), + std::make_tuple(R"(/aa/b|b/cc)", ""), + std::make_tuple(R"(/aa/b:b/cc)", "C"), + std::make_tuple(R"(/aa/b*b/cc)", "C"), + std::make_tuple(R"(/aa/b?b/cc)", "C"), + std::make_tuple(R"(/aa/bb/cc)", "C"), + std::make_tuple(R"(/aa/b|b/cc)", "C"), + + std::make_tuple(R"(/aa/b'b/cc)", ""), // Test some symbols that are normally allowed + std::make_tuple(R"(/aa/b"b/cc)", ""), + std::make_tuple(R"(/aa/b(b/cc)", ""), + std::make_tuple(R"(/aa/b)b/cc)", ""), + std::make_tuple(R"(/aa/b'b/cc)", "C"), + std::make_tuple(R"(/aa/b"b/cc)", "C"), + std::make_tuple(R"(/aa/b(b/cc)", "C"), + std::make_tuple(R"(/aa/b)b/cc)", "C"), + + std::make_tuple(R"(mount:/aa/bunt:/aa/bb/cc)", "MC") // Invalid character in mount name +); + static constexpr const auto TestData_PathFormatterNormalize_All = make_array( std::make_tuple(R"(mount:./aa/bb)", "WRM"), // Normalized path with both mount name and relative path std::make_tuple(R"(mount:./aa/bb\cc/dd)", "WRM"), // Path with backslashes @@ -215,14 +287,15 @@ static constexpr const auto TestData_PathFormatterNormalize_All = make_array( std::make_tuple(R"(mount:./\\host\share/aa/bb)", "WRM"), // These next 3 form a chain where if you normalize one it'll turn into the next std::make_tuple(R"(mount:.\\host\share/aa/bb)", "WRM"), std::make_tuple(R"(mount:..\\host\share/aa/bb)", "WRM"), - std::make_tuple(R"(.\\host\share/aa/bb)", "WRM"), // These next 2 form a chain where if you normalize one it'll turn into the next + std::make_tuple(R"(.\\host\share/aa/bb)", "WRM"), // These next 2 form a chain where if you normalize one it'll turn into the next std::make_tuple(R"(..\\host\share/aa/bb)", "WRM"), std::make_tuple(R"(mount:\\host\share/aa/bb)", "MW"), // Use a mount name and windows path together std::make_tuple(R"(mount:\aa\bb)", "BM"), // Backslashes are never allowed directly after a mount name even with AllowBackslashes std::make_tuple(R"(mount:/aa\bb)", "BM"), std::make_tuple(R"(.//aa/bb)", "RW"), // Relative path followed by a Windows path won't work std::make_tuple(R"(./aa/bb)", "R"), - std::make_tuple(R"(./c:/aa/bb)", "RW") + std::make_tuple(R"(./c:/aa/bb)", "RW"), + std::make_tuple(R"(mount:./aa/b:b\cc/dd)", "WRMBC") // This path is considered normalized but the backslashes still normalize to forward slashes ); void CreateTest_PathFormatterNormalize(char const* path, char const* pathFlags) { @@ -232,7 +305,7 @@ void CreateTest_PathFormatterNormalize(char const* path, char const* pathFlags) nn::Result result = nn::fs::PathFormatter::Normalize(normalized, 0x200, path, 0x200, flags); BufPos += sprintf(&Buf[BufPos], "{@\"%s\", \"%s\", @\"%s\", %s},\n", - path, pathFlags, normalized, GetResultName(result)); + GetEscaped(path).c_str(), pathFlags, GetEscaped(normalized).c_str(), GetResultName(result)); } void CreateTest_PathFormatterIsNormalized(char const* path, char const* pathFlags) { @@ -243,7 +316,7 @@ void CreateTest_PathFormatterIsNormalized(char const* path, char const* pathFlag nn::Result result = nn::fs::PathFormatter::IsNormalized(&isNormalized, &normalizedLength, path, flags); BufPos += sprintf(&Buf[BufPos], "{@\"%s\", \"%s\", %s, %ld, %s},\n", - path, pathFlags, BoolStr(isNormalized), normalizedLength, GetResultName(result)); + GetEscaped(path).c_str(), pathFlags, BoolStr(isNormalized), normalizedLength, GetResultName(result)); } static constexpr const auto TestData_PathFormatterNormalize_SmallBuffer = make_array( @@ -259,68 +332,72 @@ void CreateTest_PathFormatterNormalize_SmallBuffer(char const* path, char const* char normalized[0x200] = { 0 }; nn::fs::PathFlags flags = GetPathFlags(pathFlags); - svcOutputDebugString(path, strnlen(path, 0x200)); - nn::Result result = nn::fs::PathFormatter::Normalize(normalized, bufferSize, path, 0x200, flags); BufPos += sprintf(&Buf[BufPos], "{@\"%s\", \"%s\", %d, @\"%s\", %s},\n", - path, pathFlags, bufferSize, normalized, GetResultName(result)); + GetEscaped(path).c_str(), pathFlags, bufferSize, GetEscaped(normalized).c_str(), GetResultName(result)); } static constexpr const auto TestData_PathNormalizerNormalize = make_array( - std::make_tuple("/aa/bb/c/", false, true), - std::make_tuple("aa/bb/c/", false, false), - std::make_tuple("aa/bb/c/", false, true), - std::make_tuple("mount:a/b", false, true), - std::make_tuple("/aa/bb/../..", true, false), - std::make_tuple("/aa/bb/../../..", true, false), - std::make_tuple("/aa/bb/../../..", false, false), - std::make_tuple("aa/bb/../../..", true, true), - std::make_tuple("aa/bb/../../..", false, true), - std::make_tuple("", false, false), - std::make_tuple("/", false, false), - std::make_tuple("/.", false, false), - std::make_tuple("/./", false, false), - std::make_tuple("/..", false, false), - std::make_tuple("//.", false, false), - std::make_tuple("/ ..", false, false), - std::make_tuple("/.. /", false, false), - std::make_tuple("/. /.", false, false), - std::make_tuple("/aa/bb/cc/dd/./.././../..", false, false), - std::make_tuple("/aa/bb/cc/dd/./.././../../..", false, false), - std::make_tuple("/./aa/./bb/./cc/./dd/.", false, false), - std::make_tuple("/aa\\bb/cc", false, false), - std::make_tuple("/aa\\bb/cc", false, false), - std::make_tuple("/a|/bb/cc", false, false), - std::make_tuple("/>a/bb/cc", false, false), - std::make_tuple("/aa/.a/bb/cc", false, false, true), + std::make_tuple("/aa/.a/bb/cc", false, false, false), + std::make_tuple("/aa/. TestData_Normalize => new() + public static TheoryData TestData_Normalize => new() { - { @"/aa/bb/c/", false, true, @"/aa/bb/c", 8, Result.Success }, - { @"aa/bb/c/", false, false, @"", 0, ResultFs.InvalidPathFormat.Value }, - { @"aa/bb/c/", false, true, @"/aa/bb/c", 8, Result.Success }, - { @"mount:a/b", false, true, @"/mount:a/b", 0, ResultFs.InvalidCharacter.Value }, - { @"/aa/bb/../..", true, false, @"/", 1, Result.Success }, - { @"/aa/bb/../../..", true, false, @"/", 1, Result.Success }, - { @"/aa/bb/../../..", false, false, @"/aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value }, - { @"aa/bb/../../..", true, true, @"/", 1, Result.Success }, - { @"aa/bb/../../..", false, true, @"/aa/bb/", 0, ResultFs.DirectoryUnobtainable.Value }, - { @"", false, false, @"", 0, ResultFs.InvalidPathFormat.Value }, - { @"/", false, false, @"/", 1, Result.Success }, - { @"/.", false, false, @"/", 1, Result.Success }, - { @"/./", false, false, @"/", 1, Result.Success }, - { @"/..", false, false, @"/", 0, ResultFs.DirectoryUnobtainable.Value }, - { @"//.", false, false, @"/", 1, Result.Success }, - { @"/ ..", false, false, @"/ ..", 4, Result.Success }, - { @"/.. /", false, false, @"/.. ", 4, Result.Success }, - { @"/. /.", false, false, @"/. ", 3, Result.Success }, - { @"/aa/bb/cc/dd/./.././../..", false, false, @"/aa", 3, Result.Success }, - { @"/aa/bb/cc/dd/./.././../../..", false, false, @"/", 1, Result.Success }, - { @"/./aa/./bb/./cc/./dd/.", false, false, @"/aa/bb/cc/dd", 12, Result.Success }, - { @"/aa\bb/cc", false, false, @"/aa\bb/cc", 9, Result.Success }, - { @"/aa\bb/cc", false, false, @"/aa\bb/cc", 9, Result.Success }, - { @"/a|/bb/cc", false, false, @"/a|/bb/cc", 0, ResultFs.InvalidCharacter.Value }, - { @"/>a/bb/cc", false, false, @"/>a/bb/cc", 0, ResultFs.InvalidCharacter.Value }, - { @"/aa/.a/bb/cc", false, false, true, @"/>a/bb/cc", 9, Result.Success }, + { @"/aa/.a/bb/cc", false, false, false, @"/", 0, ResultFs.InvalidCharacter.Value }, + { @"/aa/. TestData_IsNormalized => new() + public static TheoryData TestData_IsNormalized => new() { - { @"/aa/bb/c/", false, 9, Result.Success }, - { @"aa/bb/c/", false, 0, ResultFs.InvalidPathFormat.Value }, - { @"aa/bb/c/", false, 0, ResultFs.InvalidPathFormat.Value }, - { @"mount:a/b", false, 0, ResultFs.InvalidPathFormat.Value }, - { @"/aa/bb/../..", false, 0, Result.Success }, - { @"/aa/bb/../../..", false, 0, Result.Success }, - { @"/aa/bb/../../..", false, 0, Result.Success }, - { @"aa/bb/../../..", false, 0, ResultFs.InvalidPathFormat.Value }, - { @"aa/bb/../../..", false, 0, ResultFs.InvalidPathFormat.Value }, - { @"", false, 0, ResultFs.InvalidPathFormat.Value }, - { @"/", true, 1, Result.Success }, - { @"/.", false, 2, Result.Success }, - { @"/./", false, 0, Result.Success }, - { @"/..", false, 3, Result.Success }, - { @"//.", false, 0, Result.Success }, - { @"/ ..", true, 4, Result.Success }, - { @"/.. /", false, 5, Result.Success }, - { @"/. /.", false, 5, Result.Success }, - { @"/aa/bb/cc/dd/./.././../..", false, 0, Result.Success }, - { @"/aa/bb/cc/dd/./.././../../..", false, 0, Result.Success }, - { @"/./aa/./bb/./cc/./dd/.", false, 0, Result.Success }, - { @"/aa\bb/cc", true, 9, Result.Success }, - { @"/aa\bb/cc", true, 9, Result.Success }, - { @"/a|/bb/cc", false, 0, ResultFs.InvalidCharacter.Value }, - { @"/>a/bb/cc", false, 0, ResultFs.InvalidCharacter.Value }, - { @"/aa/.a/bb/cc", true, true, 9, Result.Success }, + { @"/aa/.a/bb/cc", false, false, 0, ResultFs.InvalidCharacter.Value }, + { @"/aa/. Date: Wed, 5 Jan 2022 00:21:45 -0700 Subject: [PATCH 31/34] Update DirectoryPathParser, PathUtility and WindowsPath for 13.1.0 --- src/LibHac/Fs/Common/DirectoryPathParser.cs | 24 +++++++++++++++-- src/LibHac/Fs/Common/PathUtility.cs | 29 +++++++++------------ src/LibHac/Fs/Common/WindowsPath.cs | 4 +-- src/LibHac/Fs/Fsa/MountUtility.cs | 6 ++--- src/LibHac/FsSystem/Utility.cs | 3 +-- 5 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/LibHac/Fs/Common/DirectoryPathParser.cs b/src/LibHac/Fs/Common/DirectoryPathParser.cs index d59673fe..6eea2a3e 100644 --- a/src/LibHac/Fs/Common/DirectoryPathParser.cs +++ b/src/LibHac/Fs/Common/DirectoryPathParser.cs @@ -3,8 +3,13 @@ using LibHac.Common; using LibHac.Diag; using static LibHac.Fs.StringTraits; -namespace LibHac.Fs.Common; +// ReSharper disable once CheckNamespace +namespace LibHac.Fs; +/// +/// Iterates through each directory in a path beginning with the root directory. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) [NonCopyableDisposable] public ref struct DirectoryPathParser { @@ -15,11 +20,26 @@ public ref struct DirectoryPathParser // Todo: Make private so we can use the GetCurrentPath method once lifetime tracking is better public Path CurrentPath; + public DirectoryPathParser() + { + _buffer = Span.Empty; + _replacedChar = 0; + _position = 0; + CurrentPath = new Path(); + } + public void Dispose() { CurrentPath.Dispose(); } + /// + /// Initializes this with a new . The + /// should not be a fixed path that was just initialized with + /// because we need it to have an allocated write buffer. + /// + /// The to iterate. Must have an allocated write buffer. + /// The of the operation. public Result Initialize(ref Path path) { Span pathBuffer = path.GetWriteBufferLength() != 0 ? path.GetWriteBuffer() : Span.Empty; @@ -116,4 +136,4 @@ public ref struct DirectoryPathParser _position = i; return entry; } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Common/PathUtility.cs b/src/LibHac/Fs/Common/PathUtility.cs index e6f1f708..a3984590 100644 --- a/src/LibHac/Fs/Common/PathUtility.cs +++ b/src/LibHac/Fs/Common/PathUtility.cs @@ -1,6 +1,7 @@ using System; using LibHac.Common; using LibHac.Diag; +using LibHac.Fs.Impl; using LibHac.FsSrv.Sf; using LibHac.Util; using static LibHac.Fs.StringTraits; @@ -11,7 +12,7 @@ namespace LibHac.Fs; /// /// Contains various utility functions for working with paths. /// -/// Based on FS 12.1.0 (nnSdk 12.3.1) +/// Based on FS 13.1.0 (nnSdk 13.4.0) public static class PathUtility { public static void Replace(Span buffer, byte currentChar, byte newChar) @@ -46,16 +47,6 @@ public static class PathUtility (path.Length < 3 || path[2] == NullTerminator || path[2] == DirectorySeparator); } - public static bool IsSeparator(byte c) - { - return c == DirectorySeparator; - } - - public static bool IsNul(byte c) - { - return c == NullTerminator; - } - public static Result ConvertToFspPath(out FspPath fspPath, ReadOnlySpan path) { UnsafeHelpers.SkipParamInit(out fspPath); @@ -65,7 +56,7 @@ public static class PathUtility if (length >= PathTool.EntryNameLengthMax + 1) return ResultFs.TooLongPath.Log(); - Result rc = PathFormatter.SkipMountName(out ReadOnlySpan pathWithoutMountName, out _, + Result rc = PathFormatter.SkipMountName(out ReadOnlySpan pathWithoutMountName, out int skipLength, new U8Span(path)); if (rc.IsFailure()) return rc; @@ -73,10 +64,16 @@ public static class PathUtility { Replace(SpanHelpers.AsByteSpan(ref fspPath).Slice(0, 0x300), AltDirectorySeparator, DirectorySeparator); } - else if (fspPath.Str[0] == DirectorySeparator && fspPath.Str[1] == DirectorySeparator) + else { - SpanHelpers.AsByteSpan(ref fspPath)[0] = AltDirectorySeparator; - SpanHelpers.AsByteSpan(ref fspPath)[1] = AltDirectorySeparator; + bool isHostOrNoMountName = skipLength == 0 || StringUtils.Compare(path, CommonMountNames.HostRootFileSystemMountName, + CommonMountNames.HostRootFileSystemMountName.Length) == 0; + + if (isHostOrNoMountName && WindowsPath.IsUncPath(path.Slice(skipLength), true, false)) + { + SpanHelpers.AsByteSpan(ref fspPath)[skipLength] = AltDirectorySeparator; + SpanHelpers.AsByteSpan(ref fspPath)[skipLength + 1] = AltDirectorySeparator; + } } return Result.Success; @@ -245,4 +242,4 @@ public static class PathUtility { return IsCurrentDirectory(path) || IsParentDirectory(path); } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Common/WindowsPath.cs b/src/LibHac/Fs/Common/WindowsPath.cs index a3bb2d63..57657537 100644 --- a/src/LibHac/Fs/Common/WindowsPath.cs +++ b/src/LibHac/Fs/Common/WindowsPath.cs @@ -10,7 +10,7 @@ namespace LibHac.Fs; /// /// Contains functions for working with Windows paths. /// -/// Based on FS 12.1.0 (nnSdk 12.3.1) +/// Based on FS 13.1.0 (nnSdk 13.4.0) public static class WindowsPath { private const int WindowsDriveLength = 2; @@ -254,4 +254,4 @@ public static class WindowsPath return Result.Success; } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/MountUtility.cs b/src/LibHac/Fs/Fsa/MountUtility.cs index 38096e77..699b8914 100644 --- a/src/LibHac/Fs/Fsa/MountUtility.cs +++ b/src/LibHac/Fs/Fsa/MountUtility.cs @@ -22,7 +22,7 @@ public static class MountUtility if (WindowsPath.IsWindowsDrive(path) || WindowsPath.IsUncPath(path)) { StringUtils.Copy(mountName.Name, CommonPaths.HostRootFileSystemMountName); - mountName.Name[PathTool.MountNameLengthMax] = StringTraits.NullTerminator; + mountName.Name[PathTool.MountNameLengthMax] = NullTerminator; subPath = path; return Result.Success; @@ -30,7 +30,7 @@ public static class MountUtility for (int i = 0; i <= maxMountLen; i++) { - if (path[i] == StringTraits.DriveSeparator) + if (path[i] == DriveSeparator) { mountLen = i; break; @@ -53,7 +53,7 @@ public static class MountUtility return ResultFs.InvalidPathFormat.Log(); path.Value.Slice(0, mountLen).CopyTo(mountName.Name); - mountName.Name[mountLen] = StringTraits.NullTerminator; + mountName.Name[mountLen] = NullTerminator; subPath = subPathTemp; return Result.Success; diff --git a/src/LibHac/FsSystem/Utility.cs b/src/LibHac/FsSystem/Utility.cs index 0cc151bb..ebfcf855 100644 --- a/src/LibHac/FsSystem/Utility.cs +++ b/src/LibHac/FsSystem/Utility.cs @@ -1,7 +1,6 @@ using System; using LibHac.Common; using LibHac.Fs; -using LibHac.Fs.Common; using LibHac.Fs.Fsa; using LibHac.Os; @@ -423,4 +422,4 @@ internal static class Utility outUniqueLock.Set(ref uniqueLock.Ref()); return Result.Success; } -} +} \ No newline at end of file From 123fc0655dfe6d8986a01fe17044acdf662dc153 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 5 Jan 2022 12:04:09 -0700 Subject: [PATCH 32/34] Update Fs.Path for 13.1.0 --- src/LibHac/Fs/Common/Path.cs | 73 +++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/src/LibHac/Fs/Common/Path.cs b/src/LibHac/Fs/Common/Path.cs index 49af516f..89dd8230 100644 --- a/src/LibHac/Fs/Common/Path.cs +++ b/src/LibHac/Fs/Common/Path.cs @@ -4,8 +4,8 @@ using System.Diagnostics; using LibHac.Common; using LibHac.Diag; using LibHac.Util; -using static LibHac.Fs.StringTraits; using static InlineIL.IL.Emit; +using static LibHac.Fs.StringTraits; // ReSharper disable once CheckNamespace namespace LibHac.Fs; @@ -21,12 +21,12 @@ public struct PathFlags public void AllowBackslash() => _value |= 1 << 4; public void AllowAllCharacters() => _value |= 1 << 5; - public bool IsWindowsPathAllowed() => (_value & (1 << 0)) != 0; - public bool IsRelativePathAllowed() => (_value & (1 << 1)) != 0; - public bool IsEmptyPathAllowed() => (_value & (1 << 2)) != 0; - public bool IsMountNameAllowed() => (_value & (1 << 3)) != 0; - public bool IsBackslashAllowed() => (_value & (1 << 4)) != 0; - public bool AreAllCharactersAllowed() => (_value & (1 << 5)) != 0; + public readonly bool IsWindowsPathAllowed() => (_value & (1 << 0)) != 0; + public readonly bool IsRelativePathAllowed() => (_value & (1 << 1)) != 0; + public readonly bool IsEmptyPathAllowed() => (_value & (1 << 2)) != 0; + public readonly bool IsMountNameAllowed() => (_value & (1 << 3)) != 0; + public readonly bool IsBackslashAllowed() => (_value & (1 << 4)) != 0; + public readonly bool AreAllCharactersAllowed() => (_value & (1 << 5)) != 0; } /// @@ -83,7 +83,7 @@ public static class PathExtensions /// ensure a write buffer is allocated and copy the input path to it. will /// directly use the input buffer without copying. If this method is used, the caller must ensure the path /// is normalized before passing it to . -/// Based on FS 12.1.0 (nnSdk 12.3.1) +/// Based on FS 13.1.0 (nnSdk 13.4.0) [DebuggerDisplay("{" + nameof(ToString) + "(),nq}")] [NonCopyableDisposable] public ref struct Path @@ -225,9 +225,9 @@ public ref struct Path } /// - /// Returns if + /// Returns if the has no buffer or has an empty string. /// - /// + /// if the current path is empty; otherwise . public readonly bool IsEmpty() { return _string.At(0) == 0; @@ -957,6 +957,59 @@ public ref struct Path return Result.Success; } + /// + /// Combines a and a path string into a single path. + /// + /// If is not empty, this 's IsNormalized flag will + /// be set to the value of 's flag. + /// Otherwise the flag will be set to . + /// The first path to combine. + /// The second path to combine. + /// : The operation was successful.
+ /// : The IsNormalized flag of + /// is not .
+ public Result Combine(in Path path1, ReadOnlySpan path2) + { + int path1Length = path1.GetLength(); + int path2Length = StringUtils.GetLength(path2); + + Result rc = Preallocate(path1Length + SeparatorLength + path2Length + NullTerminatorLength); + if (rc.IsFailure()) return rc; + + rc = Initialize(in path1); + if (rc.IsFailure()) return rc; + + rc = AppendChild(path2); + if (rc.IsFailure()) return rc; + + return Result.Success; + } + + /// + /// Combines a path string and a into a single path. + /// + /// This 's IsNormalized flag will + /// always be set to . + /// The first path to combine. + /// The second path to combine. + /// : The operation was successful. + public Result Combine(ReadOnlySpan path1, in Path path2) + { + int path1Length = StringUtils.GetLength(path1); + int path2Length = path2.GetLength(); + + Result rc = Preallocate(path1Length + SeparatorLength + path2Length + NullTerminatorLength); + if (rc.IsFailure()) return rc; + + rc = Initialize(path1); + if (rc.IsFailure()) return rc; + + rc = AppendChild(in path2); + if (rc.IsFailure()) return rc; + + return Result.Success; + } + /// /// Removes the last entry from this . /// From d412c15387b0f935b8fb3a38d4acb741d09f0f0b Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 5 Jan 2022 14:05:10 -0700 Subject: [PATCH 33/34] Add Utf8StringUtil and update MountUtility for 13.1.0 --- src/LibHac/Fs/Fsa/MountUtility.cs | 47 +++++++-- src/LibHac/Util/Utf8StringUtil.cs | 160 ++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+), 7 deletions(-) create mode 100644 src/LibHac/Util/Utf8StringUtil.cs diff --git a/src/LibHac/Fs/Fsa/MountUtility.cs b/src/LibHac/Fs/Fsa/MountUtility.cs index 699b8914..65a0cdb5 100644 --- a/src/LibHac/Fs/Fsa/MountUtility.cs +++ b/src/LibHac/Fs/Fsa/MountUtility.cs @@ -1,16 +1,35 @@ using System; +using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Diag; using LibHac.Fs.Impl; +using LibHac.Fs.Shim; using LibHac.Os; using LibHac.Util; -using static LibHac.Fs.StringTraits; using static LibHac.Fs.Impl.AccessLogStrings; +using static LibHac.Fs.StringTraits; namespace LibHac.Fs.Fsa; +/// +/// Contains functions for managing mounted file systems. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) public static class MountUtility { + /// + /// Gets the mount name and non-mounted path components from a path that has a mount name. + /// + /// If the method returns successfully, contains the mount name of the provided path; + /// otherwise the contents are undefined. + /// If the method returns successfully, contains the provided path without the + /// mount name; otherwise the contents are undefined. + /// The to process. + /// : The operation was successful.
+ /// : does not contain a sub path after + /// the mount name that begins with / or \.
+ /// : contains an invalid mount name + /// or does not have a mount name.
private static Result GetMountNameAndSubPath(out MountName mountName, out U8Span subPath, U8Span path) { UnsafeHelpers.SkipParamInit(out mountName); @@ -82,8 +101,7 @@ public static class MountUtility return false; } - // Todo: VerifyUtf8String - return true; + return Utf8StringUtil.VerifyUtf8String(name); } public static bool IsUsedReservedMountName(this FileSystemClientImpl fs, U8Span name) @@ -144,7 +162,12 @@ public static class MountUtility if (fileSystem.IsFileDataCacheAttachable()) { - // Todo: Data cache purge + using var fileDataCacheAccessor = new GlobalFileDataCacheAccessorReadableScopedPointer(); + + if (fs.TryGetGlobalFileDataCacheAccessor(ref Unsafe.AsRef(in fileDataCacheAccessor))) + { + fileSystem.PurgeFileDataCache(fileDataCacheAccessor.Get()); + } } fs.Unregister(mountName); @@ -226,13 +249,23 @@ public static class MountUtility public static Result ConvertToFsCommonPath(this FileSystemClient fs, U8SpanMutable commonPathBuffer, U8Span path) { + Result rc; + if (commonPathBuffer.IsNull()) - return ResultFs.NullptrArgument.Log(); + { + rc = ResultFs.NullptrArgument.Value; + fs.Impl.AbortIfNeeded(rc); + return rc; + } if (path.IsNull()) - return ResultFs.NullptrArgument.Log(); + { + rc = ResultFs.NullptrArgument.Value; + fs.Impl.AbortIfNeeded(rc); + return rc; + } - Result rc = GetMountNameAndSubPath(out MountName mountName, out U8Span subPath, path); + rc = GetMountNameAndSubPath(out MountName mountName, out U8Span subPath, path); fs.Impl.AbortIfNeeded(rc); if (rc.IsFailure()) return rc; diff --git a/src/LibHac/Util/Utf8StringUtil.cs b/src/LibHac/Util/Utf8StringUtil.cs new file mode 100644 index 00000000..c4fe798a --- /dev/null +++ b/src/LibHac/Util/Utf8StringUtil.cs @@ -0,0 +1,160 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using LibHac.Common; +using LibHac.Diag; + +namespace LibHac.Util; + +/// +/// Contains functions for verifying and copying UTF-8 strings. +/// +/// Based on nnSdk 13.4.0 +public static class Utf8StringUtil +{ + private static ReadOnlySpan CodePointByteLengthTable => new byte[] + { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + public static bool VerifyUtf8String(U8Span str) + { + return GetCodePointCountOfUtf8String(str) != -1; + } + + public static int GetCodePointCountOfUtf8String(U8Span str) + { + Assert.SdkRequiresGreater(str.Length, 0); + + ReadOnlySpan currentStr = str.Value; + int codePointCount = 0; + + while (currentStr.Length != 0) + { + int codePointByteLength = GetCodePointByteLength(currentStr[0]); + + if (codePointByteLength > currentStr.Length) + return -1; + + if (!VerifyCode(currentStr.Slice(0, codePointByteLength))) + return -1; + + currentStr = currentStr.Slice(codePointByteLength); + + codePointCount++; + } + + return codePointCount; + } + + public static int CopyUtf8String(Span output, ReadOnlySpan input, int maxCount) + { + Assert.SdkRequiresGreater(output.Length, 0); + Assert.SdkRequiresGreater(input.Length, 0); + Assert.SdkRequiresGreater(maxCount, 0); + + ReadOnlySpan currentInput = input; + int remainingCount = maxCount; + + while (remainingCount > 0 && currentInput.Length != 0) + { + // Verify the current code point + int codePointLength = GetCodePointByteLength(currentInput[0]); + if (codePointLength > currentInput.Length) + break; + + if (!VerifyCode(currentInput.Slice(0, codePointLength))) + break; + + // Ensure the output is large enough to hold the additional code point + int currentOutputLength = + Unsafe.ByteOffset(ref MemoryMarshal.GetReference(input), ref MemoryMarshal.GetReference(currentInput)) + .ToInt32() + codePointLength; + + if (currentOutputLength + 1 > output.Length) + break; + + // Advance to the next code point + currentInput = currentInput.Slice(codePointLength); + remainingCount--; + } + + // Copy the valid UTF-8 to the output buffer + int byteLength = Unsafe + .ByteOffset(ref MemoryMarshal.GetReference(input), ref MemoryMarshal.GetReference(currentInput)).ToInt32(); + + Assert.SdkAssert(byteLength + 1 <= output.Length); + + if (byteLength != 0) + input.Slice(0, byteLength).CopyTo(output); + + output[byteLength] = 0; + return byteLength; + } + + private static int GetCodePointByteLength(byte head) + { + return CodePointByteLengthTable[head]; + } + + private static bool IsValidTail(byte tail) + { + return (tail & 0xC0) == 0x80; + } + + private static bool VerifyCode(ReadOnlySpan str) + { + if (str.Length == 1) + return true; + + switch (str.Length) + { + case 2: + if (!IsValidTail(str[1])) + return false; + + break; + case 3: + if (str[0] == 0xE0 && (str[1] & 0x20) == 0) + return false; + + if (str[0] == 0xED && (str[1] & 0x20) != 0) + return false; + + if (!IsValidTail(str[1]) || !IsValidTail(str[2])) + return false; + + break; + case 4: + if (str[0] == 0xF0 && (str[1] & 0x30) == 0) + return false; + + if (str[0] == 0xFD && (str[1] & 0x30) != 0) + return false; + + if (!IsValidTail(str[1]) || !IsValidTail(str[2]) || !IsValidTail(str[3])) + return false; + + break; + default: + return false; + } + + return true; + } +} \ No newline at end of file From 6cae86eb9cdd9f028cce31d72d4ca52d61dbca1b Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Thu, 6 Jan 2022 11:12:07 -0700 Subject: [PATCH 34/34] Disable another warning on fixed array structs --- src/LibHac/Common/FixedArrays/Array1.cs | 8 ++++---- src/LibHac/Common/FixedArrays/Array1024.cs | 2 +- src/LibHac/Common/FixedArrays/Array11.cs | 8 ++++---- src/LibHac/Common/FixedArrays/Array112.cs | 2 +- src/LibHac/Common/FixedArrays/Array12.cs | 8 ++++---- src/LibHac/Common/FixedArrays/Array128.cs | 2 +- src/LibHac/Common/FixedArrays/Array14.cs | 2 +- src/LibHac/Common/FixedArrays/Array144.cs | 2 +- src/LibHac/Common/FixedArrays/Array15.cs | 2 +- src/LibHac/Common/FixedArrays/Array16.cs | 2 +- src/LibHac/Common/FixedArrays/Array18.cs | 2 +- src/LibHac/Common/FixedArrays/Array2.cs | 8 ++++---- src/LibHac/Common/FixedArrays/Array20.cs | 2 +- src/LibHac/Common/FixedArrays/Array2048.cs | 2 +- src/LibHac/Common/FixedArrays/Array24.cs | 2 +- src/LibHac/Common/FixedArrays/Array256.cs | 2 +- src/LibHac/Common/FixedArrays/Array26.cs | 2 +- src/LibHac/Common/FixedArrays/Array28.cs | 2 +- src/LibHac/Common/FixedArrays/Array3.cs | 8 ++++---- src/LibHac/Common/FixedArrays/Array3000.cs | 2 +- src/LibHac/Common/FixedArrays/Array32.cs | 2 +- src/LibHac/Common/FixedArrays/Array36.cs | 2 +- src/LibHac/Common/FixedArrays/Array37.cs | 2 +- src/LibHac/Common/FixedArrays/Array38.cs | 2 +- src/LibHac/Common/FixedArrays/Array4.cs | 8 ++++---- src/LibHac/Common/FixedArrays/Array400.cs | 2 +- src/LibHac/Common/FixedArrays/Array48.cs | 2 +- src/LibHac/Common/FixedArrays/Array5.cs | 8 ++++---- src/LibHac/Common/FixedArrays/Array512.cs | 2 +- src/LibHac/Common/FixedArrays/Array56.cs | 2 +- src/LibHac/Common/FixedArrays/Array6.cs | 8 ++++---- src/LibHac/Common/FixedArrays/Array60.cs | 2 +- src/LibHac/Common/FixedArrays/Array64.cs | 2 +- src/LibHac/Common/FixedArrays/Array65.cs | 2 +- src/LibHac/Common/FixedArrays/Array7.cs | 8 ++++---- src/LibHac/Common/FixedArrays/Array768.cs | 2 +- src/LibHac/Common/FixedArrays/Array769.cs | 2 +- src/LibHac/Common/FixedArrays/Array8.cs | 8 ++++---- src/LibHac/Common/FixedArrays/Array80.cs | 2 +- 39 files changed, 69 insertions(+), 69 deletions(-) diff --git a/src/LibHac/Common/FixedArrays/Array1.cs b/src/LibHac/Common/FixedArrays/Array1.cs index f468055f..2c82579a 100644 --- a/src/LibHac/Common/FixedArrays/Array1.cs +++ b/src/LibHac/Common/FixedArrays/Array1.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; @@ -8,12 +8,12 @@ public struct Array1 { public const int Length = 1; - private T _1; + private T _0; public ref T this[int i] => ref Items[i]; - public Span Items => SpanHelpers.CreateSpan(ref _1, Length); - public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length); + public Span Items => SpanHelpers.CreateSpan(ref _0, Length); + public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array1 value) => value.ItemsRo; diff --git a/src/LibHac/Common/FixedArrays/Array1024.cs b/src/LibHac/Common/FixedArrays/Array1024.cs index a1ef6dab..27895286 100644 --- a/src/LibHac/Common/FixedArrays/Array1024.cs +++ b/src/LibHac/Common/FixedArrays/Array1024.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array11.cs b/src/LibHac/Common/FixedArrays/Array11.cs index 251d6c68..55d19190 100644 --- a/src/LibHac/Common/FixedArrays/Array11.cs +++ b/src/LibHac/Common/FixedArrays/Array11.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; @@ -8,6 +8,7 @@ public struct Array11 { public const int Length = 11; + private T _0; private T _1; private T _2; private T _3; @@ -18,12 +19,11 @@ public struct Array11 private T _8; private T _9; private T _10; - private T _11; public ref T this[int i] => ref Items[i]; - public Span Items => SpanHelpers.CreateSpan(ref _1, Length); - public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length); + public Span Items => SpanHelpers.CreateSpan(ref _0, Length); + public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array11 value) => value.ItemsRo; diff --git a/src/LibHac/Common/FixedArrays/Array112.cs b/src/LibHac/Common/FixedArrays/Array112.cs index a25e7f98..d0bf171e 100644 --- a/src/LibHac/Common/FixedArrays/Array112.cs +++ b/src/LibHac/Common/FixedArrays/Array112.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array12.cs b/src/LibHac/Common/FixedArrays/Array12.cs index fc3d71ce..5a262655 100644 --- a/src/LibHac/Common/FixedArrays/Array12.cs +++ b/src/LibHac/Common/FixedArrays/Array12.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; @@ -8,6 +8,7 @@ public struct Array12 { public const int Length = 12; + private T _0; private T _1; private T _2; private T _3; @@ -19,12 +20,11 @@ public struct Array12 private T _9; private T _10; private T _11; - private T _12; public ref T this[int i] => ref Items[i]; - public Span Items => SpanHelpers.CreateSpan(ref _1, Length); - public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length); + public Span Items => SpanHelpers.CreateSpan(ref _0, Length); + public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array12 value) => value.ItemsRo; diff --git a/src/LibHac/Common/FixedArrays/Array128.cs b/src/LibHac/Common/FixedArrays/Array128.cs index ce253129..8d848188 100644 --- a/src/LibHac/Common/FixedArrays/Array128.cs +++ b/src/LibHac/Common/FixedArrays/Array128.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array14.cs b/src/LibHac/Common/FixedArrays/Array14.cs index 8d309393..28263fd8 100644 --- a/src/LibHac/Common/FixedArrays/Array14.cs +++ b/src/LibHac/Common/FixedArrays/Array14.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array144.cs b/src/LibHac/Common/FixedArrays/Array144.cs index b727dcae..ec10ebeb 100644 --- a/src/LibHac/Common/FixedArrays/Array144.cs +++ b/src/LibHac/Common/FixedArrays/Array144.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array15.cs b/src/LibHac/Common/FixedArrays/Array15.cs index 6c7d6d93..8a21ab04 100644 --- a/src/LibHac/Common/FixedArrays/Array15.cs +++ b/src/LibHac/Common/FixedArrays/Array15.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array16.cs b/src/LibHac/Common/FixedArrays/Array16.cs index 15ad2ad6..732ee8d0 100644 --- a/src/LibHac/Common/FixedArrays/Array16.cs +++ b/src/LibHac/Common/FixedArrays/Array16.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array18.cs b/src/LibHac/Common/FixedArrays/Array18.cs index c69b5990..5343b47e 100644 --- a/src/LibHac/Common/FixedArrays/Array18.cs +++ b/src/LibHac/Common/FixedArrays/Array18.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array2.cs b/src/LibHac/Common/FixedArrays/Array2.cs index 397c1ed3..fcd67bd2 100644 --- a/src/LibHac/Common/FixedArrays/Array2.cs +++ b/src/LibHac/Common/FixedArrays/Array2.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; @@ -8,13 +8,13 @@ public struct Array2 { public const int Length = 2; + private T _0; private T _1; - private T _2; public ref T this[int i] => ref Items[i]; - public Span Items => SpanHelpers.CreateSpan(ref _1, Length); - public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length); + public Span Items => SpanHelpers.CreateSpan(ref _0, Length); + public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array2 value) => value.ItemsRo; diff --git a/src/LibHac/Common/FixedArrays/Array20.cs b/src/LibHac/Common/FixedArrays/Array20.cs index 6d0f12c1..9d07fdfe 100644 --- a/src/LibHac/Common/FixedArrays/Array20.cs +++ b/src/LibHac/Common/FixedArrays/Array20.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array2048.cs b/src/LibHac/Common/FixedArrays/Array2048.cs index bfe46d18..704c5957 100644 --- a/src/LibHac/Common/FixedArrays/Array2048.cs +++ b/src/LibHac/Common/FixedArrays/Array2048.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array24.cs b/src/LibHac/Common/FixedArrays/Array24.cs index d4d833b4..7df3e247 100644 --- a/src/LibHac/Common/FixedArrays/Array24.cs +++ b/src/LibHac/Common/FixedArrays/Array24.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array256.cs b/src/LibHac/Common/FixedArrays/Array256.cs index e6b7b923..fbd9cddf 100644 --- a/src/LibHac/Common/FixedArrays/Array256.cs +++ b/src/LibHac/Common/FixedArrays/Array256.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array26.cs b/src/LibHac/Common/FixedArrays/Array26.cs index d447ae69..0d699aa2 100644 --- a/src/LibHac/Common/FixedArrays/Array26.cs +++ b/src/LibHac/Common/FixedArrays/Array26.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array28.cs b/src/LibHac/Common/FixedArrays/Array28.cs index 24b8beb7..2edb8d8c 100644 --- a/src/LibHac/Common/FixedArrays/Array28.cs +++ b/src/LibHac/Common/FixedArrays/Array28.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array3.cs b/src/LibHac/Common/FixedArrays/Array3.cs index 8a953a33..3a75362c 100644 --- a/src/LibHac/Common/FixedArrays/Array3.cs +++ b/src/LibHac/Common/FixedArrays/Array3.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; @@ -8,14 +8,14 @@ public struct Array3 { public const int Length = 3; + private T _0; private T _1; private T _2; - private T _3; public ref T this[int i] => ref Items[i]; - public Span Items => SpanHelpers.CreateSpan(ref _1, Length); - public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length); + public Span Items => SpanHelpers.CreateSpan(ref _0, Length); + public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array3 value) => value.ItemsRo; diff --git a/src/LibHac/Common/FixedArrays/Array3000.cs b/src/LibHac/Common/FixedArrays/Array3000.cs index bc1fd263..63d99435 100644 --- a/src/LibHac/Common/FixedArrays/Array3000.cs +++ b/src/LibHac/Common/FixedArrays/Array3000.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array32.cs b/src/LibHac/Common/FixedArrays/Array32.cs index 1004dc18..867c8baa 100644 --- a/src/LibHac/Common/FixedArrays/Array32.cs +++ b/src/LibHac/Common/FixedArrays/Array32.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array36.cs b/src/LibHac/Common/FixedArrays/Array36.cs index dae2d49f..1f4cf61b 100644 --- a/src/LibHac/Common/FixedArrays/Array36.cs +++ b/src/LibHac/Common/FixedArrays/Array36.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array37.cs b/src/LibHac/Common/FixedArrays/Array37.cs index 4006cfb4..7d2fba30 100644 --- a/src/LibHac/Common/FixedArrays/Array37.cs +++ b/src/LibHac/Common/FixedArrays/Array37.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array38.cs b/src/LibHac/Common/FixedArrays/Array38.cs index 2c6b8e90..25be2f85 100644 --- a/src/LibHac/Common/FixedArrays/Array38.cs +++ b/src/LibHac/Common/FixedArrays/Array38.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array4.cs b/src/LibHac/Common/FixedArrays/Array4.cs index ea867bec..2e15a502 100644 --- a/src/LibHac/Common/FixedArrays/Array4.cs +++ b/src/LibHac/Common/FixedArrays/Array4.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; @@ -8,15 +8,15 @@ public struct Array4 { public const int Length = 4; + private T _0; private T _1; private T _2; private T _3; - private T _4; public ref T this[int i] => ref Items[i]; - public Span Items => SpanHelpers.CreateSpan(ref _1, Length); - public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length); + public Span Items => SpanHelpers.CreateSpan(ref _0, Length); + public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array4 value) => value.ItemsRo; diff --git a/src/LibHac/Common/FixedArrays/Array400.cs b/src/LibHac/Common/FixedArrays/Array400.cs index 4287386a..013bd114 100644 --- a/src/LibHac/Common/FixedArrays/Array400.cs +++ b/src/LibHac/Common/FixedArrays/Array400.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array48.cs b/src/LibHac/Common/FixedArrays/Array48.cs index ce3268c4..b81e4ea3 100644 --- a/src/LibHac/Common/FixedArrays/Array48.cs +++ b/src/LibHac/Common/FixedArrays/Array48.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array5.cs b/src/LibHac/Common/FixedArrays/Array5.cs index b15e3470..97928b7a 100644 --- a/src/LibHac/Common/FixedArrays/Array5.cs +++ b/src/LibHac/Common/FixedArrays/Array5.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; @@ -8,16 +8,16 @@ public struct Array5 { public const int Length = 5; + private T _0; private T _1; private T _2; private T _3; private T _4; - private T _5; public ref T this[int i] => ref Items[i]; - public Span Items => SpanHelpers.CreateSpan(ref _1, Length); - public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length); + public Span Items => SpanHelpers.CreateSpan(ref _0, Length); + public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array5 value) => value.ItemsRo; diff --git a/src/LibHac/Common/FixedArrays/Array512.cs b/src/LibHac/Common/FixedArrays/Array512.cs index 1a617bb2..2caf57dc 100644 --- a/src/LibHac/Common/FixedArrays/Array512.cs +++ b/src/LibHac/Common/FixedArrays/Array512.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array56.cs b/src/LibHac/Common/FixedArrays/Array56.cs index b03d02cb..fa94bbff 100644 --- a/src/LibHac/Common/FixedArrays/Array56.cs +++ b/src/LibHac/Common/FixedArrays/Array56.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array6.cs b/src/LibHac/Common/FixedArrays/Array6.cs index ac181645..dff18fbc 100644 --- a/src/LibHac/Common/FixedArrays/Array6.cs +++ b/src/LibHac/Common/FixedArrays/Array6.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; @@ -8,17 +8,17 @@ public struct Array6 { public const int Length = 6; + private T _0; private T _1; private T _2; private T _3; private T _4; private T _5; - private T _6; public ref T this[int i] => ref Items[i]; - public Span Items => SpanHelpers.CreateSpan(ref _1, Length); - public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length); + public Span Items => SpanHelpers.CreateSpan(ref _0, Length); + public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array6 value) => value.ItemsRo; diff --git a/src/LibHac/Common/FixedArrays/Array60.cs b/src/LibHac/Common/FixedArrays/Array60.cs index 3cab17e7..f66c3b4f 100644 --- a/src/LibHac/Common/FixedArrays/Array60.cs +++ b/src/LibHac/Common/FixedArrays/Array60.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array64.cs b/src/LibHac/Common/FixedArrays/Array64.cs index 7f63f75a..a395eccd 100644 --- a/src/LibHac/Common/FixedArrays/Array64.cs +++ b/src/LibHac/Common/FixedArrays/Array64.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array65.cs b/src/LibHac/Common/FixedArrays/Array65.cs index 08882816..bdd5896d 100644 --- a/src/LibHac/Common/FixedArrays/Array65.cs +++ b/src/LibHac/Common/FixedArrays/Array65.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array7.cs b/src/LibHac/Common/FixedArrays/Array7.cs index a64b7207..f87a13dc 100644 --- a/src/LibHac/Common/FixedArrays/Array7.cs +++ b/src/LibHac/Common/FixedArrays/Array7.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; @@ -8,18 +8,18 @@ public struct Array7 { public const int Length = 7; + private T _0; private T _1; private T _2; private T _3; private T _4; private T _5; private T _6; - private T _7; public ref T this[int i] => ref Items[i]; - public Span Items => SpanHelpers.CreateSpan(ref _1, Length); - public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length); + public Span Items => SpanHelpers.CreateSpan(ref _0, Length); + public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array7 value) => value.ItemsRo; diff --git a/src/LibHac/Common/FixedArrays/Array768.cs b/src/LibHac/Common/FixedArrays/Array768.cs index 1fb566c2..ded4c986 100644 --- a/src/LibHac/Common/FixedArrays/Array768.cs +++ b/src/LibHac/Common/FixedArrays/Array768.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array769.cs b/src/LibHac/Common/FixedArrays/Array769.cs index 78fbad09..0872ad7c 100644 --- a/src/LibHac/Common/FixedArrays/Array769.cs +++ b/src/LibHac/Common/FixedArrays/Array769.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/LibHac/Common/FixedArrays/Array8.cs b/src/LibHac/Common/FixedArrays/Array8.cs index ac664470..d82bc02d 100644 --- a/src/LibHac/Common/FixedArrays/Array8.cs +++ b/src/LibHac/Common/FixedArrays/Array8.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; @@ -8,6 +8,7 @@ public struct Array8 { public const int Length = 8; + private T _0; private T _1; private T _2; private T _3; @@ -15,12 +16,11 @@ public struct Array8 private T _5; private T _6; private T _7; - private T _8; public ref T this[int i] => ref Items[i]; - public Span Items => SpanHelpers.CreateSpan(ref _1, Length); - public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length); + public Span Items => SpanHelpers.CreateSpan(ref _0, Length); + public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlySpan(in Array8 value) => value.ItemsRo; diff --git a/src/LibHac/Common/FixedArrays/Array80.cs b/src/LibHac/Common/FixedArrays/Array80.cs index 186efd72..7cb2c418 100644 --- a/src/LibHac/Common/FixedArrays/Array80.cs +++ b/src/LibHac/Common/FixedArrays/Array80.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS0169, IDE0051 // Remove unused private members +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices;