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.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace LibHac.IO
@ -38,7 +39,9 @@ namespace LibHac.IO
File = file,
Length = file.GetSize(),
Offset = CurrentOffset,
NameLength = Encoding.UTF8.GetByteCount(filename)
NameLength = Encoding.UTF8.GetByteCount(filename),
HashOffset = 0,
HashLength = 0x200
};
CurrentOffset += entry.Length;
@ -60,8 +63,10 @@ namespace LibHac.IO
private byte[] BuildMetaData(PartitionFileSystemType type)
{
if (type == PartitionFileSystemType.Hashed) CalculateHashes();
int entryTableSize = Entries.Count * PartitionFileEntry.GetEntrySize(type);
int stringTableSize = CalcStringTableSize(HeaderSize + entryTableSize);
int stringTableSize = CalcStringTableSize(HeaderSize + entryTableSize, type);
int metaDataSize = HeaderSize + entryTableSize + stringTableSize;
var metaData = new byte[metaDataSize];
@ -79,7 +84,17 @@ namespace LibHac.IO
writer.Write(entry.Offset);
writer.Write(entry.Length);
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;
}
@ -92,7 +107,7 @@ namespace LibHac.IO
return metaData;
}
private int CalcStringTableSize(int startOffset)
private int CalcStringTableSize(int startOffset, PartitionFileSystemType type)
{
int size = 0;
@ -101,7 +116,7 @@ namespace LibHac.IO
size += entry.NameLength + 1;
}
int endOffset = Util.AlignUp(startOffset + size, MetaDataAlignment);
int endOffset = Util.AlignUp(startOffset + size, GetMetaDataAlignment(type));
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
{
public string Name;
@ -122,6 +163,10 @@ namespace LibHac.IO
public long Length;
public long Offset;
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("sign", 0, (o, a) => o.SignSave = 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("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(" -h, --enablehash Enable hash checks when reading the input 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("NCA options:");
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(" Input path must be a directory");
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(" --rootdir <dir> Specify root 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 SignSave;
public bool ReadBench;
public bool BuildHfs;
public ulong TitleId;
public string BenchType;

View file

@ -37,10 +37,14 @@ namespace hactoolnet
return;
}
PartitionFileSystemType type = ctx.Options.BuildHfs
? PartitionFileSystemType.Hashed
: PartitionFileSystemType.Standard;
var localFs = new LocalFileSystem(ctx.Options.InFile);
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}");