Parse PFS0

This commit is contained in:
Alex Barney 2018-06-22 16:17:20 -05:00
parent b632a7df0c
commit fbe62d7d7f
5 changed files with 173 additions and 17 deletions

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using libhac;
@ -9,7 +10,7 @@ namespace hactoolnet
{
static void Main(string[] args)
{
ListSdContents(args);
DumpMeta(args);
}
static void DecryptNax0(string[] args)
@ -30,15 +31,57 @@ namespace hactoolnet
}
static void ListSdContents(string[] args)
{
Console.WriteLine($"Using key file {args[0]}");
Console.WriteLine($"SD seed {BitConverter.ToString(args[1].ToBytes())}");
Console.WriteLine($"SD path {args[2]}");
var keyset = ExternalKeys.ReadKeyFile(args[0]);
if (keyset.master_keys[0].IsEmpty())
{
Console.WriteLine("Need master key 0");
}
keyset.SetSdSeed(args[1].ToBytes());
var sdfs = new SdFs(keyset, args[2]);
var ncas = sdfs.ReadAllNca();
foreach (var nca in ncas.Where(x => x != null))
{
Console.WriteLine($"{nca.Header.TitleId:X16} {nca.Header.ContentType.ToString().PadRight(10, ' ')} {nca.Name}");
}
}
static void DumpMeta(string[] args)
{
var keyset = ExternalKeys.ReadKeyFile(args[0]);
keyset.SetSdSeed(args[1].ToBytes());
var sdfs = new SdFs(keyset, args[2]);
var ncas = sdfs.ReadAllNca().ToArray();
foreach (var nca in ncas)
var metadata = new List<byte[]>();
using (var progress = new ProgressBar())
{
Console.WriteLine($"{nca.Header.TitleId:X16} {nca.Header.ContentType} {nca.Name}");
foreach (var nca in ncas.Where(x => x.Header.ContentType == ContentType.Meta))
{
foreach (var section in nca.Sections.Where(x => x.Header.FsType == SectionFsType.Pfs0))
{
var sect = nca.OpenSection(section.SectionNum);
var pfs0 = sect.Pfs0;
pfs0.Open(sect.Stream);
foreach (var entry in pfs0.Entries)
{
var path = Path.Combine("meta", entry.Name);
Directory.CreateDirectory(Path.GetDirectoryName(path));
var file = pfs0.GetFile(entry.Index);
metadata.Add(file);
File.WriteAllBytes(path, file);
progress.LogMessage(path);
}
}
}
}
}
}

View file

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFrameworks>netcoreapp2.1;net45</TargetFrameworks>
<LangVersion>7.3</LangVersion>
</PropertyGroup>

View file

@ -38,11 +38,28 @@ namespace libhac
}
}
public Stream OpenSection(int index)
public NcaSection OpenSection(int index)
{
if (index >= Sections.Count) throw new ArgumentOutOfRangeException(nameof(index));
var sect = Sections[index];
Stream.Position = sect.Offset;
sect.Stream = null;
long offset = sect.Offset;
long size = sect.Size;
switch (sect.Header.FsType)
{
case SectionFsType.Pfs0:
offset = sect.Offset + sect.Pfs0.Superblock.Pfs0Offset;
size = sect.Pfs0.Superblock.Pfs0Size;
break;
case SectionFsType.Romfs:
break;
default:
throw new ArgumentOutOfRangeException();
}
Stream.Position = offset;
switch (sect.Header.CryptType)
{
@ -51,14 +68,15 @@ namespace libhac
case SectionCryptType.XTS:
break;
case SectionCryptType.CTR:
return new RandomAccessSectorStream(new AesCtrStream(Stream, DecryptedKeys[2], sect.Offset, sect.Size, sect.Offset));
sect.Stream = new RandomAccessSectorStream(new AesCtrStream(Stream, DecryptedKeys[2], offset, size, offset));
break;
case SectionCryptType.BKTR:
break;
default:
throw new ArgumentOutOfRangeException();
}
return null;
return sect;
}
private void ReadHeader(Keyset keyset, Stream stream)
@ -107,7 +125,7 @@ namespace libhac
public class NcaSection
{
public Stream Stream;
public Stream Stream { get; set; }
public NcaFsHeader Header { get; set; }
public SectionType Type { get; set; }
public int SectionNum { get; set; }

View file

@ -1,10 +1,56 @@
using System.IO;
using System.Text;
namespace libhac
{
public class Pfs0
{
public Pfs0Superblock Superblock { get; set; }
public Pfs0Header Header { get; set; }
public int HeaderSize { get; set; }
public Pfs0FileEntry[] Entries { get; set; }
private Stream Stream { get; set; }
public void Open(Stream file)
{
byte[] headerBytes;
using (var reader = new BinaryReader(file, Encoding.Default, true))
{
Header = new Pfs0Header(reader);
HeaderSize = (int)(16 + 24 * Header.NumFiles + Header.StringTableSize);
file.Position = 0;
headerBytes = reader.ReadBytes(HeaderSize);
}
using (var reader = new BinaryReader(new MemoryStream(headerBytes)))
{
reader.BaseStream.Position = 16;
Entries = new Pfs0FileEntry[Header.NumFiles];
for (int i = 0; i < Header.NumFiles; i++)
{
Entries[i] = new Pfs0FileEntry(reader) { Index = i };
}
int stringTableOffset = 16 + 24 * Header.NumFiles;
for (int i = 0; i < Header.NumFiles; i++)
{
reader.BaseStream.Position = stringTableOffset + Entries[i].StringTableOffset;
Entries[i].Name = reader.ReadAsciiZ();
}
}
Stream = file;
}
public byte[] GetFile(int index)
{
var entry = Entries[index];
var file = new byte[entry.Size];
Stream.Position = HeaderSize + entry.Offset;
Stream.Read(file, 0, file.Length);
return file;
}
}
public class Pfs0Superblock
@ -12,21 +58,55 @@ namespace libhac
public byte[] MasterHash; /* SHA-256 hash of the hash table. */
public uint BlockSize; /* In bytes. */
public uint Always2;
public ulong HashTableOffset; /* Normally zero. */
public ulong HashTableSize;
public ulong Pfs0Offset;
public ulong Pfs0Size;
public long HashTableOffset; /* Normally zero. */
public long HashTableSize;
public long Pfs0Offset;
public long Pfs0Size;
public Pfs0Superblock(BinaryReader reader)
{
MasterHash = reader.ReadBytes(0x20);
BlockSize = reader.ReadUInt32();
Always2 = reader.ReadUInt32();
HashTableOffset = reader.ReadUInt64();
HashTableSize = reader.ReadUInt64();
Pfs0Offset = reader.ReadUInt64();
Pfs0Size = reader.ReadUInt64();
HashTableOffset = reader.ReadInt64();
HashTableSize = reader.ReadInt64();
Pfs0Offset = reader.ReadInt64();
Pfs0Size = reader.ReadInt64();
reader.BaseStream.Position += 0xF0;
}
}
public class Pfs0Header
{
public string Magic;
public int NumFiles;
public uint StringTableSize;
public long Reserved;
public Pfs0Header(BinaryReader reader)
{
Magic = reader.ReadAscii(4);
NumFiles = reader.ReadInt32();
StringTableSize = reader.ReadUInt32();
Reserved = reader.ReadInt32();
}
}
public class Pfs0FileEntry
{
public int Index;
public long Offset;
public long Size;
public uint StringTableOffset;
public uint Reserved;
public string Name;
public Pfs0FileEntry(BinaryReader reader)
{
Offset = reader.ReadInt64();
Size = reader.ReadInt64();
StringTableOffset = reader.ReadUInt32();
Reserved = reader.ReadUInt32();
}
}
}

View file

@ -76,6 +76,21 @@ namespace libhac
}
}
public static string ReadAsciiZ(this BinaryReader reader)
{
var start = reader.BaseStream.Position;
// Read until we hit the end of the stream (-1) or a zero
while (reader.BaseStream.ReadByte() - 1 > 0) { }
int size = (int)(reader.BaseStream.Position - start - 1);
reader.BaseStream.Position = start;
string text = reader.ReadAscii(size);
reader.BaseStream.Position++; // Skip the null byte
return text;
}
public static string ReadAscii(this BinaryReader reader, int size)
{
return Encoding.ASCII.GetString(reader.ReadBytes(size), 0, size);