diff --git a/src/LibHac/IO/Save/AllocationTable.cs b/src/LibHac/IO/Save/AllocationTable.cs index 03abeb9c..904b724b 100644 --- a/src/LibHac/IO/Save/AllocationTable.cs +++ b/src/LibHac/IO/Save/AllocationTable.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; @@ -13,6 +14,9 @@ namespace LibHac.IO.Save public AllocationTableHeader Header { get; } + public IStorage GetBaseStorage() => BaseStorage.AsReadOnly(); + public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly(); + public AllocationTable(IStorage storage, IStorage header) { BaseStorage = storage; @@ -20,20 +24,68 @@ namespace LibHac.IO.Save Header = new AllocationTableHeader(HeaderStorage); } - public void ReadEntry(int index, out AllocationTableEntry entry) + public void ReadEntry(int blockIndex, out int next, out int previous, out int length) + { + int entryIndex = BlockToEntryIndex(blockIndex); + + Span entries = stackalloc AllocationTableEntry[2]; + ReadEntries(entryIndex, entries); + + if (entries[0].IsSingleBlockSegment()) + { + length = 1; + } + else + { + length = entries[1].Next - entryIndex; + } + + if (entries[0].IsListEnd()) + { + next = -1; + } + else + { + next = EntryIndexToBlock(entries[0].Next & 0x7FFFFFFF); + } + + if (entries[0].IsListStart()) + { + previous = -1; + } + else + { + previous = EntryIndexToBlock(entries[0].Prev & 0x7FFFFFFF); + } + } + + private void ReadEntries(int entryIndex, Span entries) + { + Debug.Assert(entries.Length >= 2); + + bool isLastBlock = entryIndex == BlockToEntryIndex(Header.AllocationTableBlockCount) - 1; + int entriesToRead = isLastBlock ? 1 : 2; + int offset = entryIndex * EntrySize; + + Span buffer = MemoryMarshal.Cast(entries.Slice(0, entriesToRead)); + + BaseStorage.Read(buffer, offset); + } + + private void ReadEntry(int entryIndex, out AllocationTableEntry entry) { Span bytes = stackalloc byte[EntrySize]; - int offset = index * EntrySize; + int offset = entryIndex * EntrySize; BaseStorage.Read(bytes, offset); entry = GetEntryFromBytes(bytes); } - public void WriteEntry(int index, ref AllocationTableEntry entry) + private void WriteEntry(int entryIndex, ref AllocationTableEntry entry) { Span bytes = stackalloc byte[EntrySize]; - int offset = index * EntrySize; + int offset = entryIndex * EntrySize; ref AllocationTableEntry newEntry = ref GetEntryFromBytes(bytes); newEntry = entry; @@ -41,13 +93,13 @@ namespace LibHac.IO.Save BaseStorage.Write(bytes, offset); } - private ref AllocationTableEntry GetEntryFromBytes(Span entry) + private static ref AllocationTableEntry GetEntryFromBytes(Span entry) { return ref MemoryMarshal.Cast(entry)[0]; } - public IStorage GetBaseStorage() => BaseStorage.AsReadOnly(); - public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly(); + private static int EntryIndexToBlock(int entryIndex) => entryIndex - 1; + private static int BlockToEntryIndex(int blockIndex) => blockIndex + 1; } [StructLayout(LayoutKind.Sequential)] @@ -81,7 +133,7 @@ namespace LibHac.IO.Save { public long BlockSize { get; } public long AllocationTableOffset { get; } - public long AllocationTableBlockCount { get; } + public int AllocationTableBlockCount { get; } public long DataOffset { get; } public long DataBlockCount { get; } public int DirectoryTableBlock { get; } diff --git a/src/LibHac/IO/Save/AllocationTableIterator.cs b/src/LibHac/IO/Save/AllocationTableIterator.cs index 22c1cfea..a7c8511b 100644 --- a/src/LibHac/IO/Save/AllocationTableIterator.cs +++ b/src/LibHac/IO/Save/AllocationTableIterator.cs @@ -8,7 +8,11 @@ namespace LibHac.IO.Save public int VirtualBlock { get; private set; } public int PhysicalBlock { get; private set; } - public int CurrentSegmentSize { get; private set; } + public int CurrentSegmentSize => _currentSegmentSize; + + private int _nextBlock; + private int _prevBlock; + private int _currentSegmentSize; public AllocationTableIterator(AllocationTable table, int initialBlock) { @@ -22,71 +26,34 @@ namespace LibHac.IO.Save public bool BeginIteration(int initialBlock) { - Fat.ReadEntry(initialBlock + 1, out AllocationTableEntry tableEntry); - - if (!tableEntry.IsListStart() && initialBlock != -1) - { - return false; - } - - if (tableEntry.IsSingleBlockSegment()) - { - CurrentSegmentSize = 1; - } - else - { - Fat.ReadEntry(initialBlock + 2, out AllocationTableEntry lengthEntry); - CurrentSegmentSize = lengthEntry.Next - initialBlock; - } - PhysicalBlock = initialBlock; + Fat.ReadEntry(initialBlock, out _nextBlock, out _prevBlock, out _currentSegmentSize); - return true; + return _prevBlock == -1; } public bool MoveNext() { - Fat.ReadEntry(PhysicalBlock + 1, out AllocationTableEntry currentEntry); - if (currentEntry.IsListEnd()) return false; - int newBlock = currentEntry.Next & 0x7FFFFFFF; + if (_nextBlock == -1) return false; - Fat.ReadEntry(newBlock, out AllocationTableEntry newEntry); - VirtualBlock += CurrentSegmentSize; + VirtualBlock += _currentSegmentSize; + PhysicalBlock = _nextBlock; - if (newEntry.IsSingleBlockSegment()) - { - CurrentSegmentSize = 1; - } - else - { - Fat.ReadEntry(newBlock + 1, out AllocationTableEntry lengthEntry); - CurrentSegmentSize = lengthEntry.Next - (newBlock - 1); - } - - PhysicalBlock = newBlock - 1; + Fat.ReadEntry(_nextBlock, out _nextBlock, out _prevBlock, out _currentSegmentSize); + return true; } public bool MovePrevious() { - Fat.ReadEntry(PhysicalBlock + 1, out AllocationTableEntry currentEntry); - if (currentEntry.IsListStart()) return false; - int newBlock = currentEntry.Prev & 0x7FFFFFFF; + if (_prevBlock == -1) return false; - Fat.ReadEntry(newBlock, out AllocationTableEntry newEntry); + PhysicalBlock = _prevBlock; - if (newEntry.IsSingleBlockSegment()) - { - CurrentSegmentSize = 1; - } - else - { - Fat.ReadEntry(newBlock + 1, out AllocationTableEntry lengthEntry); - CurrentSegmentSize = lengthEntry.Next - (newBlock - 1); - } + Fat.ReadEntry(_prevBlock, out _nextBlock, out _prevBlock, out _currentSegmentSize); - VirtualBlock -= CurrentSegmentSize; - PhysicalBlock = newBlock - 1; + VirtualBlock -= _currentSegmentSize; + return true; }