diff --git a/src/LibHac/Common/FixedArrays/Array356.cs b/src/LibHac/Common/FixedArrays/Array356.cs new file mode 100644 index 00000000..b8e392be --- /dev/null +++ b/src/LibHac/Common/FixedArrays/Array356.cs @@ -0,0 +1,33 @@ +#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common.FixedArrays; + +public struct Array356 +{ + public const int Length = 356; + + private Array256 _0; + private Array64 _256; + private Array32 _320; + private Array4 _352; + + public ref T this[int i] => ref Items[i]; + + public Span Items + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length); + } + + public readonly ReadOnlySpan ItemsRo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ReadOnlySpan(in Array356 value) => value.ItemsRo; +} \ No newline at end of file diff --git a/src/LibHac/Fs/SaveDataStructs.cs b/src/LibHac/Fs/Common/SaveDataTypes.cs similarity index 66% rename from src/LibHac/Fs/SaveDataStructs.cs rename to src/LibHac/Fs/Common/SaveDataTypes.cs index af163d32..d5db41c8 100644 --- a/src/LibHac/Fs/SaveDataStructs.cs +++ b/src/LibHac/Fs/Common/SaveDataTypes.cs @@ -5,8 +5,127 @@ using LibHac.FsSrv.Impl; using LibHac.Ncm; using LibHac.Util; +// ReSharper disable once CheckNamespace namespace LibHac.Fs; +public enum SaveDataSpaceId : byte +{ + System = 0, + User = 1, + SdSystem = 2, + Temporary = 3, + SdUser = 4, + ProperSystem = 100, + SafeMode = 101, + BisAuto = 127 +} + +public enum SaveDataType : byte +{ + System = 0, + Account = 1, + Bcat = 2, + Device = 3, + Temporary = 4, + Cache = 5, + SystemBcat = 6 +} + +public enum SaveDataRank : byte +{ + Primary = 0, + Secondary = 1 +} + +public enum SaveDataFormatType : byte +{ + Normal = 0, + NoJournal = 1 +} + +[Flags] +public enum SaveDataFlags +{ + None = 0, + KeepAfterResettingSystemSaveData = 1 << 0, + KeepAfterRefurbishment = 1 << 1, + KeepAfterResettingSystemSaveDataWithoutUserSaveData = 1 << 2, + NeedsSecureDelete = 1 << 3, + Restore = 1 << 4 +} + +public enum SaveDataState : byte +{ + Normal = 0, + Processing = 1, + State2 = 2, + MarkedForDeletion = 3, + Extending = 4, + ImportSuspended = 5 +} + +public struct SaveDataMetaInfo +{ + public int Size; + public SaveDataMetaType Type; + public Array11 Reserved; +} + +public enum SaveDataMetaType : byte +{ + None = 0, + Thumbnail = 1, + ExtensionContext = 2 +} + +public struct SaveDataInfo +{ + public ulong SaveDataId; + public SaveDataSpaceId SpaceId; + public SaveDataType Type; + public UserId UserId; + public ulong StaticSaveDataId; + public ProgramId ProgramId; + public long Size; + public ushort Index; + public SaveDataRank Rank; + public SaveDataState State; + public Array36 Reserved; +} + +public struct SaveDataExtraData +{ + public SaveDataAttribute Attribute; + public ulong OwnerId; + public long TimeStamp; + public SaveDataFlags Flags; + public long DataSize; + public long JournalSize; + public long CommitId; + public Array400 Reserved; +} + +public struct CommitOption +{ + public CommitOptionFlag Flags; +} + +[Flags] +public enum CommitOptionFlag +{ + None = 0, + ClearRestoreFlag = 1, + SetRestoreFlag = 2 +} + +public struct HashSalt +{ + private Array32 _value; + + public Span Hash => _value.Items; + public readonly ReadOnlySpan HashRo => _value.ItemsRo; +} + public struct SaveDataAttribute : IEquatable, IComparable { public ProgramId ProgramId; @@ -60,8 +179,8 @@ public struct SaveDataAttribute : IEquatable, IComparable left.Equals(right); @@ -84,9 +203,9 @@ public struct SaveDataAttribute : IEquatable, IComparable Reserved1; + public bool IsHashSaltEnabled; + public Array3 Reserved2; + public HashSalt HashSalt; + public SaveDataMetaType MetaType; + public Array3 Reserved3; + public int MetaSize; + public Array356 Reserved4; + + public static Result Make(out SaveDataCreationInfo2 creationInfo, in SaveDataAttribute attribute, long size, + long journalSize, long blockSize, ulong ownerId, SaveDataFlags flags, SaveDataSpaceId spaceId, + SaveDataFormatType formatType) + { + UnsafeHelpers.SkipParamInit(out creationInfo); + SaveDataCreationInfo2 tempCreationInfo = default; + + tempCreationInfo.Version = SaveDataCreationInfo2Version; + tempCreationInfo.Attribute = attribute; + tempCreationInfo.Size = size; + tempCreationInfo.JournalSize = journalSize; + tempCreationInfo.BlockSize = blockSize; + tempCreationInfo.OwnerId = ownerId; + tempCreationInfo.Flags = flags; + tempCreationInfo.SpaceId = spaceId; + tempCreationInfo.FormatType = formatType; + + if (!SaveDataTypesValidity.IsValid(in tempCreationInfo)) + return ResultFs.InvalidArgument.Log(); + + creationInfo = tempCreationInfo; + return Result.Success; + } +} + public struct SaveDataFilter { public bool FilterByProgramId; @@ -201,64 +367,45 @@ public struct SaveDataFilter } } -public struct HashSalt -{ - private Array32 _value; - - public Span Hash => _value.Items; - public readonly ReadOnlySpan HashRo => _value.ItemsRo; -} - -public struct SaveDataMetaInfo -{ - public int Size; - public SaveDataMetaType Type; - public Array11 Reserved; -} - -public struct SaveDataInfo -{ - public ulong SaveDataId; - public SaveDataSpaceId SpaceId; - public SaveDataType Type; - public UserId UserId; - public ulong StaticSaveDataId; - public ProgramId ProgramId; - public long Size; - public ushort Index; - public SaveDataRank Rank; - public SaveDataState State; - public Array36 Reserved; -} - -public struct SaveDataExtraData -{ - public SaveDataAttribute Attribute; - public ulong OwnerId; - public long TimeStamp; - public SaveDataFlags Flags; - public long DataSize; - public long JournalSize; - public long CommitId; - public Array400 Reserved; -} - -public struct CommitOption -{ - public CommitOptionFlag Flags; -} - internal static class SaveDataTypesValidity { public static bool IsValid(in SaveDataAttribute attribute) { - return IsValid(in attribute.Type) && IsValid(in attribute.Rank); + return IsValid(in attribute.Type)&& IsValid(in attribute.Rank); } public static bool IsValid(in SaveDataCreationInfo creationInfo) { - return creationInfo.Size >= 0 && creationInfo.JournalSize >= 0 && creationInfo.BlockSize >= 0 && - IsValid(in creationInfo.SpaceId); + return creationInfo.Size >= 0 + && creationInfo.JournalSize >= 0 + && creationInfo.BlockSize >= 0 + && IsValid(in creationInfo.SpaceId); + } + + public static bool IsValid(in SaveDataCreationInfo2 creationInfo) + { + foreach (byte b in creationInfo.Reserved1.ItemsRo) + if (b != 0) return false; + + foreach (byte b in creationInfo.Reserved2.ItemsRo) + if (b != 0) return false; + + foreach (byte b in creationInfo.Reserved3.ItemsRo) + if (b != 0) return false; + + foreach (byte b in creationInfo.Reserved4.ItemsRo) + if (b != 0) return false; + + foreach (byte b in creationInfo.Attribute.Reserved.ItemsRo) + if (b != 0) return false; + + return IsValid(in creationInfo.Attribute) + && creationInfo.Size >= 0 + && creationInfo.JournalSize >= 0 + && creationInfo.BlockSize >= 0 + && IsValid(in creationInfo.SpaceId) + && IsValid(in creationInfo.FormatType) + && IsValid(in creationInfo.MetaType); } public static bool IsValid(in SaveDataMetaInfo metaInfo) @@ -277,6 +424,11 @@ internal static class SaveDataTypesValidity return (uint)type <= (uint)SaveDataType.Cache; } + public static bool IsValid(in SaveDataFormatType type) + { + return (uint)type <= (uint)SaveDataFormatType.NoJournal; + } + public static bool IsValid(in SaveDataRank rank) { return (uint)rank <= (uint)SaveDataRank.Secondary; @@ -284,8 +436,9 @@ internal static class SaveDataTypesValidity public static bool IsValid(in SaveDataSpaceId spaceId) { - return (uint)spaceId <= (uint)SaveDataSpaceId.SdUser || spaceId == SaveDataSpaceId.ProperSystem || - spaceId == SaveDataSpaceId.SafeMode; + return (uint)spaceId <= (uint)SaveDataSpaceId.SdUser + || spaceId == SaveDataSpaceId.ProperSystem + || spaceId == SaveDataSpaceId.SafeMode; } public static bool IsValid(in SaveDataMetaType metaType) diff --git a/src/LibHac/Fs/FsEnums.cs b/src/LibHac/Fs/FsEnums.cs index d0e2d0d7..ceaaa0ed 100644 --- a/src/LibHac/Fs/FsEnums.cs +++ b/src/LibHac/Fs/FsEnums.cs @@ -46,18 +46,6 @@ public enum GameCardPartitionRaw RootWriteOnly = 2 } -public enum SaveDataSpaceId : byte -{ - System = 0, - User = 1, - SdSystem = 2, - Temporary = 3, - SdUser = 4, - ProperSystem = 100, - SafeMode = 101, - BisAuto = 127 -} - public enum CustomStorageId { System = 0, @@ -86,22 +74,6 @@ public enum FileSystemProxyType RegisteredUpdate = 8 } -public enum SaveDataMetaType : byte -{ - None = 0, - Thumbnail = 1, - ExtensionContext = 2 -} - -public enum SaveDataState : byte -{ - Normal = 0, - Processing = 1, - State2 = 2, - MarkedForDeletion = 3, - Extending = 4, - ImportSuspended = 5 -} public enum ImageDirectoryId { @@ -153,48 +125,6 @@ public enum OperationId ReadyLazyLoadFile = 10001 } -public enum SaveDataType : byte -{ - System = 0, - Account = 1, - Bcat = 2, - Device = 3, - Temporary = 4, - Cache = 5, - SystemBcat = 6 -} - -public enum SaveDataRank : byte -{ - Primary = 0, - Secondary = 1 -} - -public enum SaveDataFormatType : byte -{ - Normal = 0, - NoJournal = 1 -} - -[Flags] -public enum SaveDataFlags -{ - None = 0, - KeepAfterResettingSystemSaveData = 1 << 0, - KeepAfterRefurbishment = 1 << 1, - KeepAfterResettingSystemSaveDataWithoutUserSaveData = 1 << 2, - NeedsSecureDelete = 1 << 3, - Restore = 1 << 4 -} - -[Flags] -public enum CommitOptionFlag -{ - None = 0, - ClearRestoreFlag = 1, - SetRestoreFlag = 2 -} - public enum CacheStorageTargetMedia { None = 0, @@ -293,11 +223,4 @@ public enum SdmmcPort Mmc = 0, SdCard = 1, GcAsic = 2 -} - -public enum StorageType -{ - SaveData = 0, - RomFs = 1, - Authoring = 2 } \ No newline at end of file diff --git a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs index 5ac2ee69..5c102ac9 100644 --- a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs +++ b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs @@ -41,6 +41,32 @@ public class TypeLayoutTests Assert.Equal(0x26, GetOffset(in s, in s.Reserved)); } + [Fact] + public static void SaveDataCreationInfo2_Layout() + { + var s = new SaveDataCreationInfo2(); + + Assert.Equal(0x200, Unsafe.SizeOf()); + + Assert.Equal(0x00, GetOffset(in s, in s.Version)); + Assert.Equal(0x08, GetOffset(in s, in s.Attribute)); + Assert.Equal(0x48, GetOffset(in s, in s.Size)); + Assert.Equal(0x50, GetOffset(in s, in s.JournalSize)); + Assert.Equal(0x58, GetOffset(in s, in s.BlockSize)); + Assert.Equal(0x60, GetOffset(in s, in s.OwnerId)); + Assert.Equal(0x68, GetOffset(in s, in s.Flags)); + Assert.Equal(0x6C, GetOffset(in s, in s.SpaceId)); + Assert.Equal(0x6D, GetOffset(in s, in s.FormatType)); + Assert.Equal(0x6E, GetOffset(in s, in s.Reserved1)); + Assert.Equal(0x70, GetOffset(in s, in s.IsHashSaltEnabled)); + Assert.Equal(0x71, GetOffset(in s, in s.Reserved2)); + Assert.Equal(0x74, GetOffset(in s, in s.HashSalt)); + Assert.Equal(0x94, GetOffset(in s, in s.MetaType)); + Assert.Equal(0x95, GetOffset(in s, in s.Reserved3)); + Assert.Equal(0x98, GetOffset(in s, in s.MetaSize)); + Assert.Equal(0x9C, GetOffset(in s, in s.Reserved4)); + } + [Fact] public static void SaveDataFilter_Layout() {