From d291500b28bf48b163286ef0ee8a0d80480e5acf Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 2 Oct 2019 13:45:58 -0500 Subject: [PATCH] Add ExternalKeySet. Use C# 8.0 --- src/LibHac/Common/Id128.cs | 42 +++------ src/LibHac/Common/Key128.cs | 48 +++++++++++ src/LibHac/Fs/FileBase.cs | 2 +- src/LibHac/Fs/ResultFs.cs | 11 ++- src/LibHac/Fs/RightsId.cs | 19 ++++- src/LibHac/Fs/SaveData.cs | 8 +- src/LibHac/Fs/UserId.cs | 16 +++- src/LibHac/FsService/ExternalKeySet.cs | 108 ++++++++++++++++++++++++ src/LibHac/FsService/FileSystemProxy.cs | 2 +- src/LibHac/FsSystem/NcaUtils/Nca.cs | 8 +- src/LibHac/Keyset.cs | 12 ++- src/LibHac/LibHac.csproj | 2 +- src/LibHac/Ncm/ContentId.cs | 11 ++- src/LibHac/Ncm/PlaceHolderId.cs | 11 ++- src/LibHac/Spl/AccessKey.cs | 21 +++-- src/LibHac/Util.cs | 4 +- 16 files changed, 266 insertions(+), 59 deletions(-) create mode 100644 src/LibHac/Common/Key128.cs create mode 100644 src/LibHac/FsService/ExternalKeySet.cs diff --git a/src/LibHac/Common/Id128.cs b/src/LibHac/Common/Id128.cs index a0289014..2cb598e6 100644 --- a/src/LibHac/Common/Id128.cs +++ b/src/LibHac/Common/Id128.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Runtime.InteropServices; namespace LibHac.Common @@ -6,13 +7,14 @@ namespace LibHac.Common /// /// A generic 128-bit ID value. /// + [DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct Id128 : IEquatable, IComparable, IComparable { public readonly ulong High; public readonly ulong Low; - public static readonly Id128 InvalidId = new Id128(0, 0); + public static Id128 Zero => default; public Id128(ulong high, ulong low) { @@ -28,6 +30,8 @@ namespace LibHac.Common Low = longs[1]; } + public override string ToString() => AsBytes().ToHexString(); + public bool Equals(Id128 other) { return High == other.High && Low == other.Low; @@ -48,11 +52,9 @@ namespace LibHac.Common public int CompareTo(Id128 other) { - // ReSharper disable ImpureMethodCallOnReadonlyValueField int highComparison = High.CompareTo(other.High); if (highComparison != 0) return highComparison; return Low.CompareTo(other.Low); - // ReSharper restore ImpureMethodCallOnReadonlyValueField } public int CompareTo(object obj) @@ -61,7 +63,7 @@ namespace LibHac.Common return obj is Id128 other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(Id128)}"); } - public void ToBytes(Span output) + public readonly void ToBytes(Span output) { Span longs = MemoryMarshal.Cast(output); @@ -69,33 +71,17 @@ namespace LibHac.Common longs[1] = Low; } - public static bool operator ==(Id128 left, Id128 right) + public ReadOnlySpan AsBytes() { - return left.Equals(right); + return SpanHelpers.AsByteSpan(ref this); } - public static bool operator !=(Id128 left, Id128 right) - { - return !left.Equals(right); - } - public static bool operator <(Id128 left, Id128 right) - { - return left.CompareTo(right) < 0; - } + public static bool operator ==(Id128 left, Id128 right) => left.Equals(right); + public static bool operator !=(Id128 left, Id128 right) => !left.Equals(right); - public static bool operator >(Id128 left, Id128 right) - { - return left.CompareTo(right) > 0; - } - - public static bool operator <=(Id128 left, Id128 right) - { - return left.CompareTo(right) <= 0; - } - - public static bool operator >=(Id128 left, Id128 right) - { - return 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; + public static bool operator <=(Id128 left, Id128 right) => left.CompareTo(right) <= 0; + public static bool operator >=(Id128 left, Id128 right) => left.CompareTo(right) >= 0; } } diff --git a/src/LibHac/Common/Key128.cs b/src/LibHac/Common/Key128.cs new file mode 100644 index 00000000..6febda1a --- /dev/null +++ b/src/LibHac/Common/Key128.cs @@ -0,0 +1,48 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace LibHac.Common +{ + [DebuggerDisplay("{ToString()}")] + [StructLayout(LayoutKind.Sequential, Size = 0x10)] + public struct Key128 : IEquatable + { + private readonly ulong _dummy1; + private readonly ulong _dummy2; + + public Span Value => SpanHelpers.AsByteSpan(ref this); + + public Key128(ReadOnlySpan bytes) + { + ReadOnlySpan longs = MemoryMarshal.Cast(bytes); + + _dummy1 = longs[0]; + _dummy2 = longs[1]; + } + + public override string ToString() => Value.ToHexString(); + + public override bool Equals(object obj) + { + return obj is Key128 key && Equals(key); + } + + public bool Equals(Key128 other) + { + return _dummy1 == other._dummy1 && + _dummy2 == other._dummy2; + } + + public override int GetHashCode() + { + int hashCode = -1653217991; + hashCode = hashCode * -1521134295 + _dummy1.GetHashCode(); + hashCode = hashCode * -1521134295 + _dummy2.GetHashCode(); + return hashCode; + } + + public static bool operator ==(Key128 left, Key128 right) => left.Equals(right); + public static bool operator !=(Key128 left, Key128 right) => !(left == right); + } +} diff --git a/src/LibHac/Fs/FileBase.cs b/src/LibHac/Fs/FileBase.cs index 5caa7829..244c8200 100644 --- a/src/LibHac/Fs/FileBase.cs +++ b/src/LibHac/Fs/FileBase.cs @@ -121,7 +121,7 @@ namespace LibHac.Fs if (!openMode.HasFlag(OpenMode.AllowAppend)) { - return ResultFs.AllowAppendRequiredForImplicitExtension.Log(); + return ResultFs.FileExtensionWithoutOpenModeAllowAppend.Log(); } } diff --git a/src/LibHac/Fs/ResultFs.cs b/src/LibHac/Fs/ResultFs.cs index 7d547155..b9d4edfe 100644 --- a/src/LibHac/Fs/ResultFs.cs +++ b/src/LibHac/Fs/ResultFs.cs @@ -11,7 +11,9 @@ public static Result InsufficientFreeSpace => new Result(ModuleFs, 30); public static Result MountNameAlreadyExists => new Result(ModuleFs, 60); + public static Result PartitionNotFound => new Result(ModuleFs, 1001); public static Result TargetNotFound => new Result(ModuleFs, 1002); + public static Result ExternalKeyNotFound => new Result(ModuleFs, 1004); public static Result NotImplemented => new Result(ModuleFs, 3001); public static Result Result3002 => new Result(ModuleFs, 3002); @@ -80,9 +82,11 @@ public static Result InvalidSize => new Result(ModuleFs, 6062); public static Result NullArgument => new Result(ModuleFs, 6063); public static Result InvalidMountName => new Result(ModuleFs, 6065); + public static Result ExtensionSizeTooLarge => new Result(ModuleFs, 6066); + public static Result ExtensionSizeInvalid => new Result(ModuleFs, 6067); public static Result InvalidOpenModeOperation => new Result(ModuleFs, 6200); - public static Result AllowAppendRequiredForImplicitExtension => new Result(ModuleFs, 6201); + public static Result FileExtensionWithoutOpenModeAllowAppend => new Result(ModuleFs, 6201); public static Result InvalidOpenModeForRead => new Result(ModuleFs, 6202); public static Result InvalidOpenModeForWrite => new Result(ModuleFs, 6203); @@ -105,11 +109,16 @@ public static Result UnsupportedOperationInPartitionFileSetSize => new Result(ModuleFs, 6376); public static Result PermissionDenied => new Result(ModuleFs, 6400); + public static Result ExternalKeyAlreadyRegistered => new Result(ModuleFs, 6452); public static Result WriteStateUnflushed => new Result(ModuleFs, 6454); public static Result WritableFileOpen => new Result(ModuleFs, 6457); + public static Result MappingTableFull => new Result(ModuleFs, 6706); public static Result AllocationTableInsufficientFreeBlocks => new Result(ModuleFs, 6707); + public static Result OpenCountLimit => new Result(ModuleFs, 6709); + + public static Result RemapStorageMapFull => new Result(ModuleFs, 6811); public static Result Result6902 => new Result(ModuleFs, 6902); public static Result MountNameNotFound => new Result(ModuleFs, 6905); diff --git a/src/LibHac/Fs/RightsId.cs b/src/LibHac/Fs/RightsId.cs index 11f0969d..120eec89 100644 --- a/src/LibHac/Fs/RightsId.cs +++ b/src/LibHac/Fs/RightsId.cs @@ -1,9 +1,11 @@ using System; +using System.Diagnostics; using System.Runtime.InteropServices; using LibHac.Common; namespace LibHac.Fs { + [DebuggerDisplay("{DebugDisplay(),nq}")] [StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct RightsId : IEquatable, IComparable, IComparable { @@ -19,12 +21,21 @@ namespace LibHac.Fs Id = new Id128(uid); } + public override string ToString() => Id.ToString(); + + public string DebugDisplay() + { + ReadOnlySpan highBytes = AsBytes().Slice(0, 8); + ReadOnlySpan lowBytes = AsBytes().Slice(8, 8); + + return $"{highBytes.ToHexString()} {lowBytes.ToHexString()}"; + } + public bool Equals(RightsId other) => Id == other.Id; public override bool Equals(object obj) => obj is RightsId other && Equals(other); public override int GetHashCode() => Id.GetHashCode(); - // ReSharper disable once ImpureMethodCallOnReadonlyValueField public int CompareTo(RightsId other) => Id.CompareTo(other.Id); public int CompareTo(object obj) @@ -33,9 +44,13 @@ namespace LibHac.Fs return obj is RightsId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(RightsId)}"); } - // ReSharper disable once ImpureMethodCallOnReadonlyValueField public void ToBytes(Span output) => Id.ToBytes(output); + public ReadOnlySpan AsBytes() + { + return SpanHelpers.AsByteSpan(ref this); + } + public static bool operator ==(RightsId left, RightsId right) => left.Equals(right); public static bool operator !=(RightsId left, RightsId right) => !left.Equals(right); diff --git a/src/LibHac/Fs/SaveData.cs b/src/LibHac/Fs/SaveData.cs index 09d98fb2..5ae9468d 100644 --- a/src/LibHac/Fs/SaveData.cs +++ b/src/LibHac/Fs/SaveData.cs @@ -7,7 +7,7 @@ namespace LibHac.Fs { public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, ulong saveDataId) { - return MountSystemSaveData(fs, mountName, spaceId, saveDataId, UserId.EmptyId); + return MountSystemSaveData(fs, mountName, spaceId, saveDataId, UserId.Zero); } public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, @@ -72,19 +72,19 @@ namespace LibHac.Fs public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, ulong ownerId, long size, long journalSize, uint flags) { - return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.EmptyId, ownerId, size, journalSize, flags); + return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.Zero, ownerId, size, journalSize, flags); } public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, long size, long journalSize, uint flags) { - return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.EmptyId, 0, size, journalSize, flags); + return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.Zero, 0, size, journalSize, flags); } public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, ulong ownerId, long size, long journalSize, uint flags) { - return CreateSystemSaveData(fs, spaceId, saveDataId, UserId.EmptyId, ownerId, size, journalSize, flags); + return CreateSystemSaveData(fs, spaceId, saveDataId, UserId.Zero, ownerId, size, journalSize, flags); } public static Result DeleteSaveData(this FileSystemClient fs, ulong saveDataId) diff --git a/src/LibHac/Fs/UserId.cs b/src/LibHac/Fs/UserId.cs index 5d89fbcb..31db1dec 100644 --- a/src/LibHac/Fs/UserId.cs +++ b/src/LibHac/Fs/UserId.cs @@ -1,13 +1,15 @@ using System; +using System.Diagnostics; using System.Runtime.InteropServices; using LibHac.Common; namespace LibHac.Fs { + [DebuggerDisplay("{ToString(),nq}")] [StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct UserId : IEquatable, IComparable, IComparable { - public static readonly UserId EmptyId = new UserId(0, 0); + public static UserId Zero => default; public readonly Id128 Id; @@ -21,12 +23,16 @@ namespace LibHac.Fs Id = new Id128(uid); } + public override string ToString() + { + return $"0x{Id.High:x8}{Id.Low:x8}"; + } + public bool Equals(UserId other) => Id == other.Id; public override bool Equals(object obj) => obj is UserId other && Equals(other); public override int GetHashCode() => Id.GetHashCode(); - // ReSharper disable once ImpureMethodCallOnReadonlyValueField public int CompareTo(UserId other) => Id.CompareTo(other.Id); public int CompareTo(object obj) @@ -35,9 +41,13 @@ namespace LibHac.Fs return obj is UserId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(UserId)}"); } - // ReSharper disable once ImpureMethodCallOnReadonlyValueField public void ToBytes(Span output) => Id.ToBytes(output); + public ReadOnlySpan AsBytes() + { + return SpanHelpers.AsByteSpan(ref this); + } + public static bool operator ==(UserId left, UserId right) => left.Equals(right); public static bool operator !=(UserId left, UserId right) => !left.Equals(right); diff --git a/src/LibHac/FsService/ExternalKeySet.cs b/src/LibHac/FsService/ExternalKeySet.cs new file mode 100644 index 00000000..dd6172ec --- /dev/null +++ b/src/LibHac/FsService/ExternalKeySet.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using LibHac.Fs; +using LibHac.Spl; + +namespace LibHac.FsService +{ + public class ExternalKeySet + { + private readonly object _locker = new object(); + + private Dictionary ExternalKeys { get; set; } = new Dictionary(); + + public Result Add(RightsId rightsId, AccessKey key) + { + lock (_locker) + { + if (ExternalKeys.TryGetValue(rightsId, out AccessKey existingKey)) + { + if (key == existingKey) + { + return Result.Success; + } + + return ResultFs.ExternalKeyAlreadyRegistered.Log(); + } + + ExternalKeys.Add(rightsId, key); + } + + return Result.Success; + } + + public Result Get(RightsId rightsId, out AccessKey key) + { + lock (_locker) + { + if (ExternalKeys.TryGetValue(rightsId, out key)) + { + return Result.Success; + } + + return ResultFs.ExternalKeyNotFound.Log(); + } + } + + public bool Contains(RightsId rightsId) + { + lock (_locker) + { + return ExternalKeys.ContainsKey(rightsId); + } + } + + public bool Remove(RightsId rightsId) + { + lock (_locker) + { + return ExternalKeys.Remove(rightsId); + } + } + + public void Clear() + { + lock (_locker) + { + ExternalKeys.Clear(); + } + } + + public List<(RightsId rightsId, AccessKey key)> ToList() + { + lock (_locker) + { + var list = new List<(RightsId rightsId, AccessKey key)>(ExternalKeys.Count); + + foreach (KeyValuePair kvp in ExternalKeys) + { + list.Add((kvp.Key, kvp.Value)); + } + + return list; + } + } + + public void TrimExcess() => TrimExcess(0); + + public void TrimExcess(int capacity) + { + lock (_locker) + { + int newCapacity = Math.Max(capacity, ExternalKeys.Count); +#if NETCOREAPP + ExternalKeys.TrimExcess(newCapacity); +#else + var trimmedDict = new Dictionary(newCapacity); + + foreach (KeyValuePair kvp in ExternalKeys) + { + trimmedDict.Add(kvp.Key, kvp.Value); + } + + ExternalKeys = trimmedDict; +#endif + } + } + } +} diff --git a/src/LibHac/FsService/FileSystemProxy.cs b/src/LibHac/FsService/FileSystemProxy.cs index 0202cd03..f6c4a0d6 100644 --- a/src/LibHac/FsService/FileSystemProxy.cs +++ b/src/LibHac/FsService/FileSystemProxy.cs @@ -147,7 +147,7 @@ namespace LibHac.FsService private Result OpenSaveDataFileSystemImpl(out IFileSystem fileSystem, out ulong saveDataId, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute, bool openReadOnly, bool cacheExtraData) { - bool hasFixedId = attribute.SaveDataId != 0 && attribute.UserId.Id == Id128.InvalidId; + bool hasFixedId = attribute.SaveDataId != 0 && attribute.UserId == UserId.Zero; if (hasFixedId) { diff --git a/src/LibHac/FsSystem/NcaUtils/Nca.cs b/src/LibHac/FsSystem/NcaUtils/Nca.cs index f10ae281..df7af1e8 100644 --- a/src/LibHac/FsSystem/NcaUtils/Nca.cs +++ b/src/LibHac/FsSystem/NcaUtils/Nca.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.IO; using LibHac.Fs; using LibHac.FsSystem.RomFs; +using LibHac.Spl; namespace LibHac.FsSystem.NcaUtils { @@ -47,9 +48,9 @@ namespace LibHac.FsSystem.NcaUtils int keyRevision = Util.GetMasterKeyRevision(Header.KeyGeneration); byte[] titleKek = Keyset.TitleKeks[keyRevision]; - if (!Keyset.TitleKeys.TryGetValue(Header.RightsId.ToArray(), out byte[] encryptedKey)) + if (Keyset.ExternalKeySet.Get(new RightsId(Header.RightsId), out AccessKey accessKey).IsFailure()) { - throw new MissingKeyException("Missing NCA title key.", Header.RightsId.ToHexString(), KeyType.Title); + throw new MissingKeyException("Missing NCA title key.", Header.RightsId.ToString(), KeyType.Title); } if (titleKek.IsEmpty()) @@ -58,6 +59,7 @@ namespace LibHac.FsSystem.NcaUtils throw new MissingKeyException("Unable to decrypt title key.", keyName, KeyType.Common); } + byte[] encryptedKey = accessKey.Value.ToArray(); var decryptedKey = new byte[Crypto.Aes128Size]; Crypto.DecryptEcb(titleKek, encryptedKey, decryptedKey, Crypto.Aes128Size); @@ -89,7 +91,7 @@ namespace LibHac.FsSystem.NcaUtils if (Header.HasRightsId) { - return Keyset.TitleKeys.ContainsKey(Header.RightsId.ToArray()) && + return Keyset.ExternalKeySet.Contains(new RightsId(Header.RightsId)) && !Keyset.TitleKeks[keyRevision].IsEmpty(); } diff --git a/src/LibHac/Keyset.cs b/src/LibHac/Keyset.cs index e71deaa8..7b51d616 100644 --- a/src/LibHac/Keyset.cs +++ b/src/LibHac/Keyset.cs @@ -4,7 +4,10 @@ using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; +using LibHac.Fs; +using LibHac.FsService; using LibHac.FsSystem; +using LibHac.Spl; namespace LibHac { @@ -129,7 +132,7 @@ namespace LibHac 0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F }; - public Dictionary TitleKeys { get; } = new Dictionary(new ByteArray128BitComparer()); + public ExternalKeySet ExternalKeySet { get; } = new ExternalKeySet(); public void SetSdSeed(byte[] sdseed) { @@ -402,6 +405,7 @@ namespace LibHac if (filename != null) ReadMainKeys(keyset, filename, AllKeyDict, logger); if (consoleKeysFilename != null) ReadMainKeys(keyset, consoleKeysFilename, AllKeyDict, logger); if (titleKeysFilename != null) ReadTitleKeys(keyset, titleKeysFilename, logger); + keyset.ExternalKeySet.TrimExcess(); keyset.DeriveKeys(logger); } @@ -507,7 +511,7 @@ namespace LibHac continue; } - keyset.TitleKeys[rightsId] = titleKey; + keyset.ExternalKeySet.Add(new RightsId(rightsId), new AccessKey(titleKey)).ThrowIfFailure(); } } } @@ -557,9 +561,9 @@ namespace LibHac { var sb = new StringBuilder(); - foreach (KeyValuePair kv in keyset.TitleKeys.OrderBy(x => x.Key.ToHexString())) + foreach ((RightsId rightsId, AccessKey key) kv in keyset.ExternalKeySet.ToList().OrderBy(x => x.rightsId.ToString())) { - string line = $"{kv.Key.ToHexString()} = {kv.Value.ToHexString()}"; + string line = $"{kv.rightsId} = {kv.key}"; sb.AppendLine(line); } diff --git a/src/LibHac/LibHac.csproj b/src/LibHac/LibHac.csproj index c1033804..c0e7f166 100644 --- a/src/LibHac/LibHac.csproj +++ b/src/LibHac/LibHac.csproj @@ -3,7 +3,7 @@ Library netcoreapp2.1;netstandard2.0;net46 - 7.3 + 8.0 diff --git a/src/LibHac/Ncm/ContentId.cs b/src/LibHac/Ncm/ContentId.cs index 75c1d120..7df8ac93 100644 --- a/src/LibHac/Ncm/ContentId.cs +++ b/src/LibHac/Ncm/ContentId.cs @@ -1,9 +1,11 @@ using System; +using System.Diagnostics; using System.Runtime.InteropServices; using LibHac.Common; namespace LibHac.Ncm { + [DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct ContentId : IEquatable, IComparable, IComparable { @@ -19,12 +21,13 @@ namespace LibHac.Ncm Id = new Id128(uid); } + public override string ToString() => Id.ToString(); + public bool Equals(ContentId other) => Id == other.Id; public override bool Equals(object obj) => obj is ContentId other && Equals(other); public override int GetHashCode() => Id.GetHashCode(); - // ReSharper disable once ImpureMethodCallOnReadonlyValueField public int CompareTo(ContentId other) => Id.CompareTo(other.Id); public int CompareTo(object obj) @@ -33,9 +36,13 @@ namespace LibHac.Ncm return obj is ContentId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(ContentId)}"); } - // ReSharper disable once ImpureMethodCallOnReadonlyValueField public void ToBytes(Span output) => Id.ToBytes(output); + public ReadOnlySpan AsBytes() + { + return SpanHelpers.AsByteSpan(ref this); + } + public static bool operator ==(ContentId left, ContentId right) => left.Equals(right); public static bool operator !=(ContentId left, ContentId right) => !left.Equals(right); diff --git a/src/LibHac/Ncm/PlaceHolderId.cs b/src/LibHac/Ncm/PlaceHolderId.cs index 0e31a14d..27ed1631 100644 --- a/src/LibHac/Ncm/PlaceHolderId.cs +++ b/src/LibHac/Ncm/PlaceHolderId.cs @@ -1,9 +1,11 @@ using System; +using System.Diagnostics; using System.Runtime.InteropServices; using LibHac.Common; namespace LibHac.Ncm { + [DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct PlaceHolderId : IEquatable, IComparable, IComparable { @@ -19,12 +21,13 @@ namespace LibHac.Ncm Id = new Id128(uid); } + public override string ToString() => Id.ToString(); + public bool Equals(PlaceHolderId other) => Id == other.Id; public override bool Equals(object obj) => obj is PlaceHolderId other && Equals(other); public override int GetHashCode() => Id.GetHashCode(); - // ReSharper disable once ImpureMethodCallOnReadonlyValueField public int CompareTo(PlaceHolderId other) => Id.CompareTo(other.Id); public int CompareTo(object obj) @@ -33,9 +36,13 @@ namespace LibHac.Ncm return obj is PlaceHolderId other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(PlaceHolderId)}"); } - // ReSharper disable once ImpureMethodCallOnReadonlyValueField public void ToBytes(Span output) => Id.ToBytes(output); + public ReadOnlySpan AsBytes() + { + return SpanHelpers.AsByteSpan(ref this); + } + public static bool operator ==(PlaceHolderId left, PlaceHolderId right) => left.Equals(right); public static bool operator !=(PlaceHolderId left, PlaceHolderId right) => !left.Equals(right); diff --git a/src/LibHac/Spl/AccessKey.cs b/src/LibHac/Spl/AccessKey.cs index 987a55b8..2a8e0be2 100644 --- a/src/LibHac/Spl/AccessKey.cs +++ b/src/LibHac/Spl/AccessKey.cs @@ -1,6 +1,5 @@ using System; using System.Diagnostics; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; @@ -8,13 +7,23 @@ namespace LibHac.Spl { [DebuggerDisplay("{ToString()}")] [StructLayout(LayoutKind.Sequential, Size = 0x10)] - public struct AccessKey + public struct AccessKey : IEquatable { - private long _dummy1; - private long _dummy2; + private readonly Key128 Key; - public Span Key => SpanHelpers.CreateSpan(ref Unsafe.As(ref _dummy1), 0x10); + public ReadOnlySpan Value => SpanHelpers.AsByteSpan(ref this); - public override string ToString() => Key.ToHexString(); + public AccessKey(ReadOnlySpan bytes) + { + Key = new Key128(bytes); + } + + public override string ToString() => Key.ToString(); + + public override bool Equals(object obj) => obj is AccessKey key && Equals(key); + public bool Equals(AccessKey other) => Key.Equals(other.Key); + public override int GetHashCode() => Key.GetHashCode(); + public static bool operator ==(AccessKey left, AccessKey right) => left.Equals(right); + public static bool operator !=(AccessKey left, AccessKey right) => !(left == right); } } diff --git a/src/LibHac/Util.cs b/src/LibHac/Util.cs index d9f1a5e0..ea20c212 100644 --- a/src/LibHac/Util.cs +++ b/src/LibHac/Util.cs @@ -354,7 +354,9 @@ namespace LibHac public static string ToHexString(this byte[] bytes) => ToHexString(bytes.AsSpan()); - public static string ToHexString(this Span bytes) + public static string ToHexString(this Span bytes) => ToHexString((ReadOnlySpan)bytes); + + public static string ToHexString(this ReadOnlySpan bytes) { uint[] lookup32 = Lookup32; var result = new char[bytes.Length * 2];