Add a Partition FS builder

This commit is contained in:
Alex Barney 2019-02-26 18:49:56 -06:00
parent 85c5374fdd
commit 1bcbe9f94a
6 changed files with 199 additions and 47 deletions

View file

@ -119,7 +119,7 @@ namespace LibHac.IO
throw new InvalidDataException($"Invalid Partition FS type \"{Magic}\"");
}
int entrySize = GetFileEntrySize(Type);
int entrySize = PartitionFileEntry.GetEntrySize(Type);
int stringTableOffset = 16 + entrySize * NumFiles;
HeaderSize = stringTableOffset + StringTableSize;
@ -135,19 +135,6 @@ namespace LibHac.IO
Files[i].Name = reader.ReadAsciiZ();
}
}
private static int GetFileEntrySize(PartitionFileSystemType type)
{
switch (type)
{
case PartitionFileSystemType.Standard:
return 24;
case PartitionFileSystemType.Hashed:
return 0x40;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
}
}
public class PartitionFileEntry
@ -178,5 +165,18 @@ namespace LibHac.IO
reader.BaseStream.Position += 4;
}
}
public static int GetEntrySize(PartitionFileSystemType type)
{
switch (type)
{
case PartitionFileSystemType.Standard:
return 0x18;
case PartitionFileSystemType.Hashed:
return 0x40;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
}
}
}

View file

@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace LibHac.IO
{
public class PartitionFileSystemBuilder
{
private const int HeaderSize = 0x10;
private const int MetaDataAlignment = 0x20;
private List<Entry> Entries { get; } = new List<Entry>();
private long CurrentOffset { get; set; }
/// <summary>
/// Creates a new <see cref="PartitionFileSystemBuilder"/> and populates it with all
/// the files in the root directory.
/// </summary>
public PartitionFileSystemBuilder(IFileSystem input)
{
IDirectory rootDir = input.OpenDirectory("/", OpenDirectoryMode.Files);
foreach (DirectoryEntry file in rootDir.Read().OrderBy(x => x.FullPath, StringComparer.Ordinal))
{
AddFile(file.FullPath.TrimStart('/'), input.OpenFile(file.FullPath, OpenMode.Read));
}
}
public void AddFile(string filename, IFile file)
{
var entry = new Entry
{
Name = filename,
File = file,
Length = file.GetSize(),
Offset = CurrentOffset,
NameLength = Encoding.UTF8.GetByteCount(filename)
};
CurrentOffset += entry.Length;
Entries.Add(entry);
}
public IStorage Build(PartitionFileSystemType type)
{
byte[] meta = BuildMetaData(type);
var sources = new List<IStorage>();
sources.Add(new MemoryStorage(meta));
sources.AddRange(Entries.Select(x => new FileStorage(x.File)));
return new ConcatenationStorage(sources, true);
}
private byte[] BuildMetaData(PartitionFileSystemType type)
{
int entryTableSize = Entries.Count * PartitionFileEntry.GetEntrySize(type);
int stringTableSize = CalcStringTableSize(HeaderSize + entryTableSize);
int metaDataSize = HeaderSize + entryTableSize + stringTableSize;
var metaData = new byte[metaDataSize];
var writer = new BinaryWriter(new MemoryStream(metaData));
writer.WriteUTF8(GetMagicValue(type));
writer.Write(Entries.Count);
writer.Write(stringTableSize);
writer.Write(0);
int stringOffset = 0;
foreach (Entry entry in Entries)
{
writer.Write(entry.Offset);
writer.Write(entry.Length);
writer.Write(stringOffset);
writer.Write(0);
stringOffset += entry.NameLength + 1;
}
foreach (Entry entry in Entries)
{
writer.WriteUTF8Z(entry.Name);
}
return metaData;
}
private int CalcStringTableSize(int startOffset)
{
int size = 0;
foreach (Entry entry in Entries)
{
size += entry.NameLength + 1;
}
int endOffset = Util.AlignUp(startOffset + size, MetaDataAlignment);
return endOffset - startOffset;
}
private string GetMagicValue(PartitionFileSystemType type)
{
switch (type)
{
case PartitionFileSystemType.Standard: return "PFS0";
case PartitionFileSystemType.Hashed: return "HFS0";
default: throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
}
private class Entry
{
public string Name;
public IFile File;
public long Length;
public long Offset;
public int NameLength;
}
}
}

View file

@ -62,6 +62,7 @@ namespace hactoolnet
{
Nca,
Pfs0,
PfsBuild,
Nsp,
Romfs,
RomfsBuild,

View file

@ -0,0 +1,55 @@
using System.IO;
using LibHac.IO;
using LibHac.IO.RomFs;
namespace hactoolnet
{
internal static class ProcessFsBuild
{
public static void ProcessRomFs(Context ctx)
{
if (ctx.Options.OutFile == null)
{
ctx.Logger.LogMessage("Output file must be specified.");
return;
}
var localFs = new LocalFileSystem(ctx.Options.InFile);
var builder = new RomFsBuilder(localFs);
IStorage romfs = builder.Build();
ctx.Logger.LogMessage($"Building RomFS as {ctx.Options.OutFile}");
using (var outFile = new FileStream(ctx.Options.OutFile, FileMode.Create, FileAccess.ReadWrite))
{
romfs.CopyToStream(outFile, romfs.Length, ctx.Logger);
}
ctx.Logger.LogMessage($"Finished writing {ctx.Options.OutFile}");
}
public static void ProcessPartitionFs(Context ctx)
{
if (ctx.Options.OutFile == null)
{
ctx.Logger.LogMessage("Output file must be specified.");
return;
}
var localFs = new LocalFileSystem(ctx.Options.InFile);
var builder = new PartitionFileSystemBuilder(localFs);
IStorage partitionFs = builder.Build(PartitionFileSystemType.Standard);
ctx.Logger.LogMessage($"Building Partition FS as {ctx.Options.OutFile}");
using (var outFile = new FileStream(ctx.Options.OutFile, FileMode.Create, FileAccess.ReadWrite))
{
partitionFs.CopyToStream(outFile, partitionFs.Length, ctx.Logger);
}
ctx.Logger.LogMessage($"Finished writing {ctx.Options.OutFile}");
}
}
}

View file

@ -1,32 +0,0 @@
using System.IO;
using LibHac.IO;
using LibHac.IO.RomFs;
namespace hactoolnet
{
internal static class ProcessRomFsBuild
{
public static void Process(Context ctx)
{
if (ctx.Options.OutFile == null)
{
ctx.Logger.LogMessage("Output file must be specified.");
return;
}
var localFs = new LocalFileSystem(ctx.Options.InFile);
var builder = new RomFsBuilder(localFs);
IStorage romfs = builder.Build();
ctx.Logger.LogMessage($"Building RomFS as {ctx.Options.OutFile}");
using (var outFile = new FileStream(ctx.Options.OutFile, FileMode.Create, FileAccess.ReadWrite))
{
romfs.CopyToStream(outFile, romfs.Length, ctx.Logger);
}
ctx.Logger.LogMessage($"Finished writing {ctx.Options.OutFile}");
}
}
}

View file

@ -61,11 +61,14 @@ namespace hactoolnet
case FileType.Nsp:
ProcessNsp.Process(ctx);
break;
case FileType.PfsBuild:
ProcessFsBuild.ProcessPartitionFs(ctx);
break;
case FileType.Romfs:
ProcessRomfs.Process(ctx);
break;
case FileType.RomfsBuild:
ProcessRomFsBuild.Process(ctx);
ProcessFsBuild.ProcessRomFs(ctx);
break;
case FileType.Nax0:
break;