mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Parse PFS0
This commit is contained in:
parent
b632a7df0c
commit
fbe62d7d7f
5 changed files with 173 additions and 17 deletions
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using libhac;
|
using libhac;
|
||||||
|
@ -9,7 +10,7 @@ namespace hactoolnet
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
ListSdContents(args);
|
DumpMeta(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DecryptNax0(string[] args)
|
static void DecryptNax0(string[] args)
|
||||||
|
@ -30,15 +31,57 @@ namespace hactoolnet
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ListSdContents(string[] args)
|
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]);
|
var keyset = ExternalKeys.ReadKeyFile(args[0]);
|
||||||
keyset.SetSdSeed(args[1].ToBytes());
|
keyset.SetSdSeed(args[1].ToBytes());
|
||||||
var sdfs = new SdFs(keyset, args[2]);
|
var sdfs = new SdFs(keyset, args[2]);
|
||||||
var ncas = sdfs.ReadAllNca().ToArray();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFrameworks>netcoreapp2.1;net45</TargetFrameworks>
|
||||||
<LangVersion>7.3</LangVersion>
|
<LangVersion>7.3</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
@ -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));
|
if (index >= Sections.Count) throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
var sect = Sections[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)
|
switch (sect.Header.CryptType)
|
||||||
{
|
{
|
||||||
|
@ -51,14 +68,15 @@ namespace libhac
|
||||||
case SectionCryptType.XTS:
|
case SectionCryptType.XTS:
|
||||||
break;
|
break;
|
||||||
case SectionCryptType.CTR:
|
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:
|
case SectionCryptType.BKTR:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return sect;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReadHeader(Keyset keyset, Stream stream)
|
private void ReadHeader(Keyset keyset, Stream stream)
|
||||||
|
@ -107,7 +125,7 @@ namespace libhac
|
||||||
|
|
||||||
public class NcaSection
|
public class NcaSection
|
||||||
{
|
{
|
||||||
public Stream Stream;
|
public Stream Stream { get; set; }
|
||||||
public NcaFsHeader Header { get; set; }
|
public NcaFsHeader Header { get; set; }
|
||||||
public SectionType Type { get; set; }
|
public SectionType Type { get; set; }
|
||||||
public int SectionNum { get; set; }
|
public int SectionNum { get; set; }
|
||||||
|
|
|
@ -1,10 +1,56 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace libhac
|
namespace libhac
|
||||||
{
|
{
|
||||||
public class Pfs0
|
public class Pfs0
|
||||||
{
|
{
|
||||||
public Pfs0Superblock Superblock { get; set; }
|
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
|
public class Pfs0Superblock
|
||||||
|
@ -12,21 +58,55 @@ namespace libhac
|
||||||
public byte[] MasterHash; /* SHA-256 hash of the hash table. */
|
public byte[] MasterHash; /* SHA-256 hash of the hash table. */
|
||||||
public uint BlockSize; /* In bytes. */
|
public uint BlockSize; /* In bytes. */
|
||||||
public uint Always2;
|
public uint Always2;
|
||||||
public ulong HashTableOffset; /* Normally zero. */
|
public long HashTableOffset; /* Normally zero. */
|
||||||
public ulong HashTableSize;
|
public long HashTableSize;
|
||||||
public ulong Pfs0Offset;
|
public long Pfs0Offset;
|
||||||
public ulong Pfs0Size;
|
public long Pfs0Size;
|
||||||
|
|
||||||
public Pfs0Superblock(BinaryReader reader)
|
public Pfs0Superblock(BinaryReader reader)
|
||||||
{
|
{
|
||||||
MasterHash = reader.ReadBytes(0x20);
|
MasterHash = reader.ReadBytes(0x20);
|
||||||
BlockSize = reader.ReadUInt32();
|
BlockSize = reader.ReadUInt32();
|
||||||
Always2 = reader.ReadUInt32();
|
Always2 = reader.ReadUInt32();
|
||||||
HashTableOffset = reader.ReadUInt64();
|
HashTableOffset = reader.ReadInt64();
|
||||||
HashTableSize = reader.ReadUInt64();
|
HashTableSize = reader.ReadInt64();
|
||||||
Pfs0Offset = reader.ReadUInt64();
|
Pfs0Offset = reader.ReadInt64();
|
||||||
Pfs0Size = reader.ReadUInt64();
|
Pfs0Size = reader.ReadInt64();
|
||||||
reader.BaseStream.Position += 0xF0;
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
public static string ReadAscii(this BinaryReader reader, int size)
|
||||||
{
|
{
|
||||||
return Encoding.ASCII.GetString(reader.ReadBytes(size), 0, size);
|
return Encoding.ASCII.GetString(reader.ReadBytes(size), 0, size);
|
||||||
|
|
Loading…
Reference in a new issue