Update struct layout of Ns structs

This commit is contained in:
Alex Barney 2022-01-02 16:52:57 -07:00
parent b7e8ea8249
commit b0e679d000
23 changed files with 759 additions and 337 deletions

View file

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

View file

@ -0,0 +1,34 @@
#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 Array3000<T>
{
public const int Length = 3000;
private Array2048<T> _0;
private Array512<T> _2048;
private Array256<T> _2560;
private Array128<T> _2816;
private Array56<T> _2944;
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 Array3000<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 Array37<T>
{
public const int Length = 37;
private Array32<T> _0;
private Array5<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 Array37<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 Array65<T>
{
public const int Length = 65;
private Array64<T> _0;
private 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 Array65<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 Array768<T>
{
public const int Length = 768;
private Array512<T> _0;
private Array256<T> _512;
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 Array768<T> value) => value.ItemsRo;
}

View file

@ -1,51 +0,0 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace LibHac.Common;
// In order for the Visual Studio debugger to accurately display a struct, every offset
// in the struct that is used for the debugger display must be part of a field.
// These padding structs make it easier to accomplish that.
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
internal struct Padding10
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding00;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding08;
}
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
internal struct Padding20
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding00;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding08;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding10;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding18;
}
[StructLayout(LayoutKind.Sequential, Size = 0x40)]
internal struct Padding40
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding20 Padding00;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding20 Padding20;
}
[StructLayout(LayoutKind.Sequential, Size = 0x80)]
internal struct Padding80
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding40 Padding00;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding40 Padding40;
}
[StructLayout(LayoutKind.Sequential, Size = 0x100)]
internal struct Padding100
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding00;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding80;
}
[StructLayout(LayoutKind.Sequential, Size = 0x200)]
internal struct Padding200
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100;
}

View file

@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis;
using LibHac.Account; using LibHac.Account;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs.Shim; using LibHac.Fs.Shim;
using LibHac.Ncm;
using LibHac.Ns; using LibHac.Ns;
using LibHac.Util; using LibHac.Util;
@ -18,7 +17,7 @@ public static class ApplicationSaveDataManagement
long requiredSizeSum = 0; long requiredSizeSum = 0;
// Create local variable for use in closures // Create local variable for use in closures
ProgramId saveDataOwnerId = nacp.SaveDataOwnerId; ulong saveDataOwnerId = nacp.SaveDataOwnerId;
// Ensure the user account save exists // Ensure the user account save exists
if (uid != Uid.Zero && nacp.UserAccountSaveDataSize > 0) if (uid != Uid.Zero && nacp.UserAccountSaveDataSize > 0)
@ -31,7 +30,7 @@ public static class ApplicationSaveDataManagement
Result CreateAccountSaveFunc() Result CreateAccountSaveFunc()
{ {
UserId userId = ConvertAccountUidToFsUserId(uidLocal); UserId userId = ConvertAccountUidToFsUserId(uidLocal);
return fs.CreateSaveData(applicationId, userId, saveDataOwnerId.Value, accountSaveDataSize, return fs.CreateSaveData(applicationId, userId, saveDataOwnerId, accountSaveDataSize,
accountSaveJournalSize, SaveDataFlags.None); accountSaveJournalSize, SaveDataFlags.None);
} }
@ -53,8 +52,8 @@ public static class ApplicationSaveDataManagement
long deviceSaveDataSize = nacp.DeviceSaveDataSize; long deviceSaveDataSize = nacp.DeviceSaveDataSize;
long deviceSaveJournalSize = nacp.DeviceSaveDataJournalSize; long deviceSaveJournalSize = nacp.DeviceSaveDataJournalSize;
Result CreateDeviceSaveFunc() => fs.CreateDeviceSaveData(applicationId, saveDataOwnerId.Value, Result CreateDeviceSaveFunc() => fs.CreateDeviceSaveData(applicationId, saveDataOwnerId, deviceSaveDataSize,
deviceSaveDataSize, deviceSaveJournalSize, 0); deviceSaveJournalSize, 0);
var filter = new SaveDataFilter(); var filter = new SaveDataFilter();
filter.SetProgramId(applicationId); filter.SetProgramId(applicationId);
@ -108,7 +107,7 @@ public static class ApplicationSaveDataManagement
} }
else else
{ {
Result createRc = fs.CreateTemporaryStorage(applicationId, nacp.SaveDataOwnerId.Value, Result createRc = fs.CreateTemporaryStorage(applicationId, nacp.SaveDataOwnerId,
nacp.TemporaryStorageSize, 0); nacp.TemporaryStorageSize, 0);
if (createRc.IsFailure()) if (createRc.IsFailure())
@ -311,7 +310,7 @@ public static class ApplicationSaveDataManagement
public static Result EnsureApplicationCacheStorage(this FileSystemClient fs, out long requiredSize, public static Result EnsureApplicationCacheStorage(this FileSystemClient fs, out long requiredSize,
Ncm.ApplicationId applicationId, ref ApplicationControlProperty nacp) Ncm.ApplicationId applicationId, ref ApplicationControlProperty nacp)
{ {
return EnsureApplicationCacheStorageImpl(fs, out requiredSize, out _, applicationId, nacp.SaveDataOwnerId.Value, return EnsureApplicationCacheStorageImpl(fs, out requiredSize, out _, applicationId, nacp.SaveDataOwnerId,
0, nacp.CacheStorageSize, nacp.CacheStorageJournalSize, true); 0, nacp.CacheStorageSize, nacp.CacheStorageJournalSize, true);
} }
@ -324,7 +323,7 @@ public static class ApplicationSaveDataManagement
return Result.Success; return Result.Success;
return EnsureApplicationCacheStorageImpl(fs, out requiredSize, out target, applicationId, return EnsureApplicationCacheStorageImpl(fs, out requiredSize, out target, applicationId,
nacp.SaveDataOwnerId.Value, 0, nacp.CacheStorageSize, nacp.CacheStorageJournalSize, true); nacp.SaveDataOwnerId, 0, nacp.CacheStorageSize, nacp.CacheStorageJournalSize, true);
} }
@ -334,14 +333,14 @@ public static class ApplicationSaveDataManagement
{ {
UnsafeHelpers.SkipParamInit(out requiredSize, out target); UnsafeHelpers.SkipParamInit(out requiredSize, out target);
if (index > nacp.CacheStorageMaxIndex) if (index > nacp.CacheStorageIndexMax)
return ResultFs.CacheStorageIndexTooLarge.Log(); return ResultFs.CacheStorageIndexTooLarge.Log();
if (dataSize + journalSize > nacp.CacheStorageMaxSizeAndMaxJournalSize) if (dataSize + journalSize > nacp.CacheStorageDataAndJournalSizeMax)
return ResultFs.CacheStorageSizeTooLarge.Log(); return ResultFs.CacheStorageSizeTooLarge.Log();
Result rc = fs.EnsureApplicationCacheStorage(out requiredSize, out target, applicationId, Result rc = fs.EnsureApplicationCacheStorage(out requiredSize, out target, applicationId,
nacp.SaveDataOwnerId.Value, index, dataSize, journalSize, false); nacp.SaveDataOwnerId, index, dataSize, journalSize, false);
fs.Impl.AbortIfNeeded(rc); fs.Impl.AbortIfNeeded(rc);
return rc; return rc;

View file

@ -1,11 +1,11 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
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;
using LibHac.FsSrv.Sf; using LibHac.FsSrv.Sf;
using LibHac.FsSystem;
using LibHac.Os; using LibHac.Os;
using LibHac.Util; using LibHac.Util;
using static LibHac.Fs.StringTraits; using static LibHac.Fs.StringTraits;
@ -65,11 +65,11 @@ public static class Host
private class HostCommonMountNameGenerator : ICommonMountNameGenerator private class HostCommonMountNameGenerator : ICommonMountNameGenerator
{ {
private FsPath _path; private Array769<byte> _path;
public HostCommonMountNameGenerator(U8Span path) public HostCommonMountNameGenerator(U8Span path)
{ {
StringUtils.Strlcpy(_path.Str, path, FsPath.MaxLength + 1); StringUtils.Strlcpy(_path.Items, path, PathTool.EntryNameLengthMax + 1);
} }
public void Dispose() { } public void Dispose() { }
@ -77,13 +77,13 @@ public static class Host
public Result GenerateCommonMountName(Span<byte> nameBuffer) public Result GenerateCommonMountName(Span<byte> nameBuffer)
{ {
int requiredNameBufferSize = int requiredNameBufferSize =
StringUtils.GetLength(_path.Str, FsPath.MaxLength + 1) + HostRootFileSystemPathLength; StringUtils.GetLength(_path, PathTool.EntryNameLengthMax + 1) + HostRootFileSystemPathLength;
if (nameBuffer.Length < requiredNameBufferSize) if (nameBuffer.Length < requiredNameBufferSize)
return ResultFs.TooLongPath.Log(); return ResultFs.TooLongPath.Log();
var sb = new U8StringBuilder(nameBuffer); var sb = new U8StringBuilder(nameBuffer);
sb.Append(HostRootFileSystemPath).Append(_path.Str); sb.Append(HostRootFileSystemPath).Append(_path);
Assert.SdkEqual(sb.Length, requiredNameBufferSize - 1); Assert.SdkEqual(sb.Length, requiredNameBufferSize - 1);

View file

@ -79,10 +79,10 @@ internal class LocationResolverSet : IDisposable
var pathFlags = new PathFlags(); var pathFlags = new PathFlags();
pathFlags.AllowMountName(); pathFlags.AllowMountName();
if (Utility.IsHostFsMountName(lrPath.Str)) if (Utility.IsHostFsMountName(lrPath.Value))
pathFlags.AllowWindowsPath(); pathFlags.AllowWindowsPath();
Result rc = outPath.InitializeWithReplaceUnc(lrPath.Str); Result rc = outPath.InitializeWithReplaceUnc(lrPath.Value);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = outPath.Normalize(pathFlags); rc = outPath.Normalize(pathFlags);
@ -170,7 +170,7 @@ internal class LocationResolverSet : IDisposable
rc = resolver.ResolveApplicationHtmlDocumentPath(out Lr.Path path, applicationId); rc = resolver.ResolveApplicationHtmlDocumentPath(out Lr.Path path, applicationId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
isDirectory = PathUtility.IsDirectoryPath(path.Str); isDirectory = PathUtility.IsDirectoryPath(path.Value);
return SetUpFsPath(ref outPath, in path); return SetUpFsPath(ref outPath, in path);
} }
@ -185,7 +185,7 @@ internal class LocationResolverSet : IDisposable
rc = resolver.ResolveProgramPath(out Lr.Path path, programId); rc = resolver.ResolveProgramPath(out Lr.Path path, programId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
isDirectory = PathUtility.IsDirectoryPath(path.Str); isDirectory = PathUtility.IsDirectoryPath(path.Value);
return SetUpFsPath(ref outPath, in path); return SetUpFsPath(ref outPath, in path);
} }
@ -200,7 +200,7 @@ internal class LocationResolverSet : IDisposable
rc = resolver.ResolveProgramPathForDebug(out Lr.Path path, programId); rc = resolver.ResolveProgramPathForDebug(out Lr.Path path, programId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
isDirectory = PathUtility.IsDirectoryPath(path.Str); isDirectory = PathUtility.IsDirectoryPath(path.Value);
return SetUpFsPath(ref outPath, in path); return SetUpFsPath(ref outPath, in path);
} }

View file

@ -474,7 +474,7 @@ public class NcaFileSystemServiceImpl
return ResultFs.PathNotFound.Log(); return ResultFs.PathNotFound.Log();
} }
if (StringUtils.GetLength(path, FsPath.MaxLength) == 0) if (StringUtils.GetLength(path, PathTool.EntryNameLengthMax) == 0)
{ {
shouldContinue = false; shouldContinue = false;
} }

View file

@ -1,42 +0,0 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Util;
namespace LibHac.FsSystem;
[StructLayout(LayoutKind.Sequential, Size = MaxLength + 1)]
public struct FsPath
{
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
public Span<byte> Str => SpanHelpers.AsByteSpan(ref this);
public static Result FromSpan(out FsPath fsPath, ReadOnlySpan<byte> path)
{
UnsafeHelpers.SkipParamInit(out fsPath);
// Ensure null terminator even if the creation fails for safety
fsPath.Str[MaxLength] = 0;
var sb = new U8StringBuilder(fsPath.Str);
bool overflowed = sb.Append(path).Overflowed;
return overflowed ? ResultFs.TooLongPath.Log() : Result.Success;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator U8Span(in FsPath value) => new U8Span(SpanHelpers.AsReadOnlyByteSpan(in value));
public override string ToString() => StringUtils.Utf8ZToString(Str);
}

View file

@ -1,16 +1,8 @@
using System; using LibHac.Common.FixedArrays;
using System.Diagnostics;
using System.Runtime.InteropServices;
using LibHac.Common;
namespace LibHac.FsSystem; namespace LibHac.FsSystem;
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
public struct Hash public struct Hash
{ {
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0; public Array32<byte> Value;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
public readonly ReadOnlySpan<byte> Bytes => SpanHelpers.AsReadOnlyByteSpan(in this);
public Span<byte> BytesMutable => SpanHelpers.AsByteSpan(ref this);
} }

View file

@ -359,7 +359,7 @@ public class PartitionFileSystemCore<T> : IFileSystem where T : unmanaged, IPart
U8Span name = ParentFs._metaData.GetName(CurrentIndex); U8Span name = ParentFs._metaData.GetName(CurrentIndex);
StringUtils.Copy(entryBuffer[i].Name.Items, name); StringUtils.Copy(entryBuffer[i].Name.Items, name);
entryBuffer[i].Name[FsPath.MaxLength] = 0; entryBuffer[i].Name[PathTool.EntryNameLengthMax] = 0;
CurrentIndex++; CurrentIndex++;
} }

View file

@ -1,6 +1,5 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Diag; using LibHac.Diag;
using LibHac.Fs; using LibHac.Fs;
@ -96,8 +95,6 @@ public class InitialProcessBinaryReader : IDisposable
return Result.Success; return Result.Success;
} }
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct IniHeader public struct IniHeader
{ {
public uint Magic; public uint Magic;

View file

@ -2,56 +2,57 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Common.FixedArrays;
namespace LibHac.Kernel; namespace LibHac.Kernel;
[StructLayout(LayoutKind.Explicit, Size = 0x100)] [StructLayout(LayoutKind.Sequential)]
public struct KipHeader public struct KipHeader
{ {
public const uint Kip1Magic = 0x3150494B; // KIP1 public static readonly uint Kip1Magic = 0x3150494B; // KIP1
public const int NameSize = 12; public static readonly int SegmentCount = 6;
public const int SegmentCount = 6;
[FieldOffset(0x00)] public uint Magic; public uint Magic;
[FieldOffset(0x04)] private byte _name; public Array12<byte> Name;
[FieldOffset(0x10)] public ulong ProgramId; public ulong ProgramId;
[FieldOffset(0x18)] public int Version; public int Version;
[FieldOffset(0x1C)] public byte Priority; public byte Priority;
[FieldOffset(0x1D)] public byte IdealCoreId; public byte IdealCoreId;
[FieldOffset(0x1F)] public Flag Flags; private byte _reserved1E;
public Flag Flags;
[FieldOffset(0x20)] public int TextMemoryOffset; public int TextMemoryOffset;
[FieldOffset(0x24)] public int TextSize; public int TextSize;
[FieldOffset(0x28)] public int TextFileSize; public int TextFileSize;
[FieldOffset(0x2C)] public int AffinityMask; public int AffinityMask;
[FieldOffset(0x30)] public int RoMemoryOffset; public int RoMemoryOffset;
[FieldOffset(0x34)] public int RoSize; public int RoSize;
[FieldOffset(0x38)] public int RoFileSize; public int RoFileSize;
[FieldOffset(0x3C)] public int StackSize; public int StackSize;
[FieldOffset(0x40)] public int DataMemoryOffset; public int DataMemoryOffset;
[FieldOffset(0x44)] public int DataSize; public int DataSize;
[FieldOffset(0x48)] public int DataFileSize; public int DataFileSize;
private byte _reserved4C;
[FieldOffset(0x50)] public int BssMemoryOffset; public int BssMemoryOffset;
[FieldOffset(0x54)] public int BssSize; public int BssSize;
[FieldOffset(0x58)] public int BssFileSize; public int BssFileSize;
private byte _reserved5C;
[FieldOffset(0x80)] private uint _capabilities; private Array2<SegmentHeader> _unusedSegmentHeaders;
public Span<byte> Name => SpanHelpers.CreateSpan(ref _name, NameSize); public Array32<uint> Capabilities;
public Span<SegmentHeader> Segments => public Span<SegmentHeader> Segments =>
SpanHelpers.CreateSpan(ref Unsafe.As<int, SegmentHeader>(ref TextMemoryOffset), SegmentCount); SpanHelpers.CreateSpan(ref Unsafe.As<int, SegmentHeader>(ref TextMemoryOffset), SegmentCount);
public Span<uint> Capabilities => SpanHelpers.CreateSpan(ref _capabilities, 0x80 / sizeof(uint));
public bool IsValid => Magic == Kip1Magic; public bool IsValid => Magic == Kip1Magic;
[Flags] [Flags]
@ -65,11 +66,12 @@ public struct KipHeader
UseSecureMemory = 1 << 5 UseSecureMemory = 1 << 5
} }
[StructLayout(LayoutKind.Sequential, Size = 0x10)] [StructLayout(LayoutKind.Sequential)]
public struct SegmentHeader public struct SegmentHeader
{ {
public int MemoryOffset; public int MemoryOffset;
public int Size; public int Size;
public int FileSize; public int FileSize;
private int _unused;
} }
} }

View file

@ -1,24 +1,13 @@
using System; using System.Runtime.CompilerServices;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs; using LibHac.Common.FixedArrays;
using LibHac.Util; using LibHac.Util;
namespace LibHac.Lr; namespace LibHac.Lr;
[StructLayout(LayoutKind.Sequential, Size = PathTool.EntryNameLengthMax)]
public struct Path public struct Path
{ {
#if DEBUG public Array768<byte> Value;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding200;
#endif
public readonly ReadOnlySpan<byte> Str => SpanHelpers.AsReadOnlyByteSpan(in this);
public Span<byte> StrMutable => SpanHelpers.AsByteSpan(ref this);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InitEmpty(out Path path) public static void InitEmpty(out Path path)
@ -30,5 +19,5 @@ public struct Path
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator U8Span(in Path value) => new U8Span(SpanHelpers.AsReadOnlyByteSpan(in value)); public static implicit operator U8Span(in Path value) => new U8Span(SpanHelpers.AsReadOnlyByteSpan(in value));
public readonly override string ToString() => StringUtils.Utf8ZToString(Str); public readonly override string ToString() => StringUtils.Utf8ZToString(Value);
} }

View file

@ -1,188 +1,302 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Ncm; using LibHac.Common.FixedArrays;
namespace LibHac.Ns; namespace LibHac.Ns;
[StructLayout(LayoutKind.Explicit, Size = 0x4000)]
public struct ApplicationControlProperty public struct ApplicationControlProperty
{ {
private const int TitleCount = 0x10; public Array16<ApplicationTitle> Title;
private const int IsbnSize = 0x25; public Array37<byte> Isbn;
private const int RatingAgeCount = 0x20; public StartupUserAccountValue StartupUserAccount;
private const int DisplayVersionSize = 0x10; public UserAccountSwitchLockValue UserAccountSwitchLock;
private const int ApplicationErrorCodeCategorySize = 8; public AddOnContentRegistrationTypeValue AddOnContentRegistrationType;
private const int LocalCommunicationIdCount = 8; public AttributeFlagValue AttributeFlag;
private const int Reserved30F3Size = 3; public uint SupportedLanguageFlag;
private const int BcatPassphraseSize = 0x41; public ParentalControlFlagValue ParentalControlFlag;
private const int ReservedForUserAccountSaveDataOperationSize = 6; public ScreenshotValue Screenshot;
private const int PlayLogQueryableApplicationIdCount = 0x10; public VideoCaptureValue VideoCapture;
private const int ReceivableDataConfigurationCount = 0x10; public DataLossConfirmationValue DataLossConfirmation;
public PlayLogPolicyValue PlayLogPolicy;
public ulong PresenceGroupId;
public Array32<sbyte> RatingAge;
public Array16<byte> DisplayVersion;
public ulong AddOnContentBaseId;
public ulong SaveDataOwnerId;
public long UserAccountSaveDataSize;
public long UserAccountSaveDataJournalSize;
public long DeviceSaveDataSize;
public long DeviceSaveDataJournalSize;
public long BcatDeliveryCacheStorageSize;
public Array8<byte> ApplicationErrorCodeCategory;
public Array8<ulong> LocalCommunicationId;
public LogoTypeValue LogoType;
public LogoHandlingValue LogoHandling;
public RuntimeAddOnContentInstallValue RuntimeAddOnContentInstall;
public RuntimeParameterDeliveryValue RuntimeParameterDelivery;
public Array2<byte> Reserved30F4;
public CrashReportValue CrashReport;
public HdcpValue Hdcp;
public ulong SeedForPseudoDeviceId;
public Array65<byte> BcatPassphrase;
public StartupUserAccountOptionFlagValue StartupUserAccountOption;
public Array6<byte> ReservedForUserAccountSaveDataOperation;
public long UserAccountSaveDataSizeMax;
public long UserAccountSaveDataJournalSizeMax;
public long DeviceSaveDataSizeMax;
public long DeviceSaveDataJournalSizeMax;
public long TemporaryStorageSize;
public long CacheStorageSize;
public long CacheStorageJournalSize;
public long CacheStorageDataAndJournalSizeMax;
public ushort CacheStorageIndexMax;
public byte Reserved318A;
public byte RuntimeUpgrade;
public uint SupportingLimitedLicenses;
public Array16<ulong> PlayLogQueryableApplicationId;
public PlayLogQueryCapabilityValue PlayLogQueryCapability;
public RepairFlagValue RepairFlag;
public byte ProgramIndex;
public RequiredNetworkServiceLicenseOnLaunchValue RequiredNetworkServiceLicenseOnLaunchFlag;
public Array4<byte> Reserved3214;
public ApplicationNeighborDetectionClientConfiguration NeighborDetectionClientConfiguration;
public ApplicationJitConfiguration JitConfiguration;
public RequiredAddOnContentsSetBinaryDescriptor RequiredAddOnContentsSetBinaryDescriptors;
public PlayReportPermissionValue PlayReportPermission;
public CrashScreenshotForProdValue CrashScreenshotForProd;
public CrashScreenshotForDevValue CrashScreenshotForDev;
public byte ContentsAvailabilityTransitionPolicy;
public Array4<byte> Reserved3404;
public AccessibleLaunchRequiredVersionValue AccessibleLaunchRequiredVersion;
public Array3000<byte> Reserved3448;
[FieldOffset(0x0000)] private byte _titles; public struct ApplicationTitle
[FieldOffset(0x3000)] private byte _isbn; {
[FieldOffset(0x3025)] public StartupUserAccount StartupUserAccount; private Array512<byte> _name;
[FieldOffset(0x3026)] public byte UserAccountSwitchLock; private Array256<byte> _publisher;
[FieldOffset(0x3027)] public byte AddOnContentRegistrationType;
[FieldOffset(0x3028)] public ApplicationAttribute ApplicationAttribute;
[FieldOffset(0x302C)] public uint SupportedLanguages;
[FieldOffset(0x3030)] public ParentalControlFlagValue ParentalControl;
[FieldOffset(0x3034)] public ScreenshotValue Screenshot;
[FieldOffset(0x3035)] public VideoCaptureValue VideoCaptureMode;
[FieldOffset(0x3036)] public byte DataLossConfirmation;
[FieldOffset(0x3037)] public byte PlayLogPolicy;
[FieldOffset(0x3038)] public ulong PresenceGroupId;
[FieldOffset(0x3040)] private sbyte _ratingAge;
[FieldOffset(0x3060)] private byte _displayVersion;
[FieldOffset(0x3070)] public ulong AddOnContentBaseId;
[FieldOffset(0x3078)] public ProgramId SaveDataOwnerId;
[FieldOffset(0x3080)] public long UserAccountSaveDataSize;
[FieldOffset(0x3088)] public long UserAccountSaveDataJournalSize;
[FieldOffset(0x3090)] public long DeviceSaveDataSize;
[FieldOffset(0x3098)] public long DeviceSaveDataJournalSize;
[FieldOffset(0x30A0)] public long BcatDeliveryCacheStorageSize;
[FieldOffset(0x30A8)] private byte _applicationErrorCodeCategory;
[FieldOffset(0x30B0)] private ulong _localCommunicationIds;
[FieldOffset(0x30F0)] public LogoType LogoType;
[FieldOffset(0x30F1)] public LogoHandling LogoHandling;
[FieldOffset(0x30F2)] public byte RuntimeAddOnContentInstall;
[FieldOffset(0x30F3)] public byte _reserved30F3;
[FieldOffset(0x30F6)] public byte CrashReport;
[FieldOffset(0x30F7)] public byte Hdcp;
[FieldOffset(0x30F8)] public ulong SeedForPseudoDeviceId;
[FieldOffset(0x3100)] private byte _bcatPassphrase;
[FieldOffset(0x3141)] public byte StartupUserAccountOption;
[FieldOffset(0x3142)] private byte _reservedForUserAccountSaveDataOperation;
[FieldOffset(0x3148)] public long UserAccountSaveDataMaxSize;
[FieldOffset(0x3150)] public long UserAccountSaveDataMaxJournalSize;
[FieldOffset(0x3158)] public long DeviceSaveDataMaxSize;
[FieldOffset(0x3160)] public long DeviceSaveDataMaxJournalSize;
[FieldOffset(0x3168)] public long TemporaryStorageSize;
[FieldOffset(0x3170)] public long CacheStorageSize;
[FieldOffset(0x3178)] public long CacheStorageJournalSize;
[FieldOffset(0x3180)] public long CacheStorageMaxSizeAndMaxJournalSize;
[FieldOffset(0x3188)] public long CacheStorageMaxIndex;
[FieldOffset(0x3190)] private ulong _playLogQueryableApplicationId;
[FieldOffset(0x3210)] public PlayLogQueryCapability PlayLogQueryCapability;
[FieldOffset(0x3211)] public byte RepairFlag;
[FieldOffset(0x3212)] public byte ProgramIndex;
[FieldOffset(0x3213)] public byte RequiredNetworkServiceLicenseOnLaunchFlag;
[FieldOffset(0x3214)] public uint Reserved3214;
[FieldOffset(0x3218)] public ApplicationControlDataConfiguration SendDataConfiguration;
[FieldOffset(0x3230)] private ApplicationControlDataConfiguration _receivableDataConfigurations;
[FieldOffset(0x32B0)] public ulong JitConfigurationFlag;
[FieldOffset(0x32B8)] public long MemorySize;
[FieldOffset(0x3000), DebuggerBrowsable(DebuggerBrowsableState.Never)] private Padding200 _padding1; public U8SpanMutable Name => new U8SpanMutable(_name.Items);
[FieldOffset(0x3200), DebuggerBrowsable(DebuggerBrowsableState.Never)] private Padding100 _padding2; public U8SpanMutable Publisher => new U8SpanMutable(_publisher.Items);
}
public Span<ApplicationControlTitle> Titles => SpanHelpers.CreateSpan(ref Unsafe.As<byte, ApplicationControlTitle>(ref _titles), TitleCount); public struct ApplicationNeighborDetectionClientConfiguration
public U8SpanMutable Isbn => new U8SpanMutable(SpanHelpers.CreateSpan(ref _isbn, IsbnSize)); {
public Span<sbyte> RatingAge => SpanHelpers.CreateSpan(ref _ratingAge, RatingAgeCount); public ApplicationNeighborDetectionGroupConfiguration SendGroupConfiguration;
public U8SpanMutable DisplayVersion => new U8SpanMutable(SpanHelpers.CreateSpan(ref _displayVersion, DisplayVersionSize)); public Array16<ApplicationNeighborDetectionGroupConfiguration> ReceivableGroupConfigurations;
}
public U8SpanMutable ApplicationErrorCodeCategory => public struct ApplicationNeighborDetectionGroupConfiguration
new U8SpanMutable(SpanHelpers.CreateSpan(ref _applicationErrorCodeCategory, {
ApplicationErrorCodeCategorySize)); public ulong GroupId;
public Array16<byte> Key;
}
public Span<ulong> LocalCommunicationIds => SpanHelpers.CreateSpan(ref _localCommunicationIds, LocalCommunicationIdCount); public struct ApplicationJitConfiguration
public Span<byte> Reserved30F3 => SpanHelpers.CreateSpan(ref _reserved30F3, Reserved30F3Size); {
public U8SpanMutable BcatPassphrase => new U8SpanMutable(SpanHelpers.CreateSpan(ref _bcatPassphrase, BcatPassphraseSize)); public JitConfigurationFlag Flags;
public long MemorySize;
}
public Span<byte> ReservedForUserAccountSaveDataOperation => public struct RequiredAddOnContentsSetBinaryDescriptor
SpanHelpers.CreateSpan(ref _reservedForUserAccountSaveDataOperation, {
ReservedForUserAccountSaveDataOperationSize); public Array32<ushort> Descriptors;
}
public Span<ulong> PlayLogQueryableApplicationId => public struct AccessibleLaunchRequiredVersionValue
SpanHelpers.CreateSpan(ref _playLogQueryableApplicationId, PlayLogQueryableApplicationIdCount); {
public Array8<ulong> ApplicationId;
}
public Span<ApplicationControlDataConfiguration> ReceivableDataConfigurations => public enum Language
SpanHelpers.CreateSpan(ref _receivableDataConfigurations, ReceivableDataConfigurationCount); {
} AmericanEnglish = 0,
BritishEnglish = 1,
Japanese = 2,
French = 3,
German = 4,
LatinAmericanSpanish = 5,
Spanish = 6,
Italian = 7,
Dutch = 8,
CanadianFrench = 9,
Portuguese = 10,
Russian = 11,
Korean = 12,
TraditionalChinese = 13,
SimplifiedChinese = 14,
BrazilianPortuguese = 15
}
[StructLayout(LayoutKind.Explicit, Size = 0x300)] [SuppressMessage("ReSharper", "InconsistentNaming")]
public struct ApplicationControlTitle public enum Organization
{ {
private const int NameLength = 0x200; CERO = 0,
private const int PublisherLength = 0x100; GRACGCRB = 1,
GSRMR = 2,
ESRB = 3,
ClassInd = 4,
USK = 5,
PEGI = 6,
PEGIPortugal = 7,
PEGIBBFC = 8,
Russian = 9,
ACB = 10,
OFLC = 11,
IARCGeneric = 12
}
[FieldOffset(0x000)] private byte _name; public enum StartupUserAccountValue : byte
[FieldOffset(0x200)] private byte _publisher; {
[FieldOffset(0x000), DebuggerBrowsable(DebuggerBrowsableState.Never)]
private Padding200 _padding0;
[FieldOffset(0x200), DebuggerBrowsable(DebuggerBrowsableState.Never)]
private Padding100 _padding200;
public U8SpanMutable Name => new U8SpanMutable(SpanHelpers.CreateSpan(ref _name, NameLength));
public U8SpanMutable Publisher => new U8SpanMutable(SpanHelpers.CreateSpan(ref _publisher, PublisherLength));
}
[StructLayout(LayoutKind.Explicit, Size = 0x18)]
public struct ApplicationControlDataConfiguration
{
[FieldOffset(0)] public ulong Id;
[FieldOffset(8)] private byte _key;
[FieldOffset(8), DebuggerBrowsable(DebuggerBrowsableState.Never)]
private Padding10 _keyPadding;
public Span<byte> Key => SpanHelpers.CreateSpan(ref _key, 0x10);
}
public enum StartupUserAccount : byte
{
None = 0, None = 0,
Required = 1, Required = 1,
RequiredWithNetworkServiceAccountAvailable = 2 RequiredWithNetworkServiceAccountAvailable = 2
} }
public enum LogoHandling : byte public enum UserAccountSwitchLockValue : byte
{ {
Auto = 0, Disable = 0,
Manual = 1 Enable = 1
} }
public enum LogoType : byte public enum AddOnContentRegistrationTypeValue : byte
{ {
AllOnLaunch = 0,
OnDemand = 1
}
[Flags]
public enum AttributeFlagValue
{
None = 0,
Demo = 1 << 0,
RetailInteractiveDisplay = 1 << 1,
}
public enum ParentalControlFlagValue
{
None = 0,
FreeCommunication = 1
}
public enum ScreenshotValue : byte
{
Allow = 0,
Deny = 1
}
public enum VideoCaptureValue : byte
{
Disable = 0,
Manual = 1,
Enable = 2
}
public enum DataLossConfirmationValue : byte
{
None = 0,
Required = 1
}
public enum PlayLogPolicyValue : byte
{
Open = 0,
LogOnly = 1,
None = 2,
Closed = 3,
All = 0
}
public enum LogoTypeValue : byte
{
LicensedByNintendo = 0, LicensedByNintendo = 0,
DistributedByNintendo = 1, DistributedByNintendo = 1,
Nintendo = 2 Nintendo = 2
} }
[Flags] public enum LogoHandlingValue : byte
public enum ApplicationAttribute {
{ Auto = 0,
Manual = 1
}
public enum RuntimeAddOnContentInstallValue : byte
{
Deny = 0,
AllowAppend = 1,
AllowAppendButDontDownloadWhenUsingNetwork = 2
}
public enum RuntimeParameterDeliveryValue : byte
{
Always = 0,
AlwaysIfUserStateMatched = 1,
OnRestart = 2
}
public enum CrashReportValue : byte
{
Deny = 0,
Allow = 1
}
public enum HdcpValue : byte
{
None = 0, None = 0,
Demo = 1 Required = 1
} }
public enum PlayLogQueryCapability : byte [Flags]
{ public enum StartupUserAccountOptionFlagValue : byte
{
None = 0,
IsOptional = 1 << 0
}
public enum PlayLogQueryCapabilityValue : byte
{
None = 0, None = 0,
WhiteList = 1, WhiteList = 1,
All = 2 All = 2
} }
public enum ParentalControlFlagValue [Flags]
{ public enum RepairFlagValue : byte
{
None = 0, None = 0,
FreeCommunication = 1 SuppressGameCardAccess = 1 << 0
} }
public enum ScreenshotValue : byte [Flags]
{ public enum RequiredNetworkServiceLicenseOnLaunchValue : byte
Allow = 0, {
Deny = 1 None = 0,
} Common = 1 << 0
}
public enum VideoCaptureValue : byte [Flags]
{ public enum JitConfigurationFlag : ulong
{
None = 0,
Enabled = 1 << 0
}
[Flags]
public enum PlayReportPermissionValue : byte
{
None = 0,
TargetMarketing = 1 << 0
}
public enum CrashScreenshotForProdValue : byte
{
Deny = 0, Deny = 0,
Allow = 1, Allow = 1
Automatic = 2 }
public enum CrashScreenshotForDevValue : byte
{
Deny = 0,
Allow = 1
}
} }

View file

@ -229,7 +229,7 @@ public class SwitchFs : IDisposable
control.Get.Read(out _, 0, title.Control.ByteSpan).ThrowIfFailure(); control.Get.Read(out _, 0, title.Control.ByteSpan).ThrowIfFailure();
} }
foreach (ref ApplicationControlTitle desc in title.Control.Value.Titles) foreach (ref readonly ApplicationControlProperty.ApplicationTitle desc in title.Control.Value.Title.ItemsRo)
{ {
if (!desc.Name.IsEmpty()) if (!desc.Name.IsEmpty())
{ {

View file

@ -0,0 +1,19 @@
using System.Runtime.CompilerServices;
using LibHac.FsSystem;
using Xunit;
using static LibHac.Tests.Common.Layout;
namespace LibHac.Tests.FsSystem;
public class TypeLayoutTests
{
[Fact]
public static void Hash_Layout()
{
var s = new Hash();
Assert.Equal(0x20, Unsafe.SizeOf<Hash>());
Assert.Equal(0x0, GetOffset(in s, in s.Value));
}
}

View file

@ -0,0 +1,69 @@
using System.Runtime.CompilerServices;
using LibHac.Kernel;
using Xunit;
using static LibHac.Kernel.InitialProcessBinaryReader;
using static LibHac.Kernel.KipHeader;
using static LibHac.Tests.Common.Layout;
namespace LibHac.Tests.Kernel;
public class TypeLayoutTests
{
[Fact]
public static void IniHeader_Layout()
{
var s = new IniHeader();
Assert.Equal(0x10, Unsafe.SizeOf<IniHeader>());
Assert.Equal(0x0, GetOffset(in s, in s.Magic));
Assert.Equal(0x4, GetOffset(in s, in s.Size));
Assert.Equal(0x8, GetOffset(in s, in s.ProcessCount));
Assert.Equal(0xC, GetOffset(in s, in s.Reserved));
}
[Fact]
public static void KipHeader_Layout()
{
var s = new KipHeader();
Assert.Equal(0x100, Unsafe.SizeOf<KipHeader>());
Assert.Equal(0x00, GetOffset(in s, in s.Magic));
Assert.Equal(0x04, GetOffset(in s, in s.Name));
Assert.Equal(0x10, GetOffset(in s, in s.ProgramId));
Assert.Equal(0x18, GetOffset(in s, in s.Version));
Assert.Equal(0x1C, GetOffset(in s, in s.Priority));
Assert.Equal(0x1D, GetOffset(in s, in s.IdealCoreId));
Assert.Equal(0x1F, GetOffset(in s, in s.Flags));
Assert.Equal(0x20, GetOffset(in s, in s.TextMemoryOffset));
Assert.Equal(0x24, GetOffset(in s, in s.TextSize));
Assert.Equal(0x28, GetOffset(in s, in s.TextFileSize));
Assert.Equal(0x2C, GetOffset(in s, in s.AffinityMask));
Assert.Equal(0x30, GetOffset(in s, in s.RoMemoryOffset));
Assert.Equal(0x34, GetOffset(in s, in s.RoSize));
Assert.Equal(0x38, GetOffset(in s, in s.RoFileSize));
Assert.Equal(0x3C, GetOffset(in s, in s.StackSize));
Assert.Equal(0x40, GetOffset(in s, in s.DataMemoryOffset));
Assert.Equal(0x44, GetOffset(in s, in s.DataSize));
Assert.Equal(0x48, GetOffset(in s, in s.DataFileSize));
Assert.Equal(0x50, GetOffset(in s, in s.BssMemoryOffset));
Assert.Equal(0x54, GetOffset(in s, in s.BssSize));
Assert.Equal(0x58, GetOffset(in s, in s.BssFileSize));
Assert.Equal(0x80, GetOffset(in s, in s.Capabilities));
Assert.Equal(0x20, GetOffset(in s, in s.Segments[0]));
}
[Fact]
public static void KipSegmentHeader_Layout()
{
var s = new SegmentHeader();
Assert.Equal(0x10, Unsafe.SizeOf<SegmentHeader>());
Assert.Equal(0x0, GetOffset(in s, in s.MemoryOffset));
Assert.Equal(0x4, GetOffset(in s, in s.Size));
Assert.Equal(0x8, GetOffset(in s, in s.FileSize));
}
}

View file

@ -0,0 +1,19 @@
using System.Runtime.CompilerServices;
using LibHac.Lr;
using Xunit;
using static LibHac.Tests.Common.Layout;
namespace LibHac.Tests.Lr;
public class TypeLayoutTests
{
[Fact]
public static void IniHeader_Layout()
{
var s = new Path();
Assert.Equal(0x300, Unsafe.SizeOf<Path>());
Assert.Equal(0x0, GetOffset(in s, in s.Value));
}
}

View file

@ -0,0 +1,126 @@
using System.Runtime.CompilerServices;
using LibHac.Ns;
using Xunit;
using static LibHac.Ns.ApplicationControlProperty;
using static LibHac.Tests.Common.Layout;
namespace LibHac.Tests.Ns;
public class TypeLayoutTests
{
[Fact]
public static void ApplicationTitle_Layout()
{
var s = new ApplicationTitle();
Assert.Equal(0x300, Unsafe.SizeOf<ApplicationTitle>());
Assert.Equal(0x000, GetOffset(in s, in s.Name.Value[0]));
Assert.Equal(0x200, GetOffset(in s, in s.Publisher.Value[0]));
}
[Fact]
public static void ApplicationNeighborDetectionGroupConfiguration_Layout()
{
var s = new ApplicationNeighborDetectionGroupConfiguration();
Assert.Equal(0x18, Unsafe.SizeOf<ApplicationNeighborDetectionGroupConfiguration>());
Assert.Equal(0x0, GetOffset(in s, in s.GroupId));
Assert.Equal(0x8, GetOffset(in s, in s.Key));
}
[Fact]
public static void ApplicationNeighborDetectionClientConfiguration_Layout()
{
var s = new ApplicationNeighborDetectionClientConfiguration();
Assert.Equal(0x198, Unsafe.SizeOf<ApplicationNeighborDetectionClientConfiguration>());
Assert.Equal(0x00, GetOffset(in s, in s.SendGroupConfiguration));
Assert.Equal(0x18, GetOffset(in s, in s.ReceivableGroupConfigurations));
}
[Fact]
public static void ApplicationJitConfiguration_Layout()
{
var s = new ApplicationJitConfiguration();
Assert.Equal(0x10, Unsafe.SizeOf<ApplicationJitConfiguration>());
Assert.Equal(0x0, GetOffset(in s, in s.Flags));
Assert.Equal(0x8, GetOffset(in s, in s.MemorySize));
}
[Fact]
public static void ApplicationControlProperty_Layout()
{
var s = new ApplicationControlProperty();
Assert.Equal(0x4000, Unsafe.SizeOf<ApplicationControlProperty>());
Assert.Equal(0x0000, GetOffset(in s, in s.Title));
Assert.Equal(0x3000, GetOffset(in s, in s.Isbn));
Assert.Equal(0x3025, GetOffset(in s, in s.StartupUserAccount));
Assert.Equal(0x3026, GetOffset(in s, in s.UserAccountSwitchLock));
Assert.Equal(0x3027, GetOffset(in s, in s.AddOnContentRegistrationType));
Assert.Equal(0x3028, GetOffset(in s, in s.AttributeFlag));
Assert.Equal(0x302C, GetOffset(in s, in s.SupportedLanguageFlag));
Assert.Equal(0x3030, GetOffset(in s, in s.ParentalControlFlag));
Assert.Equal(0x3034, GetOffset(in s, in s.Screenshot));
Assert.Equal(0x3035, GetOffset(in s, in s.VideoCapture));
Assert.Equal(0x3036, GetOffset(in s, in s.DataLossConfirmation));
Assert.Equal(0x3037, GetOffset(in s, in s.PlayLogPolicy));
Assert.Equal(0x3038, GetOffset(in s, in s.PresenceGroupId));
Assert.Equal(0x3040, GetOffset(in s, in s.RatingAge));
Assert.Equal(0x3060, GetOffset(in s, in s.DisplayVersion));
Assert.Equal(0x3070, GetOffset(in s, in s.AddOnContentBaseId));
Assert.Equal(0x3078, GetOffset(in s, in s.SaveDataOwnerId));
Assert.Equal(0x3080, GetOffset(in s, in s.UserAccountSaveDataSize));
Assert.Equal(0x3088, GetOffset(in s, in s.UserAccountSaveDataJournalSize));
Assert.Equal(0x3090, GetOffset(in s, in s.DeviceSaveDataSize));
Assert.Equal(0x3098, GetOffset(in s, in s.DeviceSaveDataJournalSize));
Assert.Equal(0x30A0, GetOffset(in s, in s.BcatDeliveryCacheStorageSize));
Assert.Equal(0x30A8, GetOffset(in s, in s.ApplicationErrorCodeCategory));
Assert.Equal(0x30B0, GetOffset(in s, in s.LocalCommunicationId));
Assert.Equal(0x30F0, GetOffset(in s, in s.LogoType));
Assert.Equal(0x30F1, GetOffset(in s, in s.LogoHandling));
Assert.Equal(0x30F2, GetOffset(in s, in s.RuntimeAddOnContentInstall));
Assert.Equal(0x30F3, GetOffset(in s, in s.RuntimeParameterDelivery));
Assert.Equal(0x30F4, GetOffset(in s, in s.Reserved30F4));
Assert.Equal(0x30F6, GetOffset(in s, in s.CrashReport));
Assert.Equal(0x30F7, GetOffset(in s, in s.Hdcp));
Assert.Equal(0x30F8, GetOffset(in s, in s.SeedForPseudoDeviceId));
Assert.Equal(0x3100, GetOffset(in s, in s.BcatPassphrase));
Assert.Equal(0x3141, GetOffset(in s, in s.StartupUserAccountOption));
Assert.Equal(0x3142, GetOffset(in s, in s.ReservedForUserAccountSaveDataOperation));
Assert.Equal(0x3148, GetOffset(in s, in s.UserAccountSaveDataSizeMax));
Assert.Equal(0x3150, GetOffset(in s, in s.UserAccountSaveDataJournalSizeMax));
Assert.Equal(0x3158, GetOffset(in s, in s.DeviceSaveDataSizeMax));
Assert.Equal(0x3160, GetOffset(in s, in s.DeviceSaveDataJournalSizeMax));
Assert.Equal(0x3168, GetOffset(in s, in s.TemporaryStorageSize));
Assert.Equal(0x3170, GetOffset(in s, in s.CacheStorageSize));
Assert.Equal(0x3178, GetOffset(in s, in s.CacheStorageJournalSize));
Assert.Equal(0x3180, GetOffset(in s, in s.CacheStorageDataAndJournalSizeMax));
Assert.Equal(0x3188, GetOffset(in s, in s.CacheStorageIndexMax));
Assert.Equal(0x318A, GetOffset(in s, in s.Reserved318A));
Assert.Equal(0x318B, GetOffset(in s, in s.RuntimeUpgrade));
Assert.Equal(0x318C, GetOffset(in s, in s.SupportingLimitedLicenses));
Assert.Equal(0x3190, GetOffset(in s, in s.PlayLogQueryableApplicationId));
Assert.Equal(0x3210, GetOffset(in s, in s.PlayLogQueryCapability));
Assert.Equal(0x3211, GetOffset(in s, in s.RepairFlag));
Assert.Equal(0x3212, GetOffset(in s, in s.ProgramIndex));
Assert.Equal(0x3213, GetOffset(in s, in s.RequiredNetworkServiceLicenseOnLaunchFlag));
Assert.Equal(0x3214, GetOffset(in s, in s.Reserved3214));
Assert.Equal(0x3218, GetOffset(in s, in s.NeighborDetectionClientConfiguration));
Assert.Equal(0x33B0, GetOffset(in s, in s.JitConfiguration));
Assert.Equal(0x33C0, GetOffset(in s, in s.RequiredAddOnContentsSetBinaryDescriptors));
Assert.Equal(0x3400, GetOffset(in s, in s.PlayReportPermission));
Assert.Equal(0x3401, GetOffset(in s, in s.CrashScreenshotForProd));
Assert.Equal(0x3402, GetOffset(in s, in s.CrashScreenshotForDev));
Assert.Equal(0x3403, GetOffset(in s, in s.ContentsAvailabilityTransitionPolicy));
Assert.Equal(0x3404, GetOffset(in s, in s.Reserved3404));
Assert.Equal(0x3408, GetOffset(in s, in s.AccessibleLaunchRequiredVersion));
Assert.Equal(0x3448, GetOffset(in s, in s.Reserved3448));
}
}