Add EnsureApplicationSaveData and dependencies

- CreateBcatSaveData
- CreateDeviceSaveData
- CreateTemporaryStorage
- ApplicationControlProperty struct
This commit is contained in:
Alex Barney 2019-10-28 00:13:36 -05:00
parent df8dab542a
commit bfaf95026a
7 changed files with 602 additions and 3 deletions

59
src/LibHac/Account/Uid.cs Normal file
View file

@ -0,0 +1,59 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using LibHac.Common;
namespace LibHac.Account
{
[DebuggerDisplay("0x{ToString(),nq}")]
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct Uid : IEquatable<Uid>, IComparable<Uid>, IComparable
{
public static Uid Zero => default;
public readonly Id128 Id;
public Uid(ulong high, ulong low)
{
Id = new Id128(high, low);
}
public Uid(ReadOnlySpan<byte> uid)
{
Id = new Id128(uid);
}
public override string ToString()
{
return $"{Id.High:X16}{Id.Low:X16}";
}
public bool Equals(Uid other) => Id == other.Id;
public override bool Equals(object obj) => obj is Uid other && Equals(other);
public override int GetHashCode() => Id.GetHashCode();
public int CompareTo(Uid other) => Id.CompareTo(other.Id);
public int CompareTo(object obj)
{
if (obj is null) return 1;
return obj is Uid other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(Uid)}");
}
public void ToBytes(Span<byte> output) => Id.ToBytes(output);
public ReadOnlySpan<byte> AsBytes()
{
return SpanHelpers.AsByteSpan(ref this);
}
public static bool operator ==(Uid left, Uid right) => left.Equals(right);
public static bool operator !=(Uid left, Uid right) => !left.Equals(right);
public static bool operator <(Uid left, Uid right) => left.CompareTo(right) < 0;
public static bool operator >(Uid left, Uid right) => left.CompareTo(right) > 0;
public static bool operator <=(Uid left, Uid right) => left.CompareTo(right) <= 0;
public static bool operator >=(Uid left, Uid right) => left.CompareTo(right) >= 0;
}
}

View file

@ -6,6 +6,13 @@ namespace LibHac.Common
// In order for the Visual Studio debugger to accurately display a struct, every offset // 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. // 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. // 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)] [StructLayout(LayoutKind.Sequential, Size = 0x20)]
internal struct Padding20 internal struct Padding20
{ {
@ -35,4 +42,11 @@ namespace LibHac.Common
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding00; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding00;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding80; [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

@ -0,0 +1,269 @@
using System.Diagnostics.CodeAnalysis;
using LibHac.Account;
using LibHac.Fs.Shim;
using LibHac.FsSystem.Save;
using LibHac.Ncm;
using LibHac.Ns;
namespace LibHac.Fs
{
public static class ApplicationSaveDataManagement
{
public static Result EnsureApplicationSaveData(FileSystemClient fs, out long requiredSize, TitleId applicationId,
ref ApplicationControlProperty nacp, ref Uid uid)
{
requiredSize = default;
long requiredSizeSum = 0;
if (uid != Uid.Zero && nacp.UserAccountSaveDataSize > 0)
{
var filter = new SaveDataFilter();
filter.SetTitleId(applicationId);
filter.SetSaveDataType(SaveDataType.SaveData);
filter.SetUserId(new UserId(uid.Id.High, uid.Id.Low));
Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter);
if (rc.IsSuccess())
{
rc = ExtendSaveDataIfNeeded(fs, out long requiredSizeUser, SaveDataSpaceId.User,
saveDataInfo.SaveDataId, nacp.UserAccountSaveDataSize, nacp.UserAccountSaveDataJournalSize);
if (rc.IsFailure())
{
if (!ResultRangeFs.InsufficientFreeSpace.Contains(rc))
{
return rc;
}
requiredSizeSum = requiredSizeUser;
}
}
else if (rc != ResultFs.TargetNotFound)
{
return rc;
}
else
{
UserId userId = ConvertAccountUidToFsUserId(uid);
Result createRc = fs.CreateSaveData(applicationId, userId, nacp.SaveDataOwnerId,
nacp.UserAccountSaveDataSize, nacp.UserAccountSaveDataJournalSize, 0);
if (createRc.IsFailure())
{
if (ResultRangeFs.InsufficientFreeSpace.Contains(createRc))
{
// todo: Call QuerySaveDataTotalSize and assign the value to requiredSizeSum
requiredSizeSum = 0;
}
else if (createRc == ResultFs.PathAlreadyExists)
{
requiredSizeSum = 0;
}
else
{
return createRc;
}
}
}
}
if (nacp.DeviceSaveDataSize > 0)
{
var filter = new SaveDataFilter();
filter.SetTitleId(applicationId);
filter.SetSaveDataType(SaveDataType.DeviceSaveData);
Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter);
if (rc.IsSuccess())
{
rc = ExtendSaveDataIfNeeded(fs, out long requiredSizeDevice, SaveDataSpaceId.User,
saveDataInfo.SaveDataId, nacp.DeviceSaveDataSize, nacp.DeviceSaveDataJournalSize);
if (rc.IsFailure())
{
if (!ResultRangeFs.InsufficientFreeSpace.Contains(rc))
{
return rc;
}
requiredSizeSum += requiredSizeDevice;
}
}
else if (rc != ResultFs.TargetNotFound)
{
return rc;
}
else
{
Result createRc = fs.CreateDeviceSaveData(applicationId, nacp.SaveDataOwnerId,
nacp.DeviceSaveDataSize, nacp.DeviceSaveDataJournalSize, 0);
if (createRc.IsFailure())
{
if (ResultRangeFs.InsufficientFreeSpace.Contains(createRc))
{
// todo: Call QuerySaveDataTotalSize and add the value to requiredSizeSum
requiredSizeSum += 0;
}
else if (createRc == ResultFs.PathAlreadyExists)
{
requiredSizeSum += 0;
}
else
{
return createRc;
}
}
}
}
Result bcatRc = EnsureApplicationBcatDeliveryCacheStorageImpl(fs,
out long requiredSizeBcat, applicationId, ref nacp);
if (bcatRc.IsFailure())
{
if (!ResultRangeFs.InsufficientFreeSpace.Contains(bcatRc))
{
return bcatRc;
}
requiredSizeSum += requiredSizeBcat;
}
// Don't actually do this yet because the temp save indexer hasn't been implemented
// todo: Flip the operator when it is
if (nacp.TemporaryStorageSize < 0)
{
if (requiredSizeSum > 0)
{
var filter = new SaveDataFilter();
filter.SetTitleId(applicationId);
filter.SetSaveDataType(SaveDataType.TemporaryStorage);
Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.User, ref filter);
if (rc.IsFailure())
{
if(rc != ResultFs.TargetNotFound)
{
return rc;
}
// todo: Call QuerySaveDataTotalSize and add the value to requiredSizeSum
requiredSizeSum += 0;
}
}
else
{
Result createRc = fs.CreateTemporaryStorage(applicationId, nacp.SaveDataOwnerId,
nacp.TemporaryStorageSize, 0);
if (createRc.IsFailure())
{
if (ResultRangeFs.InsufficientFreeSpace.Contains(createRc))
{
// todo: Call QuerySaveDataTotalSize and assign the value to requiredSizeSum
requiredSizeSum += 0;
}
else if (createRc == ResultFs.PathAlreadyExists)
{
requiredSizeSum += 0;
}
else
{
return createRc;
}
}
}
}
requiredSize = requiredSizeSum;
return requiredSize == 0 ? Result.Success : ResultFs.InsufficientFreeSpace.Log();
}
[SuppressMessage("ReSharper", "UnusedParameter.Local")]
private static Result ExtendSaveDataIfNeeded(FileSystemClient fs, out long requiredSize,
SaveDataSpaceId spaceId, ulong saveDataId, long requiredDataSize, long requiredJournalSize)
{
// Checks the current save data size and extends it if needed.
// If there is not enough space to do the extension, the amount of space
// the extension requires will be written to requiredSize.
requiredSize = 0;
return Result.Success;
}
private static Result EnsureApplicationBcatDeliveryCacheStorageImpl(FileSystemClient fs, out long requiredSize,
TitleId applicationId, ref ApplicationControlProperty nacp)
{
const long bcatDeliveryCacheJournalSize = 0x200000;
requiredSize = default;
if (nacp.BcatDeliveryCacheStorageSize <= 0)
{
requiredSize = 0;
return Result.Success;
}
var filter = new SaveDataFilter();
filter.SetTitleId(applicationId);
filter.SetSaveDataType(SaveDataType.BcatDeliveryCacheStorage);
Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter);
if (rc.IsSuccess())
{
rc = ExtendSaveDataIfNeeded(fs, out long requiredSizeBcat, SaveDataSpaceId.User,
saveDataInfo.SaveDataId, nacp.DeviceSaveDataSize, bcatDeliveryCacheJournalSize);
if (rc.IsFailure())
{
if (!ResultRangeFs.InsufficientFreeSpace.Contains(rc))
{
return rc;
}
requiredSize = requiredSizeBcat;
}
}
else if (rc != ResultFs.TargetNotFound)
{
return rc;
}
else
{
Result createRc = fs.CreateBcatSaveData(applicationId, nacp.BcatDeliveryCacheStorageSize);
if (createRc.IsFailure())
{
if (ResultRangeFs.InsufficientFreeSpace.Contains(createRc))
{
// todo: Call QuerySaveDataTotalSize and assign the value to requiredSize
requiredSize = 0;
}
else if (createRc == ResultFs.PathAlreadyExists)
{
requiredSize = 0;
}
else
{
return createRc;
}
}
}
return requiredSize > 0 ? ResultFs.InsufficientFreeSpace.Log() : Result.Success;
}
public static UserId ConvertAccountUidToFsUserId(Uid uid)
{
return new UserId(uid.Id.High, uid.Id.Low);
}
}
}

View file

@ -2,6 +2,7 @@
{ {
public static class ResultRangeFs public static class ResultRangeFs
{ {
public static ResultRange InsufficientFreeSpace => new ResultRange(ResultFs.ModuleFs, 30, 45);
public static ResultRange DataCorrupted => new ResultRange(ResultFs.ModuleFs, 4000, 4999); public static ResultRange DataCorrupted => new ResultRange(ResultFs.ModuleFs, 4000, 4999);
public static ResultRange RomCorrupted => new ResultRange(ResultFs.ModuleFs, 4001, 4299); public static ResultRange RomCorrupted => new ResultRange(ResultFs.ModuleFs, 4001, 4299);
public static ResultRange SaveDataCorrupted => new ResultRange(ResultFs.ModuleFs, 4301, 4499); public static ResultRange SaveDataCorrupted => new ResultRange(ResultFs.ModuleFs, 4301, 4499);

View file

@ -76,6 +76,36 @@ namespace LibHac.Fs
[FieldOffset(0x20)] public ulong SaveDataId; [FieldOffset(0x20)] public ulong SaveDataId;
[FieldOffset(0x28)] public SaveDataType SaveDataType; [FieldOffset(0x28)] public SaveDataType SaveDataType;
[FieldOffset(0x2A)] public short Index; [FieldOffset(0x2A)] public short Index;
public void SetTitleId(TitleId value)
{
FilterByTitleId = true;
TitleId = value;
}
public void SetSaveDataType(SaveDataType value)
{
FilterBySaveDataType = true;
SaveDataType = value;
}
public void SetUserId(UserId value)
{
FilterByUserId = true;
UserId = value;
}
public void SetSaveDataId(ulong value)
{
FilterBySaveDataId = true;
SaveDataId = value;
}
public void SetIndex(short value)
{
FilterByIndex = true;
Index = value;
}
} }
[StructLayout(LayoutKind.Explicit, Size = HashLength)] [StructLayout(LayoutKind.Explicit, Size = HashLength)]

View file

@ -45,7 +45,7 @@ namespace LibHac.Fs.Shim
() => $", applicationid: 0x{titleId.Value:X}, userid: 0x{userId}, save_data_owner_id: 0x{ownerId.Value:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{flags:x8}"); () => $", applicationid: 0x{titleId.Value:X}, userid: 0x{userId}, save_data_owner_id: 0x{ownerId.Value:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{flags:x8}");
} }
public static Result CreateSaveData(this FileSystemClient fs, TitleId titleId, UserId userId, TitleId ownerId, public static Result CreateSaveData(this FileSystemClient fs, TitleId applicationId, UserId userId, TitleId ownerId,
long size, long journalSize, HashSalt hashSalt, uint flags) long size, long journalSize, HashSalt hashSalt, uint flags)
{ {
return fs.RunOperationWithAccessLog(LocalAccessLogMode.System, return fs.RunOperationWithAccessLog(LocalAccessLogMode.System,
@ -55,7 +55,7 @@ namespace LibHac.Fs.Shim
var attribute = new SaveDataAttribute var attribute = new SaveDataAttribute
{ {
TitleId = titleId, TitleId = applicationId,
UserId = userId, UserId = userId,
Type = SaveDataType.SaveData Type = SaveDataType.SaveData
}; };
@ -78,7 +78,97 @@ namespace LibHac.Fs.Shim
return fsProxy.CreateSaveDataFileSystemWithHashSalt(ref attribute, ref createInfo, ref metaInfo, ref hashSalt); return fsProxy.CreateSaveDataFileSystemWithHashSalt(ref attribute, ref createInfo, ref metaInfo, ref hashSalt);
}, },
() => $", applicationid: 0x{titleId.Value:X}, userid: 0x{userId}, save_data_owner_id: 0x{ownerId.Value:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{flags:x8}"); () => $", applicationid: 0x{applicationId.Value:X}, userid: 0x{userId}, save_data_owner_id: 0x{ownerId.Value:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{flags:x8}");
}
public static Result CreateBcatSaveData(this FileSystemClient fs, TitleId applicationId, long size)
{
return fs.RunOperationWithAccessLog(LocalAccessLogMode.System,
() =>
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
var attribute = new SaveDataAttribute
{
TitleId = applicationId,
Type = SaveDataType.BcatDeliveryCacheStorage
};
var createInfo = new SaveDataCreateInfo
{
Size = size,
JournalSize = 0x200000,
BlockSize = 0x4000,
OwnerId = SystemTitleIds.Bcat,
Flags = 0,
SpaceId = SaveDataSpaceId.User
};
var metaInfo = new SaveMetaCreateInfo();
return fsProxy.CreateSaveDataFileSystem(ref attribute, ref createInfo, ref metaInfo);
},
() => $", applicationid: 0x{applicationId.Value:X}, save_data_size: {size}");
}
public static Result CreateDeviceSaveData(this FileSystemClient fs, TitleId applicationId, TitleId ownerId,
long size, long journalSize, uint flags)
{
return fs.RunOperationWithAccessLog(LocalAccessLogMode.System,
() =>
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
var attribute = new SaveDataAttribute
{
TitleId = applicationId,
Type = SaveDataType.DeviceSaveData
};
var createInfo = new SaveDataCreateInfo
{
Size = size,
JournalSize = journalSize,
BlockSize = 0x4000,
OwnerId = ownerId,
Flags = flags,
SpaceId = SaveDataSpaceId.User
};
var metaInfo = new SaveMetaCreateInfo();
return fsProxy.CreateSaveDataFileSystem(ref attribute, ref createInfo, ref metaInfo);
},
() => $", applicationid: 0x{applicationId.Value:X}, save_data_owner_id: 0x{ownerId.Value:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{flags:x8}");
}
public static Result CreateTemporaryStorage(this FileSystemClient fs, TitleId applicationId, TitleId ownerId, long size, uint flags)
{
return fs.RunOperationWithAccessLog(LocalAccessLogMode.System,
() =>
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
var attribute = new SaveDataAttribute
{
TitleId = applicationId,
Type = SaveDataType.TemporaryStorage
};
var createInfo = new SaveDataCreateInfo
{
Size = size,
BlockSize = 0x4000,
OwnerId = ownerId,
Flags = flags,
SpaceId = SaveDataSpaceId.TemporaryStorage
};
var metaInfo = new SaveMetaCreateInfo();
return fsProxy.CreateSaveDataFileSystem(ref attribute, ref createInfo, ref metaInfo);
},
() => $", applicationid: 0x{applicationId.Value:X}, save_data_owner_id: 0x{ownerId.Value:X}, save_data_size: {size}, save_data_flags: 0x{flags:x8}");
} }
public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId,

View file

@ -0,0 +1,136 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Ncm;
namespace LibHac.Ns
{
[StructLayout(LayoutKind.Explicit, Size = 0x4000)]
public struct ApplicationControlProperty
{
private const int TitleCount = 0x10;
private const int IsbnSize = 0x25;
private const int RatingAgeCount = 0x20;
private const int DisplayVersionSize = 0x10;
private const int ApplicationErrorCodeCategorySize = 8;
private const int LocalCommunicationIdCount = 8;
private const int Reserved30F3Size = 3;
private const int BcatPassphraseSize = 0x41;
private const int ReservedForUserAccountSaveDataOperationSize = 6;
private const int PlayLogQueryableApplicationIdCount = 0x10;
private const int ReceivableDataConfigurationCount = 0x10;
[FieldOffset(0x0000)] private byte _titles;
[FieldOffset(0x3000)] private byte _isbn;
[FieldOffset(0x3025)] public byte StartupUserAccount;
[FieldOffset(0x3026)] public byte UserAccountSwitchLock;
[FieldOffset(0x3027)] public byte AddOnContentRegistrationType;
[FieldOffset(0x3028)] public int ApplicationAttribute;
[FieldOffset(0x302C)] public uint SupportedLanguages;
[FieldOffset(0x3030)] public uint ParentalControl;
[FieldOffset(0x3034)] public byte Screenshot;
[FieldOffset(0x3035)] public byte 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 TitleId 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 byte LogoType;
[FieldOffset(0x30F1)] public byte 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 byte 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;
[FieldOffset(0x3200), DebuggerBrowsable(DebuggerBrowsableState.Never)] private Padding100 _padding2;
public Span<ApplicationControlTitle> GetTitles() => SpanHelpers.CreateSpan(ref Unsafe.As<byte, ApplicationControlTitle>(ref _titles), TitleCount);
public U8SpanMutable Isbn => new U8SpanMutable(SpanHelpers.CreateSpan(ref _isbn, IsbnSize));
public Span<sbyte> RatingAge => SpanHelpers.CreateSpan(ref _ratingAge, RatingAgeCount);
public U8SpanMutable DisplayVersion => new U8SpanMutable(SpanHelpers.CreateSpan(ref _displayVersion, DisplayVersionSize));
public U8SpanMutable ApplicationErrorCodeCategory =>
new U8SpanMutable(SpanHelpers.CreateSpan(ref _applicationErrorCodeCategory,
ApplicationErrorCodeCategorySize));
public Span<ulong> LocalCommunicationIds => SpanHelpers.CreateSpan(ref _localCommunicationIds, LocalCommunicationIdCount);
public Span<byte> Reserved30F3 => SpanHelpers.CreateSpan(ref _reserved30F3, Reserved30F3Size);
public U8SpanMutable BcatPassphrase => new U8SpanMutable(SpanHelpers.CreateSpan(ref _bcatPassphrase, BcatPassphraseSize));
public Span<byte> ReservedForUserAccountSaveDataOperation =>
SpanHelpers.CreateSpan(ref _reservedForUserAccountSaveDataOperation,
ReservedForUserAccountSaveDataOperationSize);
public Span<ulong> PlayLogQueryableApplicationId =>
SpanHelpers.CreateSpan(ref _playLogQueryableApplicationId, PlayLogQueryableApplicationIdCount);
public Span<ApplicationControlDataConfiguration> ReceivableDataConfigurations =>
SpanHelpers.CreateSpan(ref _receivableDataConfigurations, ReceivableDataConfigurationCount);
}
[StructLayout(LayoutKind.Explicit, Size = 0x300)]
public struct ApplicationControlTitle
{
private const int NameLength = 0x200;
private const int PublisherLength = 0x100;
[FieldOffset(0x000)] private byte _name;
[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);
}
}