mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Print information about save files
This commit is contained in:
parent
7a5bd2347d
commit
a21283bf44
5 changed files with 211 additions and 113 deletions
|
@ -1,4 +1,5 @@
|
||||||
using System.IO;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace LibHac.Savefile
|
namespace LibHac.Savefile
|
||||||
|
@ -14,6 +15,7 @@ namespace LibHac.Savefile
|
||||||
|
|
||||||
public RemapHeader FileRemap { get; set; }
|
public RemapHeader FileRemap { get; set; }
|
||||||
public RemapHeader MetaRemap { get; set; }
|
public RemapHeader MetaRemap { get; set; }
|
||||||
|
public ExtraData ExtraData { get; set; }
|
||||||
|
|
||||||
public MapEntry[] FileMapEntries { get; set; }
|
public MapEntry[] FileMapEntries { get; set; }
|
||||||
public MapEntry[] MetaMapEntries { get; set; }
|
public MapEntry[] MetaMapEntries { get; set; }
|
||||||
|
@ -56,15 +58,18 @@ namespace LibHac.Savefile
|
||||||
reader.BaseStream.Position = 0x690;
|
reader.BaseStream.Position = 0x690;
|
||||||
MetaRemap = new RemapHeader(reader);
|
MetaRemap = new RemapHeader(reader);
|
||||||
|
|
||||||
reader.BaseStream.Position = Layout.MasterHashOffset0;
|
reader.BaseStream.Position = 0x6D8;
|
||||||
MasterHashA = reader.ReadBytes((int)Layout.MasterHashSize);
|
ExtraData = new ExtraData(reader);
|
||||||
reader.BaseStream.Position = Layout.MasterHashOffset1;
|
|
||||||
MasterHashB = reader.ReadBytes((int)Layout.MasterHashSize);
|
|
||||||
|
|
||||||
reader.BaseStream.Position = Layout.L1BitmapOffset0;
|
reader.BaseStream.Position = Layout.IvfcMasterHashOffsetA;
|
||||||
DuplexMasterA = reader.ReadBytes((int)Layout.L1BitmapSize);
|
MasterHashA = reader.ReadBytes((int)Layout.IvfcMasterHashSize);
|
||||||
reader.BaseStream.Position = Layout.L1BitmapOffset1;
|
reader.BaseStream.Position = Layout.IvfcMasterHashOffsetB;
|
||||||
DuplexMasterB = reader.ReadBytes((int)Layout.L1BitmapSize);
|
MasterHashB = reader.ReadBytes((int)Layout.IvfcMasterHashSize);
|
||||||
|
|
||||||
|
reader.BaseStream.Position = Layout.DuplexMasterOffsetA;
|
||||||
|
DuplexMasterA = reader.ReadBytes((int)Layout.DuplexMasterSize);
|
||||||
|
reader.BaseStream.Position = Layout.DuplexMasterOffsetB;
|
||||||
|
DuplexMasterB = reader.ReadBytes((int)Layout.DuplexMasterSize);
|
||||||
|
|
||||||
reader.BaseStream.Position = Layout.FileMapEntryOffset;
|
reader.BaseStream.Position = Layout.FileMapEntryOffset;
|
||||||
FileMapEntries = new MapEntry[FileRemap.MapEntryCount];
|
FileMapEntries = new MapEntry[FileRemap.MapEntryCount];
|
||||||
|
@ -82,8 +87,6 @@ namespace LibHac.Savefile
|
||||||
|
|
||||||
HeaderHashValidity = ValidateHeaderHash();
|
HeaderHashValidity = ValidateHeaderHash();
|
||||||
SignatureValidity = ValidateSignature(keyset);
|
SignatureValidity = ValidateSignature(keyset);
|
||||||
|
|
||||||
logger?.LogMessage($"Header hash is {HeaderHashValidity}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Validity ValidateHeaderHash()
|
private Validity ValidateHeaderHash()
|
||||||
|
@ -126,12 +129,12 @@ namespace LibHac.Savefile
|
||||||
public long JournalDataSizeA { get; set; }
|
public long JournalDataSizeA { get; set; }
|
||||||
public long JournalDataSizeB { get; set; }
|
public long JournalDataSizeB { get; set; }
|
||||||
public long SizeReservedArea { get; set; }
|
public long SizeReservedArea { get; set; }
|
||||||
public long L1BitmapOffset0 { get; set; }
|
public long DuplexMasterOffsetA { get; set; }
|
||||||
public long L1BitmapOffset1 { get; set; }
|
public long DuplexMasterOffsetB { get; set; }
|
||||||
public long L1BitmapSize { get; set; }
|
public long DuplexMasterSize { get; set; }
|
||||||
public long MasterHashOffset0 { get; set; }
|
public long IvfcMasterHashOffsetA { get; set; }
|
||||||
public long MasterHashOffset1 { get; set; }
|
public long IvfcMasterHashOffsetB { get; set; }
|
||||||
public long MasterHashSize { get; set; }
|
public long IvfcMasterHashSize { get; set; }
|
||||||
public long JournalTableOffset { get; set; }
|
public long JournalTableOffset { get; set; }
|
||||||
public long JournalTableSize { get; set; }
|
public long JournalTableSize { get; set; }
|
||||||
public long JournalBitmapUpdatedPhysicalOffset { get; set; }
|
public long JournalBitmapUpdatedPhysicalOffset { get; set; }
|
||||||
|
@ -140,14 +143,14 @@ namespace LibHac.Savefile
|
||||||
public long JournalBitmapUpdatedVirtualSize { get; set; }
|
public long JournalBitmapUpdatedVirtualSize { get; set; }
|
||||||
public long JournalBitmapUnassignedOffset { get; set; }
|
public long JournalBitmapUnassignedOffset { get; set; }
|
||||||
public long JournalBitmapUnassignedSize { get; set; }
|
public long JournalBitmapUnassignedSize { get; set; }
|
||||||
public long Layer1HashOffset { get; set; }
|
public long IvfcL1Offset { get; set; }
|
||||||
public long Layer1HashSize { get; set; }
|
public long IvfcL1Size { get; set; }
|
||||||
public long Layer2HashOffset { get; set; }
|
public long IvfcL2Offset { get; set; }
|
||||||
public long Layer2HashSize { get; set; }
|
public long IvfcL2Size { get; set; }
|
||||||
public long Layer3HashOffset { get; set; }
|
public long IvfcL3Offset { get; set; }
|
||||||
public long Layer3HashSize { get; set; }
|
public long IvfcL3Size { get; set; }
|
||||||
public long Field148 { get; set; }
|
public long FatOffset { get; set; }
|
||||||
public long Field150 { get; set; }
|
public long FatSize { get; set; }
|
||||||
public long DuplexIndex { get; set; }
|
public long DuplexIndex { get; set; }
|
||||||
|
|
||||||
public FsLayout(BinaryReader reader)
|
public FsLayout(BinaryReader reader)
|
||||||
|
@ -171,12 +174,12 @@ namespace LibHac.Savefile
|
||||||
JournalDataSizeA = reader.ReadInt64();
|
JournalDataSizeA = reader.ReadInt64();
|
||||||
JournalDataSizeB = reader.ReadInt64();
|
JournalDataSizeB = reader.ReadInt64();
|
||||||
SizeReservedArea = reader.ReadInt64();
|
SizeReservedArea = reader.ReadInt64();
|
||||||
L1BitmapOffset0 = reader.ReadInt64();
|
DuplexMasterOffsetA = reader.ReadInt64();
|
||||||
L1BitmapOffset1 = reader.ReadInt64();
|
DuplexMasterOffsetB = reader.ReadInt64();
|
||||||
L1BitmapSize = reader.ReadInt64();
|
DuplexMasterSize = reader.ReadInt64();
|
||||||
MasterHashOffset0 = reader.ReadInt64();
|
IvfcMasterHashOffsetA = reader.ReadInt64();
|
||||||
MasterHashOffset1 = reader.ReadInt64();
|
IvfcMasterHashOffsetB = reader.ReadInt64();
|
||||||
MasterHashSize = reader.ReadInt64();
|
IvfcMasterHashSize = reader.ReadInt64();
|
||||||
JournalTableOffset = reader.ReadInt64();
|
JournalTableOffset = reader.ReadInt64();
|
||||||
JournalTableSize = reader.ReadInt64();
|
JournalTableSize = reader.ReadInt64();
|
||||||
JournalBitmapUpdatedPhysicalOffset = reader.ReadInt64();
|
JournalBitmapUpdatedPhysicalOffset = reader.ReadInt64();
|
||||||
|
@ -185,15 +188,15 @@ namespace LibHac.Savefile
|
||||||
JournalBitmapUpdatedVirtualSize = reader.ReadInt64();
|
JournalBitmapUpdatedVirtualSize = reader.ReadInt64();
|
||||||
JournalBitmapUnassignedOffset = reader.ReadInt64();
|
JournalBitmapUnassignedOffset = reader.ReadInt64();
|
||||||
JournalBitmapUnassignedSize = reader.ReadInt64();
|
JournalBitmapUnassignedSize = reader.ReadInt64();
|
||||||
Layer1HashOffset = reader.ReadInt64();
|
IvfcL1Offset = reader.ReadInt64();
|
||||||
Layer1HashSize = reader.ReadInt64();
|
IvfcL1Size = reader.ReadInt64();
|
||||||
Layer2HashOffset = reader.ReadInt64();
|
IvfcL2Offset = reader.ReadInt64();
|
||||||
Layer2HashSize = reader.ReadInt64();
|
IvfcL2Size = reader.ReadInt64();
|
||||||
Layer3HashOffset = reader.ReadInt64();
|
IvfcL3Offset = reader.ReadInt64();
|
||||||
Layer3HashSize = reader.ReadInt64();
|
IvfcL3Size = reader.ReadInt64();
|
||||||
Field148 = reader.ReadInt64();
|
FatOffset = reader.ReadInt64();
|
||||||
Field150 = reader.ReadInt64();
|
FatSize = reader.ReadInt64();
|
||||||
DuplexIndex = reader.ReadInt64();
|
DuplexIndex = reader.ReadByte();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,24 +258,24 @@ namespace LibHac.Savefile
|
||||||
{
|
{
|
||||||
public string Magic { get; }
|
public string Magic { get; }
|
||||||
public uint MagicNum { get; }
|
public uint MagicNum { get; }
|
||||||
public long Field8 { get; }
|
public long TotalSize { get; }
|
||||||
public long Field10 { get; }
|
public long JournalSize { get; }
|
||||||
public long BlockSize { get; }
|
public long BlockSize { get; }
|
||||||
public int Field20 { get; }
|
public int Field20 { get; }
|
||||||
public int MappingEntryCount { get; }
|
public int MainDataBlockCount { get; }
|
||||||
public int Field28 { get; }
|
public int JournalBlockCount { get; }
|
||||||
public int Field2C { get; }
|
public int Field2C { get; }
|
||||||
|
|
||||||
public JournalHeader(BinaryReader reader)
|
public JournalHeader(BinaryReader reader)
|
||||||
{
|
{
|
||||||
Magic = reader.ReadAscii(4);
|
Magic = reader.ReadAscii(4);
|
||||||
MagicNum = reader.ReadUInt32();
|
MagicNum = reader.ReadUInt32();
|
||||||
Field8 = reader.ReadInt64();
|
TotalSize = reader.ReadInt64();
|
||||||
Field10 = reader.ReadInt64();
|
JournalSize = reader.ReadInt64();
|
||||||
BlockSize = reader.ReadInt64();
|
BlockSize = reader.ReadInt64();
|
||||||
Field20 = reader.ReadInt32();
|
Field20 = reader.ReadInt32();
|
||||||
MappingEntryCount = reader.ReadInt32();
|
MainDataBlockCount = reader.ReadInt32();
|
||||||
Field28 = reader.ReadInt32();
|
JournalBlockCount = reader.ReadInt32();
|
||||||
Field2C = reader.ReadInt32();
|
Field2C = reader.ReadInt32();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -342,4 +345,56 @@ namespace LibHac.Savefile
|
||||||
StorageType = reader.ReadInt32();
|
StorageType = reader.ReadInt32();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ExtraData
|
||||||
|
{
|
||||||
|
public ulong TitleId { get; }
|
||||||
|
public Guid UserId { get; }
|
||||||
|
public ulong SaveId { get; }
|
||||||
|
public SaveDataType Type { get; }
|
||||||
|
|
||||||
|
public ulong SaveOwnerId { get; }
|
||||||
|
public long Timestamp { get; }
|
||||||
|
public long Field50 { get; }
|
||||||
|
public uint Field54 { get; }
|
||||||
|
public long DataSize { get; }
|
||||||
|
public long JournalSize { get; }
|
||||||
|
|
||||||
|
public ExtraData(BinaryReader reader)
|
||||||
|
{
|
||||||
|
TitleId = reader.ReadUInt64();
|
||||||
|
UserId = ToGuid(reader.ReadBytes(0x10));
|
||||||
|
SaveId = reader.ReadUInt64();
|
||||||
|
Type = (SaveDataType)reader.ReadByte();
|
||||||
|
reader.BaseStream.Position += 0x1f;
|
||||||
|
|
||||||
|
SaveOwnerId = reader.ReadUInt64();
|
||||||
|
Timestamp = reader.ReadInt64();
|
||||||
|
Field50 = reader.ReadUInt32();
|
||||||
|
Field54 = reader.ReadUInt32();
|
||||||
|
DataSize = reader.ReadInt64();
|
||||||
|
JournalSize = reader.ReadInt64();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Guid ToGuid(byte[] bytes)
|
||||||
|
{
|
||||||
|
var b = new byte[0x10];
|
||||||
|
Array.Copy(bytes, b, 0x10);
|
||||||
|
|
||||||
|
// The Guid constructor uses a weird, mixed-endian format
|
||||||
|
Array.Reverse(b, 10, 6);
|
||||||
|
|
||||||
|
return new Guid(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SaveDataType
|
||||||
|
{
|
||||||
|
SystemSaveData,
|
||||||
|
SaveData,
|
||||||
|
BcatDeliveryCacheStorage,
|
||||||
|
DeviceSaveData,
|
||||||
|
TemporaryStorage,
|
||||||
|
CacheStorage
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,8 +109,5 @@ namespace LibHac.Savefile
|
||||||
{
|
{
|
||||||
public int PhysicalIndex { get; set; }
|
public int PhysicalIndex { get; set; }
|
||||||
public int VirtualIndex { get; set; }
|
public int VirtualIndex { get; set; }
|
||||||
public bool UpdatedPhysical { get; set; }
|
|
||||||
public bool UpdatedVirtual { get; set; }
|
|
||||||
public bool Unassigned { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,13 +93,13 @@ namespace LibHac.Savefile
|
||||||
JournalBitmapUpdatedPhysical = MetaRemapSource.CreateStream(layout.JournalBitmapUpdatedPhysicalOffset, layout.JournalBitmapUpdatedPhysicalSize);
|
JournalBitmapUpdatedPhysical = MetaRemapSource.CreateStream(layout.JournalBitmapUpdatedPhysicalOffset, layout.JournalBitmapUpdatedPhysicalSize);
|
||||||
JournalBitmapUpdatedVirtual = MetaRemapSource.CreateStream(layout.JournalBitmapUpdatedVirtualOffset, layout.JournalBitmapUpdatedVirtualSize);
|
JournalBitmapUpdatedVirtual = MetaRemapSource.CreateStream(layout.JournalBitmapUpdatedVirtualOffset, layout.JournalBitmapUpdatedVirtualSize);
|
||||||
JournalBitmapUnassigned = MetaRemapSource.CreateStream(layout.JournalBitmapUnassignedOffset, layout.JournalBitmapUnassignedSize);
|
JournalBitmapUnassigned = MetaRemapSource.CreateStream(layout.JournalBitmapUnassignedOffset, layout.JournalBitmapUnassignedSize);
|
||||||
JournalLayer1Hash = MetaRemapSource.CreateStream(layout.Layer1HashOffset, layout.Layer1HashSize);
|
JournalLayer1Hash = MetaRemapSource.CreateStream(layout.IvfcL1Offset, layout.IvfcL1Size);
|
||||||
JournalLayer2Hash = MetaRemapSource.CreateStream(layout.Layer2HashOffset, layout.Layer2HashSize);
|
JournalLayer2Hash = MetaRemapSource.CreateStream(layout.IvfcL2Offset, layout.IvfcL2Size);
|
||||||
JournalLayer3Hash = MetaRemapSource.CreateStream(layout.Layer3HashOffset, layout.Layer3HashSize);
|
JournalLayer3Hash = MetaRemapSource.CreateStream(layout.IvfcL3Offset, layout.IvfcL3Size);
|
||||||
JournalFat = MetaRemapSource.CreateStream(layout.Field148, layout.Field150);
|
JournalFat = MetaRemapSource.CreateStream(layout.FatOffset, layout.FatSize);
|
||||||
AllocationTable = new AllocationTable(JournalFat);
|
AllocationTable = new AllocationTable(JournalFat);
|
||||||
|
|
||||||
var journalMap = JournalStream.ReadMappingEntries(JournalTable, Header.Journal.MappingEntryCount);
|
var journalMap = JournalStream.ReadMappingEntries(JournalTable, Header.Journal.MainDataBlockCount);
|
||||||
|
|
||||||
var journalData = FileRemapSource.CreateStream(layout.JournalDataOffset,
|
var journalData = FileRemapSource.CreateStream(layout.JournalDataOffset,
|
||||||
layout.JournalDataSizeB + layout.SizeReservedArea);
|
layout.JournalDataSizeB + layout.SizeReservedArea);
|
||||||
|
|
100
hactoolnet/ProcessSave.cs
Normal file
100
hactoolnet/ProcessSave.cs
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using LibHac;
|
||||||
|
using LibHac.Savefile;
|
||||||
|
using static hactoolnet.Print;
|
||||||
|
|
||||||
|
namespace hactoolnet
|
||||||
|
{
|
||||||
|
internal static class ProcessSave
|
||||||
|
{
|
||||||
|
public static void Process(Context ctx)
|
||||||
|
{
|
||||||
|
using (var file = new FileStream(ctx.Options.InFile, FileMode.Open, FileAccess.ReadWrite))
|
||||||
|
{
|
||||||
|
var save = new Savefile(ctx.Keyset, file, ctx.Options.EnableHash, ctx.Logger);
|
||||||
|
|
||||||
|
if (ctx.Options.OutDir != null)
|
||||||
|
{
|
||||||
|
save.Extract(ctx.Options.OutDir, ctx.Logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.Options.DebugOutDir != null)
|
||||||
|
{
|
||||||
|
var dir = ctx.Options.DebugOutDir;
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
|
||||||
|
File.WriteAllBytes(Path.Combine(dir, "L0_0_MasterHashA"), save.Header.MasterHashA);
|
||||||
|
File.WriteAllBytes(Path.Combine(dir, "L0_1_MasterHashB"), save.Header.MasterHashB);
|
||||||
|
File.WriteAllBytes(Path.Combine(dir, "L0_2_DuplexMasterA"), save.Header.DuplexMasterA);
|
||||||
|
File.WriteAllBytes(Path.Combine(dir, "L0_3_DuplexMasterB"), save.Header.DuplexMasterB);
|
||||||
|
save.DuplexL1A.WriteAllBytes(Path.Combine(dir, "L0_4_DuplexL1A"), ctx.Logger);
|
||||||
|
save.DuplexL1B.WriteAllBytes(Path.Combine(dir, "L0_5_DuplexL1B"), ctx.Logger);
|
||||||
|
save.DuplexDataA.WriteAllBytes(Path.Combine(dir, "L0_6_DuplexDataA"), ctx.Logger);
|
||||||
|
save.DuplexDataB.WriteAllBytes(Path.Combine(dir, "L0_7_DuplexDataB"), ctx.Logger);
|
||||||
|
save.JournalData.WriteAllBytes(Path.Combine(dir, "L0_9_JournalData"), ctx.Logger);
|
||||||
|
|
||||||
|
save.DuplexData.WriteAllBytes(Path.Combine(dir, "L1_0_DuplexData"), ctx.Logger);
|
||||||
|
save.JournalTable.WriteAllBytes(Path.Combine(dir, "L2_0_JournalTable"), ctx.Logger);
|
||||||
|
save.JournalBitmapUpdatedPhysical.WriteAllBytes(Path.Combine(dir, "L2_1_JournalBitmapUpdatedPhysical"), ctx.Logger);
|
||||||
|
save.JournalBitmapUpdatedVirtual.WriteAllBytes(Path.Combine(dir, "L2_2_JournalBitmapUpdatedVirtual"), ctx.Logger);
|
||||||
|
save.JournalBitmapUnassigned.WriteAllBytes(Path.Combine(dir, "L2_3_JournalBitmapUnassigned"), ctx.Logger);
|
||||||
|
save.JournalLayer1Hash.WriteAllBytes(Path.Combine(dir, "L2_4_Layer1Hash"), ctx.Logger);
|
||||||
|
save.JournalLayer2Hash.WriteAllBytes(Path.Combine(dir, "L2_5_Layer2Hash"), ctx.Logger);
|
||||||
|
save.JournalLayer3Hash.WriteAllBytes(Path.Combine(dir, "L2_6_Layer3Hash"), ctx.Logger);
|
||||||
|
save.JournalFat.WriteAllBytes(Path.Combine(dir, "L2_7_FAT"), ctx.Logger);
|
||||||
|
|
||||||
|
save.IvfcStreamSource.CreateStream().WriteAllBytes(Path.Combine(dir, "L3_0_SaveData"), ctx.Logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.Options.SignSave)
|
||||||
|
{
|
||||||
|
if (save.SignHeader(ctx.Keyset))
|
||||||
|
{
|
||||||
|
ctx.Logger.LogMessage("Successfully signed save file");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ctx.Logger.LogMessage("Unable to sign save file. Do you have all the required keys?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Logger.LogMessage(save.Print());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Print(this Savefile save)
|
||||||
|
{
|
||||||
|
int colLen = 25;
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
sb.AppendLine("Savefile:");
|
||||||
|
PrintItem(sb, colLen, "CMAC Signature:", save.Header.Cmac);
|
||||||
|
PrintItem(sb, colLen, "Title ID:", $"{save.Header.ExtraData.TitleId:x16}");
|
||||||
|
PrintItem(sb, colLen, "User ID:", save.Header.ExtraData.UserId);
|
||||||
|
PrintItem(sb, colLen, "Save ID:", $"{save.Header.ExtraData.SaveId:x16}");
|
||||||
|
PrintItem(sb, colLen, "Save Type:", $"{save.Header.ExtraData.Type}");
|
||||||
|
PrintItem(sb, colLen, "Owner ID:", $"{save.Header.ExtraData.SaveOwnerId:x16}");
|
||||||
|
PrintItem(sb, colLen, "Timestamp:", $"{DateTimeOffset.FromUnixTimeSeconds(save.Header.ExtraData.Timestamp):yyyy-MM-dd HH:mm:ss} UTC");
|
||||||
|
PrintItem(sb, colLen, "Save Data Size:", $"0x{save.Header.ExtraData.DataSize:x16} ({Util.GetBytesReadable(save.Header.ExtraData.DataSize)})");
|
||||||
|
PrintItem(sb, colLen, "Journal Size:", $"0x{save.Header.ExtraData.JournalSize:x16} ({Util.GetBytesReadable(save.Header.ExtraData.JournalSize)})");
|
||||||
|
PrintItem(sb, colLen, $"Header Hash{save.Header.HeaderHashValidity.GetValidityString()}:", save.Header.Layout.Hash);
|
||||||
|
PrintItem(sb, colLen, "IVFC Salt Seed:", save.Header.Ivfc.SaltSource);
|
||||||
|
PrintItem(sb, colLen, "Number of Files:", save.Files.Length);
|
||||||
|
|
||||||
|
if (save.Files.Length > 0 && save.Files.Length < 100)
|
||||||
|
{
|
||||||
|
sb.AppendLine("Files:");
|
||||||
|
foreach (FileEntry file in save.Files.OrderBy(x => x.FullPath))
|
||||||
|
{
|
||||||
|
sb.AppendLine(file.FullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,6 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using LibHac;
|
using LibHac;
|
||||||
using LibHac.Savefile;
|
|
||||||
|
|
||||||
namespace hactoolnet
|
namespace hactoolnet
|
||||||
{
|
{
|
||||||
|
@ -42,7 +41,7 @@ namespace hactoolnet
|
||||||
ProcessSwitchFs.Process(ctx);
|
ProcessSwitchFs.Process(ctx);
|
||||||
break;
|
break;
|
||||||
case FileType.Save:
|
case FileType.Save:
|
||||||
ProcessSave(ctx);
|
ProcessSave.Process(ctx);
|
||||||
break;
|
break;
|
||||||
case FileType.Xci:
|
case FileType.Xci:
|
||||||
ProcessXci.Process(ctx);
|
ProcessXci.Process(ctx);
|
||||||
|
@ -100,59 +99,6 @@ namespace hactoolnet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ProcessSave(Context ctx)
|
|
||||||
{
|
|
||||||
using (var file = new FileStream(ctx.Options.InFile, FileMode.Open, FileAccess.ReadWrite))
|
|
||||||
{
|
|
||||||
var save = new Savefile(ctx.Keyset, file, ctx.Options.EnableHash, ctx.Logger);
|
|
||||||
|
|
||||||
if (ctx.Options.OutDir != null)
|
|
||||||
{
|
|
||||||
save.Extract(ctx.Options.OutDir, ctx.Logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.Options.DebugOutDir != null)
|
|
||||||
{
|
|
||||||
var dir = ctx.Options.DebugOutDir;
|
|
||||||
Directory.CreateDirectory(dir);
|
|
||||||
|
|
||||||
File.WriteAllBytes(Path.Combine(dir, "L0_0_MasterHashA"), save.Header.MasterHashA);
|
|
||||||
File.WriteAllBytes(Path.Combine(dir, "L0_1_MasterHashB"), save.Header.MasterHashB);
|
|
||||||
File.WriteAllBytes(Path.Combine(dir, "L0_2_DuplexMasterA"), save.Header.DuplexMasterA);
|
|
||||||
File.WriteAllBytes(Path.Combine(dir, "L0_3_DuplexMasterB"), save.Header.DuplexMasterB);
|
|
||||||
save.DuplexL1A.WriteAllBytes(Path.Combine(dir, "L0_4_DuplexL1A"), ctx.Logger);
|
|
||||||
save.DuplexL1B.WriteAllBytes(Path.Combine(dir, "L0_5_DuplexL1B"), ctx.Logger);
|
|
||||||
save.DuplexDataA.WriteAllBytes(Path.Combine(dir, "L0_6_DuplexDataA"), ctx.Logger);
|
|
||||||
save.DuplexDataB.WriteAllBytes(Path.Combine(dir, "L0_7_DuplexDataB"), ctx.Logger);
|
|
||||||
save.JournalData.WriteAllBytes(Path.Combine(dir, "L0_9_JournalData"), ctx.Logger);
|
|
||||||
|
|
||||||
save.DuplexData.WriteAllBytes(Path.Combine(dir, "L1_0_DuplexData"), ctx.Logger);
|
|
||||||
save.JournalTable.WriteAllBytes(Path.Combine(dir, "L2_0_JournalTable"), ctx.Logger);
|
|
||||||
save.JournalBitmapUpdatedPhysical.WriteAllBytes(Path.Combine(dir, "L2_1_JournalBitmapUpdatedPhysical"), ctx.Logger);
|
|
||||||
save.JournalBitmapUpdatedVirtual.WriteAllBytes(Path.Combine(dir, "L2_2_JournalBitmapUpdatedVirtual"), ctx.Logger);
|
|
||||||
save.JournalBitmapUnassigned.WriteAllBytes(Path.Combine(dir, "L2_3_JournalBitmapUnassigned"), ctx.Logger);
|
|
||||||
save.JournalLayer1Hash.WriteAllBytes(Path.Combine(dir, "L2_4_Layer1Hash"), ctx.Logger);
|
|
||||||
save.JournalLayer2Hash.WriteAllBytes(Path.Combine(dir, "L2_5_Layer2Hash"), ctx.Logger);
|
|
||||||
save.JournalLayer3Hash.WriteAllBytes(Path.Combine(dir, "L2_6_Layer3Hash"), ctx.Logger);
|
|
||||||
save.JournalFat.WriteAllBytes(Path.Combine(dir, "L2_7_FAT"), ctx.Logger);
|
|
||||||
|
|
||||||
save.IvfcStreamSource.CreateStream().WriteAllBytes(Path.Combine(dir, "L3_0_SaveData"), ctx.Logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.Options.SignSave)
|
|
||||||
{
|
|
||||||
if (save.SignHeader(ctx.Keyset))
|
|
||||||
{
|
|
||||||
ctx.Logger.LogMessage("Successfully signed save file");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ctx.Logger.LogMessage("Unable to sign save file. Do you have all the required keys?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ProcessKeygen(Context ctx)
|
private static void ProcessKeygen(Context ctx)
|
||||||
{
|
{
|
||||||
Console.WriteLine(ExternalKeys.PrintCommonKeys(ctx.Keyset));
|
Console.WriteLine(ExternalKeys.PrintCommonKeys(ctx.Keyset));
|
||||||
|
@ -161,8 +107,8 @@ namespace hactoolnet
|
||||||
// For running random stuff
|
// For running random stuff
|
||||||
// ReSharper disable once UnusedParameter.Local
|
// ReSharper disable once UnusedParameter.Local
|
||||||
private static void CustomTask(Context ctx)
|
private static void CustomTask(Context ctx)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue