mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add romfs class
This commit is contained in:
parent
3e6d2a7761
commit
b6cac8b5f2
6 changed files with 268 additions and 21 deletions
|
@ -9,8 +9,10 @@ namespace hactoolnet
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
FileReadTest(args);
|
||||||
//ReadNca();
|
//ReadNca();
|
||||||
ListSdfs(args);
|
//ListSdfs(args);
|
||||||
|
//ReadNcaSdfs(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ListSdfs(string[] args)
|
private static void ListSdfs(string[] args)
|
||||||
|
@ -27,21 +29,47 @@ namespace hactoolnet
|
||||||
//DecryptTitle(sdfs, 0x010023900AEE0000);
|
//DecryptTitle(sdfs, 0x010023900AEE0000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void FileReadTest(string[] args)
|
||||||
|
{
|
||||||
|
var sdfs = LoadSdFs(args);
|
||||||
|
var title = sdfs.Titles[0x0100E95004038000];
|
||||||
|
var nca = title.ProgramNca;
|
||||||
|
var romfsStream = nca.OpenSection(1, false);
|
||||||
|
var romfs = new Romfs(romfsStream);
|
||||||
|
var file = romfs.OpenFile("/stream/voice/us/127/127390101.nop");
|
||||||
|
|
||||||
|
using (var output = new FileStream("127390101.nop", FileMode.Create))
|
||||||
|
{
|
||||||
|
file.CopyTo(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ReadNca()
|
static void ReadNca()
|
||||||
{
|
{
|
||||||
var keyset = ExternalKeys.ReadKeyFile("keys.txt", "titlekeys.txt");
|
var keyset = ExternalKeys.ReadKeyFile("keys.txt", "titlekeys.txt");
|
||||||
using (var file = new FileStream("671d172e7993ee033d1be25ee76378e3.nca", FileMode.Open, FileAccess.Read))
|
using (var file = new FileStream("27eeccfe5f6e7637352273bc46ab97e4.nca", FileMode.Open, FileAccess.Read))
|
||||||
{
|
{
|
||||||
var nca = new Nca(keyset, file, false);
|
var nca = new Nca(keyset, file, false);
|
||||||
var romfs = nca.OpenSection(0, false);
|
var romfsStream = nca.OpenSection(1, false);
|
||||||
|
|
||||||
using (var output = new FileStream("romfs_net", FileMode.Create))
|
var romfs = new Romfs(romfsStream);
|
||||||
|
var bfstm = romfs.OpenFile("/Sound/Resource/Stream/BGM_Castle.bfstm");
|
||||||
|
|
||||||
|
using (var output = new FileStream("BGM_Castle.bfstm", FileMode.Create))
|
||||||
{
|
{
|
||||||
romfs.CopyTo(output);
|
bfstm.CopyTo(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ReadNcaSdfs(string[] args)
|
||||||
|
{
|
||||||
|
var sdfs = LoadSdFs(args);
|
||||||
|
var nca = sdfs.Ncas["8EE79C7AB0F16737BC50F049DFDBB595"];
|
||||||
|
var romfsStream =nca.OpenSection(1, false);
|
||||||
|
var romfs = new Romfs(romfsStream);
|
||||||
|
}
|
||||||
|
|
||||||
static void DecryptNax0(SdFs sdFs, string name)
|
static void DecryptNax0(SdFs sdFs, string name)
|
||||||
{
|
{
|
||||||
if (!sdFs.Ncas.ContainsKey(name)) return;
|
if (!sdFs.Ncas.ContainsKey(name)) return;
|
||||||
|
|
|
@ -106,17 +106,17 @@ namespace libhac
|
||||||
|
|
||||||
public override void Flush()
|
public override void Flush()
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override long Seek(long offset, SeekOrigin origin)
|
public override long Seek(long offset, SeekOrigin origin)
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetLength(long value)
|
public override void SetLength(long value)
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -127,11 +127,11 @@ namespace libhac
|
||||||
/// <param name="count">The number of bytes to be written to the current stream.</param>
|
/// <param name="count">The number of bytes to be written to the current stream.</param>
|
||||||
public override void Write(byte[] buffer, int offset, int count)
|
public override void Write(byte[] buffer, int offset, int count)
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool CanRead => true;
|
public override bool CanRead => true;
|
||||||
public override bool CanSeek => false;
|
public override bool CanSeek => true;
|
||||||
public override bool CanWrite => false;
|
public override bool CanWrite => false;
|
||||||
public override long Length { get; }
|
public override long Length { get; }
|
||||||
|
|
||||||
|
|
208
libhac/Romfs.cs
208
libhac/Romfs.cs
|
@ -1,5 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace libhac
|
namespace libhac
|
||||||
|
@ -7,20 +10,205 @@ namespace libhac
|
||||||
public class Romfs
|
public class Romfs
|
||||||
{
|
{
|
||||||
public static readonly int IvfcMaxLevel = 6;
|
public static readonly int IvfcMaxLevel = 6;
|
||||||
|
public RomfsHeader Header { get; }
|
||||||
|
public List<RomfsDir> Directories { get; } = new List<RomfsDir>();
|
||||||
|
public List<RomfsFile> Files { get; } = new List<RomfsFile>();
|
||||||
|
public RomfsDir RootDir { get; }
|
||||||
|
|
||||||
|
private Dictionary<string, RomfsFile> FileDict { get; }
|
||||||
|
private Stream Stream { get; set; }
|
||||||
|
|
||||||
|
public Romfs(Stream stream)
|
||||||
|
{
|
||||||
|
var watch = Stopwatch.StartNew();
|
||||||
|
|
||||||
|
byte[] dirMetaTable;
|
||||||
|
byte[] fileMetaTable;
|
||||||
|
using (var reader = new BinaryReader(stream, Encoding.Default, true))
|
||||||
|
{
|
||||||
|
Header = new RomfsHeader(reader);
|
||||||
|
stream.Position = Header.DirMetaTableOffset;
|
||||||
|
dirMetaTable = reader.ReadBytes((int)Header.DirMetaTableSize);
|
||||||
|
stream.Position = Header.FileMetaTableOffset;
|
||||||
|
fileMetaTable = reader.ReadBytes((int)Header.FileMetaTableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var reader = new BinaryReader(new MemoryStream(dirMetaTable)))
|
||||||
|
{
|
||||||
|
int position = 0;
|
||||||
|
while (position + 20 < Header.DirMetaTableSize)
|
||||||
|
{
|
||||||
|
var dir = new RomfsDir(reader) { Offset = position };
|
||||||
|
Directories.Add(dir);
|
||||||
|
if (dir.ParentOffset == position) RootDir = dir;
|
||||||
|
position = (int)reader.BaseStream.Position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var reader = new BinaryReader(new MemoryStream(fileMetaTable)))
|
||||||
|
{
|
||||||
|
int position = 0;
|
||||||
|
while (position + 20 < Header.FileMetaTableSize)
|
||||||
|
{
|
||||||
|
var file = new RomfsFile(reader) { Offset = position };
|
||||||
|
Files.Add(file);
|
||||||
|
position = (int)reader.BaseStream.Position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SetReferences();
|
||||||
|
ResolveFilenames();
|
||||||
|
FileDict = Files.ToDictionary(x => x.FullPath, x => x);
|
||||||
|
Stream = stream;
|
||||||
|
|
||||||
|
watch.Stop();
|
||||||
|
Console.WriteLine(watch.Elapsed.TotalMilliseconds);
|
||||||
|
Console.WriteLine(Files.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream OpenFile(string filename)
|
||||||
|
{
|
||||||
|
if (!FileDict.TryGetValue(filename, out var file))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var stream = new SubStream(Stream, Header.DataOffset + file.DataOffset, file.DataLength);
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FileExists(string filename) => FileDict.ContainsKey(filename);
|
||||||
|
|
||||||
|
private void SetReferences()
|
||||||
|
{
|
||||||
|
var dirDict = Directories.ToDictionary(x => x.Offset, x => x);
|
||||||
|
var fileDict = Files.ToDictionary(x => x.Offset, x => x);
|
||||||
|
|
||||||
|
foreach (var dir in Directories)
|
||||||
|
{
|
||||||
|
if (dir.ParentOffset >= 0 && dir.ParentOffset != dir.Offset) dir.Parent = dirDict[dir.ParentOffset];
|
||||||
|
if (dir.NextSiblingOffset >= 0) dir.NextSibling = dirDict[dir.NextSiblingOffset];
|
||||||
|
if (dir.FirstChildOffset >= 0) dir.FirstChild = dirDict[dir.FirstChildOffset];
|
||||||
|
if (dir.FirstFileOffset >= 0) dir.FirstFile = fileDict[dir.FirstFileOffset];
|
||||||
|
if (dir.NextDirHashOffset >= 0) dir.NextDirHash = dirDict[dir.NextDirHashOffset];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var file in Files)
|
||||||
|
{
|
||||||
|
if (file.ParentDirOffset >= 0) file.ParentDir = dirDict[file.ParentDirOffset];
|
||||||
|
if (file.NextSiblingOffset >= 0) file.NextSibling = fileDict[file.NextSiblingOffset];
|
||||||
|
if (file.NextFileHashOffset >= 0) file.NextFileHash = fileDict[file.NextFileHashOffset];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResolveFilenames()
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
foreach (var file in Files)
|
||||||
|
{
|
||||||
|
sb.Append(file.Name);
|
||||||
|
var dir = file.ParentDir;
|
||||||
|
while (dir != null)
|
||||||
|
{
|
||||||
|
sb.Insert(0, "/");
|
||||||
|
sb.Insert(0, dir.Name);
|
||||||
|
dir = dir.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.FullPath = sb.ToString();
|
||||||
|
sb.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RomfsHeader
|
public class RomfsHeader
|
||||||
{
|
{
|
||||||
public ulong HeaderSize;
|
public long HeaderSize { get; }
|
||||||
public ulong DirHashTableOffset;
|
public long DirHashTableOffset { get; }
|
||||||
public ulong DirHashTableSize;
|
public long DirHashTableSize { get; }
|
||||||
public ulong DirMetaTableOffset;
|
public long DirMetaTableOffset { get; }
|
||||||
public ulong DirMetaTableSize;
|
public long DirMetaTableSize { get; }
|
||||||
public ulong FileHashTableOffset;
|
public long FileHashTableOffset { get; }
|
||||||
public ulong FileHashTableSize;
|
public long FileHashTableSize { get; }
|
||||||
public ulong FileMetaTableOffset;
|
public long FileMetaTableOffset { get; }
|
||||||
public ulong FileMetaTableSize;
|
public long FileMetaTableSize { get; }
|
||||||
public ulong DataOffset;
|
public long DataOffset { get; }
|
||||||
|
|
||||||
|
public RomfsHeader(BinaryReader reader)
|
||||||
|
{
|
||||||
|
HeaderSize = reader.ReadInt64();
|
||||||
|
DirHashTableOffset = reader.ReadInt64();
|
||||||
|
DirHashTableSize = reader.ReadInt64();
|
||||||
|
DirMetaTableOffset = reader.ReadInt64();
|
||||||
|
DirMetaTableSize = reader.ReadInt64();
|
||||||
|
FileHashTableOffset = reader.ReadInt64();
|
||||||
|
FileHashTableSize = reader.ReadInt64();
|
||||||
|
FileMetaTableOffset = reader.ReadInt64();
|
||||||
|
FileMetaTableSize = reader.ReadInt64();
|
||||||
|
DataOffset = reader.ReadInt64();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerDisplay("{" + nameof(Name) + "}")]
|
||||||
|
public class RomfsDir
|
||||||
|
{
|
||||||
|
public int Offset { get; set; }
|
||||||
|
public int ParentOffset { get; }
|
||||||
|
public int NextSiblingOffset { get; }
|
||||||
|
public int FirstChildOffset { get; }
|
||||||
|
public int FirstFileOffset { get; }
|
||||||
|
public int NextDirHashOffset { get; }
|
||||||
|
public int NameLength { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public RomfsDir Parent { get; internal set; }
|
||||||
|
public RomfsDir NextSibling { get; internal set; }
|
||||||
|
public RomfsDir FirstChild { get; internal set; }
|
||||||
|
public RomfsFile FirstFile { get; internal set; }
|
||||||
|
public RomfsDir NextDirHash { get; internal set; }
|
||||||
|
|
||||||
|
public RomfsDir(BinaryReader reader)
|
||||||
|
{
|
||||||
|
ParentOffset = reader.ReadInt32();
|
||||||
|
NextSiblingOffset = reader.ReadInt32();
|
||||||
|
FirstChildOffset = reader.ReadInt32();
|
||||||
|
FirstFileOffset = reader.ReadInt32();
|
||||||
|
NextDirHashOffset = reader.ReadInt32();
|
||||||
|
NameLength = reader.ReadInt32();
|
||||||
|
Name = reader.ReadUtf8(NameLength);
|
||||||
|
reader.BaseStream.Position = Util.GetNextMultiple(reader.BaseStream.Position, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerDisplay("{" + nameof(Name) + "}")]
|
||||||
|
public class RomfsFile
|
||||||
|
{
|
||||||
|
public int Offset { get; set; }
|
||||||
|
public int ParentDirOffset { get; }
|
||||||
|
public int NextSiblingOffset { get; }
|
||||||
|
public long DataOffset { get; }
|
||||||
|
public long DataLength { get; }
|
||||||
|
public int NextFileHashOffset { get; }
|
||||||
|
public int NameLength { get; }
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public RomfsDir ParentDir { get; internal set; }
|
||||||
|
public RomfsFile NextSibling { get; internal set; }
|
||||||
|
public RomfsFile NextFileHash { get; internal set; }
|
||||||
|
public string FullPath { get; set; }
|
||||||
|
|
||||||
|
public RomfsFile(BinaryReader reader)
|
||||||
|
{
|
||||||
|
ParentDirOffset = reader.ReadInt32();
|
||||||
|
NextSiblingOffset = reader.ReadInt32();
|
||||||
|
DataOffset = reader.ReadInt64();
|
||||||
|
DataLength = reader.ReadInt64();
|
||||||
|
NextFileHashOffset = reader.ReadInt32();
|
||||||
|
NameLength = reader.ReadInt32();
|
||||||
|
Name = reader.ReadUtf8(NameLength);
|
||||||
|
reader.BaseStream.Position = Util.GetNextMultiple(reader.BaseStream.Position, 4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IvfcLevel
|
public class IvfcLevel
|
||||||
|
|
|
@ -70,6 +70,7 @@ namespace libhac
|
||||||
title.Id = metadata.TitleId;
|
title.Id = metadata.TitleId;
|
||||||
title.Version = new TitleVersion(metadata.TitleVersion);
|
title.Version = new TitleVersion(metadata.TitleVersion);
|
||||||
title.Metadata = metadata;
|
title.Metadata = metadata;
|
||||||
|
title.MetaNca = nca;
|
||||||
title.Ncas.Add(nca);
|
title.Ncas.Add(nca);
|
||||||
|
|
||||||
foreach (var content in metadata.ContentEntries)
|
foreach (var content in metadata.ContentEntries)
|
||||||
|
@ -80,6 +81,16 @@ namespace libhac
|
||||||
{
|
{
|
||||||
title.Ncas.Add(contentNca);
|
title.Ncas.Add(contentNca);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (content.Type)
|
||||||
|
{
|
||||||
|
case CnmtContentType.Program:
|
||||||
|
title.ProgramNca = contentNca;
|
||||||
|
break;
|
||||||
|
case CnmtContentType.Control:
|
||||||
|
title.ControlNca = contentNca;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Titles.Add(title.Id, title);
|
Titles.Add(title.Id, title);
|
||||||
|
@ -111,5 +122,9 @@ namespace libhac
|
||||||
public TitleVersion Version { get; internal set; }
|
public TitleVersion Version { get; internal set; }
|
||||||
public List<Nca> Ncas { get; } = new List<Nca>();
|
public List<Nca> Ncas { get; } = new List<Nca>();
|
||||||
public Cnmt Metadata { get; internal set; }
|
public Cnmt Metadata { get; internal set; }
|
||||||
|
|
||||||
|
public Nca MetaNca { get; internal set; }
|
||||||
|
public Nca ProgramNca { get; internal set; }
|
||||||
|
public Nca ControlNca { get; internal set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace libhac
|
||||||
Length = length;
|
Length = length;
|
||||||
Offset = offset;
|
Offset = offset;
|
||||||
|
|
||||||
baseStream.Seek(offset, SeekOrigin.Current);
|
baseStream.Seek(offset, SeekOrigin.Begin);
|
||||||
}
|
}
|
||||||
public override int Read(byte[] buffer, int offset, int count)
|
public override int Read(byte[] buffer, int offset, int count)
|
||||||
{
|
{
|
||||||
|
|
|
@ -98,6 +98,11 @@ namespace libhac
|
||||||
return Encoding.ASCII.GetString(reader.ReadBytes(size), 0, size);
|
return Encoding.ASCII.GetString(reader.ReadBytes(size), 0, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string ReadUtf8(this BinaryReader reader, int size)
|
||||||
|
{
|
||||||
|
return Encoding.UTF8.GetString(reader.ReadBytes(size), 0, size);
|
||||||
|
}
|
||||||
|
|
||||||
// todo Maybe make less naive
|
// todo Maybe make less naive
|
||||||
public static string GetRelativePath(string path, string basePath)
|
public static string GetRelativePath(string path, string basePath)
|
||||||
{
|
{
|
||||||
|
@ -256,6 +261,17 @@ namespace libhac
|
||||||
// Return formatted number with suffix
|
// Return formatted number with suffix
|
||||||
return readable.ToString("0.### ") + suffix;
|
return readable.ToString("0.### ") + suffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long GetNextMultiple(long value, int multiple)
|
||||||
|
{
|
||||||
|
if (multiple <= 0)
|
||||||
|
return value;
|
||||||
|
|
||||||
|
if (value % multiple == 0)
|
||||||
|
return value;
|
||||||
|
|
||||||
|
return value + multiple - value % multiple;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ByteArray128BitComparer : EqualityComparer<byte[]>
|
public class ByteArray128BitComparer : EqualityComparer<byte[]>
|
||||||
|
|
Loading…
Reference in a new issue