diff --git a/src/LibHac/Boot/KeyBlob.cs b/src/LibHac/Boot/KeyBlob.cs index dffa95e9..517a38a0 100644 --- a/src/LibHac/Boot/KeyBlob.cs +++ b/src/LibHac/Boot/KeyBlob.cs @@ -1,42 +1,27 @@ using System; -using System.Diagnostics; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Crypto; using LibHac.Util; namespace LibHac.Boot; -[DebuggerDisplay("{ToString()}")] -[StructLayout(LayoutKind.Explicit, Size = 0xB0)] public struct EncryptedKeyBlob { -#if DEBUG - [FieldOffset(0x00)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy1; - [FieldOffset(0x20)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy2; - [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 Payload => Bytes.Slice(0x20, Unsafe.SizeOf()); + public AesCmac Cmac; + public AesIv Counter; + public Array144 Payload; public Span Bytes => SpanHelpers.AsByteSpan(ref this); - public readonly ReadOnlySpan ReadOnlyBytes => SpanHelpers.AsReadOnlyByteSpan(in this); + public readonly ReadOnlySpan BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this); [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool IsZeros() { - ReadOnlySpan ulongSpan = MemoryMarshal.Cast(ReadOnlyBytes); - - for (int i = 0; i < ulongSpan.Length; i++) + foreach (ulong val in SpanHelpers.AsReadOnlySpan(in this)) { - if (ulongSpan[i] != 0) + if (val != 0) return false; } @@ -44,39 +29,27 @@ public struct EncryptedKeyBlob } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ReadOnlySpan(in EncryptedKeyBlob value) - { - return SpanHelpers.AsReadOnlyByteSpan(in value); - } + public static implicit operator ReadOnlySpan(in EncryptedKeyBlob value) => + 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 { -#if DEBUG - [FieldOffset(0x00)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy1; - [FieldOffset(0x20)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy2; - [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 AesKey MasterKek; + public Array112 Unused; + public AesKey Package1Key; public Span Bytes => SpanHelpers.AsByteSpan(ref this); - public readonly ReadOnlySpan ReadOnlyBytes => SpanHelpers.AsReadOnlyByteSpan(in this); + public readonly ReadOnlySpan BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this); [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool IsZeros() { - ReadOnlySpan ulongSpan = MemoryMarshal.Cast(ReadOnlyBytes); - - for (int i = 0; i < ulongSpan.Length; i++) + foreach (ulong val in SpanHelpers.AsReadOnlySpan(in this)) { - if (ulongSpan[i] != 0) + if (val != 0) return false; } @@ -84,10 +57,6 @@ public struct KeyBlob } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ReadOnlySpan(in KeyBlob value) - { - return SpanHelpers.AsReadOnlyByteSpan(in value); - } - - public readonly override string ToString() => ReadOnlyBytes.ToHexString(); + public static implicit operator ReadOnlySpan(in KeyBlob value) => SpanHelpers.AsReadOnlyByteSpan(in value); + public readonly override string ToString() => BytesRo.ToHexString(); } \ No newline at end of file diff --git a/src/LibHac/Boot/Package1.cs b/src/LibHac/Boot/Package1.cs index 70407b41..03b16e05 100644 --- a/src/LibHac/Boot/Package1.cs +++ b/src/LibHac/Boot/Package1.cs @@ -1,71 +1,63 @@ -using LibHac.Common; -using LibHac.Fs; -using System; +using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Common.Keys; using LibHac.Diag; +using LibHac.Fs; using LibHac.Tools.FsSystem; using LibHac.Util; namespace LibHac.Boot; -[StructLayout(LayoutKind.Explicit, Size = 0x170)] public struct Package1MarikoOemHeader { - [FieldOffset(0x000)] private byte _aesMac; - [FieldOffset(0x010)] private byte _rsaSig; - [FieldOffset(0x110)] private byte _salt; - [FieldOffset(0x130)] private byte _hash; - [FieldOffset(0x150)] public int Version; - [FieldOffset(0x154)] public int Size; - [FieldOffset(0x158)] public int LoadAddress; - [FieldOffset(0x15C)] public int EntryPoint; - [FieldOffset(0x160)] private byte _reserved; - - public ReadOnlySpan AesMac => SpanHelpers.CreateSpan(ref _aesMac, 0x10); - public ReadOnlySpan RsaSig => SpanHelpers.CreateSpan(ref _rsaSig, 0x100); - public ReadOnlySpan Salt => SpanHelpers.CreateSpan(ref _salt, 0x20); - public ReadOnlySpan Hash => SpanHelpers.CreateSpan(ref _hash, 0x20); - public ReadOnlySpan Reserved => SpanHelpers.CreateSpan(ref _reserved, 0x10); + public Array16 AesMac; + public Array256 RsaSig; + public Array32 Salt; + public Array32 Hash; + public int Version; + public int Size; + public int LoadAddress; + public int EntryPoint; + public Array16 Reserved; } -[StructLayout(LayoutKind.Explicit, Size = 0x20)] public struct Package1MetaData { - [FieldOffset(0x00)] public uint LoaderHash; - [FieldOffset(0x04)] public uint SecureMonitorHash; - [FieldOffset(0x08)] public uint BootloaderHash; - [FieldOffset(0x10)] private byte _buildDate; - [FieldOffset(0x1E)] public byte KeyGeneration; - [FieldOffset(0x1F)] public byte Version; + public uint LoaderHash; + public uint SecureMonitorHash; + public uint BootloaderHash; + public uint Reserved; + private Array14 _buildDate; + public byte KeyGeneration; + public byte Version; - public U8Span BuildDate => new U8Span(SpanHelpers.CreateSpan(ref _buildDate, 0xE)); - public ReadOnlySpan Iv => SpanHelpers.CreateSpan(ref _buildDate, 0x10); + public U8Span BuildDate => new U8Span(_buildDate); + public ReadOnlySpan Iv => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_buildDate.Items), 0x10); } -[StructLayout(LayoutKind.Explicit, Size = 0x20)] public struct Package1Stage1Footer { - [FieldOffset(0x00)] public int Pk11Size; - [FieldOffset(0x10)] private byte _iv; - - public ReadOnlySpan Iv => SpanHelpers.CreateSpan(ref _iv, 0x10); + public int Pk11Size; + public Array12 Reserved; + public Array16 Iv; } -[StructLayout(LayoutKind.Explicit, Size = 0x20)] public struct Package1Pk11Header { - public const uint ExpectedMagic = 0x31314B50; // PK11 + public static readonly uint ExpectedMagic = 0x31314B50; // PK11 - [FieldOffset(0x00)] public uint Magic; - [FieldOffset(0x04)] public int WarmBootSize; - [FieldOffset(0x08)] public int WarmBootOffset; - [FieldOffset(0x10)] public int BootloaderSize; - [FieldOffset(0x14)] public int BootloaderOffset; - [FieldOffset(0x18)] public int SecureMonitorSize; - [FieldOffset(0x1C)] public int SecureMonitorOffset; + public uint Magic; + public int WarmBootSize; + public int WarmBootOffset; + public int Reserved; + public int BootloaderSize; + public int BootloaderOffset; + public int SecureMonitorSize; + public int SecureMonitorOffset; } public enum Package1Section @@ -102,13 +94,13 @@ public class Package1 private Package1MetaData _metaData; private Package1Stage1Footer _stage1Footer; private Package1Pk11Header _pk11Header; - private Buffer16 _pk11Mac; + private Array16 _pk11Mac; public ref readonly Package1MarikoOemHeader MarikoOemHeader => ref _marikoOemHeader; public ref readonly Package1MetaData MetaData => ref _metaData; public ref readonly Package1Stage1Footer Stage1Footer => ref _stage1Footer; public ref readonly Package1Pk11Header Pk11Header => ref _pk11Header; - public ref readonly Buffer16 Pk11Mac => ref _pk11Mac; + public ref readonly Array16 Pk11Mac => ref _pk11Mac; public Result Initialize(KeySet keySet, in SharedRef storage) { @@ -259,7 +251,7 @@ public class Package1 private Result ReadModernEristaMac() { - return _baseStorage.Get.Read(ModernStage1Size + Pk11Size, _pk11Mac.Bytes); + return _baseStorage.Get.Read(ModernStage1Size + Pk11Size, _pk11Mac.Items); } private Result SetPk11Storage() @@ -295,7 +287,7 @@ public class Package1 else { 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); @@ -359,7 +351,7 @@ public class Package1 // MarikoOemHeader must be read first private bool IsMarikoImpl() { - return MarikoOemHeader.AesMac.IsZeros() && MarikoOemHeader.Reserved.IsZeros(); + return MarikoOemHeader.AesMac.ItemsRo.IsZeros() && MarikoOemHeader.Reserved.ItemsRo.IsZeros(); } /// @@ -392,7 +384,7 @@ public class Package1 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); } - public IStorage OpenDecryptedWarmBootStorage() { if (!IsDecrypted) diff --git a/src/LibHac/Boot/Package2.cs b/src/LibHac/Boot/Package2.cs index 7e91c81c..2529cac3 100644 --- a/src/LibHac/Boot/Package2.cs +++ b/src/LibHac/Boot/Package2.cs @@ -1,16 +1,12 @@ using System; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Crypto; using LibHac.Util; -#if DEBUG -using System.Diagnostics; -#endif namespace LibHac.Boot; -[StructLayout(LayoutKind.Explicit, Size = 0x200)] public struct Package2Header { 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 SignatureSize = 0x100; - private ReadOnlySpan RsaPublicKeyExponent => new byte[] { 0x00, 0x01, 0x00, 0x01 }; + private static ReadOnlySpan RsaPublicKeyExponent => new byte[] { 0x00, 0x01, 0x00, 0x01 }; - [FieldOffset(0x00)] private byte _signature; - [FieldOffset(0x100)] public Package2Meta Meta; + public Array256 Signature; + public Package2Meta Meta; - public ReadOnlySpan Signature => SpanHelpers.CreateSpan(ref _signature, SignatureSize); - - public Result VerifySignature(ReadOnlySpan modulus, ReadOnlySpan data) + public readonly Result VerifySignature(ReadOnlySpan modulus, ReadOnlySpan data) { if (!Rsa.VerifyRsa2048PssSha256(Signature, modulus, RsaPublicKeyExponent, data)) { @@ -34,53 +28,38 @@ public struct Package2Header return Result.Success; } - -#if DEBUG - [DebuggerBrowsable(DebuggerBrowsableState.Never)] [FieldOffset(0x00)] private readonly Padding100 PaddingForVsDebugging; -#endif } -[StructLayout(LayoutKind.Explicit, Size = 0x100)] public struct Package2Meta { - public const uint ExpectedMagicValue = 0x31324B50; // PK21 + public static readonly uint ExpectedMagicValue = 0x31324B50; // PK21 - [FieldOffset(0x00)] private Buffer16 _headerIv; + public Array16 HeaderIv; + public Array3> PayloadIvs; + public Array16 Padding40; - [FieldOffset(0x00)] private uint _package2Size; - [FieldOffset(0x04)] private byte _keyGeneration; + public uint Magic; + public uint EntryPoint; + public Array4 Padding58; + public byte Package2Version; + public byte BootloaderVersion; - [FieldOffset(0x06)] private byte _keyGenerationXor1; - [FieldOffset(0x07)] private byte _keyGenerationXor2; - [FieldOffset(0x08)] private uint _sizeXor1; - [FieldOffset(0x0C)] private uint _sizeXor2; + public Array3 PayloadSizes; + public Array4 Padding6C; + public Array3 PayloadOffsets; + public Array4 Padding7C; + public Array3> PayloadHashes; + public Array32 PaddingE0; - [FieldOffset(0x10)] private Buffer16 _payloadIvs; + public readonly uint GetSize() + { + ReadOnlySpan ints = SpanHelpers.AsReadOnlySpan, uint>(in HeaderIv); + return ints[0] ^ ints[2] ^ ints[3]; + } - [FieldOffset(0x50)] private readonly uint _magic; - [FieldOffset(0x54)] private readonly uint _entryPoint; - [FieldOffset(0x5C)] private readonly byte _package2Version; - [FieldOffset(0x5D)] private readonly byte _bootloaderVersion; + public readonly byte GetKeyGeneration() => (byte)Math.Max(0, (HeaderIv[4] ^ HeaderIv[6] ^ HeaderIv[7]) - 1); - [FieldOffset(0x60)] private uint _payloadSizes; - [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 PayloadIvs => SpanHelpers.CreateSpan(ref _payloadIvs, Package2Header.PayloadCount); - public ReadOnlySpan PayloadSizes => SpanHelpers.CreateSpan(ref _payloadSizes, Package2Header.PayloadCount); - public ReadOnlySpan PayloadOffsets => SpanHelpers.CreateSpan(ref _payloadOffsets, Package2Header.PayloadCount); - public ReadOnlySpan PayloadHashes => SpanHelpers.CreateSpan(ref _payloadHashes, Package2Header.PayloadCount); - - public int GetPayloadFileOffset(int index) + public readonly int GetPayloadFileOffset(int index) { if ((uint)index >= Package2Header.PayloadCount) throw new IndexOutOfRangeException("Invalid payload index."); @@ -95,11 +74,11 @@ public struct Package2Meta return offset; } - public Result Verify() + public readonly Result Verify() { // Get the obfuscated metadata. - uint size = Size; - byte keyGeneration = KeyGeneration; + uint size = GetSize(); + byte keyGeneration = GetKeyGeneration(); // Check that size is big enough for the header. if (size < Unsafe.SizeOf()) @@ -128,7 +107,7 @@ public struct Package2Meta } // Check that the sizes sum to the total. - if (Size != Unsafe.SizeOf() + PayloadSizes[0] + PayloadSizes[1] + PayloadSizes[2]) + if (GetSize() != Unsafe.SizeOf() + PayloadSizes[0] + PayloadSizes[1] + PayloadSizes[2]) return ResultLibHac.InvalidPackage2MetaTotalSize.Log(); // Check that the payloads do not overflow. @@ -156,8 +135,4 @@ public struct Package2Meta // No payload contains the entrypoint, so we're not valid. return ResultLibHac.InvalidPackage2MetaEntryPointNotFound.Log(); } - -#if DEBUG - [DebuggerBrowsable(DebuggerBrowsableState.Never)] [FieldOffset(0x00)] private readonly Padding100 PaddingForVsDebugging; -#endif -} +} \ No newline at end of file diff --git a/src/LibHac/Boot/Package2StorageReader.cs b/src/LibHac/Boot/Package2StorageReader.cs index e19dd186..66258236 100644 --- a/src/LibHac/Boot/Package2StorageReader.cs +++ b/src/LibHac/Boot/Package2StorageReader.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Common.Keys; using LibHac.Crypto; using LibHac.Fs; @@ -41,7 +42,7 @@ public class Package2StorageReader : IDisposable Result rc = storage.Get.Read(0, SpanHelpers.AsByteSpan(ref _header)); 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); _storage.SetByCopy(in storage); @@ -54,7 +55,7 @@ public class Package2StorageReader : IDisposable /// /// If the method returns successfully, contains an /// of the specified payload. - /// The index of the payload to get. Must me less than + /// The index of the payload to get. Must be less than /// The of the operation. public Result OpenPayload(ref UniqueRef outPayloadStorage, int index) { @@ -72,7 +73,7 @@ public class Package2StorageReader : IDisposable 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)); return Result.Success; } @@ -219,7 +220,7 @@ public class Package2StorageReader : IDisposable var storages = new List(4); // The signature and IV are unencrypted - int unencryptedHeaderSize = Package2Header.SignatureSize + Unsafe.SizeOf(); + int unencryptedHeaderSize = Package2Header.SignatureSize + Unsafe.SizeOf>(); int encryptedHeaderSize = Unsafe.SizeOf() - unencryptedHeaderSize; // 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 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); 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 key, ref Package2Meta source, ref Package2Meta dest) { - Buffer16 iv = source.HeaderIv; + Array16 iv = source.HeaderIv; 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 - Unsafe.As(ref dest) = iv; + dest.HeaderIv = iv; } private bool HasIniPayload() diff --git a/src/LibHac/Common/FixedArrays/Array112.cs b/src/LibHac/Common/FixedArrays/Array112.cs new file mode 100644 index 00000000..a25e7f98 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array112.cs @@ -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 +{ + public const int Length = 112; + + private Array80 _0; + private Array32 _80; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array112 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array14.cs b/src/LibHac/Common/FixedArrays/Array14.cs new file mode 100644 index 00000000..8d309393 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array14.cs @@ -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 +{ + public const int Length = 14; + + private Array8 _0; + private Array6 _8; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array14 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array144.cs b/src/LibHac/Common/FixedArrays/Array144.cs new file mode 100644 index 00000000..b727dcae --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array144.cs @@ -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 +{ + public const int Length = 144; + + private Array128 _0; + private Array16 _128; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array144 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array15.cs b/src/LibHac/Common/FixedArrays/Array15.cs new file mode 100644 index 00000000..6c7d6d93 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array15.cs @@ -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 +{ + public const int Length = 15; + + private Array8 _0; + private Array7 _8; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array15 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array18.cs b/src/LibHac/Common/FixedArrays/Array18.cs new file mode 100644 index 00000000..c69b5990 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array18.cs @@ -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 +{ + public const int Length = 18; + + private Array16 _0; + private Array2 _16; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array18 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array56.cs b/src/LibHac/Common/FixedArrays/Array56.cs new file mode 100644 index 00000000..b03d02cb --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array56.cs @@ -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 +{ + public const int Length = 56; + + private Array32 _0; + private Array24 _32; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array56 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array7.cs b/src/LibHac/Common/FixedArrays/Array7.cs new file mode 100644 index 00000000..a64b7207 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array7.cs @@ -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 +{ + 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 Items => SpanHelpers.CreateSpan(ref _1, Length); + public readonly ReadOnlySpan ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array7 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/FixedArrays/Array80.cs b/src/LibHac/Common/FixedArrays/Array80.cs new file mode 100644 index 00000000..186efd72 --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array80.cs @@ -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 +{ + public const int Length = 80; + + private Array64 _0; + private Array16 _64; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array80 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Common/Utilities.cs b/src/LibHac/Common/Utilities.cs index 47a4f336..98530ca5 100644 --- a/src/LibHac/Common/Utilities.cs +++ b/src/LibHac/Common/Utilities.cs @@ -289,7 +289,8 @@ public static class Utilities 8 => "8.1.0-8.1.1", 9 => "9.0.0-9.0.1", 0xA => "9.1.0-12.0.3", - 0xB => "12.1.0-", + 0xB => "12.1.0", + 0xC => "13.0.0-", _ => "Unknown" }; diff --git a/src/LibHac/Fs/AccessLog.cs b/src/LibHac/Fs/AccessLog.cs index b1a68511..693766dc 100644 --- a/src/LibHac/Fs/AccessLog.cs +++ b/src/LibHac/Fs/AccessLog.cs @@ -2,10 +2,10 @@ using System.Buffers; using System.Buffers.Text; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Text; using System.Text.Unicode; using LibHac.Common; +using LibHac.Common.FixedArrays; using LibHac.Diag; using LibHac.Fs.Fsa; using LibHac.Fs.Impl; @@ -34,13 +34,13 @@ namespace LibHac.Fs } } - [StructLayout(LayoutKind.Sequential, Size = 0x20)] public struct ApplicationInfo { public Ncm.ApplicationId ApplicationId; public uint Version; public byte LaunchType; public bool IsMultiProgram; + public Array18 Reserved; } [Flags] diff --git a/src/LibHac/Fs/CodeVerificationData.cs b/src/LibHac/Fs/CodeVerificationData.cs index 829a3114..361df014 100644 --- a/src/LibHac/Fs/CodeVerificationData.cs +++ b/src/LibHac/Fs/CodeVerificationData.cs @@ -1,17 +1,11 @@ -using System; -using System.Runtime.InteropServices; -using LibHac.Common; +using LibHac.Common.FixedArrays; namespace LibHac.Fs; -[StructLayout(LayoutKind.Explicit, Size = 0x124)] public struct CodeVerificationData { - private const int Signature2Size = 0x100; - - [FieldOffset(0x000)] private byte _signature2; - [FieldOffset(0x100)] public Buffer32 NcaHeaderHash; - [FieldOffset(0x120)] public bool IsValid; - - public Span NcaSignature2 => SpanHelpers.CreateSpan(ref _signature2, Signature2Size); -} + public Array256 Signature; + public Array32 Hash; + public bool HasData; + public Array3 Reserved; +} \ No newline at end of file diff --git a/src/LibHac/Fs/DirectoryEntry.cs b/src/LibHac/Fs/DirectoryEntry.cs index 2a0569ed..6a222d57 100644 --- a/src/LibHac/Fs/DirectoryEntry.cs +++ b/src/LibHac/Fs/DirectoryEntry.cs @@ -1,18 +1,15 @@ using System; -using System.Runtime.InteropServices; -using LibHac.Common; +using LibHac.Common.FixedArrays; namespace LibHac.Fs; - -[StructLayout(LayoutKind.Explicit)] public struct DirectoryEntry { - [FieldOffset(0)] private byte _name; - [FieldOffset(0x301)] public NxFileAttributes Attributes; - [FieldOffset(0x304)] public DirectoryEntryType Type; - [FieldOffset(0x308)] public long Size; - - public Span Name => SpanHelpers.CreateSpan(ref _name, PathTool.EntryNameLengthMax + 1); + public Array769 Name; + public NxFileAttributes Attributes; + public Array2 Reserved302; + public DirectoryEntryType Type; + public Array3 Reserved305; + public long Size; } public enum DirectoryEntryType : byte diff --git a/src/LibHac/Fs/EncryptionSeed.cs b/src/LibHac/Fs/EncryptionSeed.cs index add168b4..619abda7 100644 --- a/src/LibHac/Fs/EncryptionSeed.cs +++ b/src/LibHac/Fs/EncryptionSeed.cs @@ -1,28 +1,13 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using LibHac.Common; +using System.Diagnostics; +using LibHac.Common.FixedArrays; +using LibHac.Util; namespace LibHac.Fs; [DebuggerDisplay("{ToString()}")] -[StructLayout(LayoutKind.Sequential, Size = 0x10)] -public struct EncryptionSeed : IEquatable +public struct EncryptionSeed { - private readonly Key128 Key; + public Array16 Value; - public readonly ReadOnlySpan Value => SpanHelpers.AsReadOnlyByteSpan(in this); - - public EncryptionSeed(ReadOnlySpan 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); -} + public readonly override string ToString() => Value.ItemsRo.ToHexString(); +} \ No newline at end of file diff --git a/src/LibHac/Fs/ErrorInfo.cs b/src/LibHac/Fs/ErrorInfo.cs index e6ad9679..b3ef2385 100644 --- a/src/LibHac/Fs/ErrorInfo.cs +++ b/src/LibHac/Fs/ErrorInfo.cs @@ -1,23 +1,22 @@ -using System.Runtime.InteropServices; +using LibHac.Common.FixedArrays; using LibHac.Fat; namespace LibHac.Fs; -[StructLayout(LayoutKind.Explicit, Size = 0x80)] public struct FileSystemProxyErrorInfo { - [FieldOffset(0x00)] public int RomFsRemountForDataCorruptionCount; - [FieldOffset(0x04)] public int RomFsUnrecoverableDataCorruptionByRemountCount; - [FieldOffset(0x08)] public FatError FatError; - [FieldOffset(0x28)] public int RomFsRecoveredByInvalidateCacheCount; - [FieldOffset(0x2C)] public int SaveDataIndexCount; + public int RemountForDataCorruptionCount; + public int UnrecoverableDataCorruptionByRemountCount; + public FatError FatFsError; + public int RecoveredByInvalidateCacheCount; + public int SaveDataIndexCount; + public Array80 Reserved; } -[StructLayout(LayoutKind.Explicit, Size = 0x10)] public struct StorageErrorInfo { - [FieldOffset(0x00)] public int NumActivationFailures; - [FieldOffset(0x04)] public int NumActivationErrorCorrections; - [FieldOffset(0x08)] public int NumReadWriteFailures; - [FieldOffset(0x0C)] public int NumReadWriteErrorCorrections; -} + public int NumActivationFailures; + public int NumActivationErrorCorrections; + public int NumReadWriteFailures; + public int NumReadWriteErrorCorrections; +} \ No newline at end of file diff --git a/src/LibHac/Fs/FileTimeStamp.cs b/src/LibHac/Fs/FileTimeStamp.cs index afeccd6f..63d4ba84 100644 --- a/src/LibHac/Fs/FileTimeStamp.cs +++ b/src/LibHac/Fs/FileTimeStamp.cs @@ -1,12 +1,22 @@ -using System.Runtime.InteropServices; +using LibHac.Common.FixedArrays; +using LibHac.Time; 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 Reserved; +} + public struct FileTimeStampRaw { public long Created; public long Accessed; public long Modified; public bool IsLocalTime; -} + public Array7 Reserved; +} \ No newline at end of file diff --git a/src/LibHac/Fs/ProgramIndexMapInfo.cs b/src/LibHac/Fs/ProgramIndexMapInfo.cs index a56ba198..5d326151 100644 --- a/src/LibHac/Fs/ProgramIndexMapInfo.cs +++ b/src/LibHac/Fs/ProgramIndexMapInfo.cs @@ -1,12 +1,12 @@ -using System.Runtime.InteropServices; +using LibHac.Common.FixedArrays; using LibHac.Ncm; namespace LibHac.Fs; -[StructLayout(LayoutKind.Explicit, Size = 0x20)] public struct ProgramIndexMapInfo { - [FieldOffset(0x00)] public ProgramId ProgramId; - [FieldOffset(0x08)] public ProgramId MainProgramId; - [FieldOffset(0x10)] public byte ProgramIndex; -} + public ProgramId ProgramId; + public ProgramId MainProgramId; + public byte ProgramIndex; + public Array15 Reserved; +} \ No newline at end of file diff --git a/src/LibHac/Fs/QueryRangeInfo.cs b/src/LibHac/Fs/QueryRangeInfo.cs index 2a496a90..d44cfbf8 100644 --- a/src/LibHac/Fs/QueryRangeInfo.cs +++ b/src/LibHac/Fs/QueryRangeInfo.cs @@ -1,13 +1,13 @@ using System; -using System.Runtime.InteropServices; +using LibHac.Common.FixedArrays; namespace LibHac.Fs; -[StructLayout(LayoutKind.Sequential, Size = 0x40)] public struct QueryRangeInfo { public int AesCtrKeyType; public int SpeedEmulationType; + public Array56 Reserved; public void Clear() { @@ -27,4 +27,4 @@ public struct QueryRangeInfo InternalKeyForHardwareAes = 1 << 1, ExternalKeyForHardwareAes = 1 << 2 } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/RightsId.cs b/src/LibHac/Fs/RightsId.cs index 1653c1c0..a81b5cf5 100644 --- a/src/LibHac/Fs/RightsId.cs +++ b/src/LibHac/Fs/RightsId.cs @@ -1,62 +1,56 @@ using System; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using LibHac.Common; +using System.Runtime.Intrinsics; +using LibHac.Common.FixedArrays; +using LibHac.Diag; using LibHac.Util; namespace LibHac.Fs; [DebuggerDisplay("{DebugDisplay(),nq}")] -[StructLayout(LayoutKind.Sequential, Size = 0x10)] -public struct RightsId : IEquatable, IComparable, IComparable +public struct RightsId : IEquatable { - public readonly Id128 Id; + public Array16 Value; - public RightsId(ulong high, ulong low) + public RightsId(ReadOnlySpan value) { - Id = new Id128(high, low); + Assert.Equal(0x10, value.Length); + + Unsafe.SkipInit(out Value); + + Span longsThis = MemoryMarshal.Cast(Value.Items); + ReadOnlySpan longsValue = MemoryMarshal.Cast(value); + + longsThis[1] = longsValue[1]; + longsThis[0] = longsValue[0]; } - public RightsId(ReadOnlySpan uid) - { - Id = new Id128(uid); - } + public readonly override string ToString() => Value.ItemsRo.ToHexString(); - public override string ToString() => Id.ToString(); - - public string DebugDisplay() + public readonly string DebugDisplay() { - ReadOnlySpan highBytes = AsBytes().Slice(0, 8); - ReadOnlySpan lowBytes = AsBytes().Slice(8, 8); + ReadOnlySpan highBytes = Value.ItemsRo.Slice(0, 8); + ReadOnlySpan lowBytes = Value.ItemsRo.Slice(8, 8); return $"{highBytes.ToHexString()} {lowBytes.ToHexString()}"; } - public bool Equals(RightsId other) => Id == other.Id; - 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) + public readonly bool Equals(RightsId other) { - if (obj is null) return 1; - return obj is RightsId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(RightsId)}"); + return Unsafe.As, Vector128>(ref Unsafe.AsRef(in Value)) + .Equals(Unsafe.As, Vector128>(ref other.Value)); } - public void ToBytes(Span output) => Id.ToBytes(output); + public readonly override bool Equals(object obj) => obj is RightsId other && Equals(other); - public ReadOnlySpan AsBytes() + public readonly override int GetHashCode() { - return SpanHelpers.AsByteSpan(ref this); + ReadOnlySpan longSpan = MemoryMarshal.Cast(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.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; -} +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs b/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs index cc19675a..3a694bf7 100644 --- a/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs +++ b/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs @@ -86,7 +86,7 @@ public class NcaFileSystemServiceImpl UnsafeHelpers.SkipParamInit(out 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 var currentPath = new U8Span(path.GetString()); @@ -691,9 +691,9 @@ public class NcaFileSystemServiceImpl private Result SetExternalKeyForRightsId(Nca nca) { 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())) + if (Crypto.CryptoUtil.IsSameBytes(rightsId.Value, zero.Value, Unsafe.SizeOf())) return Result.Success; // ReSharper disable once UnusedVariable diff --git a/src/LibHac/FsSrv/StatusReportService.cs b/src/LibHac/FsSrv/StatusReportService.cs index 774f1072..6ff8be12 100644 --- a/src/LibHac/FsSrv/StatusReportService.cs +++ b/src/LibHac/FsSrv/StatusReportService.cs @@ -62,9 +62,9 @@ public class StatusReportServiceImpl { errorInfo = new FileSystemProxyErrorInfo(); - _config.NcaFsServiceImpl.GetAndClearRomFsErrorInfo(out errorInfo.RomFsRemountForDataCorruptionCount, - out errorInfo.RomFsUnrecoverableDataCorruptionByRemountCount, - out errorInfo.RomFsRecoveredByInvalidateCacheCount); + _config.NcaFsServiceImpl.GetAndClearRomFsErrorInfo(out errorInfo.RemountForDataCorruptionCount, + out errorInfo.UnrecoverableDataCorruptionByRemountCount, + out errorInfo.RecoveredByInvalidateCacheCount); // Missing: GetFatInfo diff --git a/src/LibHac/FsSystem/LocalDirectory.cs b/src/LibHac/FsSystem/LocalDirectory.cs index 2b6c1a06..67c5e2ef 100644 --- a/src/LibHac/FsSystem/LocalDirectory.cs +++ b/src/LibHac/FsSystem/LocalDirectory.cs @@ -40,7 +40,7 @@ public class LocalDirectory : IDirectory DirectoryEntryType type = isDir ? DirectoryEntryType.Directory : DirectoryEntryType.File; 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].Attributes = localEntry.Attributes.ToNxAttributes(); diff --git a/src/LibHac/FsSystem/PartitionDirectory.cs b/src/LibHac/FsSystem/PartitionDirectory.cs index a21ec0d8..7ac91655 100644 --- a/src/LibHac/FsSystem/PartitionDirectory.cs +++ b/src/LibHac/FsSystem/PartitionDirectory.cs @@ -47,7 +47,7 @@ public class PartitionDirectory : IDirectory entry.Type = DirectoryEntryType.File; entry.Size = fileEntry.Size; - StringUtils.Copy(entry.Name, nameUtf8); + StringUtils.Copy(entry.Name.Items, nameUtf8); entry.Name[PathTool.EntryNameLengthMax] = 0; CurrentIndex++; diff --git a/src/LibHac/FsSystem/PartitionFileSystemCore.cs b/src/LibHac/FsSystem/PartitionFileSystemCore.cs index db133b75..0ebac724 100644 --- a/src/LibHac/FsSystem/PartitionFileSystemCore.cs +++ b/src/LibHac/FsSystem/PartitionFileSystemCore.cs @@ -358,7 +358,7 @@ public class PartitionFileSystemCore : IFileSystem where T : unmanaged, IPart entryBuffer[i].Size = ParentFs._metaData.GetEntry(CurrentIndex).Size; 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; CurrentIndex++; @@ -388,4 +388,4 @@ public class PartitionFileSystemCore : IFileSystem where T : unmanaged, IPart return Result.Success; } } -} +} \ No newline at end of file diff --git a/src/LibHac/Tools/Fs/InMemoryFileSystem.cs b/src/LibHac/Tools/Fs/InMemoryFileSystem.cs index e799804f..0efca041 100644 --- a/src/LibHac/Tools/Fs/InMemoryFileSystem.cs +++ b/src/LibHac/Tools/Fs/InMemoryFileSystem.cs @@ -252,7 +252,7 @@ public class InMemoryFileSystem : IAttributeFileSystem { 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.Type = DirectoryEntryType.Directory; @@ -270,7 +270,7 @@ public class InMemoryFileSystem : IAttributeFileSystem { 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.Type = DirectoryEntryType.File; diff --git a/src/LibHac/Tools/FsSystem/RomFs/RomFsDirectory.cs b/src/LibHac/Tools/FsSystem/RomFs/RomFsDirectory.cs index ed2e593c..d7b25092 100644 --- a/src/LibHac/Tools/FsSystem/RomFs/RomFsDirectory.cs +++ b/src/LibHac/Tools/FsSystem/RomFs/RomFsDirectory.cs @@ -50,7 +50,7 @@ public class RomFsDirectory : IDirectory ref DirectoryEntry entry = ref entryBuffer[i]; Span nameUtf8 = Encoding.UTF8.GetBytes(name); - StringUtils.Copy(entry.Name, nameUtf8); + StringUtils.Copy(entry.Name.Items, nameUtf8); entry.Name[PathTool.EntryNameLengthMax] = 0; entry.Type = DirectoryEntryType.Directory; @@ -70,7 +70,7 @@ public class RomFsDirectory : IDirectory ref DirectoryEntry entry = ref entryBuffer[i]; Span nameUtf8 = Encoding.UTF8.GetBytes(name); - StringUtils.Copy(entry.Name, nameUtf8); + StringUtils.Copy(entry.Name.Items, nameUtf8); entry.Name[PathTool.EntryNameLengthMax] = 0; entry.Type = DirectoryEntryType.File; diff --git a/src/LibHac/Tools/FsSystem/Save/SaveDataDirectory.cs b/src/LibHac/Tools/FsSystem/Save/SaveDataDirectory.cs index adf8f9dd..d1133b8a 100644 --- a/src/LibHac/Tools/FsSystem/Save/SaveDataDirectory.cs +++ b/src/LibHac/Tools/FsSystem/Save/SaveDataDirectory.cs @@ -50,7 +50,7 @@ public class SaveDataDirectory : IDirectory ref DirectoryEntry entry = ref entryBuffer[i]; Span nameUtf8 = Encoding.UTF8.GetBytes(name); - StringUtils.Copy(entry.Name, nameUtf8); + StringUtils.Copy(entry.Name.Items, nameUtf8); entry.Name[64] = 0; entry.Type = DirectoryEntryType.Directory; @@ -70,7 +70,7 @@ public class SaveDataDirectory : IDirectory ref DirectoryEntry entry = ref entryBuffer[i]; Span nameUtf8 = Encoding.UTF8.GetBytes(name); - StringUtils.Copy(entry.Name, nameUtf8); + StringUtils.Copy(entry.Name.Items, nameUtf8); entry.Name[64] = 0; entry.Type = DirectoryEntryType.File; diff --git a/src/hactoolnet/ProcessPackage.cs b/src/hactoolnet/ProcessPackage.cs index 1ab47203..b62670af 100644 --- a/src/hactoolnet/ProcessPackage.cs +++ b/src/hactoolnet/ProcessPackage.cs @@ -64,9 +64,9 @@ internal static class ProcessPackage if (package1.IsMariko) { sb.AppendLine("Mariko OEM Header:"); - PrintItem(sb, colLen, " Signature:", package1.MarikoOemHeader.RsaSig.ToArray()); - PrintItem(sb, colLen, " Random Salt:", package1.MarikoOemHeader.Salt.ToArray()); - PrintItem(sb, colLen, " OEM Bootloader Hash:", package1.MarikoOemHeader.Hash.ToArray()); + PrintItem(sb, colLen, " Signature:", package1.MarikoOemHeader.RsaSig.ItemsRo.ToArray()); + PrintItem(sb, colLen, " Random Salt:", package1.MarikoOemHeader.Salt.ItemsRo.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 Size:", $"{package1.MarikoOemHeader.Size: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) { - PrintItem(sb, colLen, " PK11 MAC:", package1.Pk11Mac); + PrintItem(sb, colLen, " PK11 MAC:", package1.Pk11Mac.ItemsRo.ToArray()); } if (package1.IsDecrypted) @@ -162,16 +162,16 @@ internal static class ProcessPackage sb.AppendLine(); sb.AppendLine("PK21:"); - PrintItem(sb, colLen, $"Signature{signatureValidity.GetValidityString()}:", package2.Header.Signature.ToArray()); - PrintItem(sb, colLen, "Header Version:", $"{package2.Header.Meta.KeyGeneration:x2}"); + PrintItem(sb, colLen, $"Signature{signatureValidity.GetValidityString()}:", package2.Header.Signature.ItemsRo.ToArray()); + PrintItem(sb, colLen, "Header Version:", $"{package2.Header.Meta.GetKeyGeneration():x2}"); for (int i = 0; i < 3; i++) { string name = package2.Header.Meta.PayloadSizes[i] != 0 ? Package2SectionNames[i] : "Empty"; sb.AppendLine($"Section {i} ({name}):"); - PrintItem(sb, colLen, " Hash:", package2.Header.Meta.PayloadHashes[i]); - PrintItem(sb, colLen, " CTR:", package2.Header.Meta.PayloadIvs[i]); + PrintItem(sb, colLen, " Hash:", package2.Header.Meta.PayloadHashes[i].ItemsRo.ToArray()); + 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, " Size:", $"{package2.Header.Meta.PayloadSizes[i]:x8}"); } diff --git a/tests/LibHac.Tests/Boot/TypeLayoutTests.cs b/tests/LibHac.Tests/Boot/TypeLayoutTests.cs new file mode 100644 index 00000000..4effb74a --- /dev/null +++ b/tests/LibHac.Tests/Boot/TypeLayoutTests.cs @@ -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()); + + 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()); + + 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()); + + 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()); + + 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()); + + 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()); + + 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()); + + 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()); + + 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)); + } +} \ No newline at end of file diff --git a/tests/LibHac.Tests/Boot/TypeSizeTests.cs b/tests/LibHac.Tests/Boot/TypeSizeTests.cs deleted file mode 100644 index 7b62c316..00000000 --- a/tests/LibHac.Tests/Boot/TypeSizeTests.cs +++ /dev/null @@ -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()); - } - - [Fact] - public static void KeyBlobSizeIs0x90() - { - Assert.Equal(0x90, Unsafe.SizeOf()); - } -} diff --git a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs index e0746b30..057d9818 100644 --- a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs +++ b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs @@ -146,4 +146,147 @@ public class TypeLayoutTests Assert.Equal(0x58, GetOffset(in s, in s.PooledBufferFailedIdealAllocationCountOnAsyncAccess)); Assert.Equal(0x60, GetOffset(in s, in s.Reserved)); } + + [Fact] + public static void ApplicationInfo_Layout() + { + var s = new ApplicationInfo(); + + Assert.Equal(0x20, Unsafe.SizeOf()); + + 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()); + + 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()); + + 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()); + + Assert.Equal(0, GetOffset(in s, in s.Value)); + } + + [Fact] + public static void FileSystemProxyErrorInfo_Layout() + { + var s = new FileSystemProxyErrorInfo(); + + Assert.Equal(0x80, Unsafe.SizeOf()); + + 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()); + + 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()); + + 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()); + + 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()); + + 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()); + + 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()); + + Assert.Equal(0x00, GetOffset(in s, in s.Value)); + } } \ No newline at end of file