Update layout of Loader and Kvdb structs

This commit is contained in:
Alex Barney 2022-01-02 22:58:11 -07:00
parent b0e679d000
commit e1fd31c1ff
8 changed files with 251 additions and 93 deletions

View File

@ -0,0 +1,31 @@
#pragma warning disable CS0169, IDE0051 // Remove unused private members
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays;
public struct Array48<T>
{
public const int Length = 48;
private Array32<T> _0;
private Array16<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 Array48<T> value) => value.ItemsRo;
}

View File

@ -1,15 +1,13 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Diag; using LibHac.Diag;
namespace LibHac.Kvdb; namespace LibHac.Kvdb;
[StructLayout(LayoutKind.Sequential, Size = 0xC)]
public struct KeyValueArchiveHeader public struct KeyValueArchiveHeader
{ {
public const uint ExpectedMagic = 0x564B4D49; // IMKV public static readonly uint ExpectedMagic = 0x564B4D49; // IMKV
public uint Magic; public uint Magic;
public int Reserved; public int Reserved;
@ -25,10 +23,9 @@ public struct KeyValueArchiveHeader
} }
} }
[StructLayout(LayoutKind.Sequential, Size = 0xC)]
internal struct KeyValueArchiveEntryHeader internal struct KeyValueArchiveEntryHeader
{ {
public const uint ExpectedMagic = 0x4E454D49; // IMEN public static readonly uint ExpectedMagic = 0x4E454D49; // IMEN
public uint Magic; public uint Magic;
public int KeySize; public int KeySize;

View File

@ -2,65 +2,63 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Common.FixedArrays;
namespace LibHac.Loader; namespace LibHac.Loader;
[StructLayout(LayoutKind.Explicit, Size = 0x100)]
public struct NsoHeader public struct NsoHeader
{ {
public const int SegmentCount = 3; public static readonly int SegmentCount = 3;
[FieldOffset(0x00)] public uint Magic; public uint Magic;
[FieldOffset(0x04)] public uint Version; public uint Version;
[FieldOffset(0x08)] public uint Reserved08; public uint Reserved08;
[FieldOffset(0x0C)] public Flag Flags; public Flag Flags;
[FieldOffset(0x10)] public uint TextFileOffset; public uint TextFileOffset;
[FieldOffset(0x14)] public uint TextMemoryOffset; public uint TextMemoryOffset;
[FieldOffset(0x18)] public uint TextSize; public uint TextSize;
[FieldOffset(0x1C)] public uint ModuleNameOffset; public uint ModuleNameOffset;
[FieldOffset(0x20)] public uint RoFileOffset; public uint RoFileOffset;
[FieldOffset(0x24)] public uint RoMemoryOffset; public uint RoMemoryOffset;
[FieldOffset(0x28)] public uint RoSize; public uint RoSize;
[FieldOffset(0x2C)] public uint ModuleNameSize; public uint ModuleNameSize;
[FieldOffset(0x30)] public uint DataFileOffset; public uint DataFileOffset;
[FieldOffset(0x34)] public uint DataMemoryOffset; public uint DataMemoryOffset;
[FieldOffset(0x38)] public uint DataSize; public uint DataSize;
[FieldOffset(0x3C)] public uint BssSize; public uint BssSize;
[FieldOffset(0x40)] public Buffer32 ModuleId; public Array32<byte> ModuleId;
// Size of the sections in the NSO file // Size of the sections in the NSO file
[FieldOffset(0x60)] public uint TextFileSize; public uint TextFileSize;
[FieldOffset(0x64)] public uint RoFileSize; public uint RoFileSize;
[FieldOffset(0x68)] public uint DataFileSize; public uint DataFileSize;
[FieldOffset(0x6C)] private byte _reserved6C; public Array28<byte> Reserved6C;
[FieldOffset(0x88)] public uint ApiInfoOffset; public uint ApiInfoOffset;
[FieldOffset(0x8C)] public uint ApiInfoSize; public uint ApiInfoSize;
[FieldOffset(0x90)] public uint DynStrOffset; public uint DynStrOffset;
[FieldOffset(0x94)] public uint DynStrSize; public uint DynStrSize;
[FieldOffset(0x98)] public uint DynSymOffset; public uint DynSymOffset;
[FieldOffset(0x9C)] public uint DynSymSize; public uint DynSymSize;
[FieldOffset(0xA0)] public Buffer32 TextHash; public Array32<byte> TextHash;
[FieldOffset(0xC0)] public Buffer32 RoHash; public Array32<byte> RoHash;
[FieldOffset(0xE0)] public Buffer32 DataHash; public Array32<byte> DataHash;
public Span<SegmentHeader> Segments => public Span<SegmentHeader> Segments =>
SpanHelpers.CreateSpan(ref Unsafe.As<uint, SegmentHeader>(ref TextFileOffset), SegmentCount); SpanHelpers.CreateSpan(ref Unsafe.As<uint, SegmentHeader>(ref TextFileOffset), SegmentCount);
public Span<uint> CompressedSizes => SpanHelpers.CreateSpan(ref TextFileSize, SegmentCount); public Span<uint> CompressedSizes => SpanHelpers.CreateSpan(ref TextFileSize, SegmentCount);
public Span<Buffer32> SegmentHashes => SpanHelpers.CreateSpan(ref TextHash, SegmentCount); public Span<Array32<byte>> SegmentHashes => SpanHelpers.CreateSpan(ref TextHash, SegmentCount);
public Span<byte> Reserved6C => SpanHelpers.CreateSpan(ref _reserved6C, 0x1C);
[Flags] [Flags]
public enum Flag public enum Flag
@ -73,11 +71,12 @@ public struct NsoHeader
DataHash = 1 << 5 DataHash = 1 << 5
} }
[StructLayout(LayoutKind.Sequential, Size = 0x10)] [StructLayout(LayoutKind.Sequential)]
public struct SegmentHeader public struct SegmentHeader
{ {
public uint FileOffset; public uint FileOffset;
public uint MemoryOffset; public uint MemoryOffset;
public uint Size; public uint Size;
private int _unused;
} }
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Common.FixedArrays;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using LibHac.Util; using LibHac.Util;
@ -56,7 +57,7 @@ public class NsoReader
Header.SegmentHashes[(int)segment], isCompressed, checkHash, buffer); Header.SegmentHashes[(int)segment], isCompressed, checkHash, buffer);
} }
private Result ReadSegmentImpl(ref NsoHeader.SegmentHeader segment, uint fileSize, Buffer32 fileHash, private Result ReadSegmentImpl(ref NsoHeader.SegmentHeader segment, uint fileSize, Array32<byte> fileHash,
bool isCompressed, bool checkHash, Span<byte> buffer) bool isCompressed, bool checkHash, Span<byte> buffer)
{ {
// Select read size based on compression. // Select read size based on compression.
@ -90,10 +91,10 @@ public class NsoReader
// Check hash if necessary. // Check hash if necessary.
if (checkHash) if (checkHash)
{ {
var hash = new Buffer32(); var hash = new Array32<byte>();
Crypto.Sha256.GenerateSha256Hash(buffer.Slice(0, (int)segment.Size), hash.Bytes); Crypto.Sha256.GenerateSha256Hash(buffer.Slice(0, (int)segment.Size), hash.Items);
if (hash.Bytes.SequenceCompareTo(fileHash.Bytes) != 0) if (hash.ItemsRo.SequenceCompareTo(fileHash) != 0)
return ResultLoader.InvalidNso.Log(); return ResultLoader.InvalidNso.Log();
} }

View File

@ -27,26 +27,22 @@ public struct Meta
public uint Magic; public uint Magic;
public int SignatureKeyGeneration; public int SignatureKeyGeneration;
private Array4<byte> _reserved08; public Array4<byte> Reserved08;
public byte Flags; public byte Flags;
private byte _reserved0D; public byte Reserved0D;
public byte MainThreadPriority; public byte MainThreadPriority;
public byte DefaultCpuId; public byte DefaultCpuId;
private Array4<byte> _reserved10; public Array4<byte> Reserved10;
public uint SystemResourceSize; public uint SystemResourceSize;
public uint Version; public uint Version;
public uint MainThreadStackSize; public uint MainThreadStackSize;
private Array16<byte> _programName; public Array16<byte> ProgramName;
private Array16<byte> _productCode; public Array16<byte> ProductCode;
private Array32<byte> _reserved40; public Array48<byte> Reserved40;
private Array16<byte> _reserved60;
public int AciOffset; public int AciOffset;
public int AciSize; public int AciSize;
public int AcidOffset; public int AcidOffset;
public int AcidSize; public int AcidSize;
public readonly ReadOnlySpan<byte> ProgramName => _programName.ItemsRo;
public readonly ReadOnlySpan<byte> ProductCode => _productCode.ItemsRo;
} }
public struct AciHeader public struct AciHeader
@ -54,24 +50,24 @@ public struct AciHeader
public static readonly uint MagicValue = 0x30494341; // ACI0 public static readonly uint MagicValue = 0x30494341; // ACI0
public uint Magic; public uint Magic;
private Array12<byte> _reserved04; public Array12<byte> Reserved04;
public ProgramId ProgramId; public ProgramId ProgramId;
private Array8<byte> _reserved18; public Array8<byte> Reserved18;
public int FsAccessControlOffset; public int FsAccessControlOffset;
public int FsAccessControlSize; public int FsAccessControlSize;
public int ServiceAccessControlOffset; public int ServiceAccessControlOffset;
public int ServiceAccessControlSize; public int ServiceAccessControlSize;
public int KernelCapabilityOffset; public int KernelCapabilityOffset;
public int KernelCapabilitySize; public int KernelCapabilitySize;
private Array4<byte> _reserved38; public Array8<byte> Reserved38;
} }
public struct AcidHeaderData public struct AcidHeaderData
{ {
public static readonly uint MagicValue = 0x44494341; // ACID public static readonly uint MagicValue = 0x44494341; // ACID
private Array256<byte> _signature; public Array256<byte> Signature;
private Array256<byte> _modulus; public Array256<byte> Modulus;
public uint Magic; public uint Magic;
public int Size; public int Size;
public byte Version; public byte Version;
@ -84,8 +80,5 @@ public struct AcidHeaderData
public int ServiceAccessControlSize; public int ServiceAccessControlSize;
public int KernelCapabilityOffset; public int KernelCapabilityOffset;
public int KernelCapabilitySize; public int KernelCapabilitySize;
private Array4<byte> _reserved238; public Array4<byte> Reserved238;
public readonly ReadOnlySpan<byte> Signature => _signature.ItemsRo;
public readonly ReadOnlySpan<byte> Modulus => _modulus.ItemsRo;
} }

View File

@ -0,0 +1,33 @@
using System.Runtime.CompilerServices;
using LibHac.Kvdb;
using Xunit;
using static LibHac.Tests.Common.Layout;
namespace LibHac.Tests.Kvdb;
public class TypeLayoutTests
{
[Fact]
public static void KeyValueArchiveHeader_Layout()
{
var s = new KeyValueArchiveHeader();
Assert.Equal(0xC, Unsafe.SizeOf<KeyValueArchiveHeader>());
Assert.Equal(0x0, GetOffset(in s, in s.Magic));
Assert.Equal(0x4, GetOffset(in s, in s.Reserved));
Assert.Equal(0x8, GetOffset(in s, in s.EntryCount));
}
[Fact]
public static void KeyValueArchiveEntryHeader_Layout()
{
var s = new KeyValueArchiveEntryHeader();
Assert.Equal(0xC, Unsafe.SizeOf<KeyValueArchiveEntryHeader>());
Assert.Equal(0x0, GetOffset(in s, in s.Magic));
Assert.Equal(0x4, GetOffset(in s, in s.KeySize));
Assert.Equal(0x8, GetOffset(in s, in s.ValueSize));
}
}

View File

@ -0,0 +1,130 @@
using System.Runtime.CompilerServices;
using LibHac.Loader;
using Xunit;
using static LibHac.Tests.Common.Layout;
namespace LibHac.Tests.Loader;
public class TypeLayoutTests
{
[Fact]
public static void NsoHeader_Layout()
{
var s = new NsoHeader();
Assert.Equal(0x100, Unsafe.SizeOf<NsoHeader>());
Assert.Equal(0x00, GetOffset(in s, in s.Magic));
Assert.Equal(0x04, GetOffset(in s, in s.Version));
Assert.Equal(0x08, GetOffset(in s, in s.Reserved08));
Assert.Equal(0x0C, GetOffset(in s, in s.Flags));
Assert.Equal(0x10, GetOffset(in s, in s.TextFileOffset));
Assert.Equal(0x14, GetOffset(in s, in s.TextMemoryOffset));
Assert.Equal(0x18, GetOffset(in s, in s.TextSize));
Assert.Equal(0x1C, GetOffset(in s, in s.ModuleNameOffset));
Assert.Equal(0x20, GetOffset(in s, in s.RoFileOffset));
Assert.Equal(0x24, GetOffset(in s, in s.RoMemoryOffset));
Assert.Equal(0x28, GetOffset(in s, in s.RoSize));
Assert.Equal(0x2C, GetOffset(in s, in s.ModuleNameSize));
Assert.Equal(0x30, GetOffset(in s, in s.DataFileOffset));
Assert.Equal(0x34, GetOffset(in s, in s.DataMemoryOffset));
Assert.Equal(0x38, GetOffset(in s, in s.DataSize));
Assert.Equal(0x3C, GetOffset(in s, in s.BssSize));
Assert.Equal(0x40, GetOffset(in s, in s.ModuleId));
Assert.Equal(0x60, GetOffset(in s, in s.TextFileSize));
Assert.Equal(0x64, GetOffset(in s, in s.RoFileSize));
Assert.Equal(0x68, GetOffset(in s, in s.DataFileSize));
Assert.Equal(0x6C, GetOffset(in s, in s.Reserved6C));
Assert.Equal(0x88, GetOffset(in s, in s.ApiInfoOffset));
Assert.Equal(0x8C, GetOffset(in s, in s.ApiInfoSize));
Assert.Equal(0x90, GetOffset(in s, in s.DynStrOffset));
Assert.Equal(0x94, GetOffset(in s, in s.DynStrSize));
Assert.Equal(0x98, GetOffset(in s, in s.DynSymOffset));
Assert.Equal(0x9C, GetOffset(in s, in s.DynSymSize));
Assert.Equal(0xA0, GetOffset(in s, in s.TextHash));
Assert.Equal(0xC0, GetOffset(in s, in s.RoHash));
Assert.Equal(0xE0, GetOffset(in s, in s.DataHash));
}
[Fact]
public static void Meta_layout()
{
var s = new Meta();
Assert.Equal(0x80, Unsafe.SizeOf<Meta>());
Assert.Equal(0x00, GetOffset(in s, in s.Magic));
Assert.Equal(0x04, GetOffset(in s, in s.SignatureKeyGeneration));
Assert.Equal(0x08, GetOffset(in s, in s.Reserved08));
Assert.Equal(0x0C, GetOffset(in s, in s.Flags));
Assert.Equal(0x0D, GetOffset(in s, in s.Reserved0D));
Assert.Equal(0x0E, GetOffset(in s, in s.MainThreadPriority));
Assert.Equal(0x0F, GetOffset(in s, in s.DefaultCpuId));
Assert.Equal(0x10, GetOffset(in s, in s.Reserved10));
Assert.Equal(0x14, GetOffset(in s, in s.SystemResourceSize));
Assert.Equal(0x18, GetOffset(in s, in s.Version));
Assert.Equal(0x1C, GetOffset(in s, in s.MainThreadStackSize));
Assert.Equal(0x20, GetOffset(in s, in s.ProgramName));
Assert.Equal(0x30, GetOffset(in s, in s.ProductCode));
Assert.Equal(0x40, GetOffset(in s, in s.Reserved40));
Assert.Equal(0x70, GetOffset(in s, in s.AciOffset));
Assert.Equal(0x74, GetOffset(in s, in s.AciSize));
Assert.Equal(0x78, GetOffset(in s, in s.AcidOffset));
Assert.Equal(0x7C, GetOffset(in s, in s.AcidSize));
}
[Fact]
public static void AciHeader_Layout()
{
var s = new AciHeader();
Assert.Equal(0x40, Unsafe.SizeOf<AciHeader>());
Assert.Equal(0x00, GetOffset(in s, in s.Magic));
Assert.Equal(0x04, GetOffset(in s, in s.Reserved04));
Assert.Equal(0x10, GetOffset(in s, in s.ProgramId));
Assert.Equal(0x18, GetOffset(in s, in s.Reserved18));
Assert.Equal(0x20, GetOffset(in s, in s.FsAccessControlOffset));
Assert.Equal(0x24, GetOffset(in s, in s.FsAccessControlSize));
Assert.Equal(0x28, GetOffset(in s, in s.ServiceAccessControlOffset));
Assert.Equal(0x2C, GetOffset(in s, in s.ServiceAccessControlSize));
Assert.Equal(0x30, GetOffset(in s, in s.KernelCapabilityOffset));
Assert.Equal(0x34, GetOffset(in s, in s.KernelCapabilitySize));
Assert.Equal(0x38, GetOffset(in s, in s.Reserved38));
}
[Fact]
public static void AcidHeaderData_Layout()
{
var s = new AcidHeaderData();
Assert.Equal(0x240, Unsafe.SizeOf<AcidHeaderData>());
Assert.Equal(0x000, GetOffset(in s, in s.Signature));
Assert.Equal(0x100, GetOffset(in s, in s.Modulus));
Assert.Equal(0x200, GetOffset(in s, in s.Magic));
Assert.Equal(0x204, GetOffset(in s, in s.Size));
Assert.Equal(0x208, GetOffset(in s, in s.Version));
Assert.Equal(0x20C, GetOffset(in s, in s.Flags));
Assert.Equal(0x210, GetOffset(in s, in s.ProgramIdMin));
Assert.Equal(0x218, GetOffset(in s, in s.ProgramIdMax));
Assert.Equal(0x220, GetOffset(in s, in s.FsAccessControlOffset));
Assert.Equal(0x224, GetOffset(in s, in s.FsAccessControlSize));
Assert.Equal(0x228, GetOffset(in s, in s.ServiceAccessControlOffset));
Assert.Equal(0x22C, GetOffset(in s, in s.ServiceAccessControlSize));
Assert.Equal(0x230, GetOffset(in s, in s.KernelCapabilityOffset));
Assert.Equal(0x234, GetOffset(in s, in s.KernelCapabilitySize));
Assert.Equal(0x238, GetOffset(in s, in s.Reserved238));
}
}

View File

@ -1,26 +0,0 @@
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>());
}
}