Read directly from save allocation table when requested

This commit is contained in:
Alex Barney 2019-04-02 17:21:38 -05:00
parent 90fc0e096c
commit 6112e35eb2
2 changed files with 39 additions and 26 deletions

View file

@ -1,13 +1,16 @@
using System.IO; using System;
using System.IO;
using System.Runtime.InteropServices;
namespace LibHac.IO.Save namespace LibHac.IO.Save
{ {
public class AllocationTable public class AllocationTable
{ {
private const int EntrySize = 8;
private IStorage BaseStorage { get; } private IStorage BaseStorage { get; }
private IStorage HeaderStorage { get; } private IStorage HeaderStorage { get; }
public AllocationTableEntry[] Entries { get; }
public AllocationTableHeader Header { get; } public AllocationTableHeader Header { get; }
public AllocationTable(IStorage storage, IStorage header) public AllocationTable(IStorage storage, IStorage header)
@ -15,33 +18,43 @@ namespace LibHac.IO.Save
BaseStorage = storage; BaseStorage = storage;
HeaderStorage = header; HeaderStorage = header;
Header = new AllocationTableHeader(HeaderStorage); Header = new AllocationTableHeader(HeaderStorage);
}
Stream tableStream = storage.AsStream(); public void ReadEntry(int index, out AllocationTableEntry entry)
{
Span<byte> bytes = stackalloc byte[EntrySize];
int offset = index * EntrySize;
// The first entry in the table is reserved. Block 0 is at table index 1 BaseStorage.Read(bytes, offset);
int blockCount = (int)(Header.AllocationTableBlockCount) + 1;
Entries = new AllocationTableEntry[blockCount]; entry = GetEntryFromBytes(bytes);
tableStream.Position = 0; }
var reader = new BinaryReader(tableStream);
for (int i = 0; i < blockCount; i++) public void WriteEntry(int index, ref AllocationTableEntry entry)
{ {
int parent = reader.ReadInt32(); Span<byte> bytes = stackalloc byte[EntrySize];
int child = reader.ReadInt32(); 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<byte> entry)
{
return ref MemoryMarshal.Cast<byte, AllocationTableEntry>(entry)[0];
} }
public IStorage GetBaseStorage() => BaseStorage.AsReadOnly(); public IStorage GetBaseStorage() => BaseStorage.AsReadOnly();
public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly(); public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly();
} }
public class AllocationTableEntry [StructLayout(LayoutKind.Sequential)]
public struct AllocationTableEntry
{ {
public int Prev { get; set; } public int Prev;
public int Next { get; set; } public int Next;
public bool IsListStart() public bool IsListStart()
{ {

View file

@ -22,11 +22,11 @@ namespace LibHac.IO.Save
public bool BeginIteration(int initialBlock) public bool BeginIteration(int initialBlock)
{ {
AllocationTableEntry tableEntry = Fat.Entries[initialBlock + 1]; Fat.ReadEntry(initialBlock + 1, out AllocationTableEntry tableEntry);
if (!tableEntry.IsListStart() && initialBlock != -1) if (!tableEntry.IsListStart() && initialBlock != -1)
{ {
return false; return false;
} }
if (tableEntry.IsSingleBlockSegment()) if (tableEntry.IsSingleBlockSegment())
@ -35,7 +35,7 @@ namespace LibHac.IO.Save
} }
else else
{ {
AllocationTableEntry lengthEntry = Fat.Entries[initialBlock + 2]; Fat.ReadEntry(initialBlock + 2, out AllocationTableEntry lengthEntry);
CurrentSegmentSize = lengthEntry.Next - initialBlock; CurrentSegmentSize = lengthEntry.Next - initialBlock;
} }
@ -46,11 +46,11 @@ namespace LibHac.IO.Save
public bool MoveNext() public bool MoveNext()
{ {
AllocationTableEntry currentEntry = Fat.Entries[PhysicalBlock + 1]; Fat.ReadEntry(PhysicalBlock + 1, out AllocationTableEntry currentEntry);
if (currentEntry.IsListEnd()) return false; if (currentEntry.IsListEnd()) return false;
int newBlock = currentEntry.Next & 0x7FFFFFFF; int newBlock = currentEntry.Next & 0x7FFFFFFF;
AllocationTableEntry newEntry = Fat.Entries[newBlock]; Fat.ReadEntry(newBlock, out AllocationTableEntry newEntry);
VirtualBlock += CurrentSegmentSize; VirtualBlock += CurrentSegmentSize;
if (newEntry.IsSingleBlockSegment()) if (newEntry.IsSingleBlockSegment())
@ -59,7 +59,7 @@ namespace LibHac.IO.Save
} }
else else
{ {
AllocationTableEntry lengthEntry = Fat.Entries[newBlock + 1]; Fat.ReadEntry(newBlock + 1, out AllocationTableEntry lengthEntry);
CurrentSegmentSize = lengthEntry.Next - (newBlock - 1); CurrentSegmentSize = lengthEntry.Next - (newBlock - 1);
} }
@ -69,11 +69,11 @@ namespace LibHac.IO.Save
public bool MovePrevious() public bool MovePrevious()
{ {
AllocationTableEntry currentEntry = Fat.Entries[PhysicalBlock + 1]; Fat.ReadEntry(PhysicalBlock + 1, out AllocationTableEntry currentEntry);
if (currentEntry.IsListStart()) return false; if (currentEntry.IsListStart()) return false;
int newBlock = currentEntry.Prev & 0x7FFFFFFF; int newBlock = currentEntry.Prev & 0x7FFFFFFF;
AllocationTableEntry newEntry = Fat.Entries[newBlock]; Fat.ReadEntry(newBlock, out AllocationTableEntry newEntry);
if (newEntry.IsSingleBlockSegment()) if (newEntry.IsSingleBlockSegment())
{ {
@ -81,7 +81,7 @@ namespace LibHac.IO.Save
} }
else else
{ {
AllocationTableEntry lengthEntry = Fat.Entries[newBlock + 1]; Fat.ReadEntry(newBlock + 1, out AllocationTableEntry lengthEntry);
CurrentSegmentSize = lengthEntry.Next - (newBlock - 1); CurrentSegmentSize = lengthEntry.Next - (newBlock - 1);
} }