Update struct layout of most remaining Fs structs

This commit is contained in:
Alex Barney 2022-01-02 14:14:37 -07:00
parent 9a358a4e30
commit b7e8ea8249
12 changed files with 272 additions and 112 deletions

View file

@ -0,0 +1,32 @@
#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 Array28<T>
{
public const int Length = 28;
private Array16<T> _0;
private Array8<T> _16;
private Array4<T> _24;
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 Array28<T> value) => value.ItemsRo;
}

View file

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

View file

@ -1,53 +1,27 @@
using System;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Common.FixedArrays;
namespace LibHac.Fs.Impl;
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
public struct InitialDataAad
{
public byte this[int i]
{
readonly get => BytesRo[i];
set => Bytes[i] = value;
public Array32<byte> Value;
}
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
public readonly ReadOnlySpan<byte> BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this);
}
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct KeySeed
{
public byte this[int i]
{
readonly get => BytesRo[i];
set => Bytes[i] = value;
public Array16<byte> Value;
}
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
public readonly ReadOnlySpan<byte> BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this);
}
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct InitialDataMac
{
public byte this[int i]
{
readonly get => BytesRo[i];
set => Bytes[i] = value;
public Array16<byte> Value;
}
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
public readonly ReadOnlySpan<byte> BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this);
}
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
public struct ImportReportInfo
{
public byte DiffChunkCount;
public byte DoubleDivisionDiffChunkCount;
public byte HalfDivisionDiffChunkCount;
public byte CompressionRate;
public Array28<byte> Reserved;
}

View file

@ -1,32 +1,13 @@
using System;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Common.FixedArrays;
namespace LibHac.Fs;
[StructLayout(LayoutKind.Sequential, Size = 0x100)]
public struct RsaEncryptedKey
{
public byte this[int i]
{
readonly get => BytesRo[i];
set => Bytes[i] = value;
public Array256<byte> Value;
}
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
public readonly ReadOnlySpan<byte> BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this);
}
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct AesKey
{
public byte this[int i]
{
readonly get => BytesRo[i];
set => Bytes[i] = value;
}
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
public readonly ReadOnlySpan<byte> BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this);
public Array16<byte> Value;
}

View file

@ -752,30 +752,30 @@ public readonly struct AccessControlBits
public bool CanWriteSaveDataFileSystemExtraDataTimeStamp() => Has(Bits.SaveDataBackUp);
}
[StructLayout(LayoutKind.Explicit, Size = 0x2C)]
[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct AccessControlDescriptor
{
[FieldOffset(0x00)] public byte Version;
[FieldOffset(0x01)] public byte ContentOwnerIdCount;
[FieldOffset(0x02)] public byte SaveDataOwnerIdCount;
[FieldOffset(0x04)] public ulong AccessFlags;
[FieldOffset(0x0C)] public ulong ContentOwnerIdMin;
[FieldOffset(0x14)] public ulong ContentOwnerIdMax;
[FieldOffset(0x1C)] public ulong SaveDataOwnerIdMin;
[FieldOffset(0x24)] public ulong SaveDataOwnerIdMax;
public byte Version;
public byte ContentOwnerIdCount;
public byte SaveDataOwnerIdCount;
public ulong AccessFlags;
public ulong ContentOwnerIdMin;
public ulong ContentOwnerIdMax;
public ulong SaveDataOwnerIdMin;
public ulong SaveDataOwnerIdMax;
// public ulong ContentOwnerIds[ContentOwnerIdCount];
// public ulong SaveDataOwnerIds[SaveDataOwnerIdCount];
}
[StructLayout(LayoutKind.Explicit, Size = 0x1C)]
[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct AccessControlDataHeader
{
[FieldOffset(0x00)] public byte Version;
[FieldOffset(0x04)] public ulong AccessFlags;
[FieldOffset(0x0C)] public int ContentOwnerInfoOffset;
[FieldOffset(0x10)] public int ContentOwnerInfoSize;
[FieldOffset(0x14)] public int SaveDataOwnerInfoOffset;
[FieldOffset(0x18)] public int SaveDataOwnerInfoSize;
public byte Version;
public ulong AccessFlags;
public int ContentOwnerInfoOffset;
public int ContentOwnerInfoSize;
public int SaveDataOwnerInfoOffset;
public int SaveDataOwnerInfoSize;
// [FieldOffset(ContentOwnerInfoOffset)]
// public int ContentOwnerInfoCount;

View file

@ -498,13 +498,12 @@ internal class MultiCommitManager : IMultiCommitManager
return Recover(multiCommitInterface, fileSystem.Get, saveService);
}
[StructLayout(LayoutKind.Explicit, Size = 0x18)]
private struct Context
{
[FieldOffset(0x00)] public int Version;
[FieldOffset(0x04)] public CommitState State;
[FieldOffset(0x08)] public int FileSystemCount;
[FieldOffset(0x10)] public long Counter;
public int Version;
public CommitState State;
public int FileSystemCount;
public long Counter;
}
private enum CommitState

View file

@ -1,14 +1,14 @@
using System.Runtime.InteropServices;
using LibHac.Common.FixedArrays;
using LibHac.Fs;
namespace LibHac.FsSrv;
[StructLayout(LayoutKind.Explicit, Size = 0x40)]
public struct SaveDataIndexerValue
{
[FieldOffset(0x00)] public ulong SaveDataId;
[FieldOffset(0x08)] public long Size;
[FieldOffset(0x10)] public ulong Field10;
[FieldOffset(0x18)] public SaveDataSpaceId SpaceId;
[FieldOffset(0x19)] public SaveDataState State;
public ulong SaveDataId;
public long Size;
public ulong Field10;
public SaveDataSpaceId SpaceId;
public SaveDataState State;
public Array38<byte> Reserved;
}

View file

@ -1,26 +1,21 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Common.FixedArrays;
using LibHac.Fs;
using LibHac.Util;
namespace LibHac.FsSrv.Sf;
[StructLayout(LayoutKind.Sequential, Size = MaxLength + 1)]
[StructLayout(LayoutKind.Sequential)]
public readonly struct FspPath
{
internal const int MaxLength = 0x300;
#if DEBUG
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding200;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly byte Padding300;
#endif
private readonly Array769<byte> _value;
public ReadOnlySpan<byte> Str => SpanHelpers.AsReadOnlyByteSpan(in this);
public ReadOnlySpan<byte> Str => SpanHelpers.AsReadOnlyByteSpan(in _value);
public static Result FromSpan(out FspPath fspPath, ReadOnlySpan<byte> path)
{
@ -28,7 +23,7 @@ public readonly struct FspPath
Span<byte> str = SpanHelpers.AsByteSpan(ref fspPath);
// Ensure null terminator even if the creation fails for safety
// Ensure null terminator even if the creation fails
str[MaxLength] = 0;
var sb = new U8StringBuilder(str);

View file

@ -1,23 +1,14 @@
using System;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
#if DEBUG
using System.Diagnostics;
#endif
using LibHac.Common.FixedArrays;
namespace LibHac.FsSrv.Sf;
[StructLayout(LayoutKind.Sequential, Size = PathTool.EntryNameLengthMax + 1)]
[StructLayout(LayoutKind.Sequential)]
public readonly struct Path
{
#if DEBUG
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding200;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly byte Padding300;
#endif
private readonly Array769<byte> _value;
public ReadOnlySpan<byte> Str => SpanHelpers.AsReadOnlyByteSpan(in this);
public ReadOnlySpan<byte> Str => SpanHelpers.AsReadOnlyByteSpan(in _value);
}

View file

@ -1,18 +1,19 @@
using System;
using System.Runtime.InteropServices;
using LibHac.Common.FixedArrays;
namespace LibHac.FsSrv.Storage;
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public readonly struct StorageDeviceHandle : IEquatable<StorageDeviceHandle>
{
public readonly uint Value;
public readonly StorageDevicePortId PortId;
public readonly Array11<byte> Reserved;
public StorageDeviceHandle(uint value, StorageDevicePortId portId)
{
Value = value;
PortId = portId;
Reserved = default;
}
public override bool Equals(object obj) => obj is StorageDeviceHandle other && Equals(other);

View file

@ -1,5 +1,6 @@
using System.Runtime.CompilerServices;
using LibHac.Fs;
using LibHac.Fs.Impl;
using Xunit;
using static LibHac.Tests.Common.Layout;
@ -289,4 +290,68 @@ public class TypeLayoutTests
Assert.Equal(0x00, GetOffset(in s, in s.Value));
}
[Fact]
public static void RsaEncryptedKey_Layout()
{
var s = new RsaEncryptedKey();
Assert.Equal(0x100, Unsafe.SizeOf<RsaEncryptedKey>());
Assert.Equal(0x00, GetOffset(in s, in s.Value));
}
[Fact]
public static void AesKey_Layout()
{
var s = new AesKey();
Assert.Equal(0x10, Unsafe.SizeOf<AesKey>());
Assert.Equal(0x00, GetOffset(in s, in s.Value));
}
[Fact]
public static void InitialDataAad_Layout()
{
var s = new InitialDataAad();
Assert.Equal(0x20, Unsafe.SizeOf<InitialDataAad>());
Assert.Equal(0x00, GetOffset(in s, in s.Value));
}
[Fact]
public static void KeySeed_Layout()
{
var s = new KeySeed();
Assert.Equal(0x10, Unsafe.SizeOf<KeySeed>());
Assert.Equal(0x00, GetOffset(in s, in s.Value));
}
[Fact]
public static void InitialDataMac_Layout()
{
var s = new InitialDataMac();
Assert.Equal(0x10, Unsafe.SizeOf<InitialDataMac>());
Assert.Equal(0x00, GetOffset(in s, in s.Value));
}
[Fact]
public static void ImportReportInfo_Layout()
{
var s = new ImportReportInfo();
Assert.Equal(0x20, Unsafe.SizeOf<ImportReportInfo>());
Assert.Equal(0, GetOffset(in s, in s.DiffChunkCount));
Assert.Equal(1, GetOffset(in s, in s.DoubleDivisionDiffChunkCount));
Assert.Equal(2, GetOffset(in s, in s.HalfDivisionDiffChunkCount));
Assert.Equal(3, GetOffset(in s, in s.CompressionRate));
Assert.Equal(4, GetOffset(in s, in s.Reserved));
}
}

View file

@ -0,0 +1,91 @@
using System.Runtime.CompilerServices;
using LibHac.FsSrv;
using LibHac.FsSrv.Impl;
using LibHac.FsSrv.Sf;
using LibHac.FsSrv.Storage;
using Xunit;
using static LibHac.Tests.Common.Layout;
namespace LibHac.Tests.FsSrv;
public class TypeLayoutTests
{
[Fact]
public static void AccessControlDescriptor_Layout()
{
var s = new AccessControlDescriptor();
Assert.Equal(0x2C, Unsafe.SizeOf<AccessControlDescriptor>());
Assert.Equal(0x00, GetOffset(in s, in s.Version));
Assert.Equal(0x01, GetOffset(in s, in s.ContentOwnerIdCount));
Assert.Equal(0x02, GetOffset(in s, in s.SaveDataOwnerIdCount));
Assert.Equal(0x04, GetOffset(in s, in s.AccessFlags));
Assert.Equal(0x0C, GetOffset(in s, in s.ContentOwnerIdMin));
Assert.Equal(0x14, GetOffset(in s, in s.ContentOwnerIdMax));
Assert.Equal(0x1C, GetOffset(in s, in s.SaveDataOwnerIdMin));
Assert.Equal(0x24, GetOffset(in s, in s.SaveDataOwnerIdMax));
}
[Fact]
public static void AccessControlDataHeader_Layout()
{
var s = new AccessControlDataHeader();
Assert.Equal(0x1C, Unsafe.SizeOf<AccessControlDataHeader>());
Assert.Equal(0x00, GetOffset(in s, in s.Version));
Assert.Equal(0x04, GetOffset(in s, in s.AccessFlags));
Assert.Equal(0x0C, GetOffset(in s, in s.ContentOwnerInfoOffset));
Assert.Equal(0x10, GetOffset(in s, in s.ContentOwnerInfoSize));
Assert.Equal(0x14, GetOffset(in s, in s.SaveDataOwnerInfoOffset));
Assert.Equal(0x18, GetOffset(in s, in s.SaveDataOwnerInfoSize));
}
[Fact]
public static void FspPath_Layout()
{
var s = new FspPath();
Assert.Equal(0x301, Unsafe.SizeOf<FspPath>());
Assert.Equal(0x00, GetOffset(in s, in s.Str[0]));
}
[Fact]
public static void Path_Layout()
{
var s = new Path();
Assert.Equal(0x301, Unsafe.SizeOf<Path>());
Assert.Equal(0x00, GetOffset(in s, in s.Str[0]));
}
[Fact]
public static void StorageDeviceHandle_Layout()
{
var s = new StorageDeviceHandle();
Assert.Equal(0x10, Unsafe.SizeOf<StorageDeviceHandle>());
Assert.Equal(0x0, GetOffset(in s, in s.Value));
Assert.Equal(0x4, GetOffset(in s, in s.PortId));
Assert.Equal(0x5, GetOffset(in s, in s.Reserved));
}
[Fact]
public static void SaveDataIndexerValue_Layout()
{
var s = new SaveDataIndexerValue();
Assert.Equal(0x40, Unsafe.SizeOf<SaveDataIndexerValue>());
Assert.Equal(0x00, GetOffset(in s, in s.SaveDataId));
Assert.Equal(0x08, GetOffset(in s, in s.Size));
Assert.Equal(0x10, GetOffset(in s, in s.Field10));
Assert.Equal(0x18, GetOffset(in s, in s.SpaceId));
Assert.Equal(0x19, GetOffset(in s, in s.State));
Assert.Equal(0x1A, GetOffset(in s, in s.Reserved));
}
}