mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Merge pull request #227 from Thealexbarney/fs-stuff
Update some FS classes for system version 13.1.0 Updated: - Fs.DirectoryPathParser - Fs.FileHandleStorage - Fs.FileStorage - Fs.FileStorageBasedFileSystem - Fs.PathFormatter - Fs.PathNormalizer - Fs.PathUtility - Fs.Path - Fs.WindowsPath - Fs.Fsa.MountUtility - Fs.Fsa.Registrar - Fs.Fsa.UserDirectory - Fs.Fsa.UserFile - Fs.Fsa.UserFileSystem - Fs.Fsa.UserFileSystemPrivate - Fs.Fsa.UserMountTable - Fs.Impl.DirectoryAccessor - Fs.Impl.FileAccessor - Fs.Impl.FileDataCacheAccessor - Fs.Impl.FileSystemAccessor - Fs.Impl.IFileDataCache - Fs.Impl.MountTable - FsSrv.Impl.FileSystemInterfaceAdapter - FsSystem.BufferedStorage - FsSystem.ConcatenationFileSystem - FsSystem.FileSystemBufferManager Added: - Fs.Range - Fs.Fsa.UserFileSystemForDebug - Fs.Impl.GlobalFileDataCacheAccessorReadableScopedPointer - Fs.Shim.FileDataCache partially - Fs.Shim.PathBasedFileDataCache skeleton - FsSystem.CompressedStorage skeleton - FsSystem.DefaultAsynchronousAccessSplitter - FsSystem.IAsynchronousAccessSplitter - FsSystem.Impl.BlockCacheManager - Util.Utf8StringUtil Change the way some structs are laid out so that they don't use LayoutKind.Explicit or the size parameter of StructLayout. The ArrayN<T> types are now used for structs containing inline arrays. The offsets of all fields in structs where layout matters are now documented in the LibHac.Tests project inside tests that make sure struct layouts are correct.
This commit is contained in:
commit
e0deb7ea54
@ -37,17 +37,17 @@ public static class KeysCodeGen
|
||||
sb.AppendLine("internal static partial class DefaultKeySet");
|
||||
sb.AppendLineAndIncrease("{");
|
||||
|
||||
BuildArray(sb, "RootKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._rootKeysDev));
|
||||
BuildArray(sb, "RootKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._rootKeysProd));
|
||||
BuildArray(sb, "KeySeeds", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._keySeeds));
|
||||
BuildArray(sb, "StoredKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._storedKeysDev));
|
||||
BuildArray(sb, "StoredKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._storedKeysProd));
|
||||
BuildArray(sb, "DerivedKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._derivedKeysDev));
|
||||
BuildArray(sb, "DerivedKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._derivedKeysProd));
|
||||
BuildArray(sb, "DeviceKeys", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._deviceKeys));
|
||||
BuildArray(sb, "RsaSigningKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._rsaSigningKeysDev));
|
||||
BuildArray(sb, "RsaSigningKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._rsaSigningKeysProd));
|
||||
BuildArray(sb, "RsaKeys", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct._rsaKeys));
|
||||
BuildArray(sb, "RootKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.RootKeysDev));
|
||||
BuildArray(sb, "RootKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.RootKeysProd));
|
||||
BuildArray(sb, "KeySeeds", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.KeySeeds));
|
||||
BuildArray(sb, "StoredKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.StoredKeysDev));
|
||||
BuildArray(sb, "StoredKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.StoredKeysProd));
|
||||
BuildArray(sb, "DerivedKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.DerivedKeysDev));
|
||||
BuildArray(sb, "DerivedKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.DerivedKeysProd));
|
||||
BuildArray(sb, "DeviceKeys", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.DeviceKeys));
|
||||
BuildArray(sb, "RsaSigningKeysDev", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.RsaSigningKeysDev));
|
||||
BuildArray(sb, "RsaSigningKeysProd", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.RsaSigningKeysProd));
|
||||
BuildArray(sb, "RsaKeys", SpanHelpers.AsReadOnlyByteSpan(in keySet.KeyStruct.RsaKeys));
|
||||
|
||||
sb.DecreaseAndAppendLine("}");
|
||||
|
||||
@ -99,46 +99,46 @@ public static class KeysCodeGen
|
||||
RSAParameters betaNca0Params =
|
||||
Rsa.RecoverParameters(BetaNca0Modulus, StandardPublicExponent, BetaNca0Exponent);
|
||||
|
||||
betaNca0Params.D.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.PrivateExponent.Data);
|
||||
betaNca0Params.DP.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Dp.Data);
|
||||
betaNca0Params.DQ.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Dq.Data);
|
||||
betaNca0Params.Exponent.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.PublicExponent.Data);
|
||||
betaNca0Params.InverseQ.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.InverseQ.Data);
|
||||
betaNca0Params.Modulus.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Modulus.Data);
|
||||
betaNca0Params.P.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.P.Data);
|
||||
betaNca0Params.Q.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Q.Data);
|
||||
betaNca0Params.D.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.PrivateExponent.Items);
|
||||
betaNca0Params.DP.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Dp.Items);
|
||||
betaNca0Params.DQ.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Dq.Items);
|
||||
betaNca0Params.Exponent.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.PublicExponent.Items);
|
||||
betaNca0Params.InverseQ.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.InverseQ.Items);
|
||||
betaNca0Params.Modulus.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Modulus.Items);
|
||||
betaNca0Params.P.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.P.Items);
|
||||
betaNca0Params.Q.AsSpan().CopyTo(keySet.BetaNca0KeyAreaKey.Q.Items);
|
||||
|
||||
// First populate the prod RSA keys
|
||||
keySet.SetMode(KeySet.Mode.Prod);
|
||||
|
||||
StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[0].PublicExponent.Data);
|
||||
StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[1].PublicExponent.Data);
|
||||
NcaHdrFixedKeyModulus0Prod.CopyTo(keySet.NcaHeaderSigningKeys[0].Modulus.Data);
|
||||
NcaHdrFixedKeyModulus1Prod.CopyTo(keySet.NcaHeaderSigningKeys[1].Modulus.Data);
|
||||
StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[0].PublicExponent.Items);
|
||||
StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[1].PublicExponent.Items);
|
||||
NcaHdrFixedKeyModulus0Prod.CopyTo(keySet.NcaHeaderSigningKeys[0].Modulus.Items);
|
||||
NcaHdrFixedKeyModulus1Prod.CopyTo(keySet.NcaHeaderSigningKeys[1].Modulus.Items);
|
||||
|
||||
StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[0].PublicExponent.Data);
|
||||
StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[1].PublicExponent.Data);
|
||||
AcidFixedKeyModulus0Prod.CopyTo(keySet.AcidSigningKeys[0].Modulus.Data);
|
||||
AcidFixedKeyModulus1Prod.CopyTo(keySet.AcidSigningKeys[1].Modulus.Data);
|
||||
StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[0].PublicExponent.Items);
|
||||
StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[1].PublicExponent.Items);
|
||||
AcidFixedKeyModulus0Prod.CopyTo(keySet.AcidSigningKeys[0].Modulus.Items);
|
||||
AcidFixedKeyModulus1Prod.CopyTo(keySet.AcidSigningKeys[1].Modulus.Items);
|
||||
|
||||
StandardPublicExponent.CopyTo(keySet.Package2SigningKey.PublicExponent.Data);
|
||||
Package2FixedKeyModulusProd.CopyTo(keySet.Package2SigningKey.Modulus.Data);
|
||||
StandardPublicExponent.CopyTo(keySet.Package2SigningKey.PublicExponent.Items);
|
||||
Package2FixedKeyModulusProd.CopyTo(keySet.Package2SigningKey.Modulus.Items);
|
||||
|
||||
// Populate the dev RSA keys
|
||||
keySet.SetMode(KeySet.Mode.Dev);
|
||||
|
||||
StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[0].PublicExponent.Data);
|
||||
StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[1].PublicExponent.Data);
|
||||
NcaHdrFixedKeyModulus0Dev.CopyTo(keySet.NcaHeaderSigningKeys[0].Modulus.Data);
|
||||
NcaHdrFixedKeyModulus1Dev.CopyTo(keySet.NcaHeaderSigningKeys[1].Modulus.Data);
|
||||
StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[0].PublicExponent.Items);
|
||||
StandardPublicExponent.CopyTo(keySet.NcaHeaderSigningKeys[1].PublicExponent.Items);
|
||||
NcaHdrFixedKeyModulus0Dev.CopyTo(keySet.NcaHeaderSigningKeys[0].Modulus.Items);
|
||||
NcaHdrFixedKeyModulus1Dev.CopyTo(keySet.NcaHeaderSigningKeys[1].Modulus.Items);
|
||||
|
||||
StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[0].PublicExponent.Data);
|
||||
StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[1].PublicExponent.Data);
|
||||
AcidFixedKeyModulus0Dev.CopyTo(keySet.AcidSigningKeys[0].Modulus.Data);
|
||||
AcidFixedKeyModulus1Dev.CopyTo(keySet.AcidSigningKeys[1].Modulus.Data);
|
||||
StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[0].PublicExponent.Items);
|
||||
StandardPublicExponent.CopyTo(keySet.AcidSigningKeys[1].PublicExponent.Items);
|
||||
AcidFixedKeyModulus0Dev.CopyTo(keySet.AcidSigningKeys[0].Modulus.Items);
|
||||
AcidFixedKeyModulus1Dev.CopyTo(keySet.AcidSigningKeys[1].Modulus.Items);
|
||||
|
||||
StandardPublicExponent.CopyTo(keySet.Package2SigningKey.PublicExponent.Data);
|
||||
Package2FixedKeyModulusDev.CopyTo(keySet.Package2SigningKey.Modulus.Data);
|
||||
StandardPublicExponent.CopyTo(keySet.Package2SigningKey.PublicExponent.Items);
|
||||
Package2FixedKeyModulusDev.CopyTo(keySet.Package2SigningKey.Modulus.Items);
|
||||
|
||||
return keySet;
|
||||
}
|
||||
|
@ -1,12 +1,16 @@
|
||||
using System.Runtime.InteropServices;
|
||||
namespace LibHac.Arp;
|
||||
|
||||
namespace LibHac.Arp;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
|
||||
public struct ApplicationLaunchProperty
|
||||
{
|
||||
[FieldOffset(0x0)] public ApplicationId ApplicationId;
|
||||
[FieldOffset(0x8)] public uint Version;
|
||||
[FieldOffset(0xC)] public Ncm.StorageId BaseStorageId;
|
||||
[FieldOffset(0xD)] public Ncm.StorageId UpdateStorageId;
|
||||
public ApplicationId ApplicationId;
|
||||
public uint Version;
|
||||
public Ncm.StorageId StorageId;
|
||||
public Ncm.StorageId PatchStorageId;
|
||||
public ApplicationKind ApplicationKind;
|
||||
}
|
||||
|
||||
public enum ApplicationKind : byte
|
||||
{
|
||||
Application = 0,
|
||||
MicroApplication = 1
|
||||
}
|
@ -1,18 +1,15 @@
|
||||
using System.Runtime.InteropServices;
|
||||
namespace LibHac.Bcat;
|
||||
|
||||
namespace LibHac.Bcat;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x38)]
|
||||
public struct DeliveryCacheDirectoryEntry
|
||||
{
|
||||
[FieldOffset(0x00)] public FileName Name;
|
||||
[FieldOffset(0x20)] public long Size;
|
||||
[FieldOffset(0x28)] public Digest Digest;
|
||||
public FileName Name;
|
||||
public long Size;
|
||||
public Digest Digest;
|
||||
|
||||
public DeliveryCacheDirectoryEntry(ref FileName name, long size, ref Digest digest)
|
||||
public DeliveryCacheDirectoryEntry(in FileName name, long size, in Digest digest)
|
||||
{
|
||||
Name = name;
|
||||
Size = size;
|
||||
Digest = digest;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +1,14 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.FixedArrays;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.Bcat;
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Sequential, Size = 16)]
|
||||
public struct Digest
|
||||
{
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
|
||||
public Array16<byte> Value;
|
||||
|
||||
public byte this[int i]
|
||||
public readonly override string ToString()
|
||||
{
|
||||
get => Bytes[i];
|
||||
set => Bytes[i] = value;
|
||||
return Value.ItemsRo.ToHexString();
|
||||
}
|
||||
|
||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Bytes.ToHexString();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +1,18 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.FixedArrays;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.Bcat;
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Sequential, Size = MaxSize)]
|
||||
public struct DirectoryName
|
||||
{
|
||||
private const int MaxSize = 0x20;
|
||||
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy2;
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy3;
|
||||
public Array32<byte> Value;
|
||||
|
||||
public byte this[int i]
|
||||
public readonly bool IsValid()
|
||||
{
|
||||
get => Bytes[i];
|
||||
set => Bytes[i] = value;
|
||||
}
|
||||
|
||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||
|
||||
public bool IsValid()
|
||||
{
|
||||
Span<byte> name = Bytes;
|
||||
ReadOnlySpan<byte> name = Value.ItemsRo;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < name.Length; i++)
|
||||
@ -45,8 +30,8 @@ public struct DirectoryName
|
||||
return name[i] == 0;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
public readonly override string ToString()
|
||||
{
|
||||
return StringUtils.Utf8ZToString(Bytes);
|
||||
return StringUtils.Utf8ZToString(Value.ItemsRo);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +1,18 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.FixedArrays;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.Bcat;
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Sequential, Size = MaxSize)]
|
||||
public struct FileName
|
||||
{
|
||||
private const int MaxSize = 0x20;
|
||||
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy2;
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy3;
|
||||
public Array32<byte> Value;
|
||||
|
||||
public byte this[int i]
|
||||
public readonly bool IsValid()
|
||||
{
|
||||
get => Bytes[i];
|
||||
set => Bytes[i] = value;
|
||||
}
|
||||
|
||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||
|
||||
public bool IsValid()
|
||||
{
|
||||
Span<byte> name = Bytes;
|
||||
ReadOnlySpan<byte> name = Value.ItemsRo;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < name.Length; i++)
|
||||
@ -48,8 +33,8 @@ public struct FileName
|
||||
return name[i - 1] != '.';
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
public readonly override string ToString()
|
||||
{
|
||||
return StringUtils.Utf8ZToString(Bytes);
|
||||
return StringUtils.Utf8ZToString(Value.ItemsRo);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common.FixedArrays;
|
||||
|
||||
namespace LibHac.Bcat.Impl.Service.Core;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x40)]
|
||||
internal struct DeliveryCacheDirectoryMetaEntry
|
||||
{
|
||||
[FieldOffset(0x00)] public DirectoryName Name;
|
||||
[FieldOffset(0x20)] public Digest Digest;
|
||||
}
|
||||
public DirectoryName Name;
|
||||
public Digest Digest;
|
||||
public Array16<byte> Reserved;
|
||||
}
|
@ -56,7 +56,7 @@ internal class DeliveryCacheFileMetaAccessor
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (StringUtils.CompareCaseInsensitive(Entries[i].Name.Bytes, fileName.Bytes) == 0)
|
||||
if (StringUtils.CompareCaseInsensitive(Entries[i].Name.Value, fileName.Value) == 0)
|
||||
{
|
||||
entry = Entries[i];
|
||||
return Result.Success;
|
||||
@ -118,4 +118,4 @@ internal class DeliveryCacheFileMetaAccessor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common.FixedArrays;
|
||||
|
||||
namespace LibHac.Bcat.Impl.Service.Core;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x80)]
|
||||
internal struct DeliveryCacheFileMetaEntry
|
||||
{
|
||||
[FieldOffset(0x00)] public FileName Name;
|
||||
[FieldOffset(0x20)] public long Id;
|
||||
[FieldOffset(0x28)] public long Size;
|
||||
[FieldOffset(0x30)] public Digest Digest;
|
||||
}
|
||||
public FileName Name;
|
||||
public long Id;
|
||||
public long Size;
|
||||
public Digest Digest;
|
||||
public Array64<byte> Reserved;
|
||||
}
|
@ -222,9 +222,9 @@ internal class DeliveryCacheStorageManager
|
||||
AppendMountName(ref sb, applicationId);
|
||||
|
||||
sb.Append(DirectoriesPath)
|
||||
.Append(DirectorySeparator).Append(directoryName.Bytes)
|
||||
.Append(DirectorySeparator).Append(directoryName.Value)
|
||||
.Append(DirectorySeparator).Append(FilesDirectoryName)
|
||||
.Append(DirectorySeparator).Append(fileName.Bytes);
|
||||
.Append(DirectorySeparator).Append(fileName.Value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,7 +237,7 @@ internal class DeliveryCacheStorageManager
|
||||
AppendMountName(ref sb, applicationId);
|
||||
|
||||
sb.Append(DirectoriesPath)
|
||||
.Append(DirectorySeparator).Append(directoryName.Bytes)
|
||||
.Append(DirectorySeparator).Append(directoryName.Value)
|
||||
.Append(DirectorySeparator).Append(FilesMetaFileName);
|
||||
}
|
||||
}
|
||||
@ -262,7 +262,7 @@ internal class DeliveryCacheStorageManager
|
||||
AppendMountName(ref sb, applicationId);
|
||||
|
||||
sb.Append(DirectoriesPath)
|
||||
.Append(DirectorySeparator).Append(directoryName.Bytes);
|
||||
.Append(DirectorySeparator).Append(directoryName.Value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -395,4 +395,4 @@ internal class DeliveryCacheStorageManager
|
||||
|
||||
private static ReadOnlySpan<byte> FilesDirectoryName => // files
|
||||
new[] { (byte)'f', (byte)'i', (byte)'l', (byte)'e', (byte)'s' };
|
||||
}
|
||||
}
|
@ -75,7 +75,7 @@ internal class DeliveryCacheDirectoryService : IDeliveryCacheDirectoryService
|
||||
break;
|
||||
}
|
||||
|
||||
entryBuffer[i] = new DeliveryCacheDirectoryEntry(ref entry.Name, entry.Size, ref entry.Digest);
|
||||
entryBuffer[i] = new DeliveryCacheDirectoryEntry(in entry.Name, entry.Size, in entry.Digest);
|
||||
}
|
||||
|
||||
entriesRead = i;
|
||||
@ -103,4 +103,4 @@ internal class DeliveryCacheDirectoryService : IDeliveryCacheDirectoryService
|
||||
{
|
||||
Parent.NotifyCloseDirectory();
|
||||
}
|
||||
}
|
||||
}
|
@ -76,7 +76,7 @@ internal class DeliveryCacheStorageService : IDeliveryCacheStorageService
|
||||
break;
|
||||
}
|
||||
|
||||
StringUtils.Copy(nameBuffer[i].Bytes, entry.Name.Bytes);
|
||||
StringUtils.Copy(nameBuffer[i].Value.Items, entry.Name.Value);
|
||||
}
|
||||
|
||||
namesRead = i;
|
||||
@ -108,4 +108,4 @@ internal class DeliveryCacheStorageService : IDeliveryCacheStorageService
|
||||
{
|
||||
Server.GetStorageManager().Release(ApplicationId);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,42 +1,27 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.FixedArrays;
|
||||
using LibHac.Crypto;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.Boot;
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0xB0)]
|
||||
public struct EncryptedKeyBlob
|
||||
{
|
||||
#if DEBUG
|
||||
[FieldOffset(0x00)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy1;
|
||||
[FieldOffset(0x20)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy2;
|
||||
[FieldOffset(0x40)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy3;
|
||||
[FieldOffset(0x60)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy4;
|
||||
[FieldOffset(0x80)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy5;
|
||||
[FieldOffset(0xA0)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer16 _dummy6;
|
||||
#endif
|
||||
|
||||
[FieldOffset(0x00)] public AesCmac Cmac;
|
||||
[FieldOffset(0x10)] public AesIv Counter;
|
||||
|
||||
public Span<byte> Payload => Bytes.Slice(0x20, Unsafe.SizeOf<KeyBlob>());
|
||||
public AesCmac Cmac;
|
||||
public AesIv Counter;
|
||||
public Array144<byte> Payload;
|
||||
|
||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||
public readonly ReadOnlySpan<byte> ReadOnlyBytes => SpanHelpers.AsReadOnlyByteSpan(in this);
|
||||
public readonly ReadOnlySpan<byte> BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool IsZeros()
|
||||
{
|
||||
ReadOnlySpan<ulong> ulongSpan = MemoryMarshal.Cast<byte, ulong>(ReadOnlyBytes);
|
||||
|
||||
for (int i = 0; i < ulongSpan.Length; i++)
|
||||
foreach (ulong val in SpanHelpers.AsReadOnlySpan<EncryptedKeyBlob, ulong>(in this))
|
||||
{
|
||||
if (ulongSpan[i] != 0)
|
||||
if (val != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -44,39 +29,27 @@ public struct EncryptedKeyBlob
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<byte>(in EncryptedKeyBlob value)
|
||||
{
|
||||
return SpanHelpers.AsReadOnlyByteSpan(in value);
|
||||
}
|
||||
public static implicit operator ReadOnlySpan<byte>(in EncryptedKeyBlob value) =>
|
||||
SpanHelpers.AsReadOnlyByteSpan(in value);
|
||||
|
||||
public readonly override string ToString() => ReadOnlyBytes.ToHexString();
|
||||
public readonly override string ToString() => BytesRo.ToHexString();
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x90)]
|
||||
public struct KeyBlob
|
||||
{
|
||||
#if DEBUG
|
||||
[FieldOffset(0x00)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy1;
|
||||
[FieldOffset(0x20)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy2;
|
||||
[FieldOffset(0x40)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy3;
|
||||
[FieldOffset(0x60)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy4;
|
||||
#endif
|
||||
|
||||
[FieldOffset(0x00)] public AesKey MasterKek;
|
||||
[FieldOffset(0x80)] public AesKey Package1Key;
|
||||
public AesKey MasterKek;
|
||||
public Array112<byte> Unused;
|
||||
public AesKey Package1Key;
|
||||
|
||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||
public readonly ReadOnlySpan<byte> ReadOnlyBytes => SpanHelpers.AsReadOnlyByteSpan(in this);
|
||||
public readonly ReadOnlySpan<byte> BytesRo => SpanHelpers.AsReadOnlyByteSpan(in this);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool IsZeros()
|
||||
{
|
||||
ReadOnlySpan<ulong> ulongSpan = MemoryMarshal.Cast<byte, ulong>(ReadOnlyBytes);
|
||||
|
||||
for (int i = 0; i < ulongSpan.Length; i++)
|
||||
foreach (ulong val in SpanHelpers.AsReadOnlySpan<KeyBlob, ulong>(in this))
|
||||
{
|
||||
if (ulongSpan[i] != 0)
|
||||
if (val != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -84,10 +57,6 @@ public struct KeyBlob
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<byte>(in KeyBlob value)
|
||||
{
|
||||
return SpanHelpers.AsReadOnlyByteSpan(in value);
|
||||
}
|
||||
|
||||
public readonly override string ToString() => ReadOnlyBytes.ToHexString();
|
||||
public static implicit operator ReadOnlySpan<byte>(in KeyBlob value) => SpanHelpers.AsReadOnlyByteSpan(in value);
|
||||
public readonly override string ToString() => BytesRo.ToHexString();
|
||||
}
|
@ -1,71 +1,63 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.FixedArrays;
|
||||
using LibHac.Common.Keys;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.Boot;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x170)]
|
||||
public struct Package1MarikoOemHeader
|
||||
{
|
||||
[FieldOffset(0x000)] private byte _aesMac;
|
||||
[FieldOffset(0x010)] private byte _rsaSig;
|
||||
[FieldOffset(0x110)] private byte _salt;
|
||||
[FieldOffset(0x130)] private byte _hash;
|
||||
[FieldOffset(0x150)] public int Version;
|
||||
[FieldOffset(0x154)] public int Size;
|
||||
[FieldOffset(0x158)] public int LoadAddress;
|
||||
[FieldOffset(0x15C)] public int EntryPoint;
|
||||
[FieldOffset(0x160)] private byte _reserved;
|
||||
|
||||
public ReadOnlySpan<byte> AesMac => SpanHelpers.CreateSpan(ref _aesMac, 0x10);
|
||||
public ReadOnlySpan<byte> RsaSig => SpanHelpers.CreateSpan(ref _rsaSig, 0x100);
|
||||
public ReadOnlySpan<byte> Salt => SpanHelpers.CreateSpan(ref _salt, 0x20);
|
||||
public ReadOnlySpan<byte> Hash => SpanHelpers.CreateSpan(ref _hash, 0x20);
|
||||
public ReadOnlySpan<byte> Reserved => SpanHelpers.CreateSpan(ref _reserved, 0x10);
|
||||
public Array16<byte> AesMac;
|
||||
public Array256<byte> RsaSig;
|
||||
public Array32<byte> Salt;
|
||||
public Array32<byte> Hash;
|
||||
public int Version;
|
||||
public int Size;
|
||||
public int LoadAddress;
|
||||
public int EntryPoint;
|
||||
public Array16<byte> Reserved;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x20)]
|
||||
public struct Package1MetaData
|
||||
{
|
||||
[FieldOffset(0x00)] public uint LoaderHash;
|
||||
[FieldOffset(0x04)] public uint SecureMonitorHash;
|
||||
[FieldOffset(0x08)] public uint BootloaderHash;
|
||||
[FieldOffset(0x10)] private byte _buildDate;
|
||||
[FieldOffset(0x1E)] public byte KeyGeneration;
|
||||
[FieldOffset(0x1F)] public byte Version;
|
||||
public uint LoaderHash;
|
||||
public uint SecureMonitorHash;
|
||||
public uint BootloaderHash;
|
||||
public uint Reserved;
|
||||
private Array14<byte> _buildDate;
|
||||
public byte KeyGeneration;
|
||||
public byte Version;
|
||||
|
||||
public U8Span BuildDate => new U8Span(SpanHelpers.CreateSpan(ref _buildDate, 0xE));
|
||||
public ReadOnlySpan<byte> Iv => SpanHelpers.CreateSpan(ref _buildDate, 0x10);
|
||||
public U8Span BuildDate => new U8Span(_buildDate);
|
||||
public ReadOnlySpan<byte> Iv => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_buildDate.Items), 0x10);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x20)]
|
||||
public struct Package1Stage1Footer
|
||||
{
|
||||
[FieldOffset(0x00)] public int Pk11Size;
|
||||
[FieldOffset(0x10)] private byte _iv;
|
||||
|
||||
public ReadOnlySpan<byte> Iv => SpanHelpers.CreateSpan(ref _iv, 0x10);
|
||||
public int Pk11Size;
|
||||
public Array12<byte> Reserved;
|
||||
public Array16<byte> Iv;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x20)]
|
||||
public struct Package1Pk11Header
|
||||
{
|
||||
public const uint ExpectedMagic = 0x31314B50; // PK11
|
||||
public static readonly uint ExpectedMagic = 0x31314B50; // PK11
|
||||
|
||||
[FieldOffset(0x00)] public uint Magic;
|
||||
[FieldOffset(0x04)] public int WarmBootSize;
|
||||
[FieldOffset(0x08)] public int WarmBootOffset;
|
||||
[FieldOffset(0x10)] public int BootloaderSize;
|
||||
[FieldOffset(0x14)] public int BootloaderOffset;
|
||||
[FieldOffset(0x18)] public int SecureMonitorSize;
|
||||
[FieldOffset(0x1C)] public int SecureMonitorOffset;
|
||||
public uint Magic;
|
||||
public int WarmBootSize;
|
||||
public int WarmBootOffset;
|
||||
public int Reserved;
|
||||
public int BootloaderSize;
|
||||
public int BootloaderOffset;
|
||||
public int SecureMonitorSize;
|
||||
public int SecureMonitorOffset;
|
||||
}
|
||||
|
||||
public enum Package1Section
|
||||
@ -102,13 +94,13 @@ public class Package1
|
||||
private Package1MetaData _metaData;
|
||||
private Package1Stage1Footer _stage1Footer;
|
||||
private Package1Pk11Header _pk11Header;
|
||||
private Buffer16 _pk11Mac;
|
||||
private Array16<byte> _pk11Mac;
|
||||
|
||||
public ref readonly Package1MarikoOemHeader MarikoOemHeader => ref _marikoOemHeader;
|
||||
public ref readonly Package1MetaData MetaData => ref _metaData;
|
||||
public ref readonly Package1Stage1Footer Stage1Footer => ref _stage1Footer;
|
||||
public ref readonly Package1Pk11Header Pk11Header => ref _pk11Header;
|
||||
public ref readonly Buffer16 Pk11Mac => ref _pk11Mac;
|
||||
public ref readonly Array16<byte> Pk11Mac => ref _pk11Mac;
|
||||
|
||||
public Result Initialize(KeySet keySet, in SharedRef<IStorage> storage)
|
||||
{
|
||||
@ -259,7 +251,7 @@ public class Package1
|
||||
|
||||
private Result ReadModernEristaMac()
|
||||
{
|
||||
return _baseStorage.Get.Read(ModernStage1Size + Pk11Size, _pk11Mac.Bytes);
|
||||
return _baseStorage.Get.Read(ModernStage1Size + Pk11Size, _pk11Mac.Items);
|
||||
}
|
||||
|
||||
private Result SetPk11Storage()
|
||||
@ -295,7 +287,7 @@ public class Package1
|
||||
else
|
||||
{
|
||||
decPk11Storage = new Aes128CtrStorage(encPk11Storage,
|
||||
KeySet.Package1Keys[KeyRevision].DataRo.ToArray(), _stage1Footer.Iv.ToArray(), true);
|
||||
KeySet.Package1Keys[KeyRevision].DataRo.ToArray(), _stage1Footer.Iv.ItemsRo.ToArray(), true);
|
||||
}
|
||||
|
||||
_pk11Storage = new SubStorage(new CachedStorage(decPk11Storage, 0x4000, 1, true), 0, Pk11Size);
|
||||
@ -359,7 +351,7 @@ public class Package1
|
||||
// MarikoOemHeader must be read first
|
||||
private bool IsMarikoImpl()
|
||||
{
|
||||
return MarikoOemHeader.AesMac.IsZeros() && MarikoOemHeader.Reserved.IsZeros();
|
||||
return MarikoOemHeader.AesMac.ItemsRo.IsZeros() && MarikoOemHeader.Reserved.ItemsRo.IsZeros();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -392,7 +384,7 @@ public class Package1
|
||||
|
||||
if (IsModern)
|
||||
{
|
||||
storages.Add(new MemoryStorage(_pk11Mac.Bytes.ToArray()));
|
||||
storages.Add(new MemoryStorage(_pk11Mac.ItemsRo.ToArray()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -437,7 +429,6 @@ public class Package1
|
||||
return new SubStorage(_pk11Storage, offset, size);
|
||||
}
|
||||
|
||||
|
||||
public IStorage OpenDecryptedWarmBootStorage()
|
||||
{
|
||||
if (!IsDecrypted)
|
||||
|
@ -1,16 +1,12 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.FixedArrays;
|
||||
using LibHac.Crypto;
|
||||
using LibHac.Util;
|
||||
#if DEBUG
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace LibHac.Boot;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x200)]
|
||||
public struct Package2Header
|
||||
{
|
||||
internal const int Package2SizeMax = (1024 * 1024 * 8) - (1024 * 16); // 8MB - 16KB
|
||||
@ -18,14 +14,12 @@ public struct Package2Header
|
||||
internal const int PayloadCount = 3;
|
||||
|
||||
internal const int SignatureSize = 0x100;
|
||||
private ReadOnlySpan<byte> RsaPublicKeyExponent => new byte[] { 0x00, 0x01, 0x00, 0x01 };
|
||||
private static ReadOnlySpan<byte> RsaPublicKeyExponent => new byte[] { 0x00, 0x01, 0x00, 0x01 };
|
||||
|
||||
[FieldOffset(0x00)] private byte _signature;
|
||||
[FieldOffset(0x100)] public Package2Meta Meta;
|
||||
public Array256<byte> Signature;
|
||||
public Package2Meta Meta;
|
||||
|
||||
public ReadOnlySpan<byte> Signature => SpanHelpers.CreateSpan(ref _signature, SignatureSize);
|
||||
|
||||
public Result VerifySignature(ReadOnlySpan<byte> modulus, ReadOnlySpan<byte> data)
|
||||
public readonly Result VerifySignature(ReadOnlySpan<byte> modulus, ReadOnlySpan<byte> data)
|
||||
{
|
||||
if (!Rsa.VerifyRsa2048PssSha256(Signature, modulus, RsaPublicKeyExponent, data))
|
||||
{
|
||||
@ -34,53 +28,38 @@ public struct Package2Header
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] [FieldOffset(0x00)] private readonly Padding100 PaddingForVsDebugging;
|
||||
#endif
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x100)]
|
||||
public struct Package2Meta
|
||||
{
|
||||
public const uint ExpectedMagicValue = 0x31324B50; // PK21
|
||||
public static readonly uint ExpectedMagicValue = 0x31324B50; // PK21
|
||||
|
||||
[FieldOffset(0x00)] private Buffer16 _headerIv;
|
||||
public Array16<byte> HeaderIv;
|
||||
public Array3<Array16<byte>> PayloadIvs;
|
||||
public Array16<byte> Padding40;
|
||||
|
||||
[FieldOffset(0x00)] private uint _package2Size;
|
||||
[FieldOffset(0x04)] private byte _keyGeneration;
|
||||
public uint Magic;
|
||||
public uint EntryPoint;
|
||||
public Array4<byte> Padding58;
|
||||
public byte Package2Version;
|
||||
public byte BootloaderVersion;
|
||||
|
||||
[FieldOffset(0x06)] private byte _keyGenerationXor1;
|
||||
[FieldOffset(0x07)] private byte _keyGenerationXor2;
|
||||
[FieldOffset(0x08)] private uint _sizeXor1;
|
||||
[FieldOffset(0x0C)] private uint _sizeXor2;
|
||||
public Array3<uint> PayloadSizes;
|
||||
public Array4<byte> Padding6C;
|
||||
public Array3<uint> PayloadOffsets;
|
||||
public Array4<byte> Padding7C;
|
||||
public Array3<Array32<byte>> PayloadHashes;
|
||||
public Array32<byte> PaddingE0;
|
||||
|
||||
[FieldOffset(0x10)] private Buffer16 _payloadIvs;
|
||||
public readonly uint GetSize()
|
||||
{
|
||||
ReadOnlySpan<uint> ints = SpanHelpers.AsReadOnlySpan<Array16<byte>, uint>(in HeaderIv);
|
||||
return ints[0] ^ ints[2] ^ ints[3];
|
||||
}
|
||||
|
||||
[FieldOffset(0x50)] private readonly uint _magic;
|
||||
[FieldOffset(0x54)] private readonly uint _entryPoint;
|
||||
[FieldOffset(0x5C)] private readonly byte _package2Version;
|
||||
[FieldOffset(0x5D)] private readonly byte _bootloaderVersion;
|
||||
public readonly byte GetKeyGeneration() => (byte)Math.Max(0, (HeaderIv[4] ^ HeaderIv[6] ^ HeaderIv[7]) - 1);
|
||||
|
||||
[FieldOffset(0x60)] private uint _payloadSizes;
|
||||
[FieldOffset(0x70)] private uint _payloadOffsets;
|
||||
[FieldOffset(0x80)] private Buffer32 _payloadHashes;
|
||||
|
||||
public uint Magic => _magic;
|
||||
public uint EntryPoint => _entryPoint;
|
||||
public byte Package2Version => _package2Version;
|
||||
public byte BootloaderVersion => _bootloaderVersion;
|
||||
|
||||
public Buffer16 HeaderIv => _headerIv;
|
||||
public readonly uint Size => _package2Size ^ _sizeXor1 ^ _sizeXor2;
|
||||
public byte KeyGeneration => (byte)Math.Max(0, (_keyGeneration ^ _keyGenerationXor1 ^ _keyGenerationXor2) - 1);
|
||||
|
||||
public ReadOnlySpan<Buffer16> PayloadIvs => SpanHelpers.CreateSpan(ref _payloadIvs, Package2Header.PayloadCount);
|
||||
public ReadOnlySpan<uint> PayloadSizes => SpanHelpers.CreateSpan(ref _payloadSizes, Package2Header.PayloadCount);
|
||||
public ReadOnlySpan<uint> PayloadOffsets => SpanHelpers.CreateSpan(ref _payloadOffsets, Package2Header.PayloadCount);
|
||||
public ReadOnlySpan<Buffer32> PayloadHashes => SpanHelpers.CreateSpan(ref _payloadHashes, Package2Header.PayloadCount);
|
||||
|
||||
public int GetPayloadFileOffset(int index)
|
||||
public readonly int GetPayloadFileOffset(int index)
|
||||
{
|
||||
if ((uint)index >= Package2Header.PayloadCount)
|
||||
throw new IndexOutOfRangeException("Invalid payload index.");
|
||||
@ -95,11 +74,11 @@ public struct Package2Meta
|
||||
return offset;
|
||||
}
|
||||
|
||||
public Result Verify()
|
||||
public readonly Result Verify()
|
||||
{
|
||||
// Get the obfuscated metadata.
|
||||
uint size = Size;
|
||||
byte keyGeneration = KeyGeneration;
|
||||
uint size = GetSize();
|
||||
byte keyGeneration = GetKeyGeneration();
|
||||
|
||||
// Check that size is big enough for the header.
|
||||
if (size < Unsafe.SizeOf<Package2Header>())
|
||||
@ -128,7 +107,7 @@ public struct Package2Meta
|
||||
}
|
||||
|
||||
// Check that the sizes sum to the total.
|
||||
if (Size != Unsafe.SizeOf<Package2Header>() + PayloadSizes[0] + PayloadSizes[1] + PayloadSizes[2])
|
||||
if (GetSize() != Unsafe.SizeOf<Package2Header>() + PayloadSizes[0] + PayloadSizes[1] + PayloadSizes[2])
|
||||
return ResultLibHac.InvalidPackage2MetaTotalSize.Log();
|
||||
|
||||
// Check that the payloads do not overflow.
|
||||
@ -156,8 +135,4 @@ public struct Package2Meta
|
||||
// No payload contains the entrypoint, so we're not valid.
|
||||
return ResultLibHac.InvalidPackage2MetaEntryPointNotFound.Log();
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] [FieldOffset(0x00)] private readonly Padding100 PaddingForVsDebugging;
|
||||
#endif
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.FixedArrays;
|
||||
using LibHac.Common.Keys;
|
||||
using LibHac.Crypto;
|
||||
using LibHac.Fs;
|
||||
@ -41,7 +42,7 @@ public class Package2StorageReader : IDisposable
|
||||
Result rc = storage.Get.Read(0, SpanHelpers.AsByteSpan(ref _header));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
_key = keySet.Package2Keys[_header.Meta.KeyGeneration];
|
||||
_key = keySet.Package2Keys[_header.Meta.GetKeyGeneration()];
|
||||
DecryptHeader(_key, ref _header.Meta, ref _header.Meta);
|
||||
|
||||
_storage.SetByCopy(in storage);
|
||||
@ -54,7 +55,7 @@ public class Package2StorageReader : IDisposable
|
||||
/// </summary>
|
||||
/// <param name="outPayloadStorage">If the method returns successfully, contains an <see cref="IStorage"/>
|
||||
/// of the specified payload.</param>
|
||||
/// <param name="index">The index of the payload to get. Must me less than <see cref="Package2Header.PayloadCount"/></param>
|
||||
/// <param name="index">The index of the payload to get. Must be less than <see cref="Package2Header.PayloadCount"/></param>
|
||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||
public Result OpenPayload(ref UniqueRef<IStorage> outPayloadStorage, int index)
|
||||
{
|
||||
@ -72,7 +73,7 @@ public class Package2StorageReader : IDisposable
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
byte[] iv = _header.Meta.PayloadIvs[index].Bytes.ToArray();
|
||||
byte[] iv = _header.Meta.PayloadIvs[index].ItemsRo.ToArray();
|
||||
outPayloadStorage.Reset(new CachedStorage(new Aes128CtrStorage(payloadSubStorage, _key.DataRo.ToArray(), iv, true), 0x4000, 1, true));
|
||||
return Result.Success;
|
||||
}
|
||||
@ -219,7 +220,7 @@ public class Package2StorageReader : IDisposable
|
||||
var storages = new List<IStorage>(4);
|
||||
|
||||
// The signature and IV are unencrypted
|
||||
int unencryptedHeaderSize = Package2Header.SignatureSize + Unsafe.SizeOf<Buffer16>();
|
||||
int unencryptedHeaderSize = Package2Header.SignatureSize + Unsafe.SizeOf<Array16<byte>>();
|
||||
int encryptedHeaderSize = Unsafe.SizeOf<Package2Header>() - unencryptedHeaderSize;
|
||||
|
||||
// Get signature and IV
|
||||
@ -230,7 +231,7 @@ public class Package2StorageReader : IDisposable
|
||||
|
||||
// The counter starts counting at the beginning of the meta struct, but the first block in
|
||||
// the struct isn't encrypted. Increase the counter by one to skip that block.
|
||||
byte[] iv = _header.Meta.HeaderIv.Bytes.ToArray();
|
||||
byte[] iv = _header.Meta.HeaderIv.ItemsRo.ToArray();
|
||||
Utilities.IncrementByteArray(iv);
|
||||
|
||||
storages.Add(new CachedStorage(new Aes128CtrStorage(encMetaStorage, _key.DataRo.ToArray(), iv, true), 0x100, 1, true));
|
||||
@ -254,12 +255,12 @@ public class Package2StorageReader : IDisposable
|
||||
|
||||
private void DecryptHeader(ReadOnlySpan<byte> key, ref Package2Meta source, ref Package2Meta dest)
|
||||
{
|
||||
Buffer16 iv = source.HeaderIv;
|
||||
Array16<byte> iv = source.HeaderIv;
|
||||
|
||||
Aes.DecryptCtr128(SpanHelpers.AsByteSpan(ref source), SpanHelpers.AsByteSpan(ref dest), key, iv);
|
||||
|
||||
// Copy the IV to the output because the IV field will be garbage after "decrypting" it
|
||||
Unsafe.As<Package2Meta, Buffer16>(ref dest) = iv;
|
||||
dest.HeaderIv = iv;
|
||||
}
|
||||
|
||||
private bool HasIniPayload()
|
||||
|
13
src/LibHac/Common/Box.cs
Normal file
13
src/LibHac/Common/Box.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace LibHac.Common;
|
||||
|
||||
public class Box<T> where T : struct
|
||||
{
|
||||
private T _value;
|
||||
|
||||
public ref T Value => ref _value;
|
||||
|
||||
public Box()
|
||||
{
|
||||
_value = new T();
|
||||
}
|
||||
}
|
@ -10,7 +10,6 @@ namespace LibHac.Common;
|
||||
/// Represents a buffer of 16 bytes.
|
||||
/// Contains functions that assist with common operations on small buffers.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Sequential, Size = 16)]
|
||||
public struct Buffer16
|
||||
{
|
||||
@ -67,7 +66,6 @@ public struct Buffer16
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Sequential, Size = 32)]
|
||||
public struct Buffer32
|
||||
{
|
||||
@ -124,4 +122,4 @@ public struct Buffer32
|
||||
{
|
||||
return Bytes.ToHexString();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +1,20 @@
|
||||
using System;
|
||||
#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;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array1<T>
|
||||
{
|
||||
public const int Length = 1;
|
||||
|
||||
private T _1;
|
||||
private T _0;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _0, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array1<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array1024.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array1024.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 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;
|
||||
}
|
30
src/LibHac/Common/FixedArrays/Array11.cs
Normal file
30
src/LibHac/Common/FixedArrays/Array11.cs
Normal file
@ -0,0 +1,30 @@
|
||||
#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;
|
||||
|
||||
namespace LibHac.Common.FixedArrays;
|
||||
|
||||
public struct Array11<T>
|
||||
{
|
||||
public const int Length = 11;
|
||||
|
||||
private T _0;
|
||||
private T _1;
|
||||
private T _2;
|
||||
private T _3;
|
||||
private T _4;
|
||||
private T _5;
|
||||
private T _6;
|
||||
private T _7;
|
||||
private T _8;
|
||||
private T _9;
|
||||
private T _10;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _0, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array11<T> value) => value.ItemsRo;
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array112.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array112.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 Array112<T>
|
||||
{
|
||||
public const int Length = 112;
|
||||
|
||||
private Array80<T> _0;
|
||||
private Array32<T> _80;
|
||||
|
||||
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 Array112<T> value) => value.ItemsRo;
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
using System;
|
||||
#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;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array12<T>
|
||||
{
|
||||
public const int Length = 12;
|
||||
|
||||
private T _0;
|
||||
private T _1;
|
||||
private T _2;
|
||||
private T _3;
|
||||
@ -20,13 +20,12 @@ public struct Array12<T>
|
||||
private T _9;
|
||||
private T _10;
|
||||
private T _11;
|
||||
private T _12;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _0, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array12<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
using System;
|
||||
#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;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array128<T>
|
||||
{
|
||||
public const int Length = 128;
|
||||
@ -28,4 +28,4 @@ public struct Array128<T>
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array128<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array14.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array14.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 Array14<T>
|
||||
{
|
||||
public const int Length = 14;
|
||||
|
||||
private Array8<T> _0;
|
||||
private Array6<T> _8;
|
||||
|
||||
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 Array14<T> value) => value.ItemsRo;
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array144.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array144.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 Array144<T>
|
||||
{
|
||||
public const int Length = 144;
|
||||
|
||||
private Array128<T> _0;
|
||||
private Array16<T> _128;
|
||||
|
||||
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 Array144<T> value) => value.ItemsRo;
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array15.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array15.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 Array15<T>
|
||||
{
|
||||
public const int Length = 15;
|
||||
|
||||
private Array8<T> _0;
|
||||
private Array7<T> _8;
|
||||
|
||||
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 Array15<T> value) => value.ItemsRo;
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
using System;
|
||||
#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;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array16<T>
|
||||
{
|
||||
public const int Length = 16;
|
||||
@ -28,4 +28,4 @@ public struct Array16<T>
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array16<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array18.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array18.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 Array18<T>
|
||||
{
|
||||
public const int Length = 18;
|
||||
|
||||
private Array16<T> _0;
|
||||
private Array2<T> _16;
|
||||
|
||||
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 Array18<T> value) => value.ItemsRo;
|
||||
}
|
@ -1,22 +1,21 @@
|
||||
using System;
|
||||
#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;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array2<T>
|
||||
{
|
||||
public const int Length = 2;
|
||||
|
||||
private T _0;
|
||||
private T _1;
|
||||
private T _2;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _0, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array2<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array20.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array20.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 Array20<T>
|
||||
{
|
||||
public const int Length = 20;
|
||||
|
||||
private Array16<T> _0;
|
||||
private Array4<T> _16;
|
||||
|
||||
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 Array20<T> value) => value.ItemsRo;
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array2048.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array2048.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 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;
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array24.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array24.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 Array24<T>
|
||||
{
|
||||
public const int Length = 24;
|
||||
|
||||
private Array16<T> _0;
|
||||
private Array8<T> _16;
|
||||
|
||||
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 Array24<T> value) => value.ItemsRo;
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
using System;
|
||||
#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;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array256<T>
|
||||
{
|
||||
public const int Length = 256;
|
||||
@ -28,4 +28,4 @@ public struct Array256<T>
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array256<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
32
src/LibHac/Common/FixedArrays/Array26.cs
Normal file
32
src/LibHac/Common/FixedArrays/Array26.cs
Normal file
@ -0,0 +1,32 @@
|
||||
#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 Array26<T>
|
||||
{
|
||||
public const int Length = 26;
|
||||
|
||||
private Array16<T> _0;
|
||||
private Array8<T> _16;
|
||||
private Array2<T> _24;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length);
|
||||
}
|
||||
|
||||
public readonly ReadOnlySpan<T> ItemsRo
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array26<T> value) => value.ItemsRo;
|
||||
}
|
32
src/LibHac/Common/FixedArrays/Array28.cs
Normal file
32
src/LibHac/Common/FixedArrays/Array28.cs
Normal file
@ -0,0 +1,32 @@
|
||||
#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 Array28<T>
|
||||
{
|
||||
public const int Length = 28;
|
||||
|
||||
private Array16<T> _0;
|
||||
private Array8<T> _16;
|
||||
private Array4<T> _24;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length);
|
||||
}
|
||||
|
||||
public readonly ReadOnlySpan<T> ItemsRo
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array28<T> value) => value.ItemsRo;
|
||||
}
|
@ -1,23 +1,22 @@
|
||||
using System;
|
||||
#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;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array3<T>
|
||||
{
|
||||
public const int Length = 3;
|
||||
|
||||
private T _0;
|
||||
private T _1;
|
||||
private T _2;
|
||||
private T _3;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _0, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array3<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
34
src/LibHac/Common/FixedArrays/Array3000.cs
Normal file
34
src/LibHac/Common/FixedArrays/Array3000.cs
Normal file
@ -0,0 +1,34 @@
|
||||
#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 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;
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
using System;
|
||||
#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;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array32<T>
|
||||
{
|
||||
public const int Length = 32;
|
||||
@ -28,4 +28,4 @@ public struct Array32<T>
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array32<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array36.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array36.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 Array36<T>
|
||||
{
|
||||
public const int Length = 36;
|
||||
|
||||
private Array32<T> _0;
|
||||
private Array4<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 Array36<T> value) => value.ItemsRo;
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array37.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array37.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 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;
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array38.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array38.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 Array38<T>
|
||||
{
|
||||
public const int Length = 38;
|
||||
|
||||
private Array32<T> _0;
|
||||
private Array6<T> _32;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length);
|
||||
}
|
||||
|
||||
public readonly ReadOnlySpan<T> ItemsRo
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array38<T> value) => value.ItemsRo;
|
||||
}
|
@ -1,24 +1,23 @@
|
||||
using System;
|
||||
#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;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array4<T>
|
||||
{
|
||||
public const int Length = 4;
|
||||
|
||||
private T _0;
|
||||
private T _1;
|
||||
private T _2;
|
||||
private T _3;
|
||||
private T _4;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _0, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array4<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
32
src/LibHac/Common/FixedArrays/Array400.cs
Normal file
32
src/LibHac/Common/FixedArrays/Array400.cs
Normal file
@ -0,0 +1,32 @@
|
||||
#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 Array400<T>
|
||||
{
|
||||
public const int Length = 400;
|
||||
|
||||
private Array256<T> _0;
|
||||
private Array128<T> _256;
|
||||
private Array16<T> _384;
|
||||
|
||||
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 Array400<T> value) => value.ItemsRo;
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array48.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array48.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 Array48<T>
|
||||
{
|
||||
public const int Length = 48;
|
||||
|
||||
private Array32<T> _0;
|
||||
private Array16<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 Array48<T> value) => value.ItemsRo;
|
||||
}
|
@ -1,25 +1,24 @@
|
||||
using System;
|
||||
#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;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array5<T>
|
||||
{
|
||||
public const int Length = 5;
|
||||
|
||||
private T _0;
|
||||
private T _1;
|
||||
private T _2;
|
||||
private T _3;
|
||||
private T _4;
|
||||
private T _5;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _0, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array5<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array512.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array512.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 Array512<T>
|
||||
{
|
||||
public const int Length = 512;
|
||||
|
||||
private Array256<T> _0;
|
||||
private Array256<T> _256;
|
||||
|
||||
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 Array512<T> value) => value.ItemsRo;
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array56.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array56.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 Array56<T>
|
||||
{
|
||||
public const int Length = 56;
|
||||
|
||||
private Array32<T> _0;
|
||||
private Array24<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 Array56<T> value) => value.ItemsRo;
|
||||
}
|
@ -1,26 +1,25 @@
|
||||
using System;
|
||||
#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;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array6<T>
|
||||
{
|
||||
public const int Length = 6;
|
||||
|
||||
private T _0;
|
||||
private T _1;
|
||||
private T _2;
|
||||
private T _3;
|
||||
private T _4;
|
||||
private T _5;
|
||||
private T _6;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _0, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array6<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
32
src/LibHac/Common/FixedArrays/Array60.cs
Normal file
32
src/LibHac/Common/FixedArrays/Array60.cs
Normal file
@ -0,0 +1,32 @@
|
||||
#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 Array60<T>
|
||||
{
|
||||
public const int Length = 60;
|
||||
|
||||
private Array32<T> _0;
|
||||
private Array16<T> _32;
|
||||
private Array12<T> _48;
|
||||
|
||||
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 Array60<T> value) => value.ItemsRo;
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
using System;
|
||||
#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;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array64<T>
|
||||
{
|
||||
public const int Length = 64;
|
||||
@ -28,4 +28,4 @@ public struct Array64<T>
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array64<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array65.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array65.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 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;
|
||||
}
|
26
src/LibHac/Common/FixedArrays/Array7.cs
Normal file
26
src/LibHac/Common/FixedArrays/Array7.cs
Normal file
@ -0,0 +1,26 @@
|
||||
#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;
|
||||
|
||||
namespace LibHac.Common.FixedArrays;
|
||||
|
||||
public struct Array7<T>
|
||||
{
|
||||
public const int Length = 7;
|
||||
|
||||
private T _0;
|
||||
private T _1;
|
||||
private T _2;
|
||||
private T _3;
|
||||
private T _4;
|
||||
private T _5;
|
||||
private T _6;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _0, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array7<T> value) => value.ItemsRo;
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array768.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array768.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 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;
|
||||
}
|
32
src/LibHac/Common/FixedArrays/Array769.cs
Normal file
32
src/LibHac/Common/FixedArrays/Array769.cs
Normal file
@ -0,0 +1,32 @@
|
||||
#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 Array769<T>
|
||||
{
|
||||
public const int Length = 769;
|
||||
|
||||
private Array512<T> _0;
|
||||
private Array256<T> _512;
|
||||
private Array1<T> _768;
|
||||
|
||||
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 Array769<T> value) => value.ItemsRo;
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
using System;
|
||||
#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;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Array8<T>
|
||||
{
|
||||
public const int Length = 8;
|
||||
|
||||
private T _0;
|
||||
private T _1;
|
||||
private T _2;
|
||||
private T _3;
|
||||
@ -16,13 +16,12 @@ public struct Array8<T>
|
||||
private T _5;
|
||||
private T _6;
|
||||
private T _7;
|
||||
private T _8;
|
||||
|
||||
public ref T this[int i] => ref Items[i];
|
||||
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _0, Length);
|
||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _0, Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array8<T> value) => value.ItemsRo;
|
||||
}
|
||||
}
|
31
src/LibHac/Common/FixedArrays/Array80.cs
Normal file
31
src/LibHac/Common/FixedArrays/Array80.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#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 Array80<T>
|
||||
{
|
||||
public const int Length = 80;
|
||||
|
||||
private Array64<T> _0;
|
||||
private Array16<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 Array80<T> value) => value.ItemsRo;
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Util;
|
||||
|
||||
@ -8,7 +7,6 @@ namespace LibHac.Common;
|
||||
/// <summary>
|
||||
/// A generic 128-bit ID value.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||
public struct Id128 : IEquatable<Id128>, IComparable<Id128>, IComparable
|
||||
{
|
||||
@ -81,4 +79,4 @@ public struct Id128 : IEquatable<Id128>, IComparable<Id128>, IComparable
|
||||
public static bool operator >(Id128 left, Id128 right) => left.CompareTo(right) > 0;
|
||||
public static bool operator <=(Id128 left, Id128 right) => left.CompareTo(right) <= 0;
|
||||
public static bool operator >=(Id128 left, Id128 right) => left.CompareTo(right) >= 0;
|
||||
}
|
||||
}
|
@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.Common;
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||
public struct Key128 : IEquatable<Key128>
|
||||
{
|
||||
@ -42,4 +40,4 @@ public struct Key128 : IEquatable<Key128>
|
||||
|
||||
public static bool operator ==(Key128 left, Key128 right) => left.Equals(right);
|
||||
public static bool operator !=(Key128 left, Key128 right) => !(left == right);
|
||||
}
|
||||
}
|
@ -19,57 +19,57 @@ internal static partial class DefaultKeySet
|
||||
// Fill the key set with any key structs included in the library.
|
||||
if (RootKeysDev.Length == Unsafe.SizeOf<RootKeys>())
|
||||
{
|
||||
keySet.KeyStruct._rootKeysDev = MemoryMarshal.Cast<byte, RootKeys>(RootKeysDev)[0];
|
||||
keySet.KeyStruct.RootKeysDev = MemoryMarshal.Cast<byte, RootKeys>(RootKeysDev)[0];
|
||||
}
|
||||
|
||||
if (RootKeysProd.Length == Unsafe.SizeOf<RootKeys>())
|
||||
{
|
||||
keySet.KeyStruct._rootKeysProd = MemoryMarshal.Cast<byte, RootKeys>(RootKeysProd)[0];
|
||||
keySet.KeyStruct.RootKeysProd = MemoryMarshal.Cast<byte, RootKeys>(RootKeysProd)[0];
|
||||
}
|
||||
|
||||
if (KeySeeds.Length == Unsafe.SizeOf<KeySeeds>())
|
||||
{
|
||||
keySet.KeyStruct._keySeeds = MemoryMarshal.Cast<byte, KeySeeds>(KeySeeds)[0];
|
||||
keySet.KeyStruct.KeySeeds = MemoryMarshal.Cast<byte, KeySeeds>(KeySeeds)[0];
|
||||
}
|
||||
|
||||
if (StoredKeysDev.Length == Unsafe.SizeOf<StoredKeys>())
|
||||
{
|
||||
keySet.KeyStruct._storedKeysDev = MemoryMarshal.Cast<byte, StoredKeys>(StoredKeysDev)[0];
|
||||
keySet.KeyStruct.StoredKeysDev = MemoryMarshal.Cast<byte, StoredKeys>(StoredKeysDev)[0];
|
||||
}
|
||||
|
||||
if (StoredKeysProd.Length == Unsafe.SizeOf<StoredKeys>())
|
||||
{
|
||||
keySet.KeyStruct._storedKeysProd = MemoryMarshal.Cast<byte, StoredKeys>(StoredKeysProd)[0];
|
||||
keySet.KeyStruct.StoredKeysProd = MemoryMarshal.Cast<byte, StoredKeys>(StoredKeysProd)[0];
|
||||
}
|
||||
|
||||
if (DerivedKeysDev.Length == Unsafe.SizeOf<DerivedKeys>())
|
||||
{
|
||||
keySet.KeyStruct._derivedKeysDev = MemoryMarshal.Cast<byte, DerivedKeys>(DerivedKeysDev)[0];
|
||||
keySet.KeyStruct.DerivedKeysDev = MemoryMarshal.Cast<byte, DerivedKeys>(DerivedKeysDev)[0];
|
||||
}
|
||||
|
||||
if (DerivedKeysProd.Length == Unsafe.SizeOf<DerivedKeys>())
|
||||
{
|
||||
keySet.KeyStruct._derivedKeysProd = MemoryMarshal.Cast<byte, DerivedKeys>(DerivedKeysProd)[0];
|
||||
keySet.KeyStruct.DerivedKeysProd = MemoryMarshal.Cast<byte, DerivedKeys>(DerivedKeysProd)[0];
|
||||
}
|
||||
|
||||
if (DeviceKeys.Length == Unsafe.SizeOf<DeviceKeys>())
|
||||
{
|
||||
keySet.KeyStruct._deviceKeys = MemoryMarshal.Cast<byte, DeviceKeys>(DeviceKeys)[0];
|
||||
keySet.KeyStruct.DeviceKeys = MemoryMarshal.Cast<byte, DeviceKeys>(DeviceKeys)[0];
|
||||
}
|
||||
|
||||
if (RsaSigningKeysDev.Length == Unsafe.SizeOf<RsaSigningKeys>())
|
||||
{
|
||||
keySet.KeyStruct._rsaSigningKeysDev = MemoryMarshal.Cast<byte, RsaSigningKeys>(RsaSigningKeysDev)[0];
|
||||
keySet.KeyStruct.RsaSigningKeysDev = MemoryMarshal.Cast<byte, RsaSigningKeys>(RsaSigningKeysDev)[0];
|
||||
}
|
||||
|
||||
if (RsaSigningKeysProd.Length == Unsafe.SizeOf<RsaSigningKeys>())
|
||||
{
|
||||
keySet.KeyStruct._rsaSigningKeysProd = MemoryMarshal.Cast<byte, RsaSigningKeys>(RsaSigningKeysProd)[0];
|
||||
keySet.KeyStruct.RsaSigningKeysProd = MemoryMarshal.Cast<byte, RsaSigningKeys>(RsaSigningKeysProd)[0];
|
||||
}
|
||||
|
||||
if (RsaKeys.Length == Unsafe.SizeOf<RsaKeys>())
|
||||
{
|
||||
keySet.KeyStruct._rsaKeys = MemoryMarshal.Cast<byte, RsaKeys>(RsaKeys)[0];
|
||||
keySet.KeyStruct.RsaKeys = MemoryMarshal.Cast<byte, RsaKeys>(RsaKeys)[0];
|
||||
}
|
||||
|
||||
return keySet;
|
||||
@ -177,4 +177,4 @@ internal static partial class DefaultKeySet
|
||||
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using LibHac.Boot;
|
||||
using LibHac.Common.FixedArrays;
|
||||
@ -31,11 +30,11 @@ public class KeySet
|
||||
public ref AllKeys KeyStruct => ref _keys;
|
||||
public Mode CurrentMode => _mode;
|
||||
|
||||
private ref RootKeys RootKeys => ref _mode == Mode.Dev ? ref _keys._rootKeysDev : ref _keys._rootKeysProd;
|
||||
private ref StoredKeys StoredKeys => ref _mode == Mode.Dev ? ref _keys._storedKeysDev : ref _keys._storedKeysProd;
|
||||
private ref DerivedKeys DerivedKeys => ref _mode == Mode.Dev ? ref _keys._derivedKeysDev : ref _keys._derivedKeysProd;
|
||||
private ref RsaSigningKeys RsaSigningKeys => ref _mode == Mode.Dev ? ref _keys._rsaSigningKeysDev : ref _keys._rsaSigningKeysProd;
|
||||
private ref RsaKeys RsaKeys => ref _keys._rsaKeys;
|
||||
private ref RootKeys RootKeys => ref _mode == Mode.Dev ? ref _keys.RootKeysDev : ref _keys.RootKeysProd;
|
||||
private ref StoredKeys StoredKeys => ref _mode == Mode.Dev ? ref _keys.StoredKeysDev : ref _keys.StoredKeysProd;
|
||||
private ref DerivedKeys DerivedKeys => ref _mode == Mode.Dev ? ref _keys.DerivedKeysDev : ref _keys.DerivedKeysProd;
|
||||
private ref RsaSigningKeys RsaSigningKeys => ref _mode == Mode.Dev ? ref _keys.RsaSigningKeysDev : ref _keys.RsaSigningKeysProd;
|
||||
private ref RsaKeys RsaKeys => ref _keys.RsaKeys;
|
||||
|
||||
private ref RsaSigningKeyParameters RsaSigningKeyParams => ref _mode == Mode.Dev
|
||||
? ref _rsaSigningKeyParamsDev
|
||||
@ -47,44 +46,44 @@ public class KeySet
|
||||
public ref AesKey MarikoKek => ref RootKeys.MarikoKek;
|
||||
public ref AesKey MarikoBek => ref RootKeys.MarikoBek;
|
||||
public Span<KeyBlob> KeyBlobs => RootKeys.KeyBlobs.Items;
|
||||
public Span<AesKey> KeyBlobKeySources => _keys._keySeeds.KeyBlobKeySources.Items;
|
||||
public ref AesKey KeyBlobMacKeySource => ref _keys._keySeeds.KeyBlobMacKeySource;
|
||||
public Span<AesKey> KeyBlobKeySources => _keys.KeySeeds.KeyBlobKeySources.Items;
|
||||
public ref AesKey KeyBlobMacKeySource => ref _keys.KeySeeds.KeyBlobMacKeySource;
|
||||
public ref AesKey TsecRootKek => ref RootKeys.TsecRootKek;
|
||||
public ref AesKey Package1MacKek => ref RootKeys.Package1MacKek;
|
||||
public ref AesKey Package1Kek => ref RootKeys.Package1Kek;
|
||||
public Span<AesKey> TsecAuthSignatures => RootKeys.TsecAuthSignatures.Items;
|
||||
public Span<AesKey> TsecRootKeys => RootKeys.TsecRootKeys.Items;
|
||||
public Span<AesKey> MasterKekSources => _keys._keySeeds.MasterKekSources.Items;
|
||||
public Span<AesKey> MasterKekSources => _keys.KeySeeds.MasterKekSources.Items;
|
||||
|
||||
public Span<AesKey> MarikoMasterKekSources => _mode == Mode.Dev
|
||||
? _keys._keySeeds.MarikoMasterKekSources_dev.Items
|
||||
: _keys._keySeeds.MarikoMasterKekSources.Items;
|
||||
? _keys.KeySeeds.MarikoMasterKekSourcesDev.Items
|
||||
: _keys.KeySeeds.MarikoMasterKekSources.Items;
|
||||
|
||||
public Span<AesKey> MasterKeks => DerivedKeys.MasterKeks.Items;
|
||||
public ref AesKey MasterKeySource => ref _keys._keySeeds.MasterKeySource;
|
||||
public ref AesKey MasterKeySource => ref _keys.KeySeeds.MasterKeySource;
|
||||
public Span<AesKey> MasterKeys => DerivedKeys.MasterKeys.Items;
|
||||
public Span<AesKey> Package1MacKeys => DerivedKeys.Package1MacKeys.Items;
|
||||
public Span<AesKey> Package1Keys => DerivedKeys.Package1Keys.Items;
|
||||
public Span<AesKey> Package2Keys => DerivedKeys.Package2Keys.Items;
|
||||
public ref AesKey Package2KeySource => ref _keys._keySeeds.Package2KeySource;
|
||||
public ref AesKey PerConsoleKeySource => ref _keys._keySeeds.PerConsoleKeySource;
|
||||
public ref AesKey RetailSpecificAesKeySource => ref _keys._keySeeds.RetailSpecificAesKeySource;
|
||||
public ref AesKey BisKekSource => ref _keys._keySeeds.BisKekSource;
|
||||
public Span<AesXtsKey> BisKeySources => _keys._keySeeds.BisKeySources.Items;
|
||||
public ref AesKey AesKekGenerationSource => ref _keys._keySeeds.AesKekGenerationSource;
|
||||
public ref AesKey AesKeyGenerationSource => ref _keys._keySeeds.AesKeyGenerationSource;
|
||||
public ref AesKey KeyAreaKeyApplicationSource => ref _keys._keySeeds.KeyAreaKeyApplicationSource;
|
||||
public ref AesKey KeyAreaKeyOceanSource => ref _keys._keySeeds.KeyAreaKeyOceanSource;
|
||||
public ref AesKey KeyAreaKeySystemSource => ref _keys._keySeeds.KeyAreaKeySystemSource;
|
||||
public ref AesKey TitleKekSource => ref _keys._keySeeds.TitleKekSource;
|
||||
public ref AesKey HeaderKekSource => ref _keys._keySeeds.HeaderKekSource;
|
||||
public ref AesKey SdCardKekSource => ref _keys._keySeeds.SdCardKekSource;
|
||||
public Span<AesXtsKey> SdCardKeySources => _keys._keySeeds.SdCardKeySources.Items;
|
||||
public ref AesKey DeviceUniqueSaveMacKekSource => ref _keys._keySeeds.DeviceUniqueSaveMacKekSource;
|
||||
public Span<AesKey> DeviceUniqueSaveMacKeySources => _keys._keySeeds.DeviceUniqueSaveMacKeySources.Items;
|
||||
public ref AesKey SeedUniqueSaveMacKekSource => ref _keys._keySeeds.SeedUniqueSaveMacKekSource;
|
||||
public ref AesKey SeedUniqueSaveMacKeySource => ref _keys._keySeeds.SeedUniqueSaveMacKeySource;
|
||||
public ref AesXtsKey HeaderKeySource => ref _keys._keySeeds.HeaderKeySource;
|
||||
public ref AesKey Package2KeySource => ref _keys.KeySeeds.Package2KeySource;
|
||||
public ref AesKey PerConsoleKeySource => ref _keys.KeySeeds.PerConsoleKeySource;
|
||||
public ref AesKey RetailSpecificAesKeySource => ref _keys.KeySeeds.RetailSpecificAesKeySource;
|
||||
public ref AesKey BisKekSource => ref _keys.KeySeeds.BisKekSource;
|
||||
public Span<AesXtsKey> BisKeySources => _keys.KeySeeds.BisKeySources.Items;
|
||||
public ref AesKey AesKekGenerationSource => ref _keys.KeySeeds.AesKekGenerationSource;
|
||||
public ref AesKey AesKeyGenerationSource => ref _keys.KeySeeds.AesKeyGenerationSource;
|
||||
public ref AesKey KeyAreaKeyApplicationSource => ref _keys.KeySeeds.KeyAreaKeyApplicationSource;
|
||||
public ref AesKey KeyAreaKeyOceanSource => ref _keys.KeySeeds.KeyAreaKeyOceanSource;
|
||||
public ref AesKey KeyAreaKeySystemSource => ref _keys.KeySeeds.KeyAreaKeySystemSource;
|
||||
public ref AesKey TitleKekSource => ref _keys.KeySeeds.TitleKekSource;
|
||||
public ref AesKey HeaderKekSource => ref _keys.KeySeeds.HeaderKekSource;
|
||||
public ref AesKey SdCardKekSource => ref _keys.KeySeeds.SdCardKekSource;
|
||||
public Span<AesXtsKey> SdCardKeySources => _keys.KeySeeds.SdCardKeySources.Items;
|
||||
public ref AesKey DeviceUniqueSaveMacKekSource => ref _keys.KeySeeds.DeviceUniqueSaveMacKekSource;
|
||||
public Span<AesKey> DeviceUniqueSaveMacKeySources => _keys.KeySeeds.DeviceUniqueSaveMacKeySources.Items;
|
||||
public ref AesKey SeedUniqueSaveMacKekSource => ref _keys.KeySeeds.SeedUniqueSaveMacKekSource;
|
||||
public ref AesKey SeedUniqueSaveMacKeySource => ref _keys.KeySeeds.SeedUniqueSaveMacKeySource;
|
||||
public ref AesXtsKey HeaderKeySource => ref _keys.KeySeeds.HeaderKeySource;
|
||||
public ref AesXtsKey HeaderKey => ref DerivedKeys.HeaderKey;
|
||||
public Span<AesKey> TitleKeks => DerivedKeys.TitleKeks.Items;
|
||||
public Span<Array3<AesKey>> KeyAreaKeys => DerivedKeys.KeyAreaKeys.Items;
|
||||
@ -92,19 +91,19 @@ public class KeySet
|
||||
public ref AesKey ETicketRsaKek => ref DerivedKeys.ETicketRsaKek;
|
||||
public ref AesKey SslRsaKek => ref DerivedKeys.SslRsaKek;
|
||||
|
||||
public ref AesKey SecureBootKey => ref _keys._deviceKeys.SecureBootKey;
|
||||
public ref AesKey TsecKey => ref _keys._deviceKeys.TsecKey;
|
||||
public Span<AesKey> KeyBlobKeys => _keys._deviceKeys.KeyBlobKeys.Items;
|
||||
public Span<AesKey> KeyBlobMacKeys => _keys._deviceKeys.KeyBlobMacKeys.Items;
|
||||
public Span<EncryptedKeyBlob> EncryptedKeyBlobs => _keys._deviceKeys.EncryptedKeyBlobs.Items;
|
||||
public ref AesKey DeviceKey => ref _keys._deviceKeys.DeviceKey;
|
||||
public Span<AesXtsKey> BisKeys => _keys._deviceKeys.BisKeys.Items;
|
||||
public Span<AesKey> DeviceUniqueSaveMacKeys => _keys._deviceKeys.DeviceUniqueSaveMacKeys.Items;
|
||||
public ref AesKey SeedUniqueSaveMacKey => ref _keys._deviceKeys.SeedUniqueSaveMacKey;
|
||||
public ref AesKey SdCardEncryptionSeed => ref _keys._deviceKeys.SdCardEncryptionSeed;
|
||||
public ref AesKey SecureBootKey => ref _keys.DeviceKeys.SecureBootKey;
|
||||
public ref AesKey TsecKey => ref _keys.DeviceKeys.TsecKey;
|
||||
public Span<AesKey> KeyBlobKeys => _keys.DeviceKeys.KeyBlobKeys.Items;
|
||||
public Span<AesKey> KeyBlobMacKeys => _keys.DeviceKeys.KeyBlobMacKeys.Items;
|
||||
public Span<EncryptedKeyBlob> EncryptedKeyBlobs => _keys.DeviceKeys.EncryptedKeyBlobs.Items;
|
||||
public ref AesKey DeviceKey => ref _keys.DeviceKeys.DeviceKey;
|
||||
public Span<AesXtsKey> BisKeys => _keys.DeviceKeys.BisKeys.Items;
|
||||
public Span<AesKey> DeviceUniqueSaveMacKeys => _keys.DeviceKeys.DeviceUniqueSaveMacKeys.Items;
|
||||
public ref AesKey SeedUniqueSaveMacKey => ref _keys.DeviceKeys.SeedUniqueSaveMacKey;
|
||||
public ref AesKey SdCardEncryptionSeed => ref _keys.DeviceKeys.SdCardEncryptionSeed;
|
||||
|
||||
// Todo: Make a separate type? Not actually an AES-XTS key, but it's still the same shape.
|
||||
public Span<AesXtsKey> SdCardEncryptionKeys => _keys._deviceKeys.SdCardEncryptionKeys.Items;
|
||||
public Span<AesXtsKey> SdCardEncryptionKeys => _keys.DeviceKeys.SdCardEncryptionKeys.Items;
|
||||
|
||||
public Span<RsaKey> NcaHeaderSigningKeys => RsaSigningKeys.NcaHeaderSigningKeys.Items;
|
||||
public Span<RsaKey> AcidSigningKeys => RsaSigningKeys.AcidSigningKeys.Items;
|
||||
@ -226,8 +225,8 @@ public class KeySet
|
||||
{
|
||||
return new RSAParameters
|
||||
{
|
||||
Exponent = key.PublicExponent.DataRo.ToArray(),
|
||||
Modulus = key.Modulus.DataRo.ToArray()
|
||||
Exponent = key.PublicExponent.ItemsRo.ToArray(),
|
||||
Modulus = key.Modulus.ItemsRo.ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
@ -235,14 +234,14 @@ public class KeySet
|
||||
{
|
||||
return new RSAParameters
|
||||
{
|
||||
D = key.PrivateExponent.DataRo.ToArray(),
|
||||
DP = key.Dp.DataRo.ToArray(),
|
||||
DQ = key.Dq.DataRo.ToArray(),
|
||||
Exponent = key.PublicExponent.DataRo.ToArray(),
|
||||
InverseQ = key.InverseQ.DataRo.ToArray(),
|
||||
Modulus = key.Modulus.DataRo.ToArray(),
|
||||
P = key.P.DataRo.ToArray(),
|
||||
Q = key.Q.DataRo.ToArray()
|
||||
D = key.PrivateExponent.ItemsRo.ToArray(),
|
||||
DP = key.Dp.ItemsRo.ToArray(),
|
||||
DQ = key.Dq.ItemsRo.ToArray(),
|
||||
Exponent = key.PublicExponent.ItemsRo.ToArray(),
|
||||
InverseQ = key.InverseQ.ItemsRo.ToArray(),
|
||||
Modulus = key.Modulus.ItemsRo.ToArray(),
|
||||
P = key.P.ItemsRo.ToArray(),
|
||||
Q = key.Q.ItemsRo.ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
@ -259,23 +258,21 @@ public class KeySet
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct AllKeys
|
||||
{
|
||||
public RootKeys _rootKeysDev;
|
||||
public RootKeys _rootKeysProd;
|
||||
public KeySeeds _keySeeds;
|
||||
public StoredKeys _storedKeysDev;
|
||||
public StoredKeys _storedKeysProd;
|
||||
public DerivedKeys _derivedKeysDev;
|
||||
public DerivedKeys _derivedKeysProd;
|
||||
public DeviceKeys _deviceKeys;
|
||||
public RsaSigningKeys _rsaSigningKeysDev;
|
||||
public RsaSigningKeys _rsaSigningKeysProd;
|
||||
public RsaKeys _rsaKeys;
|
||||
public RootKeys RootKeysDev;
|
||||
public RootKeys RootKeysProd;
|
||||
public KeySeeds KeySeeds;
|
||||
public StoredKeys StoredKeysDev;
|
||||
public StoredKeys StoredKeysProd;
|
||||
public DerivedKeys DerivedKeysDev;
|
||||
public DerivedKeys DerivedKeysProd;
|
||||
public DeviceKeys DeviceKeys;
|
||||
public RsaSigningKeys RsaSigningKeysDev;
|
||||
public RsaSigningKeys RsaSigningKeysProd;
|
||||
public RsaKeys RsaKeys;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RootKeys
|
||||
{
|
||||
// Mariko keys. The AES class keys are currently unused.
|
||||
@ -298,14 +295,13 @@ public struct RootKeys
|
||||
public Array32<AesKey> TsecRootKeys;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct KeySeeds
|
||||
{
|
||||
public Array32<AesKey> KeyBlobKeySources;
|
||||
public AesKey KeyBlobMacKeySource;
|
||||
public Array32<AesKey> MasterKekSources;
|
||||
public Array32<AesKey> MarikoMasterKekSources;
|
||||
public Array32<AesKey> MarikoMasterKekSources_dev;
|
||||
public Array32<AesKey> MarikoMasterKekSourcesDev;
|
||||
public AesKey MasterKeySource;
|
||||
public AesKey Package2KeySource;
|
||||
public AesKey PerConsoleKeySource;
|
||||
@ -331,13 +327,11 @@ public struct KeySeeds
|
||||
/// <summary>
|
||||
/// Holds keys that are stored directly in Horizon programs.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct StoredKeys
|
||||
{
|
||||
public AesKey XciHeaderKey;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct DerivedKeys
|
||||
{
|
||||
public Array32<AesKey> MasterKeks;
|
||||
@ -352,7 +346,6 @@ public struct DerivedKeys
|
||||
public AesKey SslRsaKek;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct DeviceKeys
|
||||
{
|
||||
public AesKey SecureBootKey;
|
||||
@ -368,7 +361,6 @@ public struct DeviceKeys
|
||||
public Array3<AesXtsKey> SdCardEncryptionKeys;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RsaSigningKeys
|
||||
{
|
||||
public Array2<RsaKey> NcaHeaderSigningKeys;
|
||||
@ -376,8 +368,7 @@ public struct RsaSigningKeys
|
||||
public RsaKey Package2SigningKey;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RsaKeys
|
||||
{
|
||||
public RsaFullKey BetaNca0KeyAreaKey;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
@ -7,7 +6,6 @@ using LibHac.Util;
|
||||
|
||||
namespace LibHac.Common;
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
public readonly ref struct U8Span
|
||||
{
|
||||
private readonly ReadOnlySpan<byte> _buffer;
|
||||
@ -51,7 +49,7 @@ public readonly ref struct U8Span
|
||||
#if DEBUG
|
||||
return _buffer[i];
|
||||
#else
|
||||
return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i);
|
||||
return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -99,4 +97,4 @@ public readonly ref struct U8Span
|
||||
/// <returns><see langword="true"/> if the span has no buffer or begins with a null terminator.
|
||||
/// Otherwise, <see langword="false"/>.</returns>
|
||||
public bool IsEmpty() => _buffer.IsEmpty || MemoryMarshal.GetReference(_buffer) == 0;
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
@ -7,7 +6,6 @@ using LibHac.Util;
|
||||
|
||||
namespace LibHac.Common;
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
public readonly ref struct U8SpanMutable
|
||||
{
|
||||
private readonly Span<byte> _buffer;
|
||||
@ -50,7 +48,7 @@ public readonly ref struct U8SpanMutable
|
||||
#if DEBUG
|
||||
return _buffer[i];
|
||||
#else
|
||||
return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i);
|
||||
return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -95,4 +93,4 @@ public readonly ref struct U8SpanMutable
|
||||
/// <returns><see langword="true"/> if the span has no buffer or begins with a null terminator.
|
||||
/// Otherwise, <see langword="false"/>.</returns>
|
||||
public bool IsEmpty() => _buffer.IsEmpty || MemoryMarshal.GetReference(_buffer) == 0;
|
||||
}
|
||||
}
|
@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.Common;
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
public readonly struct U8String
|
||||
{
|
||||
private readonly byte[] _buffer;
|
||||
@ -60,4 +58,4 @@ public readonly struct U8String
|
||||
/// <returns><see langword="true"/> if the string has no buffer or begins with a null terminator.
|
||||
/// Otherwise, <see langword="false"/>.</returns>
|
||||
public bool IsEmpty() => _buffer == null || _buffer.Length < 1 || _buffer[0] == 0;
|
||||
}
|
||||
}
|
@ -1,14 +1,12 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Text;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.Common;
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
public ref struct U8StringBuilder
|
||||
{
|
||||
private const int NullTerminatorLength = 1;
|
||||
|
@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.Common;
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
public readonly struct U8StringMutable
|
||||
{
|
||||
private readonly byte[] _buffer;
|
||||
@ -67,4 +65,4 @@ public readonly struct U8StringMutable
|
||||
/// <returns><see langword="true"/> if the string has no buffer or begins with a null terminator.
|
||||
/// Otherwise, <see langword="false"/>.</returns>
|
||||
public bool IsEmpty() => _buffer == null || _buffer.Length < 1 || _buffer[0] == 0;
|
||||
}
|
||||
}
|
@ -289,7 +289,8 @@ public static class Utilities
|
||||
8 => "8.1.0-8.1.1",
|
||||
9 => "9.0.0-9.0.1",
|
||||
0xA => "9.1.0-12.0.3",
|
||||
0xB => "12.1.0-",
|
||||
0xB => "12.1.0",
|
||||
0xC => "13.0.0-",
|
||||
_ => "Unknown"
|
||||
};
|
||||
|
||||
|
@ -3,11 +3,11 @@ using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.FixedArrays;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.Crypto;
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Explicit, Size = Size)]
|
||||
public struct AesKey
|
||||
{
|
||||
@ -35,7 +35,6 @@ public struct AesKey
|
||||
#endif
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Explicit, Size = Size)]
|
||||
public struct AesXtsKey
|
||||
{
|
||||
@ -63,7 +62,6 @@ public struct AesXtsKey
|
||||
public readonly override string ToString() => DataRo.ToHexString();
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Explicit, Size = Size)]
|
||||
public struct AesIv
|
||||
{
|
||||
@ -90,7 +88,6 @@ public struct AesIv
|
||||
#endif
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Explicit, Size = Size)]
|
||||
public struct AesCmac
|
||||
{
|
||||
@ -117,49 +114,21 @@ public struct AesCmac
|
||||
#endif
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RsaFullKey
|
||||
{
|
||||
public Data100 PrivateExponent;
|
||||
public Data80 Dp;
|
||||
public Data80 Dq;
|
||||
public Data3 PublicExponent;
|
||||
public Data80 InverseQ;
|
||||
public Data100 Modulus;
|
||||
public Data80 P;
|
||||
public Data80 Q;
|
||||
// ReSharper disable once UnassignedField.Global
|
||||
public Array256<byte> PrivateExponent;
|
||||
public Array128<byte> Dp;
|
||||
public Array128<byte> Dq;
|
||||
public Array3<byte> PublicExponent;
|
||||
public Array128<byte> InverseQ;
|
||||
public Array256<byte> Modulus;
|
||||
public Array128<byte> P;
|
||||
public Array128<byte> Q;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RsaKey
|
||||
{
|
||||
public Data100 Modulus;
|
||||
public Data3 PublicExponent;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x100)]
|
||||
public struct Data100
|
||||
{
|
||||
[FieldOffset(0)] private byte _byte;
|
||||
|
||||
public Span<byte> Data => SpanHelpers.CreateSpan(ref _byte, 0x100);
|
||||
public readonly ReadOnlySpan<byte> DataRo => SpanHelpers.CreateReadOnlySpan(in _byte, 0x100);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x80)]
|
||||
public struct Data80
|
||||
{
|
||||
[FieldOffset(0)] private byte _byte;
|
||||
|
||||
public Span<byte> Data => SpanHelpers.CreateSpan(ref _byte, 0x80);
|
||||
public readonly ReadOnlySpan<byte> DataRo => SpanHelpers.CreateReadOnlySpan(in _byte, 0x80);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 3)]
|
||||
public struct Data3
|
||||
{
|
||||
[FieldOffset(0)] private byte _byte;
|
||||
|
||||
public Span<byte> Data => SpanHelpers.CreateSpan(ref _byte, 3);
|
||||
public readonly ReadOnlySpan<byte> DataRo => SpanHelpers.CreateReadOnlySpan(in _byte, 3);
|
||||
public Array256<byte> Modulus;
|
||||
public Array3<byte> PublicExponent;
|
||||
}
|
@ -37,16 +37,9 @@ public static class Assert
|
||||
{
|
||||
private const string AssertCondition = "ENABLE_ASSERTS";
|
||||
|
||||
private static SdkMutexType _mutex = InitMutex();
|
||||
private static SdkMutexType _mutex = new SdkMutexType();
|
||||
private static AssertionFailureHandler _assertionFailureHandler = DefaultAssertionFailureHandler;
|
||||
|
||||
private static SdkMutexType InitMutex()
|
||||
{
|
||||
var mutex = new SdkMutexType();
|
||||
mutex.Initialize();
|
||||
return mutex;
|
||||
}
|
||||
|
||||
private static AbortReason ToAbortReason(AssertionType assertionType)
|
||||
{
|
||||
switch (assertionType)
|
||||
@ -1182,4 +1175,4 @@ public static class Assert
|
||||
AlignedImpl(AssertionType.SdkRequires, value, alignment, valueText, alignmentText, functionName, fileName,
|
||||
lineNumber);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.FixedArrays;
|
||||
|
||||
namespace LibHac.Fat;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x20)]
|
||||
public struct FatError
|
||||
{
|
||||
private const int FunctionNameLength = 0x10;
|
||||
|
||||
[FieldOffset(0x00)] public int Error;
|
||||
[FieldOffset(0x04)] public int ExtraError;
|
||||
[FieldOffset(0x08)] public int DriveId;
|
||||
[FieldOffset(0x0C)] private byte _functionName;
|
||||
|
||||
public U8SpanMutable ErrorName =>
|
||||
new U8SpanMutable(SpanHelpers.CreateSpan(ref _functionName, FunctionNameLength));
|
||||
}
|
||||
public int Error;
|
||||
public int ExtraError;
|
||||
public int DriveId;
|
||||
public Array16<byte> ErrorName;
|
||||
public int Reserved;
|
||||
}
|
@ -2,10 +2,10 @@
|
||||
using System.Buffers;
|
||||
using System.Buffers.Text;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.Unicode;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.FixedArrays;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Impl;
|
||||
@ -34,13 +34,13 @@ namespace LibHac.Fs
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
|
||||
public struct ApplicationInfo
|
||||
{
|
||||
public Ncm.ApplicationId ApplicationId;
|
||||
public uint Version;
|
||||
public byte LaunchType;
|
||||
public bool IsMultiProgram;
|
||||
public Array18<byte> Reserved;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
@ -730,23 +730,29 @@ namespace LibHac.Fs.Impl
|
||||
|
||||
internal static class AccessLogStrings
|
||||
{
|
||||
/// <summary>"<c>$fs</c>"</summary>
|
||||
public static ReadOnlySpan<byte> FsModuleName => // "$fs"
|
||||
new[] { (byte)'$', (byte)'f', (byte)'s' };
|
||||
|
||||
public static ReadOnlySpan<byte> LogLibHacVersion => // "0.13.0"
|
||||
/// <summary>"<c>0.15.0</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogLibHacVersion => // "0.15.0"
|
||||
new[]
|
||||
{
|
||||
(byte)'0', (byte)'.', (byte)'1', (byte)'3', (byte)'.', (byte)'0'
|
||||
(byte)'0', (byte)'.', (byte)'1', (byte)'5', (byte)'.', (byte)'0'
|
||||
};
|
||||
|
||||
/// <summary>"<c>"</c>"</summary>
|
||||
public static byte LogQuote => (byte)'"';
|
||||
|
||||
/// <summary>"<c>true</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogTrue => // "true"
|
||||
new[] { (byte)'t', (byte)'r', (byte)'u', (byte)'e' };
|
||||
|
||||
/// <summary>"<c>false</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogFalse => // "false"
|
||||
new[] { (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e' };
|
||||
|
||||
/// <summary>"<c>, entry_buffer_count: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogEntryBufferCount => // ", entry_buffer_count: "
|
||||
new[]
|
||||
{
|
||||
@ -755,6 +761,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, entry_count: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogEntryCount => // ", entry_count: "
|
||||
new[]
|
||||
{
|
||||
@ -762,6 +769,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'c', (byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, offset: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogOffset => // ", offset: "
|
||||
new[]
|
||||
{
|
||||
@ -769,12 +777,14 @@ namespace LibHac.Fs.Impl
|
||||
(byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, size: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogSize => // ", size: "
|
||||
new[]
|
||||
{
|
||||
(byte)',', (byte)' ', (byte)'s', (byte)'i', (byte)'z', (byte)'e', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, read_size: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogReadSize => // ", read_size: "
|
||||
new[]
|
||||
{
|
||||
@ -782,6 +792,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'i', (byte)'z', (byte)'e', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, write_option: Flush</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogWriteOptionFlush => // ", write_option: Flush"
|
||||
new[]
|
||||
{
|
||||
@ -790,6 +801,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'F', (byte)'l', (byte)'u', (byte)'s', (byte)'h'
|
||||
};
|
||||
|
||||
/// <summary>"<c>, open_mode: 0x</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogOpenMode => // ", open_mode: 0x"
|
||||
new[]
|
||||
{
|
||||
@ -797,6 +809,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'o', (byte)'d', (byte)'e', (byte)':', (byte)' ', (byte)'0', (byte)'x'
|
||||
};
|
||||
|
||||
/// <summary>"<c>, path: "</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogPath => // ", path: ""
|
||||
new[]
|
||||
{
|
||||
@ -804,6 +817,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'"'
|
||||
};
|
||||
|
||||
/// <summary>"<c>", new_path: "</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogNewPath => // "", new_path: ""
|
||||
new[]
|
||||
{
|
||||
@ -811,6 +825,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'a', (byte)'t', (byte)'h', (byte)':', (byte)' ', (byte)'"'
|
||||
};
|
||||
|
||||
/// <summary>"<c>", entry_type: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogEntryType => // "", entry_type: "
|
||||
new[]
|
||||
{
|
||||
@ -818,6 +833,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'_', (byte)'t', (byte)'y', (byte)'p', (byte)'e', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, name: "</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogName => // ", name: ""
|
||||
new[]
|
||||
{
|
||||
@ -825,6 +841,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'"'
|
||||
};
|
||||
|
||||
/// <summary>"<c>", commit_option: 0x</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogCommitOption => // "", commit_option: 0x"
|
||||
new[]
|
||||
{
|
||||
@ -833,6 +850,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)':', (byte)' ', (byte)'0', (byte)'x'
|
||||
};
|
||||
|
||||
/// <summary>"<c>", is_mounted: "</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogIsMounted => // "", is_mounted: ""
|
||||
new[]
|
||||
{
|
||||
@ -840,6 +858,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'u', (byte)'n', (byte)'t', (byte)'e', (byte)'d', (byte)':', (byte)' ', (byte)'"'
|
||||
};
|
||||
|
||||
/// <summary>"<c>, applicationid: 0x</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogApplicationId => // ", applicationid: 0x"
|
||||
new[]
|
||||
{
|
||||
@ -848,6 +867,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)' ', (byte)'0', (byte)'x'
|
||||
};
|
||||
|
||||
/// <summary>"<c>, programid: 0x</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogProgramId => // ", programid: 0x"
|
||||
new[]
|
||||
{
|
||||
@ -855,6 +875,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'m', (byte)'i', (byte)'d', (byte)':', (byte)' ', (byte)'0', (byte)'x'
|
||||
};
|
||||
|
||||
/// <summary>"<c>, dataid: 0x</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogDataId => // ", dataid: 0x"
|
||||
new[]
|
||||
{
|
||||
@ -862,6 +883,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)':', (byte)' ', (byte)'0', (byte)'x'
|
||||
};
|
||||
|
||||
/// <summary>"<c>, bispartitionid: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogBisPartitionId => // ", bispartitionid: "
|
||||
new[]
|
||||
{
|
||||
@ -870,6 +892,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, content_type: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogContentType => // ", content_type: "
|
||||
new[]
|
||||
{
|
||||
@ -877,6 +900,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'t', (byte)'_', (byte)'t', (byte)'y', (byte)'p', (byte)'e', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, contentstorageid: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogContentStorageId => // ", contentstorageid: "
|
||||
new[]
|
||||
{
|
||||
@ -885,6 +909,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'i', (byte)'d', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, gamecard_handle: 0x</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogGameCardHandle => // ", gamecard_handle: 0x"
|
||||
new[]
|
||||
{
|
||||
@ -893,6 +918,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'e', (byte)':', (byte)' ', (byte)'0', (byte)'x'
|
||||
};
|
||||
|
||||
/// <summary>"<c>, gamecard_partition: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogGameCardPartition => // ", gamecard_partition: "
|
||||
new[]
|
||||
{
|
||||
@ -901,6 +927,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, mount_host_option: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogMountHostOption => // ", mount_host_option: "
|
||||
new[]
|
||||
{
|
||||
@ -909,6 +936,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'i', (byte)'o', (byte)'n', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, root_path: "</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogRootPath => // ", root_path: ""
|
||||
new[]
|
||||
{
|
||||
@ -916,6 +944,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'a', (byte)'t', (byte)'h', (byte)':', (byte)' ', (byte)'"'
|
||||
};
|
||||
|
||||
/// <summary>"<c>, userid: 0x</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogUserId => // ", userid: 0x"
|
||||
new[]
|
||||
{
|
||||
@ -923,6 +952,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)':', (byte)' ', (byte)'0', (byte)'x'
|
||||
};
|
||||
|
||||
/// <summary>"<c>, index: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogIndex => // ", index: "
|
||||
new[]
|
||||
{
|
||||
@ -930,6 +960,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, save_data_owner_id: 0x</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogSaveDataOwnerId => // ", save_data_owner_id: 0x"
|
||||
new[]
|
||||
{
|
||||
@ -938,6 +969,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'r', (byte)'_', (byte)'i', (byte)'d', (byte)':', (byte)' ', (byte)'0', (byte)'x'
|
||||
};
|
||||
|
||||
/// <summary>"<c>, save_data_size: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogSaveDataSize => // ", save_data_size: "
|
||||
new[]
|
||||
{
|
||||
@ -946,6 +978,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, save_data_journal_size: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogSaveDataJournalSize => // ", save_data_journal_size: "
|
||||
new[]
|
||||
{
|
||||
@ -955,6 +988,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, save_data_flags: 0x</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogSaveDataFlags => // ", save_data_flags: 0x"
|
||||
new[]
|
||||
{
|
||||
@ -963,6 +997,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'s', (byte)':', (byte)' ', (byte)'0', (byte)'x'
|
||||
};
|
||||
|
||||
/// <summary>"<c>, savedataid: 0x</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogSaveDataId => // ", savedataid: 0x"
|
||||
new[]
|
||||
{
|
||||
@ -970,6 +1005,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'t', (byte)'a', (byte)'i', (byte)'d', (byte)':', (byte)' ', (byte)'0', (byte)'x'
|
||||
};
|
||||
|
||||
/// <summary>"<c>, savedataspaceid: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogSaveDataSpaceId => // ", savedataspaceid: "
|
||||
new[]
|
||||
{
|
||||
@ -978,6 +1014,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'d', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, save_data_time_stamp: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogSaveDataTimeStamp => // ", save_data_time_stamp: "
|
||||
new[]
|
||||
{
|
||||
@ -986,6 +1023,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'_', (byte)'s', (byte)'t', (byte)'a', (byte)'m', (byte)'p', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, save_data_commit_id: 0x</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogSaveDataCommitId => // ", save_data_commit_id: 0x"
|
||||
new[]
|
||||
{
|
||||
@ -995,6 +1033,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'x'
|
||||
};
|
||||
|
||||
/// <summary>"<c>, restore_flag: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogRestoreFlag => // ", restore_flag: "
|
||||
new[]
|
||||
{
|
||||
@ -1002,6 +1041,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'e', (byte)'_', (byte)'f', (byte)'l', (byte)'a', (byte)'g', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>sdk_version: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogSdkVersion => // "sdk_version: "
|
||||
new[]
|
||||
{
|
||||
@ -1009,15 +1049,18 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'i', (byte)'o', (byte)'n', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, spec: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogSpec => // ", spec: "
|
||||
new[]
|
||||
{
|
||||
(byte)',', (byte)' ', (byte)'s', (byte)'p', (byte)'e', (byte)'c', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>NX</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogNx => // "NX"
|
||||
new[] { (byte)'N', (byte)'X' };
|
||||
|
||||
/// <summary>"<c>, program_index: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogProgramIndex => // ", program_index: "
|
||||
new[]
|
||||
{
|
||||
@ -1026,6 +1069,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, for_system: true</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogForSystem => // ", for_system: true"
|
||||
new[]
|
||||
{
|
||||
@ -1034,6 +1078,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'u', (byte)'e'
|
||||
};
|
||||
|
||||
/// <summary>"<c>"FS_ACCESS: { </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogLineStart => // "FS_ACCESS: { "
|
||||
new[]
|
||||
{
|
||||
@ -1041,21 +1086,25 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'S', (byte)':', (byte)' ', (byte)'{', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c> }\n</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogLineEnd => // " }\n"
|
||||
new[] { (byte)' ', (byte)'}', (byte)'\n' };
|
||||
|
||||
/// <summary>"<c>start: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogStart => // "start: "
|
||||
new[]
|
||||
{
|
||||
(byte)'s', (byte)'t', (byte)'a', (byte)'r', (byte)'t', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, end: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogEnd => // ", end: "
|
||||
new[]
|
||||
{
|
||||
(byte)',', (byte)' ', (byte)'e', (byte)'n', (byte)'d', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, result: 0x</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogResult => // ", result: 0x"
|
||||
new[]
|
||||
{
|
||||
@ -1063,6 +1112,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)':', (byte)' ', (byte)'0', (byte)'x'
|
||||
};
|
||||
|
||||
/// <summary>"<c>, handle: 0x</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogHandle => // ", handle: 0x"
|
||||
new[]
|
||||
{
|
||||
@ -1070,6 +1120,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)':', (byte)' ', (byte)'0', (byte)'x'
|
||||
};
|
||||
|
||||
/// <summary>"<c>, priority: </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogPriority => // ", priority: "
|
||||
new[]
|
||||
{
|
||||
@ -1077,6 +1128,7 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'t', (byte)'y', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, function: "</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogFunction => // ", function: ""
|
||||
new[]
|
||||
{
|
||||
@ -1084,4 +1136,4 @@ namespace LibHac.Fs.Impl
|
||||
(byte)'o', (byte)'n', (byte)':', (byte)' ', (byte)'"'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using LibHac.Account;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Ns;
|
||||
using LibHac.Util;
|
||||
|
||||
@ -18,7 +17,7 @@ public static class ApplicationSaveDataManagement
|
||||
long requiredSizeSum = 0;
|
||||
|
||||
// Create local variable for use in closures
|
||||
ProgramId saveDataOwnerId = nacp.SaveDataOwnerId;
|
||||
ulong saveDataOwnerId = nacp.SaveDataOwnerId;
|
||||
|
||||
// Ensure the user account save exists
|
||||
if (uid != Uid.Zero && nacp.UserAccountSaveDataSize > 0)
|
||||
@ -31,7 +30,7 @@ public static class ApplicationSaveDataManagement
|
||||
Result CreateAccountSaveFunc()
|
||||
{
|
||||
UserId userId = ConvertAccountUidToFsUserId(uidLocal);
|
||||
return fs.CreateSaveData(applicationId, userId, saveDataOwnerId.Value, accountSaveDataSize,
|
||||
return fs.CreateSaveData(applicationId, userId, saveDataOwnerId, accountSaveDataSize,
|
||||
accountSaveJournalSize, SaveDataFlags.None);
|
||||
}
|
||||
|
||||
@ -53,8 +52,8 @@ public static class ApplicationSaveDataManagement
|
||||
long deviceSaveDataSize = nacp.DeviceSaveDataSize;
|
||||
long deviceSaveJournalSize = nacp.DeviceSaveDataJournalSize;
|
||||
|
||||
Result CreateDeviceSaveFunc() => fs.CreateDeviceSaveData(applicationId, saveDataOwnerId.Value,
|
||||
deviceSaveDataSize, deviceSaveJournalSize, 0);
|
||||
Result CreateDeviceSaveFunc() => fs.CreateDeviceSaveData(applicationId, saveDataOwnerId, deviceSaveDataSize,
|
||||
deviceSaveJournalSize, 0);
|
||||
|
||||
var filter = new SaveDataFilter();
|
||||
filter.SetProgramId(applicationId);
|
||||
@ -108,7 +107,7 @@ public static class ApplicationSaveDataManagement
|
||||
}
|
||||
else
|
||||
{
|
||||
Result createRc = fs.CreateTemporaryStorage(applicationId, nacp.SaveDataOwnerId.Value,
|
||||
Result createRc = fs.CreateTemporaryStorage(applicationId, nacp.SaveDataOwnerId,
|
||||
nacp.TemporaryStorageSize, 0);
|
||||
|
||||
if (createRc.IsFailure())
|
||||
@ -253,7 +252,7 @@ public static class ApplicationSaveDataManagement
|
||||
}
|
||||
else if (targetMedia == CacheStorageTargetMedia.SdCard)
|
||||
{
|
||||
rc = TryCreateCacheStorage(fs, out requiredSizeLocal, SaveDataSpaceId.SdCache, applicationId,
|
||||
rc = TryCreateCacheStorage(fs, out requiredSizeLocal, SaveDataSpaceId.SdUser, applicationId,
|
||||
saveDataOwnerId, index, dataSize, journalSize, allowExisting);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
@ -265,7 +264,7 @@ public static class ApplicationSaveDataManagement
|
||||
{
|
||||
target = CacheStorageTargetMedia.SdCard;
|
||||
|
||||
Result CreateFuncSdCard() => fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdCache,
|
||||
Result CreateFuncSdCard() => fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdUser,
|
||||
saveDataOwnerId, index, dataSize, journalSize, SaveDataFlags.None);
|
||||
|
||||
rc = CreateSaveData(fs, CreateFuncSdCard, ref requiredSizeLocal, 0x4000, dataSize, journalSize);
|
||||
@ -311,7 +310,7 @@ public static class ApplicationSaveDataManagement
|
||||
public static Result EnsureApplicationCacheStorage(this FileSystemClient fs, out long requiredSize,
|
||||
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);
|
||||
}
|
||||
|
||||
@ -324,7 +323,7 @@ public static class ApplicationSaveDataManagement
|
||||
return Result.Success;
|
||||
|
||||
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);
|
||||
|
||||
if (index > nacp.CacheStorageMaxIndex)
|
||||
if (index > nacp.CacheStorageIndexMax)
|
||||
return ResultFs.CacheStorageIndexTooLarge.Log();
|
||||
|
||||
if (dataSize + journalSize > nacp.CacheStorageMaxSizeAndMaxJournalSize)
|
||||
if (dataSize + journalSize > nacp.CacheStorageDataAndJournalSizeMax)
|
||||
return ResultFs.CacheStorageSizeTooLarge.Log();
|
||||
|
||||
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);
|
||||
return rc;
|
||||
@ -420,7 +419,7 @@ public static class ApplicationSaveDataManagement
|
||||
|
||||
if (fs.IsSdCardAccessible())
|
||||
{
|
||||
Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.SdCache, in filter);
|
||||
Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.SdUser, in filter);
|
||||
if (rc.IsFailure() && !ResultFs.TargetNotFound.Includes(rc)) return rc;
|
||||
|
||||
if (rc.IsSuccess())
|
||||
@ -471,4 +470,4 @@ public static class ApplicationSaveDataManagement
|
||||
{
|
||||
return new UserId(uid.Id.High, uid.Id.Low);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
|
||||
using Buffer = LibHac.Mem.Buffer;
|
||||
using CacheHandle = System.Int64;
|
||||
using CacheHandle = System.UInt64;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace LibHac.Fs;
|
||||
@ -28,6 +29,8 @@ public abstract class IBufferManager : IDisposable
|
||||
|
||||
public const int BufferLevelMin = 0;
|
||||
|
||||
public virtual void Dispose() { }
|
||||
|
||||
public Buffer AllocateBuffer(int size, BufferAttribute attribute) =>
|
||||
DoAllocateBuffer(size, attribute);
|
||||
|
||||
@ -118,12 +121,4 @@ public abstract class IBufferManager : IDisposable
|
||||
protected abstract int DoGetTotalAllocatableSizePeak();
|
||||
protected abstract int DoGetRetriedCount();
|
||||
protected abstract void DoClearPeak();
|
||||
|
||||
protected virtual void Dispose(bool disposing) { }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +1,11 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.FixedArrays;
|
||||
|
||||
namespace LibHac.Fs;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x124)]
|
||||
public struct CodeVerificationData
|
||||
{
|
||||
private const int Signature2Size = 0x100;
|
||||
|
||||
[FieldOffset(0x000)] private byte _signature2;
|
||||
[FieldOffset(0x100)] public Buffer32 NcaHeaderHash;
|
||||
[FieldOffset(0x120)] public bool IsValid;
|
||||
|
||||
public Span<byte> NcaSignature2 => SpanHelpers.CreateSpan(ref _signature2, Signature2Size);
|
||||
}
|
||||
public Array256<byte> Signature;
|
||||
public Array32<byte> Hash;
|
||||
public bool HasData;
|
||||
public Array3<byte> Reserved;
|
||||
}
|
@ -3,8 +3,13 @@ using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using static LibHac.Fs.StringTraits;
|
||||
|
||||
namespace LibHac.Fs.Common;
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace LibHac.Fs;
|
||||
|
||||
/// <summary>
|
||||
/// Iterates through each directory in a path beginning with the root directory.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
[NonCopyableDisposable]
|
||||
public ref struct DirectoryPathParser
|
||||
{
|
||||
@ -15,11 +20,26 @@ public ref struct DirectoryPathParser
|
||||
// Todo: Make private so we can use the GetCurrentPath method once lifetime tracking is better
|
||||
public Path CurrentPath;
|
||||
|
||||
public DirectoryPathParser()
|
||||
{
|
||||
_buffer = Span<byte>.Empty;
|
||||
_replacedChar = 0;
|
||||
_position = 0;
|
||||
CurrentPath = new Path();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CurrentPath.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this <see cref="DirectoryPathParser"/> with a new <see cref="Path"/>. The <see cref="Path"/>
|
||||
/// should not be a fixed path that was just initialized with <see cref="PathFunctions.SetUpFixedPath"/>
|
||||
/// because we need it to have an allocated write buffer.
|
||||
/// </summary>
|
||||
/// <param name="path">The <see cref="Path"/> to iterate. Must have an allocated write buffer.</param>
|
||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||
public Result Initialize(ref Path path)
|
||||
{
|
||||
Span<byte> pathBuffer = path.GetWriteBufferLength() != 0 ? path.GetWriteBuffer() : Span<byte>.Empty;
|
||||
@ -116,4 +136,4 @@ public ref struct DirectoryPathParser
|
||||
_position = i;
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,6 +8,10 @@ using LibHac.Os;
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace LibHac.Fs;
|
||||
|
||||
/// <summary>
|
||||
/// Allows interacting with an <see cref="IFile"/> via an <see cref="IStorage"/> interface.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public class FileStorage : IStorage
|
||||
{
|
||||
private const long InvalidSize = -1;
|
||||
@ -48,7 +52,7 @@ public class FileStorage : IStorage
|
||||
_baseFile = file;
|
||||
}
|
||||
|
||||
protected override Result DoRead(long offset, Span<byte> destination)
|
||||
public override Result Read(long offset, Span<byte> destination)
|
||||
{
|
||||
if (destination.Length == 0)
|
||||
return Result.Success;
|
||||
@ -62,7 +66,7 @@ public class FileStorage : IStorage
|
||||
return _baseFile.Read(out _, offset, destination, ReadOption.None);
|
||||
}
|
||||
|
||||
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source)
|
||||
public override Result Write(long offset, ReadOnlySpan<byte> source)
|
||||
{
|
||||
if (source.Length == 0)
|
||||
return Result.Success;
|
||||
@ -76,12 +80,12 @@ public class FileStorage : IStorage
|
||||
return _baseFile.Write(offset, source, WriteOption.None);
|
||||
}
|
||||
|
||||
protected override Result DoFlush()
|
||||
public override Result Flush()
|
||||
{
|
||||
return _baseFile.Flush();
|
||||
}
|
||||
|
||||
protected override Result DoGetSize(out long size)
|
||||
public override Result GetSize(out long size)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out size);
|
||||
|
||||
@ -92,26 +96,30 @@ public class FileStorage : IStorage
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected override Result DoSetSize(long size)
|
||||
public override Result SetSize(long size)
|
||||
{
|
||||
_fileSize = InvalidSize;
|
||||
return _baseFile.SetSize(size);
|
||||
}
|
||||
|
||||
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
|
||||
public override Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
|
||||
{
|
||||
if (operationId == OperationId.InvalidateCache || operationId == OperationId.QueryRange)
|
||||
if (operationId == OperationId.InvalidateCache)
|
||||
{
|
||||
Result rc = _baseFile.OperateRange(OperationId.InvalidateCache, offset, size);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
if (operationId == OperationId.QueryRange)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
if (operationId == OperationId.QueryRange)
|
||||
{
|
||||
if (outBuffer.Length != Unsafe.SizeOf<QueryRangeInfo>())
|
||||
return ResultFs.InvalidSize.Log();
|
||||
|
||||
SpanHelpers.AsStruct<QueryRangeInfo>(outBuffer).Clear();
|
||||
}
|
||||
if (outBuffer.Length != Unsafe.SizeOf<QueryRangeInfo>())
|
||||
return ResultFs.InvalidSize.Log();
|
||||
|
||||
SpanHelpers.AsStruct<QueryRangeInfo>(outBuffer).Clear();
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
@ -140,6 +148,12 @@ public class FileStorage : IStorage
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a file from an <see cref="IFileSystem"/> and allows interacting with it through an
|
||||
/// <see cref="IStorage"/> interface. The opened file will automatically be closed when the
|
||||
/// <see cref="FileStorageBasedFileSystem"/> is disposed.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public class FileStorageBasedFileSystem : FileStorage
|
||||
{
|
||||
private SharedRef<IFileSystem> _baseFileSystem;
|
||||
@ -153,6 +167,16 @@ public class FileStorageBasedFileSystem : FileStorage
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this <see cref="FileStorageBasedFileSystem"/> with the file at the specified path.
|
||||
/// </summary>
|
||||
/// <param name="baseFileSystem">The <see cref="IFileSystem"/> containing the file to open.</param>
|
||||
/// <param name="path">The full path of the file to open.</param>
|
||||
/// <param name="mode">Specifies the access permissions of the opened file.</param>
|
||||
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
|
||||
/// <see cref="ResultFs.PathNotFound"/>: The specified path does not exist or is a directory.<br/>
|
||||
/// <see cref="ResultFs.TargetLocked"/>: When opening as <see cref="OpenMode.Write"/>,
|
||||
/// the file is already opened as <see cref="OpenMode.Write"/>.</returns>
|
||||
public Result Initialize(ref SharedRef<IFileSystem> baseFileSystem, in Path path, OpenMode mode)
|
||||
{
|
||||
using var baseFile = new UniqueRef<IFile>();
|
||||
@ -168,6 +192,11 @@ public class FileStorageBasedFileSystem : FileStorage
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides an <see cref="IStorage"/> interface for interacting with an opened file from a mounted file system.
|
||||
/// The caller may choose whether or not the file will be closed when the <see cref="FileHandleStorage"/> is disposed.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public class FileHandleStorage : IStorage
|
||||
{
|
||||
private const long InvalidSize = -1;
|
||||
@ -177,11 +206,24 @@ public class FileHandleStorage : IStorage
|
||||
private long _size;
|
||||
private SdkMutexType _mutex;
|
||||
|
||||
// LibHac addition
|
||||
// LibHac addition because we don't use global state for the FS client
|
||||
private FileSystemClient _fsClient;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="FileHandleStorage"/> with the provided <see cref="FileHandle"/>.
|
||||
/// The file will not be closed when this <see cref="FileHandleStorage"/> is disposed.
|
||||
/// </summary>
|
||||
/// <param name="fsClient">The <see cref="FileSystemClient"/> of the provided <see cref="FileHandle"/>.</param>
|
||||
/// <param name="handle">The handle of the file to use.</param>
|
||||
public FileHandleStorage(FileSystemClient fsClient, FileHandle handle) : this(fsClient, handle, false) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="FileHandleStorage"/> with the provided <see cref="FileHandle"/>.
|
||||
/// </summary>
|
||||
/// <param name="fsClient">The <see cref="FileSystemClient"/> of the provided <see cref="FileHandle"/>.</param>
|
||||
/// <param name="handle">The handle of the file to use.</param>
|
||||
/// <param name="closeFile">Should <paramref name="handle"/> be closed when this
|
||||
/// <see cref="FileHandleStorage"/> is disposed?</param>
|
||||
public FileHandleStorage(FileSystemClient fsClient, FileHandle handle, bool closeFile)
|
||||
{
|
||||
_fsClient = fsClient;
|
||||
@ -203,7 +245,7 @@ public class FileHandleStorage : IStorage
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
protected override Result DoRead(long offset, Span<byte> destination)
|
||||
public override Result Read(long offset, Span<byte> destination)
|
||||
{
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||
|
||||
@ -219,7 +261,7 @@ public class FileHandleStorage : IStorage
|
||||
return _fsClient.ReadFile(_handle, offset, destination, ReadOption.None);
|
||||
}
|
||||
|
||||
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source)
|
||||
public override Result Write(long offset, ReadOnlySpan<byte> source)
|
||||
{
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||
|
||||
@ -235,12 +277,12 @@ public class FileHandleStorage : IStorage
|
||||
return _fsClient.WriteFile(_handle, offset, source, WriteOption.None);
|
||||
}
|
||||
|
||||
protected override Result DoFlush()
|
||||
public override Result Flush()
|
||||
{
|
||||
return _fsClient.FlushFile(_handle);
|
||||
}
|
||||
|
||||
protected override Result DoGetSize(out long size)
|
||||
public override Result GetSize(out long size)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out size);
|
||||
|
||||
@ -251,13 +293,13 @@ public class FileHandleStorage : IStorage
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected override Result DoSetSize(long size)
|
||||
public override Result SetSize(long size)
|
||||
{
|
||||
_size = InvalidSize;
|
||||
return _fsClient.SetFileSize(_handle, size);
|
||||
}
|
||||
|
||||
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
|
||||
public override Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
|
||||
{
|
||||
if (operationId != OperationId.QueryRange)
|
||||
return ResultFs.UnsupportedOperateRangeForFileHandleStorage.Log();
|
||||
@ -282,4 +324,4 @@ public class FileHandleStorage : IStorage
|
||||
_size = size;
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,8 +4,8 @@ using System.Diagnostics;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Util;
|
||||
using static LibHac.Fs.StringTraits;
|
||||
using static InlineIL.IL.Emit;
|
||||
using static LibHac.Fs.StringTraits;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace LibHac.Fs;
|
||||
@ -19,12 +19,14 @@ public struct PathFlags
|
||||
public void AllowEmptyPath() => _value |= 1 << 2;
|
||||
public void AllowMountName() => _value |= 1 << 3;
|
||||
public void AllowBackslash() => _value |= 1 << 4;
|
||||
public void AllowAllCharacters() => _value |= 1 << 5;
|
||||
|
||||
public bool IsWindowsPathAllowed() => (_value & (1 << 0)) != 0;
|
||||
public bool IsRelativePathAllowed() => (_value & (1 << 1)) != 0;
|
||||
public bool IsEmptyPathAllowed() => (_value & (1 << 2)) != 0;
|
||||
public bool IsMountNameAllowed() => (_value & (1 << 3)) != 0;
|
||||
public bool IsBackslashAllowed() => (_value & (1 << 4)) != 0;
|
||||
public readonly bool IsWindowsPathAllowed() => (_value & (1 << 0)) != 0;
|
||||
public readonly bool IsRelativePathAllowed() => (_value & (1 << 1)) != 0;
|
||||
public readonly bool IsEmptyPathAllowed() => (_value & (1 << 2)) != 0;
|
||||
public readonly bool IsMountNameAllowed() => (_value & (1 << 3)) != 0;
|
||||
public readonly bool IsBackslashAllowed() => (_value & (1 << 4)) != 0;
|
||||
public readonly bool AreAllCharactersAllowed() => (_value & (1 << 5)) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -81,7 +83,7 @@ public static class PathExtensions
|
||||
/// ensure a write buffer is allocated and copy the input path to it. <see cref="SetShallowBuffer"/> will
|
||||
/// directly use the input buffer without copying. If this method is used, the caller must ensure the path
|
||||
/// is normalized before passing it to <see cref="SetShallowBuffer"/>.</para>
|
||||
/// <para>Based on FS 12.1.0 (nnSdk 12.3.1)</para></remarks>
|
||||
/// <para>Based on FS 13.1.0 (nnSdk 13.4.0)</para></remarks>
|
||||
[DebuggerDisplay("{" + nameof(ToString) + "(),nq}")]
|
||||
[NonCopyableDisposable]
|
||||
public ref struct Path
|
||||
@ -223,9 +225,9 @@ public ref struct Path
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see langword="true"/> if
|
||||
/// Returns <see langword="true"/> if the <see cref="Path"/> has no buffer or has an empty string.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <returns><see langword="true"/> if the current path is empty; otherwise <see langword="false"/>.</returns>
|
||||
public readonly bool IsEmpty()
|
||||
{
|
||||
return _string.At(0) == 0;
|
||||
@ -955,6 +957,59 @@ public ref struct Path
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines a <see cref="Path"/> and a path string into a single path.
|
||||
/// </summary>
|
||||
/// <remarks>If <paramref name="path1"/> is not empty, this <see cref="Path"/>'s <c>IsNormalized</c> flag will
|
||||
/// be set to the value of <paramref name="path1"/>'s flag.
|
||||
/// Otherwise the flag will be set to <see langword="false"/>.</remarks>
|
||||
/// <param name="path1">The first path to combine.</param>
|
||||
/// <param name="path2">The second path to combine.</param>
|
||||
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
|
||||
/// <see cref="ResultFs.NotNormalized"/>: The <c>IsNormalized</c> flag of
|
||||
/// <paramref name="path1"/> is not <see langword="true"/>.</returns>
|
||||
public Result Combine(in Path path1, ReadOnlySpan<byte> path2)
|
||||
{
|
||||
int path1Length = path1.GetLength();
|
||||
int path2Length = StringUtils.GetLength(path2);
|
||||
|
||||
Result rc = Preallocate(path1Length + SeparatorLength + path2Length + NullTerminatorLength);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = Initialize(in path1);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = AppendChild(path2);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines a path string and a <see cref="Path"/> into a single path.
|
||||
/// </summary>
|
||||
/// <remarks>This <see cref="Path"/>'s <c>IsNormalized</c> flag will
|
||||
/// always be set to <see langword="false"/>.</remarks>
|
||||
/// <param name="path1">The first path to combine.</param>
|
||||
/// <param name="path2">The second path to combine.</param>
|
||||
/// <returns><see cref="Result.Success"/>: The operation was successful.</returns>
|
||||
public Result Combine(ReadOnlySpan<byte> path1, in Path path2)
|
||||
{
|
||||
int path1Length = StringUtils.GetLength(path1);
|
||||
int path2Length = path2.GetLength();
|
||||
|
||||
Result rc = Preallocate(path1Length + SeparatorLength + path2Length + NullTerminatorLength);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = Initialize(path1);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = AppendChild(in path2);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the last entry from this <see cref="Path"/>.
|
||||
/// </summary>
|
||||
|
@ -13,9 +13,19 @@ namespace LibHac.Fs;
|
||||
/// <summary>
|
||||
/// Contains functions for working with path formatting and normalization.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 12.1.0 (nnSdk 12.3.1)</remarks>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public static class PathFormatter
|
||||
{
|
||||
private static ReadOnlySpan<byte> InvalidCharacter =>
|
||||
new[] { (byte)':', (byte)'*', (byte)'?', (byte)'<', (byte)'>', (byte)'|' };
|
||||
|
||||
private static ReadOnlySpan<byte> InvalidCharacterForHostName =>
|
||||
new[] { (byte)':', (byte)'*', (byte)'<', (byte)'>', (byte)'|', (byte)'$' };
|
||||
|
||||
private static ReadOnlySpan<byte> InvalidCharacterForMountName =>
|
||||
new[] { (byte)'*', (byte)'?', (byte)'<', (byte)'>', (byte)'|' };
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Result CheckHostName(ReadOnlySpan<byte> name)
|
||||
{
|
||||
@ -24,8 +34,11 @@ public static class PathFormatter
|
||||
|
||||
for (int i = 0; i < name.Length; i++)
|
||||
{
|
||||
if (name[i] == ':' || name[i] == '$')
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
foreach (byte c in InvalidCharacterForHostName)
|
||||
{
|
||||
if (name[i] == c)
|
||||
return ResultFs.InvalidCharacter.Log();
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
@ -41,8 +54,11 @@ public static class PathFormatter
|
||||
|
||||
for (int i = 0; i < name.Length; i++)
|
||||
{
|
||||
if (name[i] == ':')
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
foreach (byte c in InvalidCharacter)
|
||||
{
|
||||
if (name[i] == c)
|
||||
return ResultFs.InvalidCharacter.Log();
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
@ -90,8 +106,11 @@ public static class PathFormatter
|
||||
|
||||
for (int i = 0; i < mountLength; i++)
|
||||
{
|
||||
if (path.At(i) is (byte)'*' or (byte)'?' or (byte)'<' or (byte)'>' or (byte)'|')
|
||||
return ResultFs.InvalidCharacter.Log();
|
||||
foreach (byte c in InvalidCharacterForMountName)
|
||||
{
|
||||
if (path.At(i) == c)
|
||||
return ResultFs.InvalidCharacter.Log();
|
||||
}
|
||||
}
|
||||
|
||||
if (!outMountNameBuffer.IsEmpty)
|
||||
@ -150,6 +169,12 @@ public static class PathFormatter
|
||||
int winPathLength;
|
||||
for (winPathLength = 2; currentPath.At(winPathLength) != NullTerminator; winPathLength++)
|
||||
{
|
||||
foreach (byte c in InvalidCharacter)
|
||||
{
|
||||
if (currentPath[winPathLength] == c)
|
||||
return ResultFs.InvalidCharacter.Log();
|
||||
}
|
||||
|
||||
if (currentPath[winPathLength] == DirectorySeparator ||
|
||||
currentPath[winPathLength] == AltDirectorySeparator)
|
||||
{
|
||||
@ -478,8 +503,11 @@ public static class PathFormatter
|
||||
}
|
||||
}
|
||||
|
||||
if (PathNormalizer.IsParentDirectoryPathReplacementNeeded(buffer))
|
||||
return ResultFs.DirectoryUnobtainable.Log();
|
||||
if (flags.IsBackslashAllowed() && PathNormalizer.IsParentDirectoryPathReplacementNeeded(buffer))
|
||||
{
|
||||
isNormalized = false;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
rc = PathUtility.CheckInvalidBackslash(out bool isBackslashContained, buffer,
|
||||
flags.IsWindowsPathAllowed() || flags.IsBackslashAllowed());
|
||||
@ -491,7 +519,7 @@ public static class PathFormatter
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
rc = PathNormalizer.IsNormalized(out isNormalized, out int length, buffer);
|
||||
rc = PathNormalizer.IsNormalized(out isNormalized, out int length, buffer, flags.AreAllCharactersAllowed());
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
totalLength += length;
|
||||
@ -612,7 +640,8 @@ public static class PathFormatter
|
||||
src = srcBufferSlashReplaced.AsSpan(srcOffset);
|
||||
}
|
||||
|
||||
rc = PathNormalizer.Normalize(outputBuffer.Slice(currentPos), out _, src, isWindowsPath, isDriveRelative);
|
||||
rc = PathNormalizer.Normalize(outputBuffer.Slice(currentPos), out _, src, isWindowsPath, isDriveRelative,
|
||||
flags.AreAllCharactersAllowed());
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return Result.Success;
|
||||
|
@ -10,7 +10,7 @@ namespace LibHac.Fs;
|
||||
/// <summary>
|
||||
/// Contains functions for doing with basic path normalization.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 12.1.0 (nnSdk 12.3.1)</remarks>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public static class PathNormalizer
|
||||
{
|
||||
private enum PathState
|
||||
@ -25,6 +25,12 @@ public static class PathNormalizer
|
||||
|
||||
public static Result Normalize(Span<byte> outputBuffer, out int length, ReadOnlySpan<byte> path, bool isWindowsPath,
|
||||
bool isDriveRelativePath)
|
||||
{
|
||||
return Normalize(outputBuffer, out length, path, isWindowsPath, isDriveRelativePath, false);
|
||||
}
|
||||
|
||||
public static Result Normalize(Span<byte> outputBuffer, out int length, ReadOnlySpan<byte> path, bool isWindowsPath,
|
||||
bool isDriveRelativePath, bool allowAllCharacters)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out length);
|
||||
|
||||
@ -32,7 +38,7 @@ public static class PathNormalizer
|
||||
int totalLength = 0;
|
||||
int i = 0;
|
||||
|
||||
if (!IsSeparator(path.At(0)))
|
||||
if (path.At(0) != DirectorySeparator)
|
||||
{
|
||||
if (!isDriveRelativePath)
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
@ -43,6 +49,7 @@ public static class PathNormalizer
|
||||
var convertedPath = new RentedArray<byte>();
|
||||
try
|
||||
{
|
||||
Result rc;
|
||||
// Check if parent directory path replacement is needed.
|
||||
if (IsParentDirectoryPathReplacementNeeded(currentPath))
|
||||
{
|
||||
@ -58,20 +65,22 @@ public static class PathNormalizer
|
||||
|
||||
bool skipNextSeparator = false;
|
||||
|
||||
while (!IsNul(currentPath.At(i)))
|
||||
while (currentPath.At(i) != NullTerminator)
|
||||
{
|
||||
if (IsSeparator(currentPath[i]))
|
||||
if (currentPath[i] == DirectorySeparator)
|
||||
{
|
||||
do
|
||||
{
|
||||
i++;
|
||||
} while (IsSeparator(currentPath.At(i)));
|
||||
} while (currentPath.At(i) == DirectorySeparator);
|
||||
|
||||
if (IsNul(currentPath.At(i)))
|
||||
if (currentPath.At(i) == NullTerminator)
|
||||
break;
|
||||
|
||||
if (!skipNextSeparator)
|
||||
{
|
||||
// Note: Nintendo returns TooLongPath in some cases where the output buffer is actually long
|
||||
// enough to hold the normalized path. e.g. "/aa/bb/." with an output buffer length of 7
|
||||
if (totalLength + 1 == outputBuffer.Length)
|
||||
{
|
||||
outputBuffer[totalLength] = NullTerminator;
|
||||
@ -87,8 +96,14 @@ public static class PathNormalizer
|
||||
}
|
||||
|
||||
int dirLen = 0;
|
||||
while (!IsSeparator(currentPath.At(i + dirLen)) && !IsNul(currentPath.At(i + dirLen)))
|
||||
while (currentPath.At(i + dirLen) != DirectorySeparator && currentPath.At(i + dirLen) != NullTerminator)
|
||||
{
|
||||
if (!allowAllCharacters)
|
||||
{
|
||||
rc = CheckInvalidCharacter(currentPath[i + dirLen]);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
}
|
||||
|
||||
dirLen++;
|
||||
}
|
||||
|
||||
@ -163,12 +178,14 @@ public static class PathNormalizer
|
||||
}
|
||||
|
||||
// Note: This bug is in the original code. They probably meant to put "totalLength + 1"
|
||||
if (totalLength - 1 > outputBuffer.Length)
|
||||
// The buffer needs to be able to contain the total length of the normalized string plus
|
||||
// one for the null terminator
|
||||
if (outputBuffer.Length < totalLength - 1)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
||||
outputBuffer[totalLength] = NullTerminator;
|
||||
|
||||
Result rc = IsNormalized(out bool isNormalized, out _, outputBuffer);
|
||||
rc = IsNormalized(out bool isNormalized, out _, outputBuffer, allowAllCharacters);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
Assert.SdkAssert(isNormalized);
|
||||
@ -200,6 +217,12 @@ public static class PathNormalizer
|
||||
/// <see cref="ResultFs.InvalidCharacter"/>: The path contains an invalid character.<br/>
|
||||
/// <see cref="ResultFs.InvalidPathFormat"/>: The path is not in a valid format.</returns>
|
||||
public static Result IsNormalized(out bool isNormalized, out int length, ReadOnlySpan<byte> path)
|
||||
{
|
||||
return IsNormalized(out isNormalized, out length, path, false);
|
||||
}
|
||||
|
||||
public static Result IsNormalized(out bool isNormalized, out int length, ReadOnlySpan<byte> path,
|
||||
bool allowAllCharacters)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out isNormalized, out length);
|
||||
|
||||
@ -213,7 +236,7 @@ public static class PathNormalizer
|
||||
|
||||
pathLength++;
|
||||
|
||||
if (state != PathState.Initial)
|
||||
if (!allowAllCharacters && state != PathState.Initial)
|
||||
{
|
||||
Result rc = CheckInvalidCharacter(c);
|
||||
if (rc.IsFailure()) return rc;
|
||||
@ -292,7 +315,6 @@ public static class PathNormalizer
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a path begins with / or \ and contains any of these patterns:
|
||||
/// "/..\", "\..\", "\../", "\..0" where '0' is the null terminator.
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Util;
|
||||
using static LibHac.Fs.StringTraits;
|
||||
@ -11,7 +12,7 @@ namespace LibHac.Fs;
|
||||
/// <summary>
|
||||
/// Contains various utility functions for working with paths.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 12.1.0 (nnSdk 12.3.1)</remarks>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public static class PathUtility
|
||||
{
|
||||
public static void Replace(Span<byte> buffer, byte currentChar, byte newChar)
|
||||
@ -46,16 +47,6 @@ public static class PathUtility
|
||||
(path.Length < 3 || path[2] == NullTerminator || path[2] == DirectorySeparator);
|
||||
}
|
||||
|
||||
public static bool IsSeparator(byte c)
|
||||
{
|
||||
return c == DirectorySeparator;
|
||||
}
|
||||
|
||||
public static bool IsNul(byte c)
|
||||
{
|
||||
return c == NullTerminator;
|
||||
}
|
||||
|
||||
public static Result ConvertToFspPath(out FspPath fspPath, ReadOnlySpan<byte> path)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out fspPath);
|
||||
@ -65,7 +56,7 @@ public static class PathUtility
|
||||
if (length >= PathTool.EntryNameLengthMax + 1)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
||||
Result rc = PathFormatter.SkipMountName(out ReadOnlySpan<byte> pathWithoutMountName, out _,
|
||||
Result rc = PathFormatter.SkipMountName(out ReadOnlySpan<byte> pathWithoutMountName, out int skipLength,
|
||||
new U8Span(path));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
@ -73,10 +64,16 @@ public static class PathUtility
|
||||
{
|
||||
Replace(SpanHelpers.AsByteSpan(ref fspPath).Slice(0, 0x300), AltDirectorySeparator, DirectorySeparator);
|
||||
}
|
||||
else if (fspPath.Str[0] == DirectorySeparator && fspPath.Str[1] == DirectorySeparator)
|
||||
else
|
||||
{
|
||||
SpanHelpers.AsByteSpan(ref fspPath)[0] = AltDirectorySeparator;
|
||||
SpanHelpers.AsByteSpan(ref fspPath)[1] = AltDirectorySeparator;
|
||||
bool isHostOrNoMountName = skipLength == 0 || StringUtils.Compare(path, CommonMountNames.HostRootFileSystemMountName,
|
||||
CommonMountNames.HostRootFileSystemMountName.Length) == 0;
|
||||
|
||||
if (isHostOrNoMountName && WindowsPath.IsUncPath(path.Slice(skipLength), true, false))
|
||||
{
|
||||
SpanHelpers.AsByteSpan(ref fspPath)[skipLength] = AltDirectorySeparator;
|
||||
SpanHelpers.AsByteSpan(ref fspPath)[skipLength + 1] = AltDirectorySeparator;
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
@ -245,4 +242,4 @@ public static class PathUtility
|
||||
{
|
||||
return IsCurrentDirectory(path) || IsParentDirectory(path);
|
||||
}
|
||||
}
|
||||
}
|
66
src/LibHac/Fs/Common/Range.cs
Normal file
66
src/LibHac/Fs/Common/Range.cs
Normal file
@ -0,0 +1,66 @@
|
||||
// ReSharper disable InconsistentNaming CheckNamespace
|
||||
|
||||
using System;
|
||||
using LibHac.Diag;
|
||||
|
||||
namespace LibHac.Fs;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a contiguous range of offsets in a piece of data.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public readonly struct Range : IEquatable<Range>, IComparable<Range>
|
||||
{
|
||||
public readonly long Offset;
|
||||
public readonly long Size;
|
||||
|
||||
public Range(long offset, long size)
|
||||
{
|
||||
Offset = offset;
|
||||
Size = size;
|
||||
}
|
||||
|
||||
public bool Contains(Range range)
|
||||
{
|
||||
return Offset <= range.Offset &&
|
||||
range.Offset + range.Size <= Offset + Size;
|
||||
}
|
||||
|
||||
public bool HasIntersection(Range range)
|
||||
{
|
||||
return Offset + Size > range.Offset &&
|
||||
range.Offset + range.Size > Offset;
|
||||
}
|
||||
|
||||
public bool IsAdjacent(Range range)
|
||||
{
|
||||
return Offset + Size == range.Offset ||
|
||||
range.Offset + range.Size == Offset;
|
||||
}
|
||||
|
||||
public Range MakeMerge(Range range)
|
||||
{
|
||||
Assert.SdkAssert(HasIntersection(range) || IsAdjacent(range));
|
||||
|
||||
long endOffsetThis = Offset + Size;
|
||||
long endOffsetOther = range.Offset + range.Size;
|
||||
|
||||
long startOffsetMerged = Math.Min(Offset, range.Offset);
|
||||
long endOffsetMerged = Math.Max(endOffsetThis, endOffsetOther);
|
||||
|
||||
return new Range(startOffsetMerged, endOffsetMerged - startOffsetMerged);
|
||||
}
|
||||
|
||||
// Equality is done with only the offset because this type is mainly used with RangeSet
|
||||
// which will never contain multiple ranges with the same offset.
|
||||
public static bool operator <(Range lhs, Range rhs) => lhs.Offset < rhs.Offset;
|
||||
public static bool operator >(Range lhs, Range rhs) => lhs.Offset > rhs.Offset;
|
||||
public static bool operator ==(Range lhs, Range rhs) => lhs.Offset == rhs.Offset;
|
||||
public static bool operator !=(Range lhs, Range rhs) => lhs.Offset != rhs.Offset;
|
||||
|
||||
public bool Equals(Range other) => Offset == other.Offset;
|
||||
|
||||
public override bool Equals(object obj) => obj is Range other && Equals(other);
|
||||
public int CompareTo(Range other) => Offset.CompareTo(other.Offset);
|
||||
public override int GetHashCode() => Offset.GetHashCode();
|
||||
}
|
@ -10,7 +10,7 @@ namespace LibHac.Fs;
|
||||
/// <summary>
|
||||
/// Contains functions for working with Windows paths.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 12.1.0 (nnSdk 12.3.1)</remarks>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public static class WindowsPath
|
||||
{
|
||||
private const int WindowsDriveLength = 2;
|
||||
@ -254,4 +254,4 @@ public static class WindowsPath
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +1,15 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.FixedArrays;
|
||||
|
||||
namespace LibHac.Fs;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct DirectoryEntry
|
||||
{
|
||||
[FieldOffset(0)] private byte _name;
|
||||
[FieldOffset(0x301)] public NxFileAttributes Attributes;
|
||||
[FieldOffset(0x304)] public DirectoryEntryType Type;
|
||||
[FieldOffset(0x308)] public long Size;
|
||||
|
||||
public Span<byte> Name => SpanHelpers.CreateSpan(ref _name, PathTool.EntryNameLengthMax + 1);
|
||||
public Array769<byte> Name;
|
||||
public NxFileAttributes Attributes;
|
||||
public Array2<byte> Reserved302;
|
||||
public DirectoryEntryType Type;
|
||||
public Array3<byte> Reserved305;
|
||||
public long Size;
|
||||
}
|
||||
|
||||
public enum DirectoryEntryType : byte
|
||||
|
@ -1,28 +1,11 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.FixedArrays;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.Fs;
|
||||
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||
public struct EncryptionSeed : IEquatable<EncryptionSeed>
|
||||
public struct EncryptionSeed
|
||||
{
|
||||
private readonly Key128 Key;
|
||||
public Array16<byte> Value;
|
||||
|
||||
public readonly ReadOnlySpan<byte> Value => SpanHelpers.AsReadOnlyByteSpan(in this);
|
||||
|
||||
public EncryptionSeed(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
Key = new Key128(bytes);
|
||||
}
|
||||
|
||||
public override string ToString() => Key.ToString();
|
||||
|
||||
public override bool Equals(object obj) => obj is EncryptionSeed key && Equals(key);
|
||||
public bool Equals(EncryptionSeed other) => Key.Equals(other.Key);
|
||||
public override int GetHashCode() => Key.GetHashCode();
|
||||
public static bool operator ==(EncryptionSeed left, EncryptionSeed right) => left.Equals(right);
|
||||
public static bool operator !=(EncryptionSeed left, EncryptionSeed right) => !(left == right);
|
||||
}
|
||||
public readonly override string ToString() => Value.ItemsRo.ToHexString();
|
||||
}
|
@ -1,23 +1,22 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common.FixedArrays;
|
||||
using LibHac.Fat;
|
||||
|
||||
namespace LibHac.Fs;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x80)]
|
||||
public struct FileSystemProxyErrorInfo
|
||||
{
|
||||
[FieldOffset(0x00)] public int RomFsRemountForDataCorruptionCount;
|
||||
[FieldOffset(0x04)] public int RomFsUnrecoverableDataCorruptionByRemountCount;
|
||||
[FieldOffset(0x08)] public FatError FatError;
|
||||
[FieldOffset(0x28)] public int RomFsRecoveredByInvalidateCacheCount;
|
||||
[FieldOffset(0x2C)] public int SaveDataIndexCount;
|
||||
public int RemountForDataCorruptionCount;
|
||||
public int UnrecoverableDataCorruptionByRemountCount;
|
||||
public FatError FatFsError;
|
||||
public int RecoveredByInvalidateCacheCount;
|
||||
public int SaveDataIndexCount;
|
||||
public Array80<byte> Reserved;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
|
||||
public struct StorageErrorInfo
|
||||
{
|
||||
[FieldOffset(0x00)] public int NumActivationFailures;
|
||||
[FieldOffset(0x04)] public int NumActivationErrorCorrections;
|
||||
[FieldOffset(0x08)] public int NumReadWriteFailures;
|
||||
[FieldOffset(0x0C)] public int NumReadWriteErrorCorrections;
|
||||
}
|
||||
public int NumActivationFailures;
|
||||
public int NumActivationErrorCorrections;
|
||||
public int NumReadWriteFailures;
|
||||
public int NumReadWriteErrorCorrections;
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
//using System;
|
||||
//using LibHac.Common;
|
||||
//using LibHac.Fs.Fsa;
|
||||
|
||||
//namespace LibHac.Fs
|
||||
//{
|
||||
// public class FileHandleStorage : IStorage
|
||||
// {
|
||||
// private const long InvalidSize = -1;
|
||||
// private readonly object _locker = new object();
|
||||
|
||||
// private FileSystemClient FsClient { get; }
|
||||
// private FileHandle Handle { get; }
|
||||
// private long FileSize { get; set; } = InvalidSize;
|
||||
// private bool CloseHandle { get; }
|
||||
|
||||
// public FileHandleStorage(FileSystemClient fsClient, FileHandle handle) : this(fsClient, handle, false) { }
|
||||
|
||||
// public FileHandleStorage(FileSystemClient fsClient, FileHandle handle, bool closeHandleOnDispose)
|
||||
// {
|
||||
// FsClient = fsClient;
|
||||
// Handle = handle;
|
||||
// CloseHandle = closeHandleOnDispose;
|
||||
// }
|
||||
|
||||
// protected override Result DoRead(long offset, Span<byte> destination)
|
||||
// {
|
||||
// lock (_locker)
|
||||
// {
|
||||
// if (destination.Length == 0) return Result.Success;
|
||||
|
||||
// Result rc = UpdateSize();
|
||||
// if (rc.IsFailure()) return rc;
|
||||
|
||||
// if (!CheckAccessRange(offset, destination.Length, FileSize)) return ResultFs.OutOfRange.Log();
|
||||
|
||||
// return FsClient.ReadFile(Handle, offset, destination);
|
||||
// }
|
||||
// }
|
||||
|
||||
// protected override Result DoWrite(long offset, ReadOnlySpan<byte> source)
|
||||
// {
|
||||
// lock (_locker)
|
||||
// {
|
||||
// if (source.Length == 0) return Result.Success;
|
||||
|
||||
// Result rc = UpdateSize();
|
||||
// if (rc.IsFailure()) return rc;
|
||||
|
||||
// if (!CheckAccessRange(offset, source.Length, FileSize)) return ResultFs.OutOfRange.Log();
|
||||
|
||||
// return FsClient.WriteFile(Handle, offset, source, WriteOption.None);
|
||||
// }
|
||||
// }
|
||||
|
||||
// protected override Result DoFlush()
|
||||
// {
|
||||
// return FsClient.FlushFile(Handle);
|
||||
// }
|
||||
|
||||
// protected override Result DoSetSize(long size)
|
||||
// {
|
||||
// FileSize = InvalidSize;
|
||||
|
||||
// return FsClient.SetFileSize(Handle, size);
|
||||
// }
|
||||
|
||||
// protected override Result DoGetSize(out long size)
|
||||
// {
|
||||
// UnsafeHelpers.SkipParamInit(out size);
|
||||
|
||||
// Result rc = UpdateSize();
|
||||
// if (rc.IsFailure()) return rc;
|
||||
|
||||
// size = FileSize;
|
||||
// return Result.Success;
|
||||
// }
|
||||
|
||||
// private Result UpdateSize()
|
||||
// {
|
||||
// if (FileSize != InvalidSize) return Result.Success;
|
||||
|
||||
// Result rc = FsClient.GetFileSize(out long fileSize, Handle);
|
||||
// if (rc.IsFailure()) return rc;
|
||||
|
||||
// FileSize = fileSize;
|
||||
// return Result.Success;
|
||||
// }
|
||||
|
||||
// protected override void Dispose(bool disposing)
|
||||
// {
|
||||
// if (CloseHandle)
|
||||
// {
|
||||
// FsClient.CloseFile(Handle);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
@ -1,41 +0,0 @@
|
||||
//using LibHac.Common;
|
||||
//using LibHac.Fs.Fsa;
|
||||
|
||||
//namespace LibHac.Fs
|
||||
//{
|
||||
// public class FileStorageBasedFileSystem : FileStorage2
|
||||
// {
|
||||
// private ReferenceCountedDisposable<IFileSystem> _baseFileSystem;
|
||||
// private UniqueRef<IFile> _baseFile;
|
||||
|
||||
// public FileStorageBasedFileSystem()
|
||||
// {
|
||||
// FileSize = SizeNotInitialized;
|
||||
// }
|
||||
|
||||
// public Result Initialize(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, in Path path,
|
||||
// OpenMode mode)
|
||||
// {
|
||||
// using var baseFile = new UniqueRef<IFile>();
|
||||
// Result rc = baseFileSystem.Get.OpenFile(ref baseFile.Ref(), in path, mode);
|
||||
// if (rc.IsFailure()) return rc;
|
||||
|
||||
// SetFile(baseFile.Get);
|
||||
// _baseFileSystem = Shared.Move(ref baseFileSystem);
|
||||
// _baseFile.Set(ref _baseFile.Ref());
|
||||
|
||||
// return Result.Success;
|
||||
// }
|
||||
|
||||
// protected override void Dispose(bool disposing)
|
||||
// {
|
||||
// if (disposing)
|
||||
// {
|
||||
// _baseFile.Destroy();
|
||||
// _baseFileSystem?.Dispose();
|
||||
// }
|
||||
|
||||
// base.Dispose(disposing);
|
||||
// }
|
||||
// }
|
||||
//}
|
@ -35,6 +35,7 @@ internal struct FileSystemClientGlobals : IDisposable
|
||||
public FsContextHandlerGlobals FsContextHandler;
|
||||
public ResultHandlingUtilityGlobals ResultHandlingUtility;
|
||||
public DirectorySaveDataFileSystemGlobals DirectorySaveDataFileSystem;
|
||||
public FileDataCacheShim.Globals FileDataCache;
|
||||
|
||||
public void Initialize(FileSystemClient fsClient, HorizonClient horizonClient)
|
||||
{
|
||||
@ -60,4 +61,4 @@ public readonly struct FileSystemClientImpl
|
||||
internal ref FileSystemClientGlobals Globals => ref Fs.Globals;
|
||||
|
||||
internal FileSystemClientImpl(FileSystemClient parentClient) => Fs = parentClient;
|
||||
}
|
||||
}
|
@ -1,12 +1,22 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common.FixedArrays;
|
||||
using LibHac.Time;
|
||||
|
||||
namespace LibHac.Fs;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
|
||||
public struct FileTimeStamp
|
||||
{
|
||||
public PosixTime Created;
|
||||
public PosixTime Accessed;
|
||||
public PosixTime Modified;
|
||||
public bool IsLocalTime;
|
||||
public Array7<byte> Reserved;
|
||||
}
|
||||
|
||||
public struct FileTimeStampRaw
|
||||
{
|
||||
public long Created;
|
||||
public long Accessed;
|
||||
public long Modified;
|
||||
public bool IsLocalTime;
|
||||
}
|
||||
public Array7<byte> Reserved;
|
||||
}
|
@ -52,7 +52,7 @@ public enum SaveDataSpaceId : byte
|
||||
User = 1,
|
||||
SdSystem = 2,
|
||||
Temporary = 3,
|
||||
SdCache = 4,
|
||||
SdUser = 4,
|
||||
ProperSystem = 100,
|
||||
SafeMode = 101,
|
||||
BisAuto = 127
|
||||
@ -260,5 +260,5 @@ public enum SdCardSpeedMode
|
||||
Sdr50 = 5,
|
||||
Sdr104 = 6,
|
||||
Ddr50 = 7,
|
||||
Unknown = 8,
|
||||
}
|
||||
Unknown = 8
|
||||
}
|
@ -5,6 +5,10 @@ using LibHac.Fs.Fsa;
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace LibHac.Fs.Impl;
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to a directory in a mounted file system and handles closing the directory.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
internal class DirectoryAccessor : IDisposable
|
||||
{
|
||||
private UniqueRef<IDirectory> _directory;
|
||||
@ -35,4 +39,4 @@ internal class DirectoryAccessor : IDisposable
|
||||
{
|
||||
return _directory.Get.GetEntryCount(out entryCount);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.Os;
|
||||
using static LibHac.Fs.Impl.AccessLogStrings;
|
||||
|
||||
@ -15,6 +16,10 @@ internal enum WriteState
|
||||
Failed,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to a mount <see cref="IFile"/> and handles caching it.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
internal class FileAccessor : IDisposable
|
||||
{
|
||||
private const string NeedFlushMessage = "Error: nn::fs::CloseFile() failed because the file was not flushed.\n";
|
||||
@ -24,8 +29,7 @@ internal class FileAccessor : IDisposable
|
||||
private WriteState _writeState;
|
||||
private Result _lastResult;
|
||||
private OpenMode _openMode;
|
||||
private FilePathHash _filePathHash;
|
||||
// ReSharper disable once NotAccessedField.Local
|
||||
private Box<FilePathHash> _filePathHash;
|
||||
private int _pathHashIndex;
|
||||
|
||||
internal HorizonClient Hos { get; }
|
||||
@ -59,7 +63,7 @@ internal class FileAccessor : IDisposable
|
||||
public WriteState GetWriteState() => _writeState;
|
||||
public FileSystemAccessor GetParent() => _parentFileSystem;
|
||||
|
||||
public void SetFilePathHash(FilePathHash filePathHash, int index)
|
||||
public void SetFilePathHash(Box<FilePathHash> filePathHash, int index)
|
||||
{
|
||||
_filePathHash = filePathHash;
|
||||
_pathHashIndex = index;
|
||||
@ -73,18 +77,18 @@ internal class FileAccessor : IDisposable
|
||||
return result;
|
||||
}
|
||||
|
||||
public Result ReadWithoutCacheAccessLog(out long bytesRead, long offset, Span<byte> destination,
|
||||
in ReadOption option)
|
||||
{
|
||||
return _file.Get.Read(out bytesRead, offset, destination, in option);
|
||||
}
|
||||
|
||||
private Result ReadWithCacheAccessLog(out long bytesRead, long offset, Span<byte> destination,
|
||||
in ReadOption option, bool usePathCache, bool useDataCache)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result ReadWithoutCacheAccessLog(out long bytesRead, long offset, Span<byte> destination,
|
||||
in ReadOption option)
|
||||
{
|
||||
return _file.Get.Read(out bytesRead, offset, destination, in option);
|
||||
}
|
||||
|
||||
public Result Read(out long bytesRead, long offset, Span<byte> destination, in ReadOption option)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out bytesRead);
|
||||
@ -113,42 +117,36 @@ internal class FileAccessor : IDisposable
|
||||
return _lastResult;
|
||||
}
|
||||
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalse
|
||||
bool usePathCache = _parentFileSystem is not null && _filePathHash.Data != 0;
|
||||
bool usePathCache = _parentFileSystem is not null && _filePathHash is not null;
|
||||
bool useDataCache = Hos.Fs.Impl.IsGlobalFileDataCacheEnabled() && _parentFileSystem is not null &&
|
||||
_parentFileSystem.IsFileDataCacheAttachable();
|
||||
|
||||
// Todo: Call IsGlobalFileDataCacheEnabled
|
||||
#pragma warning disable 162
|
||||
bool useDataCache = false && _parentFileSystem is not null && _parentFileSystem.IsFileDataCacheAttachable();
|
||||
#pragma warning restore 162
|
||||
if (usePathCache || useDataCache)
|
||||
{
|
||||
return ReadWithCacheAccessLog(out bytesRead, offset, destination, in option, usePathCache,
|
||||
useDataCache);
|
||||
}
|
||||
|
||||
if (Hos.Fs.Impl.IsEnabledAccessLog() && Hos.Fs.Impl.IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
Tick start = Hos.Os.GetSystemTick();
|
||||
rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option);
|
||||
Tick end = Hos.Os.GetSystemTick();
|
||||
|
||||
var sb = new U8StringBuilder(logBuffer, true);
|
||||
sb.Append(LogOffset).AppendFormat(offset)
|
||||
.Append(LogSize).AppendFormat(destination.Length)
|
||||
.Append(LogReadSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in bytesRead, rc));
|
||||
|
||||
Hos.Fs.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(logBuffer),
|
||||
nameof(UserFile.ReadFile));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Hos.Fs.Impl.IsEnabledAccessLog() && Hos.Fs.Impl.IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
Tick start = Hos.Os.GetSystemTick();
|
||||
rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option);
|
||||
Tick end = Hos.Os.GetSystemTick();
|
||||
|
||||
var sb = new U8StringBuilder(logBuffer, true);
|
||||
sb.Append(LogOffset).AppendFormat(offset)
|
||||
.Append(LogSize).AppendFormat(destination.Length)
|
||||
.Append(LogReadSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in bytesRead, rc));
|
||||
|
||||
Hos.Fs.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(logBuffer),
|
||||
nameof(UserFile.ReadFile));
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option);
|
||||
}
|
||||
|
||||
return rc;
|
||||
rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option);
|
||||
}
|
||||
// ReSharper restore ConditionIsAlwaysTrueOrFalse
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public Result Write(long offset, ReadOnlySpan<byte> source, in WriteOption option)
|
||||
@ -159,9 +157,11 @@ internal class FileAccessor : IDisposable
|
||||
using ScopedSetter<WriteState> setter =
|
||||
ScopedSetter<WriteState>.MakeScopedSetter(ref _writeState, WriteState.Failed);
|
||||
|
||||
if (_filePathHash.Data != 0)
|
||||
if (_filePathHash is not null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Result rc = UpdateLastResult(Hos.Fs.Impl.WriteViaPathBasedFileDataCache(_file.Get, (int)GetOpenMode(),
|
||||
_parentFileSystem, in _filePathHash.Value, _pathHashIndex, offset, source, in option));
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -193,19 +193,27 @@ internal class FileAccessor : IDisposable
|
||||
if (_lastResult.IsFailure())
|
||||
return _lastResult;
|
||||
|
||||
WriteState oldWriteState = _writeState;
|
||||
WriteState originalWriteState = _writeState;
|
||||
using ScopedSetter<WriteState> setter =
|
||||
ScopedSetter<WriteState>.MakeScopedSetter(ref _writeState, WriteState.Failed);
|
||||
|
||||
Result rc = UpdateLastResult(_file.Get.SetSize(size));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (_filePathHash.Data != 0)
|
||||
if (_filePathHash is not null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries();
|
||||
|
||||
Result rc = UpdateLastResult(_file.Get.SetSize(size));
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntry(_parentFileSystem, in _filePathHash.Value, _pathHashIndex);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
}
|
||||
else
|
||||
{
|
||||
Result rc = UpdateLastResult(_file.Get.SetSize(size));
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
setter.Set(oldWriteState);
|
||||
setter.Set(originalWriteState);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
@ -224,4 +232,4 @@ internal class FileAccessor : IDisposable
|
||||
{
|
||||
return _file.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.Os;
|
||||
using LibHac.Util;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
@ -10,6 +11,13 @@ using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace LibHac.Fs.Impl;
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to a mounted <see cref="IFileSystem"/> and contains metadata and objects related to it.
|
||||
/// This data includes the mount name, open files and directories, whether the access log is enabled for this file
|
||||
/// system, whether caching is being used, how to get a save file system's <see cref="SaveDataAttribute"/> and
|
||||
/// the target used to include a save file system in a multi-commit operation.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
internal class FileSystemAccessor : IDisposable
|
||||
{
|
||||
private const string EmptyMountNameMessage = "Error: Mount failed because the mount name was empty.\n";
|
||||
@ -31,7 +39,7 @@ internal class FileSystemAccessor : IDisposable
|
||||
private bool _isPathCacheAttached;
|
||||
private IMultiCommitTarget _multiCommitTarget;
|
||||
private PathFlags _pathFlags;
|
||||
private Optional<Ncm.DataId> _dataId;
|
||||
private IStorage _storageForPurgeFileDataCache;
|
||||
|
||||
internal HorizonClient Hos { get; }
|
||||
|
||||
@ -44,7 +52,7 @@ internal class FileSystemAccessor : IDisposable
|
||||
_fileSystem = new UniqueRef<IFileSystem>(ref fileSystem);
|
||||
_openFiles = new LinkedList<FileAccessor>();
|
||||
_openDirectories = new LinkedList<DirectoryAccessor>();
|
||||
_openListLock.Initialize();
|
||||
_openListLock = new SdkMutexType();
|
||||
_mountNameGenerator = new UniqueRef<ICommonMountNameGenerator>(ref mountNameGenerator);
|
||||
_saveDataAttributeGetter = new UniqueRef<ISaveDataAttributeGetter>(ref saveAttributeGetter);
|
||||
_multiCommitTarget = multiCommitTarget;
|
||||
@ -90,7 +98,8 @@ internal class FileSystemAccessor : IDisposable
|
||||
|
||||
if (_isPathCacheAttached)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries();
|
||||
Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntries(this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +122,16 @@ internal class FileSystemAccessor : IDisposable
|
||||
}
|
||||
|
||||
public void SetAccessLog(bool isEnabled) => _isAccessLogEnabled = isEnabled;
|
||||
public void SetFileDataCacheAttachable(bool isAttachable) => _isDataCacheAttachable = isAttachable;
|
||||
|
||||
public void SetFileDataCacheAttachable(bool isAttachable, IStorage storageForPurgeFileDataCache)
|
||||
{
|
||||
if (isAttachable)
|
||||
Assert.SdkAssert(storageForPurgeFileDataCache is not null);
|
||||
|
||||
_isDataCacheAttachable = isAttachable;
|
||||
_storageForPurgeFileDataCache = storageForPurgeFileDataCache;
|
||||
}
|
||||
|
||||
public void SetPathBasedFileDataCacheAttachable(bool isAttachable) => _isPathCacheAttachable = isAttachable;
|
||||
|
||||
public bool IsEnabledAccessLog() => _isAccessLogEnabled;
|
||||
@ -126,10 +144,7 @@ internal class FileSystemAccessor : IDisposable
|
||||
_isPathCacheAttached = true;
|
||||
}
|
||||
|
||||
public Optional<Ncm.DataId> GetDataId() => _dataId;
|
||||
public void SetDataId(Optional<Ncm.DataId> dataId) => _dataId = dataId;
|
||||
|
||||
public Result SetUpPath(ref Path path, U8Span pathBuffer)
|
||||
private Result SetUpPath(ref Path path, U8Span pathBuffer)
|
||||
{
|
||||
Result rc = PathFormatter.IsNormalized(out bool isNormalized, out _, pathBuffer, _pathFlags);
|
||||
|
||||
@ -168,7 +183,12 @@ internal class FileSystemAccessor : IDisposable
|
||||
|
||||
if (_isPathCacheAttached)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries();
|
||||
|
||||
rc = _fileSystem.Get.CreateFile(in pathNormalized, size, option);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntry(this, in pathNormalized);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -251,7 +271,12 @@ internal class FileSystemAccessor : IDisposable
|
||||
|
||||
if (_isPathCacheAttached)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries();
|
||||
|
||||
rc = _fileSystem.Get.RenameFile(in currentPathNormalized, in newPathNormalized);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntry(this, in newPathNormalized);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -274,7 +299,12 @@ internal class FileSystemAccessor : IDisposable
|
||||
|
||||
if (_isPathCacheAttached)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries();
|
||||
|
||||
rc = _fileSystem.Get.RenameDirectory(in currentPathNormalized, in newPathNormalized);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntries(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -347,11 +377,17 @@ internal class FileSystemAccessor : IDisposable
|
||||
{
|
||||
if (mode.HasFlag(OpenMode.AllowAppend))
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries();
|
||||
Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntry(this, in pathNormalized);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var hash = new Box<FilePathHash>();
|
||||
|
||||
if (Hos.Fs.Impl.FindPathBasedFileDataCacheEntry(out hash.Value, out int hashIndex, this, in pathNormalized))
|
||||
{
|
||||
accessor.SetFilePathHash(hash, hashIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -432,6 +468,13 @@ internal class FileSystemAccessor : IDisposable
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void PurgeFileDataCache(FileDataCacheAccessor accessor)
|
||||
{
|
||||
Assert.SdkAssert(_storageForPurgeFileDataCache is not null);
|
||||
|
||||
accessor.Purge(_storageForPurgeFileDataCache);
|
||||
}
|
||||
|
||||
public U8Span GetName()
|
||||
{
|
||||
return new U8Span(_mountName.Name);
|
||||
@ -470,11 +513,6 @@ internal class FileSystemAccessor : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public void PurgeFileDataCache(FileDataCacheAccessor cacheAccessor)
|
||||
{
|
||||
cacheAccessor.Purge(_fileSystem.Get);
|
||||
}
|
||||
|
||||
internal void NotifyCloseFile(FileAccessor file)
|
||||
{
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _openListLock);
|
||||
@ -487,8 +525,10 @@ internal class FileSystemAccessor : IDisposable
|
||||
Remove(_openDirectories, directory);
|
||||
}
|
||||
|
||||
/// <summary>"<c>$fs</c>"</summary>
|
||||
private static ReadOnlySpan<byte> LogFsModuleName => new[] { (byte)'$', (byte)'f', (byte)'s' }; // "$fs"
|
||||
|
||||
/// <summary>"<c>------ FS ERROR INFORMATION ------\n</c>"</summary>
|
||||
private static ReadOnlySpan<byte> LogFsErrorInfo => // "------ FS ERROR INFORMATION ------\n"
|
||||
new[]
|
||||
{
|
||||
@ -499,6 +539,7 @@ internal class FileSystemAccessor : IDisposable
|
||||
(byte)'-', (byte)'-', (byte)'\\', (byte)'n'
|
||||
};
|
||||
|
||||
/// <summary>"<c>Error: File not closed</c>"</summary>
|
||||
private static ReadOnlySpan<byte> LogFileNotClosed => // "Error: File not closed"
|
||||
new[]
|
||||
{
|
||||
@ -507,6 +548,7 @@ internal class FileSystemAccessor : IDisposable
|
||||
(byte)'c', (byte)'l', (byte)'o', (byte)'s', (byte)'e', (byte)'d'
|
||||
};
|
||||
|
||||
/// <summary>"<c>Error: Directory not closed</c>"</summary>
|
||||
private static ReadOnlySpan<byte> LogDirectoryNotClosed => // "Error: Directory not closed"
|
||||
new[]
|
||||
{
|
||||
@ -516,6 +558,7 @@ internal class FileSystemAccessor : IDisposable
|
||||
(byte)'s', (byte)'e', (byte)'d'
|
||||
};
|
||||
|
||||
/// <summary>"<c> (mount_name: "</c>"</summary>
|
||||
private static ReadOnlySpan<byte> LogMountName => // " (mount_name: ""
|
||||
new[]
|
||||
{
|
||||
@ -523,6 +566,7 @@ internal class FileSystemAccessor : IDisposable
|
||||
(byte)'n', (byte)'a', (byte)'m', (byte)'e', (byte)':', (byte)' ', (byte)'"'
|
||||
};
|
||||
|
||||
/// <summary>"<c>", count: </c>"</summary>
|
||||
private static ReadOnlySpan<byte> LogCount => // "", count: "
|
||||
new[]
|
||||
{
|
||||
@ -530,10 +574,13 @@ internal class FileSystemAccessor : IDisposable
|
||||
(byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>)\n</c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogLineEnd => new[] { (byte)')', (byte)'\\', (byte)'n' }; // ")\n"
|
||||
|
||||
/// <summary>"<c> | </c>"</summary>
|
||||
public static ReadOnlySpan<byte> LogOrOperator => new[] { (byte)' ', (byte)'|', (byte)' ' }; // " | "
|
||||
|
||||
/// <summary>"<c>OpenMode_Read</c>"</summary>
|
||||
private static ReadOnlySpan<byte> LogOpenModeRead => // "OpenMode_Read"
|
||||
new[]
|
||||
{
|
||||
@ -541,6 +588,7 @@ internal class FileSystemAccessor : IDisposable
|
||||
(byte)'_', (byte)'R', (byte)'e', (byte)'a', (byte)'d'
|
||||
};
|
||||
|
||||
/// <summary>"<c>OpenMode_Write</c>"</summary>
|
||||
private static ReadOnlySpan<byte> LogOpenModeWrite => // "OpenMode_Write"
|
||||
new[]
|
||||
{
|
||||
@ -548,6 +596,7 @@ internal class FileSystemAccessor : IDisposable
|
||||
(byte)'_', (byte)'W', (byte)'r', (byte)'i', (byte)'t', (byte)'e'
|
||||
};
|
||||
|
||||
/// <summary>"<c>OpenMode_AllowAppend</c>"</summary>
|
||||
private static ReadOnlySpan<byte> LogOpenModeAppend => // "OpenMode_AllowAppend"
|
||||
new[]
|
||||
{
|
||||
@ -556,6 +605,7 @@ internal class FileSystemAccessor : IDisposable
|
||||
(byte)'p', (byte)'e', (byte)'n', (byte)'d'
|
||||
};
|
||||
|
||||
/// <summary>"<c> handle: 0x</c>"</summary>
|
||||
private static ReadOnlySpan<byte> LogHandle => // " handle: 0x"
|
||||
new[]
|
||||
{
|
||||
@ -563,6 +613,7 @@ internal class FileSystemAccessor : IDisposable
|
||||
(byte)'d', (byte)'l', (byte)'e', (byte)':', (byte)' ', (byte)'0', (byte)'x'
|
||||
};
|
||||
|
||||
/// <summary>"<c>, open_mode: </c>"</summary>
|
||||
private static ReadOnlySpan<byte> LogOpenMode => // ", open_mode: "
|
||||
new[]
|
||||
{
|
||||
@ -570,6 +621,7 @@ internal class FileSystemAccessor : IDisposable
|
||||
(byte)'o', (byte)'d', (byte)'e', (byte)':', (byte)' '
|
||||
};
|
||||
|
||||
/// <summary>"<c>, size:</c>"</summary>
|
||||
private static ReadOnlySpan<byte> LogSize => // ", size: "
|
||||
new[]
|
||||
{
|
||||
@ -673,4 +725,4 @@ internal class FileSystemAccessor : IDisposable
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,8 +6,11 @@ namespace LibHac.Fs.Fsa;
|
||||
/// <summary>
|
||||
/// Provides an interface for enumerating the child entries of a directory.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public abstract class IDirectory : IDisposable
|
||||
{
|
||||
public virtual void Dispose() { }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the next entries that this directory contains. Does not search subdirectories.
|
||||
/// </summary>
|
||||
@ -43,6 +46,4 @@ public abstract class IDirectory : IDisposable
|
||||
|
||||
protected abstract Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer);
|
||||
protected abstract Result DoGetEntryCount(out long entryCount);
|
||||
|
||||
public virtual void Dispose() { }
|
||||
}
|
||||
}
|
@ -10,17 +10,20 @@ namespace LibHac.Fs.Fsa;
|
||||
/// </summary>
|
||||
/// <remarks><see cref="IFile"/> is similar to <see cref="IStorage"/>, and has a few main differences:
|
||||
///
|
||||
/// - <see cref="IFile"/> allows an <see cref="OpenMode"/> to be set that controls read, write
|
||||
/// and append permissions for the file.
|
||||
/// <para>- <see cref="IFile"/> allows an <see cref="OpenMode"/> to be set that controls read, write
|
||||
/// and append permissions for the file.</para>
|
||||
///
|
||||
/// - If the <see cref="IFile"/> cannot read or write as many bytes as requested, it will read
|
||||
/// or write as many bytes as it can and return that number of bytes to the caller.
|
||||
/// <para>- If the <see cref="IFile"/> cannot read or write as many bytes as requested, it will read
|
||||
/// or write as many bytes as it can and return that number of bytes to the caller.</para>
|
||||
///
|
||||
/// - If <see cref="Write"/> is called on an offset past the end of the <see cref="IFile"/>,
|
||||
/// <para>- If <see cref="Write"/> is called on an offset past the end of the <see cref="IFile"/>,
|
||||
/// the <see cref="OpenMode.AllowAppend"/> mode is set and the file supports expansion,
|
||||
/// the file will be expanded so that it is large enough to contain the written data.</remarks>
|
||||
/// the file will be expanded so that it is large enough to contain the written data.</para>
|
||||
/// <para>Based on FS 13.1.0 (nnSdk 13.4.0)</para></remarks>
|
||||
public abstract class IFile : IDisposable
|
||||
{
|
||||
public virtual void Dispose() { }
|
||||
|
||||
/// <summary>
|
||||
/// Reads a sequence of bytes from the current <see cref="IFile"/>.
|
||||
/// </summary>
|
||||
@ -174,15 +177,6 @@ public abstract class IFile : IDisposable
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected Result DrySetSize(long size, OpenMode openMode)
|
||||
{
|
||||
// Check that we can write.
|
||||
if (!openMode.HasFlag(OpenMode.Write))
|
||||
return ResultFs.WriteUnpermitted.Log();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected Result DryWrite(out bool needsAppend, long offset, long size, in WriteOption option,
|
||||
OpenMode openMode)
|
||||
{
|
||||
@ -196,6 +190,8 @@ public abstract class IFile : IDisposable
|
||||
Result rc = GetSize(out long fileSize);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
needsAppend = false;
|
||||
|
||||
if (fileSize < offset + size)
|
||||
{
|
||||
if (!openMode.HasFlag(OpenMode.AllowAppend))
|
||||
@ -203,10 +199,15 @@ public abstract class IFile : IDisposable
|
||||
|
||||
needsAppend = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
needsAppend = false;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected Result DrySetSize(long size, OpenMode openMode)
|
||||
{
|
||||
// Check that we can write.
|
||||
if (!openMode.HasFlag(OpenMode.Write))
|
||||
return ResultFs.WriteUnpermitted.Log();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
@ -218,6 +219,4 @@ public abstract class IFile : IDisposable
|
||||
protected abstract Result DoGetSize(out long size);
|
||||
protected abstract Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
|
||||
ReadOnlySpan<byte> inBuffer);
|
||||
|
||||
public virtual void Dispose() { }
|
||||
}
|
||||
}
|
@ -8,8 +8,11 @@ namespace LibHac.Fs.Fsa;
|
||||
/// <summary>
|
||||
/// Provides an interface for accessing a file system. <c>/</c> is used as the path delimiter.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public abstract class IFileSystem : IDisposable
|
||||
{
|
||||
public virtual void Dispose() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates or overwrites a file at the specified path.
|
||||
/// </summary>
|
||||
@ -171,25 +174,22 @@ public abstract class IFileSystem : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of available free space on a drive, in bytes.
|
||||
/// Opens an <see cref="IFile"/> instance for the specified path.
|
||||
/// </summary>
|
||||
/// <param name="freeSpace">If the operation returns successfully, the amount of free space available on the drive, in bytes.</param>
|
||||
/// <param name="path">The path of the drive to query. Unused in almost all cases.</param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
public Result GetFreeSpaceSize(out long freeSpace, in Path path)
|
||||
/// <param name="file">If the operation returns successfully,
|
||||
/// An <see cref="IFile"/> instance for the specified path.</param>
|
||||
/// <param name="path">The full path of the file to open.</param>
|
||||
/// <param name="mode">Specifies the access permissions of the created <see cref="IFile"/>.</param>
|
||||
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
|
||||
/// <see cref="ResultFs.PathNotFound"/>: The specified path does not exist or is a directory.<br/>
|
||||
/// <see cref="ResultFs.TargetLocked"/>: When opening as <see cref="OpenMode.Write"/>,
|
||||
/// the file is already opened as <see cref="OpenMode.Write"/>.</returns>
|
||||
public Result OpenFile(ref UniqueRef<IFile> file, in Path path, OpenMode mode)
|
||||
{
|
||||
return DoGetFreeSpaceSize(out freeSpace, in path);
|
||||
}
|
||||
if ((mode & OpenMode.ReadWrite) == 0 || (mode & ~OpenMode.All) != 0)
|
||||
return ResultFs.InvalidModeForFileOpen.Log();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total size of storage space on a drive, in bytes.
|
||||
/// </summary>
|
||||
/// <param name="totalSpace">If the operation returns successfully, the total size of the drive, in bytes.</param>
|
||||
/// <param name="path">The path of the drive to query. Unused in almost all cases.</param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
public Result GetTotalSpaceSize(out long totalSpace, in Path path)
|
||||
{
|
||||
return DoGetTotalSpaceSize(out totalSpace, in path);
|
||||
return DoOpenFile(ref file, in path, mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -215,25 +215,6 @@ public abstract class IFileSystem : IDisposable
|
||||
return DoOpenFile(ref file, in pathNormalized, mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens an <see cref="IFile"/> instance for the specified path.
|
||||
/// </summary>
|
||||
/// <param name="file">If the operation returns successfully,
|
||||
/// An <see cref="IFile"/> instance for the specified path.</param>
|
||||
/// <param name="path">The full path of the file to open.</param>
|
||||
/// <param name="mode">Specifies the access permissions of the created <see cref="IFile"/>.</param>
|
||||
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
|
||||
/// <see cref="ResultFs.PathNotFound"/>: The specified path does not exist or is a directory.<br/>
|
||||
/// <see cref="ResultFs.TargetLocked"/>: When opening as <see cref="OpenMode.Write"/>,
|
||||
/// the file is already opened as <see cref="OpenMode.Write"/>.</returns>
|
||||
public Result OpenFile(ref UniqueRef<IFile> file, in Path path, OpenMode mode)
|
||||
{
|
||||
if ((mode & OpenMode.ReadWrite) == 0 || (mode & ~OpenMode.All) != 0)
|
||||
return ResultFs.InvalidModeForFileOpen.Log();
|
||||
|
||||
return DoOpenFile(ref file, in path, mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IDirectory"/> instance for enumerating the specified directory.
|
||||
/// </summary>
|
||||
@ -264,6 +245,28 @@ public abstract class IFileSystem : IDisposable
|
||||
|
||||
public Result Flush() => DoFlush();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the amount of available free space on a drive, in bytes.
|
||||
/// </summary>
|
||||
/// <param name="freeSpace">If the operation returns successfully, the amount of free space available on the drive, in bytes.</param>
|
||||
/// <param name="path">The path of the drive to query. Unused in almost all cases.</param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
public Result GetFreeSpaceSize(out long freeSpace, in Path path)
|
||||
{
|
||||
return DoGetFreeSpaceSize(out freeSpace, in path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total size of storage space on a drive, in bytes.
|
||||
/// </summary>
|
||||
/// <param name="totalSpace">If the operation returns successfully, the total size of the drive, in bytes.</param>
|
||||
/// <param name="path">The path of the drive to query. Unused in almost all cases.</param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
public Result GetTotalSpaceSize(out long totalSpace, in Path path)
|
||||
{
|
||||
return DoGetTotalSpaceSize(out totalSpace, in path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation, last accessed, and last modified timestamps of a file or directory.
|
||||
/// </summary>
|
||||
@ -333,8 +336,6 @@ public abstract class IFileSystem : IDisposable
|
||||
|
||||
protected virtual Result DoQueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId,
|
||||
in Path path) => ResultFs.NotImplemented.Log();
|
||||
|
||||
public virtual void Dispose() { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -372,4 +373,4 @@ public enum QueryId
|
||||
UpdateMac = 1,
|
||||
IsSignedSystemPartitionOnSdCardValid = 2,
|
||||
QueryUnpreparedFileInformation = 3
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Os;
|
||||
using LibHac.Util;
|
||||
|
||||
@ -14,7 +13,7 @@ namespace LibHac.Fs.Impl;
|
||||
/// Holds a list of <see cref="FileSystemAccessor"/>s that are indexed by their name.
|
||||
/// These may be retrieved or removed using their name as a key.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 12.1.0 (nnSdk 12.3.1)</remarks>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
internal class MountTable : IDisposable
|
||||
{
|
||||
private LinkedList<FileSystemAccessor> _fileSystemList;
|
||||
@ -27,7 +26,6 @@ internal class MountTable : IDisposable
|
||||
{
|
||||
_fileSystemList = new LinkedList<FileSystemAccessor>();
|
||||
_mutex = new SdkMutexType();
|
||||
_mutex.Initialize();
|
||||
|
||||
_fsClient = fsClient;
|
||||
}
|
||||
@ -77,9 +75,11 @@ internal class MountTable : IDisposable
|
||||
currentNode is not null;
|
||||
currentNode = currentNode.Next)
|
||||
{
|
||||
if (!Matches(currentNode.Value, name)) continue;
|
||||
accessor = currentNode.Value;
|
||||
return Result.Success;
|
||||
if (Matches(currentNode.Value, name))
|
||||
{
|
||||
accessor = currentNode.Value;
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
return ResultFs.NotMounted.Log();
|
||||
@ -122,44 +122,4 @@ internal class MountTable : IDisposable
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int GetDataIdCount()
|
||||
{
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (LinkedListNode<FileSystemAccessor> currentNode = _fileSystemList.First;
|
||||
currentNode is not null;
|
||||
currentNode = currentNode.Next)
|
||||
{
|
||||
if (currentNode.Value.GetDataId().HasValue)
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public Result ListDataId(out int dataIdCount, Span<DataId> dataIdBuffer)
|
||||
{
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (LinkedListNode<FileSystemAccessor> currentNode = _fileSystemList.First;
|
||||
currentNode is not null && count < dataIdBuffer.Length;
|
||||
currentNode = currentNode.Next)
|
||||
{
|
||||
Optional<DataId> dataId = currentNode.Value.GetDataId();
|
||||
|
||||
if (dataId.HasValue)
|
||||
{
|
||||
dataIdBuffer[count] = dataId.Value;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
dataIdCount = count;
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +1,36 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.Os;
|
||||
using LibHac.Util;
|
||||
using static LibHac.Fs.StringTraits;
|
||||
using static LibHac.Fs.Impl.AccessLogStrings;
|
||||
using static LibHac.Fs.StringTraits;
|
||||
|
||||
namespace LibHac.Fs.Fsa;
|
||||
|
||||
/// <summary>
|
||||
/// Contains functions for managing mounted file systems.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public static class MountUtility
|
||||
{
|
||||
internal static Result GetMountNameAndSubPath(out MountName mountName, out U8Span subPath, U8Span path)
|
||||
/// <summary>
|
||||
/// Gets the mount name and non-mounted path components from a path that has a mount name.
|
||||
/// </summary>
|
||||
/// <param name="mountName">If the method returns successfully, contains the mount name of the provided path;
|
||||
/// otherwise the contents are undefined.</param>
|
||||
/// <param name="subPath">If the method returns successfully, contains the provided path without the
|
||||
/// mount name; otherwise the contents are undefined.</param>
|
||||
/// <param name="path">The <see cref="Path"/> to process.</param>
|
||||
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
|
||||
/// <see cref="ResultFs.InvalidPathFormat"/>: <paramref name="path"/> does not contain a sub path after
|
||||
/// the mount name that begins with <c>/</c> or <c>\</c>.<br/>
|
||||
/// <see cref="ResultFs.InvalidMountName"/>: <paramref name="path"/> contains an invalid mount name
|
||||
/// or does not have a mount name.</returns>
|
||||
private static Result GetMountNameAndSubPath(out MountName mountName, out U8Span subPath, U8Span path)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out mountName);
|
||||
subPath = default;
|
||||
@ -22,7 +41,7 @@ public static class MountUtility
|
||||
if (WindowsPath.IsWindowsDrive(path) || WindowsPath.IsUncPath(path))
|
||||
{
|
||||
StringUtils.Copy(mountName.Name, CommonPaths.HostRootFileSystemMountName);
|
||||
mountName.Name[PathTool.MountNameLengthMax] = StringTraits.NullTerminator;
|
||||
mountName.Name[PathTool.MountNameLengthMax] = NullTerminator;
|
||||
|
||||
subPath = path;
|
||||
return Result.Success;
|
||||
@ -30,7 +49,7 @@ public static class MountUtility
|
||||
|
||||
for (int i = 0; i <= maxMountLen; i++)
|
||||
{
|
||||
if (path[i] == StringTraits.DriveSeparator)
|
||||
if (path[i] == DriveSeparator)
|
||||
{
|
||||
mountLen = i;
|
||||
break;
|
||||
@ -53,7 +72,7 @@ public static class MountUtility
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
|
||||
path.Value.Slice(0, mountLen).CopyTo(mountName.Name);
|
||||
mountName.Name[mountLen] = StringTraits.NullTerminator;
|
||||
mountName.Name[mountLen] = NullTerminator;
|
||||
subPath = subPathTemp;
|
||||
|
||||
return Result.Success;
|
||||
@ -82,8 +101,7 @@ public static class MountUtility
|
||||
return false;
|
||||
}
|
||||
|
||||
// Todo: VerifyUtf8String
|
||||
return true;
|
||||
return Utf8StringUtil.VerifyUtf8String(name);
|
||||
}
|
||||
|
||||
public static bool IsUsedReservedMountName(this FileSystemClientImpl fs, U8Span name)
|
||||
@ -144,7 +162,12 @@ public static class MountUtility
|
||||
|
||||
if (fileSystem.IsFileDataCacheAttachable())
|
||||
{
|
||||
// Todo: Data cache purge
|
||||
using var fileDataCacheAccessor = new GlobalFileDataCacheAccessorReadableScopedPointer();
|
||||
|
||||
if (fs.TryGetGlobalFileDataCacheAccessor(ref Unsafe.AsRef(in fileDataCacheAccessor)))
|
||||
{
|
||||
fileSystem.PurgeFileDataCache(fileDataCacheAccessor.Get());
|
||||
}
|
||||
}
|
||||
|
||||
fs.Unregister(mountName);
|
||||
@ -226,13 +249,23 @@ public static class MountUtility
|
||||
public static Result ConvertToFsCommonPath(this FileSystemClient fs, U8SpanMutable commonPathBuffer,
|
||||
U8Span path)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (commonPathBuffer.IsNull())
|
||||
return ResultFs.NullptrArgument.Log();
|
||||
{
|
||||
rc = ResultFs.NullptrArgument.Value;
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (path.IsNull())
|
||||
return ResultFs.NullptrArgument.Log();
|
||||
{
|
||||
rc = ResultFs.NullptrArgument.Value;
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result rc = GetMountNameAndSubPath(out MountName mountName, out U8Span subPath, path);
|
||||
rc = GetMountNameAndSubPath(out MountName mountName, out U8Span subPath, path);
|
||||
fs.Impl.AbortIfNeeded(rc);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.Fs.Fsa;
|
||||
|
||||
@ -15,12 +15,93 @@ public interface ISaveDataAttributeGetter : IDisposable
|
||||
Result GetSaveDataAttribute(out SaveDataAttribute attribute);
|
||||
}
|
||||
|
||||
public interface IUnmountHookInvoker : IDisposable
|
||||
{
|
||||
void Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains functions for registering and unregistering mounted <see cref="IFileSystem"/>s.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 12.1.0 (nnSdk 12.3.1)</remarks>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public static class Registrar
|
||||
{
|
||||
private class UnmountHookFileSystem : IFileSystem
|
||||
{
|
||||
private UniqueRef<IFileSystem> _fileSystem;
|
||||
private UniqueRef<IUnmountHookInvoker> _unmountHookInvoker;
|
||||
|
||||
public UnmountHookFileSystem(ref UniqueRef<IFileSystem> fileSystem,
|
||||
ref UniqueRef<IUnmountHookInvoker> unmountHookInvoker)
|
||||
{
|
||||
_fileSystem = new UniqueRef<IFileSystem>(ref fileSystem);
|
||||
_unmountHookInvoker = new UniqueRef<IUnmountHookInvoker>(ref unmountHookInvoker);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (_unmountHookInvoker.HasValue)
|
||||
_unmountHookInvoker.Get.Invoke();
|
||||
|
||||
_unmountHookInvoker.Destroy();
|
||||
_fileSystem.Destroy();
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option) =>
|
||||
_fileSystem.Get.CreateFile(in path, size, option);
|
||||
|
||||
protected override Result DoDeleteFile(in Path path) => _fileSystem.Get.DeleteFile(in path);
|
||||
|
||||
protected override Result DoCreateDirectory(in Path path) => _fileSystem.Get.CreateDirectory(in path);
|
||||
|
||||
protected override Result DoDeleteDirectory(in Path path) => _fileSystem.Get.DeleteDirectory(in path);
|
||||
|
||||
protected override Result DoDeleteDirectoryRecursively(in Path path) =>
|
||||
_fileSystem.Get.DeleteDirectoryRecursively(in path);
|
||||
|
||||
protected override Result DoCleanDirectoryRecursively(in Path path) =>
|
||||
_fileSystem.Get.CleanDirectoryRecursively(in path);
|
||||
|
||||
protected override Result DoRenameFile(in Path currentPath, in Path newPath) =>
|
||||
_fileSystem.Get.RenameFile(in currentPath, in newPath);
|
||||
|
||||
protected override Result DoRenameDirectory(in Path currentPath, in Path newPath) =>
|
||||
_fileSystem.Get.RenameDirectory(in currentPath, in newPath);
|
||||
|
||||
protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path) =>
|
||||
_fileSystem.Get.GetEntryType(out entryType, in path);
|
||||
|
||||
protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path) =>
|
||||
_fileSystem.Get.GetFreeSpaceSize(out freeSpace, in path);
|
||||
|
||||
protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path) =>
|
||||
_fileSystem.Get.GetTotalSpaceSize(out totalSpace, in path);
|
||||
|
||||
protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode) =>
|
||||
_fileSystem.Get.OpenFile(ref outFile, in path, mode);
|
||||
|
||||
protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
|
||||
OpenDirectoryMode mode) =>
|
||||
_fileSystem.Get.OpenDirectory(ref outDirectory, in path, mode);
|
||||
|
||||
protected override Result DoCommit() => _fileSystem.Get.Commit();
|
||||
|
||||
protected override Result DoCommitProvisionally(long counter) =>
|
||||
_fileSystem.Get.CommitProvisionally(counter);
|
||||
|
||||
protected override Result DoRollback() => _fileSystem.Get.Rollback();
|
||||
|
||||
protected override Result DoFlush() => _fileSystem.Get.Flush();
|
||||
|
||||
protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path) =>
|
||||
_fileSystem.Get.GetFileTimeStampRaw(out timeStamp, in path);
|
||||
|
||||
protected override Result DoQueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId,
|
||||
in Path path) => _fileSystem.Get.QueryEntry(outBuffer, inBuffer, queryId, in path);
|
||||
}
|
||||
|
||||
public static Result Register(this FileSystemClient fs, U8Span name, ref UniqueRef<IFileSystem> fileSystem)
|
||||
{
|
||||
using var attributeGetter = new UniqueRef<ISaveDataAttributeGetter>();
|
||||
@ -51,12 +132,28 @@ public static class Registrar
|
||||
|
||||
public static Result Register(this FileSystemClient fs, U8Span name, IMultiCommitTarget multiCommitTarget,
|
||||
ref UniqueRef<IFileSystem> fileSystem, ref UniqueRef<ICommonMountNameGenerator> mountNameGenerator,
|
||||
bool useDataCache, bool usePathCache)
|
||||
bool useDataCache, IStorage storageForPurgeFileDataCache, bool usePathCache)
|
||||
{
|
||||
using var unmountHookInvoker = new UniqueRef<IUnmountHookInvoker>();
|
||||
using var attributeGetter = new UniqueRef<ISaveDataAttributeGetter>();
|
||||
|
||||
Result rc = Register(fs, name, multiCommitTarget, ref fileSystem, ref mountNameGenerator,
|
||||
ref attributeGetter.Ref(), useDataCache, storageForPurgeFileDataCache, usePathCache,
|
||||
ref unmountHookInvoker.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result Register(this FileSystemClient fs, U8Span name, IMultiCommitTarget multiCommitTarget,
|
||||
ref UniqueRef<IFileSystem> fileSystem, ref UniqueRef<ICommonMountNameGenerator> mountNameGenerator,
|
||||
bool useDataCache, IStorage storageForPurgeFileDataCache, bool usePathCache,
|
||||
ref UniqueRef<IUnmountHookInvoker> unmountHook)
|
||||
{
|
||||
using var attributeGetter = new UniqueRef<ISaveDataAttributeGetter>();
|
||||
|
||||
Result rc = Register(fs, name, multiCommitTarget, ref fileSystem, ref mountNameGenerator,
|
||||
ref attributeGetter.Ref(), useDataCache, usePathCache, new Optional<Ncm.DataId>());
|
||||
ref attributeGetter.Ref(), useDataCache, storageForPurgeFileDataCache, usePathCache, ref unmountHook);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return Result.Success;
|
||||
@ -64,12 +161,14 @@ public static class Registrar
|
||||
|
||||
public static Result Register(this FileSystemClient fs, U8Span name, IMultiCommitTarget multiCommitTarget,
|
||||
ref UniqueRef<IFileSystem> fileSystem, ref UniqueRef<ICommonMountNameGenerator> mountNameGenerator,
|
||||
bool useDataCache, bool usePathCache, Optional<Ncm.DataId> dataId)
|
||||
ref UniqueRef<ISaveDataAttributeGetter> saveAttributeGetter, bool useDataCache,
|
||||
IStorage storageForPurgeFileDataCache, bool usePathCache)
|
||||
{
|
||||
using var attributeGetter = new UniqueRef<ISaveDataAttributeGetter>();
|
||||
using var unmountHookInvoker = new UniqueRef<IUnmountHookInvoker>();
|
||||
|
||||
Result rc = Register(fs, name, multiCommitTarget, ref fileSystem, ref mountNameGenerator,
|
||||
ref attributeGetter.Ref(), useDataCache, usePathCache, dataId);
|
||||
ref saveAttributeGetter, useDataCache, storageForPurgeFileDataCache, usePathCache,
|
||||
ref unmountHookInvoker.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return Result.Success;
|
||||
@ -77,29 +176,29 @@ public static class Registrar
|
||||
|
||||
public static Result Register(this FileSystemClient fs, U8Span name, IMultiCommitTarget multiCommitTarget,
|
||||
ref UniqueRef<IFileSystem> fileSystem, ref UniqueRef<ICommonMountNameGenerator> mountNameGenerator,
|
||||
ref UniqueRef<ISaveDataAttributeGetter> saveAttributeGetter, bool useDataCache, bool usePathCache)
|
||||
ref UniqueRef<ISaveDataAttributeGetter> saveAttributeGetter, bool useDataCache,
|
||||
IStorage storageForPurgeFileDataCache, bool usePathCache, ref UniqueRef<IUnmountHookInvoker> unmountHook)
|
||||
{
|
||||
Result rc = Register(fs, name, multiCommitTarget, ref fileSystem, ref mountNameGenerator,
|
||||
ref saveAttributeGetter, useDataCache, usePathCache, new Optional<Ncm.DataId>());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
if (useDataCache)
|
||||
Assert.SdkAssert(storageForPurgeFileDataCache is not null);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
using (var unmountHookFileSystem =
|
||||
new UniqueRef<UnmountHookFileSystem>(new UnmountHookFileSystem(ref fileSystem, ref unmountHook)))
|
||||
{
|
||||
fileSystem.Set(ref unmountHookFileSystem.Ref());
|
||||
}
|
||||
|
||||
if (!fileSystem.HasValue)
|
||||
return ResultFs.AllocationMemoryFailedInRegisterB.Log();
|
||||
|
||||
public static Result Register(this FileSystemClient fs, U8Span name, IMultiCommitTarget multiCommitTarget,
|
||||
ref UniqueRef<IFileSystem> fileSystem, ref UniqueRef<ICommonMountNameGenerator> mountNameGenerator,
|
||||
ref UniqueRef<ISaveDataAttributeGetter> saveAttributeGetter, bool useDataCache, bool usePathCache,
|
||||
Optional<Ncm.DataId> dataId)
|
||||
{
|
||||
using var accessor = new UniqueRef<FileSystemAccessor>(new FileSystemAccessor(fs.Hos, name,
|
||||
multiCommitTarget, ref fileSystem, ref mountNameGenerator, ref saveAttributeGetter));
|
||||
|
||||
if (!accessor.HasValue)
|
||||
return ResultFs.AllocationMemoryFailedInRegisterB.Log();
|
||||
|
||||
accessor.Get.SetFileDataCacheAttachable(useDataCache);
|
||||
accessor.Get.SetFileDataCacheAttachable(useDataCache, storageForPurgeFileDataCache);
|
||||
accessor.Get.SetPathBasedFileDataCacheAttachable(usePathCache);
|
||||
accessor.Get.SetDataId(dataId);
|
||||
|
||||
Result rc = fs.Impl.Register(ref accessor.Ref());
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
@ -111,4 +210,4 @@ public static class Registrar
|
||||
{
|
||||
fs.Impl.Unregister(name);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user