mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Update layout of Boot and some Fs structs
Boot: - EncryptedKeyBlob - KeyBlob - Package1MarikoOemHeader - Package1MetaData - Package1Stage1Footer - Package1Pk11Header - Package2Header - Package2Meta Fs: - ApplicationInfo - CodeVerificationData - DirectoryEntry - EncryptionSeed - FileSystemProxyErrorInfo - StorageErrorInfo - FileTimeStamp - FileTimeStampRaw - ProgramIndexMapInfo - QueryRangeInfo - RightsId
This commit is contained in:
parent
57750b896d
commit
ec38f80066
34 changed files with 726 additions and 313 deletions
|
@ -1,42 +1,27 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
using LibHac.Common.FixedArrays;
|
||||||
using LibHac.Crypto;
|
using LibHac.Crypto;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Boot;
|
namespace LibHac.Boot;
|
||||||
|
|
||||||
[DebuggerDisplay("{ToString()}")]
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0xB0)]
|
|
||||||
public struct EncryptedKeyBlob
|
public struct EncryptedKeyBlob
|
||||||
{
|
{
|
||||||
#if DEBUG
|
public AesCmac Cmac;
|
||||||
[FieldOffset(0x00)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy1;
|
public AesIv Counter;
|
||||||
[FieldOffset(0x20)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy2;
|
public Array144<byte> Payload;
|
||||||
[FieldOffset(0x40)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy3;
|
|
||||||
[FieldOffset(0x60)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy4;
|
|
||||||
[FieldOffset(0x80)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy5;
|
|
||||||
[FieldOffset(0xA0)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer16 _dummy6;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[FieldOffset(0x00)] public AesCmac Cmac;
|
|
||||||
[FieldOffset(0x10)] public AesIv Counter;
|
|
||||||
|
|
||||||
public Span<byte> Payload => Bytes.Slice(0x20, Unsafe.SizeOf<KeyBlob>());
|
|
||||||
|
|
||||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||||
public readonly ReadOnlySpan<byte> ReadOnlyBytes => SpanHelpers.AsReadOnlyByteSpan(in this);
|
public readonly ReadOnlySpan<byte> BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly bool IsZeros()
|
public readonly bool IsZeros()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<ulong> ulongSpan = MemoryMarshal.Cast<byte, ulong>(ReadOnlyBytes);
|
foreach (ulong val in SpanHelpers.AsReadOnlySpan<EncryptedKeyBlob, ulong>(in this))
|
||||||
|
|
||||||
for (int i = 0; i < ulongSpan.Length; i++)
|
|
||||||
{
|
{
|
||||||
if (ulongSpan[i] != 0)
|
if (val != 0)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,39 +29,27 @@ public struct EncryptedKeyBlob
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static implicit operator ReadOnlySpan<byte>(in EncryptedKeyBlob value)
|
public static implicit operator ReadOnlySpan<byte>(in EncryptedKeyBlob value) =>
|
||||||
{
|
SpanHelpers.AsReadOnlyByteSpan(in value);
|
||||||
return SpanHelpers.AsReadOnlyByteSpan(in value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly override string ToString() => ReadOnlyBytes.ToHexString();
|
public readonly override string ToString() => BytesRo.ToHexString();
|
||||||
}
|
}
|
||||||
|
|
||||||
[DebuggerDisplay("{ToString()}")]
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x90)]
|
|
||||||
public struct KeyBlob
|
public struct KeyBlob
|
||||||
{
|
{
|
||||||
#if DEBUG
|
public AesKey MasterKek;
|
||||||
[FieldOffset(0x00)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy1;
|
public Array112<byte> Unused;
|
||||||
[FieldOffset(0x20)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy2;
|
public AesKey Package1Key;
|
||||||
[FieldOffset(0x40)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy3;
|
|
||||||
[FieldOffset(0x60)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy4;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[FieldOffset(0x00)] public AesKey MasterKek;
|
|
||||||
[FieldOffset(0x80)] public AesKey Package1Key;
|
|
||||||
|
|
||||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||||
public readonly ReadOnlySpan<byte> ReadOnlyBytes => SpanHelpers.AsReadOnlyByteSpan(in this);
|
public readonly ReadOnlySpan<byte> BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly bool IsZeros()
|
public readonly bool IsZeros()
|
||||||
{
|
{
|
||||||
ReadOnlySpan<ulong> ulongSpan = MemoryMarshal.Cast<byte, ulong>(ReadOnlyBytes);
|
foreach (ulong val in SpanHelpers.AsReadOnlySpan<KeyBlob, ulong>(in this))
|
||||||
|
|
||||||
for (int i = 0; i < ulongSpan.Length; i++)
|
|
||||||
{
|
{
|
||||||
if (ulongSpan[i] != 0)
|
if (val != 0)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,10 +57,6 @@ public struct KeyBlob
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static implicit operator ReadOnlySpan<byte>(in KeyBlob value)
|
public static implicit operator ReadOnlySpan<byte>(in KeyBlob value) => SpanHelpers.AsReadOnlyByteSpan(in value);
|
||||||
{
|
public readonly override string ToString() => BytesRo.ToHexString();
|
||||||
return SpanHelpers.AsReadOnlyByteSpan(in value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly override string ToString() => ReadOnlyBytes.ToHexString();
|
|
||||||
}
|
}
|
|
@ -1,71 +1,63 @@
|
||||||
using LibHac.Common;
|
using System;
|
||||||
using LibHac.Fs;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Common.FixedArrays;
|
||||||
using LibHac.Common.Keys;
|
using LibHac.Common.Keys;
|
||||||
using LibHac.Diag;
|
using LibHac.Diag;
|
||||||
|
using LibHac.Fs;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Boot;
|
namespace LibHac.Boot;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x170)]
|
|
||||||
public struct Package1MarikoOemHeader
|
public struct Package1MarikoOemHeader
|
||||||
{
|
{
|
||||||
[FieldOffset(0x000)] private byte _aesMac;
|
public Array16<byte> AesMac;
|
||||||
[FieldOffset(0x010)] private byte _rsaSig;
|
public Array256<byte> RsaSig;
|
||||||
[FieldOffset(0x110)] private byte _salt;
|
public Array32<byte> Salt;
|
||||||
[FieldOffset(0x130)] private byte _hash;
|
public Array32<byte> Hash;
|
||||||
[FieldOffset(0x150)] public int Version;
|
public int Version;
|
||||||
[FieldOffset(0x154)] public int Size;
|
public int Size;
|
||||||
[FieldOffset(0x158)] public int LoadAddress;
|
public int LoadAddress;
|
||||||
[FieldOffset(0x15C)] public int EntryPoint;
|
public int EntryPoint;
|
||||||
[FieldOffset(0x160)] private byte _reserved;
|
public Array16<byte> Reserved;
|
||||||
|
|
||||||
public ReadOnlySpan<byte> AesMac => SpanHelpers.CreateSpan(ref _aesMac, 0x10);
|
|
||||||
public ReadOnlySpan<byte> RsaSig => SpanHelpers.CreateSpan(ref _rsaSig, 0x100);
|
|
||||||
public ReadOnlySpan<byte> Salt => SpanHelpers.CreateSpan(ref _salt, 0x20);
|
|
||||||
public ReadOnlySpan<byte> Hash => SpanHelpers.CreateSpan(ref _hash, 0x20);
|
|
||||||
public ReadOnlySpan<byte> Reserved => SpanHelpers.CreateSpan(ref _reserved, 0x10);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x20)]
|
|
||||||
public struct Package1MetaData
|
public struct Package1MetaData
|
||||||
{
|
{
|
||||||
[FieldOffset(0x00)] public uint LoaderHash;
|
public uint LoaderHash;
|
||||||
[FieldOffset(0x04)] public uint SecureMonitorHash;
|
public uint SecureMonitorHash;
|
||||||
[FieldOffset(0x08)] public uint BootloaderHash;
|
public uint BootloaderHash;
|
||||||
[FieldOffset(0x10)] private byte _buildDate;
|
public uint Reserved;
|
||||||
[FieldOffset(0x1E)] public byte KeyGeneration;
|
private Array14<byte> _buildDate;
|
||||||
[FieldOffset(0x1F)] public byte Version;
|
public byte KeyGeneration;
|
||||||
|
public byte Version;
|
||||||
|
|
||||||
public U8Span BuildDate => new U8Span(SpanHelpers.CreateSpan(ref _buildDate, 0xE));
|
public U8Span BuildDate => new U8Span(_buildDate);
|
||||||
public ReadOnlySpan<byte> Iv => SpanHelpers.CreateSpan(ref _buildDate, 0x10);
|
public ReadOnlySpan<byte> Iv => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_buildDate.Items), 0x10);
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x20)]
|
|
||||||
public struct Package1Stage1Footer
|
public struct Package1Stage1Footer
|
||||||
{
|
{
|
||||||
[FieldOffset(0x00)] public int Pk11Size;
|
public int Pk11Size;
|
||||||
[FieldOffset(0x10)] private byte _iv;
|
public Array12<byte> Reserved;
|
||||||
|
public Array16<byte> Iv;
|
||||||
public ReadOnlySpan<byte> Iv => SpanHelpers.CreateSpan(ref _iv, 0x10);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x20)]
|
|
||||||
public struct Package1Pk11Header
|
public struct Package1Pk11Header
|
||||||
{
|
{
|
||||||
public const uint ExpectedMagic = 0x31314B50; // PK11
|
public static readonly uint ExpectedMagic = 0x31314B50; // PK11
|
||||||
|
|
||||||
[FieldOffset(0x00)] public uint Magic;
|
public uint Magic;
|
||||||
[FieldOffset(0x04)] public int WarmBootSize;
|
public int WarmBootSize;
|
||||||
[FieldOffset(0x08)] public int WarmBootOffset;
|
public int WarmBootOffset;
|
||||||
[FieldOffset(0x10)] public int BootloaderSize;
|
public int Reserved;
|
||||||
[FieldOffset(0x14)] public int BootloaderOffset;
|
public int BootloaderSize;
|
||||||
[FieldOffset(0x18)] public int SecureMonitorSize;
|
public int BootloaderOffset;
|
||||||
[FieldOffset(0x1C)] public int SecureMonitorOffset;
|
public int SecureMonitorSize;
|
||||||
|
public int SecureMonitorOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Package1Section
|
public enum Package1Section
|
||||||
|
@ -102,13 +94,13 @@ public class Package1
|
||||||
private Package1MetaData _metaData;
|
private Package1MetaData _metaData;
|
||||||
private Package1Stage1Footer _stage1Footer;
|
private Package1Stage1Footer _stage1Footer;
|
||||||
private Package1Pk11Header _pk11Header;
|
private Package1Pk11Header _pk11Header;
|
||||||
private Buffer16 _pk11Mac;
|
private Array16<byte> _pk11Mac;
|
||||||
|
|
||||||
public ref readonly Package1MarikoOemHeader MarikoOemHeader => ref _marikoOemHeader;
|
public ref readonly Package1MarikoOemHeader MarikoOemHeader => ref _marikoOemHeader;
|
||||||
public ref readonly Package1MetaData MetaData => ref _metaData;
|
public ref readonly Package1MetaData MetaData => ref _metaData;
|
||||||
public ref readonly Package1Stage1Footer Stage1Footer => ref _stage1Footer;
|
public ref readonly Package1Stage1Footer Stage1Footer => ref _stage1Footer;
|
||||||
public ref readonly Package1Pk11Header Pk11Header => ref _pk11Header;
|
public ref readonly Package1Pk11Header Pk11Header => ref _pk11Header;
|
||||||
public ref readonly Buffer16 Pk11Mac => ref _pk11Mac;
|
public ref readonly Array16<byte> Pk11Mac => ref _pk11Mac;
|
||||||
|
|
||||||
public Result Initialize(KeySet keySet, in SharedRef<IStorage> storage)
|
public Result Initialize(KeySet keySet, in SharedRef<IStorage> storage)
|
||||||
{
|
{
|
||||||
|
@ -259,7 +251,7 @@ public class Package1
|
||||||
|
|
||||||
private Result ReadModernEristaMac()
|
private Result ReadModernEristaMac()
|
||||||
{
|
{
|
||||||
return _baseStorage.Get.Read(ModernStage1Size + Pk11Size, _pk11Mac.Bytes);
|
return _baseStorage.Get.Read(ModernStage1Size + Pk11Size, _pk11Mac.Items);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result SetPk11Storage()
|
private Result SetPk11Storage()
|
||||||
|
@ -295,7 +287,7 @@ public class Package1
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
decPk11Storage = new Aes128CtrStorage(encPk11Storage,
|
decPk11Storage = new Aes128CtrStorage(encPk11Storage,
|
||||||
KeySet.Package1Keys[KeyRevision].DataRo.ToArray(), _stage1Footer.Iv.ToArray(), true);
|
KeySet.Package1Keys[KeyRevision].DataRo.ToArray(), _stage1Footer.Iv.ItemsRo.ToArray(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
_pk11Storage = new SubStorage(new CachedStorage(decPk11Storage, 0x4000, 1, true), 0, Pk11Size);
|
_pk11Storage = new SubStorage(new CachedStorage(decPk11Storage, 0x4000, 1, true), 0, Pk11Size);
|
||||||
|
@ -359,7 +351,7 @@ public class Package1
|
||||||
// MarikoOemHeader must be read first
|
// MarikoOemHeader must be read first
|
||||||
private bool IsMarikoImpl()
|
private bool IsMarikoImpl()
|
||||||
{
|
{
|
||||||
return MarikoOemHeader.AesMac.IsZeros() && MarikoOemHeader.Reserved.IsZeros();
|
return MarikoOemHeader.AesMac.ItemsRo.IsZeros() && MarikoOemHeader.Reserved.ItemsRo.IsZeros();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -392,7 +384,7 @@ public class Package1
|
||||||
|
|
||||||
if (IsModern)
|
if (IsModern)
|
||||||
{
|
{
|
||||||
storages.Add(new MemoryStorage(_pk11Mac.Bytes.ToArray()));
|
storages.Add(new MemoryStorage(_pk11Mac.ItemsRo.ToArray()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,7 +429,6 @@ public class Package1
|
||||||
return new SubStorage(_pk11Storage, offset, size);
|
return new SubStorage(_pk11Storage, offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public IStorage OpenDecryptedWarmBootStorage()
|
public IStorage OpenDecryptedWarmBootStorage()
|
||||||
{
|
{
|
||||||
if (!IsDecrypted)
|
if (!IsDecrypted)
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
using LibHac.Common.FixedArrays;
|
||||||
using LibHac.Crypto;
|
using LibHac.Crypto;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
#if DEBUG
|
|
||||||
using System.Diagnostics;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace LibHac.Boot;
|
namespace LibHac.Boot;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x200)]
|
|
||||||
public struct Package2Header
|
public struct Package2Header
|
||||||
{
|
{
|
||||||
internal const int Package2SizeMax = (1024 * 1024 * 8) - (1024 * 16); // 8MB - 16KB
|
internal const int Package2SizeMax = (1024 * 1024 * 8) - (1024 * 16); // 8MB - 16KB
|
||||||
|
@ -18,14 +14,12 @@ public struct Package2Header
|
||||||
internal const int PayloadCount = 3;
|
internal const int PayloadCount = 3;
|
||||||
|
|
||||||
internal const int SignatureSize = 0x100;
|
internal const int SignatureSize = 0x100;
|
||||||
private ReadOnlySpan<byte> RsaPublicKeyExponent => new byte[] { 0x00, 0x01, 0x00, 0x01 };
|
private static ReadOnlySpan<byte> RsaPublicKeyExponent => new byte[] { 0x00, 0x01, 0x00, 0x01 };
|
||||||
|
|
||||||
[FieldOffset(0x00)] private byte _signature;
|
public Array256<byte> Signature;
|
||||||
[FieldOffset(0x100)] public Package2Meta Meta;
|
public Package2Meta Meta;
|
||||||
|
|
||||||
public ReadOnlySpan<byte> Signature => SpanHelpers.CreateSpan(ref _signature, SignatureSize);
|
public readonly Result VerifySignature(ReadOnlySpan<byte> modulus, ReadOnlySpan<byte> data)
|
||||||
|
|
||||||
public Result VerifySignature(ReadOnlySpan<byte> modulus, ReadOnlySpan<byte> data)
|
|
||||||
{
|
{
|
||||||
if (!Rsa.VerifyRsa2048PssSha256(Signature, modulus, RsaPublicKeyExponent, data))
|
if (!Rsa.VerifyRsa2048PssSha256(Signature, modulus, RsaPublicKeyExponent, data))
|
||||||
{
|
{
|
||||||
|
@ -34,53 +28,38 @@ public struct Package2Header
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] [FieldOffset(0x00)] private readonly Padding100 PaddingForVsDebugging;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x100)]
|
|
||||||
public struct Package2Meta
|
public struct Package2Meta
|
||||||
{
|
{
|
||||||
public const uint ExpectedMagicValue = 0x31324B50; // PK21
|
public static readonly uint ExpectedMagicValue = 0x31324B50; // PK21
|
||||||
|
|
||||||
[FieldOffset(0x00)] private Buffer16 _headerIv;
|
public Array16<byte> HeaderIv;
|
||||||
|
public Array3<Array16<byte>> PayloadIvs;
|
||||||
|
public Array16<byte> Padding40;
|
||||||
|
|
||||||
[FieldOffset(0x00)] private uint _package2Size;
|
public uint Magic;
|
||||||
[FieldOffset(0x04)] private byte _keyGeneration;
|
public uint EntryPoint;
|
||||||
|
public Array4<byte> Padding58;
|
||||||
|
public byte Package2Version;
|
||||||
|
public byte BootloaderVersion;
|
||||||
|
|
||||||
[FieldOffset(0x06)] private byte _keyGenerationXor1;
|
public Array3<uint> PayloadSizes;
|
||||||
[FieldOffset(0x07)] private byte _keyGenerationXor2;
|
public Array4<byte> Padding6C;
|
||||||
[FieldOffset(0x08)] private uint _sizeXor1;
|
public Array3<uint> PayloadOffsets;
|
||||||
[FieldOffset(0x0C)] private uint _sizeXor2;
|
public Array4<byte> Padding7C;
|
||||||
|
public Array3<Array32<byte>> PayloadHashes;
|
||||||
|
public Array32<byte> PaddingE0;
|
||||||
|
|
||||||
[FieldOffset(0x10)] private Buffer16 _payloadIvs;
|
public readonly uint GetSize()
|
||||||
|
{
|
||||||
|
ReadOnlySpan<uint> ints = SpanHelpers.AsReadOnlySpan<Array16<byte>, uint>(in HeaderIv);
|
||||||
|
return ints[0] ^ ints[2] ^ ints[3];
|
||||||
|
}
|
||||||
|
|
||||||
[FieldOffset(0x50)] private readonly uint _magic;
|
public readonly byte GetKeyGeneration() => (byte)Math.Max(0, (HeaderIv[4] ^ HeaderIv[6] ^ HeaderIv[7]) - 1);
|
||||||
[FieldOffset(0x54)] private readonly uint _entryPoint;
|
|
||||||
[FieldOffset(0x5C)] private readonly byte _package2Version;
|
|
||||||
[FieldOffset(0x5D)] private readonly byte _bootloaderVersion;
|
|
||||||
|
|
||||||
[FieldOffset(0x60)] private uint _payloadSizes;
|
public readonly int GetPayloadFileOffset(int index)
|
||||||
[FieldOffset(0x70)] private uint _payloadOffsets;
|
|
||||||
[FieldOffset(0x80)] private Buffer32 _payloadHashes;
|
|
||||||
|
|
||||||
public uint Magic => _magic;
|
|
||||||
public uint EntryPoint => _entryPoint;
|
|
||||||
public byte Package2Version => _package2Version;
|
|
||||||
public byte BootloaderVersion => _bootloaderVersion;
|
|
||||||
|
|
||||||
public Buffer16 HeaderIv => _headerIv;
|
|
||||||
public readonly uint Size => _package2Size ^ _sizeXor1 ^ _sizeXor2;
|
|
||||||
public byte KeyGeneration => (byte)Math.Max(0, (_keyGeneration ^ _keyGenerationXor1 ^ _keyGenerationXor2) - 1);
|
|
||||||
|
|
||||||
public ReadOnlySpan<Buffer16> PayloadIvs => SpanHelpers.CreateSpan(ref _payloadIvs, Package2Header.PayloadCount);
|
|
||||||
public ReadOnlySpan<uint> PayloadSizes => SpanHelpers.CreateSpan(ref _payloadSizes, Package2Header.PayloadCount);
|
|
||||||
public ReadOnlySpan<uint> PayloadOffsets => SpanHelpers.CreateSpan(ref _payloadOffsets, Package2Header.PayloadCount);
|
|
||||||
public ReadOnlySpan<Buffer32> PayloadHashes => SpanHelpers.CreateSpan(ref _payloadHashes, Package2Header.PayloadCount);
|
|
||||||
|
|
||||||
public int GetPayloadFileOffset(int index)
|
|
||||||
{
|
{
|
||||||
if ((uint)index >= Package2Header.PayloadCount)
|
if ((uint)index >= Package2Header.PayloadCount)
|
||||||
throw new IndexOutOfRangeException("Invalid payload index.");
|
throw new IndexOutOfRangeException("Invalid payload index.");
|
||||||
|
@ -95,11 +74,11 @@ public struct Package2Meta
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Verify()
|
public readonly Result Verify()
|
||||||
{
|
{
|
||||||
// Get the obfuscated metadata.
|
// Get the obfuscated metadata.
|
||||||
uint size = Size;
|
uint size = GetSize();
|
||||||
byte keyGeneration = KeyGeneration;
|
byte keyGeneration = GetKeyGeneration();
|
||||||
|
|
||||||
// Check that size is big enough for the header.
|
// Check that size is big enough for the header.
|
||||||
if (size < Unsafe.SizeOf<Package2Header>())
|
if (size < Unsafe.SizeOf<Package2Header>())
|
||||||
|
@ -128,7 +107,7 @@ public struct Package2Meta
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the sizes sum to the total.
|
// Check that the sizes sum to the total.
|
||||||
if (Size != Unsafe.SizeOf<Package2Header>() + PayloadSizes[0] + PayloadSizes[1] + PayloadSizes[2])
|
if (GetSize() != Unsafe.SizeOf<Package2Header>() + PayloadSizes[0] + PayloadSizes[1] + PayloadSizes[2])
|
||||||
return ResultLibHac.InvalidPackage2MetaTotalSize.Log();
|
return ResultLibHac.InvalidPackage2MetaTotalSize.Log();
|
||||||
|
|
||||||
// Check that the payloads do not overflow.
|
// Check that the payloads do not overflow.
|
||||||
|
@ -156,8 +135,4 @@ public struct Package2Meta
|
||||||
// No payload contains the entrypoint, so we're not valid.
|
// No payload contains the entrypoint, so we're not valid.
|
||||||
return ResultLibHac.InvalidPackage2MetaEntryPointNotFound.Log();
|
return ResultLibHac.InvalidPackage2MetaEntryPointNotFound.Log();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#if DEBUG
|
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] [FieldOffset(0x00)] private readonly Padding100 PaddingForVsDebugging;
|
|
||||||
#endif
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
using LibHac.Common.FixedArrays;
|
||||||
using LibHac.Common.Keys;
|
using LibHac.Common.Keys;
|
||||||
using LibHac.Crypto;
|
using LibHac.Crypto;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
|
@ -41,7 +42,7 @@ public class Package2StorageReader : IDisposable
|
||||||
Result rc = storage.Get.Read(0, SpanHelpers.AsByteSpan(ref _header));
|
Result rc = storage.Get.Read(0, SpanHelpers.AsByteSpan(ref _header));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
_key = keySet.Package2Keys[_header.Meta.KeyGeneration];
|
_key = keySet.Package2Keys[_header.Meta.GetKeyGeneration()];
|
||||||
DecryptHeader(_key, ref _header.Meta, ref _header.Meta);
|
DecryptHeader(_key, ref _header.Meta, ref _header.Meta);
|
||||||
|
|
||||||
_storage.SetByCopy(in storage);
|
_storage.SetByCopy(in storage);
|
||||||
|
@ -54,7 +55,7 @@ public class Package2StorageReader : IDisposable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="outPayloadStorage">If the method returns successfully, contains an <see cref="IStorage"/>
|
/// <param name="outPayloadStorage">If the method returns successfully, contains an <see cref="IStorage"/>
|
||||||
/// of the specified payload.</param>
|
/// of the specified payload.</param>
|
||||||
/// <param name="index">The index of the payload to get. Must me less than <see cref="Package2Header.PayloadCount"/></param>
|
/// <param name="index">The index of the payload to get. Must be less than <see cref="Package2Header.PayloadCount"/></param>
|
||||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
public Result OpenPayload(ref UniqueRef<IStorage> outPayloadStorage, int index)
|
public Result OpenPayload(ref UniqueRef<IStorage> outPayloadStorage, int index)
|
||||||
{
|
{
|
||||||
|
@ -72,7 +73,7 @@ public class Package2StorageReader : IDisposable
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] iv = _header.Meta.PayloadIvs[index].Bytes.ToArray();
|
byte[] iv = _header.Meta.PayloadIvs[index].ItemsRo.ToArray();
|
||||||
outPayloadStorage.Reset(new CachedStorage(new Aes128CtrStorage(payloadSubStorage, _key.DataRo.ToArray(), iv, true), 0x4000, 1, true));
|
outPayloadStorage.Reset(new CachedStorage(new Aes128CtrStorage(payloadSubStorage, _key.DataRo.ToArray(), iv, true), 0x4000, 1, true));
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
@ -219,7 +220,7 @@ public class Package2StorageReader : IDisposable
|
||||||
var storages = new List<IStorage>(4);
|
var storages = new List<IStorage>(4);
|
||||||
|
|
||||||
// The signature and IV are unencrypted
|
// The signature and IV are unencrypted
|
||||||
int unencryptedHeaderSize = Package2Header.SignatureSize + Unsafe.SizeOf<Buffer16>();
|
int unencryptedHeaderSize = Package2Header.SignatureSize + Unsafe.SizeOf<Array16<byte>>();
|
||||||
int encryptedHeaderSize = Unsafe.SizeOf<Package2Header>() - unencryptedHeaderSize;
|
int encryptedHeaderSize = Unsafe.SizeOf<Package2Header>() - unencryptedHeaderSize;
|
||||||
|
|
||||||
// Get signature and IV
|
// Get signature and IV
|
||||||
|
@ -230,7 +231,7 @@ public class Package2StorageReader : IDisposable
|
||||||
|
|
||||||
// The counter starts counting at the beginning of the meta struct, but the first block in
|
// The counter starts counting at the beginning of the meta struct, but the first block in
|
||||||
// the struct isn't encrypted. Increase the counter by one to skip that block.
|
// the struct isn't encrypted. Increase the counter by one to skip that block.
|
||||||
byte[] iv = _header.Meta.HeaderIv.Bytes.ToArray();
|
byte[] iv = _header.Meta.HeaderIv.ItemsRo.ToArray();
|
||||||
Utilities.IncrementByteArray(iv);
|
Utilities.IncrementByteArray(iv);
|
||||||
|
|
||||||
storages.Add(new CachedStorage(new Aes128CtrStorage(encMetaStorage, _key.DataRo.ToArray(), iv, true), 0x100, 1, true));
|
storages.Add(new CachedStorage(new Aes128CtrStorage(encMetaStorage, _key.DataRo.ToArray(), iv, true), 0x100, 1, true));
|
||||||
|
@ -254,12 +255,12 @@ public class Package2StorageReader : IDisposable
|
||||||
|
|
||||||
private void DecryptHeader(ReadOnlySpan<byte> key, ref Package2Meta source, ref Package2Meta dest)
|
private void DecryptHeader(ReadOnlySpan<byte> key, ref Package2Meta source, ref Package2Meta dest)
|
||||||
{
|
{
|
||||||
Buffer16 iv = source.HeaderIv;
|
Array16<byte> iv = source.HeaderIv;
|
||||||
|
|
||||||
Aes.DecryptCtr128(SpanHelpers.AsByteSpan(ref source), SpanHelpers.AsByteSpan(ref dest), key, iv);
|
Aes.DecryptCtr128(SpanHelpers.AsByteSpan(ref source), SpanHelpers.AsByteSpan(ref dest), key, iv);
|
||||||
|
|
||||||
// Copy the IV to the output because the IV field will be garbage after "decrypting" it
|
// Copy the IV to the output because the IV field will be garbage after "decrypting" it
|
||||||
Unsafe.As<Package2Meta, Buffer16>(ref dest) = iv;
|
dest.HeaderIv = iv;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HasIniPayload()
|
private bool HasIniPayload()
|
||||||
|
|
31
src/LibHac/Common/FixedArrays/Array112.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array112.cs
Normal 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 Array112<T>
|
||||||
|
{
|
||||||
|
public const int Length = 112;
|
||||||
|
|
||||||
|
private Array80<T> _0;
|
||||||
|
private Array32<T> _80;
|
||||||
|
|
||||||
|
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 Array112<T> value) => value.ItemsRo;
|
||||||
|
}
|
31
src/LibHac/Common/FixedArrays/Array14.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array14.cs
Normal 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 Array14<T>
|
||||||
|
{
|
||||||
|
public const int Length = 14;
|
||||||
|
|
||||||
|
private Array8<T> _0;
|
||||||
|
private Array6<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 Array14<T> value) => value.ItemsRo;
|
||||||
|
}
|
31
src/LibHac/Common/FixedArrays/Array144.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array144.cs
Normal 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 Array144<T>
|
||||||
|
{
|
||||||
|
public const int Length = 144;
|
||||||
|
|
||||||
|
private Array128<T> _0;
|
||||||
|
private Array16<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 Array144<T> value) => value.ItemsRo;
|
||||||
|
}
|
31
src/LibHac/Common/FixedArrays/Array15.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array15.cs
Normal 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 Array15<T>
|
||||||
|
{
|
||||||
|
public const int Length = 15;
|
||||||
|
|
||||||
|
private Array8<T> _0;
|
||||||
|
private Array7<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 Array15<T> value) => value.ItemsRo;
|
||||||
|
}
|
31
src/LibHac/Common/FixedArrays/Array18.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array18.cs
Normal 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 Array18<T>
|
||||||
|
{
|
||||||
|
public const int Length = 18;
|
||||||
|
|
||||||
|
private Array16<T> _0;
|
||||||
|
private Array2<T> _16;
|
||||||
|
|
||||||
|
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 Array18<T> value) => value.ItemsRo;
|
||||||
|
}
|
31
src/LibHac/Common/FixedArrays/Array56.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array56.cs
Normal 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 Array56<T>
|
||||||
|
{
|
||||||
|
public const int Length = 56;
|
||||||
|
|
||||||
|
private Array32<T> _0;
|
||||||
|
private Array24<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 Array56<T> value) => value.ItemsRo;
|
||||||
|
}
|
26
src/LibHac/Common/FixedArrays/Array7.cs
Normal file
26
src/LibHac/Common/FixedArrays/Array7.cs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma warning disable CS0169, IDE0051 // Remove unused private members
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace LibHac.Common.FixedArrays;
|
||||||
|
|
||||||
|
public struct Array7<T>
|
||||||
|
{
|
||||||
|
public const int Length = 7;
|
||||||
|
|
||||||
|
private T _1;
|
||||||
|
private T _2;
|
||||||
|
private T _3;
|
||||||
|
private T _4;
|
||||||
|
private T _5;
|
||||||
|
private T _6;
|
||||||
|
private T _7;
|
||||||
|
|
||||||
|
public ref T this[int i] => ref Items[i];
|
||||||
|
|
||||||
|
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 Array7<T> value) => value.ItemsRo;
|
||||||
|
}
|
31
src/LibHac/Common/FixedArrays/Array80.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array80.cs
Normal 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 Array80<T>
|
||||||
|
{
|
||||||
|
public const int Length = 80;
|
||||||
|
|
||||||
|
private Array64<T> _0;
|
||||||
|
private Array16<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 Array80<T> value) => value.ItemsRo;
|
||||||
|
}
|
|
@ -289,7 +289,8 @@ public static class Utilities
|
||||||
8 => "8.1.0-8.1.1",
|
8 => "8.1.0-8.1.1",
|
||||||
9 => "9.0.0-9.0.1",
|
9 => "9.0.0-9.0.1",
|
||||||
0xA => "9.1.0-12.0.3",
|
0xA => "9.1.0-12.0.3",
|
||||||
0xB => "12.1.0-",
|
0xB => "12.1.0",
|
||||||
|
0xC => "13.0.0-",
|
||||||
_ => "Unknown"
|
_ => "Unknown"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Buffers.Text;
|
using System.Buffers.Text;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Unicode;
|
using System.Text.Unicode;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
using LibHac.Common.FixedArrays;
|
||||||
using LibHac.Diag;
|
using LibHac.Diag;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.Fs.Impl;
|
using LibHac.Fs.Impl;
|
||||||
|
@ -34,13 +34,13 @@ namespace LibHac.Fs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
|
|
||||||
public struct ApplicationInfo
|
public struct ApplicationInfo
|
||||||
{
|
{
|
||||||
public Ncm.ApplicationId ApplicationId;
|
public Ncm.ApplicationId ApplicationId;
|
||||||
public uint Version;
|
public uint Version;
|
||||||
public byte LaunchType;
|
public byte LaunchType;
|
||||||
public bool IsMultiProgram;
|
public bool IsMultiProgram;
|
||||||
|
public Array18<byte> Reserved;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
|
|
|
@ -1,17 +1,11 @@
|
||||||
using System;
|
using LibHac.Common.FixedArrays;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using LibHac.Common;
|
|
||||||
|
|
||||||
namespace LibHac.Fs;
|
namespace LibHac.Fs;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x124)]
|
|
||||||
public struct CodeVerificationData
|
public struct CodeVerificationData
|
||||||
{
|
{
|
||||||
private const int Signature2Size = 0x100;
|
public Array256<byte> Signature;
|
||||||
|
public Array32<byte> Hash;
|
||||||
[FieldOffset(0x000)] private byte _signature2;
|
public bool HasData;
|
||||||
[FieldOffset(0x100)] public Buffer32 NcaHeaderHash;
|
public Array3<byte> Reserved;
|
||||||
[FieldOffset(0x120)] public bool IsValid;
|
}
|
||||||
|
|
||||||
public Span<byte> NcaSignature2 => SpanHelpers.CreateSpan(ref _signature2, Signature2Size);
|
|
||||||
}
|
|
|
@ -1,18 +1,15 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using LibHac.Common.FixedArrays;
|
||||||
using LibHac.Common;
|
|
||||||
|
|
||||||
namespace LibHac.Fs;
|
namespace LibHac.Fs;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit)]
|
|
||||||
public struct DirectoryEntry
|
public struct DirectoryEntry
|
||||||
{
|
{
|
||||||
[FieldOffset(0)] private byte _name;
|
public Array769<byte> Name;
|
||||||
[FieldOffset(0x301)] public NxFileAttributes Attributes;
|
public NxFileAttributes Attributes;
|
||||||
[FieldOffset(0x304)] public DirectoryEntryType Type;
|
public Array2<byte> Reserved302;
|
||||||
[FieldOffset(0x308)] public long Size;
|
public DirectoryEntryType Type;
|
||||||
|
public Array3<byte> Reserved305;
|
||||||
public Span<byte> Name => SpanHelpers.CreateSpan(ref _name, PathTool.EntryNameLengthMax + 1);
|
public long Size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum DirectoryEntryType : byte
|
public enum DirectoryEntryType : byte
|
||||||
|
|
|
@ -1,28 +1,13 @@
|
||||||
using System;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics;
|
using LibHac.Common.FixedArrays;
|
||||||
using System.Runtime.InteropServices;
|
using LibHac.Util;
|
||||||
using LibHac.Common;
|
|
||||||
|
|
||||||
namespace LibHac.Fs;
|
namespace LibHac.Fs;
|
||||||
|
|
||||||
[DebuggerDisplay("{ToString()}")]
|
[DebuggerDisplay("{ToString()}")]
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
public struct EncryptionSeed
|
||||||
public struct EncryptionSeed : IEquatable<EncryptionSeed>
|
|
||||||
{
|
{
|
||||||
private readonly Key128 Key;
|
public Array16<byte> Value;
|
||||||
|
|
||||||
public readonly ReadOnlySpan<byte> Value => SpanHelpers.AsReadOnlyByteSpan(in this);
|
public readonly override string ToString() => Value.ItemsRo.ToHexString();
|
||||||
|
}
|
||||||
public EncryptionSeed(ReadOnlySpan<byte> bytes)
|
|
||||||
{
|
|
||||||
Key = new Key128(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() => Key.ToString();
|
|
||||||
|
|
||||||
public override bool Equals(object obj) => obj is EncryptionSeed key && Equals(key);
|
|
||||||
public bool Equals(EncryptionSeed other) => Key.Equals(other.Key);
|
|
||||||
public override int GetHashCode() => Key.GetHashCode();
|
|
||||||
public static bool operator ==(EncryptionSeed left, EncryptionSeed right) => left.Equals(right);
|
|
||||||
public static bool operator !=(EncryptionSeed left, EncryptionSeed right) => !(left == right);
|
|
||||||
}
|
|
|
@ -1,23 +1,22 @@
|
||||||
using System.Runtime.InteropServices;
|
using LibHac.Common.FixedArrays;
|
||||||
using LibHac.Fat;
|
using LibHac.Fat;
|
||||||
|
|
||||||
namespace LibHac.Fs;
|
namespace LibHac.Fs;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x80)]
|
|
||||||
public struct FileSystemProxyErrorInfo
|
public struct FileSystemProxyErrorInfo
|
||||||
{
|
{
|
||||||
[FieldOffset(0x00)] public int RomFsRemountForDataCorruptionCount;
|
public int RemountForDataCorruptionCount;
|
||||||
[FieldOffset(0x04)] public int RomFsUnrecoverableDataCorruptionByRemountCount;
|
public int UnrecoverableDataCorruptionByRemountCount;
|
||||||
[FieldOffset(0x08)] public FatError FatError;
|
public FatError FatFsError;
|
||||||
[FieldOffset(0x28)] public int RomFsRecoveredByInvalidateCacheCount;
|
public int RecoveredByInvalidateCacheCount;
|
||||||
[FieldOffset(0x2C)] public int SaveDataIndexCount;
|
public int SaveDataIndexCount;
|
||||||
|
public Array80<byte> Reserved;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
|
|
||||||
public struct StorageErrorInfo
|
public struct StorageErrorInfo
|
||||||
{
|
{
|
||||||
[FieldOffset(0x00)] public int NumActivationFailures;
|
public int NumActivationFailures;
|
||||||
[FieldOffset(0x04)] public int NumActivationErrorCorrections;
|
public int NumActivationErrorCorrections;
|
||||||
[FieldOffset(0x08)] public int NumReadWriteFailures;
|
public int NumReadWriteFailures;
|
||||||
[FieldOffset(0x0C)] public int NumReadWriteErrorCorrections;
|
public int NumReadWriteErrorCorrections;
|
||||||
}
|
}
|
|
@ -1,12 +1,22 @@
|
||||||
using System.Runtime.InteropServices;
|
using LibHac.Common.FixedArrays;
|
||||||
|
using LibHac.Time;
|
||||||
|
|
||||||
namespace LibHac.Fs;
|
namespace LibHac.Fs;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
|
public struct FileTimeStamp
|
||||||
|
{
|
||||||
|
public PosixTime Created;
|
||||||
|
public PosixTime Accessed;
|
||||||
|
public PosixTime Modified;
|
||||||
|
public bool IsLocalTime;
|
||||||
|
public Array7<byte> Reserved;
|
||||||
|
}
|
||||||
|
|
||||||
public struct FileTimeStampRaw
|
public struct FileTimeStampRaw
|
||||||
{
|
{
|
||||||
public long Created;
|
public long Created;
|
||||||
public long Accessed;
|
public long Accessed;
|
||||||
public long Modified;
|
public long Modified;
|
||||||
public bool IsLocalTime;
|
public bool IsLocalTime;
|
||||||
}
|
public Array7<byte> Reserved;
|
||||||
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
using System.Runtime.InteropServices;
|
using LibHac.Common.FixedArrays;
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
|
|
||||||
namespace LibHac.Fs;
|
namespace LibHac.Fs;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x20)]
|
|
||||||
public struct ProgramIndexMapInfo
|
public struct ProgramIndexMapInfo
|
||||||
{
|
{
|
||||||
[FieldOffset(0x00)] public ProgramId ProgramId;
|
public ProgramId ProgramId;
|
||||||
[FieldOffset(0x08)] public ProgramId MainProgramId;
|
public ProgramId MainProgramId;
|
||||||
[FieldOffset(0x10)] public byte ProgramIndex;
|
public byte ProgramIndex;
|
||||||
}
|
public Array15<byte> Reserved;
|
||||||
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using LibHac.Common.FixedArrays;
|
||||||
|
|
||||||
namespace LibHac.Fs;
|
namespace LibHac.Fs;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x40)]
|
|
||||||
public struct QueryRangeInfo
|
public struct QueryRangeInfo
|
||||||
{
|
{
|
||||||
public int AesCtrKeyType;
|
public int AesCtrKeyType;
|
||||||
public int SpeedEmulationType;
|
public int SpeedEmulationType;
|
||||||
|
public Array56<byte> Reserved;
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
|
@ -27,4 +27,4 @@ public struct QueryRangeInfo
|
||||||
InternalKeyForHardwareAes = 1 << 1,
|
InternalKeyForHardwareAes = 1 << 1,
|
||||||
ExternalKeyForHardwareAes = 1 << 2
|
ExternalKeyForHardwareAes = 1 << 2
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,62 +1,56 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.Common;
|
using System.Runtime.Intrinsics;
|
||||||
|
using LibHac.Common.FixedArrays;
|
||||||
|
using LibHac.Diag;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Fs;
|
namespace LibHac.Fs;
|
||||||
|
|
||||||
[DebuggerDisplay("{DebugDisplay(),nq}")]
|
[DebuggerDisplay("{DebugDisplay(),nq}")]
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
public struct RightsId : IEquatable<RightsId>
|
||||||
public struct RightsId : IEquatable<RightsId>, IComparable<RightsId>, IComparable
|
|
||||||
{
|
{
|
||||||
public readonly Id128 Id;
|
public Array16<byte> Value;
|
||||||
|
|
||||||
public RightsId(ulong high, ulong low)
|
public RightsId(ReadOnlySpan<byte> value)
|
||||||
{
|
{
|
||||||
Id = new Id128(high, low);
|
Assert.Equal(0x10, value.Length);
|
||||||
|
|
||||||
|
Unsafe.SkipInit(out Value);
|
||||||
|
|
||||||
|
Span<ulong> longsThis = MemoryMarshal.Cast<byte, ulong>(Value.Items);
|
||||||
|
ReadOnlySpan<ulong> longsValue = MemoryMarshal.Cast<byte, ulong>(value);
|
||||||
|
|
||||||
|
longsThis[1] = longsValue[1];
|
||||||
|
longsThis[0] = longsValue[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public RightsId(ReadOnlySpan<byte> uid)
|
public readonly override string ToString() => Value.ItemsRo.ToHexString();
|
||||||
{
|
|
||||||
Id = new Id128(uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() => Id.ToString();
|
public readonly string DebugDisplay()
|
||||||
|
|
||||||
public string DebugDisplay()
|
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> highBytes = AsBytes().Slice(0, 8);
|
ReadOnlySpan<byte> highBytes = Value.ItemsRo.Slice(0, 8);
|
||||||
ReadOnlySpan<byte> lowBytes = AsBytes().Slice(8, 8);
|
ReadOnlySpan<byte> lowBytes = Value.ItemsRo.Slice(8, 8);
|
||||||
|
|
||||||
return $"{highBytes.ToHexString()} {lowBytes.ToHexString()}";
|
return $"{highBytes.ToHexString()} {lowBytes.ToHexString()}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(RightsId other) => Id == other.Id;
|
public readonly bool Equals(RightsId other)
|
||||||
public override bool Equals(object obj) => obj is RightsId other && Equals(other);
|
|
||||||
|
|
||||||
public override int GetHashCode() => Id.GetHashCode();
|
|
||||||
|
|
||||||
public int CompareTo(RightsId other) => Id.CompareTo(other.Id);
|
|
||||||
|
|
||||||
public int CompareTo(object obj)
|
|
||||||
{
|
{
|
||||||
if (obj is null) return 1;
|
return Unsafe.As<Array16<byte>, Vector128<byte>>(ref Unsafe.AsRef(in Value))
|
||||||
return obj is RightsId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(RightsId)}");
|
.Equals(Unsafe.As<Array16<byte>, Vector128<byte>>(ref other.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ToBytes(Span<byte> output) => Id.ToBytes(output);
|
public readonly override bool Equals(object obj) => obj is RightsId other && Equals(other);
|
||||||
|
|
||||||
public ReadOnlySpan<byte> AsBytes()
|
public readonly override int GetHashCode()
|
||||||
{
|
{
|
||||||
return SpanHelpers.AsByteSpan(ref this);
|
ReadOnlySpan<ulong> longSpan = MemoryMarshal.Cast<byte, ulong>(Value.ItemsRo);
|
||||||
|
return HashCode.Combine(longSpan[0], longSpan[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator ==(RightsId left, RightsId right) => left.Equals(right);
|
public static bool operator ==(RightsId left, RightsId right) => left.Equals(right);
|
||||||
public static bool operator !=(RightsId left, RightsId right) => !left.Equals(right);
|
public static bool operator !=(RightsId left, RightsId right) => !left.Equals(right);
|
||||||
|
}
|
||||||
public static bool operator <(RightsId left, RightsId right) => left.CompareTo(right) < 0;
|
|
||||||
public static bool operator >(RightsId left, RightsId right) => left.CompareTo(right) > 0;
|
|
||||||
public static bool operator <=(RightsId left, RightsId right) => left.CompareTo(right) <= 0;
|
|
||||||
public static bool operator >=(RightsId left, RightsId right) => left.CompareTo(right) >= 0;
|
|
||||||
}
|
|
|
@ -86,7 +86,7 @@ public class NcaFileSystemServiceImpl
|
||||||
UnsafeHelpers.SkipParamInit(out verificationData);
|
UnsafeHelpers.SkipParamInit(out verificationData);
|
||||||
|
|
||||||
if (!Unsafe.IsNullRef(ref verificationData))
|
if (!Unsafe.IsNullRef(ref verificationData))
|
||||||
verificationData.IsValid = false;
|
verificationData.HasData = false;
|
||||||
|
|
||||||
// Get a reference to the path that will be advanced as each part of the path is parsed
|
// Get a reference to the path that will be advanced as each part of the path is parsed
|
||||||
var currentPath = new U8Span(path.GetString());
|
var currentPath = new U8Span(path.GetString());
|
||||||
|
@ -691,9 +691,9 @@ public class NcaFileSystemServiceImpl
|
||||||
private Result SetExternalKeyForRightsId(Nca nca)
|
private Result SetExternalKeyForRightsId(Nca nca)
|
||||||
{
|
{
|
||||||
var rightsId = new RightsId(nca.Header.RightsId);
|
var rightsId = new RightsId(nca.Header.RightsId);
|
||||||
var zero = new RightsId(0, 0);
|
var zero = new RightsId();
|
||||||
|
|
||||||
if (Crypto.CryptoUtil.IsSameBytes(rightsId.AsBytes(), zero.AsBytes(), Unsafe.SizeOf<RightsId>()))
|
if (Crypto.CryptoUtil.IsSameBytes(rightsId.Value, zero.Value, Unsafe.SizeOf<RightsId>()))
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
|
||||||
// ReSharper disable once UnusedVariable
|
// ReSharper disable once UnusedVariable
|
||||||
|
|
|
@ -62,9 +62,9 @@ public class StatusReportServiceImpl
|
||||||
{
|
{
|
||||||
errorInfo = new FileSystemProxyErrorInfo();
|
errorInfo = new FileSystemProxyErrorInfo();
|
||||||
|
|
||||||
_config.NcaFsServiceImpl.GetAndClearRomFsErrorInfo(out errorInfo.RomFsRemountForDataCorruptionCount,
|
_config.NcaFsServiceImpl.GetAndClearRomFsErrorInfo(out errorInfo.RemountForDataCorruptionCount,
|
||||||
out errorInfo.RomFsUnrecoverableDataCorruptionByRemountCount,
|
out errorInfo.UnrecoverableDataCorruptionByRemountCount,
|
||||||
out errorInfo.RomFsRecoveredByInvalidateCacheCount);
|
out errorInfo.RecoveredByInvalidateCacheCount);
|
||||||
|
|
||||||
// Missing: GetFatInfo
|
// Missing: GetFatInfo
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ public class LocalDirectory : IDirectory
|
||||||
DirectoryEntryType type = isDir ? DirectoryEntryType.Directory : DirectoryEntryType.File;
|
DirectoryEntryType type = isDir ? DirectoryEntryType.Directory : DirectoryEntryType.File;
|
||||||
long length = isDir ? 0 : ((FileInfo)localEntry).Length;
|
long length = isDir ? 0 : ((FileInfo)localEntry).Length;
|
||||||
|
|
||||||
StringUtils.Copy(entryBuffer[i].Name, name);
|
StringUtils.Copy(entryBuffer[i].Name.Items, name);
|
||||||
entryBuffer[i].Name[PathTool.EntryNameLengthMax] = 0;
|
entryBuffer[i].Name[PathTool.EntryNameLengthMax] = 0;
|
||||||
|
|
||||||
entryBuffer[i].Attributes = localEntry.Attributes.ToNxAttributes();
|
entryBuffer[i].Attributes = localEntry.Attributes.ToNxAttributes();
|
||||||
|
|
|
@ -47,7 +47,7 @@ public class PartitionDirectory : IDirectory
|
||||||
entry.Type = DirectoryEntryType.File;
|
entry.Type = DirectoryEntryType.File;
|
||||||
entry.Size = fileEntry.Size;
|
entry.Size = fileEntry.Size;
|
||||||
|
|
||||||
StringUtils.Copy(entry.Name, nameUtf8);
|
StringUtils.Copy(entry.Name.Items, nameUtf8);
|
||||||
entry.Name[PathTool.EntryNameLengthMax] = 0;
|
entry.Name[PathTool.EntryNameLengthMax] = 0;
|
||||||
|
|
||||||
CurrentIndex++;
|
CurrentIndex++;
|
||||||
|
|
|
@ -358,7 +358,7 @@ public class PartitionFileSystemCore<T> : IFileSystem where T : unmanaged, IPart
|
||||||
entryBuffer[i].Size = ParentFs._metaData.GetEntry(CurrentIndex).Size;
|
entryBuffer[i].Size = ParentFs._metaData.GetEntry(CurrentIndex).Size;
|
||||||
|
|
||||||
U8Span name = ParentFs._metaData.GetName(CurrentIndex);
|
U8Span name = ParentFs._metaData.GetName(CurrentIndex);
|
||||||
StringUtils.Copy(entryBuffer[i].Name, name);
|
StringUtils.Copy(entryBuffer[i].Name.Items, name);
|
||||||
entryBuffer[i].Name[FsPath.MaxLength] = 0;
|
entryBuffer[i].Name[FsPath.MaxLength] = 0;
|
||||||
|
|
||||||
CurrentIndex++;
|
CurrentIndex++;
|
||||||
|
@ -388,4 +388,4 @@ public class PartitionFileSystemCore<T> : IFileSystem where T : unmanaged, IPart
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -252,7 +252,7 @@ public class InMemoryFileSystem : IAttributeFileSystem
|
||||||
{
|
{
|
||||||
ref DirectoryEntry entry = ref entryBuffer[i];
|
ref DirectoryEntry entry = ref entryBuffer[i];
|
||||||
|
|
||||||
StringUtils.Copy(entry.Name, CurrentDir.Name);
|
StringUtils.Copy(entry.Name.Items, CurrentDir.Name);
|
||||||
entry.Name[PathTool.EntryNameLengthMax] = 0;
|
entry.Name[PathTool.EntryNameLengthMax] = 0;
|
||||||
|
|
||||||
entry.Type = DirectoryEntryType.Directory;
|
entry.Type = DirectoryEntryType.Directory;
|
||||||
|
@ -270,7 +270,7 @@ public class InMemoryFileSystem : IAttributeFileSystem
|
||||||
{
|
{
|
||||||
ref DirectoryEntry entry = ref entryBuffer[i];
|
ref DirectoryEntry entry = ref entryBuffer[i];
|
||||||
|
|
||||||
StringUtils.Copy(entry.Name, CurrentFile.Name);
|
StringUtils.Copy(entry.Name.Items, CurrentFile.Name);
|
||||||
entry.Name[PathTool.EntryNameLengthMax] = 0;
|
entry.Name[PathTool.EntryNameLengthMax] = 0;
|
||||||
|
|
||||||
entry.Type = DirectoryEntryType.File;
|
entry.Type = DirectoryEntryType.File;
|
||||||
|
|
|
@ -50,7 +50,7 @@ public class RomFsDirectory : IDirectory
|
||||||
ref DirectoryEntry entry = ref entryBuffer[i];
|
ref DirectoryEntry entry = ref entryBuffer[i];
|
||||||
Span<byte> nameUtf8 = Encoding.UTF8.GetBytes(name);
|
Span<byte> nameUtf8 = Encoding.UTF8.GetBytes(name);
|
||||||
|
|
||||||
StringUtils.Copy(entry.Name, nameUtf8);
|
StringUtils.Copy(entry.Name.Items, nameUtf8);
|
||||||
entry.Name[PathTool.EntryNameLengthMax] = 0;
|
entry.Name[PathTool.EntryNameLengthMax] = 0;
|
||||||
|
|
||||||
entry.Type = DirectoryEntryType.Directory;
|
entry.Type = DirectoryEntryType.Directory;
|
||||||
|
@ -70,7 +70,7 @@ public class RomFsDirectory : IDirectory
|
||||||
ref DirectoryEntry entry = ref entryBuffer[i];
|
ref DirectoryEntry entry = ref entryBuffer[i];
|
||||||
Span<byte> nameUtf8 = Encoding.UTF8.GetBytes(name);
|
Span<byte> nameUtf8 = Encoding.UTF8.GetBytes(name);
|
||||||
|
|
||||||
StringUtils.Copy(entry.Name, nameUtf8);
|
StringUtils.Copy(entry.Name.Items, nameUtf8);
|
||||||
entry.Name[PathTool.EntryNameLengthMax] = 0;
|
entry.Name[PathTool.EntryNameLengthMax] = 0;
|
||||||
|
|
||||||
entry.Type = DirectoryEntryType.File;
|
entry.Type = DirectoryEntryType.File;
|
||||||
|
|
|
@ -50,7 +50,7 @@ public class SaveDataDirectory : IDirectory
|
||||||
ref DirectoryEntry entry = ref entryBuffer[i];
|
ref DirectoryEntry entry = ref entryBuffer[i];
|
||||||
Span<byte> nameUtf8 = Encoding.UTF8.GetBytes(name);
|
Span<byte> nameUtf8 = Encoding.UTF8.GetBytes(name);
|
||||||
|
|
||||||
StringUtils.Copy(entry.Name, nameUtf8);
|
StringUtils.Copy(entry.Name.Items, nameUtf8);
|
||||||
entry.Name[64] = 0;
|
entry.Name[64] = 0;
|
||||||
|
|
||||||
entry.Type = DirectoryEntryType.Directory;
|
entry.Type = DirectoryEntryType.Directory;
|
||||||
|
@ -70,7 +70,7 @@ public class SaveDataDirectory : IDirectory
|
||||||
ref DirectoryEntry entry = ref entryBuffer[i];
|
ref DirectoryEntry entry = ref entryBuffer[i];
|
||||||
Span<byte> nameUtf8 = Encoding.UTF8.GetBytes(name);
|
Span<byte> nameUtf8 = Encoding.UTF8.GetBytes(name);
|
||||||
|
|
||||||
StringUtils.Copy(entry.Name, nameUtf8);
|
StringUtils.Copy(entry.Name.Items, nameUtf8);
|
||||||
entry.Name[64] = 0;
|
entry.Name[64] = 0;
|
||||||
|
|
||||||
entry.Type = DirectoryEntryType.File;
|
entry.Type = DirectoryEntryType.File;
|
||||||
|
|
|
@ -64,9 +64,9 @@ internal static class ProcessPackage
|
||||||
if (package1.IsMariko)
|
if (package1.IsMariko)
|
||||||
{
|
{
|
||||||
sb.AppendLine("Mariko OEM Header:");
|
sb.AppendLine("Mariko OEM Header:");
|
||||||
PrintItem(sb, colLen, " Signature:", package1.MarikoOemHeader.RsaSig.ToArray());
|
PrintItem(sb, colLen, " Signature:", package1.MarikoOemHeader.RsaSig.ItemsRo.ToArray());
|
||||||
PrintItem(sb, colLen, " Random Salt:", package1.MarikoOemHeader.Salt.ToArray());
|
PrintItem(sb, colLen, " Random Salt:", package1.MarikoOemHeader.Salt.ItemsRo.ToArray());
|
||||||
PrintItem(sb, colLen, " OEM Bootloader Hash:", package1.MarikoOemHeader.Hash.ToArray());
|
PrintItem(sb, colLen, " OEM Bootloader Hash:", package1.MarikoOemHeader.Hash.ItemsRo.ToArray());
|
||||||
PrintItem(sb, colLen, " OEM Bootloader Version:", $"{package1.MarikoOemHeader.Version:x2}");
|
PrintItem(sb, colLen, " OEM Bootloader Version:", $"{package1.MarikoOemHeader.Version:x2}");
|
||||||
PrintItem(sb, colLen, " OEM Bootloader Size:", $"{package1.MarikoOemHeader.Size:x8}");
|
PrintItem(sb, colLen, " OEM Bootloader Size:", $"{package1.MarikoOemHeader.Size:x8}");
|
||||||
PrintItem(sb, colLen, " OEM Bootloader Load Address:", $"{package1.MarikoOemHeader.LoadAddress:x8}");
|
PrintItem(sb, colLen, " OEM Bootloader Load Address:", $"{package1.MarikoOemHeader.LoadAddress:x8}");
|
||||||
|
@ -82,7 +82,7 @@ internal static class ProcessPackage
|
||||||
|
|
||||||
if (!package1.IsMariko && package1.IsModern)
|
if (!package1.IsMariko && package1.IsModern)
|
||||||
{
|
{
|
||||||
PrintItem(sb, colLen, " PK11 MAC:", package1.Pk11Mac);
|
PrintItem(sb, colLen, " PK11 MAC:", package1.Pk11Mac.ItemsRo.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (package1.IsDecrypted)
|
if (package1.IsDecrypted)
|
||||||
|
@ -162,16 +162,16 @@ internal static class ProcessPackage
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
|
|
||||||
sb.AppendLine("PK21:");
|
sb.AppendLine("PK21:");
|
||||||
PrintItem(sb, colLen, $"Signature{signatureValidity.GetValidityString()}:", package2.Header.Signature.ToArray());
|
PrintItem(sb, colLen, $"Signature{signatureValidity.GetValidityString()}:", package2.Header.Signature.ItemsRo.ToArray());
|
||||||
PrintItem(sb, colLen, "Header Version:", $"{package2.Header.Meta.KeyGeneration:x2}");
|
PrintItem(sb, colLen, "Header Version:", $"{package2.Header.Meta.GetKeyGeneration():x2}");
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
string name = package2.Header.Meta.PayloadSizes[i] != 0 ? Package2SectionNames[i] : "Empty";
|
string name = package2.Header.Meta.PayloadSizes[i] != 0 ? Package2SectionNames[i] : "Empty";
|
||||||
sb.AppendLine($"Section {i} ({name}):");
|
sb.AppendLine($"Section {i} ({name}):");
|
||||||
|
|
||||||
PrintItem(sb, colLen, " Hash:", package2.Header.Meta.PayloadHashes[i]);
|
PrintItem(sb, colLen, " Hash:", package2.Header.Meta.PayloadHashes[i].ItemsRo.ToArray());
|
||||||
PrintItem(sb, colLen, " CTR:", package2.Header.Meta.PayloadIvs[i]);
|
PrintItem(sb, colLen, " CTR:", package2.Header.Meta.PayloadIvs[i].ItemsRo.ToArray());
|
||||||
PrintItem(sb, colLen, " Load Address:", $"{package2.Header.Meta.PayloadOffsets[i] + 0x80000000:x8}");
|
PrintItem(sb, colLen, " Load Address:", $"{package2.Header.Meta.PayloadOffsets[i] + 0x80000000:x8}");
|
||||||
PrintItem(sb, colLen, " Size:", $"{package2.Header.Meta.PayloadSizes[i]:x8}");
|
PrintItem(sb, colLen, " Size:", $"{package2.Header.Meta.PayloadSizes[i]:x8}");
|
||||||
}
|
}
|
||||||
|
|
132
tests/LibHac.Tests/Boot/TypeLayoutTests.cs
Normal file
132
tests/LibHac.Tests/Boot/TypeLayoutTests.cs
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using LibHac.Boot;
|
||||||
|
using Xunit;
|
||||||
|
using static LibHac.Tests.Common.Layout;
|
||||||
|
|
||||||
|
namespace LibHac.Tests.Boot;
|
||||||
|
|
||||||
|
public class TypeLayoutTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public static void EncryptedKeyBlob_Layout()
|
||||||
|
{
|
||||||
|
var s = new EncryptedKeyBlob();
|
||||||
|
|
||||||
|
Assert.Equal(0xB0, Unsafe.SizeOf<EncryptedKeyBlob>());
|
||||||
|
|
||||||
|
Assert.Equal(0x00, GetOffset(in s, in s.Cmac));
|
||||||
|
Assert.Equal(0x10, GetOffset(in s, in s.Counter));
|
||||||
|
Assert.Equal(0x20, GetOffset(in s, in s.Payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void KeyBlob_Layout()
|
||||||
|
{
|
||||||
|
var s = new KeyBlob();
|
||||||
|
|
||||||
|
Assert.Equal(0x90, Unsafe.SizeOf<KeyBlob>());
|
||||||
|
|
||||||
|
Assert.Equal(0x00, GetOffset(in s, in s.MasterKek));
|
||||||
|
Assert.Equal(0x10, GetOffset(in s, in s.Unused));
|
||||||
|
Assert.Equal(0x80, GetOffset(in s, in s.Package1Key));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void Package1MarikoOemHeader_Layout()
|
||||||
|
{
|
||||||
|
var s = new Package1MarikoOemHeader();
|
||||||
|
|
||||||
|
Assert.Equal(0x170, Unsafe.SizeOf<Package1MarikoOemHeader>());
|
||||||
|
|
||||||
|
Assert.Equal(0x000, GetOffset(in s, in s.AesMac));
|
||||||
|
Assert.Equal(0x010, GetOffset(in s, in s.RsaSig));
|
||||||
|
Assert.Equal(0x110, GetOffset(in s, in s.Salt));
|
||||||
|
Assert.Equal(0x130, GetOffset(in s, in s.Hash));
|
||||||
|
Assert.Equal(0x150, GetOffset(in s, in s.Version));
|
||||||
|
Assert.Equal(0x154, GetOffset(in s, in s.Size));
|
||||||
|
Assert.Equal(0x158, GetOffset(in s, in s.LoadAddress));
|
||||||
|
Assert.Equal(0x15C, GetOffset(in s, in s.EntryPoint));
|
||||||
|
Assert.Equal(0x160, GetOffset(in s, in s.Reserved));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void Package1MetaData_Layout()
|
||||||
|
{
|
||||||
|
var s = new Package1MetaData();
|
||||||
|
|
||||||
|
Assert.Equal(0x20, Unsafe.SizeOf<Package1MetaData>());
|
||||||
|
|
||||||
|
Assert.Equal(0x00, GetOffset(in s, in s.LoaderHash));
|
||||||
|
Assert.Equal(0x04, GetOffset(in s, in s.SecureMonitorHash));
|
||||||
|
Assert.Equal(0x08, GetOffset(in s, in s.BootloaderHash));
|
||||||
|
Assert.Equal(0x0C, GetOffset(in s, in s.Reserved));
|
||||||
|
Assert.Equal(0x10, GetOffset(in s, in s.BuildDate.Value[0]));
|
||||||
|
Assert.Equal(0x1E, GetOffset(in s, in s.KeyGeneration));
|
||||||
|
Assert.Equal(0x1F, GetOffset(in s, in s.Version));
|
||||||
|
|
||||||
|
Assert.Equal(0x10, GetOffset(in s, in s.Iv[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void Package1Stage1Footer_Layout()
|
||||||
|
{
|
||||||
|
var s = new Package1Stage1Footer();
|
||||||
|
|
||||||
|
Assert.Equal(0x20, Unsafe.SizeOf<Package1Stage1Footer>());
|
||||||
|
|
||||||
|
Assert.Equal(0x00, GetOffset(in s, in s.Pk11Size));
|
||||||
|
Assert.Equal(0x04, GetOffset(in s, in s.Reserved));
|
||||||
|
Assert.Equal(0x10, GetOffset(in s, in s.Iv));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void Package1Pk11Header_Layout()
|
||||||
|
{
|
||||||
|
var s = new Package1Pk11Header();
|
||||||
|
|
||||||
|
Assert.Equal(0x20, Unsafe.SizeOf<Package1Pk11Header>());
|
||||||
|
|
||||||
|
Assert.Equal(0x00, GetOffset(in s, in s.Magic));
|
||||||
|
Assert.Equal(0x04, GetOffset(in s, in s.WarmBootSize));
|
||||||
|
Assert.Equal(0x08, GetOffset(in s, in s.WarmBootOffset));
|
||||||
|
Assert.Equal(0x0C, GetOffset(in s, in s.Reserved));
|
||||||
|
Assert.Equal(0x10, GetOffset(in s, in s.BootloaderSize));
|
||||||
|
Assert.Equal(0x14, GetOffset(in s, in s.BootloaderOffset));
|
||||||
|
Assert.Equal(0x18, GetOffset(in s, in s.SecureMonitorSize));
|
||||||
|
Assert.Equal(0x1C, GetOffset(in s, in s.SecureMonitorOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void Package2Header_Layout()
|
||||||
|
{
|
||||||
|
var s = new Package2Header();
|
||||||
|
|
||||||
|
Assert.Equal(0x200, Unsafe.SizeOf<Package2Header>());
|
||||||
|
|
||||||
|
Assert.Equal(0x000, GetOffset(in s, in s.Signature));
|
||||||
|
Assert.Equal(0x100, GetOffset(in s, in s.Meta));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void Package2Meta_Layout()
|
||||||
|
{
|
||||||
|
var s = new Package2Meta();
|
||||||
|
|
||||||
|
Assert.Equal(0x100, Unsafe.SizeOf<Package2Meta>());
|
||||||
|
|
||||||
|
Assert.Equal(0x00, GetOffset(in s, in s.HeaderIv));
|
||||||
|
Assert.Equal(0x10, GetOffset(in s, in s.PayloadIvs));
|
||||||
|
Assert.Equal(0x40, GetOffset(in s, in s.Padding40));
|
||||||
|
Assert.Equal(0x50, GetOffset(in s, in s.Magic));
|
||||||
|
Assert.Equal(0x54, GetOffset(in s, in s.EntryPoint));
|
||||||
|
Assert.Equal(0x5C, GetOffset(in s, in s.Package2Version));
|
||||||
|
Assert.Equal(0x58, GetOffset(in s, in s.Padding58));
|
||||||
|
Assert.Equal(0x5D, GetOffset(in s, in s.BootloaderVersion));
|
||||||
|
Assert.Equal(0x60, GetOffset(in s, in s.PayloadSizes));
|
||||||
|
Assert.Equal(0x6C, GetOffset(in s, in s.Padding6C));
|
||||||
|
Assert.Equal(0x70, GetOffset(in s, in s.PayloadOffsets));
|
||||||
|
Assert.Equal(0x7C, GetOffset(in s, in s.Padding7C));
|
||||||
|
Assert.Equal(0x80, GetOffset(in s, in s.PayloadHashes));
|
||||||
|
Assert.Equal(0xE0, GetOffset(in s, in s.PaddingE0));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +0,0 @@
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using LibHac.Boot;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace LibHac.Tests.Boot;
|
|
||||||
|
|
||||||
public class TypeSizeTests
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public static void EncryptedKeyBlobSizeIs0xB0()
|
|
||||||
{
|
|
||||||
Assert.Equal(0xB0, Unsafe.SizeOf<EncryptedKeyBlob>());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public static void KeyBlobSizeIs0x90()
|
|
||||||
{
|
|
||||||
Assert.Equal(0x90, Unsafe.SizeOf<KeyBlob>());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -146,4 +146,147 @@ public class TypeLayoutTests
|
||||||
Assert.Equal(0x58, GetOffset(in s, in s.PooledBufferFailedIdealAllocationCountOnAsyncAccess));
|
Assert.Equal(0x58, GetOffset(in s, in s.PooledBufferFailedIdealAllocationCountOnAsyncAccess));
|
||||||
Assert.Equal(0x60, GetOffset(in s, in s.Reserved));
|
Assert.Equal(0x60, GetOffset(in s, in s.Reserved));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void ApplicationInfo_Layout()
|
||||||
|
{
|
||||||
|
var s = new ApplicationInfo();
|
||||||
|
|
||||||
|
Assert.Equal(0x20, Unsafe.SizeOf<ApplicationInfo>());
|
||||||
|
|
||||||
|
Assert.Equal(0x0, GetOffset(in s, in s.ApplicationId));
|
||||||
|
Assert.Equal(0x8, GetOffset(in s, in s.Version));
|
||||||
|
Assert.Equal(0xC, GetOffset(in s, in s.LaunchType));
|
||||||
|
Assert.Equal(0xD, GetOffset(in s, in s.IsMultiProgram));
|
||||||
|
Assert.Equal(0xE, GetOffset(in s, in s.Reserved));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void CodeVerificationData_Layout()
|
||||||
|
{
|
||||||
|
var s = new CodeVerificationData();
|
||||||
|
|
||||||
|
Assert.Equal(0x124, Unsafe.SizeOf<CodeVerificationData>());
|
||||||
|
|
||||||
|
Assert.Equal(0x000, GetOffset(in s, in s.Signature));
|
||||||
|
Assert.Equal(0x100, GetOffset(in s, in s.Hash));
|
||||||
|
Assert.Equal(0x120, GetOffset(in s, in s.HasData));
|
||||||
|
Assert.Equal(0x121, GetOffset(in s, in s.Reserved));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void DirectoryEntry_Layout()
|
||||||
|
{
|
||||||
|
var s = new DirectoryEntry();
|
||||||
|
|
||||||
|
Assert.Equal(0x310, Unsafe.SizeOf<DirectoryEntry>());
|
||||||
|
|
||||||
|
Assert.Equal(0x000, GetOffset(in s, in s.Name));
|
||||||
|
Assert.Equal(0x301, GetOffset(in s, in s.Attributes));
|
||||||
|
Assert.Equal(0x302, GetOffset(in s, in s.Reserved302));
|
||||||
|
Assert.Equal(0x304, GetOffset(in s, in s.Type));
|
||||||
|
Assert.Equal(0x305, GetOffset(in s, in s.Reserved305));
|
||||||
|
Assert.Equal(0x308, GetOffset(in s, in s.Size));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void EncryptionSeed_Layout()
|
||||||
|
{
|
||||||
|
var s = new EncryptionSeed();
|
||||||
|
|
||||||
|
Assert.Equal(0x10, Unsafe.SizeOf<EncryptionSeed>());
|
||||||
|
|
||||||
|
Assert.Equal(0, GetOffset(in s, in s.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void FileSystemProxyErrorInfo_Layout()
|
||||||
|
{
|
||||||
|
var s = new FileSystemProxyErrorInfo();
|
||||||
|
|
||||||
|
Assert.Equal(0x80, Unsafe.SizeOf<FileSystemProxyErrorInfo>());
|
||||||
|
|
||||||
|
Assert.Equal(0x00, GetOffset(in s, in s.RemountForDataCorruptionCount));
|
||||||
|
Assert.Equal(0x04, GetOffset(in s, in s.UnrecoverableDataCorruptionByRemountCount));
|
||||||
|
Assert.Equal(0x08, GetOffset(in s, in s.FatFsError));
|
||||||
|
Assert.Equal(0x28, GetOffset(in s, in s.RecoveredByInvalidateCacheCount));
|
||||||
|
Assert.Equal(0x2C, GetOffset(in s, in s.SaveDataIndexCount));
|
||||||
|
Assert.Equal(0x30, GetOffset(in s, in s.Reserved));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void StorageErrorInfo_Layout()
|
||||||
|
{
|
||||||
|
var s = new StorageErrorInfo();
|
||||||
|
|
||||||
|
Assert.Equal(0x10, Unsafe.SizeOf<StorageErrorInfo>());
|
||||||
|
|
||||||
|
Assert.Equal(0x0, GetOffset(in s, in s.NumActivationFailures));
|
||||||
|
Assert.Equal(0x4, GetOffset(in s, in s.NumActivationErrorCorrections));
|
||||||
|
Assert.Equal(0x8, GetOffset(in s, in s.NumReadWriteFailures));
|
||||||
|
Assert.Equal(0xC, GetOffset(in s, in s.NumReadWriteErrorCorrections));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void FileTimeStamp_Layout()
|
||||||
|
{
|
||||||
|
var s = new FileTimeStamp();
|
||||||
|
|
||||||
|
Assert.Equal(0x20, Unsafe.SizeOf<FileTimeStamp>());
|
||||||
|
|
||||||
|
Assert.Equal(0x00, GetOffset(in s, in s.Created));
|
||||||
|
Assert.Equal(0x08, GetOffset(in s, in s.Accessed));
|
||||||
|
Assert.Equal(0x10, GetOffset(in s, in s.Modified));
|
||||||
|
Assert.Equal(0x18, GetOffset(in s, in s.IsLocalTime));
|
||||||
|
Assert.Equal(0x19, GetOffset(in s, in s.Reserved));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void FileTimeStampRaw_Layout()
|
||||||
|
{
|
||||||
|
var s = new FileTimeStampRaw();
|
||||||
|
|
||||||
|
Assert.Equal(0x20, Unsafe.SizeOf<FileTimeStampRaw>());
|
||||||
|
|
||||||
|
Assert.Equal(0x00, GetOffset(in s, in s.Created));
|
||||||
|
Assert.Equal(0x08, GetOffset(in s, in s.Accessed));
|
||||||
|
Assert.Equal(0x10, GetOffset(in s, in s.Modified));
|
||||||
|
Assert.Equal(0x18, GetOffset(in s, in s.IsLocalTime));
|
||||||
|
Assert.Equal(0x19, GetOffset(in s, in s.Reserved));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void ProgramIndexMapInfo_Layout()
|
||||||
|
{
|
||||||
|
var s = new ProgramIndexMapInfo();
|
||||||
|
|
||||||
|
Assert.Equal(0x20, Unsafe.SizeOf<ProgramIndexMapInfo>());
|
||||||
|
|
||||||
|
Assert.Equal(0x00, GetOffset(in s, in s.ProgramId));
|
||||||
|
Assert.Equal(0x08, GetOffset(in s, in s.MainProgramId));
|
||||||
|
Assert.Equal(0x10, GetOffset(in s, in s.ProgramIndex));
|
||||||
|
Assert.Equal(0x11, GetOffset(in s, in s.Reserved));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void QueryRangeInfo_Layout()
|
||||||
|
{
|
||||||
|
var s = new QueryRangeInfo();
|
||||||
|
|
||||||
|
Assert.Equal(0x40, Unsafe.SizeOf<QueryRangeInfo>());
|
||||||
|
|
||||||
|
Assert.Equal(0x00, GetOffset(in s, in s.AesCtrKeyType));
|
||||||
|
Assert.Equal(0x04, GetOffset(in s, in s.SpeedEmulationType));
|
||||||
|
Assert.Equal(0x08, GetOffset(in s, in s.Reserved));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public static void RightsId_Layout()
|
||||||
|
{
|
||||||
|
var s = new RightsId();
|
||||||
|
|
||||||
|
Assert.Equal(0x10, Unsafe.SizeOf<RightsId>());
|
||||||
|
|
||||||
|
Assert.Equal(0x00, GetOffset(in s, in s.Value));
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue