mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add a Partition FS builder
This commit is contained in:
parent
85c5374fdd
commit
1bcbe9f94a
6 changed files with 199 additions and 47 deletions
|
@ -119,7 +119,7 @@ namespace LibHac.IO
|
||||||
throw new InvalidDataException($"Invalid Partition FS type \"{Magic}\"");
|
throw new InvalidDataException($"Invalid Partition FS type \"{Magic}\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
int entrySize = GetFileEntrySize(Type);
|
int entrySize = PartitionFileEntry.GetEntrySize(Type);
|
||||||
int stringTableOffset = 16 + entrySize * NumFiles;
|
int stringTableOffset = 16 + entrySize * NumFiles;
|
||||||
HeaderSize = stringTableOffset + StringTableSize;
|
HeaderSize = stringTableOffset + StringTableSize;
|
||||||
|
|
||||||
|
@ -135,19 +135,6 @@ namespace LibHac.IO
|
||||||
Files[i].Name = reader.ReadAsciiZ();
|
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
|
public class PartitionFileEntry
|
||||||
|
@ -178,5 +165,18 @@ namespace LibHac.IO
|
||||||
reader.BaseStream.Position += 4;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
125
src/LibHac/IO/PartitionFileSystemBuilder.cs
Normal file
125
src/LibHac/IO/PartitionFileSystemBuilder.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -62,6 +62,7 @@ namespace hactoolnet
|
||||||
{
|
{
|
||||||
Nca,
|
Nca,
|
||||||
Pfs0,
|
Pfs0,
|
||||||
|
PfsBuild,
|
||||||
Nsp,
|
Nsp,
|
||||||
Romfs,
|
Romfs,
|
||||||
RomfsBuild,
|
RomfsBuild,
|
||||||
|
|
55
src/hactoolnet/ProcessFsBuild.cs
Normal file
55
src/hactoolnet/ProcessFsBuild.cs
Normal 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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -61,11 +61,14 @@ namespace hactoolnet
|
||||||
case FileType.Nsp:
|
case FileType.Nsp:
|
||||||
ProcessNsp.Process(ctx);
|
ProcessNsp.Process(ctx);
|
||||||
break;
|
break;
|
||||||
|
case FileType.PfsBuild:
|
||||||
|
ProcessFsBuild.ProcessPartitionFs(ctx);
|
||||||
|
break;
|
||||||
case FileType.Romfs:
|
case FileType.Romfs:
|
||||||
ProcessRomfs.Process(ctx);
|
ProcessRomfs.Process(ctx);
|
||||||
break;
|
break;
|
||||||
case FileType.RomfsBuild:
|
case FileType.RomfsBuild:
|
||||||
ProcessRomFsBuild.Process(ctx);
|
ProcessFsBuild.ProcessRomFs(ctx);
|
||||||
break;
|
break;
|
||||||
case FileType.Nax0:
|
case FileType.Nax0:
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in a new issue