Add Loader.MetaLoader

This commit is contained in:
Alex Barney 2021-04-15 17:50:41 -07:00
parent bb2c870f27
commit a730c17bc5
16 changed files with 641 additions and 77 deletions

View File

@ -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;

View File

@ -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;

View 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;
}
}

View 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;
}
}

View File

@ -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;

View 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;
}
}

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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;

View 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;
}
}

View File

@ -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;

View File

@ -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;
}
}
}

View 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;
}
}
}

View 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;
}
}

View 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>>());
}
}

View 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>());
}
}
}