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}\"");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
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,
|
||||
Pfs0,
|
||||
PfsBuild,
|
||||
Nsp,
|
||||
Romfs,
|
||||
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:
|
||||
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;
|
||||
|
|
Loading…
Reference in a new issue