diff --git a/src/LibHac/IO/Save/AllocationTable.cs b/src/LibHac/IO/Save/AllocationTable.cs index 654f7dc3..03abeb9c 100644 --- a/src/LibHac/IO/Save/AllocationTable.cs +++ b/src/LibHac/IO/Save/AllocationTable.cs @@ -1,13 +1,16 @@ -using System.IO; +using System; +using System.IO; +using System.Runtime.InteropServices; namespace LibHac.IO.Save { public class AllocationTable { + private const int EntrySize = 8; + private IStorage BaseStorage { get; } private IStorage HeaderStorage { get; } - public AllocationTableEntry[] Entries { get; } public AllocationTableHeader Header { get; } public AllocationTable(IStorage storage, IStorage header) @@ -15,33 +18,43 @@ namespace LibHac.IO.Save BaseStorage = storage; HeaderStorage = header; Header = new AllocationTableHeader(HeaderStorage); + } - Stream tableStream = storage.AsStream(); + public void ReadEntry(int index, out AllocationTableEntry entry) + { + Span bytes = stackalloc byte[EntrySize]; + int offset = index * EntrySize; - // The first entry in the table is reserved. Block 0 is at table index 1 - int blockCount = (int)(Header.AllocationTableBlockCount) + 1; + BaseStorage.Read(bytes, offset); - Entries = new AllocationTableEntry[blockCount]; - tableStream.Position = 0; - var reader = new BinaryReader(tableStream); + entry = GetEntryFromBytes(bytes); + } - for (int i = 0; i < blockCount; i++) - { - int parent = reader.ReadInt32(); - int child = reader.ReadInt32(); + public void WriteEntry(int index, ref AllocationTableEntry entry) + { + Span bytes = stackalloc byte[EntrySize]; + int offset = index * EntrySize; - Entries[i] = new AllocationTableEntry { Next = child, Prev = parent }; - } + ref AllocationTableEntry newEntry = ref GetEntryFromBytes(bytes); + newEntry = entry; + + BaseStorage.Write(bytes, offset); + } + + private ref AllocationTableEntry GetEntryFromBytes(Span entry) + { + return ref MemoryMarshal.Cast(entry)[0]; } public IStorage GetBaseStorage() => BaseStorage.AsReadOnly(); public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly(); } - public class AllocationTableEntry + [StructLayout(LayoutKind.Sequential)] + public struct AllocationTableEntry { - public int Prev { get; set; } - public int Next { get; set; } + public int Prev; + public int Next; public bool IsListStart() { diff --git a/src/LibHac/IO/Save/AllocationTableIterator.cs b/src/LibHac/IO/Save/AllocationTableIterator.cs index e64a6882..22c1cfea 100644 --- a/src/LibHac/IO/Save/AllocationTableIterator.cs +++ b/src/LibHac/IO/Save/AllocationTableIterator.cs @@ -22,11 +22,11 @@ namespace LibHac.IO.Save public bool BeginIteration(int initialBlock) { - AllocationTableEntry tableEntry = Fat.Entries[initialBlock + 1]; + Fat.ReadEntry(initialBlock + 1, out AllocationTableEntry tableEntry); if (!tableEntry.IsListStart() && initialBlock != -1) { - return false; + return false; } if (tableEntry.IsSingleBlockSegment()) @@ -35,7 +35,7 @@ namespace LibHac.IO.Save } else { - AllocationTableEntry lengthEntry = Fat.Entries[initialBlock + 2]; + Fat.ReadEntry(initialBlock + 2, out AllocationTableEntry lengthEntry); CurrentSegmentSize = lengthEntry.Next - initialBlock; } @@ -46,11 +46,11 @@ namespace LibHac.IO.Save public bool MoveNext() { - AllocationTableEntry currentEntry = Fat.Entries[PhysicalBlock + 1]; + Fat.ReadEntry(PhysicalBlock + 1, out AllocationTableEntry currentEntry); if (currentEntry.IsListEnd()) return false; int newBlock = currentEntry.Next & 0x7FFFFFFF; - AllocationTableEntry newEntry = Fat.Entries[newBlock]; + Fat.ReadEntry(newBlock, out AllocationTableEntry newEntry); VirtualBlock += CurrentSegmentSize; if (newEntry.IsSingleBlockSegment()) @@ -59,7 +59,7 @@ namespace LibHac.IO.Save } else { - AllocationTableEntry lengthEntry = Fat.Entries[newBlock + 1]; + Fat.ReadEntry(newBlock + 1, out AllocationTableEntry lengthEntry); CurrentSegmentSize = lengthEntry.Next - (newBlock - 1); } @@ -69,11 +69,11 @@ namespace LibHac.IO.Save public bool MovePrevious() { - AllocationTableEntry currentEntry = Fat.Entries[PhysicalBlock + 1]; + Fat.ReadEntry(PhysicalBlock + 1, out AllocationTableEntry currentEntry); if (currentEntry.IsListStart()) return false; int newBlock = currentEntry.Prev & 0x7FFFFFFF; - AllocationTableEntry newEntry = Fat.Entries[newBlock]; + Fat.ReadEntry(newBlock, out AllocationTableEntry newEntry); if (newEntry.IsSingleBlockSegment()) { @@ -81,7 +81,7 @@ namespace LibHac.IO.Save } else { - AllocationTableEntry lengthEntry = Fat.Entries[newBlock + 1]; + Fat.ReadEntry(newBlock + 1, out AllocationTableEntry lengthEntry); CurrentSegmentSize = lengthEntry.Next - (newBlock - 1); }