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 LibHac.Common.FixedArrays;
using System.Runtime.InteropServices;
using LibHac.Common;
namespace LibHac.Fs.Impl; namespace LibHac.Fs.Impl;
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
public struct InitialDataAad public struct InitialDataAad
{ {
public byte this[int i] public Array32<byte> Value;
{
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);
} }
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct KeySeed public struct KeySeed
{ {
public byte this[int i] public Array16<byte> Value;
{
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);
} }
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct InitialDataMac public struct InitialDataMac
{ {
public byte this[int i] public Array16<byte> Value;
{
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);
} }
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
public struct ImportReportInfo public struct ImportReportInfo
{ {
public byte DiffChunkCount; public byte DiffChunkCount;
public byte DoubleDivisionDiffChunkCount; public byte DoubleDivisionDiffChunkCount;
public byte HalfDivisionDiffChunkCount; public byte HalfDivisionDiffChunkCount;
public byte CompressionRate; public byte CompressionRate;
public Array28<byte> Reserved;
} }

View file

@ -1,32 +1,13 @@
using System; using LibHac.Common.FixedArrays;
using System.Runtime.InteropServices;
using LibHac.Common;
namespace LibHac.Fs; namespace LibHac.Fs;
[StructLayout(LayoutKind.Sequential, Size = 0x100)]
public struct RsaEncryptedKey public struct RsaEncryptedKey
{ {
public byte this[int i] public Array256<byte> Value;
{
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);
} }
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct AesKey public struct AesKey
{ {
public byte this[int i] public Array16<byte> Value;
{
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);
} }

View file

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

View file

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

View file

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

View file

@ -1,26 +1,21 @@
using System; using System;
using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Common.FixedArrays;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Util; using LibHac.Util;
namespace LibHac.FsSrv.Sf; namespace LibHac.FsSrv.Sf;
[StructLayout(LayoutKind.Sequential, Size = MaxLength + 1)] [StructLayout(LayoutKind.Sequential)]
public readonly struct FspPath public readonly struct FspPath
{ {
internal const int MaxLength = 0x300; internal const int MaxLength = 0x300;
#if DEBUG private readonly Array769<byte> _value;
[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
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) 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); 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; str[MaxLength] = 0;
var sb = new U8StringBuilder(str); var sb = new U8StringBuilder(str);

View file

@ -1,23 +1,14 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs; using LibHac.Common.FixedArrays;
#if DEBUG
using System.Diagnostics;
#endif
namespace LibHac.FsSrv.Sf; namespace LibHac.FsSrv.Sf;
[StructLayout(LayoutKind.Sequential, Size = PathTool.EntryNameLengthMax + 1)] [StructLayout(LayoutKind.Sequential)]
public readonly struct Path public readonly struct Path
{ {
#if DEBUG private readonly Array769<byte> _value;
[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
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;
using System.Runtime.InteropServices; using LibHac.Common.FixedArrays;
namespace LibHac.FsSrv.Storage; namespace LibHac.FsSrv.Storage;
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public readonly struct StorageDeviceHandle : IEquatable<StorageDeviceHandle> public readonly struct StorageDeviceHandle : IEquatable<StorageDeviceHandle>
{ {
public readonly uint Value; public readonly uint Value;
public readonly StorageDevicePortId PortId; public readonly StorageDevicePortId PortId;
public readonly Array11<byte> Reserved;
public StorageDeviceHandle(uint value, StorageDevicePortId portId) public StorageDeviceHandle(uint value, StorageDevicePortId portId)
{ {
Value = value; Value = value;
PortId = portId; PortId = portId;
Reserved = default;
} }
public override bool Equals(object obj) => obj is StorageDeviceHandle other && Equals(other); public override bool Equals(object obj) => obj is StorageDeviceHandle other && Equals(other);

View file

@ -1,5 +1,6 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Impl;
using Xunit; using Xunit;
using static LibHac.Tests.Common.Layout; using static LibHac.Tests.Common.Layout;
@ -289,4 +290,68 @@ public class TypeLayoutTests
Assert.Equal(0x00, GetOffset(in s, in s.Value)); 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));
}
}