LibHac/hactoolnet/ProcessNsp.cs
Alex Barney 3d50085e22
Use Storage throughout the library instead of Stream (#18)
*Create an IStorage interface and Storage abstract class to use instead of Stream

* Improve AES-XTS performance by ~16x

* Double AES-CTR performance: 800 MB/s -> 1600 MB/s on a 6700K

* Add AES-XTS tests

* Add AES benchmark and AES-CTR writing

* Add support for a hashed FAT in save files

* Add option to export decrypted NCA

* Allow opening decrypted package1 and package2

* Make sure romfs disposal can cascade all the way down

* Validate NCA, NPDM and package2 signatures
2018-11-18 23:20:34 -05:00

100 lines
3.2 KiB
C#

using System.IO;
using System.Reflection;
using System.Text;
using LibHac;
using LibHac.IO;
using static hactoolnet.Print;
namespace hactoolnet
{
internal static class ProcessNsp
{
public static void Process(Context ctx)
{
using (var file = new FileStream(ctx.Options.InFile, FileMode.Open, FileAccess.Read))
{
Pfs pfs = new Pfs(file.AsStorage());
ctx.Logger.LogMessage(pfs.Print());
if (ctx.Options.OutDir != null)
{
pfs.Extract(ctx.Options.OutDir, ctx.Logger);
}
}
}
private static string Print(this Pfs pfs)
{
const int colLen = 36;
const int fileNameLen = 39;
var sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine("PFS0:");
PrintItem(sb, colLen, "Magic:", pfs.Header.Magic);
PrintItem(sb, colLen, "Number of files:", pfs.Header.NumFiles);
for (int i = 0; i < pfs.Files.Length; i++)
{
PfsFileEntry file = pfs.Files[i];
string label = i == 0 ? "Files:" : "";
string offsets = $"{file.Offset:x12}-{file.Offset + file.Size:x12}{file.HashValidity.GetValidityString()}";
string data = $"pfs0:/{file.Name}".PadRight(fileNameLen) + offsets;
PrintItem(sb, colLen, label, data);
}
return sb.ToString();
}
public static void CreateNsp(Context ctx, SwitchFs switchFs)
{
ulong id = ctx.Options.TitleId;
if (id == 0)
{
ctx.Logger.LogMessage("Title ID must be specified to save title");
return;
}
if (!switchFs.Titles.TryGetValue(id, out Title title))
{
ctx.Logger.LogMessage($"Could not find title {id:X16}");
return;
}
var builder = new Pfs0Builder();
foreach (Nca nca in title.Ncas)
{
builder.AddFile(nca.Filename, nca.GetStorage().AsStream());
}
var ticket = new Ticket
{
SignatureType = TicketSigType.Rsa2048Sha256,
Signature = new byte[0x200],
Issuer = "Root-CA00000003-XS00000020",
FormatVersion = 2,
RightsId = title.MainNca.Header.RightsId,
TitleKeyBlock = title.MainNca.TitleKey,
CryptoType = title.MainNca.Header.CryptoType2,
SectHeaderOffset = 0x2C0
};
byte[] ticketBytes = ticket.GetBytes();
builder.AddFile($"{ticket.RightsId.ToHexString()}.tik", new MemoryStream(ticketBytes));
Assembly thisAssembly = Assembly.GetExecutingAssembly();
Stream cert = thisAssembly.GetManifestResourceStream("hactoolnet.CA00000003_XS00000020");
builder.AddFile($"{ticket.RightsId.ToHexString()}.cert", cert);
using (var outStream = new FileStream(ctx.Options.NspOut, FileMode.Create, FileAccess.ReadWrite))
{
builder.Build(outStream, ctx.Logger);
}
}
}
}