mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add FsTrim for savedata
This commit is contained in:
parent
9c0e6030e5
commit
5c84f5c2a4
8 changed files with 134 additions and 9 deletions
|
@ -299,6 +299,42 @@ namespace LibHac.IO.Save
|
|||
return totalLength;
|
||||
}
|
||||
|
||||
public void FsTrimList(int blockIndex)
|
||||
{
|
||||
int index = blockIndex;
|
||||
|
||||
int tableSize = Header.AllocationTableBlockCount;
|
||||
int nodesIterated = 0;
|
||||
|
||||
while (index != -1)
|
||||
{
|
||||
ReadEntry(index, out int next, out int _, out int length);
|
||||
|
||||
if (length > 3)
|
||||
{
|
||||
int fillOffset = BlockToEntryIndex(index + 2) * EntrySize;
|
||||
int fillLength = (length - 3) * EntrySize;
|
||||
|
||||
BaseStorage.Slice(fillOffset, fillLength).Fill(0x00);
|
||||
}
|
||||
|
||||
nodesIterated++;
|
||||
|
||||
if (nodesIterated > tableSize)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
index = next;
|
||||
}
|
||||
}
|
||||
|
||||
public void FsTrim()
|
||||
{
|
||||
int tableSize = BlockToEntryIndex(Header.AllocationTableBlockCount) * EntrySize;
|
||||
BaseStorage.Slice(tableSize).Fill(0x00);
|
||||
}
|
||||
|
||||
private void ReadEntries(int entryIndex, Span<AllocationTableEntry> entries)
|
||||
{
|
||||
Debug.Assert(entries.Length >= 2);
|
||||
|
|
|
@ -11,13 +11,14 @@ namespace LibHac.IO.Save
|
|||
|
||||
private long _length;
|
||||
|
||||
public AllocationTableStorage(IStorage data, AllocationTable table, int blockSize, int initialBlock, long length)
|
||||
public AllocationTableStorage(IStorage data, AllocationTable table, int blockSize, int initialBlock)
|
||||
{
|
||||
BaseStorage = data;
|
||||
BlockSize = blockSize;
|
||||
_length = length;
|
||||
Fat = table;
|
||||
InitialBlock = initialBlock;
|
||||
|
||||
_length = table.GetListLength(initialBlock) * blockSize;
|
||||
}
|
||||
|
||||
protected override void ReadImpl(Span<byte> destination, long offset)
|
||||
|
|
|
@ -200,6 +200,11 @@ namespace LibHac.IO.Save
|
|||
return true;
|
||||
}
|
||||
|
||||
public void FsTrim()
|
||||
{
|
||||
SaveDataFileSystemCore.FsTrim();
|
||||
}
|
||||
|
||||
public Validity Verify(IProgressReport logger = null)
|
||||
{
|
||||
Validity validity = IvfcStorage.Validate(true, logger);
|
||||
|
|
|
@ -20,9 +20,8 @@ namespace LibHac.IO.Save
|
|||
|
||||
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);
|
||||
AllocationTableStorage dirTableStorage = OpenFatStorage(AllocationTable.Header.DirectoryTableBlock);
|
||||
AllocationTableStorage fileTableStorage = OpenFatStorage(AllocationTable.Header.FileTableBlock);
|
||||
|
||||
FileTable = new HierarchicalSaveFileTable(dirTableStorage, fileTableStorage);
|
||||
}
|
||||
|
@ -92,7 +91,7 @@ namespace LibHac.IO.Save
|
|||
return new NullFile();
|
||||
}
|
||||
|
||||
AllocationTableStorage storage = OpenFatBlock(file.StartBlock, file.Length);
|
||||
AllocationTableStorage storage = OpenFatStorage(file.StartBlock);
|
||||
|
||||
return new SaveDataFile(storage, 0, file.Length, mode);
|
||||
}
|
||||
|
@ -139,9 +138,31 @@ namespace LibHac.IO.Save
|
|||
public IStorage GetBaseStorage() => BaseStorage.AsReadOnly();
|
||||
public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly();
|
||||
|
||||
private AllocationTableStorage OpenFatBlock(int blockIndex, long size)
|
||||
public void FsTrim()
|
||||
{
|
||||
return new AllocationTableStorage(BaseStorage, AllocationTable, (int)Header.BlockSize, blockIndex, size);
|
||||
AllocationTable.FsTrim();
|
||||
|
||||
foreach (DirectoryEntry file in this.EnumerateEntries("*", SearchOptions.RecurseSubdirectories))
|
||||
{
|
||||
if (FileTable.TryOpenFile(file.FullPath, out SaveFileInfo fileInfo) && fileInfo.StartBlock >= 0)
|
||||
{
|
||||
AllocationTable.FsTrimList(fileInfo.StartBlock);
|
||||
|
||||
OpenFatStorage(fileInfo.StartBlock).Slice(fileInfo.Length).Fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
int freeIndex = AllocationTable.GetFreeListBlockIndex();
|
||||
if (freeIndex == 0) return;
|
||||
|
||||
AllocationTable.FsTrimList(freeIndex);
|
||||
|
||||
OpenFatStorage(freeIndex).Fill(0);
|
||||
}
|
||||
|
||||
private AllocationTableStorage OpenFatStorage(int blockIndex)
|
||||
{
|
||||
return new AllocationTableStorage(BaseStorage, AllocationTable, (int)Header.BlockSize, blockIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -97,6 +97,59 @@ namespace LibHac.IO
|
|||
progress?.SetTotal(0);
|
||||
}
|
||||
|
||||
public static void Fill(this IStorage input, byte value, IProgressReport progress = null)
|
||||
{
|
||||
const int threshold = 0x400;
|
||||
|
||||
long length = input.GetSize();
|
||||
if (length > threshold)
|
||||
{
|
||||
input.FillLarge(value, progress);
|
||||
return;
|
||||
}
|
||||
|
||||
Span<byte> buf = stackalloc byte[(int)length];
|
||||
buf.Fill(value);
|
||||
|
||||
input.Write(buf, 0);
|
||||
}
|
||||
|
||||
private static void FillLarge(this IStorage input, byte value, IProgressReport progress = null)
|
||||
{
|
||||
const int bufferSize = 0x4000;
|
||||
|
||||
long remaining = input.GetSize();
|
||||
if (remaining < 0) throw new ArgumentException("Storage must have an explicit length");
|
||||
progress?.SetTotal(remaining);
|
||||
|
||||
long pos = 0;
|
||||
|
||||
byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||
try
|
||||
{
|
||||
buffer.AsSpan(0, (int)Math.Min(remaining, bufferSize)).Fill(value);
|
||||
|
||||
while (remaining > 0)
|
||||
{
|
||||
int toFill = (int)Math.Min(bufferSize, remaining);
|
||||
Span<byte> buf = buffer.AsSpan(0, toFill);
|
||||
|
||||
input.Write(buf, pos);
|
||||
|
||||
remaining -= toFill;
|
||||
pos += toFill;
|
||||
|
||||
progress?.ReportAdd(toFill);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
|
||||
progress?.SetTotal(0);
|
||||
}
|
||||
|
||||
public static void WriteAllBytes(this IStorage input, string filename, IProgressReport progress = null)
|
||||
{
|
||||
using (var outFile = new FileStream(filename, FileMode.Create, FileAccess.Write))
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace hactoolnet
|
|||
new CliOption("listromfs", 0, (o, a) => o.ListRomFs = true),
|
||||
new CliOption("listfiles", 0, (o, a) => o.ListFiles = true),
|
||||
new CliOption("sign", 0, (o, a) => o.SignSave = true),
|
||||
new CliOption("trim", 0, (o, a) => o.TrimSave = true),
|
||||
new CliOption("readbench", 0, (o, a) => o.ReadBench = true),
|
||||
new CliOption("hashedfs", 0, (o, a) => o.BuildHfs = true),
|
||||
new CliOption("title", 1, (o, a) => o.TitleId = ParseTitleId(a[0])),
|
||||
|
@ -232,6 +233,7 @@ namespace hactoolnet
|
|||
sb.AppendLine(" --outdir <dir> Specify directory path to save contents to.");
|
||||
sb.AppendLine(" --debugoutdir <dir> Specify directory path to save intermediate data to for debugging.");
|
||||
sb.AppendLine(" --sign Sign the save file. (Requires device_key in key file)");
|
||||
sb.AppendLine(" --trim Trim garbage data in the save file. (Requires device_key in key file)");
|
||||
sb.AppendLine(" --listfiles List files in save file.");
|
||||
sb.AppendLine(" --replacefile <filename in save> <file> Replaces a file in the save data");
|
||||
sb.AppendLine("NDV0 (Delta) options:");
|
||||
|
|
|
@ -45,6 +45,7 @@ namespace hactoolnet
|
|||
public bool ListRomFs;
|
||||
public bool ListFiles;
|
||||
public bool SignSave;
|
||||
public bool TrimSave;
|
||||
public bool ReadBench;
|
||||
public bool BuildHfs;
|
||||
public ulong TitleId;
|
||||
|
|
|
@ -129,8 +129,14 @@ namespace hactoolnet
|
|||
return;
|
||||
}
|
||||
|
||||
if (ctx.Options.SignSave)
|
||||
if (ctx.Options.SignSave || ctx.Options.TrimSave)
|
||||
{
|
||||
if (ctx.Options.TrimSave)
|
||||
{
|
||||
save.FsTrim();
|
||||
ctx.Logger.LogMessage("Trimmed save file");
|
||||
}
|
||||
|
||||
if (save.Commit(ctx.Keyset))
|
||||
{
|
||||
ctx.Logger.LogMessage("Successfully signed save file");
|
||||
|
|
Loading…
Reference in a new issue