Support hashed partition FS building

This commit is contained in:
Alex Barney 2019-02-26 22:13:47 -06:00
parent 468c78aadd
commit caccc5a677
4 changed files with 64 additions and 7 deletions

View file

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Security.Cryptography;
using System.Text; using System.Text;
namespace LibHac.IO namespace LibHac.IO
@ -38,7 +39,9 @@ namespace LibHac.IO
File = file, File = file,
Length = file.GetSize(), Length = file.GetSize(),
Offset = CurrentOffset, Offset = CurrentOffset,
NameLength = Encoding.UTF8.GetByteCount(filename) NameLength = Encoding.UTF8.GetByteCount(filename),
HashOffset = 0,
HashLength = 0x200
}; };
CurrentOffset += entry.Length; CurrentOffset += entry.Length;
@ -60,8 +63,10 @@ namespace LibHac.IO
private byte[] BuildMetaData(PartitionFileSystemType type) private byte[] BuildMetaData(PartitionFileSystemType type)
{ {
if (type == PartitionFileSystemType.Hashed) CalculateHashes();
int entryTableSize = Entries.Count * PartitionFileEntry.GetEntrySize(type); int entryTableSize = Entries.Count * PartitionFileEntry.GetEntrySize(type);
int stringTableSize = CalcStringTableSize(HeaderSize + entryTableSize); int stringTableSize = CalcStringTableSize(HeaderSize + entryTableSize, type);
int metaDataSize = HeaderSize + entryTableSize + stringTableSize; int metaDataSize = HeaderSize + entryTableSize + stringTableSize;
var metaData = new byte[metaDataSize]; var metaData = new byte[metaDataSize];
@ -79,7 +84,17 @@ namespace LibHac.IO
writer.Write(entry.Offset); writer.Write(entry.Offset);
writer.Write(entry.Length); writer.Write(entry.Length);
writer.Write(stringOffset); writer.Write(stringOffset);
writer.Write(0);
if (type == PartitionFileSystemType.Standard)
{
writer.Write(0);
}
else
{
writer.Write(entry.HashLength);
writer.Write(entry.HashOffset);
writer.Write(entry.Hash);
}
stringOffset += entry.NameLength + 1; stringOffset += entry.NameLength + 1;
} }
@ -92,7 +107,7 @@ namespace LibHac.IO
return metaData; return metaData;
} }
private int CalcStringTableSize(int startOffset) private int CalcStringTableSize(int startOffset, PartitionFileSystemType type)
{ {
int size = 0; int size = 0;
@ -101,7 +116,7 @@ namespace LibHac.IO
size += entry.NameLength + 1; size += entry.NameLength + 1;
} }
int endOffset = Util.AlignUp(startOffset + size, MetaDataAlignment); int endOffset = Util.AlignUp(startOffset + size, GetMetaDataAlignment(type));
return endOffset - startOffset; return endOffset - startOffset;
} }
@ -115,6 +130,32 @@ namespace LibHac.IO
} }
} }
private int GetMetaDataAlignment(PartitionFileSystemType type)
{
switch (type)
{
case PartitionFileSystemType.Standard: return 0x20;
case PartitionFileSystemType.Hashed: return 0x200;
default: throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
}
private void CalculateHashes()
{
using (SHA256 sha = SHA256.Create())
{
foreach (Entry entry in Entries)
{
if (entry.HashLength == 0) entry.HashLength = 0x200;
var data = new byte[entry.HashLength];
entry.File.Read(data, entry.HashOffset);
entry.Hash = sha.ComputeHash(data);
}
}
}
private class Entry private class Entry
{ {
public string Name; public string Name;
@ -122,6 +163,10 @@ namespace LibHac.IO
public long Length; public long Length;
public long Offset; public long Offset;
public int NameLength; public int NameLength;
public int HashLength;
public long HashOffset;
public byte[] Hash;
} }
} }
} }

View file

@ -51,6 +51,7 @@ namespace hactoolnet
new CliOption("listfiles", 0, (o, a) => o.ListFiles = true), new CliOption("listfiles", 0, (o, a) => o.ListFiles = true),
new CliOption("sign", 0, (o, a) => o.SignSave = true), new CliOption("sign", 0, (o, a) => o.SignSave = true),
new CliOption("readbench", 0, (o, a) => o.ReadBench = 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])), new CliOption("title", 1, (o, a) => o.TitleId = ParseTitleId(a[0])),
new CliOption("bench", 1, (o, a) => o.BenchType = a[0]), new CliOption("bench", 1, (o, a) => o.BenchType = a[0]),
@ -162,7 +163,7 @@ namespace hactoolnet
sb.AppendLine(" -y, --verify Verify all hashes in the input file."); sb.AppendLine(" -y, --verify Verify all hashes in the input file.");
sb.AppendLine(" -h, --enablehash Enable hash checks when reading the input file."); sb.AppendLine(" -h, --enablehash Enable hash checks when reading the input file.");
sb.AppendLine(" -k, --keyset Load keys from an external file."); sb.AppendLine(" -k, --keyset Load keys from an external file.");
sb.AppendLine(" -t, --intype=type Specify input file type [nca, xci, romfs, pk11, pk21, ini1, kip1, switchfs, save, ndv0, keygen, romfsbuild]"); sb.AppendLine(" -t, --intype=type Specify input file type [nca, xci, romfs, pfs0, pk11, pk21, ini1, kip1, switchfs, save, ndv0, keygen, romfsbuild, pfsbuild]");
sb.AppendLine(" --titlekeys <file> Load title keys from an external file."); sb.AppendLine(" --titlekeys <file> Load title keys from an external file.");
sb.AppendLine("NCA options:"); sb.AppendLine("NCA options:");
sb.AppendLine(" --plaintext <file> Specify file path for saving a decrypted copy of the NCA."); sb.AppendLine(" --plaintext <file> Specify file path for saving a decrypted copy of the NCA.");
@ -186,6 +187,12 @@ namespace hactoolnet
sb.AppendLine("RomFS creation options:"); sb.AppendLine("RomFS creation options:");
sb.AppendLine(" Input path must be a directory"); sb.AppendLine(" Input path must be a directory");
sb.AppendLine(" --outfile <file> Specify created RomFS file path."); sb.AppendLine(" --outfile <file> Specify created RomFS file path.");
sb.AppendLine("Partition FS options:");
sb.AppendLine(" --outdir <dir> Specify extracted FS directory path.");
sb.AppendLine("Partition FS creation options:");
sb.AppendLine(" Input path must be a directory");
sb.AppendLine(" --outfile <file> Specify created Partition FS file path.");
sb.AppendLine(" --hashedfs Create a hashed Partition FS (HFS0).");
sb.AppendLine("XCI options:"); sb.AppendLine("XCI options:");
sb.AppendLine(" --rootdir <dir> Specify root XCI directory path."); sb.AppendLine(" --rootdir <dir> Specify root XCI directory path.");
sb.AppendLine(" --updatedir <dir> Specify update XCI directory path."); sb.AppendLine(" --updatedir <dir> Specify update XCI directory path.");

View file

@ -44,6 +44,7 @@ namespace hactoolnet
public bool ListFiles; public bool ListFiles;
public bool SignSave; public bool SignSave;
public bool ReadBench; public bool ReadBench;
public bool BuildHfs;
public ulong TitleId; public ulong TitleId;
public string BenchType; public string BenchType;

View file

@ -37,10 +37,14 @@ namespace hactoolnet
return; return;
} }
PartitionFileSystemType type = ctx.Options.BuildHfs
? PartitionFileSystemType.Hashed
: PartitionFileSystemType.Standard;
var localFs = new LocalFileSystem(ctx.Options.InFile); var localFs = new LocalFileSystem(ctx.Options.InFile);
var builder = new PartitionFileSystemBuilder(localFs); var builder = new PartitionFileSystemBuilder(localFs);
IStorage partitionFs = builder.Build(PartitionFileSystemType.Standard); IStorage partitionFs = builder.Build(type);
ctx.Logger.LogMessage($"Building Partition FS as {ctx.Options.OutFile}"); ctx.Logger.LogMessage($"Building Partition FS as {ctx.Options.OutFile}");