From 7bb99ff92691297bc41d4ce8476d6a6f57502adf Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Fri, 8 Mar 2019 19:18:38 -0600 Subject: [PATCH] Add code for printing save FAT chains --- src/LibHac/IO/Save/AllocationTableIterator.cs | 6 +- src/LibHac/IO/Save/SaveDataFileSystemCore.cs | 1 + src/LibHac/IO/Save/SaveExtensions.cs | 17 ++++ src/LibHac/LibHac.csproj | 5 +- src/hactoolnet/ProcessSave.cs | 82 +++++++++++++++++++ 5 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 src/LibHac/IO/Save/SaveExtensions.cs diff --git a/src/LibHac/IO/Save/AllocationTableIterator.cs b/src/LibHac/IO/Save/AllocationTableIterator.cs index 78adec21..e64a6882 100644 --- a/src/LibHac/IO/Save/AllocationTableIterator.cs +++ b/src/LibHac/IO/Save/AllocationTableIterator.cs @@ -16,7 +16,7 @@ namespace LibHac.IO.Save if (!BeginIteration(initialBlock)) { - throw new ArgumentException($"Attempted to start FAT iteration from an invalid block. ({initialBlock}"); + throw new ArgumentException($"Attempted to start FAT iteration from an invalid block. ({initialBlock})"); } } @@ -24,9 +24,9 @@ namespace LibHac.IO.Save { AllocationTableEntry tableEntry = Fat.Entries[initialBlock + 1]; - if (!tableEntry.IsListStart()) + if (!tableEntry.IsListStart() && initialBlock != -1) { - return false; + return false; } if (tableEntry.IsSingleBlockSegment()) diff --git a/src/LibHac/IO/Save/SaveDataFileSystemCore.cs b/src/LibHac/IO/Save/SaveDataFileSystemCore.cs index 22a108b3..35a8cdd1 100644 --- a/src/LibHac/IO/Save/SaveDataFileSystemCore.cs +++ b/src/LibHac/IO/Save/SaveDataFileSystemCore.cs @@ -152,6 +152,7 @@ namespace LibHac.IO.Save public IStorage GetBaseStorage() => BaseStorage.AsReadOnly(); public IStorage GetHeaderStorage() => HeaderStorage.AsReadOnly(); + public SaveFileEntry GetFileEntry(string path) => FileDictionary[path]; private void ReadFileInfo() { diff --git a/src/LibHac/IO/Save/SaveExtensions.cs b/src/LibHac/IO/Save/SaveExtensions.cs new file mode 100644 index 00000000..64d114f8 --- /dev/null +++ b/src/LibHac/IO/Save/SaveExtensions.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace LibHac.IO.Save +{ + public static class SaveExtensions + { + public static IEnumerable<(int block, int length)> DumpChain(this AllocationTable table, int startBlock) + { + var iterator = new AllocationTableIterator(table, startBlock); + + do + { + yield return (iterator.PhysicalBlock, iterator.CurrentSegmentSize); + } while (iterator.MoveNext()); + } + } +} diff --git a/src/LibHac/LibHac.csproj b/src/LibHac/LibHac.csproj index 8f04d210..5e7e8904 100644 --- a/src/LibHac/LibHac.csproj +++ b/src/LibHac/LibHac.csproj @@ -34,9 +34,10 @@ - - + + + diff --git a/src/hactoolnet/ProcessSave.cs b/src/hactoolnet/ProcessSave.cs index bd6ed932..a7d70704 100644 --- a/src/hactoolnet/ProcessSave.cs +++ b/src/hactoolnet/ProcessSave.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; @@ -151,6 +152,87 @@ namespace hactoolnet } ctx.Logger.LogMessage(save.Print()); + //ctx.Logger.LogMessage(PrintFatLayout(save)); + } + } + + // ReSharper disable once UnusedMember.Local + private static string PrintFatLayout(this SaveDataFileSystem save) + { + var sb = new StringBuilder(); + + foreach (DirectoryEntry entry in save.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File)) + { + SaveFileEntry saveEntry = save.SaveDataFileSystemCore.GetFileEntry(entry.FullPath); + + if (saveEntry.BlockIndex < 0) continue; + + IEnumerable<(int block, int length)> chain = save.SaveDataFileSystemCore.AllocationTable.DumpChain(saveEntry.BlockIndex); + + sb.AppendLine(entry.FullPath); + sb.AppendLine(PrintBlockChain(chain)); + } + + sb.AppendLine("Directory Table"); + sb.AppendLine(PrintBlockChain(save.SaveDataFileSystemCore.AllocationTable.DumpChain(0))); + + sb.AppendLine("File Table"); + sb.AppendLine(PrintBlockChain(save.SaveDataFileSystemCore.AllocationTable.DumpChain(1))); + + sb.AppendLine("Free blocks"); + sb.AppendLine(PrintBlockChain(save.SaveDataFileSystemCore.AllocationTable.DumpChain(-1))); + + return sb.ToString(); + } + + private static string PrintBlockChain(IEnumerable<(int block, int length)> chain) + { + var sb = new StringBuilder(); + int segmentCount = 0; + int segmentStart = -1; + int segmentEnd = -1; + + foreach ((int block, int length) in chain) + { + if (segmentStart == -1) + { + segmentStart = block; + segmentEnd = block + length - 1; + continue; + } + + if (block == segmentEnd + 1) + { + segmentEnd += length; + continue; + } + + PrintSegment(); + + segmentStart = block; + segmentEnd = block + length - 1; + } + + PrintSegment(); + + return sb.ToString(); + + void PrintSegment() + { + if (segmentCount > 0) sb.Append(", "); + + if (segmentStart == segmentEnd) + { + sb.Append(segmentStart); + } + else + { + sb.Append($"{segmentStart}-{segmentEnd}"); + } + + segmentCount++; + segmentStart = -1; + segmentEnd = -1; } }