mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add EnsureApplicationSaveData and dependencies
- CreateBcatSaveData - CreateDeviceSaveData - CreateTemporaryStorage - ApplicationControlProperty struct
This commit is contained in:
parent
df8dab542a
commit
bfaf95026a
7 changed files with 602 additions and 3 deletions
59
src/LibHac/Account/Uid.cs
Normal file
59
src/LibHac/Account/Uid.cs
Normal 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;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,13 @@ 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
|
||||
{
|
||||
|
@ -35,4 +42,11 @@ namespace LibHac.Common
|
|||
[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;
|
||||
}
|
||||
}
|
||||
|
|
269
src/LibHac/Fs/ApplicationSaveDataManagement.cs
Normal file
269
src/LibHac/Fs/ApplicationSaveDataManagement.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
{
|
||||
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 RomCorrupted => new ResultRange(ResultFs.ModuleFs, 4001, 4299);
|
||||
public static ResultRange SaveDataCorrupted => new ResultRange(ResultFs.ModuleFs, 4301, 4499);
|
||||
|
|
|
@ -76,6 +76,36 @@ namespace LibHac.Fs
|
|||
[FieldOffset(0x20)] public ulong SaveDataId;
|
||||
[FieldOffset(0x28)] public SaveDataType SaveDataType;
|
||||
[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)]
|
||||
|
|
|
@ -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}");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return fs.RunOperationWithAccessLog(LocalAccessLogMode.System,
|
||||
|
@ -55,7 +55,7 @@ namespace LibHac.Fs.Shim
|
|||
|
||||
var attribute = new SaveDataAttribute
|
||||
{
|
||||
TitleId = titleId,
|
||||
TitleId = applicationId,
|
||||
UserId = userId,
|
||||
Type = SaveDataType.SaveData
|
||||
};
|
||||
|
@ -78,7 +78,97 @@ namespace LibHac.Fs.Shim
|
|||
|
||||
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,
|
||||
|
|
136
src/LibHac/Ns/ApplicationControlProperty.cs
Normal file
136
src/LibHac/Ns/ApplicationControlProperty.cs
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue