mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add savedata CreateFile
This commit is contained in:
parent
c89b8be887
commit
b1997806c1
2 changed files with 197 additions and 5 deletions
|
@ -7,6 +7,7 @@ namespace LibHac.IO.Save
|
|||
{
|
||||
public class AllocationTable
|
||||
{
|
||||
private const int FreeListEntryIndex = 0;
|
||||
private const int EntrySize = 8;
|
||||
|
||||
private IStorage BaseStorage { get; }
|
||||
|
@ -37,7 +38,7 @@ namespace LibHac.IO.Save
|
|||
}
|
||||
else
|
||||
{
|
||||
length = entries[1].Next - entryIndex;
|
||||
length = entries[1].Next - entryIndex + 1;
|
||||
}
|
||||
|
||||
if (entries[0].IsListEnd())
|
||||
|
@ -46,7 +47,7 @@ namespace LibHac.IO.Save
|
|||
}
|
||||
else
|
||||
{
|
||||
next = EntryIndexToBlock(entries[0].Next & 0x7FFFFFFF);
|
||||
next = EntryIndexToBlock(entries[0].GetNext());
|
||||
}
|
||||
|
||||
if (entries[0].IsListStart())
|
||||
|
@ -55,10 +56,44 @@ namespace LibHac.IO.Save
|
|||
}
|
||||
else
|
||||
{
|
||||
previous = EntryIndexToBlock(entries[0].Prev & 0x7FFFFFFF);
|
||||
previous = EntryIndexToBlock(entries[0].GetPrev());
|
||||
}
|
||||
}
|
||||
|
||||
public int GetFreeListBlockIndex()
|
||||
{
|
||||
AllocationTableEntry freeList = ReadEntry(FreeListEntryIndex);
|
||||
return EntryIndexToBlock(freeList.GetNext());
|
||||
}
|
||||
|
||||
public void SetFreeListBlockIndex(int headBlockIndex)
|
||||
{
|
||||
var freeList = new AllocationTableEntry() { Next = BlockToEntryIndex(headBlockIndex) };
|
||||
WriteEntry(FreeListEntryIndex, freeList);
|
||||
}
|
||||
|
||||
public int Allocate(int blockCount)
|
||||
{
|
||||
if (blockCount <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(blockCount));
|
||||
}
|
||||
|
||||
int freeList = GetFreeListBlockIndex();
|
||||
|
||||
int newFreeList = Trim(freeList, blockCount);
|
||||
if (newFreeList == -1) return -1;
|
||||
|
||||
SetFreeListBlockIndex(newFreeList);
|
||||
|
||||
return freeList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines 2 lists into one list. The second list will be attached to the end of the first list.
|
||||
/// </summary>
|
||||
/// <param name="frontListBlockIndex">The index of the first block.</param>
|
||||
/// <param name="backListBlockIndex">The index of the second block.</param>
|
||||
public void Join(int frontListBlockIndex, int backListBlockIndex)
|
||||
{
|
||||
int frontEntryIndex = BlockToEntryIndex(frontListBlockIndex);
|
||||
|
@ -76,6 +111,136 @@ namespace LibHac.IO.Save
|
|||
WriteEntry(backEntryIndex, backHead);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trims an existing list to the specified length and returns the excess blocks as a new list.
|
||||
/// </summary>
|
||||
/// <param name="listHeadBlockIndex">The starting block of the list to trim.</param>
|
||||
/// <param name="newListLength">The length in blocks that the list will be shortened to.</param>
|
||||
/// <returns>The index of the head node of the removed blocks.</returns>
|
||||
public int Trim(int listHeadBlockIndex, int newListLength)
|
||||
{
|
||||
int blocksRemaining = newListLength;
|
||||
int next = BlockToEntryIndex(listHeadBlockIndex);
|
||||
int listAIndex = -1;
|
||||
int listBIndex = -1;
|
||||
|
||||
while (blocksRemaining > 0)
|
||||
{
|
||||
if (next < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int currentEntryIndex = next;
|
||||
|
||||
ReadEntry(EntryIndexToBlock(currentEntryIndex), out next, out int _, out int segmentLength);
|
||||
|
||||
int start = EntryIndexToBlock(currentEntryIndex);
|
||||
int end = start + segmentLength - 1;
|
||||
|
||||
next = BlockToEntryIndex(next);
|
||||
|
||||
if (segmentLength == blocksRemaining)
|
||||
{
|
||||
listAIndex = currentEntryIndex;
|
||||
listBIndex = next;
|
||||
}
|
||||
else if (segmentLength > blocksRemaining)
|
||||
{
|
||||
Split(EntryIndexToBlock(currentEntryIndex), blocksRemaining);
|
||||
|
||||
listAIndex = currentEntryIndex;
|
||||
listBIndex = currentEntryIndex + blocksRemaining;
|
||||
}
|
||||
|
||||
blocksRemaining -= segmentLength;
|
||||
}
|
||||
|
||||
if (listAIndex == -1 || listBIndex == -1) return -1;
|
||||
|
||||
AllocationTableEntry listANode = ReadEntry(listAIndex);
|
||||
AllocationTableEntry listBNode = ReadEntry(listBIndex);
|
||||
|
||||
listANode.SetNext(0);
|
||||
listBNode.MakeListStart();
|
||||
|
||||
WriteEntry(listAIndex, listANode);
|
||||
WriteEntry(listBIndex, listBNode);
|
||||
|
||||
return EntryIndexToBlock(listBIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits a single list segment into 2 segments. The sequence of blocks in the full list will remain the same.
|
||||
/// </summary>
|
||||
/// <param name="segmentBlockIndex">The block index of the segment to split.</param>
|
||||
/// <param name="firstSubSegmentLength">The length of the first subsegment.</param>
|
||||
public void Split(int segmentBlockIndex, int firstSubSegmentLength)
|
||||
{
|
||||
Debug.Assert(firstSubSegmentLength > 0);
|
||||
|
||||
int segAIndex = BlockToEntryIndex(segmentBlockIndex);
|
||||
|
||||
AllocationTableEntry segA = ReadEntry(segAIndex);
|
||||
if (!segA.IsMultiBlockSegment()) throw new ArgumentException("Cannot split a single-entry segment.");
|
||||
|
||||
AllocationTableEntry segARange = ReadEntry(segAIndex + 1);
|
||||
int originalLength = segARange.GetNext() - segARange.GetPrev() + 1;
|
||||
|
||||
if (firstSubSegmentLength >= originalLength)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(firstSubSegmentLength),
|
||||
$"Requested sub-segment length ({firstSubSegmentLength}) must be less than the full segment length ({originalLength})");
|
||||
}
|
||||
|
||||
int segBIndex = segAIndex + firstSubSegmentLength;
|
||||
|
||||
int segALength = firstSubSegmentLength;
|
||||
int segBLength = originalLength - segALength;
|
||||
|
||||
var segB = new AllocationTableEntry();
|
||||
|
||||
// Insert segment B between segments A and C
|
||||
segB.SetPrev(segAIndex);
|
||||
segB.SetNext(segA.GetNext());
|
||||
segA.SetNext(segBIndex);
|
||||
|
||||
if (!segB.IsListEnd())
|
||||
{
|
||||
AllocationTableEntry segC = ReadEntry(segB.GetNext());
|
||||
segC.SetPrev(segBIndex);
|
||||
WriteEntry(segB.GetNext(), segC);
|
||||
}
|
||||
|
||||
// Write the new range entries if needed
|
||||
if (segBLength > 1)
|
||||
{
|
||||
segB.MakeMultiBlockSegment();
|
||||
|
||||
var segBRange = new AllocationTableEntry();
|
||||
segBRange.SetRange(segBIndex, segBIndex + segBLength - 1);
|
||||
|
||||
WriteEntry(segBIndex + 1, segBRange);
|
||||
WriteEntry(segBIndex + segBLength - 1, segBRange);
|
||||
}
|
||||
|
||||
WriteEntry(segBIndex, segB);
|
||||
|
||||
if (segALength == 1)
|
||||
{
|
||||
segA.MakeSingleBlockSegment();
|
||||
}
|
||||
else
|
||||
{
|
||||
segARange.SetRange(segAIndex, segAIndex + segALength - 1);
|
||||
|
||||
WriteEntry(segAIndex + 1, segARange);
|
||||
WriteEntry(segAIndex + segALength - 1, segARange);
|
||||
}
|
||||
|
||||
WriteEntry(segAIndex, segA);
|
||||
}
|
||||
|
||||
public int GetListLength(int blockIndex)
|
||||
{
|
||||
int index = blockIndex;
|
||||
|
@ -195,6 +360,16 @@ namespace LibHac.IO.Save
|
|||
public int Prev;
|
||||
public int Next;
|
||||
|
||||
public int GetPrev()
|
||||
{
|
||||
return Prev & 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
public int GetNext()
|
||||
{
|
||||
return Next & 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
public bool IsListStart()
|
||||
{
|
||||
return Prev == int.MinValue;
|
||||
|
@ -248,6 +423,16 @@ namespace LibHac.IO.Save
|
|||
|
||||
Prev = value;
|
||||
}
|
||||
|
||||
public void SetRange(int startIndex, int endIndex)
|
||||
{
|
||||
Debug.Assert(startIndex > 0);
|
||||
Debug.Assert(endIndex > 0);
|
||||
|
||||
Next = endIndex;
|
||||
Prev = startIndex;
|
||||
MakeRangeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
public class AllocationTableHeader
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace LibHac.IO.Save
|
|||
AllocationTable = new AllocationTable(allocationTable, header.Slice(0x18, 0x30));
|
||||
|
||||
Header = new SaveHeader(HeaderStorage);
|
||||
|
||||
|
||||
// todo: Query the FAT for the file size when none is given
|
||||
AllocationTableStorage dirTableStorage = OpenFatBlock(AllocationTable.Header.DirectoryTableBlock, 1000000);
|
||||
AllocationTableStorage fileTableStorage = OpenFatBlock(AllocationTable.Header.FileTableBlock, 1000000);
|
||||
|
@ -34,7 +34,14 @@ namespace LibHac.IO.Save
|
|||
|
||||
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
path = PathTools.Normalize(path);
|
||||
|
||||
int blockCount = (int)Util.DivideByRoundUp(size, AllocationTable.Header.BlockSize);
|
||||
int startBlock = AllocationTable.Allocate(blockCount);
|
||||
|
||||
var fileEntry = new SaveFileInfo { StartBlock = startBlock, Length = size };
|
||||
|
||||
FileTable.AddFile(path, ref fileEntry);
|
||||
}
|
||||
|
||||
public void DeleteDirectory(string path)
|
||||
|
|
Loading…
Reference in a new issue