diff --git a/src/LibHac/FsSystem/Aes128CtrExStorage.cs b/src/LibHac/FsSystem/Aes128CtrExStorage.cs index 42446e43..79f305b1 100644 --- a/src/LibHac/FsSystem/Aes128CtrExStorage.cs +++ b/src/LibHac/FsSystem/Aes128CtrExStorage.cs @@ -9,7 +9,7 @@ namespace LibHac.FsSystem { public static readonly int NodeSize = 1024 * 16; - private BucketTree2 Table { get; } = new BucketTree2(); + private BucketTree Table { get; } = new BucketTree(); private readonly object _locker = new object(); @@ -34,7 +34,7 @@ namespace LibHac.FsSystem if (destination.Length == 0) return Result.Success; - var visitor = new BucketTree2.Visitor(); + var visitor = new BucketTree.Visitor(); Result rc = Table.Find(ref visitor, offset); if (rc.IsFailure()) return rc; diff --git a/src/LibHac/FsSystem/BucketTree.cs b/src/LibHac/FsSystem/BucketTree.cs index 85f6608e..a9cb125b 100644 --- a/src/LibHac/FsSystem/BucketTree.cs +++ b/src/LibHac/FsSystem/BucketTree.cs @@ -1,109 +1,738 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using LibHac.Common; +using LibHac.Diag; using LibHac.Fs; namespace LibHac.FsSystem { - public class BucketTree where T : BucketTreeEntry, new() + public partial class BucketTree { - private const int BucketAlignment = 0x4000; - public BucketTreeBucket BucketOffsets { get; } - public BucketTreeBucket[] Buckets { get; } + private const uint ExpectedMagic = 0x52544B42; // BKTR + private const int MaxVersion = 1; - public BucketTree(IStorage data) + private const int NodeSizeMin = 1024; + private const int NodeSizeMax = 1024 * 512; + + private static int NodeHeaderSize => Unsafe.SizeOf(); + + private SubStorage2 NodeStorage { get; set; } + private SubStorage2 EntryStorage { get; set; } + + private NodeBuffer _nodeL1 = new NodeBuffer(); + + private long NodeSize { get; set; } + private long EntrySize { get; set; } + private int OffsetCount { get; set; } + private int EntrySetCount { get; set; } + private long StartOffset { get; set; } + private long EndOffset { get; set; } + + public Result Initialize(SubStorage2 nodeStorage, SubStorage2 entryStorage, int nodeSize, int entrySize, + int entryCount) { - var reader = new BinaryReader(data.AsStream()); + Assert.AssertTrue(entrySize >= sizeof(long)); + Assert.AssertTrue(nodeSize >= entrySize + Unsafe.SizeOf()); + Assert.AssertTrue(NodeSizeMin <= nodeSize && nodeSize <= NodeSizeMax); + Assert.AssertTrue(Util.IsPowerOfTwo(nodeSize)); + Assert.AssertTrue(!IsInitialized()); - BucketOffsets = new BucketTreeBucket(reader); + // Ensure valid entry count. + if (entryCount <= 0) + return ResultFs.InvalidArgument.Log(); - Buckets = new BucketTreeBucket[BucketOffsets.EntryCount]; + // Allocate node. + if (!_nodeL1.Allocate(nodeSize)) + return ResultFs.BufferAllocationFailed.Log(); - for (int i = 0; i < BucketOffsets.EntryCount; i++) + bool needFree = true; + try { - reader.BaseStream.Position = (i + 1) * BucketAlignment; - Buckets[i] = new BucketTreeBucket(reader); + // Read node. + Result rc = nodeStorage.Read(0, _nodeL1.GetBuffer()); + if (rc.IsFailure()) return rc; + + // Verify node. + rc = _nodeL1.GetHeader().Verify(0, nodeSize, sizeof(long)); + if (rc.IsFailure()) return rc; + + // Validate offsets. + int offsetCount = GetOffsetCount(nodeSize); + int entrySetCount = GetEntrySetCount(nodeSize, entrySize, entryCount); + BucketTreeNode node = _nodeL1.GetNode(); + + long startOffset; + if (offsetCount < entrySetCount && node.GetCount() < offsetCount) + { + startOffset = node.GetL2BeginOffset(); + } + else + { + startOffset = node.GetBeginOffset(); + } + + long endOffset = node.GetEndOffset(); + + if (startOffset < 0 || startOffset > node.GetBeginOffset() || startOffset >= endOffset) + return ResultFs.InvalidBucketTreeEntryOffset.Log(); + + NodeStorage = nodeStorage; + EntryStorage = entryStorage; + NodeSize = nodeSize; + EntrySize = entrySize; + OffsetCount = offsetCount; + EntrySetCount = entrySetCount; + StartOffset = startOffset; + EndOffset = endOffset; + + needFree = false; + + return Result.Success; + } + finally + { + if (needFree) + _nodeL1.Free(); } } - public List GetEntryList() - { - List list = Buckets.SelectMany(x => x.Entries).ToList(); + public bool IsInitialized() => NodeSize > 0; + public bool IsEmpty() => EntrySize == 0; - for (int i = 0; i < list.Count - 1; i++) + public long GetStart() => StartOffset; + public long GetEnd() => EndOffset; + public long GetSize() => EndOffset - StartOffset; + + public bool Includes(long offset) + { + return StartOffset <= offset && offset < EndOffset; + } + + public bool Includes(long offset, long size) + { + return size > 0 && StartOffset <= offset && size <= EndOffset - offset; + } + + public Result Find(ref Visitor visitor, long virtualAddress) + { + Assert.AssertTrue(IsInitialized()); + + if (virtualAddress < 0) + return ResultFs.InvalidOffset.Log(); + + if (IsEmpty()) + return ResultFs.OutOfRange.Log(); + + Result rc = visitor.Initialize(this); + if (rc.IsFailure()) return rc; + + return visitor.Find(virtualAddress); + } + + public static int QueryHeaderStorageSize() => Unsafe.SizeOf
(); + + public static long QueryNodeStorageSize(long nodeSize, long entrySize, int entryCount) + { + Assert.AssertTrue(entrySize >= sizeof(long)); + Assert.AssertTrue(nodeSize >= entrySize + Unsafe.SizeOf()); + Assert.AssertTrue(NodeSizeMin <= nodeSize && nodeSize <= NodeSizeMax); + Assert.AssertTrue(Util.IsPowerOfTwo(nodeSize)); + Assert.AssertTrue(entryCount >= 0); + + if (entryCount <= 0) + return 0; + + return (1 + GetNodeL2Count(nodeSize, entrySize, entryCount)) * nodeSize; + } + + public static long QueryEntryStorageSize(long nodeSize, long entrySize, int entryCount) + { + Assert.AssertTrue(entrySize >= sizeof(long)); + Assert.AssertTrue(nodeSize >= entrySize + Unsafe.SizeOf()); + Assert.AssertTrue(NodeSizeMin <= nodeSize && nodeSize <= NodeSizeMax); + Assert.AssertTrue(Util.IsPowerOfTwo(nodeSize)); + Assert.AssertTrue(entryCount >= 0); + + if (entryCount <= 0) + return 0; + + return GetEntrySetCount(nodeSize, entrySize, entryCount) * nodeSize; + } + + private static int GetEntryCount(long nodeSize, long entrySize) + { + return (int)((nodeSize - Unsafe.SizeOf()) / entrySize); + } + + private static int GetOffsetCount(long nodeSize) + { + return (int)((nodeSize - Unsafe.SizeOf()) / sizeof(long)); + } + + private static int GetEntrySetCount(long nodeSize, long entrySize, int entryCount) + { + int entryCountPerNode = GetEntryCount(nodeSize, entrySize); + return Util.DivideByRoundUp(entryCount, entryCountPerNode); + } + + public static int GetNodeL2Count(long nodeSize, long entrySize, int entryCount) + { + int offsetCountPerNode = GetOffsetCount(nodeSize); + int entrySetCount = GetEntrySetCount(nodeSize, entrySize, entryCount); + + if (entrySetCount <= offsetCountPerNode) + return 0; + + int nodeL2Count = Util.DivideByRoundUp(entrySetCount, offsetCountPerNode); + Abort.DoAbortUnless(nodeL2Count <= offsetCountPerNode); + + return Util.DivideByRoundUp(entrySetCount - (offsetCountPerNode - (nodeL2Count - 1)), offsetCountPerNode); + } + + private static long GetBucketTreeEntryOffset(long entrySetOffset, long entrySize, int entryIndex) + { + return entrySetOffset + Unsafe.SizeOf() + entryIndex * entrySize; + } + + private static long GetBucketTreeEntryOffset(int entrySetIndex, long nodeSize, long entrySize, int entryIndex) + { + return GetBucketTreeEntryOffset(entrySetIndex * nodeSize, entrySize, entryIndex); + } + + private bool IsExistL2() => OffsetCount < EntrySetCount; + private bool IsExistOffsetL2OnL1() => IsExistL2() && _nodeL1.GetHeader().Count < OffsetCount; + + private long GetEntrySetIndex(int nodeIndex, int offsetIndex) + { + return (OffsetCount - _nodeL1.GetHeader().Count) + (OffsetCount * nodeIndex) + offsetIndex; + } + + public struct Header + { + public uint Magic; + public uint Version; + public int EntryCount; +#pragma warning disable 414 + private int _reserved; +#pragma warning restore 414 + + public void Format(int entryCount) { - list[i].Next = list[i + 1]; - list[i].OffsetEnd = list[i + 1].Offset; + Magic = ExpectedMagic; + Version = MaxVersion; + EntryCount = entryCount; + _reserved = 0; } - list[list.Count - 1].OffsetEnd = BucketOffsets.OffsetEnd; - - return list; - } - } - - public class BucketTreeBucket where T : BucketTreeEntry, new() - { - public int Index; - public int EntryCount; - public long OffsetEnd; - public T[] Entries; - - public BucketTreeBucket(BinaryReader reader) - { - Index = reader.ReadInt32(); - EntryCount = reader.ReadInt32(); - OffsetEnd = reader.ReadInt64(); - Entries = new T[EntryCount]; - - for (int i = 0; i < EntryCount; i++) + public Result Verify() { - Entries[i] = new T().Read(reader); + if (Magic != ExpectedMagic) + return ResultFs.InvalidBucketTreeSignature.Log(); + + if (EntryCount < 0) + return ResultFs.InvalidBucketTreeEntryCount.Log(); + + if (Version > MaxVersion) + return ResultFs.UnsupportedVersion.Log(); + + return Result.Success; } } - } - public abstract class BucketTreeEntry where T : BucketTreeEntry - { - public long Offset { get; set; } - public long OffsetEnd { get; set; } - public T Next { get; set; } - - protected abstract void ReadSpecific(BinaryReader reader); - internal T Read(BinaryReader reader) + public struct NodeHeader { - Offset = reader.ReadInt64(); - ReadSpecific(reader); - return (T)this; + public int Index; + public int Count; + public long Offset; + + public Result Verify(int nodeIndex, long nodeSize, long entrySize) + { + if (Index != nodeIndex) + return ResultFs.InvalidBucketTreeNodeIndex.Log(); + + if (entrySize == 0 || nodeSize < entrySize + NodeHeaderSize) + return ResultFs.InvalidSize.Log(); + + long maxEntryCount = (nodeSize - NodeHeaderSize) / entrySize; + + if (Count <= 0 || maxEntryCount < Count) + return ResultFs.InvalidBucketTreeNodeEntryCount.Log(); + + if (Offset < 0) + return ResultFs.InvalidBucketTreeNodeOffset.Log(); + + return Result.Success; + } } - } - public class OffsetEntry : BucketTreeEntry - { - protected override void ReadSpecific(BinaryReader reader) { } - } - - public class AesSubsectionEntry : BucketTreeEntry - { - public uint Field8 { get; set; } - public uint Counter { get; set; } - - protected override void ReadSpecific(BinaryReader reader) + private struct NodeBuffer { - Field8 = reader.ReadUInt32(); - Counter = reader.ReadUInt32(); + // Use long to ensure alignment + private long[] _header; + + public bool Allocate(int nodeSize) + { + Assert.AssertTrue(_header == null); + + _header = new long[nodeSize / sizeof(long)]; + + return _header != null; + } + + public void Free() + { + _header = null; + } + + public void FillZero() + { + if (_header != null) + { + Array.Fill(_header, 0); + } + } + + public ref NodeHeader GetHeader() + { + Assert.AssertTrue(_header.Length / sizeof(long) >= Unsafe.SizeOf()); + + return ref Unsafe.As(ref _header[0]); + } + + public Span GetBuffer() + { + return MemoryMarshal.AsBytes(_header.AsSpan()); + } + + public BucketTreeNode GetNode() where TEntry : unmanaged + { + return new BucketTreeNode(GetBuffer()); + } } - } - public class RelocationEntry : BucketTreeEntry - { - public long SourceOffset { get; set; } - public int SourceIndex { get; set; } - - protected override void ReadSpecific(BinaryReader reader) + public readonly ref struct BucketTreeNode where TEntry : unmanaged { - SourceOffset = reader.ReadInt64(); - SourceIndex = reader.ReadInt32(); + private readonly Span _buffer; + + public BucketTreeNode(Span buffer) + { + _buffer = buffer; + + Assert.AssertTrue(_buffer.Length >= Unsafe.SizeOf()); + Assert.AssertTrue(_buffer.Length >= Unsafe.SizeOf() + GetHeader().Count * Unsafe.SizeOf()); + } + + public int GetCount() => GetHeader().Count; + + public ReadOnlySpan GetArray() => GetWritableArray(); + internal Span GetWritableArray() => GetWritableArray(); + + public long GetBeginOffset() => GetArray()[0]; + public long GetEndOffset() => GetHeader().Offset; + public long GetL2BeginOffset() => GetArray()[GetCount()]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan GetArray() where TElement : unmanaged + { + return GetWritableArray(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Span GetWritableArray() where TElement : unmanaged + { + return MemoryMarshal.Cast(_buffer.Slice(Unsafe.SizeOf())); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ref NodeHeader GetHeader() + { + return ref Unsafe.As(ref MemoryMarshal.GetReference(_buffer)); + } + } + + public ref struct Visitor + { + private BucketTree Tree { get; set; } + private byte[] Entry { get; set; } + private int EntryIndex { get; set; } + private int EntrySetCount { get; set; } + private EntrySetHeader _entrySet; + + [StructLayout(LayoutKind.Explicit)] + private struct EntrySetHeader + { + // ReSharper disable once MemberHidesStaticFromOuterClass + [FieldOffset(0)] public NodeHeader Header; + [FieldOffset(0)] public EntrySetInfo Info; + + [StructLayout(LayoutKind.Sequential)] + public struct EntrySetInfo + { + public int Index; + public int Count; + public long End; + public long Start; + } + } + + public Result Initialize(BucketTree tree) + { + Assert.AssertTrue(tree != null); + Assert.AssertTrue(Tree == null || tree == Tree); + + if (Entry == null) + { + Entry = new byte[tree.EntrySize]; + Tree = tree; + EntryIndex = -1; + } + + return Result.Success; + } + + public void Dispose() + { + // todo: try using shared arrays + } + + public bool IsValid() => EntryIndex >= 0; + + public bool CanMoveNext() + { + return IsValid() && (EntryIndex + 1 < _entrySet.Info.Count || _entrySet.Info.Index + 1 < EntrySetCount); + } + + public bool CanMovePrevious() + { + return IsValid() && (EntryIndex > 0 || _entrySet.Info.Index > 0); + } + + public ref T Get() where T : unmanaged + { + return ref MemoryMarshal.Cast(Entry)[0]; + } + + public Result MoveNext() + { + Result rc; + + if (!IsValid()) + return ResultFs.OutOfRange.Log(); + + int entryIndex = EntryIndex + 1; + + // Invalidate our index, and read the header for the next index. + if (entryIndex == _entrySet.Info.Count) + { + int entrySetIndex = _entrySet.Info.Index + 1; + if (entrySetIndex >= EntrySetCount) + return ResultFs.OutOfRange.Log(); + + EntryIndex = -1; + + long end = _entrySet.Info.End; + + long entrySetSize = Tree.NodeSize; + long entrySetOffset = entrySetIndex * entrySetSize; + + rc = Tree.EntryStorage.Read(entrySetOffset, SpanHelpers.AsByteSpan(ref _entrySet)); + if (rc.IsFailure()) return rc; + + rc = _entrySet.Header.Verify(entrySetIndex, entrySetSize, Tree.EntrySize); + if (rc.IsFailure()) return rc; + + if (_entrySet.Info.Start != end || _entrySet.Info.Start >= _entrySet.Info.End) + return ResultFs.InvalidBucketTreeEntrySetOffset.Log(); + + entryIndex = 0; + } + else + { + EntryIndex = 1; + } + + // Read the new entry + long entrySize = Tree.EntrySize; + long entryOffset = GetBucketTreeEntryOffset(_entrySet.Info.Index, Tree.NodeSize, entrySize, entryIndex); + + rc = Tree.EntryStorage.Read(entryOffset, Entry); + if (rc.IsFailure()) return rc; + + // Note that we changed index. + EntryIndex = entryIndex; + return Result.Success; + } + + public Result MovePrevious() + { + Result rc; + + if (!IsValid()) + return ResultFs.OutOfRange.Log(); + + int entryIndex = EntryIndex; + + if (entryIndex == 0) + { + if (_entrySet.Info.Index <= 0) + return ResultFs.OutOfRange.Log(); + + EntryIndex = -1; + + long start = _entrySet.Info.Start; + + long entrySetSize = Tree.NodeSize; + int entrySetIndex = _entrySet.Info.Index - 1; + long entrySetOffset = entrySetIndex * entrySetSize; + + rc = Tree.EntryStorage.Read(entrySetOffset, SpanHelpers.AsByteSpan(ref _entrySet)); + if (rc.IsFailure()) return rc; + + rc = _entrySet.Header.Verify(entrySetIndex, entrySetSize, Tree.EntrySize); + if (rc.IsFailure()) return rc; + + if (_entrySet.Info.End != start || _entrySet.Info.Start >= _entrySet.Info.End) + return ResultFs.InvalidBucketTreeEntrySetOffset.Log(); + + entryIndex = _entrySet.Info.Count; + } + else + { + EntryIndex = -1; + } + + // Read the new entry + long entrySize = Tree.EntrySize; + long entryOffset = GetBucketTreeEntryOffset(_entrySet.Info.Index, Tree.NodeSize, entrySize, entryIndex); + + rc = Tree.EntryStorage.Read(entryOffset, Entry); + if (rc.IsFailure()) return rc; + + // Note that we changed index. + EntryIndex = entryIndex; + return Result.Success; + } + + public Result Find(long virtualAddress) + { + Result rc; + + // Get the node. + BucketTreeNode node = Tree._nodeL1.GetNode(); + + if (virtualAddress >= node.GetEndOffset()) + return ResultFs.OutOfRange.Log(); + + int entrySetIndex; + + if (Tree.IsExistOffsetL2OnL1() && virtualAddress < node.GetBeginOffset()) + { + // The portion of the L2 offsets containing our target offset is stored in the L1 node + ReadOnlySpan offsets = node.GetArray().Slice(node.GetCount()); + int index = offsets.BinarySearch(virtualAddress); + if (index < 0) index = (~index) - 1; + + if (index < 0) + return ResultFs.OutOfRange.Log(); + + entrySetIndex = index; + } + else + { + ReadOnlySpan offsets = node.GetArray().Slice(0, node.GetCount()); + int index = offsets.BinarySearch(virtualAddress); + if (index < 0) index = (~index) - 1; + + if (index < 0) + return ResultFs.OutOfRange.Log(); + + if (Tree.IsExistL2()) + { + if (index >= Tree.OffsetCount) + return ResultFs.InvalidBucketTreeNodeOffset.Log(); + + rc = FindEntrySet(out entrySetIndex, virtualAddress, index); + if (rc.IsFailure()) return rc; + } + else + { + entrySetIndex = index; + } + } + + // Validate the entry set index. + if (entrySetIndex < 0 || entrySetIndex >= Tree.EntrySetCount) + return ResultFs.InvalidBucketTreeNodeOffset.Log(); + + // Find the entry. + rc = FindEntry(virtualAddress, entrySetIndex); + if (rc.IsFailure()) return rc; + + // Set count. + EntrySetCount = Tree.EntrySetCount; + return Result.Success; + } + + private Result FindEntrySet(out int entrySetIndex, long virtualAddress, int nodeIndex) + { + long nodeSize = Tree.NodeSize; + + using (var rented = new RentedArray((int)nodeSize)) + { + return FindEntrySetWithBuffer(out entrySetIndex, virtualAddress, nodeIndex, rented.Span); + } + } + + private Result FindEntrySetWithBuffer(out int outIndex, long virtualAddress, int nodeIndex, + Span buffer) + { + outIndex = default; + + // Calculate node extents. + long nodeSize = Tree.NodeSize; + long nodeOffset = (nodeIndex + 1) * nodeSize; + SubStorage2 storage = Tree.NodeStorage; + + // Read the node. + Result rc = storage.Read(nodeOffset, buffer.Slice(0, (int)nodeSize)); + if (rc.IsFailure()) return rc; + + // Validate the header. + NodeHeader header = MemoryMarshal.Cast(buffer)[0]; + rc = header.Verify(nodeIndex, nodeSize, sizeof(long)); + if (rc.IsFailure()) return rc; + + // Create the node and find. + var node = new StorageNode(sizeof(long), header.Count); + node.Find(buffer, virtualAddress); + + if (node.GetIndex() < 0) + return ResultFs.InvalidBucketTreeVirtualOffset.Log(); + + // Return the index. + outIndex = (int)Tree.GetEntrySetIndex(header.Index, node.GetIndex()); + return Result.Success; + } + + private Result FindEntry(long virtualAddress, int entrySetIndex) + { + long entrySetSize = Tree.NodeSize; + + using (var rented = new RentedArray((int)entrySetSize)) + { + return FindEntryWithBuffer(virtualAddress, entrySetIndex, rented.Span); + } + } + + private Result FindEntryWithBuffer(long virtualAddress, int entrySetIndex, Span buffer) + { + // Calculate entry set extents. + long entrySize = Tree.EntrySize; + long entrySetSize = Tree.NodeSize; + long entrySetOffset = entrySetIndex * entrySetSize; + SubStorage2 storage = Tree.EntryStorage; + + // Read the entry set. + Result rc = storage.Read(entrySetOffset, buffer.Slice(0, (int)entrySetSize)); + if (rc.IsFailure()) return rc; + + // Validate the entry set. + EntrySetHeader entrySet = MemoryMarshal.Cast(buffer)[0]; + rc = entrySet.Header.Verify(entrySetIndex, entrySetSize, entrySize); + if (rc.IsFailure()) return rc; + + // Create the node, and find. + var node = new StorageNode(entrySize, entrySet.Info.Count); + node.Find(buffer, virtualAddress); + + if (node.GetIndex() < 0) + return ResultFs.InvalidBucketTreeVirtualOffset.Log(); + + // Copy the data into entry. + int entryIndex = node.GetIndex(); + long entryOffset = GetBucketTreeEntryOffset(0, entrySize, entryIndex); + buffer.Slice((int)entryOffset, (int)entrySize).CopyTo(Entry); + + // Set our entry set/index. + _entrySet = entrySet; + EntryIndex = entryIndex; + + return Result.Success; + } + + private struct StorageNode + { + private Offset _start; + private int _count; + private int _index; + + public StorageNode(long size, int count) + { + _start = new Offset(NodeHeaderSize, (int)size); + _count = count; + _index = -1; + } + + public int GetIndex() => _index; + + public void Find(ReadOnlySpan buffer, long virtualAddress) + { + int end = _count; + Offset pos = _start; + + while (end > 0) + { + int half = end / 2; + Offset mid = pos + half; + + long offset = BinaryPrimitives.ReadInt64LittleEndian(buffer.Slice((int)mid.Get())); + + if (offset <= virtualAddress) + { + pos = mid + 1; + end -= half + 1; + } + else + { + end = half; + } + } + + _index = (int)(pos - _start) - 1; + } + + private readonly struct Offset + { + private readonly long _offset; + private readonly int _stride; + + public Offset(long offset, int stride) + { + _offset = offset; + _stride = stride; + } + + public long Get() => _offset; + + public static Offset operator ++(Offset left) => left + 1; + public static Offset operator --(Offset left) => left - 1; + + public static Offset operator +(Offset left, long right) => new Offset(left._offset + right * left._stride, left._stride); + public static Offset operator -(Offset left, long right) => new Offset(left._offset - right * left._stride, left._stride); + + public static long operator -(Offset left, Offset right) => + (left._offset - right._offset) / left._stride; + + public static bool operator ==(Offset left, Offset right) => left._offset == right._offset; + public static bool operator !=(Offset left, Offset right) => left._offset != right._offset; + + public bool Equals(Offset other) => _offset == other._offset; + public override bool Equals(object obj) => obj is Offset other && Equals(other); + public override int GetHashCode() => _offset.GetHashCode(); + } + } } } } diff --git a/src/LibHac/FsSystem/BucketTree2.cs b/src/LibHac/FsSystem/BucketTree2.cs deleted file mode 100644 index 97598b30..00000000 --- a/src/LibHac/FsSystem/BucketTree2.cs +++ /dev/null @@ -1,738 +0,0 @@ -using System; -using System.Buffers.Binary; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using LibHac.Common; -using LibHac.Diag; -using LibHac.Fs; - -namespace LibHac.FsSystem -{ - public partial class BucketTree2 - { - private const uint ExpectedMagic = 0x52544B42; // BKTR - private const int MaxVersion = 1; - - private const int NodeSizeMin = 1024; - private const int NodeSizeMax = 1024 * 512; - - private static int NodeHeaderSize => Unsafe.SizeOf(); - - private SubStorage2 NodeStorage { get; set; } - private SubStorage2 EntryStorage { get; set; } - - private NodeBuffer _nodeL1 = new NodeBuffer(); - - private long NodeSize { get; set; } - private long EntrySize { get; set; } - private int OffsetCount { get; set; } - private int EntrySetCount { get; set; } - private long StartOffset { get; set; } - private long EndOffset { get; set; } - - public Result Initialize(SubStorage2 nodeStorage, SubStorage2 entryStorage, int nodeSize, int entrySize, - int entryCount) - { - Assert.AssertTrue(entrySize >= sizeof(long)); - Assert.AssertTrue(nodeSize >= entrySize + Unsafe.SizeOf()); - Assert.AssertTrue(NodeSizeMin <= nodeSize && nodeSize <= NodeSizeMax); - Assert.AssertTrue(Util.IsPowerOfTwo(nodeSize)); - Assert.AssertTrue(!IsInitialized()); - - // Ensure valid entry count. - if (entryCount <= 0) - return ResultFs.InvalidArgument.Log(); - - // Allocate node. - if (!_nodeL1.Allocate(nodeSize)) - return ResultFs.BufferAllocationFailed.Log(); - - bool needFree = true; - try - { - // Read node. - Result rc = nodeStorage.Read(0, _nodeL1.GetBuffer()); - if (rc.IsFailure()) return rc; - - // Verify node. - rc = _nodeL1.GetHeader().Verify(0, nodeSize, sizeof(long)); - if (rc.IsFailure()) return rc; - - // Validate offsets. - int offsetCount = GetOffsetCount(nodeSize); - int entrySetCount = GetEntrySetCount(nodeSize, entrySize, entryCount); - BucketTreeNode node = _nodeL1.GetNode(); - - long startOffset; - if (offsetCount < entrySetCount && node.GetCount() < offsetCount) - { - startOffset = node.GetL2BeginOffset(); - } - else - { - startOffset = node.GetBeginOffset(); - } - - long endOffset = node.GetEndOffset(); - - if (startOffset < 0 || startOffset > node.GetBeginOffset() || startOffset >= endOffset) - return ResultFs.InvalidBucketTreeEntryOffset.Log(); - - NodeStorage = nodeStorage; - EntryStorage = entryStorage; - NodeSize = nodeSize; - EntrySize = entrySize; - OffsetCount = offsetCount; - EntrySetCount = entrySetCount; - StartOffset = startOffset; - EndOffset = endOffset; - - needFree = false; - - return Result.Success; - } - finally - { - if (needFree) - _nodeL1.Free(); - } - } - - public bool IsInitialized() => NodeSize > 0; - public bool IsEmpty() => EntrySize == 0; - - public long GetStart() => StartOffset; - public long GetEnd() => EndOffset; - public long GetSize() => EndOffset - StartOffset; - - public bool Includes(long offset) - { - return StartOffset <= offset && offset < EndOffset; - } - - public bool Includes(long offset, long size) - { - return size > 0 && StartOffset <= offset && size <= EndOffset - offset; - } - - public Result Find(ref Visitor visitor, long virtualAddress) - { - Assert.AssertTrue(IsInitialized()); - - if (virtualAddress < 0) - return ResultFs.InvalidOffset.Log(); - - if (IsEmpty()) - return ResultFs.OutOfRange.Log(); - - Result rc = visitor.Initialize(this); - if (rc.IsFailure()) return rc; - - return visitor.Find(virtualAddress); - } - - public static int QueryHeaderStorageSize() => Unsafe.SizeOf
(); - - public static long QueryNodeStorageSize(long nodeSize, long entrySize, int entryCount) - { - Assert.AssertTrue(entrySize >= sizeof(long)); - Assert.AssertTrue(nodeSize >= entrySize + Unsafe.SizeOf()); - Assert.AssertTrue(NodeSizeMin <= nodeSize && nodeSize <= NodeSizeMax); - Assert.AssertTrue(Util.IsPowerOfTwo(nodeSize)); - Assert.AssertTrue(entryCount >= 0); - - if (entryCount <= 0) - return 0; - - return (1 + GetNodeL2Count(nodeSize, entrySize, entryCount)) * nodeSize; - } - - public static long QueryEntryStorageSize(long nodeSize, long entrySize, int entryCount) - { - Assert.AssertTrue(entrySize >= sizeof(long)); - Assert.AssertTrue(nodeSize >= entrySize + Unsafe.SizeOf()); - Assert.AssertTrue(NodeSizeMin <= nodeSize && nodeSize <= NodeSizeMax); - Assert.AssertTrue(Util.IsPowerOfTwo(nodeSize)); - Assert.AssertTrue(entryCount >= 0); - - if (entryCount <= 0) - return 0; - - return GetEntrySetCount(nodeSize, entrySize, entryCount) * nodeSize; - } - - private static int GetEntryCount(long nodeSize, long entrySize) - { - return (int)((nodeSize - Unsafe.SizeOf()) / entrySize); - } - - private static int GetOffsetCount(long nodeSize) - { - return (int)((nodeSize - Unsafe.SizeOf()) / sizeof(long)); - } - - private static int GetEntrySetCount(long nodeSize, long entrySize, int entryCount) - { - int entryCountPerNode = GetEntryCount(nodeSize, entrySize); - return Util.DivideByRoundUp(entryCount, entryCountPerNode); - } - - public static int GetNodeL2Count(long nodeSize, long entrySize, int entryCount) - { - int offsetCountPerNode = GetOffsetCount(nodeSize); - int entrySetCount = GetEntrySetCount(nodeSize, entrySize, entryCount); - - if (entrySetCount <= offsetCountPerNode) - return 0; - - int nodeL2Count = Util.DivideByRoundUp(entrySetCount, offsetCountPerNode); - Abort.DoAbortUnless(nodeL2Count <= offsetCountPerNode); - - return Util.DivideByRoundUp(entrySetCount - (offsetCountPerNode - (nodeL2Count - 1)), offsetCountPerNode); - } - - private static long GetBucketTreeEntryOffset(long entrySetOffset, long entrySize, int entryIndex) - { - return entrySetOffset + Unsafe.SizeOf() + entryIndex * entrySize; - } - - private static long GetBucketTreeEntryOffset(int entrySetIndex, long nodeSize, long entrySize, int entryIndex) - { - return GetBucketTreeEntryOffset(entrySetIndex * nodeSize, entrySize, entryIndex); - } - - private bool IsExistL2() => OffsetCount < EntrySetCount; - private bool IsExistOffsetL2OnL1() => IsExistL2() && _nodeL1.GetHeader().Count < OffsetCount; - - private long GetEntrySetIndex(int nodeIndex, int offsetIndex) - { - return (OffsetCount - _nodeL1.GetHeader().Count) + (OffsetCount * nodeIndex) + offsetIndex; - } - - public struct Header - { - public uint Magic; - public uint Version; - public int EntryCount; -#pragma warning disable 414 - private int _reserved; -#pragma warning restore 414 - - public void Format(int entryCount) - { - Magic = ExpectedMagic; - Version = MaxVersion; - EntryCount = entryCount; - _reserved = 0; - } - - public Result Verify() - { - if (Magic != ExpectedMagic) - return ResultFs.InvalidBucketTreeSignature.Log(); - - if (EntryCount < 0) - return ResultFs.InvalidBucketTreeEntryCount.Log(); - - if (Version > MaxVersion) - return ResultFs.UnsupportedVersion.Log(); - - return Result.Success; - } - } - - public struct NodeHeader - { - public int Index; - public int Count; - public long Offset; - - public Result Verify(int nodeIndex, long nodeSize, long entrySize) - { - if (Index != nodeIndex) - return ResultFs.InvalidBucketTreeNodeIndex.Log(); - - if (entrySize == 0 || nodeSize < entrySize + NodeHeaderSize) - return ResultFs.InvalidSize.Log(); - - long maxEntryCount = (nodeSize - NodeHeaderSize) / entrySize; - - if (Count <= 0 || maxEntryCount < Count) - return ResultFs.InvalidBucketTreeNodeEntryCount.Log(); - - if (Offset < 0) - return ResultFs.InvalidBucketTreeNodeOffset.Log(); - - return Result.Success; - } - } - - private struct NodeBuffer - { - // Use long to ensure alignment - private long[] _header; - - public bool Allocate(int nodeSize) - { - Assert.AssertTrue(_header == null); - - _header = new long[nodeSize / sizeof(long)]; - - return _header != null; - } - - public void Free() - { - _header = null; - } - - public void FillZero() - { - if (_header != null) - { - Array.Fill(_header, 0); - } - } - - public ref NodeHeader GetHeader() - { - Assert.AssertTrue(_header.Length / sizeof(long) >= Unsafe.SizeOf()); - - return ref Unsafe.As(ref _header[0]); - } - - public Span GetBuffer() - { - return MemoryMarshal.AsBytes(_header.AsSpan()); - } - - public BucketTreeNode GetNode() where TEntry : unmanaged - { - return new BucketTreeNode(GetBuffer()); - } - } - - public readonly ref struct BucketTreeNode where TEntry : unmanaged - { - private readonly Span _buffer; - - public BucketTreeNode(Span buffer) - { - _buffer = buffer; - - Assert.AssertTrue(_buffer.Length >= Unsafe.SizeOf()); - Assert.AssertTrue(_buffer.Length >= Unsafe.SizeOf() + GetHeader().Count * Unsafe.SizeOf()); - } - - public int GetCount() => GetHeader().Count; - - public ReadOnlySpan GetArray() => GetWritableArray(); - internal Span GetWritableArray() => GetWritableArray(); - - public long GetBeginOffset() => GetArray()[0]; - public long GetEndOffset() => GetHeader().Offset; - public long GetL2BeginOffset() => GetArray()[GetCount()]; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan GetArray() where TElement : unmanaged - { - return GetWritableArray(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Span GetWritableArray() where TElement : unmanaged - { - return MemoryMarshal.Cast(_buffer.Slice(Unsafe.SizeOf())); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ref NodeHeader GetHeader() - { - return ref Unsafe.As(ref MemoryMarshal.GetReference(_buffer)); - } - } - - public ref struct Visitor - { - private BucketTree2 Tree { get; set; } - private byte[] Entry { get; set; } - private int EntryIndex { get; set; } - private int EntrySetCount { get; set; } - private EntrySetHeader _entrySet; - - [StructLayout(LayoutKind.Explicit)] - private struct EntrySetHeader - { - // ReSharper disable once MemberHidesStaticFromOuterClass - [FieldOffset(0)] public NodeHeader Header; - [FieldOffset(0)] public EntrySetInfo Info; - - [StructLayout(LayoutKind.Sequential)] - public struct EntrySetInfo - { - public int Index; - public int Count; - public long End; - public long Start; - } - } - - public Result Initialize(BucketTree2 tree) - { - Assert.AssertTrue(tree != null); - Assert.AssertTrue(Tree == null || tree == Tree); - - if (Entry == null) - { - Entry = new byte[tree.EntrySize]; - Tree = tree; - EntryIndex = -1; - } - - return Result.Success; - } - - public void Dispose() - { - // todo: try using shared arrays - } - - public bool IsValid() => EntryIndex >= 0; - - public bool CanMoveNext() - { - return IsValid() && (EntryIndex + 1 < _entrySet.Info.Count || _entrySet.Info.Index + 1 < EntrySetCount); - } - - public bool CanMovePrevious() - { - return IsValid() && (EntryIndex > 0 || _entrySet.Info.Index > 0); - } - - public ref T Get() where T : unmanaged - { - return ref MemoryMarshal.Cast(Entry)[0]; - } - - public Result MoveNext() - { - Result rc; - - if (!IsValid()) - return ResultFs.OutOfRange.Log(); - - int entryIndex = EntryIndex + 1; - - // Invalidate our index, and read the header for the next index. - if (entryIndex == _entrySet.Info.Count) - { - int entrySetIndex = _entrySet.Info.Index + 1; - if (entrySetIndex >= EntrySetCount) - return ResultFs.OutOfRange.Log(); - - EntryIndex = -1; - - long end = _entrySet.Info.End; - - long entrySetSize = Tree.NodeSize; - long entrySetOffset = entrySetIndex * entrySetSize; - - rc = Tree.EntryStorage.Read(entrySetOffset, SpanHelpers.AsByteSpan(ref _entrySet)); - if (rc.IsFailure()) return rc; - - rc = _entrySet.Header.Verify(entrySetIndex, entrySetSize, Tree.EntrySize); - if (rc.IsFailure()) return rc; - - if (_entrySet.Info.Start != end || _entrySet.Info.Start >= _entrySet.Info.End) - return ResultFs.InvalidBucketTreeEntrySetOffset.Log(); - - entryIndex = 0; - } - else - { - EntryIndex = 1; - } - - // Read the new entry - long entrySize = Tree.EntrySize; - long entryOffset = GetBucketTreeEntryOffset(_entrySet.Info.Index, Tree.NodeSize, entrySize, entryIndex); - - rc = Tree.EntryStorage.Read(entryOffset, Entry); - if (rc.IsFailure()) return rc; - - // Note that we changed index. - EntryIndex = entryIndex; - return Result.Success; - } - - public Result MovePrevious() - { - Result rc; - - if (!IsValid()) - return ResultFs.OutOfRange.Log(); - - int entryIndex = EntryIndex; - - if (entryIndex == 0) - { - if (_entrySet.Info.Index <= 0) - return ResultFs.OutOfRange.Log(); - - EntryIndex = -1; - - long start = _entrySet.Info.Start; - - long entrySetSize = Tree.NodeSize; - int entrySetIndex = _entrySet.Info.Index - 1; - long entrySetOffset = entrySetIndex * entrySetSize; - - rc = Tree.EntryStorage.Read(entrySetOffset, SpanHelpers.AsByteSpan(ref _entrySet)); - if (rc.IsFailure()) return rc; - - rc = _entrySet.Header.Verify(entrySetIndex, entrySetSize, Tree.EntrySize); - if (rc.IsFailure()) return rc; - - if (_entrySet.Info.End != start || _entrySet.Info.Start >= _entrySet.Info.End) - return ResultFs.InvalidBucketTreeEntrySetOffset.Log(); - - entryIndex = _entrySet.Info.Count; - } - else - { - EntryIndex = -1; - } - - // Read the new entry - long entrySize = Tree.EntrySize; - long entryOffset = GetBucketTreeEntryOffset(_entrySet.Info.Index, Tree.NodeSize, entrySize, entryIndex); - - rc = Tree.EntryStorage.Read(entryOffset, Entry); - if (rc.IsFailure()) return rc; - - // Note that we changed index. - EntryIndex = entryIndex; - return Result.Success; - } - - public Result Find(long virtualAddress) - { - Result rc; - - // Get the node. - BucketTreeNode node = Tree._nodeL1.GetNode(); - - if (virtualAddress >= node.GetEndOffset()) - return ResultFs.OutOfRange.Log(); - - int entrySetIndex; - - if (Tree.IsExistOffsetL2OnL1() && virtualAddress < node.GetBeginOffset()) - { - // The portion of the L2 offsets containing our target offset is stored in the L1 node - ReadOnlySpan offsets = node.GetArray().Slice(node.GetCount()); - int index = offsets.BinarySearch(virtualAddress); - if (index < 0) index = (~index) - 1; - - if (index < 0) - return ResultFs.OutOfRange.Log(); - - entrySetIndex = index; - } - else - { - ReadOnlySpan offsets = node.GetArray().Slice(0, node.GetCount()); - int index = offsets.BinarySearch(virtualAddress); - if (index < 0) index = (~index) - 1; - - if (index < 0) - return ResultFs.OutOfRange.Log(); - - if (Tree.IsExistL2()) - { - if (index >= Tree.OffsetCount) - return ResultFs.InvalidBucketTreeNodeOffset.Log(); - - rc = FindEntrySet(out entrySetIndex, virtualAddress, index); - if (rc.IsFailure()) return rc; - } - else - { - entrySetIndex = index; - } - } - - // Validate the entry set index. - if (entrySetIndex < 0 || entrySetIndex >= Tree.EntrySetCount) - return ResultFs.InvalidBucketTreeNodeOffset.Log(); - - // Find the entry. - rc = FindEntry(virtualAddress, entrySetIndex); - if (rc.IsFailure()) return rc; - - // Set count. - EntrySetCount = Tree.EntrySetCount; - return Result.Success; - } - - private Result FindEntrySet(out int entrySetIndex, long virtualAddress, int nodeIndex) - { - long nodeSize = Tree.NodeSize; - - using (var rented = new RentedArray((int)nodeSize)) - { - return FindEntrySetWithBuffer(out entrySetIndex, virtualAddress, nodeIndex, rented.Span); - } - } - - private Result FindEntrySetWithBuffer(out int outIndex, long virtualAddress, int nodeIndex, - Span buffer) - { - outIndex = default; - - // Calculate node extents. - long nodeSize = Tree.NodeSize; - long nodeOffset = (nodeIndex + 1) * nodeSize; - SubStorage2 storage = Tree.NodeStorage; - - // Read the node. - Result rc = storage.Read(nodeOffset, buffer.Slice(0, (int)nodeSize)); - if (rc.IsFailure()) return rc; - - // Validate the header. - NodeHeader header = MemoryMarshal.Cast(buffer)[0]; - rc = header.Verify(nodeIndex, nodeSize, sizeof(long)); - if (rc.IsFailure()) return rc; - - // Create the node and find. - var node = new StorageNode(sizeof(long), header.Count); - node.Find(buffer, virtualAddress); - - if (node.GetIndex() < 0) - return ResultFs.InvalidBucketTreeVirtualOffset.Log(); - - // Return the index. - outIndex = (int)Tree.GetEntrySetIndex(header.Index, node.GetIndex()); - return Result.Success; - } - - private Result FindEntry(long virtualAddress, int entrySetIndex) - { - long entrySetSize = Tree.NodeSize; - - using (var rented = new RentedArray((int)entrySetSize)) - { - return FindEntryWithBuffer(virtualAddress, entrySetIndex, rented.Span); - } - } - - private Result FindEntryWithBuffer(long virtualAddress, int entrySetIndex, Span buffer) - { - // Calculate entry set extents. - long entrySize = Tree.EntrySize; - long entrySetSize = Tree.NodeSize; - long entrySetOffset = entrySetIndex * entrySetSize; - SubStorage2 storage = Tree.EntryStorage; - - // Read the entry set. - Result rc = storage.Read(entrySetOffset, buffer.Slice(0, (int)entrySetSize)); - if (rc.IsFailure()) return rc; - - // Validate the entry set. - EntrySetHeader entrySet = MemoryMarshal.Cast(buffer)[0]; - rc = entrySet.Header.Verify(entrySetIndex, entrySetSize, entrySize); - if (rc.IsFailure()) return rc; - - // Create the node, and find. - var node = new StorageNode(entrySize, entrySet.Info.Count); - node.Find(buffer, virtualAddress); - - if (node.GetIndex() < 0) - return ResultFs.InvalidBucketTreeVirtualOffset.Log(); - - // Copy the data into entry. - int entryIndex = node.GetIndex(); - long entryOffset = GetBucketTreeEntryOffset(0, entrySize, entryIndex); - buffer.Slice((int)entryOffset, (int)entrySize).CopyTo(Entry); - - // Set our entry set/index. - _entrySet = entrySet; - EntryIndex = entryIndex; - - return Result.Success; - } - - private struct StorageNode - { - private Offset _start; - private int _count; - private int _index; - - public StorageNode(long size, int count) - { - _start = new Offset(NodeHeaderSize, (int)size); - _count = count; - _index = -1; - } - - public int GetIndex() => _index; - - public void Find(ReadOnlySpan buffer, long virtualAddress) - { - int end = _count; - Offset pos = _start; - - while (end > 0) - { - int half = end / 2; - Offset mid = pos + half; - - long offset = BinaryPrimitives.ReadInt64LittleEndian(buffer.Slice((int)mid.Get())); - - if (offset <= virtualAddress) - { - pos = mid + 1; - end -= half + 1; - } - else - { - end = half; - } - } - - _index = (int)(pos - _start) - 1; - } - - private readonly struct Offset - { - private readonly long _offset; - private readonly int _stride; - - public Offset(long offset, int stride) - { - _offset = offset; - _stride = stride; - } - - public long Get() => _offset; - - public static Offset operator ++(Offset left) => left + 1; - public static Offset operator --(Offset left) => left - 1; - - public static Offset operator +(Offset left, long right) => new Offset(left._offset + right * left._stride, left._stride); - public static Offset operator -(Offset left, long right) => new Offset(left._offset - right * left._stride, left._stride); - - public static long operator -(Offset left, Offset right) => - (left._offset - right._offset) / left._stride; - - public static bool operator ==(Offset left, Offset right) => left._offset == right._offset; - public static bool operator !=(Offset left, Offset right) => left._offset != right._offset; - - public bool Equals(Offset other) => _offset == other._offset; - public override bool Equals(object obj) => obj is Offset other && Equals(other); - public override int GetHashCode() => _offset.GetHashCode(); - } - } - } - } -} diff --git a/src/LibHac/FsSystem/BucketTreeBuilder.cs b/src/LibHac/FsSystem/BucketTreeBuilder.cs index 309b08e4..01d82ebb 100644 --- a/src/LibHac/FsSystem/BucketTreeBuilder.cs +++ b/src/LibHac/FsSystem/BucketTreeBuilder.cs @@ -7,7 +7,7 @@ using LibHac.Fs; namespace LibHac.FsSystem { - public partial class BucketTree2 + public partial class BucketTree { public class Builder { diff --git a/src/LibHac/FsSystem/IndirectStorage.cs b/src/LibHac/FsSystem/IndirectStorage.cs index d1df2015..65664251 100644 --- a/src/LibHac/FsSystem/IndirectStorage.cs +++ b/src/LibHac/FsSystem/IndirectStorage.cs @@ -12,10 +12,10 @@ namespace LibHac.FsSystem public static readonly int StorageCount = 2; public static readonly int NodeSize = 1024 * 16; - private BucketTree2 Table { get; } = new BucketTree2(); + private BucketTree Table { get; } = new BucketTree(); private SubStorage2[] DataStorage { get; } = new SubStorage2[StorageCount]; - [StructLayout(LayoutKind.Sequential, Size = 0x14)] + [StructLayout(LayoutKind.Sequential, Size = 0x14, Pack = 4)] public struct Entry { private long VirtualOffset; @@ -29,13 +29,13 @@ namespace LibHac.FsSystem public long GetPhysicalOffset() => PhysicalOffset; } - public static long QueryHeaderStorageSize() => BucketTree2.QueryHeaderStorageSize(); + public static long QueryHeaderStorageSize() => BucketTree.QueryHeaderStorageSize(); public static long QueryNodeStorageSize(int entryCount) => - BucketTree2.QueryNodeStorageSize(NodeSize, Unsafe.SizeOf(), entryCount); + BucketTree.QueryNodeStorageSize(NodeSize, Unsafe.SizeOf(), entryCount); public static long QueryEntryStorageSize(int entryCount) => - BucketTree2.QueryEntryStorageSize(NodeSize, Unsafe.SizeOf(), entryCount); + BucketTree.QueryEntryStorageSize(NodeSize, Unsafe.SizeOf(), entryCount); public bool IsInitialized() => Table.IsInitialized(); @@ -43,7 +43,7 @@ namespace LibHac.FsSystem { // Read and verify the bucket tree header. // note: skip init - var header = new BucketTree2.Header(); + var header = new BucketTree.Header(); Result rc = tableStorage.Read(0, SpanHelpers.AsByteSpan(ref header)); if (rc.IsFailure()) return rc; @@ -100,7 +100,7 @@ namespace LibHac.FsSystem return ResultFs.OutOfRange.Log(); // Find the offset in our tree - var visitor = new BucketTree2.Visitor(); + var visitor = new BucketTree.Visitor(); Result rc = Table.Find(ref visitor, offset); if (rc.IsFailure()) return rc; @@ -214,7 +214,7 @@ namespace LibHac.FsSystem return ResultFs.OutOfRange.Log(); // Find the offset in our tree - var visitor = new BucketTree2.Visitor(); + var visitor = new BucketTree.Visitor(); Result rc = Table.Find(ref visitor, offset); if (rc.IsFailure()) return rc; diff --git a/src/LibHac/FsSystem/NcaUtils/Nca.cs b/src/LibHac/FsSystem/NcaUtils/Nca.cs index 1681ef08..672dcfa6 100644 --- a/src/LibHac/FsSystem/NcaUtils/Nca.cs +++ b/src/LibHac/FsSystem/NcaUtils/Nca.cs @@ -199,7 +199,7 @@ namespace LibHac.FsSystem.NcaUtils var cachedBucketTreeData = new CachedStorage(encryptionBucketTreeData, IndirectStorage.NodeSize, 6, true); - var treeHeader = new BucketTree2.Header(); + var treeHeader = new BucketTree.Header(); info.EncryptionTreeHeader.CopyTo(SpanHelpers.AsByteSpan(ref treeHeader)); long nodeStorageSize = IndirectStorage.QueryNodeStorageSize(treeHeader.EntryCount); long entryStorageSize = IndirectStorage.QueryEntryStorageSize(treeHeader.EntryCount); @@ -237,7 +237,7 @@ namespace LibHac.FsSystem.NcaUtils return patchStorage; } - var treeHeader = new BucketTree2.Header(); + var treeHeader = new BucketTree.Header(); patchInfo.RelocationTreeHeader.CopyTo(SpanHelpers.AsByteSpan(ref treeHeader)); long nodeStorageSize = IndirectStorage.QueryNodeStorageSize(treeHeader.EntryCount); long entryStorageSize = IndirectStorage.QueryEntryStorageSize(treeHeader.EntryCount);