mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Add Loader.MetaLoader
This commit is contained in:
parent
bb2c870f27
commit
a730c17bc5
@ -9,12 +9,12 @@ namespace LibHac.Common.FixedArrays
|
||||
{
|
||||
public const int Length = 1;
|
||||
|
||||
private T _item1;
|
||||
private T _1;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _item1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _item1, Length);
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array1<T> value) => value.ItemsRo;
|
||||
|
@ -9,23 +9,23 @@ namespace LibHac.Common.FixedArrays
|
||||
{
|
||||
public const int Length = 12;
|
||||
|
||||
private T _item01;
|
||||
private T _item02;
|
||||
private T _item03;
|
||||
private T _item04;
|
||||
private T _item05;
|
||||
private T _item06;
|
||||
private T _item07;
|
||||
private T _item08;
|
||||
private T _item09;
|
||||
private T _item10;
|
||||
private T _item11;
|
||||
private T _item12;
|
||||
private T _1;
|
||||
private T _2;
|
||||
private T _3;
|
||||
private T _4;
|
||||
private T _5;
|
||||
private T _6;
|
||||
private T _7;
|
||||
private T _8;
|
||||
private T _9;
|
||||
private T _10;
|
||||
private T _11;
|
||||
private T _12;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _item01, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _item01, Length);
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array12<T> value) => value.ItemsRo;
|
||||
|
32
src/LibHac/Common/FixedArrays/Array128.cs
Normal file
32
src/LibHac/Common/FixedArrays/Array128.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LibHac.Common.FixedArrays
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array128<T>
|
||||
{
|
||||
public const int Length = 128;
|
||||
|
||||
private Array64<T> _0;
|
||||
private Array64<T> _64;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length);
|
||||
}
|
||||
|
||||
public readonly ReadOnlySpan<T> ItemsRo
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array128<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
32
src/LibHac/Common/FixedArrays/Array16.cs
Normal file
32
src/LibHac/Common/FixedArrays/Array16.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LibHac.Common.FixedArrays
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array16<T>
|
||||
{
|
||||
public const int Length = 16;
|
||||
|
||||
private Array8<T> _0;
|
||||
private Array8<T> _8;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length);
|
||||
}
|
||||
|
||||
public readonly ReadOnlySpan<T> ItemsRo
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array16<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
@ -9,13 +9,13 @@ namespace LibHac.Common.FixedArrays
|
||||
{
|
||||
public const int Length = 2;
|
||||
|
||||
private T _item1;
|
||||
private T _item2;
|
||||
private T _1;
|
||||
private T _2;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _item1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _item1, Length);
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array2<T> value) => value.ItemsRo;
|
||||
|
32
src/LibHac/Common/FixedArrays/Array256.cs
Normal file
32
src/LibHac/Common/FixedArrays/Array256.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LibHac.Common.FixedArrays
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array256<T>
|
||||
{
|
||||
public const int Length = 256;
|
||||
|
||||
private Array128<T> _0;
|
||||
private Array128<T> _128;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length);
|
||||
}
|
||||
|
||||
public readonly ReadOnlySpan<T> ItemsRo
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array256<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
@ -9,14 +9,14 @@ namespace LibHac.Common.FixedArrays
|
||||
{
|
||||
public const int Length = 3;
|
||||
|
||||
private T _item1;
|
||||
private T _item2;
|
||||
private T _item3;
|
||||
private T _1;
|
||||
private T _2;
|
||||
private T _3;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _item1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _item1, Length);
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array3<T> value) => value.ItemsRo;
|
||||
|
@ -9,45 +9,24 @@ namespace LibHac.Common.FixedArrays
|
||||
{
|
||||
public const int Length = 32;
|
||||
|
||||
private T _item01;
|
||||
private T _item02;
|
||||
private T _item03;
|
||||
private T _item04;
|
||||
private T _item05;
|
||||
private T _item06;
|
||||
private T _item07;
|
||||
private T _item08;
|
||||
private T _item09;
|
||||
private T _item10;
|
||||
private T _item11;
|
||||
private T _item12;
|
||||
private T _item13;
|
||||
private T _item14;
|
||||
private T _item15;
|
||||
private T _item16;
|
||||
private T _item17;
|
||||
private T _item18;
|
||||
private T _item19;
|
||||
private T _item20;
|
||||
private T _item21;
|
||||
private T _item22;
|
||||
private T _item23;
|
||||
private T _item24;
|
||||
private T _item25;
|
||||
private T _item26;
|
||||
private T _item27;
|
||||
private T _item28;
|
||||
private T _item29;
|
||||
private T _item30;
|
||||
private T _item31;
|
||||
private T _item32;
|
||||
private Array16<T> _0;
|
||||
private Array16<T> _16;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _item01, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _item01, Length);
|
||||
public Span<T> Items
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length);
|
||||
}
|
||||
|
||||
public readonly ReadOnlySpan<T> ItemsRo
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array32<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
||||
}
|
@ -9,15 +9,15 @@ namespace LibHac.Common.FixedArrays
|
||||
{
|
||||
public const int Length = 4;
|
||||
|
||||
private T _item1;
|
||||
private T _item2;
|
||||
private T _item3;
|
||||
private T _item4;
|
||||
private T _1;
|
||||
private T _2;
|
||||
private T _3;
|
||||
private T _4;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _item1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _item1, Length);
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array4<T> value) => value.ItemsRo;
|
||||
|
32
src/LibHac/Common/FixedArrays/Array64.cs
Normal file
32
src/LibHac/Common/FixedArrays/Array64.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LibHac.Common.FixedArrays
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array64<T>
|
||||
{
|
||||
public const int Length = 64;
|
||||
|
||||
private Array32<T> _0;
|
||||
private Array32<T> _32;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length);
|
||||
}
|
||||
|
||||
public readonly ReadOnlySpan<T> ItemsRo
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array64<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
@ -9,19 +9,19 @@ namespace LibHac.Common.FixedArrays
|
||||
{
|
||||
public const int Length = 8;
|
||||
|
||||
private T _item01;
|
||||
private T _item02;
|
||||
private T _item03;
|
||||
private T _item04;
|
||||
private T _item05;
|
||||
private T _item06;
|
||||
private T _item07;
|
||||
private T _item08;
|
||||
private T _1;
|
||||
private T _2;
|
||||
private T _3;
|
||||
private T _4;
|
||||
private T _5;
|
||||
private T _6;
|
||||
private T _7;
|
||||
private T _8;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _item01, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _item01, Length);
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array8<T> value) => value.ItemsRo;
|
||||
|
@ -69,4 +69,66 @@ namespace LibHac.Common
|
||||
return reference.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see langword="struct"/> that can store a reference to a value of a specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of value to reference.</typeparam>
|
||||
public readonly ref struct ReadOnlyRef<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The 1-length <see cref="ReadOnlySpan{T}"/> instance used to track the target <typeparamref name="T"/> value.
|
||||
/// </summary>
|
||||
private readonly ReadOnlySpan<T> _span;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Ref{T}"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">The reference to the target <typeparamref name="T"/> value.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ReadOnlyRef(in T value)
|
||||
{
|
||||
_span = MemoryMarshal.CreateSpan(ref Unsafe.AsRef(in value), 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Ref{T}"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="pointer">The pointer to the target value.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe ReadOnlyRef(void* pointer)
|
||||
: this(in Unsafe.AsRef<T>(pointer))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <typeparamref name="T"/> reference represented by the current <see cref="Ref{T}"/> instance.
|
||||
/// </summary>
|
||||
public ref readonly T Value
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => ref MemoryMarshal.GetReference(_span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value that indicates whether the current <see cref="Ref{T}"/> is <see langword="null"/>.
|
||||
/// </summary>
|
||||
/// <returns><see langword="true"/> if the held reference is <see langword="null"/>;
|
||||
/// otherwise <see langword="false"/>.</returns>
|
||||
public bool IsNull
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => Unsafe.IsNullRef(ref Unsafe.AsRef(in Value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly gets the <typeparamref name="T"/> value from a given <see cref="Ref{T}"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="reference">The input <see cref="Ref{T}"/> instance.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator T(ReadOnlyRef<T> reference)
|
||||
{
|
||||
return reference.Value;
|
||||
}
|
||||
}
|
||||
}
|
235
src/LibHac/Loader/MetaLoader.cs
Normal file
235
src/LibHac/Loader/MetaLoader.cs
Normal file
@ -0,0 +1,235 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
|
||||
namespace LibHac.Loader
|
||||
{
|
||||
public class MetaLoader
|
||||
{
|
||||
private const int MetaCacheBufferSize = 0x8000;
|
||||
|
||||
private readonly byte[] _npdmBuffer;
|
||||
private bool _isValid;
|
||||
|
||||
public MetaLoader()
|
||||
{
|
||||
_npdmBuffer = new byte[MetaCacheBufferSize];
|
||||
}
|
||||
|
||||
public Result Load(ReadOnlySpan<byte> npdmBuffer)
|
||||
{
|
||||
_isValid = false;
|
||||
|
||||
// Validate the meta
|
||||
Result rc = GetNpdmFromBuffer(out _, npdmBuffer);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
npdmBuffer.CopyTo(_npdmBuffer);
|
||||
_isValid = true;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result LoadFromFile(HorizonClient hos, FileHandle file)
|
||||
{
|
||||
_isValid = false;
|
||||
|
||||
// Get file size
|
||||
Result rc = hos.Fs.GetFileSize(out long npdmSize, file);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (npdmSize > MetaCacheBufferSize)
|
||||
return ResultLoader.TooLargeMeta.Log();
|
||||
|
||||
// Read data into cache buffer
|
||||
rc = hos.Fs.ReadFile(file, 0, _npdmBuffer.AsSpan(0, (int)npdmSize));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Validate the meta
|
||||
rc = GetNpdmFromBuffer(out _, _npdmBuffer);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
_isValid = true;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result GetNpdm(out Npdm npdm)
|
||||
{
|
||||
Assert.SdkRequires(_isValid);
|
||||
Assert.SdkRequiresEqual(MetaCacheBufferSize, _npdmBuffer.Length);
|
||||
|
||||
if (!_isValid)
|
||||
{
|
||||
npdm = default;
|
||||
return ResultLoader.InvalidMeta.Log();
|
||||
}
|
||||
|
||||
npdm = GetNpdmFromBufferUnsafe(ref MemoryMarshal.GetArrayDataReference(_npdmBuffer));
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result GetNpdmFromBuffer(out Npdm npdm, ReadOnlySpan<byte> npdmBuffer)
|
||||
{
|
||||
npdm = default;
|
||||
|
||||
int npdmSize = npdmBuffer.Length;
|
||||
|
||||
if (npdmSize > MetaCacheBufferSize)
|
||||
return ResultLoader.TooLargeMeta.Log();
|
||||
|
||||
Result rc = ValidateMeta(npdmBuffer);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
ref readonly Meta meta = ref Unsafe.As<byte, Meta>(ref MemoryMarshal.GetReference(npdmBuffer));
|
||||
|
||||
ReadOnlySpan<byte> acidBuffer = npdmBuffer.Slice(meta.AcidOffset, meta.AcidSize);
|
||||
ReadOnlySpan<byte> aciBuffer = npdmBuffer.Slice(meta.AciOffset, meta.AciSize);
|
||||
|
||||
ref readonly AcidHeaderData acid = ref Unsafe.As<byte, AcidHeaderData>(ref MemoryMarshal.GetReference(acidBuffer));
|
||||
ref readonly AciHeader aci = ref Unsafe.As<byte, AciHeader>(ref MemoryMarshal.GetReference(aciBuffer));
|
||||
|
||||
rc = ValidateAcid(acidBuffer);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = ValidateAci(aciBuffer);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Set Npdm members.
|
||||
npdm.Meta = new ReadOnlyRef<Meta>(in meta);
|
||||
npdm.Acid = new ReadOnlyRef<AcidHeaderData>(in acid);
|
||||
npdm.Aci = new ReadOnlyRef<AciHeader>(in aci);
|
||||
|
||||
npdm.FsAccessControlDescriptor = acidBuffer.Slice(acid.FsAccessControlOffset, acid.FsAccessControlSize);
|
||||
npdm.ServiceAccessControlDescriptor = acidBuffer.Slice(acid.ServiceAccessControlOffset, acid.ServiceAccessControlSize);
|
||||
npdm.KernelCapabilityDescriptor = acidBuffer.Slice(acid.KernelCapabilityOffset, acid.KernelCapabilitySize);
|
||||
|
||||
npdm.FsAccessControlData = aciBuffer.Slice(aci.FsAccessControlOffset, aci.FsAccessControlSize);
|
||||
npdm.ServiceAccessControlData = aciBuffer.Slice(aci.ServiceAccessControlOffset, aci.ServiceAccessControlSize);
|
||||
npdm.KernelCapabilityData = aciBuffer.Slice(aci.KernelCapabilityOffset, aci.KernelCapabilitySize);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static Npdm GetNpdmFromBufferUnsafe(ref byte npdmBuffer)
|
||||
{
|
||||
var npdm = new Npdm();
|
||||
|
||||
ref Meta meta = ref Unsafe.As<byte, Meta>(ref npdmBuffer);
|
||||
ref AcidHeaderData acid = ref Unsafe.As<byte, AcidHeaderData>(ref Unsafe.Add(ref npdmBuffer, meta.AcidOffset));
|
||||
ref AciHeader aci = ref Unsafe.As<byte, AciHeader>(ref Unsafe.Add(ref npdmBuffer, meta.AciOffset));
|
||||
|
||||
// Set Npdm members.
|
||||
npdm.Meta = new ReadOnlyRef<Meta>(in meta);
|
||||
npdm.Acid = new ReadOnlyRef<AcidHeaderData>(in acid);
|
||||
npdm.Aci = new ReadOnlyRef<AciHeader>(in aci);
|
||||
|
||||
npdm.FsAccessControlDescriptor = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref Unsafe.As<AcidHeaderData, byte>(ref acid), acid.FsAccessControlOffset), acid.FsAccessControlSize);
|
||||
npdm.ServiceAccessControlDescriptor = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref Unsafe.As<AcidHeaderData, byte>(ref acid), acid.ServiceAccessControlOffset), acid.ServiceAccessControlSize);
|
||||
npdm.KernelCapabilityDescriptor = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref Unsafe.As<AcidHeaderData, byte>(ref acid), acid.KernelCapabilitySize), acid.KernelCapabilitySize);
|
||||
|
||||
npdm.FsAccessControlData = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref Unsafe.As<AciHeader, byte>(ref aci), aci.FsAccessControlOffset), aci.FsAccessControlSize);
|
||||
npdm.ServiceAccessControlData = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref Unsafe.As<AciHeader, byte>(ref aci), aci.ServiceAccessControlOffset), aci.ServiceAccessControlSize);
|
||||
npdm.KernelCapabilityData = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref Unsafe.As<AciHeader, byte>(ref aci), aci.KernelCapabilitySize), aci.KernelCapabilitySize);
|
||||
|
||||
return npdm;
|
||||
}
|
||||
|
||||
private static Result ValidateSubregion(int allowedStart, int allowedEnd, int start, int size, int minSize = 0)
|
||||
{
|
||||
if (size < minSize) return ResultLoader.InvalidMeta.Log();
|
||||
if (allowedStart > start) return ResultLoader.InvalidMeta.Log();
|
||||
if (start > allowedEnd) return ResultLoader.InvalidMeta.Log();
|
||||
if (start + size > allowedEnd) return ResultLoader.InvalidMeta.Log();
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static Result ValidateMeta(ReadOnlySpan<byte> metaBuffer)
|
||||
{
|
||||
// Validate the buffer is large enough
|
||||
if (metaBuffer.Length < Unsafe.SizeOf<Meta>())
|
||||
return ResultLoader.InvalidMeta.Log();
|
||||
|
||||
ref Meta meta = ref Unsafe.As<byte, Meta>(ref MemoryMarshal.GetReference(metaBuffer));
|
||||
|
||||
// Validate magic.
|
||||
if (meta.Magic != Meta.MagicValue)
|
||||
return ResultLoader.InvalidMeta.Log();
|
||||
|
||||
// Validate flags.
|
||||
uint invalidFlagsMask = ~0x3Fu;
|
||||
|
||||
if ((meta.Flags & invalidFlagsMask) != 0)
|
||||
return ResultLoader.InvalidMeta.Log();
|
||||
|
||||
// Validate Acid extents.
|
||||
Result rc = ValidateSubregion(Unsafe.SizeOf<Meta>(), metaBuffer.Length, meta.AcidOffset,
|
||||
Unsafe.SizeOf<AcidHeaderData>());
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Validate Aci extends.
|
||||
rc = ValidateSubregion(Unsafe.SizeOf<Meta>(), metaBuffer.Length, meta.AciOffset, Unsafe.SizeOf<AciHeader>());
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static Result ValidateAcid(ReadOnlySpan<byte> acidBuffer)
|
||||
{
|
||||
// Validate the buffer is large enough
|
||||
if (acidBuffer.Length < Unsafe.SizeOf<AcidHeaderData>())
|
||||
return ResultLoader.InvalidMeta.Log();
|
||||
|
||||
ref AcidHeaderData acid = ref Unsafe.As<byte, AcidHeaderData>(ref MemoryMarshal.GetReference(acidBuffer));
|
||||
|
||||
// Validate magic.
|
||||
if (acid.Magic != AcidHeaderData.MagicValue)
|
||||
return ResultLoader.InvalidMeta.Log();
|
||||
|
||||
// Validate Fac, Sac, Kac.
|
||||
Result rc = ValidateSubregion(Unsafe.SizeOf<AcidHeaderData>(), acidBuffer.Length, acid.FsAccessControlOffset,
|
||||
acid.FsAccessControlSize);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = ValidateSubregion(Unsafe.SizeOf<AcidHeaderData>(), acidBuffer.Length, acid.ServiceAccessControlOffset,
|
||||
acid.ServiceAccessControlSize);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = ValidateSubregion(Unsafe.SizeOf<AcidHeaderData>(), acidBuffer.Length, acid.KernelCapabilityOffset,
|
||||
acid.KernelCapabilitySize);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static Result ValidateAci(ReadOnlySpan<byte> aciBuffer)
|
||||
{
|
||||
// Validate the buffer is large enough
|
||||
if (aciBuffer.Length < Unsafe.SizeOf<AciHeader>())
|
||||
return ResultLoader.InvalidMeta.Log();
|
||||
|
||||
ref AciHeader aci = ref Unsafe.As<byte, AciHeader>(ref MemoryMarshal.GetReference(aciBuffer));
|
||||
|
||||
// Validate magic.
|
||||
if (aci.Magic != AciHeader.MagicValue)
|
||||
return ResultLoader.InvalidMeta.Log();
|
||||
|
||||
// Validate Fac, Sac, Kac.
|
||||
Result rc = ValidateSubregion(Unsafe.SizeOf<AciHeader>(), aciBuffer.Length, aci.FsAccessControlOffset,
|
||||
aci.FsAccessControlSize);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = ValidateSubregion(Unsafe.SizeOf<AciHeader>(), aciBuffer.Length, aci.ServiceAccessControlOffset,
|
||||
aci.ServiceAccessControlSize);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = ValidateSubregion(Unsafe.SizeOf<AciHeader>(), aciBuffer.Length, aci.KernelCapabilityOffset,
|
||||
aci.KernelCapabilitySize);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
92
src/LibHac/Loader/Types.cs
Normal file
92
src/LibHac/Loader/Types.cs
Normal file
@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.FixedArrays;
|
||||
using LibHac.Ncm;
|
||||
#pragma warning disable 169 // Unused private fields
|
||||
|
||||
namespace LibHac.Loader
|
||||
{
|
||||
public ref struct Npdm
|
||||
{
|
||||
public ReadOnlyRef<Meta> Meta;
|
||||
public ReadOnlyRef<AcidHeaderData> Acid;
|
||||
public ReadOnlyRef<AciHeader> Aci;
|
||||
|
||||
public ReadOnlySpan<byte> FsAccessControlDescriptor;
|
||||
public ReadOnlySpan<byte> ServiceAccessControlDescriptor;
|
||||
public ReadOnlySpan<byte> KernelCapabilityDescriptor;
|
||||
|
||||
public ReadOnlySpan<byte> FsAccessControlData;
|
||||
public ReadOnlySpan<byte> ServiceAccessControlData;
|
||||
public ReadOnlySpan<byte> KernelCapabilityData;
|
||||
}
|
||||
|
||||
public struct Meta
|
||||
{
|
||||
public static readonly uint MagicValue = 0x4154454D; // META
|
||||
|
||||
public uint Magic;
|
||||
public int SignatureKeyGeneration;
|
||||
private Array4<byte> _reserved08;
|
||||
public byte Flags;
|
||||
private byte _reserved0D;
|
||||
public byte MainThreadPriority;
|
||||
public byte DefaultCpuId;
|
||||
private Array4<byte> _reserved10;
|
||||
public uint SystemResourceSize;
|
||||
public uint Version;
|
||||
public uint MainThreadStackSize;
|
||||
private Array16<byte> _programName;
|
||||
private Array16<byte> _productCode;
|
||||
private Array32<byte> _reserved40;
|
||||
private Array16<byte> _reserved60;
|
||||
public int AciOffset;
|
||||
public int AciSize;
|
||||
public int AcidOffset;
|
||||
public int AcidSize;
|
||||
|
||||
public readonly ReadOnlySpan<byte> ProgramName => _programName.ItemsRo;
|
||||
public readonly ReadOnlySpan<byte> ProductCode => _productCode.ItemsRo;
|
||||
}
|
||||
|
||||
public struct AciHeader
|
||||
{
|
||||
public static readonly uint MagicValue = 0x30494341; // ACI0
|
||||
|
||||
public uint Magic;
|
||||
private Array12<byte> _reserved04;
|
||||
public ProgramId ProgramId;
|
||||
private Array8<byte> _reserved18;
|
||||
public int FsAccessControlOffset;
|
||||
public int FsAccessControlSize;
|
||||
public int ServiceAccessControlOffset;
|
||||
public int ServiceAccessControlSize;
|
||||
public int KernelCapabilityOffset;
|
||||
public int KernelCapabilitySize;
|
||||
private Array4<byte> _reserved38;
|
||||
}
|
||||
|
||||
public struct AcidHeaderData
|
||||
{
|
||||
public static readonly uint MagicValue = 0x44494341; // ACID
|
||||
|
||||
private Array256<byte> _signature;
|
||||
private Array256<byte> _modulus;
|
||||
public uint Magic;
|
||||
public int Size;
|
||||
public byte Version;
|
||||
public uint Flags;
|
||||
public ProgramId ProgramIdMin;
|
||||
public ProgramId ProgramIdMax;
|
||||
public int FsAccessControlOffset;
|
||||
public int FsAccessControlSize;
|
||||
public int ServiceAccessControlOffset;
|
||||
public int ServiceAccessControlSize;
|
||||
public int KernelCapabilityOffset;
|
||||
public int KernelCapabilitySize;
|
||||
private Array4<byte> _reserved238;
|
||||
|
||||
public readonly ReadOnlySpan<byte> Signature => _signature.ItemsRo;
|
||||
public readonly ReadOnlySpan<byte> Modulus => _modulus.ItemsRo;
|
||||
}
|
||||
}
|
41
tests/LibHac.Tests/Common/FixedArraySizeTests.cs
Normal file
41
tests/LibHac.Tests/Common/FixedArraySizeTests.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common.FixedArrays;
|
||||
using Xunit;
|
||||
|
||||
namespace LibHac.Tests.Common
|
||||
{
|
||||
public class FixedArraySizeTests
|
||||
{
|
||||
[Fact] public static void Array1SpanSizeIsCorrect() => Assert.Equal(1, new Array1<byte>().Items.Length);
|
||||
[Fact] public static void Array1ByteSizeIsCorrect() => Assert.Equal(sizeof(byte) * 1, Unsafe.SizeOf<Array1<byte>>());
|
||||
[Fact] public static void Array1LongSizeIsCorrect() => Assert.Equal(sizeof(long) * 1, Unsafe.SizeOf<Array1<long>>());
|
||||
|
||||
[Fact] public static void Array3SpanSizeIsCorrect() => Assert.Equal(3, new Array3<byte>().Items.Length);
|
||||
[Fact] public static void Array3ByteSizeIsCorrect() => Assert.Equal(sizeof(byte) * 3, Unsafe.SizeOf<Array3<byte>>());
|
||||
[Fact] public static void Array3LongSizeIsCorrect() => Assert.Equal(sizeof(long) * 3, Unsafe.SizeOf<Array3<long>>());
|
||||
|
||||
[Fact] public static void Array8SpanSizeIsCorrect() => Assert.Equal(8, new Array8<byte>().Items.Length);
|
||||
[Fact] public static void Array8ByteSizeIsCorrect() => Assert.Equal(sizeof(byte) * 8, Unsafe.SizeOf<Array8<byte>>());
|
||||
[Fact] public static void Array8LongSizeIsCorrect() => Assert.Equal(sizeof(long) * 8, Unsafe.SizeOf<Array8<long>>());
|
||||
|
||||
[Fact] public static void Array12SpanSizeIsCorrect() => Assert.Equal(12, new Array12<byte>().Items.Length);
|
||||
[Fact] public static void Array12ByteSizeIsCorrect() => Assert.Equal(sizeof(byte) * 12, Unsafe.SizeOf<Array12<byte>>());
|
||||
[Fact] public static void Array12LongSizeIsCorrect() => Assert.Equal(sizeof(long) * 12, Unsafe.SizeOf<Array12<long>>());
|
||||
|
||||
[Fact] public static void Array32SpanSizeIsCorrect() => Assert.Equal(32, new Array32<byte>().Items.Length);
|
||||
[Fact] public static void Array32ByteSizeIsCorrect() => Assert.Equal(sizeof(byte) * 32, Unsafe.SizeOf<Array32<byte>>());
|
||||
[Fact] public static void Array32LongSizeIsCorrect() => Assert.Equal(sizeof(long) * 32, Unsafe.SizeOf<Array32<long>>());
|
||||
|
||||
[Fact] public static void Array64SpanSizeIsCorrect() => Assert.Equal(64, new Array64<byte>().Items.Length);
|
||||
[Fact] public static void Array64ByteSizeIsCorrect() => Assert.Equal(sizeof(byte) * 64, Unsafe.SizeOf<Array64<byte>>());
|
||||
[Fact] public static void Array64LongSizeIsCorrect() => Assert.Equal(sizeof(long) * 64, Unsafe.SizeOf<Array64<long>>());
|
||||
|
||||
[Fact] public static void Array128SpanSizeIsCorrect() => Assert.Equal(128, new Array128<byte>().Items.Length);
|
||||
[Fact] public static void Array128ByteSizeIsCorrect() => Assert.Equal(sizeof(byte) * 128, Unsafe.SizeOf<Array128<byte>>());
|
||||
[Fact] public static void Array128LongSizeIsCorrect() => Assert.Equal(sizeof(long) * 128, Unsafe.SizeOf<Array128<long>>());
|
||||
|
||||
[Fact] public static void Array256SpanSizeIsCorrect() => Assert.Equal(256, new Array256<byte>().Items.Length);
|
||||
[Fact] public static void Array256ByteSizeIsCorrect() => Assert.Equal(sizeof(byte) * 256, Unsafe.SizeOf<Array256<byte>>());
|
||||
[Fact] public static void Array256LongSizeIsCorrect() => Assert.Equal(sizeof(long) * 256, Unsafe.SizeOf<Array256<long>>());
|
||||
}
|
||||
}
|
27
tests/LibHac.Tests/Loader/TypeSizeTests.cs
Normal file
27
tests/LibHac.Tests/Loader/TypeSizeTests.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Loader;
|
||||
using Xunit;
|
||||
|
||||
namespace LibHac.Tests.Loader
|
||||
{
|
||||
public class TypeSizeTests
|
||||
{
|
||||
[Fact]
|
||||
public static void MetaSizeIsCorrect()
|
||||
{
|
||||
Assert.Equal(0x80, Unsafe.SizeOf<Meta>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void AciSizeIsCorrect()
|
||||
{
|
||||
Assert.Equal(0x40, Unsafe.SizeOf<AciHeader>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void AcidSizeIsCorrect()
|
||||
{
|
||||
Assert.Equal(0x240, Unsafe.SizeOf<AcidHeaderData>());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user