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];