mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add NintendoSubmissionPackageRootFileSystem
This commit is contained in:
parent
436f71e6a7
commit
3f845e2964
5 changed files with 313 additions and 16 deletions
|
@ -68,4 +68,47 @@ public struct Sha256PartitionFileSystemFormat : IPartitionFileSystemFormat
|
|||
readonly long IPartitionFileSystemEntry.Size => Size;
|
||||
readonly int IPartitionFileSystemEntry.NameOffset => NameOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public struct NintendoSubmissionPackageRootFileSystemFormat : IPartitionFileSystemFormat
|
||||
{
|
||||
public static ReadOnlySpan<byte> VersionSignature => "NSP1"u8;
|
||||
public static uint EntryNameLengthMax => PathTool.EntryNameLengthMax;
|
||||
public static uint FileDataAlignmentSize => 0x20;
|
||||
public static Result ResultSignatureVerificationFailed => ResultFs.PartitionSignatureVerificationFailed.Value;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct PartitionEntry : IPartitionFileSystemEntry
|
||||
{
|
||||
public long Offset;
|
||||
public long Size;
|
||||
public int NameOffset;
|
||||
public uint Reserved;
|
||||
|
||||
readonly long IPartitionFileSystemEntry.Offset => Offset;
|
||||
readonly long IPartitionFileSystemEntry.Size => Size;
|
||||
readonly int IPartitionFileSystemEntry.NameOffset => NameOffset;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct PartitionFileSystemHeaderImpl : IPartitionFileSystemHeader
|
||||
{
|
||||
private Array4<byte> _signature;
|
||||
public int EntryCount;
|
||||
public int NameTableSize;
|
||||
public byte TargetPlatform;
|
||||
public long TotalSize;
|
||||
|
||||
public readonly ReadOnlySpan<byte> Signature
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadOnlySpan<byte> span = _signature;
|
||||
return MemoryMarshal.CreateReadOnlySpan(ref MemoryMarshal.GetReference(span), span.Length);
|
||||
}
|
||||
}
|
||||
|
||||
readonly int IPartitionFileSystemHeader.EntryCount => EntryCount;
|
||||
readonly int IPartitionFileSystemHeader.NameTableSize => NameTableSize;
|
||||
}
|
||||
}
|
194
src/LibHac/FsSystem/NintendoSubmissionPackageRootFileSystem.cs
Normal file
194
src/LibHac/FsSystem/NintendoSubmissionPackageRootFileSystem.cs
Normal file
|
@ -0,0 +1,194 @@
|
|||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSystem.Impl;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.FsSystem;
|
||||
|
||||
using NspRootFileSystemCore =
|
||||
PartitionFileSystemCore<NintendoSubmissionPackageRootFileSystemMeta, NintendoSubmissionPackageRootFileSystemFormat,
|
||||
NintendoSubmissionPackageRootFileSystemFormat.PartitionFileSystemHeaderImpl,
|
||||
NintendoSubmissionPackageRootFileSystemFormat.PartitionEntry>;
|
||||
|
||||
/// <summary>
|
||||
/// Reads a standard partition file system of version 0 or version 1. These files start with "PFS0" or "PFS1" respectively.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 18.3.0 (FS 18.0.0)</remarks>
|
||||
public class NintendoSubmissionPackageRootFileSystem : IFileSystem
|
||||
{
|
||||
private Optional<NspRootFileSystemCore> _nspRootFileSystem;
|
||||
private Optional<PartitionFileSystem> _partitionFileSystem;
|
||||
|
||||
public NintendoSubmissionPackageRootFileSystem()
|
||||
{
|
||||
_nspRootFileSystem = new Optional<NspRootFileSystemCore>();
|
||||
_partitionFileSystem = new Optional<PartitionFileSystem>();
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_nspRootFileSystem.HasValue)
|
||||
{
|
||||
_nspRootFileSystem.Value.Dispose();
|
||||
_nspRootFileSystem.Clear();
|
||||
}
|
||||
|
||||
if (_partitionFileSystem.HasValue)
|
||||
{
|
||||
_partitionFileSystem.Value.Dispose();
|
||||
_partitionFileSystem.Clear();
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
public Result Initialize(ref readonly SharedRef<IStorage> baseStorage)
|
||||
{
|
||||
return InitializeImpl(in baseStorage).Ret();
|
||||
}
|
||||
|
||||
protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, ref readonly Path path, OpenMode mode)
|
||||
{
|
||||
return GetImpl().OpenFile(ref outFile, in path, mode).Ret();
|
||||
}
|
||||
|
||||
protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, ref readonly Path path, OpenDirectoryMode mode)
|
||||
{
|
||||
return GetImpl().OpenDirectory(ref outDirectory, in path, mode).Ret();
|
||||
}
|
||||
|
||||
protected override Result DoGetEntryType(out DirectoryEntryType entryType, ref readonly Path path)
|
||||
{
|
||||
return GetImpl().GetEntryType(out entryType, in path).Ret();
|
||||
}
|
||||
|
||||
protected override Result DoCreateFile(ref readonly Path path, long size, CreateFileOptions option)
|
||||
{
|
||||
return GetImpl().CreateFile(in path, size, option).Ret();
|
||||
}
|
||||
|
||||
protected override Result DoDeleteFile(ref readonly Path path)
|
||||
{
|
||||
return GetImpl().DeleteFile(in path).Ret();
|
||||
}
|
||||
|
||||
protected override Result DoCreateDirectory(ref readonly Path path)
|
||||
{
|
||||
return GetImpl().CreateDirectory(in path).Ret();
|
||||
}
|
||||
|
||||
protected override Result DoDeleteDirectory(ref readonly Path path)
|
||||
{
|
||||
return GetImpl().DeleteDirectory(in path).Ret();
|
||||
}
|
||||
|
||||
protected override Result DoDeleteDirectoryRecursively(ref readonly Path path)
|
||||
{
|
||||
return GetImpl().DeleteDirectoryRecursively(in path).Ret();
|
||||
}
|
||||
|
||||
protected override Result DoCleanDirectoryRecursively(ref readonly Path path)
|
||||
{
|
||||
return GetImpl().CleanDirectoryRecursively(in path).Ret();
|
||||
}
|
||||
|
||||
protected override Result DoRenameFile(ref readonly Path currentPath, ref readonly Path newPath)
|
||||
{
|
||||
return GetImpl().RenameFile(in currentPath, in newPath).Ret();
|
||||
}
|
||||
|
||||
protected override Result DoRenameDirectory(ref readonly Path currentPath, ref readonly Path newPath)
|
||||
{
|
||||
return GetImpl().RenameDirectory(in currentPath, in newPath).Ret();
|
||||
}
|
||||
|
||||
protected override Result DoCommit()
|
||||
{
|
||||
return GetImpl().Commit().Ret();
|
||||
}
|
||||
|
||||
protected override Result DoCommitProvisionally(long counter)
|
||||
{
|
||||
return GetImpl().CommitProvisionally(counter).Ret();
|
||||
}
|
||||
|
||||
private Result InitializeImpl(ref readonly SharedRef<IStorage> baseStorage)
|
||||
{
|
||||
bool successNsp = false;
|
||||
try
|
||||
{
|
||||
if (_nspRootFileSystem.HasValue)
|
||||
_nspRootFileSystem.Value.Dispose();
|
||||
|
||||
// First try to open the file system as an NspRootFileSystem
|
||||
_nspRootFileSystem.Set(new NspRootFileSystemCore());
|
||||
|
||||
Result res = _nspRootFileSystem.Value.Initialize(in baseStorage);
|
||||
|
||||
if (!res.IsSuccess())
|
||||
{
|
||||
if (ResultFs.PartitionSignatureVerificationFailed.Includes(res))
|
||||
{
|
||||
// If that fails, try to open the file system as a PartitionFileSystem
|
||||
bool successPfs = false;
|
||||
try
|
||||
{
|
||||
if (_partitionFileSystem.HasValue)
|
||||
_partitionFileSystem.Value.Dispose();
|
||||
|
||||
_partitionFileSystem.Set(new PartitionFileSystem());
|
||||
|
||||
res = _partitionFileSystem.Value.Initialize(in baseStorage);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
successPfs = true;
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!successPfs)
|
||||
{
|
||||
if (_partitionFileSystem.HasValue)
|
||||
{
|
||||
_partitionFileSystem.Value.Dispose();
|
||||
_partitionFileSystem.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return res.Miss();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
successNsp = true;
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!successNsp)
|
||||
{
|
||||
if (_nspRootFileSystem.HasValue)
|
||||
{
|
||||
_nspRootFileSystem.Value.Dispose();
|
||||
_nspRootFileSystem.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IFileSystem GetImpl()
|
||||
{
|
||||
if (_nspRootFileSystem.HasValue)
|
||||
return _nspRootFileSystem.Value;
|
||||
|
||||
Assert.SdkAssert(_partitionFileSystem.HasValue);
|
||||
|
||||
return _partitionFileSystem.Value;
|
||||
}
|
||||
}
|
|
@ -6,11 +6,17 @@ using LibHac.Crypto;
|
|||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSystem.Impl;
|
||||
using LibHac.Util;
|
||||
using Buffer = LibHac.Mem.Buffer;
|
||||
|
||||
namespace LibHac.FsSystem;
|
||||
|
||||
using NspRootFileSystemCore =
|
||||
PartitionFileSystemCore<NintendoSubmissionPackageRootFileSystemMeta, NintendoSubmissionPackageRootFileSystemFormat,
|
||||
NintendoSubmissionPackageRootFileSystemFormat.PartitionFileSystemHeaderImpl,
|
||||
NintendoSubmissionPackageRootFileSystemFormat.PartitionEntry>;
|
||||
|
||||
/// <summary>
|
||||
/// The allocator used by a <see cref="PartitionFileSystemCore{TMetaData,TFormat,THeader,TEntry}"/> when none is provided.
|
||||
/// </summary>
|
||||
|
@ -52,18 +58,18 @@ file sealed class DefaultAllocatorForPartitionFileSystem : MemoryResource
|
|||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 16.2.0 (FS 16.0.0)</remarks>
|
||||
public class PartitionFileSystem : PartitionFileSystemCore<PartitionFileSystemMeta,
|
||||
Impl.PartitionFileSystemFormat,
|
||||
Impl.PartitionFileSystemFormat.PartitionFileSystemHeaderImpl,
|
||||
Impl.PartitionFileSystemFormat.PartitionEntry> { }
|
||||
PartitionFileSystemFormat,
|
||||
PartitionFileSystemFormat.PartitionFileSystemHeaderImpl,
|
||||
PartitionFileSystemFormat.PartitionEntry>;
|
||||
|
||||
/// <summary>
|
||||
/// Reads a hashed partition file system. These files start with "HFS0" and are typically found inside XCIs.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 16.2.0 (FS 16.0.0)</remarks>
|
||||
public class Sha256PartitionFileSystem : PartitionFileSystemCore<Sha256PartitionFileSystemMeta,
|
||||
Impl.Sha256PartitionFileSystemFormat,
|
||||
Impl.PartitionFileSystemFormat.PartitionFileSystemHeaderImpl,
|
||||
Impl.Sha256PartitionFileSystemFormat.PartitionEntry> { }
|
||||
Sha256PartitionFileSystemFormat,
|
||||
PartitionFileSystemFormat.PartitionFileSystemHeaderImpl,
|
||||
Sha256PartitionFileSystemFormat.PartitionEntry>;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the base for an <see cref="IFileSystem"/> that can read from different partition file system files.
|
||||
|
@ -197,6 +203,11 @@ public class PartitionFileSystemCore<TMetaData, TFormat, THeader, TEntry> : IFil
|
|||
return DoRead(fileSha, out bytesRead, offset, destination, in option).Ret();
|
||||
}
|
||||
|
||||
if (this is NspRootFileSystemCore.PartitionFile fileNsp)
|
||||
{
|
||||
return DoRead(fileNsp, out bytesRead, offset, destination, in option).Ret();
|
||||
}
|
||||
|
||||
UnsafeHelpers.SkipParamInit(out bytesRead);
|
||||
Abort.DoAbort("PartitionFileSystemCore.PartitionFile type is not supported.");
|
||||
return ResultFs.NotImplemented.Log();
|
||||
|
@ -330,6 +341,22 @@ public class PartitionFileSystemCore<TMetaData, TFormat, THeader, TEntry> : IFil
|
|||
bytesRead = readSize;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static Result DoRead(NspRootFileSystemCore.PartitionFile fs, out long bytesRead, long offset,
|
||||
Span<byte> destination, in ReadOption option)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out bytesRead);
|
||||
|
||||
Result res = fs.DryRead(out long readSize, offset, destination.Length, in option, fs._mode);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = fs._parent._baseStorage.Read(fs._parent._metaDataSize + fs._partitionEntry.Offset + offset,
|
||||
destination.Slice(0, (int)readSize));
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
bytesRead = readSize;
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -73,7 +73,7 @@ namespace LibHac.FsSystem
|
|||
protected MemoryResource Allocator;
|
||||
protected Buffer MetaDataBuffer;
|
||||
|
||||
private ref readonly THeader Header => ref MemoryMarshal.GetReference(HeaderBuffer.GetSpan<THeader>());
|
||||
protected ref readonly THeader Header => ref MemoryMarshal.GetReference(HeaderBuffer.GetSpan<THeader>());
|
||||
private ReadOnlySpan<TEntry> Entries => EntryBuffer.GetSpan<TEntry>();
|
||||
private ReadOnlySpan<byte> NameTable => NameTableBuffer.Span;
|
||||
|
||||
|
@ -356,11 +356,23 @@ namespace LibHac.FsSystem
|
|||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace LibHac.FsSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads the metadata for a <see cref="PartitionFileSystem"/>.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 16.2.0 (FS 16.0.0)</remarks>
|
||||
public class PartitionFileSystemMeta : PartitionFileSystemMetaCore<PartitionFileSystemFormat,
|
||||
PartitionFileSystemFormat.PartitionFileSystemHeaderImpl, PartitionFileSystemFormat.PartitionEntry> { }
|
||||
|
||||
public class NintendoSubmissionPackageRootFileSystemMeta : PartitionFileSystemMetaCore<
|
||||
NintendoSubmissionPackageRootFileSystemFormat,
|
||||
NintendoSubmissionPackageRootFileSystemFormat.PartitionFileSystemHeaderImpl,
|
||||
NintendoSubmissionPackageRootFileSystemFormat.PartitionEntry>
|
||||
{
|
||||
public byte GetTargetPlatform() => Header.TargetPlatform;
|
||||
public long GetTotalSize() => Header.TotalSize;
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ using LibHac.Common;
|
|||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.FsSystem.Impl;
|
||||
using LibHac.Tools.Es;
|
||||
using LibHac.Tools.Fs;
|
||||
using LibHac.Tools.FsSystem;
|
||||
|
@ -17,6 +18,11 @@ using Path = LibHac.Fs.Path;
|
|||
|
||||
namespace hactoolnet;
|
||||
|
||||
using NspRootFileSystemCore =
|
||||
PartitionFileSystemCore<NintendoSubmissionPackageRootFileSystemMeta, NintendoSubmissionPackageRootFileSystemFormat,
|
||||
NintendoSubmissionPackageRootFileSystemFormat.PartitionFileSystemHeaderImpl,
|
||||
NintendoSubmissionPackageRootFileSystemFormat.PartitionEntry>;
|
||||
|
||||
internal static class ProcessPfs
|
||||
{
|
||||
public static void Process(Context ctx)
|
||||
|
@ -26,9 +32,12 @@ internal static class ProcessPfs
|
|||
IFileSystem fs = null;
|
||||
using UniqueRef<PartitionFileSystem> pfs = new UniqueRef<PartitionFileSystem>();
|
||||
using UniqueRef<Sha256PartitionFileSystem> hfs = new UniqueRef<Sha256PartitionFileSystem>();
|
||||
using UniqueRef<NspRootFileSystemCore> nsp = new UniqueRef<NspRootFileSystemCore>();
|
||||
|
||||
using var sharedFile = new SharedRef<IStorage>(file);
|
||||
|
||||
pfs.Reset(new PartitionFileSystem());
|
||||
Result res = pfs.Get.Initialize(file);
|
||||
Result res = pfs.Get.Initialize(in sharedFile);
|
||||
if (res.IsSuccess())
|
||||
{
|
||||
fs = pfs.Get;
|
||||
|
@ -43,18 +52,30 @@ internal static class ProcessPfs
|
|||
// Reading the input as a PartitionFileSystem didn't work. Try reading it as an Sha256PartitionFileSystem
|
||||
hfs.Reset(new Sha256PartitionFileSystem());
|
||||
res = hfs.Get.Initialize(file);
|
||||
if (res.IsFailure())
|
||||
if (res.IsSuccess())
|
||||
{
|
||||
fs = hfs.Get;
|
||||
ctx.Logger.LogMessage(hfs.Get.Print());
|
||||
}
|
||||
else if (!ResultFs.Sha256PartitionSignatureVerificationFailed.Includes(res))
|
||||
{
|
||||
if (ResultFs.Sha256PartitionSignatureVerificationFailed.Includes(res))
|
||||
{
|
||||
ResultFs.PartitionSignatureVerificationFailed.Value.ThrowIfFailure();
|
||||
}
|
||||
|
||||
res.ThrowIfFailure();
|
||||
}
|
||||
else
|
||||
{
|
||||
nsp.Reset(new NspRootFileSystemCore());
|
||||
res = nsp.Get.Initialize(file);
|
||||
|
||||
fs = hfs.Get;
|
||||
ctx.Logger.LogMessage(hfs.Get.Print());
|
||||
if (res.IsSuccess())
|
||||
{
|
||||
fs = nsp.Get;
|
||||
ctx.Logger.LogMessage(nsp.Get.Print());
|
||||
}
|
||||
else
|
||||
{
|
||||
res.ThrowIfFailure();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.Options.OutDir != null)
|
||||
|
|
Loading…
Reference in a new issue