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.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<TargetFrameworks>netcoreapp2.1;net45</TargetFrameworks>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
</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));
|
||||
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; }
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue