LibHac/libhac/Pfs0.cs

152 lines
4.6 KiB
C#
Raw Normal View History

2018-07-03 04:21:35 +02:00
using System.Collections.Generic;
using System.IO;
using System.Linq;
2018-06-22 23:17:20 +02:00
using System.Text;
2018-06-22 21:05:29 +02:00
namespace libhac
{
public class Pfs0
{
2018-06-22 23:17:20 +02:00
public Pfs0Header Header { get; set; }
public int HeaderSize { get; set; }
2018-07-03 04:21:35 +02:00
public Pfs0FileEntry[] Files { get; set; }
private Dictionary<string, Pfs0FileEntry> FileDict { get; }
2018-06-22 23:17:20 +02:00
private Stream Stream { get; set; }
2018-06-26 00:26:47 +02:00
public Pfs0(Stream stream)
2018-06-22 23:17:20 +02:00
{
byte[] headerBytes;
2018-06-26 00:26:47 +02:00
using (var reader = new BinaryReader(stream, Encoding.Default, true))
2018-06-22 23:17:20 +02:00
{
Header = new Pfs0Header(reader);
HeaderSize = (int)(16 + 24 * Header.NumFiles + Header.StringTableSize);
2018-06-26 00:26:47 +02:00
stream.Position = 0;
2018-06-22 23:17:20 +02:00
headerBytes = reader.ReadBytes(HeaderSize);
}
using (var reader = new BinaryReader(new MemoryStream(headerBytes)))
{
reader.BaseStream.Position = 16;
2018-07-03 04:21:35 +02:00
Files = new Pfs0FileEntry[Header.NumFiles];
2018-06-22 23:17:20 +02:00
for (int i = 0; i < Header.NumFiles; i++)
{
2018-07-03 04:21:35 +02:00
Files[i] = new Pfs0FileEntry(reader) { Index = i };
2018-06-22 23:17:20 +02:00
}
int stringTableOffset = 16 + 24 * Header.NumFiles;
for (int i = 0; i < Header.NumFiles; i++)
{
2018-07-03 04:21:35 +02:00
reader.BaseStream.Position = stringTableOffset + Files[i].StringTableOffset;
Files[i].Name = reader.ReadAsciiZ();
2018-06-22 23:17:20 +02:00
}
}
2018-07-03 04:21:35 +02:00
FileDict = Files.ToDictionary(x => x.Name, x => x);
2018-06-26 00:26:47 +02:00
Stream = stream;
2018-06-22 23:17:20 +02:00
}
2018-07-03 04:21:35 +02:00
public Stream OpenFile(string filename)
{
if (!FileDict.TryGetValue(filename, out Pfs0FileEntry file))
{
throw new FileNotFoundException();
}
return OpenFile(file);
}
public Stream OpenFile(Pfs0FileEntry file)
{
return new SubStream(Stream, HeaderSize + file.Offset, file.Size);
}
2018-06-22 23:17:20 +02:00
public byte[] GetFile(int index)
{
2018-07-03 04:21:35 +02:00
var entry = Files[index];
2018-06-22 23:17:20 +02:00
var file = new byte[entry.Size];
Stream.Position = HeaderSize + entry.Offset;
Stream.Read(file, 0, file.Length);
return file;
}
2018-06-22 21:05:29 +02:00
}
public class Pfs0Superblock
{
public byte[] MasterHash; /* SHA-256 hash of the hash table. */
public uint BlockSize; /* In bytes. */
public uint Always2;
2018-06-22 23:17:20 +02:00
public long HashTableOffset; /* Normally zero. */
public long HashTableSize;
public long Pfs0Offset;
public long Pfs0Size;
2018-06-22 21:05:29 +02:00
public Pfs0Superblock(BinaryReader reader)
{
MasterHash = reader.ReadBytes(0x20);
BlockSize = reader.ReadUInt32();
Always2 = reader.ReadUInt32();
2018-06-22 23:17:20 +02:00
HashTableOffset = reader.ReadInt64();
HashTableSize = reader.ReadInt64();
Pfs0Offset = reader.ReadInt64();
Pfs0Size = reader.ReadInt64();
2018-06-22 21:05:29 +02:00
reader.BaseStream.Position += 0xF0;
}
}
2018-06-22 23:17:20 +02:00
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();
}
}
2018-07-03 04:21:35 +02:00
public static class Pfs0Extensions
{
public static void Extract(this Pfs0 pfs0, string outDir, IProgressReport logger = null)
{
foreach (var file in pfs0.Files)
{
var stream = pfs0.OpenFile(file);
var outName = Path.Combine(outDir, file.Name);
2018-07-05 23:37:30 +02:00
var dir = Path.GetDirectoryName(outName);
if (!string.IsNullOrWhiteSpace(dir)) Directory.CreateDirectory(dir);
2018-07-03 04:21:35 +02:00
using (var outFile = new FileStream(outName, FileMode.Create, FileAccess.ReadWrite))
{
logger?.LogMessage(file.Name);
stream.CopyStream(outFile, stream.Length, logger);
}
}
}
}
2018-06-22 21:05:29 +02:00
}