mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Use file-scoped namespaces
This commit is contained in:
parent
6fe89a2966
commit
34dda02c38
@ -1 +1 @@
|
|||||||
5.0.402
|
6.0.100
|
1279
build/Build.cs
1279
build/Build.cs
File diff suppressed because it is too large
Load Diff
@ -5,105 +5,104 @@ using System.Reflection;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Nuke.Common;
|
using Nuke.Common;
|
||||||
|
|
||||||
namespace LibHacBuild.CodeGen
|
namespace LibHacBuild.CodeGen;
|
||||||
|
|
||||||
|
public static class Common
|
||||||
{
|
{
|
||||||
public static class Common
|
public static string GetHeader()
|
||||||
{
|
{
|
||||||
public static string GetHeader()
|
string nl = Environment.NewLine;
|
||||||
|
return
|
||||||
|
"//-----------------------------------------------------------------------------" + nl +
|
||||||
|
"// This file was automatically generated." + nl +
|
||||||
|
"// Changes to this file will be lost when the file is regenerated." + nl +
|
||||||
|
"//" + nl +
|
||||||
|
"// To change this file, modify /build/CodeGen/results.csv at the root of this" + nl +
|
||||||
|
"// repo and run the build script." + nl +
|
||||||
|
"//" + nl +
|
||||||
|
"// The script can be run with the \"codegen\" option to run only the" + nl +
|
||||||
|
"// code generation portion of the build." + nl +
|
||||||
|
"//-----------------------------------------------------------------------------";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the file only if it has changed
|
||||||
|
// Preserve the UTF-8 BOM usage if the file already exists
|
||||||
|
public static void WriteOutput(string relativePath, string text)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(relativePath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
string rootPath = FindProjectDirectory();
|
||||||
|
string fullPath = Path.Combine(rootPath, relativePath);
|
||||||
|
string directoryName = Path.GetDirectoryName(fullPath);
|
||||||
|
|
||||||
|
if (directoryName == null)
|
||||||
|
throw new InvalidDataException($"Invalid output path {relativePath}");
|
||||||
|
|
||||||
|
if (!Directory.Exists(directoryName))
|
||||||
{
|
{
|
||||||
string nl = Environment.NewLine;
|
Directory.CreateDirectory(directoryName);
|
||||||
return
|
|
||||||
"//-----------------------------------------------------------------------------" + nl +
|
|
||||||
"// This file was automatically generated." + nl +
|
|
||||||
"// Changes to this file will be lost when the file is regenerated." + nl +
|
|
||||||
"//" + nl +
|
|
||||||
"// To change this file, modify /build/CodeGen/results.csv at the root of this" + nl +
|
|
||||||
"// repo and run the build script." + nl +
|
|
||||||
"//" + nl +
|
|
||||||
"// The script can be run with the \"codegen\" option to run only the" + nl +
|
|
||||||
"// code generation portion of the build." + nl +
|
|
||||||
"//-----------------------------------------------------------------------------";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the file only if it has changed
|
// Default is true because Visual Studio saves .cs files with the BOM by default
|
||||||
// Preserve the UTF-8 BOM usage if the file already exists
|
bool hasBom = true;
|
||||||
public static void WriteOutput(string relativePath, string text)
|
byte[] bom = Encoding.UTF8.GetPreamble();
|
||||||
|
byte[] oldFile = null;
|
||||||
|
|
||||||
|
if (File.Exists(fullPath))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(relativePath))
|
oldFile = File.ReadAllBytes(fullPath);
|
||||||
return;
|
|
||||||
|
|
||||||
string rootPath = FindProjectDirectory();
|
if (oldFile.Length >= 3)
|
||||||
string fullPath = Path.Combine(rootPath, relativePath);
|
hasBom = oldFile.AsSpan(0, 3).SequenceEqual(bom);
|
||||||
string directoryName = Path.GetDirectoryName(fullPath);
|
|
||||||
|
|
||||||
if (directoryName == null)
|
|
||||||
throw new InvalidDataException($"Invalid output path {relativePath}");
|
|
||||||
|
|
||||||
if (!Directory.Exists(directoryName))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(directoryName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default is true because Visual Studio saves .cs files with the BOM by default
|
|
||||||
bool hasBom = true;
|
|
||||||
byte[] bom = Encoding.UTF8.GetPreamble();
|
|
||||||
byte[] oldFile = null;
|
|
||||||
|
|
||||||
if (File.Exists(fullPath))
|
|
||||||
{
|
|
||||||
oldFile = File.ReadAllBytes(fullPath);
|
|
||||||
|
|
||||||
if (oldFile.Length >= 3)
|
|
||||||
hasBom = oldFile.AsSpan(0, 3).SequenceEqual(bom);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make line endings the same on Windows and Unix
|
|
||||||
if (Environment.NewLine == "\n")
|
|
||||||
{
|
|
||||||
text = text.Replace("\n", "\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] newFile = (hasBom ? bom : new byte[0]).Concat(Encoding.UTF8.GetBytes(text)).ToArray();
|
|
||||||
|
|
||||||
if (oldFile?.SequenceEqual(newFile) == true)
|
|
||||||
{
|
|
||||||
Logger.Normal($"{relativePath} is already up-to-date");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Normal($"Generated file {relativePath}");
|
|
||||||
File.WriteAllBytes(fullPath, newFile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Stream GetResource(string name)
|
// Make line endings the same on Windows and Unix
|
||||||
|
if (Environment.NewLine == "\n")
|
||||||
{
|
{
|
||||||
var assembly = Assembly.GetExecutingAssembly();
|
text = text.Replace("\n", "\r\n");
|
||||||
string path = $"LibHacBuild.CodeGen.{name}";
|
|
||||||
|
|
||||||
Stream stream = assembly.GetManifestResourceStream(path);
|
|
||||||
if (stream == null) throw new FileNotFoundException($"Resource {path} was not found.");
|
|
||||||
|
|
||||||
return stream;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string FindProjectDirectory()
|
byte[] newFile = (hasBom ? bom : new byte[0]).Concat(Encoding.UTF8.GetBytes(text)).ToArray();
|
||||||
|
|
||||||
|
if (oldFile?.SequenceEqual(newFile) == true)
|
||||||
{
|
{
|
||||||
string currentDir = Environment.CurrentDirectory;
|
Logger.Normal($"{relativePath} is already up-to-date");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while (currentDir != null)
|
Logger.Normal($"Generated file {relativePath}");
|
||||||
|
File.WriteAllBytes(fullPath, newFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream GetResource(string name)
|
||||||
|
{
|
||||||
|
var assembly = Assembly.GetExecutingAssembly();
|
||||||
|
string path = $"LibHacBuild.CodeGen.{name}";
|
||||||
|
|
||||||
|
Stream stream = assembly.GetManifestResourceStream(path);
|
||||||
|
if (stream == null) throw new FileNotFoundException($"Resource {path} was not found.");
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FindProjectDirectory()
|
||||||
|
{
|
||||||
|
string currentDir = Environment.CurrentDirectory;
|
||||||
|
|
||||||
|
while (currentDir != null)
|
||||||
|
{
|
||||||
|
if (File.Exists(Path.Combine(currentDir, "LibHac.sln")))
|
||||||
{
|
{
|
||||||
if (File.Exists(Path.Combine(currentDir, "LibHac.sln")))
|
break;
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentDir = Path.GetDirectoryName(currentDir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentDir == null)
|
currentDir = Path.GetDirectoryName(currentDir);
|
||||||
throw new DirectoryNotFoundException("Unable to find project directory.");
|
|
||||||
|
|
||||||
return Path.Combine(currentDir, "src");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentDir == null)
|
||||||
|
throw new DirectoryNotFoundException("Unable to find project directory.");
|
||||||
|
|
||||||
|
return Path.Combine(currentDir, "src");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,89 +1,88 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace LibHacBuild.CodeGen
|
namespace LibHacBuild.CodeGen;
|
||||||
|
|
||||||
|
public class IndentingStringBuilder
|
||||||
{
|
{
|
||||||
public class IndentingStringBuilder
|
public int LevelSize { get; set; } = 4;
|
||||||
|
public int Level { get; private set; }
|
||||||
|
|
||||||
|
private StringBuilder _sb = new StringBuilder();
|
||||||
|
private string _indentation = string.Empty;
|
||||||
|
private bool _hasIndentedCurrentLine;
|
||||||
|
private bool _lastLineWasEmpty;
|
||||||
|
|
||||||
|
public IndentingStringBuilder() { }
|
||||||
|
public IndentingStringBuilder(int levelSize) => LevelSize = levelSize;
|
||||||
|
|
||||||
|
public void SetLevel(int level)
|
||||||
{
|
{
|
||||||
public int LevelSize { get; set; } = 4;
|
Level = Math.Max(level, 0);
|
||||||
public int Level { get; private set; }
|
_indentation = new string(' ', Level * LevelSize);
|
||||||
|
}
|
||||||
|
|
||||||
private StringBuilder _sb = new StringBuilder();
|
public void IncreaseLevel() => SetLevel(Level + 1);
|
||||||
private string _indentation = string.Empty;
|
public void DecreaseLevel() => SetLevel(Level - 1);
|
||||||
private bool _hasIndentedCurrentLine;
|
|
||||||
private bool _lastLineWasEmpty;
|
|
||||||
|
|
||||||
public IndentingStringBuilder() { }
|
public IndentingStringBuilder AppendLine()
|
||||||
public IndentingStringBuilder(int levelSize) => LevelSize = levelSize;
|
{
|
||||||
|
_sb.AppendLine();
|
||||||
|
_hasIndentedCurrentLine = false;
|
||||||
|
_lastLineWasEmpty = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetLevel(int level)
|
public IndentingStringBuilder AppendSpacerLine()
|
||||||
{
|
{
|
||||||
Level = Math.Max(level, 0);
|
if (!_lastLineWasEmpty)
|
||||||
_indentation = new string(' ', Level * LevelSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void IncreaseLevel() => SetLevel(Level + 1);
|
|
||||||
public void DecreaseLevel() => SetLevel(Level - 1);
|
|
||||||
|
|
||||||
public IndentingStringBuilder AppendLine()
|
|
||||||
{
|
{
|
||||||
_sb.AppendLine();
|
_sb.AppendLine();
|
||||||
_hasIndentedCurrentLine = false;
|
_hasIndentedCurrentLine = false;
|
||||||
_lastLineWasEmpty = true;
|
_lastLineWasEmpty = true;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IndentingStringBuilder AppendSpacerLine()
|
return this;
|
||||||
{
|
|
||||||
if (!_lastLineWasEmpty)
|
|
||||||
{
|
|
||||||
_sb.AppendLine();
|
|
||||||
_hasIndentedCurrentLine = false;
|
|
||||||
_lastLineWasEmpty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IndentingStringBuilder AppendLine(string value)
|
|
||||||
{
|
|
||||||
IndentIfNeeded();
|
|
||||||
_sb.AppendLine(value);
|
|
||||||
_hasIndentedCurrentLine = false;
|
|
||||||
_lastLineWasEmpty = string.IsNullOrWhiteSpace(value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IndentingStringBuilder Append(string value)
|
|
||||||
{
|
|
||||||
IndentIfNeeded();
|
|
||||||
_sb.Append(value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IndentingStringBuilder AppendLineAndIncrease(string value)
|
|
||||||
{
|
|
||||||
AppendLine(value);
|
|
||||||
IncreaseLevel();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IndentingStringBuilder DecreaseAndAppendLine(string value)
|
|
||||||
{
|
|
||||||
DecreaseLevel();
|
|
||||||
AppendLine(value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void IndentIfNeeded()
|
|
||||||
{
|
|
||||||
if (!_hasIndentedCurrentLine)
|
|
||||||
{
|
|
||||||
_sb.Append(_indentation);
|
|
||||||
_hasIndentedCurrentLine = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() => _sb.ToString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IndentingStringBuilder AppendLine(string value)
|
||||||
|
{
|
||||||
|
IndentIfNeeded();
|
||||||
|
_sb.AppendLine(value);
|
||||||
|
_hasIndentedCurrentLine = false;
|
||||||
|
_lastLineWasEmpty = string.IsNullOrWhiteSpace(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IndentingStringBuilder Append(string value)
|
||||||
|
{
|
||||||
|
IndentIfNeeded();
|
||||||
|
_sb.Append(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IndentingStringBuilder AppendLineAndIncrease(string value)
|
||||||
|
{
|
||||||
|
AppendLine(value);
|
||||||
|
IncreaseLevel();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IndentingStringBuilder DecreaseAndAppendLine(string value)
|
||||||
|
{
|
||||||
|
DecreaseLevel();
|
||||||
|
AppendLine(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void IndentIfNeeded()
|
||||||
|
{
|
||||||
|
if (!_hasIndentedCurrentLine)
|
||||||
|
{
|
||||||
|
_sb.Append(_indentation);
|
||||||
|
_hasIndentedCurrentLine = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => _sb.ToString();
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -8,386 +8,385 @@ using LibHac.Common.Keys;
|
|||||||
using LibHac.Crypto;
|
using LibHac.Crypto;
|
||||||
using static LibHacBuild.CodeGen.Common;
|
using static LibHacBuild.CodeGen.Common;
|
||||||
|
|
||||||
namespace LibHacBuild.CodeGen.Stage2
|
namespace LibHacBuild.CodeGen.Stage2;
|
||||||
|
|
||||||
|
public static class KeysCodeGen
|
||||||
{
|
{
|
||||||
public static class KeysCodeGen
|
private static string InputMainKeyFileName = "IncludedKeys.txt";
|
||||||
|
private static string GeneratedFilePath = "LibHac/Common/Keys/DefaultKeySet.Generated.cs";
|
||||||
|
|
||||||
|
public static void Run()
|
||||||
{
|
{
|
||||||
private static string InputMainKeyFileName = "IncludedKeys.txt";
|
KeySet keySet = CreateKeySet();
|
||||||
private static string GeneratedFilePath = "LibHac/Common/Keys/DefaultKeySet.Generated.cs";
|
|
||||||
|
|
||||||
public static void Run()
|
WriteOutput(GeneratedFilePath, BuildDefaultKeySetFile(keySet));
|
||||||
{
|
|
||||||
KeySet keySet = CreateKeySet();
|
|
||||||
|
|
||||||
WriteOutput(GeneratedFilePath, BuildDefaultKeySetFile(keySet));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string BuildDefaultKeySetFile(KeySet keySet)
|
|
||||||
{
|
|
||||||
var sb = new IndentingStringBuilder();
|
|
||||||
|
|
||||||
sb.AppendLine(GetHeader());
|
|
||||||
sb.AppendLine();
|
|
||||||
|
|
||||||
sb.AppendLine("using System;");
|
|
||||||
sb.AppendLine();
|
|
||||||
|
|
||||||
sb.AppendLine("namespace LibHac.Common.Keys");
|
|
||||||
sb.AppendLineAndIncrease("{");
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
sb.DecreaseAndAppendLine("}");
|
|
||||||
sb.DecreaseAndAppendLine("}");
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void BuildArray(IndentingStringBuilder sb, string name, ReadOnlySpan<byte> data)
|
|
||||||
{
|
|
||||||
sb.AppendSpacerLine();
|
|
||||||
sb.Append($"private static ReadOnlySpan<byte> {name} => new byte[]");
|
|
||||||
|
|
||||||
if (data.IsZeros())
|
|
||||||
{
|
|
||||||
sb.AppendLine(" { };");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.AppendLine();
|
|
||||||
sb.AppendLineAndIncrease("{");
|
|
||||||
|
|
||||||
for (int i = 0; i < data.Length; i++)
|
|
||||||
{
|
|
||||||
if (i % 16 != 0) sb.Append(" ");
|
|
||||||
sb.Append($"0x{data[i]:x2}");
|
|
||||||
|
|
||||||
if (i != data.Length - 1)
|
|
||||||
{
|
|
||||||
sb.Append(",");
|
|
||||||
if (i % 16 == 15) sb.AppendLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.AppendLine();
|
|
||||||
sb.DecreaseAndAppendLine("};");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static KeySet CreateKeySet()
|
|
||||||
{
|
|
||||||
var keySet = new KeySet();
|
|
||||||
|
|
||||||
// Populate the key set with all the keys in IncludedKeys.txt
|
|
||||||
using (Stream keyFile = GetResource(InputMainKeyFileName))
|
|
||||||
{
|
|
||||||
List<KeyInfo> list = KeySet.CreateKeyInfoList();
|
|
||||||
ExternalKeyReader.ReadMainKeys(keySet, keyFile, list);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recover all the RSA key parameters and write the key to the key set
|
|
||||||
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);
|
|
||||||
|
|
||||||
// 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.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.Package2SigningKey.PublicExponent.Data);
|
|
||||||
Package2FixedKeyModulusProd.CopyTo(keySet.Package2SigningKey.Modulus.Data);
|
|
||||||
|
|
||||||
// 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.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.Package2SigningKey.PublicExponent.Data);
|
|
||||||
Package2FixedKeyModulusDev.CopyTo(keySet.Package2SigningKey.Modulus.Data);
|
|
||||||
|
|
||||||
return keySet;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> StandardPublicExponent => new byte[]
|
|
||||||
{
|
|
||||||
0x01, 0x00, 0x01
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> BetaNca0Modulus => new byte[]
|
|
||||||
{
|
|
||||||
0xAD, 0x58, 0xEE, 0x97, 0xF9, 0x47, 0x90, 0x7D, 0xF9, 0x29, 0x5F, 0x1F, 0x39, 0x68, 0xEE, 0x49,
|
|
||||||
0x4C, 0x1E, 0x8D, 0x84, 0x91, 0x31, 0x5D, 0xE5, 0x96, 0x27, 0xB2, 0xB3, 0x59, 0x7B, 0xDE, 0xFD,
|
|
||||||
0xB7, 0xEB, 0x40, 0xA1, 0xE7, 0xEB, 0xDC, 0x60, 0xD0, 0x3D, 0xC5, 0x50, 0x92, 0xAD, 0x3D, 0xC4,
|
|
||||||
0x8C, 0x17, 0xD2, 0x37, 0x66, 0xE3, 0xF7, 0x14, 0x34, 0x38, 0x6B, 0xA7, 0x2B, 0x21, 0x10, 0x9B,
|
|
||||||
0x73, 0x49, 0x15, 0xD9, 0x2A, 0x90, 0x86, 0x76, 0x81, 0x6A, 0x10, 0xBD, 0x74, 0xC4, 0x20, 0x55,
|
|
||||||
0x25, 0xA8, 0x02, 0xC5, 0xA0, 0x34, 0x36, 0x7B, 0x66, 0x47, 0x2C, 0x7E, 0x47, 0x82, 0xA5, 0xD4,
|
|
||||||
0xA3, 0x42, 0x45, 0xE8, 0xFD, 0x65, 0x72, 0x48, 0xA1, 0xB0, 0x44, 0x10, 0xEF, 0xAC, 0x1D, 0x0F,
|
|
||||||
0xB5, 0x12, 0x19, 0xA8, 0x41, 0x0B, 0x76, 0x3B, 0xBC, 0xF1, 0x4A, 0x10, 0x46, 0x22, 0xB8, 0xF1,
|
|
||||||
0xBC, 0x21, 0x81, 0x69, 0x9B, 0x63, 0x6F, 0xD7, 0xB9, 0x60, 0x2A, 0x9A, 0xE5, 0x2C, 0x47, 0x72,
|
|
||||||
0x59, 0x65, 0xA2, 0x21, 0x60, 0xC4, 0xFC, 0xB0, 0xD7, 0x6F, 0x42, 0xC9, 0x0C, 0xF5, 0x76, 0x7D,
|
|
||||||
0xF2, 0x5C, 0xE0, 0x80, 0x0F, 0xEE, 0x45, 0x7E, 0x4E, 0x3A, 0x8D, 0x9C, 0x5B, 0x5B, 0xD9, 0xD1,
|
|
||||||
0x43, 0x94, 0x2C, 0xC7, 0x2E, 0xB9, 0x4A, 0xE5, 0x3E, 0x15, 0xDD, 0x43, 0x00, 0xF7, 0x78, 0xE7,
|
|
||||||
0x7C, 0x39, 0xB0, 0x4D, 0xC5, 0xD1, 0x1C, 0xF2, 0xB4, 0x7A, 0x2A, 0xEA, 0x0A, 0x8E, 0xB9, 0x13,
|
|
||||||
0xB4, 0x4F, 0xD7, 0x5B, 0x4D, 0x7B, 0x43, 0xB0, 0x3A, 0x9A, 0x60, 0x22, 0x47, 0x91, 0x78, 0xC7,
|
|
||||||
0x10, 0x64, 0xE0, 0x2C, 0x69, 0xD1, 0x66, 0x3C, 0x42, 0x2E, 0xEF, 0x19, 0x21, 0x89, 0x8E, 0xE1,
|
|
||||||
0xB0, 0xB4, 0xD0, 0x17, 0xA1, 0x0F, 0x73, 0x98, 0x5A, 0xF6, 0xEE, 0xC0, 0x2F, 0x9E, 0xCE, 0xC5
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> BetaNca0Exponent => new byte[]
|
|
||||||
{
|
|
||||||
0x3C, 0x66, 0x37, 0x44, 0x26, 0xAC, 0x63, 0xD1, 0x30, 0xE6, 0xD4, 0x68, 0xF9, 0xC4, 0xF0, 0xFA,
|
|
||||||
0x03, 0x16, 0xC6, 0x32, 0x81, 0xB0, 0x94, 0xC9, 0xF1, 0x26, 0xC5, 0xE2, 0x2D, 0xF4, 0xB6, 0x3E,
|
|
||||||
0xEB, 0x3D, 0x82, 0x18, 0xA7, 0xC9, 0x8B, 0xD1, 0x03, 0xDD, 0xF2, 0x09, 0x60, 0x02, 0x12, 0xFA,
|
|
||||||
0x8F, 0xE1, 0xA0, 0xF2, 0x82, 0xDC, 0x3D, 0x74, 0x01, 0xBA, 0x02, 0xF0, 0x8D, 0x5B, 0x89, 0x00,
|
|
||||||
0xD1, 0x0B, 0x8F, 0x1C, 0x4A, 0xF3, 0x6E, 0x96, 0x8E, 0x03, 0x19, 0xF0, 0x19, 0x66, 0x58, 0xE9,
|
|
||||||
0xB2, 0x24, 0x37, 0x4B, 0x0A, 0xC6, 0x06, 0x91, 0xBA, 0x92, 0x64, 0x13, 0x5F, 0xF1, 0x4A, 0xBC,
|
|
||||||
0xAB, 0x61, 0xE5, 0x20, 0x08, 0x62, 0xB7, 0x8E, 0x4D, 0x20, 0x30, 0xA7, 0x42, 0x0B, 0x53, 0x58,
|
|
||||||
0xEC, 0xBB, 0x70, 0xCB, 0x2A, 0x56, 0xC7, 0x0C, 0x8B, 0x89, 0xFB, 0x47, 0x6E, 0x58, 0x9C, 0xDD,
|
|
||||||
0xB2, 0xE5, 0x4F, 0x49, 0x52, 0x0B, 0xD9, 0x96, 0x30, 0x8D, 0xDE, 0xC9, 0x0F, 0x6A, 0x82, 0xC7,
|
|
||||||
0xE8, 0x20, 0xB6, 0xB3, 0x95, 0xDD, 0xEB, 0xDF, 0xF7, 0x25, 0x23, 0x6B, 0xF8, 0x5B, 0xD4, 0x81,
|
|
||||||
0x7A, 0xBC, 0x94, 0x13, 0x30, 0x59, 0x28, 0xC8, 0xC9, 0x3A, 0x5D, 0xCC, 0x8D, 0xFD, 0x1A, 0xE1,
|
|
||||||
0xCB, 0xA4, 0x1D, 0xD4, 0x45, 0xF1, 0xBF, 0x87, 0x6C, 0x0E, 0xB1, 0x44, 0xC7, 0x88, 0x62, 0x2B,
|
|
||||||
0x43, 0xAD, 0x75, 0xE6, 0x69, 0xFF, 0xD3, 0x39, 0xF5, 0x7F, 0x2A, 0xA2, 0x5F, 0x7A, 0x5E, 0xE6,
|
|
||||||
0xEF, 0xCB, 0x2F, 0x2C, 0x90, 0xE6, 0x4B, 0x2D, 0x94, 0x62, 0xE8, 0xEC, 0x54, 0x7B, 0x94, 0xB7,
|
|
||||||
0xFB, 0x72, 0x05, 0xFB, 0xB3, 0x23, 0xCA, 0xF8, 0xD4, 0x5C, 0xF6, 0xAC, 0x7D, 0xEC, 0x47, 0xC6,
|
|
||||||
0xD3, 0x22, 0x5D, 0x7C, 0x15, 0xDD, 0x48, 0xE9, 0xBF, 0xA8, 0x99, 0x33, 0x02, 0x79, 0xD3, 0x65
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> NcaHdrFixedKeyModulus0Prod => new byte[]
|
|
||||||
{
|
|
||||||
0xBF, 0xBE, 0x40, 0x6C, 0xF4, 0xA7, 0x80, 0xE9, 0xF0, 0x7D, 0x0C, 0x99, 0x61, 0x1D, 0x77, 0x2F,
|
|
||||||
0x96, 0xBC, 0x4B, 0x9E, 0x58, 0x38, 0x1B, 0x03, 0xAB, 0xB1, 0x75, 0x49, 0x9F, 0x2B, 0x4D, 0x58,
|
|
||||||
0x34, 0xB0, 0x05, 0xA3, 0x75, 0x22, 0xBE, 0x1A, 0x3F, 0x03, 0x73, 0xAC, 0x70, 0x68, 0xD1, 0x16,
|
|
||||||
0xB9, 0x04, 0x46, 0x5E, 0xB7, 0x07, 0x91, 0x2F, 0x07, 0x8B, 0x26, 0xDE, 0xF6, 0x00, 0x07, 0xB2,
|
|
||||||
0xB4, 0x51, 0xF8, 0x0D, 0x0A, 0x5E, 0x58, 0xAD, 0xEB, 0xBC, 0x9A, 0xD6, 0x49, 0xB9, 0x64, 0xEF,
|
|
||||||
0xA7, 0x82, 0xB5, 0xCF, 0x6D, 0x70, 0x13, 0xB0, 0x0F, 0x85, 0xF6, 0xA9, 0x08, 0xAA, 0x4D, 0x67,
|
|
||||||
0x66, 0x87, 0xFA, 0x89, 0xFF, 0x75, 0x90, 0x18, 0x1E, 0x6B, 0x3D, 0xE9, 0x8A, 0x68, 0xC9, 0x26,
|
|
||||||
0x04, 0xD9, 0x80, 0xCE, 0x3F, 0x5E, 0x92, 0xCE, 0x01, 0xFF, 0x06, 0x3B, 0xF2, 0xC1, 0xA9, 0x0C,
|
|
||||||
0xCE, 0x02, 0x6F, 0x16, 0xBC, 0x92, 0x42, 0x0A, 0x41, 0x64, 0xCD, 0x52, 0xB6, 0x34, 0x4D, 0xAE,
|
|
||||||
0xC0, 0x2E, 0xDE, 0xA4, 0xDF, 0x27, 0x68, 0x3C, 0xC1, 0xA0, 0x60, 0xAD, 0x43, 0xF3, 0xFC, 0x86,
|
|
||||||
0xC1, 0x3E, 0x6C, 0x46, 0xF7, 0x7C, 0x29, 0x9F, 0xFA, 0xFD, 0xF0, 0xE3, 0xCE, 0x64, 0xE7, 0x35,
|
|
||||||
0xF2, 0xF6, 0x56, 0x56, 0x6F, 0x6D, 0xF1, 0xE2, 0x42, 0xB0, 0x83, 0x40, 0xA5, 0xC3, 0x20, 0x2B,
|
|
||||||
0xCC, 0x9A, 0xAE, 0xCA, 0xED, 0x4D, 0x70, 0x30, 0xA8, 0x70, 0x1C, 0x70, 0xFD, 0x13, 0x63, 0x29,
|
|
||||||
0x02, 0x79, 0xEA, 0xD2, 0xA7, 0xAF, 0x35, 0x28, 0x32, 0x1C, 0x7B, 0xE6, 0x2F, 0x1A, 0xAA, 0x40,
|
|
||||||
0x7E, 0x32, 0x8C, 0x27, 0x42, 0xFE, 0x82, 0x78, 0xEC, 0x0D, 0xEB, 0xE6, 0x83, 0x4B, 0x6D, 0x81,
|
|
||||||
0x04, 0x40, 0x1A, 0x9E, 0x9A, 0x67, 0xF6, 0x72, 0x29, 0xFA, 0x04, 0xF0, 0x9D, 0xE4, 0xF4, 0x03
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> NcaHdrFixedKeyModulus1Prod => new byte[]
|
|
||||||
{
|
|
||||||
0xAD, 0xE3, 0xE1, 0xFA, 0x04, 0x35, 0xE5, 0xB6, 0xDD, 0x49, 0xEA, 0x89, 0x29, 0xB1, 0xFF, 0xB6,
|
|
||||||
0x43, 0xDF, 0xCA, 0x96, 0xA0, 0x4A, 0x13, 0xDF, 0x43, 0xD9, 0x94, 0x97, 0x96, 0x43, 0x65, 0x48,
|
|
||||||
0x70, 0x58, 0x33, 0xA2, 0x7D, 0x35, 0x7B, 0x96, 0x74, 0x5E, 0x0B, 0x5C, 0x32, 0x18, 0x14, 0x24,
|
|
||||||
0xC2, 0x58, 0xB3, 0x6C, 0x22, 0x7A, 0xA1, 0xB7, 0xCB, 0x90, 0xA7, 0xA3, 0xF9, 0x7D, 0x45, 0x16,
|
|
||||||
0xA5, 0xC8, 0xED, 0x8F, 0xAD, 0x39, 0x5E, 0x9E, 0x4B, 0x51, 0x68, 0x7D, 0xF8, 0x0C, 0x35, 0xC6,
|
|
||||||
0x3F, 0x91, 0xAE, 0x44, 0xA5, 0x92, 0x30, 0x0D, 0x46, 0xF8, 0x40, 0xFF, 0xD0, 0xFF, 0x06, 0xD2,
|
|
||||||
0x1C, 0x7F, 0x96, 0x18, 0xDC, 0xB7, 0x1D, 0x66, 0x3E, 0xD1, 0x73, 0xBC, 0x15, 0x8A, 0x2F, 0x94,
|
|
||||||
0xF3, 0x00, 0xC1, 0x83, 0xF1, 0xCD, 0xD7, 0x81, 0x88, 0xAB, 0xDF, 0x8C, 0xEF, 0x97, 0xDD, 0x1B,
|
|
||||||
0x17, 0x5F, 0x58, 0xF6, 0x9A, 0xE9, 0xE8, 0xC2, 0x2F, 0x38, 0x15, 0xF5, 0x21, 0x07, 0xF8, 0x37,
|
|
||||||
0x90, 0x5D, 0x2E, 0x02, 0x40, 0x24, 0x15, 0x0D, 0x25, 0xB7, 0x26, 0x5D, 0x09, 0xCC, 0x4C, 0xF4,
|
|
||||||
0xF2, 0x1B, 0x94, 0x70, 0x5A, 0x9E, 0xEE, 0xED, 0x77, 0x77, 0xD4, 0x51, 0x99, 0xF5, 0xDC, 0x76,
|
|
||||||
0x1E, 0xE3, 0x6C, 0x8C, 0xD1, 0x12, 0xD4, 0x57, 0xD1, 0xB6, 0x83, 0xE4, 0xE4, 0xFE, 0xDA, 0xE9,
|
|
||||||
0xB4, 0x3B, 0x33, 0xE5, 0x37, 0x8A, 0xDF, 0xB5, 0x7F, 0x89, 0xF1, 0x9B, 0x9E, 0xB0, 0x15, 0xB2,
|
|
||||||
0x3A, 0xFE, 0xEA, 0x61, 0x84, 0x5B, 0x7D, 0x4B, 0x23, 0x12, 0x0B, 0x83, 0x12, 0xF2, 0x22, 0x6B,
|
|
||||||
0xB9, 0x22, 0x96, 0x4B, 0x26, 0x0B, 0x63, 0x5E, 0x96, 0x57, 0x52, 0xA3, 0x67, 0x64, 0x22, 0xCA,
|
|
||||||
0xD0, 0x56, 0x3E, 0x74, 0xB5, 0x98, 0x1F, 0x0D, 0xF8, 0xB3, 0x34, 0xE6, 0x98, 0x68, 0x5A, 0xAD
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> AcidFixedKeyModulus0Prod => new byte[]
|
|
||||||
{
|
|
||||||
0xDD, 0xC8, 0xDD, 0xF2, 0x4E, 0x6D, 0xF0, 0xCA, 0x9E, 0xC7, 0x5D, 0xC7, 0x7B, 0xAD, 0xFE, 0x7D,
|
|
||||||
0x23, 0x89, 0x69, 0xB6, 0xF2, 0x06, 0xA2, 0x02, 0x88, 0xE1, 0x55, 0x91, 0xAB, 0xCB, 0x4D, 0x50,
|
|
||||||
0x2E, 0xFC, 0x9D, 0x94, 0x76, 0xD6, 0x4C, 0xD8, 0xFF, 0x10, 0xFA, 0x5E, 0x93, 0x0A, 0xB4, 0x57,
|
|
||||||
0xAC, 0x51, 0xC7, 0x16, 0x66, 0xF4, 0x1A, 0x54, 0xC2, 0xC5, 0x04, 0x3D, 0x1B, 0xFE, 0x30, 0x20,
|
|
||||||
0x8A, 0xAC, 0x6F, 0x6F, 0xF5, 0xC7, 0xB6, 0x68, 0xB8, 0xC9, 0x40, 0x6B, 0x42, 0xAD, 0x11, 0x21,
|
|
||||||
0xE7, 0x8B, 0xE9, 0x75, 0x01, 0x86, 0xE4, 0x48, 0x9B, 0x0A, 0x0A, 0xF8, 0x7F, 0xE8, 0x87, 0xF2,
|
|
||||||
0x82, 0x01, 0xE6, 0xA3, 0x0F, 0xE4, 0x66, 0xAE, 0x83, 0x3F, 0x4E, 0x9F, 0x5E, 0x01, 0x30, 0xA4,
|
|
||||||
0x00, 0xB9, 0x9A, 0xAE, 0x5F, 0x03, 0xCC, 0x18, 0x60, 0xE5, 0xEF, 0x3B, 0x5E, 0x15, 0x16, 0xFE,
|
|
||||||
0x1C, 0x82, 0x78, 0xB5, 0x2F, 0x47, 0x7C, 0x06, 0x66, 0x88, 0x5D, 0x35, 0xA2, 0x67, 0x20, 0x10,
|
|
||||||
0xE7, 0x6C, 0x43, 0x68, 0xD3, 0xE4, 0x5A, 0x68, 0x2A, 0x5A, 0xE2, 0x6D, 0x73, 0xB0, 0x31, 0x53,
|
|
||||||
0x1C, 0x20, 0x09, 0x44, 0xF5, 0x1A, 0x9D, 0x22, 0xBE, 0x12, 0xA1, 0x77, 0x11, 0xE2, 0xA1, 0xCD,
|
|
||||||
0x40, 0x9A, 0xA2, 0x8B, 0x60, 0x9B, 0xEF, 0xA0, 0xD3, 0x48, 0x63, 0xA2, 0xF8, 0xA3, 0x2C, 0x08,
|
|
||||||
0x56, 0x52, 0x2E, 0x60, 0x19, 0x67, 0x5A, 0xA7, 0x9F, 0xDC, 0x3F, 0x3F, 0x69, 0x2B, 0x31, 0x6A,
|
|
||||||
0xB7, 0x88, 0x4A, 0x14, 0x84, 0x80, 0x33, 0x3C, 0x9D, 0x44, 0xB7, 0x3F, 0x4C, 0xE1, 0x75, 0xEA,
|
|
||||||
0x37, 0xEA, 0xE8, 0x1E, 0x7C, 0x77, 0xB7, 0xC6, 0x1A, 0xA2, 0xF0, 0x9F, 0x10, 0x61, 0xCD, 0x7B,
|
|
||||||
0x5B, 0x32, 0x4C, 0x37, 0xEF, 0xB1, 0x71, 0x68, 0x53, 0x0A, 0xED, 0x51, 0x7D, 0x35, 0x22, 0xFD
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> AcidFixedKeyModulus1Prod => new byte[]
|
|
||||||
{
|
|
||||||
0xE7, 0xAA, 0x25, 0xC8, 0x01, 0xA5, 0x14, 0x6B, 0x01, 0x60, 0x3E, 0xD9, 0x96, 0x5A, 0xBF, 0x90,
|
|
||||||
0xAC, 0xA7, 0xFD, 0x9B, 0x5B, 0xBD, 0x8A, 0x26, 0xB0, 0xCB, 0x20, 0x28, 0x9A, 0x72, 0x12, 0xF5,
|
|
||||||
0x20, 0x65, 0xB3, 0xB9, 0x84, 0x58, 0x1F, 0x27, 0xBC, 0x7C, 0xA2, 0xC9, 0x9E, 0x18, 0x95, 0xCF,
|
|
||||||
0xC2, 0x73, 0x2E, 0x74, 0x8C, 0x66, 0xE5, 0x9E, 0x79, 0x2B, 0xB8, 0x07, 0x0C, 0xB0, 0x4E, 0x8E,
|
|
||||||
0xAB, 0x85, 0x21, 0x42, 0xC4, 0xC5, 0x6D, 0x88, 0x9C, 0xDB, 0x15, 0x95, 0x3F, 0x80, 0xDB, 0x7A,
|
|
||||||
0x9A, 0x7D, 0x41, 0x56, 0x25, 0x17, 0x18, 0x42, 0x4D, 0x8C, 0xAC, 0xA5, 0x7B, 0xDB, 0x42, 0x5D,
|
|
||||||
0x59, 0x35, 0x45, 0x5D, 0x8A, 0x02, 0xB5, 0x70, 0xC0, 0x72, 0x35, 0x46, 0xD0, 0x1D, 0x60, 0x01,
|
|
||||||
0x4A, 0xCC, 0x1C, 0x46, 0xD3, 0xD6, 0x35, 0x52, 0xD6, 0xE1, 0xF8, 0x3B, 0x5D, 0xEA, 0xDD, 0xB8,
|
|
||||||
0xFE, 0x7D, 0x50, 0xCB, 0x35, 0x23, 0x67, 0x8B, 0xB6, 0xE4, 0x74, 0xD2, 0x60, 0xFC, 0xFD, 0x43,
|
|
||||||
0xBF, 0x91, 0x08, 0x81, 0xC5, 0x4F, 0x5D, 0x16, 0x9A, 0xC4, 0x9A, 0xC6, 0xF6, 0xF3, 0xE1, 0xF6,
|
|
||||||
0x5C, 0x07, 0xAA, 0x71, 0x6C, 0x13, 0xA4, 0xB1, 0xB3, 0x66, 0xBF, 0x90, 0x4C, 0x3D, 0xA2, 0xC4,
|
|
||||||
0x0B, 0xB8, 0x3D, 0x7A, 0x8C, 0x19, 0xFA, 0xFF, 0x6B, 0xB9, 0x1F, 0x02, 0xCC, 0xB6, 0xD3, 0x0C,
|
|
||||||
0x7D, 0x19, 0x1F, 0x47, 0xF9, 0xC7, 0x40, 0x01, 0xFA, 0x46, 0xEA, 0x0B, 0xD4, 0x02, 0xE0, 0x3D,
|
|
||||||
0x30, 0x9A, 0x1A, 0x0F, 0xEA, 0xA7, 0x66, 0x55, 0xF7, 0xCB, 0x28, 0xE2, 0xBB, 0x99, 0xE4, 0x83,
|
|
||||||
0xC3, 0x43, 0x03, 0xEE, 0xDC, 0x1F, 0x02, 0x23, 0xDD, 0xD1, 0x2D, 0x39, 0xA4, 0x65, 0x75, 0x03,
|
|
||||||
0xEF, 0x37, 0x9C, 0x06, 0xD6, 0xFA, 0xA1, 0x15, 0xF0, 0xDB, 0x17, 0x47, 0x26, 0x4F, 0x49, 0x03
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> Package2FixedKeyModulusProd => new byte[]
|
|
||||||
{
|
|
||||||
0x8D, 0x13, 0xA7, 0x77, 0x6A, 0xE5, 0xDC, 0xC0, 0x3B, 0x25, 0xD0, 0x58, 0xE4, 0x20, 0x69, 0x59,
|
|
||||||
0x55, 0x4B, 0xAB, 0x70, 0x40, 0x08, 0x28, 0x07, 0xA8, 0xA7, 0xFD, 0x0F, 0x31, 0x2E, 0x11, 0xFE,
|
|
||||||
0x47, 0xA0, 0xF9, 0x9D, 0xDF, 0x80, 0xDB, 0x86, 0x5A, 0x27, 0x89, 0xCD, 0x97, 0x6C, 0x85, 0xC5,
|
|
||||||
0x6C, 0x39, 0x7F, 0x41, 0xF2, 0xFF, 0x24, 0x20, 0xC3, 0x95, 0xA6, 0xF7, 0x9D, 0x4A, 0x45, 0x74,
|
|
||||||
0x8B, 0x5D, 0x28, 0x8A, 0xC6, 0x99, 0x35, 0x68, 0x85, 0xA5, 0x64, 0x32, 0x80, 0x9F, 0xD3, 0x48,
|
|
||||||
0x39, 0xA2, 0x1D, 0x24, 0x67, 0x69, 0xDF, 0x75, 0xAC, 0x12, 0xB5, 0xBD, 0xC3, 0x29, 0x90, 0xBE,
|
|
||||||
0x37, 0xE4, 0xA0, 0x80, 0x9A, 0xBE, 0x36, 0xBF, 0x1F, 0x2C, 0xAB, 0x2B, 0xAD, 0xF5, 0x97, 0x32,
|
|
||||||
0x9A, 0x42, 0x9D, 0x09, 0x8B, 0x08, 0xF0, 0x63, 0x47, 0xA3, 0xE9, 0x1B, 0x36, 0xD8, 0x2D, 0x8A,
|
|
||||||
0xD7, 0xE1, 0x54, 0x11, 0x95, 0xE4, 0x45, 0x88, 0x69, 0x8A, 0x2B, 0x35, 0xCE, 0xD0, 0xA5, 0x0B,
|
|
||||||
0xD5, 0x5D, 0xAC, 0xDB, 0xAF, 0x11, 0x4D, 0xCA, 0xB8, 0x1E, 0xE7, 0x01, 0x9E, 0xF4, 0x46, 0xA3,
|
|
||||||
0x8A, 0x94, 0x6D, 0x76, 0xBD, 0x8A, 0xC8, 0x3B, 0xD2, 0x31, 0x58, 0x0C, 0x79, 0xA8, 0x26, 0xE9,
|
|
||||||
0xD1, 0x79, 0x9C, 0xCB, 0xD4, 0x2B, 0x6A, 0x4F, 0xC6, 0xCC, 0xCF, 0x90, 0xA7, 0xB9, 0x98, 0x47,
|
|
||||||
0xFD, 0xFA, 0x4C, 0x6C, 0x6F, 0x81, 0x87, 0x3B, 0xCA, 0xB8, 0x50, 0xF6, 0x3E, 0x39, 0x5D, 0x4D,
|
|
||||||
0x97, 0x3F, 0x0F, 0x35, 0x39, 0x53, 0xFB, 0xFA, 0xCD, 0xAB, 0xA8, 0x7A, 0x62, 0x9A, 0x3F, 0xF2,
|
|
||||||
0x09, 0x27, 0x96, 0x3F, 0x07, 0x9A, 0x91, 0xF7, 0x16, 0xBF, 0xC6, 0x3A, 0x82, 0x5A, 0x4B, 0xCF,
|
|
||||||
0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> NcaHdrFixedKeyModulus0Dev => new byte[]
|
|
||||||
{
|
|
||||||
0xD8, 0xF1, 0x18, 0xEF, 0x32, 0x72, 0x4C, 0xA7, 0x47, 0x4C, 0xB9, 0xEA, 0xB3, 0x04, 0xA8, 0xA4,
|
|
||||||
0xAC, 0x99, 0x08, 0x08, 0x04, 0xBF, 0x68, 0x57, 0xB8, 0x43, 0x94, 0x2B, 0xC7, 0xB9, 0x66, 0x49,
|
|
||||||
0x85, 0xE5, 0x8A, 0x9B, 0xC1, 0x00, 0x9A, 0x6A, 0x8D, 0xD0, 0xEF, 0xCE, 0xFF, 0x86, 0xC8, 0x5C,
|
|
||||||
0x5D, 0xE9, 0x53, 0x7B, 0x19, 0x2A, 0xA8, 0xC0, 0x22, 0xD1, 0xF3, 0x22, 0x0A, 0x50, 0xF2, 0x2B,
|
|
||||||
0x65, 0x05, 0x1B, 0x9E, 0xEC, 0x61, 0xB5, 0x63, 0xA3, 0x6F, 0x3B, 0xBA, 0x63, 0x3A, 0x53, 0xF4,
|
|
||||||
0x49, 0x2F, 0xCF, 0x03, 0xCC, 0xD7, 0x50, 0x82, 0x1B, 0x29, 0x4F, 0x08, 0xDE, 0x1B, 0x6D, 0x47,
|
|
||||||
0x4F, 0xA8, 0xB6, 0x6A, 0x26, 0xA0, 0x83, 0x3F, 0x1A, 0xAF, 0x83, 0x8F, 0x0E, 0x17, 0x3F, 0xFE,
|
|
||||||
0x44, 0x1C, 0x56, 0x94, 0x2E, 0x49, 0x83, 0x83, 0x03, 0xE9, 0xB6, 0xAD, 0xD5, 0xDE, 0xE3, 0x2D,
|
|
||||||
0xA1, 0xD9, 0x66, 0x20, 0x5D, 0x1F, 0x5E, 0x96, 0x5D, 0x5B, 0x55, 0x0D, 0xD4, 0xB4, 0x77, 0x6E,
|
|
||||||
0xAE, 0x1B, 0x69, 0xF3, 0xA6, 0x61, 0x0E, 0x51, 0x62, 0x39, 0x28, 0x63, 0x75, 0x76, 0xBF, 0xB0,
|
|
||||||
0xD2, 0x22, 0xEF, 0x98, 0x25, 0x02, 0x05, 0xC0, 0xD7, 0x6A, 0x06, 0x2C, 0xA5, 0xD8, 0x5A, 0x9D,
|
|
||||||
0x7A, 0xA4, 0x21, 0x55, 0x9F, 0xF9, 0x3E, 0xBF, 0x16, 0xF6, 0x07, 0xC2, 0xB9, 0x6E, 0x87, 0x9E,
|
|
||||||
0xB5, 0x1C, 0xBE, 0x97, 0xFA, 0x82, 0x7E, 0xED, 0x30, 0xD4, 0x66, 0x3F, 0xDE, 0xD8, 0x1B, 0x4B,
|
|
||||||
0x15, 0xD9, 0xFB, 0x2F, 0x50, 0xF0, 0x9D, 0x1D, 0x52, 0x4C, 0x1C, 0x4D, 0x8D, 0xAE, 0x85, 0x1E,
|
|
||||||
0xEA, 0x7F, 0x86, 0xF3, 0x0B, 0x7B, 0x87, 0x81, 0x98, 0x23, 0x80, 0x63, 0x4F, 0x2F, 0xB0, 0x62,
|
|
||||||
0xCC, 0x6E, 0xD2, 0x46, 0x13, 0x65, 0x2B, 0xD6, 0x44, 0x33, 0x59, 0xB5, 0x8F, 0xB9, 0x4A, 0xA9
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> NcaHdrFixedKeyModulus1Dev => new byte[]
|
|
||||||
{
|
|
||||||
0x9A, 0xBC, 0x88, 0xBD, 0x0A, 0xBE, 0xD7, 0x0C, 0x9B, 0x42, 0x75, 0x65, 0x38, 0x5E, 0xD1, 0x01,
|
|
||||||
0xCD, 0x12, 0xAE, 0xEA, 0xE9, 0x4B, 0xDB, 0xB4, 0x5E, 0x36, 0x10, 0x96, 0xDA, 0x3D, 0x2E, 0x66,
|
|
||||||
0xD3, 0x99, 0x13, 0x8A, 0xBE, 0x67, 0x41, 0xC8, 0x93, 0xD9, 0x3E, 0x42, 0xCE, 0x34, 0xCE, 0x96,
|
|
||||||
0xFA, 0x0B, 0x23, 0xCC, 0x2C, 0xDF, 0x07, 0x3F, 0x3B, 0x24, 0x4B, 0x12, 0x67, 0x3A, 0x29, 0x36,
|
|
||||||
0xA3, 0xAA, 0x06, 0xF0, 0x65, 0xA5, 0x85, 0xBA, 0xFD, 0x12, 0xEC, 0xF1, 0x60, 0x67, 0xF0, 0x8F,
|
|
||||||
0xD3, 0x5B, 0x01, 0x1B, 0x1E, 0x84, 0xA3, 0x5C, 0x65, 0x36, 0xF9, 0x23, 0x7E, 0xF3, 0x26, 0x38,
|
|
||||||
0x64, 0x98, 0xBA, 0xE4, 0x19, 0x91, 0x4C, 0x02, 0xCF, 0xC9, 0x6D, 0x86, 0xEC, 0x1D, 0x41, 0x69,
|
|
||||||
0xDD, 0x56, 0xEA, 0x5C, 0xA3, 0x2A, 0x58, 0xB4, 0x39, 0xCC, 0x40, 0x31, 0xFD, 0xFB, 0x42, 0x74,
|
|
||||||
0xF8, 0xEC, 0xEA, 0x00, 0xF0, 0xD9, 0x28, 0xEA, 0xFA, 0x2D, 0x00, 0xE1, 0x43, 0x53, 0xC6, 0x32,
|
|
||||||
0xF4, 0xA2, 0x07, 0xD4, 0x5F, 0xD4, 0xCB, 0xAC, 0xCA, 0xFF, 0xDF, 0x84, 0xD2, 0x86, 0x14, 0x3C,
|
|
||||||
0xDE, 0x22, 0x75, 0xA5, 0x73, 0xFF, 0x68, 0x07, 0x4A, 0xF9, 0x7C, 0x2C, 0xCC, 0xDE, 0x45, 0xB6,
|
|
||||||
0x54, 0x82, 0x90, 0x36, 0x1F, 0x2C, 0x51, 0x96, 0xC5, 0x0A, 0x53, 0x5B, 0xF0, 0x8B, 0x4A, 0xAA,
|
|
||||||
0x3B, 0x68, 0x97, 0x19, 0x17, 0x1F, 0x01, 0xB8, 0xED, 0xB9, 0x9A, 0x5E, 0x08, 0xC5, 0x20, 0x1E,
|
|
||||||
0x6A, 0x09, 0xF0, 0xE9, 0x73, 0xA3, 0xBE, 0x10, 0x06, 0x02, 0xE9, 0xFB, 0x85, 0xFA, 0x5F, 0x01,
|
|
||||||
0xAC, 0x60, 0xE0, 0xED, 0x7D, 0xB9, 0x49, 0xA8, 0x9E, 0x98, 0x7D, 0x91, 0x40, 0x05, 0xCF, 0xF9,
|
|
||||||
0x1A, 0xFC, 0x40, 0x22, 0xA8, 0x96, 0x5B, 0xB0, 0xDC, 0x7A, 0xF5, 0xB7, 0xE9, 0x91, 0x4C, 0x49
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> AcidFixedKeyModulus0Dev => new byte[]
|
|
||||||
{
|
|
||||||
0xD6, 0x34, 0xA5, 0x78, 0x6C, 0x68, 0xCE, 0x5A, 0xC2, 0x37, 0x17, 0xF3, 0x82, 0x45, 0xC6, 0x89,
|
|
||||||
0xE1, 0x2D, 0x06, 0x67, 0xBF, 0xB4, 0x06, 0x19, 0x55, 0x6B, 0x27, 0x66, 0x0C, 0xA4, 0xB5, 0x87,
|
|
||||||
0x81, 0x25, 0xF4, 0x30, 0xBC, 0x53, 0x08, 0x68, 0xA2, 0x48, 0x49, 0x8C, 0x3F, 0x38, 0x40, 0x9C,
|
|
||||||
0xC4, 0x26, 0xF4, 0x79, 0xE2, 0xA1, 0x85, 0xF5, 0x5C, 0x7F, 0x58, 0xBA, 0xA6, 0x1C, 0xA0, 0x8B,
|
|
||||||
0x84, 0x16, 0x14, 0x6F, 0x85, 0xD9, 0x7C, 0xE1, 0x3C, 0x67, 0x22, 0x1E, 0xFB, 0xD8, 0xA7, 0xA5,
|
|
||||||
0x9A, 0xBF, 0xEC, 0x0E, 0xCF, 0x96, 0x7E, 0x85, 0xC2, 0x1D, 0x49, 0x5D, 0x54, 0x26, 0xCB, 0x32,
|
|
||||||
0x7C, 0xF6, 0xBB, 0x58, 0x03, 0x80, 0x2B, 0x5D, 0xF7, 0xFB, 0xD1, 0x9D, 0xC7, 0xC6, 0x2E, 0x53,
|
|
||||||
0xC0, 0x6F, 0x39, 0x2C, 0x1F, 0xA9, 0x92, 0xF2, 0x4D, 0x7D, 0x4E, 0x74, 0xFF, 0xE4, 0xEF, 0xE4,
|
|
||||||
0x7C, 0x3D, 0x34, 0x2A, 0x71, 0xA4, 0x97, 0x59, 0xFF, 0x4F, 0xA2, 0xF4, 0x66, 0x78, 0xD8, 0xBA,
|
|
||||||
0x99, 0xE3, 0xE6, 0xDB, 0x54, 0xB9, 0xE9, 0x54, 0xA1, 0x70, 0xFC, 0x05, 0x1F, 0x11, 0x67, 0x4B,
|
|
||||||
0x26, 0x8C, 0x0C, 0x3E, 0x03, 0xD2, 0xA3, 0x55, 0x5C, 0x7D, 0xC0, 0x5D, 0x9D, 0xFF, 0x13, 0x2F,
|
|
||||||
0xFD, 0x19, 0xBF, 0xED, 0x44, 0xC3, 0x8C, 0xA7, 0x28, 0xCB, 0xE5, 0xE0, 0xB1, 0xA7, 0x9C, 0x33,
|
|
||||||
0x8D, 0xB8, 0x6E, 0xDE, 0x87, 0x18, 0x22, 0x60, 0xC4, 0xAE, 0xF2, 0x87, 0x9F, 0xCE, 0x09, 0x5C,
|
|
||||||
0xB5, 0x99, 0xA5, 0x9F, 0x49, 0xF2, 0xD7, 0x58, 0xFA, 0xF9, 0xC0, 0x25, 0x7D, 0xD6, 0xCB, 0xF3,
|
|
||||||
0xD8, 0x6C, 0xA2, 0x69, 0x91, 0x68, 0x73, 0xB1, 0x94, 0x6F, 0xA3, 0xF3, 0xB9, 0x7D, 0xF8, 0xE0,
|
|
||||||
0x72, 0x9E, 0x93, 0x7B, 0x7A, 0xA2, 0x57, 0x60, 0xB7, 0x5B, 0xA9, 0x84, 0xAE, 0x64, 0x88, 0x69
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> AcidFixedKeyModulus1Dev => new byte[]
|
|
||||||
{
|
|
||||||
0xBC, 0xA5, 0x6A, 0x7E, 0xEA, 0x38, 0x34, 0x62, 0xA6, 0x10, 0x18, 0x3C, 0xE1, 0x63, 0x7B, 0xF0,
|
|
||||||
0xD3, 0x08, 0x8C, 0xF5, 0xC5, 0xC4, 0xC7, 0x93, 0xE9, 0xD9, 0xE6, 0x32, 0xF3, 0xA0, 0xF6, 0x6E,
|
|
||||||
0x8A, 0x98, 0x76, 0x47, 0x33, 0x47, 0x65, 0x02, 0x70, 0xDC, 0x86, 0x5F, 0x3D, 0x61, 0x5A, 0x70,
|
|
||||||
0xBC, 0x5A, 0xCA, 0xCA, 0x50, 0xAD, 0x61, 0x7E, 0xC9, 0xEC, 0x27, 0xFF, 0xE8, 0x64, 0x42, 0x9A,
|
|
||||||
0xEE, 0xBE, 0xC3, 0xD1, 0x0B, 0xC0, 0xE9, 0xBF, 0x83, 0x8D, 0xC0, 0x0C, 0xD8, 0x00, 0x5B, 0x76,
|
|
||||||
0x90, 0xD2, 0x4B, 0x30, 0x84, 0x35, 0x8B, 0x1E, 0x20, 0xB7, 0xE4, 0xDC, 0x63, 0xE5, 0xDF, 0xCD,
|
|
||||||
0x00, 0x5F, 0x81, 0x5F, 0x67, 0xC5, 0x8B, 0xDF, 0xFC, 0xE1, 0x37, 0x5F, 0x07, 0xD9, 0xDE, 0x4F,
|
|
||||||
0xE6, 0x7B, 0xF1, 0xFB, 0xA1, 0x5A, 0x71, 0x40, 0xFE, 0xBA, 0x1E, 0xAE, 0x13, 0x22, 0xD2, 0xFE,
|
|
||||||
0x37, 0xA2, 0xB6, 0x8B, 0xAB, 0xEB, 0x84, 0x81, 0x4E, 0x7C, 0x1E, 0x02, 0xD1, 0xFB, 0xD7, 0x5D,
|
|
||||||
0x11, 0x84, 0x64, 0xD2, 0x4D, 0xBB, 0x50, 0x00, 0x67, 0x54, 0xE2, 0x77, 0x89, 0xBA, 0x0B, 0xE7,
|
|
||||||
0x05, 0x57, 0x9A, 0x22, 0x5A, 0xEC, 0x76, 0x1C, 0xFD, 0xE8, 0xA8, 0x18, 0x16, 0x41, 0x65, 0x03,
|
|
||||||
0xFA, 0xC4, 0xA6, 0x31, 0x5C, 0x1A, 0x7F, 0xAB, 0x11, 0xC8, 0x4A, 0x99, 0xB9, 0xE6, 0xCF, 0x62,
|
|
||||||
0x21, 0xA6, 0x72, 0x47, 0xDB, 0xBA, 0x96, 0x26, 0x4E, 0x2E, 0xD4, 0x8C, 0x46, 0xD6, 0xA7, 0x1A,
|
|
||||||
0x6C, 0x32, 0xA7, 0xDF, 0x85, 0x1C, 0x03, 0xC3, 0x6D, 0xA9, 0xE9, 0x68, 0xF4, 0x17, 0x1E, 0xB2,
|
|
||||||
0x70, 0x2A, 0xA1, 0xE5, 0xE1, 0xF3, 0x8F, 0x6F, 0x63, 0xAC, 0xEB, 0x72, 0x0B, 0x4C, 0x4A, 0x36,
|
|
||||||
0x3C, 0x60, 0x91, 0x9F, 0x6E, 0x1C, 0x71, 0xEA, 0xD0, 0x78, 0x78, 0xA0, 0x2E, 0xC6, 0x32, 0x6B
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> Package2FixedKeyModulusDev => new byte[]
|
|
||||||
{
|
|
||||||
0xB3, 0x65, 0x54, 0xFB, 0x0A, 0xB0, 0x1E, 0x85, 0xA7, 0xF6, 0xCF, 0x91, 0x8E, 0xBA, 0x96, 0x99,
|
|
||||||
0x0D, 0x8B, 0x91, 0x69, 0x2A, 0xEE, 0x01, 0x20, 0x4F, 0x34, 0x5C, 0x2C, 0x4F, 0x4E, 0x37, 0xC7,
|
|
||||||
0xF1, 0x0B, 0xD4, 0xCD, 0xA1, 0x7F, 0x93, 0xF1, 0x33, 0x59, 0xCE, 0xB1, 0xE9, 0xDD, 0x26, 0xE6,
|
|
||||||
0xF3, 0xBB, 0x77, 0x87, 0x46, 0x7A, 0xD6, 0x4E, 0x47, 0x4A, 0xD1, 0x41, 0xB7, 0x79, 0x4A, 0x38,
|
|
||||||
0x06, 0x6E, 0xCF, 0x61, 0x8F, 0xCD, 0xC1, 0x40, 0x0B, 0xFA, 0x26, 0xDC, 0xC0, 0x34, 0x51, 0x83,
|
|
||||||
0xD9, 0x3B, 0x11, 0x54, 0x3B, 0x96, 0x27, 0x32, 0x9A, 0x95, 0xBE, 0x1E, 0x68, 0x11, 0x50, 0xA0,
|
|
||||||
0x6B, 0x10, 0xA8, 0x83, 0x8B, 0xF5, 0xFC, 0xBC, 0x90, 0x84, 0x7A, 0x5A, 0x5C, 0x43, 0x52, 0xE6,
|
|
||||||
0xC8, 0x26, 0xE9, 0xFE, 0x06, 0xA0, 0x8B, 0x53, 0x0F, 0xAF, 0x1E, 0xC4, 0x1C, 0x0B, 0xCF, 0x50,
|
|
||||||
0x1A, 0xA4, 0xF3, 0x5C, 0xFB, 0xF0, 0x97, 0xE4, 0xDE, 0x32, 0x0A, 0x9F, 0xE3, 0x5A, 0xAA, 0xB7,
|
|
||||||
0x44, 0x7F, 0x5C, 0x33, 0x60, 0xB9, 0x0F, 0x22, 0x2D, 0x33, 0x2A, 0xE9, 0x69, 0x79, 0x31, 0x42,
|
|
||||||
0x8F, 0xE4, 0x3A, 0x13, 0x8B, 0xE7, 0x26, 0xBD, 0x08, 0x87, 0x6C, 0xA6, 0xF2, 0x73, 0xF6, 0x8E,
|
|
||||||
0xA7, 0xF2, 0xFE, 0xFB, 0x6C, 0x28, 0x66, 0x0D, 0xBD, 0xD7, 0xEB, 0x42, 0xA8, 0x78, 0xE6, 0xB8,
|
|
||||||
0x6B, 0xAE, 0xC7, 0xA9, 0xE2, 0x40, 0x6E, 0x89, 0x20, 0x82, 0x25, 0x8E, 0x3C, 0x6A, 0x60, 0xD7,
|
|
||||||
0xF3, 0x56, 0x8E, 0xEC, 0x8D, 0x51, 0x8A, 0x63, 0x3C, 0x04, 0x78, 0x23, 0x0E, 0x90, 0x0C, 0xB4,
|
|
||||||
0xE7, 0x86, 0x3B, 0x4F, 0x8E, 0x13, 0x09, 0x47, 0x32, 0x0E, 0x04, 0xB8, 0x4D, 0x5B, 0xB0, 0x46,
|
|
||||||
0x71, 0xB0, 0x5C, 0xF4, 0xAD, 0x63, 0x4F, 0xC5, 0xE2, 0xAC, 0x1E, 0xC4, 0x33, 0x96, 0x09, 0x7B
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string BuildDefaultKeySetFile(KeySet keySet)
|
||||||
|
{
|
||||||
|
var sb = new IndentingStringBuilder();
|
||||||
|
|
||||||
|
sb.AppendLine(GetHeader());
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
sb.AppendLine("using System;");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
sb.AppendLine("namespace LibHac.Common.Keys");
|
||||||
|
sb.AppendLineAndIncrease("{");
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
sb.DecreaseAndAppendLine("}");
|
||||||
|
sb.DecreaseAndAppendLine("}");
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void BuildArray(IndentingStringBuilder sb, string name, ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
sb.AppendSpacerLine();
|
||||||
|
sb.Append($"private static ReadOnlySpan<byte> {name} => new byte[]");
|
||||||
|
|
||||||
|
if (data.IsZeros())
|
||||||
|
{
|
||||||
|
sb.AppendLine(" { };");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.AppendLineAndIncrease("{");
|
||||||
|
|
||||||
|
for (int i = 0; i < data.Length; i++)
|
||||||
|
{
|
||||||
|
if (i % 16 != 0) sb.Append(" ");
|
||||||
|
sb.Append($"0x{data[i]:x2}");
|
||||||
|
|
||||||
|
if (i != data.Length - 1)
|
||||||
|
{
|
||||||
|
sb.Append(",");
|
||||||
|
if (i % 16 == 15) sb.AppendLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.DecreaseAndAppendLine("};");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static KeySet CreateKeySet()
|
||||||
|
{
|
||||||
|
var keySet = new KeySet();
|
||||||
|
|
||||||
|
// Populate the key set with all the keys in IncludedKeys.txt
|
||||||
|
using (Stream keyFile = GetResource(InputMainKeyFileName))
|
||||||
|
{
|
||||||
|
List<KeyInfo> list = KeySet.CreateKeyInfoList();
|
||||||
|
ExternalKeyReader.ReadMainKeys(keySet, keyFile, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recover all the RSA key parameters and write the key to the key set
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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.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.Package2SigningKey.PublicExponent.Data);
|
||||||
|
Package2FixedKeyModulusProd.CopyTo(keySet.Package2SigningKey.Modulus.Data);
|
||||||
|
|
||||||
|
// 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.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.Package2SigningKey.PublicExponent.Data);
|
||||||
|
Package2FixedKeyModulusDev.CopyTo(keySet.Package2SigningKey.Modulus.Data);
|
||||||
|
|
||||||
|
return keySet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> StandardPublicExponent => new byte[]
|
||||||
|
{
|
||||||
|
0x01, 0x00, 0x01
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> BetaNca0Modulus => new byte[]
|
||||||
|
{
|
||||||
|
0xAD, 0x58, 0xEE, 0x97, 0xF9, 0x47, 0x90, 0x7D, 0xF9, 0x29, 0x5F, 0x1F, 0x39, 0x68, 0xEE, 0x49,
|
||||||
|
0x4C, 0x1E, 0x8D, 0x84, 0x91, 0x31, 0x5D, 0xE5, 0x96, 0x27, 0xB2, 0xB3, 0x59, 0x7B, 0xDE, 0xFD,
|
||||||
|
0xB7, 0xEB, 0x40, 0xA1, 0xE7, 0xEB, 0xDC, 0x60, 0xD0, 0x3D, 0xC5, 0x50, 0x92, 0xAD, 0x3D, 0xC4,
|
||||||
|
0x8C, 0x17, 0xD2, 0x37, 0x66, 0xE3, 0xF7, 0x14, 0x34, 0x38, 0x6B, 0xA7, 0x2B, 0x21, 0x10, 0x9B,
|
||||||
|
0x73, 0x49, 0x15, 0xD9, 0x2A, 0x90, 0x86, 0x76, 0x81, 0x6A, 0x10, 0xBD, 0x74, 0xC4, 0x20, 0x55,
|
||||||
|
0x25, 0xA8, 0x02, 0xC5, 0xA0, 0x34, 0x36, 0x7B, 0x66, 0x47, 0x2C, 0x7E, 0x47, 0x82, 0xA5, 0xD4,
|
||||||
|
0xA3, 0x42, 0x45, 0xE8, 0xFD, 0x65, 0x72, 0x48, 0xA1, 0xB0, 0x44, 0x10, 0xEF, 0xAC, 0x1D, 0x0F,
|
||||||
|
0xB5, 0x12, 0x19, 0xA8, 0x41, 0x0B, 0x76, 0x3B, 0xBC, 0xF1, 0x4A, 0x10, 0x46, 0x22, 0xB8, 0xF1,
|
||||||
|
0xBC, 0x21, 0x81, 0x69, 0x9B, 0x63, 0x6F, 0xD7, 0xB9, 0x60, 0x2A, 0x9A, 0xE5, 0x2C, 0x47, 0x72,
|
||||||
|
0x59, 0x65, 0xA2, 0x21, 0x60, 0xC4, 0xFC, 0xB0, 0xD7, 0x6F, 0x42, 0xC9, 0x0C, 0xF5, 0x76, 0x7D,
|
||||||
|
0xF2, 0x5C, 0xE0, 0x80, 0x0F, 0xEE, 0x45, 0x7E, 0x4E, 0x3A, 0x8D, 0x9C, 0x5B, 0x5B, 0xD9, 0xD1,
|
||||||
|
0x43, 0x94, 0x2C, 0xC7, 0x2E, 0xB9, 0x4A, 0xE5, 0x3E, 0x15, 0xDD, 0x43, 0x00, 0xF7, 0x78, 0xE7,
|
||||||
|
0x7C, 0x39, 0xB0, 0x4D, 0xC5, 0xD1, 0x1C, 0xF2, 0xB4, 0x7A, 0x2A, 0xEA, 0x0A, 0x8E, 0xB9, 0x13,
|
||||||
|
0xB4, 0x4F, 0xD7, 0x5B, 0x4D, 0x7B, 0x43, 0xB0, 0x3A, 0x9A, 0x60, 0x22, 0x47, 0x91, 0x78, 0xC7,
|
||||||
|
0x10, 0x64, 0xE0, 0x2C, 0x69, 0xD1, 0x66, 0x3C, 0x42, 0x2E, 0xEF, 0x19, 0x21, 0x89, 0x8E, 0xE1,
|
||||||
|
0xB0, 0xB4, 0xD0, 0x17, 0xA1, 0x0F, 0x73, 0x98, 0x5A, 0xF6, 0xEE, 0xC0, 0x2F, 0x9E, 0xCE, 0xC5
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> BetaNca0Exponent => new byte[]
|
||||||
|
{
|
||||||
|
0x3C, 0x66, 0x37, 0x44, 0x26, 0xAC, 0x63, 0xD1, 0x30, 0xE6, 0xD4, 0x68, 0xF9, 0xC4, 0xF0, 0xFA,
|
||||||
|
0x03, 0x16, 0xC6, 0x32, 0x81, 0xB0, 0x94, 0xC9, 0xF1, 0x26, 0xC5, 0xE2, 0x2D, 0xF4, 0xB6, 0x3E,
|
||||||
|
0xEB, 0x3D, 0x82, 0x18, 0xA7, 0xC9, 0x8B, 0xD1, 0x03, 0xDD, 0xF2, 0x09, 0x60, 0x02, 0x12, 0xFA,
|
||||||
|
0x8F, 0xE1, 0xA0, 0xF2, 0x82, 0xDC, 0x3D, 0x74, 0x01, 0xBA, 0x02, 0xF0, 0x8D, 0x5B, 0x89, 0x00,
|
||||||
|
0xD1, 0x0B, 0x8F, 0x1C, 0x4A, 0xF3, 0x6E, 0x96, 0x8E, 0x03, 0x19, 0xF0, 0x19, 0x66, 0x58, 0xE9,
|
||||||
|
0xB2, 0x24, 0x37, 0x4B, 0x0A, 0xC6, 0x06, 0x91, 0xBA, 0x92, 0x64, 0x13, 0x5F, 0xF1, 0x4A, 0xBC,
|
||||||
|
0xAB, 0x61, 0xE5, 0x20, 0x08, 0x62, 0xB7, 0x8E, 0x4D, 0x20, 0x30, 0xA7, 0x42, 0x0B, 0x53, 0x58,
|
||||||
|
0xEC, 0xBB, 0x70, 0xCB, 0x2A, 0x56, 0xC7, 0x0C, 0x8B, 0x89, 0xFB, 0x47, 0x6E, 0x58, 0x9C, 0xDD,
|
||||||
|
0xB2, 0xE5, 0x4F, 0x49, 0x52, 0x0B, 0xD9, 0x96, 0x30, 0x8D, 0xDE, 0xC9, 0x0F, 0x6A, 0x82, 0xC7,
|
||||||
|
0xE8, 0x20, 0xB6, 0xB3, 0x95, 0xDD, 0xEB, 0xDF, 0xF7, 0x25, 0x23, 0x6B, 0xF8, 0x5B, 0xD4, 0x81,
|
||||||
|
0x7A, 0xBC, 0x94, 0x13, 0x30, 0x59, 0x28, 0xC8, 0xC9, 0x3A, 0x5D, 0xCC, 0x8D, 0xFD, 0x1A, 0xE1,
|
||||||
|
0xCB, 0xA4, 0x1D, 0xD4, 0x45, 0xF1, 0xBF, 0x87, 0x6C, 0x0E, 0xB1, 0x44, 0xC7, 0x88, 0x62, 0x2B,
|
||||||
|
0x43, 0xAD, 0x75, 0xE6, 0x69, 0xFF, 0xD3, 0x39, 0xF5, 0x7F, 0x2A, 0xA2, 0x5F, 0x7A, 0x5E, 0xE6,
|
||||||
|
0xEF, 0xCB, 0x2F, 0x2C, 0x90, 0xE6, 0x4B, 0x2D, 0x94, 0x62, 0xE8, 0xEC, 0x54, 0x7B, 0x94, 0xB7,
|
||||||
|
0xFB, 0x72, 0x05, 0xFB, 0xB3, 0x23, 0xCA, 0xF8, 0xD4, 0x5C, 0xF6, 0xAC, 0x7D, 0xEC, 0x47, 0xC6,
|
||||||
|
0xD3, 0x22, 0x5D, 0x7C, 0x15, 0xDD, 0x48, 0xE9, 0xBF, 0xA8, 0x99, 0x33, 0x02, 0x79, 0xD3, 0x65
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> NcaHdrFixedKeyModulus0Prod => new byte[]
|
||||||
|
{
|
||||||
|
0xBF, 0xBE, 0x40, 0x6C, 0xF4, 0xA7, 0x80, 0xE9, 0xF0, 0x7D, 0x0C, 0x99, 0x61, 0x1D, 0x77, 0x2F,
|
||||||
|
0x96, 0xBC, 0x4B, 0x9E, 0x58, 0x38, 0x1B, 0x03, 0xAB, 0xB1, 0x75, 0x49, 0x9F, 0x2B, 0x4D, 0x58,
|
||||||
|
0x34, 0xB0, 0x05, 0xA3, 0x75, 0x22, 0xBE, 0x1A, 0x3F, 0x03, 0x73, 0xAC, 0x70, 0x68, 0xD1, 0x16,
|
||||||
|
0xB9, 0x04, 0x46, 0x5E, 0xB7, 0x07, 0x91, 0x2F, 0x07, 0x8B, 0x26, 0xDE, 0xF6, 0x00, 0x07, 0xB2,
|
||||||
|
0xB4, 0x51, 0xF8, 0x0D, 0x0A, 0x5E, 0x58, 0xAD, 0xEB, 0xBC, 0x9A, 0xD6, 0x49, 0xB9, 0x64, 0xEF,
|
||||||
|
0xA7, 0x82, 0xB5, 0xCF, 0x6D, 0x70, 0x13, 0xB0, 0x0F, 0x85, 0xF6, 0xA9, 0x08, 0xAA, 0x4D, 0x67,
|
||||||
|
0x66, 0x87, 0xFA, 0x89, 0xFF, 0x75, 0x90, 0x18, 0x1E, 0x6B, 0x3D, 0xE9, 0x8A, 0x68, 0xC9, 0x26,
|
||||||
|
0x04, 0xD9, 0x80, 0xCE, 0x3F, 0x5E, 0x92, 0xCE, 0x01, 0xFF, 0x06, 0x3B, 0xF2, 0xC1, 0xA9, 0x0C,
|
||||||
|
0xCE, 0x02, 0x6F, 0x16, 0xBC, 0x92, 0x42, 0x0A, 0x41, 0x64, 0xCD, 0x52, 0xB6, 0x34, 0x4D, 0xAE,
|
||||||
|
0xC0, 0x2E, 0xDE, 0xA4, 0xDF, 0x27, 0x68, 0x3C, 0xC1, 0xA0, 0x60, 0xAD, 0x43, 0xF3, 0xFC, 0x86,
|
||||||
|
0xC1, 0x3E, 0x6C, 0x46, 0xF7, 0x7C, 0x29, 0x9F, 0xFA, 0xFD, 0xF0, 0xE3, 0xCE, 0x64, 0xE7, 0x35,
|
||||||
|
0xF2, 0xF6, 0x56, 0x56, 0x6F, 0x6D, 0xF1, 0xE2, 0x42, 0xB0, 0x83, 0x40, 0xA5, 0xC3, 0x20, 0x2B,
|
||||||
|
0xCC, 0x9A, 0xAE, 0xCA, 0xED, 0x4D, 0x70, 0x30, 0xA8, 0x70, 0x1C, 0x70, 0xFD, 0x13, 0x63, 0x29,
|
||||||
|
0x02, 0x79, 0xEA, 0xD2, 0xA7, 0xAF, 0x35, 0x28, 0x32, 0x1C, 0x7B, 0xE6, 0x2F, 0x1A, 0xAA, 0x40,
|
||||||
|
0x7E, 0x32, 0x8C, 0x27, 0x42, 0xFE, 0x82, 0x78, 0xEC, 0x0D, 0xEB, 0xE6, 0x83, 0x4B, 0x6D, 0x81,
|
||||||
|
0x04, 0x40, 0x1A, 0x9E, 0x9A, 0x67, 0xF6, 0x72, 0x29, 0xFA, 0x04, 0xF0, 0x9D, 0xE4, 0xF4, 0x03
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> NcaHdrFixedKeyModulus1Prod => new byte[]
|
||||||
|
{
|
||||||
|
0xAD, 0xE3, 0xE1, 0xFA, 0x04, 0x35, 0xE5, 0xB6, 0xDD, 0x49, 0xEA, 0x89, 0x29, 0xB1, 0xFF, 0xB6,
|
||||||
|
0x43, 0xDF, 0xCA, 0x96, 0xA0, 0x4A, 0x13, 0xDF, 0x43, 0xD9, 0x94, 0x97, 0x96, 0x43, 0x65, 0x48,
|
||||||
|
0x70, 0x58, 0x33, 0xA2, 0x7D, 0x35, 0x7B, 0x96, 0x74, 0x5E, 0x0B, 0x5C, 0x32, 0x18, 0x14, 0x24,
|
||||||
|
0xC2, 0x58, 0xB3, 0x6C, 0x22, 0x7A, 0xA1, 0xB7, 0xCB, 0x90, 0xA7, 0xA3, 0xF9, 0x7D, 0x45, 0x16,
|
||||||
|
0xA5, 0xC8, 0xED, 0x8F, 0xAD, 0x39, 0x5E, 0x9E, 0x4B, 0x51, 0x68, 0x7D, 0xF8, 0x0C, 0x35, 0xC6,
|
||||||
|
0x3F, 0x91, 0xAE, 0x44, 0xA5, 0x92, 0x30, 0x0D, 0x46, 0xF8, 0x40, 0xFF, 0xD0, 0xFF, 0x06, 0xD2,
|
||||||
|
0x1C, 0x7F, 0x96, 0x18, 0xDC, 0xB7, 0x1D, 0x66, 0x3E, 0xD1, 0x73, 0xBC, 0x15, 0x8A, 0x2F, 0x94,
|
||||||
|
0xF3, 0x00, 0xC1, 0x83, 0xF1, 0xCD, 0xD7, 0x81, 0x88, 0xAB, 0xDF, 0x8C, 0xEF, 0x97, 0xDD, 0x1B,
|
||||||
|
0x17, 0x5F, 0x58, 0xF6, 0x9A, 0xE9, 0xE8, 0xC2, 0x2F, 0x38, 0x15, 0xF5, 0x21, 0x07, 0xF8, 0x37,
|
||||||
|
0x90, 0x5D, 0x2E, 0x02, 0x40, 0x24, 0x15, 0x0D, 0x25, 0xB7, 0x26, 0x5D, 0x09, 0xCC, 0x4C, 0xF4,
|
||||||
|
0xF2, 0x1B, 0x94, 0x70, 0x5A, 0x9E, 0xEE, 0xED, 0x77, 0x77, 0xD4, 0x51, 0x99, 0xF5, 0xDC, 0x76,
|
||||||
|
0x1E, 0xE3, 0x6C, 0x8C, 0xD1, 0x12, 0xD4, 0x57, 0xD1, 0xB6, 0x83, 0xE4, 0xE4, 0xFE, 0xDA, 0xE9,
|
||||||
|
0xB4, 0x3B, 0x33, 0xE5, 0x37, 0x8A, 0xDF, 0xB5, 0x7F, 0x89, 0xF1, 0x9B, 0x9E, 0xB0, 0x15, 0xB2,
|
||||||
|
0x3A, 0xFE, 0xEA, 0x61, 0x84, 0x5B, 0x7D, 0x4B, 0x23, 0x12, 0x0B, 0x83, 0x12, 0xF2, 0x22, 0x6B,
|
||||||
|
0xB9, 0x22, 0x96, 0x4B, 0x26, 0x0B, 0x63, 0x5E, 0x96, 0x57, 0x52, 0xA3, 0x67, 0x64, 0x22, 0xCA,
|
||||||
|
0xD0, 0x56, 0x3E, 0x74, 0xB5, 0x98, 0x1F, 0x0D, 0xF8, 0xB3, 0x34, 0xE6, 0x98, 0x68, 0x5A, 0xAD
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> AcidFixedKeyModulus0Prod => new byte[]
|
||||||
|
{
|
||||||
|
0xDD, 0xC8, 0xDD, 0xF2, 0x4E, 0x6D, 0xF0, 0xCA, 0x9E, 0xC7, 0x5D, 0xC7, 0x7B, 0xAD, 0xFE, 0x7D,
|
||||||
|
0x23, 0x89, 0x69, 0xB6, 0xF2, 0x06, 0xA2, 0x02, 0x88, 0xE1, 0x55, 0x91, 0xAB, 0xCB, 0x4D, 0x50,
|
||||||
|
0x2E, 0xFC, 0x9D, 0x94, 0x76, 0xD6, 0x4C, 0xD8, 0xFF, 0x10, 0xFA, 0x5E, 0x93, 0x0A, 0xB4, 0x57,
|
||||||
|
0xAC, 0x51, 0xC7, 0x16, 0x66, 0xF4, 0x1A, 0x54, 0xC2, 0xC5, 0x04, 0x3D, 0x1B, 0xFE, 0x30, 0x20,
|
||||||
|
0x8A, 0xAC, 0x6F, 0x6F, 0xF5, 0xC7, 0xB6, 0x68, 0xB8, 0xC9, 0x40, 0x6B, 0x42, 0xAD, 0x11, 0x21,
|
||||||
|
0xE7, 0x8B, 0xE9, 0x75, 0x01, 0x86, 0xE4, 0x48, 0x9B, 0x0A, 0x0A, 0xF8, 0x7F, 0xE8, 0x87, 0xF2,
|
||||||
|
0x82, 0x01, 0xE6, 0xA3, 0x0F, 0xE4, 0x66, 0xAE, 0x83, 0x3F, 0x4E, 0x9F, 0x5E, 0x01, 0x30, 0xA4,
|
||||||
|
0x00, 0xB9, 0x9A, 0xAE, 0x5F, 0x03, 0xCC, 0x18, 0x60, 0xE5, 0xEF, 0x3B, 0x5E, 0x15, 0x16, 0xFE,
|
||||||
|
0x1C, 0x82, 0x78, 0xB5, 0x2F, 0x47, 0x7C, 0x06, 0x66, 0x88, 0x5D, 0x35, 0xA2, 0x67, 0x20, 0x10,
|
||||||
|
0xE7, 0x6C, 0x43, 0x68, 0xD3, 0xE4, 0x5A, 0x68, 0x2A, 0x5A, 0xE2, 0x6D, 0x73, 0xB0, 0x31, 0x53,
|
||||||
|
0x1C, 0x20, 0x09, 0x44, 0xF5, 0x1A, 0x9D, 0x22, 0xBE, 0x12, 0xA1, 0x77, 0x11, 0xE2, 0xA1, 0xCD,
|
||||||
|
0x40, 0x9A, 0xA2, 0x8B, 0x60, 0x9B, 0xEF, 0xA0, 0xD3, 0x48, 0x63, 0xA2, 0xF8, 0xA3, 0x2C, 0x08,
|
||||||
|
0x56, 0x52, 0x2E, 0x60, 0x19, 0x67, 0x5A, 0xA7, 0x9F, 0xDC, 0x3F, 0x3F, 0x69, 0x2B, 0x31, 0x6A,
|
||||||
|
0xB7, 0x88, 0x4A, 0x14, 0x84, 0x80, 0x33, 0x3C, 0x9D, 0x44, 0xB7, 0x3F, 0x4C, 0xE1, 0x75, 0xEA,
|
||||||
|
0x37, 0xEA, 0xE8, 0x1E, 0x7C, 0x77, 0xB7, 0xC6, 0x1A, 0xA2, 0xF0, 0x9F, 0x10, 0x61, 0xCD, 0x7B,
|
||||||
|
0x5B, 0x32, 0x4C, 0x37, 0xEF, 0xB1, 0x71, 0x68, 0x53, 0x0A, 0xED, 0x51, 0x7D, 0x35, 0x22, 0xFD
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> AcidFixedKeyModulus1Prod => new byte[]
|
||||||
|
{
|
||||||
|
0xE7, 0xAA, 0x25, 0xC8, 0x01, 0xA5, 0x14, 0x6B, 0x01, 0x60, 0x3E, 0xD9, 0x96, 0x5A, 0xBF, 0x90,
|
||||||
|
0xAC, 0xA7, 0xFD, 0x9B, 0x5B, 0xBD, 0x8A, 0x26, 0xB0, 0xCB, 0x20, 0x28, 0x9A, 0x72, 0x12, 0xF5,
|
||||||
|
0x20, 0x65, 0xB3, 0xB9, 0x84, 0x58, 0x1F, 0x27, 0xBC, 0x7C, 0xA2, 0xC9, 0x9E, 0x18, 0x95, 0xCF,
|
||||||
|
0xC2, 0x73, 0x2E, 0x74, 0x8C, 0x66, 0xE5, 0x9E, 0x79, 0x2B, 0xB8, 0x07, 0x0C, 0xB0, 0x4E, 0x8E,
|
||||||
|
0xAB, 0x85, 0x21, 0x42, 0xC4, 0xC5, 0x6D, 0x88, 0x9C, 0xDB, 0x15, 0x95, 0x3F, 0x80, 0xDB, 0x7A,
|
||||||
|
0x9A, 0x7D, 0x41, 0x56, 0x25, 0x17, 0x18, 0x42, 0x4D, 0x8C, 0xAC, 0xA5, 0x7B, 0xDB, 0x42, 0x5D,
|
||||||
|
0x59, 0x35, 0x45, 0x5D, 0x8A, 0x02, 0xB5, 0x70, 0xC0, 0x72, 0x35, 0x46, 0xD0, 0x1D, 0x60, 0x01,
|
||||||
|
0x4A, 0xCC, 0x1C, 0x46, 0xD3, 0xD6, 0x35, 0x52, 0xD6, 0xE1, 0xF8, 0x3B, 0x5D, 0xEA, 0xDD, 0xB8,
|
||||||
|
0xFE, 0x7D, 0x50, 0xCB, 0x35, 0x23, 0x67, 0x8B, 0xB6, 0xE4, 0x74, 0xD2, 0x60, 0xFC, 0xFD, 0x43,
|
||||||
|
0xBF, 0x91, 0x08, 0x81, 0xC5, 0x4F, 0x5D, 0x16, 0x9A, 0xC4, 0x9A, 0xC6, 0xF6, 0xF3, 0xE1, 0xF6,
|
||||||
|
0x5C, 0x07, 0xAA, 0x71, 0x6C, 0x13, 0xA4, 0xB1, 0xB3, 0x66, 0xBF, 0x90, 0x4C, 0x3D, 0xA2, 0xC4,
|
||||||
|
0x0B, 0xB8, 0x3D, 0x7A, 0x8C, 0x19, 0xFA, 0xFF, 0x6B, 0xB9, 0x1F, 0x02, 0xCC, 0xB6, 0xD3, 0x0C,
|
||||||
|
0x7D, 0x19, 0x1F, 0x47, 0xF9, 0xC7, 0x40, 0x01, 0xFA, 0x46, 0xEA, 0x0B, 0xD4, 0x02, 0xE0, 0x3D,
|
||||||
|
0x30, 0x9A, 0x1A, 0x0F, 0xEA, 0xA7, 0x66, 0x55, 0xF7, 0xCB, 0x28, 0xE2, 0xBB, 0x99, 0xE4, 0x83,
|
||||||
|
0xC3, 0x43, 0x03, 0xEE, 0xDC, 0x1F, 0x02, 0x23, 0xDD, 0xD1, 0x2D, 0x39, 0xA4, 0x65, 0x75, 0x03,
|
||||||
|
0xEF, 0x37, 0x9C, 0x06, 0xD6, 0xFA, 0xA1, 0x15, 0xF0, 0xDB, 0x17, 0x47, 0x26, 0x4F, 0x49, 0x03
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> Package2FixedKeyModulusProd => new byte[]
|
||||||
|
{
|
||||||
|
0x8D, 0x13, 0xA7, 0x77, 0x6A, 0xE5, 0xDC, 0xC0, 0x3B, 0x25, 0xD0, 0x58, 0xE4, 0x20, 0x69, 0x59,
|
||||||
|
0x55, 0x4B, 0xAB, 0x70, 0x40, 0x08, 0x28, 0x07, 0xA8, 0xA7, 0xFD, 0x0F, 0x31, 0x2E, 0x11, 0xFE,
|
||||||
|
0x47, 0xA0, 0xF9, 0x9D, 0xDF, 0x80, 0xDB, 0x86, 0x5A, 0x27, 0x89, 0xCD, 0x97, 0x6C, 0x85, 0xC5,
|
||||||
|
0x6C, 0x39, 0x7F, 0x41, 0xF2, 0xFF, 0x24, 0x20, 0xC3, 0x95, 0xA6, 0xF7, 0x9D, 0x4A, 0x45, 0x74,
|
||||||
|
0x8B, 0x5D, 0x28, 0x8A, 0xC6, 0x99, 0x35, 0x68, 0x85, 0xA5, 0x64, 0x32, 0x80, 0x9F, 0xD3, 0x48,
|
||||||
|
0x39, 0xA2, 0x1D, 0x24, 0x67, 0x69, 0xDF, 0x75, 0xAC, 0x12, 0xB5, 0xBD, 0xC3, 0x29, 0x90, 0xBE,
|
||||||
|
0x37, 0xE4, 0xA0, 0x80, 0x9A, 0xBE, 0x36, 0xBF, 0x1F, 0x2C, 0xAB, 0x2B, 0xAD, 0xF5, 0x97, 0x32,
|
||||||
|
0x9A, 0x42, 0x9D, 0x09, 0x8B, 0x08, 0xF0, 0x63, 0x47, 0xA3, 0xE9, 0x1B, 0x36, 0xD8, 0x2D, 0x8A,
|
||||||
|
0xD7, 0xE1, 0x54, 0x11, 0x95, 0xE4, 0x45, 0x88, 0x69, 0x8A, 0x2B, 0x35, 0xCE, 0xD0, 0xA5, 0x0B,
|
||||||
|
0xD5, 0x5D, 0xAC, 0xDB, 0xAF, 0x11, 0x4D, 0xCA, 0xB8, 0x1E, 0xE7, 0x01, 0x9E, 0xF4, 0x46, 0xA3,
|
||||||
|
0x8A, 0x94, 0x6D, 0x76, 0xBD, 0x8A, 0xC8, 0x3B, 0xD2, 0x31, 0x58, 0x0C, 0x79, 0xA8, 0x26, 0xE9,
|
||||||
|
0xD1, 0x79, 0x9C, 0xCB, 0xD4, 0x2B, 0x6A, 0x4F, 0xC6, 0xCC, 0xCF, 0x90, 0xA7, 0xB9, 0x98, 0x47,
|
||||||
|
0xFD, 0xFA, 0x4C, 0x6C, 0x6F, 0x81, 0x87, 0x3B, 0xCA, 0xB8, 0x50, 0xF6, 0x3E, 0x39, 0x5D, 0x4D,
|
||||||
|
0x97, 0x3F, 0x0F, 0x35, 0x39, 0x53, 0xFB, 0xFA, 0xCD, 0xAB, 0xA8, 0x7A, 0x62, 0x9A, 0x3F, 0xF2,
|
||||||
|
0x09, 0x27, 0x96, 0x3F, 0x07, 0x9A, 0x91, 0xF7, 0x16, 0xBF, 0xC6, 0x3A, 0x82, 0x5A, 0x4B, 0xCF,
|
||||||
|
0x49, 0x50, 0x95, 0x8C, 0x55, 0x80, 0x7E, 0x39, 0xB1, 0x48, 0x05, 0x1E, 0x21, 0xC7, 0x24, 0x4F
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> NcaHdrFixedKeyModulus0Dev => new byte[]
|
||||||
|
{
|
||||||
|
0xD8, 0xF1, 0x18, 0xEF, 0x32, 0x72, 0x4C, 0xA7, 0x47, 0x4C, 0xB9, 0xEA, 0xB3, 0x04, 0xA8, 0xA4,
|
||||||
|
0xAC, 0x99, 0x08, 0x08, 0x04, 0xBF, 0x68, 0x57, 0xB8, 0x43, 0x94, 0x2B, 0xC7, 0xB9, 0x66, 0x49,
|
||||||
|
0x85, 0xE5, 0x8A, 0x9B, 0xC1, 0x00, 0x9A, 0x6A, 0x8D, 0xD0, 0xEF, 0xCE, 0xFF, 0x86, 0xC8, 0x5C,
|
||||||
|
0x5D, 0xE9, 0x53, 0x7B, 0x19, 0x2A, 0xA8, 0xC0, 0x22, 0xD1, 0xF3, 0x22, 0x0A, 0x50, 0xF2, 0x2B,
|
||||||
|
0x65, 0x05, 0x1B, 0x9E, 0xEC, 0x61, 0xB5, 0x63, 0xA3, 0x6F, 0x3B, 0xBA, 0x63, 0x3A, 0x53, 0xF4,
|
||||||
|
0x49, 0x2F, 0xCF, 0x03, 0xCC, 0xD7, 0x50, 0x82, 0x1B, 0x29, 0x4F, 0x08, 0xDE, 0x1B, 0x6D, 0x47,
|
||||||
|
0x4F, 0xA8, 0xB6, 0x6A, 0x26, 0xA0, 0x83, 0x3F, 0x1A, 0xAF, 0x83, 0x8F, 0x0E, 0x17, 0x3F, 0xFE,
|
||||||
|
0x44, 0x1C, 0x56, 0x94, 0x2E, 0x49, 0x83, 0x83, 0x03, 0xE9, 0xB6, 0xAD, 0xD5, 0xDE, 0xE3, 0x2D,
|
||||||
|
0xA1, 0xD9, 0x66, 0x20, 0x5D, 0x1F, 0x5E, 0x96, 0x5D, 0x5B, 0x55, 0x0D, 0xD4, 0xB4, 0x77, 0x6E,
|
||||||
|
0xAE, 0x1B, 0x69, 0xF3, 0xA6, 0x61, 0x0E, 0x51, 0x62, 0x39, 0x28, 0x63, 0x75, 0x76, 0xBF, 0xB0,
|
||||||
|
0xD2, 0x22, 0xEF, 0x98, 0x25, 0x02, 0x05, 0xC0, 0xD7, 0x6A, 0x06, 0x2C, 0xA5, 0xD8, 0x5A, 0x9D,
|
||||||
|
0x7A, 0xA4, 0x21, 0x55, 0x9F, 0xF9, 0x3E, 0xBF, 0x16, 0xF6, 0x07, 0xC2, 0xB9, 0x6E, 0x87, 0x9E,
|
||||||
|
0xB5, 0x1C, 0xBE, 0x97, 0xFA, 0x82, 0x7E, 0xED, 0x30, 0xD4, 0x66, 0x3F, 0xDE, 0xD8, 0x1B, 0x4B,
|
||||||
|
0x15, 0xD9, 0xFB, 0x2F, 0x50, 0xF0, 0x9D, 0x1D, 0x52, 0x4C, 0x1C, 0x4D, 0x8D, 0xAE, 0x85, 0x1E,
|
||||||
|
0xEA, 0x7F, 0x86, 0xF3, 0x0B, 0x7B, 0x87, 0x81, 0x98, 0x23, 0x80, 0x63, 0x4F, 0x2F, 0xB0, 0x62,
|
||||||
|
0xCC, 0x6E, 0xD2, 0x46, 0x13, 0x65, 0x2B, 0xD6, 0x44, 0x33, 0x59, 0xB5, 0x8F, 0xB9, 0x4A, 0xA9
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> NcaHdrFixedKeyModulus1Dev => new byte[]
|
||||||
|
{
|
||||||
|
0x9A, 0xBC, 0x88, 0xBD, 0x0A, 0xBE, 0xD7, 0x0C, 0x9B, 0x42, 0x75, 0x65, 0x38, 0x5E, 0xD1, 0x01,
|
||||||
|
0xCD, 0x12, 0xAE, 0xEA, 0xE9, 0x4B, 0xDB, 0xB4, 0x5E, 0x36, 0x10, 0x96, 0xDA, 0x3D, 0x2E, 0x66,
|
||||||
|
0xD3, 0x99, 0x13, 0x8A, 0xBE, 0x67, 0x41, 0xC8, 0x93, 0xD9, 0x3E, 0x42, 0xCE, 0x34, 0xCE, 0x96,
|
||||||
|
0xFA, 0x0B, 0x23, 0xCC, 0x2C, 0xDF, 0x07, 0x3F, 0x3B, 0x24, 0x4B, 0x12, 0x67, 0x3A, 0x29, 0x36,
|
||||||
|
0xA3, 0xAA, 0x06, 0xF0, 0x65, 0xA5, 0x85, 0xBA, 0xFD, 0x12, 0xEC, 0xF1, 0x60, 0x67, 0xF0, 0x8F,
|
||||||
|
0xD3, 0x5B, 0x01, 0x1B, 0x1E, 0x84, 0xA3, 0x5C, 0x65, 0x36, 0xF9, 0x23, 0x7E, 0xF3, 0x26, 0x38,
|
||||||
|
0x64, 0x98, 0xBA, 0xE4, 0x19, 0x91, 0x4C, 0x02, 0xCF, 0xC9, 0x6D, 0x86, 0xEC, 0x1D, 0x41, 0x69,
|
||||||
|
0xDD, 0x56, 0xEA, 0x5C, 0xA3, 0x2A, 0x58, 0xB4, 0x39, 0xCC, 0x40, 0x31, 0xFD, 0xFB, 0x42, 0x74,
|
||||||
|
0xF8, 0xEC, 0xEA, 0x00, 0xF0, 0xD9, 0x28, 0xEA, 0xFA, 0x2D, 0x00, 0xE1, 0x43, 0x53, 0xC6, 0x32,
|
||||||
|
0xF4, 0xA2, 0x07, 0xD4, 0x5F, 0xD4, 0xCB, 0xAC, 0xCA, 0xFF, 0xDF, 0x84, 0xD2, 0x86, 0x14, 0x3C,
|
||||||
|
0xDE, 0x22, 0x75, 0xA5, 0x73, 0xFF, 0x68, 0x07, 0x4A, 0xF9, 0x7C, 0x2C, 0xCC, 0xDE, 0x45, 0xB6,
|
||||||
|
0x54, 0x82, 0x90, 0x36, 0x1F, 0x2C, 0x51, 0x96, 0xC5, 0x0A, 0x53, 0x5B, 0xF0, 0x8B, 0x4A, 0xAA,
|
||||||
|
0x3B, 0x68, 0x97, 0x19, 0x17, 0x1F, 0x01, 0xB8, 0xED, 0xB9, 0x9A, 0x5E, 0x08, 0xC5, 0x20, 0x1E,
|
||||||
|
0x6A, 0x09, 0xF0, 0xE9, 0x73, 0xA3, 0xBE, 0x10, 0x06, 0x02, 0xE9, 0xFB, 0x85, 0xFA, 0x5F, 0x01,
|
||||||
|
0xAC, 0x60, 0xE0, 0xED, 0x7D, 0xB9, 0x49, 0xA8, 0x9E, 0x98, 0x7D, 0x91, 0x40, 0x05, 0xCF, 0xF9,
|
||||||
|
0x1A, 0xFC, 0x40, 0x22, 0xA8, 0x96, 0x5B, 0xB0, 0xDC, 0x7A, 0xF5, 0xB7, 0xE9, 0x91, 0x4C, 0x49
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> AcidFixedKeyModulus0Dev => new byte[]
|
||||||
|
{
|
||||||
|
0xD6, 0x34, 0xA5, 0x78, 0x6C, 0x68, 0xCE, 0x5A, 0xC2, 0x37, 0x17, 0xF3, 0x82, 0x45, 0xC6, 0x89,
|
||||||
|
0xE1, 0x2D, 0x06, 0x67, 0xBF, 0xB4, 0x06, 0x19, 0x55, 0x6B, 0x27, 0x66, 0x0C, 0xA4, 0xB5, 0x87,
|
||||||
|
0x81, 0x25, 0xF4, 0x30, 0xBC, 0x53, 0x08, 0x68, 0xA2, 0x48, 0x49, 0x8C, 0x3F, 0x38, 0x40, 0x9C,
|
||||||
|
0xC4, 0x26, 0xF4, 0x79, 0xE2, 0xA1, 0x85, 0xF5, 0x5C, 0x7F, 0x58, 0xBA, 0xA6, 0x1C, 0xA0, 0x8B,
|
||||||
|
0x84, 0x16, 0x14, 0x6F, 0x85, 0xD9, 0x7C, 0xE1, 0x3C, 0x67, 0x22, 0x1E, 0xFB, 0xD8, 0xA7, 0xA5,
|
||||||
|
0x9A, 0xBF, 0xEC, 0x0E, 0xCF, 0x96, 0x7E, 0x85, 0xC2, 0x1D, 0x49, 0x5D, 0x54, 0x26, 0xCB, 0x32,
|
||||||
|
0x7C, 0xF6, 0xBB, 0x58, 0x03, 0x80, 0x2B, 0x5D, 0xF7, 0xFB, 0xD1, 0x9D, 0xC7, 0xC6, 0x2E, 0x53,
|
||||||
|
0xC0, 0x6F, 0x39, 0x2C, 0x1F, 0xA9, 0x92, 0xF2, 0x4D, 0x7D, 0x4E, 0x74, 0xFF, 0xE4, 0xEF, 0xE4,
|
||||||
|
0x7C, 0x3D, 0x34, 0x2A, 0x71, 0xA4, 0x97, 0x59, 0xFF, 0x4F, 0xA2, 0xF4, 0x66, 0x78, 0xD8, 0xBA,
|
||||||
|
0x99, 0xE3, 0xE6, 0xDB, 0x54, 0xB9, 0xE9, 0x54, 0xA1, 0x70, 0xFC, 0x05, 0x1F, 0x11, 0x67, 0x4B,
|
||||||
|
0x26, 0x8C, 0x0C, 0x3E, 0x03, 0xD2, 0xA3, 0x55, 0x5C, 0x7D, 0xC0, 0x5D, 0x9D, 0xFF, 0x13, 0x2F,
|
||||||
|
0xFD, 0x19, 0xBF, 0xED, 0x44, 0xC3, 0x8C, 0xA7, 0x28, 0xCB, 0xE5, 0xE0, 0xB1, 0xA7, 0x9C, 0x33,
|
||||||
|
0x8D, 0xB8, 0x6E, 0xDE, 0x87, 0x18, 0x22, 0x60, 0xC4, 0xAE, 0xF2, 0x87, 0x9F, 0xCE, 0x09, 0x5C,
|
||||||
|
0xB5, 0x99, 0xA5, 0x9F, 0x49, 0xF2, 0xD7, 0x58, 0xFA, 0xF9, 0xC0, 0x25, 0x7D, 0xD6, 0xCB, 0xF3,
|
||||||
|
0xD8, 0x6C, 0xA2, 0x69, 0x91, 0x68, 0x73, 0xB1, 0x94, 0x6F, 0xA3, 0xF3, 0xB9, 0x7D, 0xF8, 0xE0,
|
||||||
|
0x72, 0x9E, 0x93, 0x7B, 0x7A, 0xA2, 0x57, 0x60, 0xB7, 0x5B, 0xA9, 0x84, 0xAE, 0x64, 0x88, 0x69
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> AcidFixedKeyModulus1Dev => new byte[]
|
||||||
|
{
|
||||||
|
0xBC, 0xA5, 0x6A, 0x7E, 0xEA, 0x38, 0x34, 0x62, 0xA6, 0x10, 0x18, 0x3C, 0xE1, 0x63, 0x7B, 0xF0,
|
||||||
|
0xD3, 0x08, 0x8C, 0xF5, 0xC5, 0xC4, 0xC7, 0x93, 0xE9, 0xD9, 0xE6, 0x32, 0xF3, 0xA0, 0xF6, 0x6E,
|
||||||
|
0x8A, 0x98, 0x76, 0x47, 0x33, 0x47, 0x65, 0x02, 0x70, 0xDC, 0x86, 0x5F, 0x3D, 0x61, 0x5A, 0x70,
|
||||||
|
0xBC, 0x5A, 0xCA, 0xCA, 0x50, 0xAD, 0x61, 0x7E, 0xC9, 0xEC, 0x27, 0xFF, 0xE8, 0x64, 0x42, 0x9A,
|
||||||
|
0xEE, 0xBE, 0xC3, 0xD1, 0x0B, 0xC0, 0xE9, 0xBF, 0x83, 0x8D, 0xC0, 0x0C, 0xD8, 0x00, 0x5B, 0x76,
|
||||||
|
0x90, 0xD2, 0x4B, 0x30, 0x84, 0x35, 0x8B, 0x1E, 0x20, 0xB7, 0xE4, 0xDC, 0x63, 0xE5, 0xDF, 0xCD,
|
||||||
|
0x00, 0x5F, 0x81, 0x5F, 0x67, 0xC5, 0x8B, 0xDF, 0xFC, 0xE1, 0x37, 0x5F, 0x07, 0xD9, 0xDE, 0x4F,
|
||||||
|
0xE6, 0x7B, 0xF1, 0xFB, 0xA1, 0x5A, 0x71, 0x40, 0xFE, 0xBA, 0x1E, 0xAE, 0x13, 0x22, 0xD2, 0xFE,
|
||||||
|
0x37, 0xA2, 0xB6, 0x8B, 0xAB, 0xEB, 0x84, 0x81, 0x4E, 0x7C, 0x1E, 0x02, 0xD1, 0xFB, 0xD7, 0x5D,
|
||||||
|
0x11, 0x84, 0x64, 0xD2, 0x4D, 0xBB, 0x50, 0x00, 0x67, 0x54, 0xE2, 0x77, 0x89, 0xBA, 0x0B, 0xE7,
|
||||||
|
0x05, 0x57, 0x9A, 0x22, 0x5A, 0xEC, 0x76, 0x1C, 0xFD, 0xE8, 0xA8, 0x18, 0x16, 0x41, 0x65, 0x03,
|
||||||
|
0xFA, 0xC4, 0xA6, 0x31, 0x5C, 0x1A, 0x7F, 0xAB, 0x11, 0xC8, 0x4A, 0x99, 0xB9, 0xE6, 0xCF, 0x62,
|
||||||
|
0x21, 0xA6, 0x72, 0x47, 0xDB, 0xBA, 0x96, 0x26, 0x4E, 0x2E, 0xD4, 0x8C, 0x46, 0xD6, 0xA7, 0x1A,
|
||||||
|
0x6C, 0x32, 0xA7, 0xDF, 0x85, 0x1C, 0x03, 0xC3, 0x6D, 0xA9, 0xE9, 0x68, 0xF4, 0x17, 0x1E, 0xB2,
|
||||||
|
0x70, 0x2A, 0xA1, 0xE5, 0xE1, 0xF3, 0x8F, 0x6F, 0x63, 0xAC, 0xEB, 0x72, 0x0B, 0x4C, 0x4A, 0x36,
|
||||||
|
0x3C, 0x60, 0x91, 0x9F, 0x6E, 0x1C, 0x71, 0xEA, 0xD0, 0x78, 0x78, 0xA0, 0x2E, 0xC6, 0x32, 0x6B
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> Package2FixedKeyModulusDev => new byte[]
|
||||||
|
{
|
||||||
|
0xB3, 0x65, 0x54, 0xFB, 0x0A, 0xB0, 0x1E, 0x85, 0xA7, 0xF6, 0xCF, 0x91, 0x8E, 0xBA, 0x96, 0x99,
|
||||||
|
0x0D, 0x8B, 0x91, 0x69, 0x2A, 0xEE, 0x01, 0x20, 0x4F, 0x34, 0x5C, 0x2C, 0x4F, 0x4E, 0x37, 0xC7,
|
||||||
|
0xF1, 0x0B, 0xD4, 0xCD, 0xA1, 0x7F, 0x93, 0xF1, 0x33, 0x59, 0xCE, 0xB1, 0xE9, 0xDD, 0x26, 0xE6,
|
||||||
|
0xF3, 0xBB, 0x77, 0x87, 0x46, 0x7A, 0xD6, 0x4E, 0x47, 0x4A, 0xD1, 0x41, 0xB7, 0x79, 0x4A, 0x38,
|
||||||
|
0x06, 0x6E, 0xCF, 0x61, 0x8F, 0xCD, 0xC1, 0x40, 0x0B, 0xFA, 0x26, 0xDC, 0xC0, 0x34, 0x51, 0x83,
|
||||||
|
0xD9, 0x3B, 0x11, 0x54, 0x3B, 0x96, 0x27, 0x32, 0x9A, 0x95, 0xBE, 0x1E, 0x68, 0x11, 0x50, 0xA0,
|
||||||
|
0x6B, 0x10, 0xA8, 0x83, 0x8B, 0xF5, 0xFC, 0xBC, 0x90, 0x84, 0x7A, 0x5A, 0x5C, 0x43, 0x52, 0xE6,
|
||||||
|
0xC8, 0x26, 0xE9, 0xFE, 0x06, 0xA0, 0x8B, 0x53, 0x0F, 0xAF, 0x1E, 0xC4, 0x1C, 0x0B, 0xCF, 0x50,
|
||||||
|
0x1A, 0xA4, 0xF3, 0x5C, 0xFB, 0xF0, 0x97, 0xE4, 0xDE, 0x32, 0x0A, 0x9F, 0xE3, 0x5A, 0xAA, 0xB7,
|
||||||
|
0x44, 0x7F, 0x5C, 0x33, 0x60, 0xB9, 0x0F, 0x22, 0x2D, 0x33, 0x2A, 0xE9, 0x69, 0x79, 0x31, 0x42,
|
||||||
|
0x8F, 0xE4, 0x3A, 0x13, 0x8B, 0xE7, 0x26, 0xBD, 0x08, 0x87, 0x6C, 0xA6, 0xF2, 0x73, 0xF6, 0x8E,
|
||||||
|
0xA7, 0xF2, 0xFE, 0xFB, 0x6C, 0x28, 0x66, 0x0D, 0xBD, 0xD7, 0xEB, 0x42, 0xA8, 0x78, 0xE6, 0xB8,
|
||||||
|
0x6B, 0xAE, 0xC7, 0xA9, 0xE2, 0x40, 0x6E, 0x89, 0x20, 0x82, 0x25, 0x8E, 0x3C, 0x6A, 0x60, 0xD7,
|
||||||
|
0xF3, 0x56, 0x8E, 0xEC, 0x8D, 0x51, 0x8A, 0x63, 0x3C, 0x04, 0x78, 0x23, 0x0E, 0x90, 0x0C, 0xB4,
|
||||||
|
0xE7, 0x86, 0x3B, 0x4F, 0x8E, 0x13, 0x09, 0x47, 0x32, 0x0E, 0x04, 0xB8, 0x4D, 0x5B, 0xB0, 0x46,
|
||||||
|
0x71, 0xB0, 0x5C, 0xF4, 0xAD, 0x63, 0x4F, 0xC5, 0xE2, 0xAC, 0x1E, 0xC4, 0x33, 0x96, 0x09, 0x7B
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,25 +2,24 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using Octokit;
|
using Octokit;
|
||||||
|
|
||||||
namespace LibHacBuild.CodeGen.Stage2
|
namespace LibHacBuild.CodeGen.Stage2;
|
||||||
|
|
||||||
|
// Some codegen depends on classes in LibHac.
|
||||||
|
// The part that does is split out into a separate project so the main build project
|
||||||
|
// doesn't depend on LibHac.
|
||||||
|
public static class RunStage2
|
||||||
{
|
{
|
||||||
// Some codegen depends on classes in LibHac.
|
private const string SolutionFileName = "LibHac.sln";
|
||||||
// The part that does is split out into a separate project so the main build project
|
public static int Main(string[] args)
|
||||||
// doesn't depend on LibHac.
|
|
||||||
public static class RunStage2
|
|
||||||
{
|
{
|
||||||
private const string SolutionFileName = "LibHac.sln";
|
if (!File.Exists(SolutionFileName))
|
||||||
public static int Main(string[] args)
|
|
||||||
{
|
{
|
||||||
if (!File.Exists(SolutionFileName))
|
Console.Error.WriteLine($"Could not find the solution file {SolutionFileName}.");
|
||||||
{
|
return 1;
|
||||||
Console.Error.WriteLine($"Could not find the solution file {SolutionFileName}.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
KeysCodeGen.Run();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeysCodeGen.Run();
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
<LangVersion>10.0</LangVersion>
|
||||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
<RootNamespace>LibHacBuild.CodeGen</RootNamespace>
|
<RootNamespace>LibHacBuild.CodeGen</RootNamespace>
|
||||||
<IsPackable>False</IsPackable>
|
<IsPackable>False</IsPackable>
|
||||||
|
@ -11,126 +11,125 @@ using Nuke.Common.IO;
|
|||||||
using Nuke.Common.Tools.NuGet;
|
using Nuke.Common.Tools.NuGet;
|
||||||
using static Nuke.Common.IO.FileSystemTasks;
|
using static Nuke.Common.IO.FileSystemTasks;
|
||||||
|
|
||||||
namespace LibHacBuild
|
namespace LibHacBuild;
|
||||||
|
|
||||||
|
public partial class Build
|
||||||
{
|
{
|
||||||
public partial class Build
|
public void RepackNugetPackage(string path)
|
||||||
{
|
{
|
||||||
public void RepackNugetPackage(string path)
|
AbsolutePath tempDir = TempDirectory / Path.GetFileName(path);
|
||||||
|
AbsolutePath libDir = tempDir / "lib";
|
||||||
|
AbsolutePath relsFile = tempDir / "_rels" / ".rels";
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
AbsolutePath tempDir = TempDirectory / Path.GetFileName(path);
|
EnsureCleanDirectory(tempDir);
|
||||||
AbsolutePath libDir = tempDir / "lib";
|
List<string> fileList = UnzipPackage(path, tempDir);
|
||||||
AbsolutePath relsFile = tempDir / "_rels" / ".rels";
|
|
||||||
|
|
||||||
try
|
string newPsmdcpName = CalcPsmdcpName(libDir);
|
||||||
|
string newPsmdcpPath = RenamePsmdcp(tempDir, newPsmdcpName);
|
||||||
|
EditManifestRelationships(relsFile, newPsmdcpPath);
|
||||||
|
|
||||||
|
int index = fileList.FindIndex(x => x.Contains(".psmdcp"));
|
||||||
|
fileList[index] = newPsmdcpPath;
|
||||||
|
|
||||||
|
IEnumerable<string> files = Directory.EnumerateFiles(tempDir, "*.json", SearchOption.AllDirectories)
|
||||||
|
.Concat(Directory.EnumerateFiles(tempDir, "*.xml", SearchOption.AllDirectories))
|
||||||
|
.Concat(Directory.EnumerateFiles(tempDir, "*.rels", SearchOption.AllDirectories))
|
||||||
|
.Concat(Directory.EnumerateFiles(tempDir, "*.psmdcp", SearchOption.AllDirectories))
|
||||||
|
.Concat(Directory.EnumerateFiles(tempDir, "*.nuspec", SearchOption.AllDirectories));
|
||||||
|
|
||||||
|
foreach (string filename in files)
|
||||||
{
|
{
|
||||||
EnsureCleanDirectory(tempDir);
|
Console.WriteLine(filename);
|
||||||
List<string> fileList = UnzipPackage(path, tempDir);
|
ReplaceLineEndings(filename);
|
||||||
|
|
||||||
string newPsmdcpName = CalcPsmdcpName(libDir);
|
|
||||||
string newPsmdcpPath = RenamePsmdcp(tempDir, newPsmdcpName);
|
|
||||||
EditManifestRelationships(relsFile, newPsmdcpPath);
|
|
||||||
|
|
||||||
int index = fileList.FindIndex(x => x.Contains(".psmdcp"));
|
|
||||||
fileList[index] = newPsmdcpPath;
|
|
||||||
|
|
||||||
IEnumerable<string> files = Directory.EnumerateFiles(tempDir, "*.json", SearchOption.AllDirectories)
|
|
||||||
.Concat(Directory.EnumerateFiles(tempDir, "*.xml", SearchOption.AllDirectories))
|
|
||||||
.Concat(Directory.EnumerateFiles(tempDir, "*.rels", SearchOption.AllDirectories))
|
|
||||||
.Concat(Directory.EnumerateFiles(tempDir, "*.psmdcp", SearchOption.AllDirectories))
|
|
||||||
.Concat(Directory.EnumerateFiles(tempDir, "*.nuspec", SearchOption.AllDirectories));
|
|
||||||
|
|
||||||
foreach (string filename in files)
|
|
||||||
{
|
|
||||||
Console.WriteLine(filename);
|
|
||||||
ReplaceLineEndings(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
ZipDirectory(path, tempDir, fileList);
|
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
|
ZipDirectory(path, tempDir, fileList);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Directory.Delete(tempDir, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> UnzipPackage(string package, string dest)
|
||||||
|
{
|
||||||
|
var fileList = new List<string>();
|
||||||
|
|
||||||
|
UnzipFiles(package, dest);
|
||||||
|
|
||||||
|
using (var s = new ZipInputStream(File.OpenRead(package)))
|
||||||
|
{
|
||||||
|
ZipEntry entry;
|
||||||
|
while ((entry = s.GetNextEntry()) != null)
|
||||||
{
|
{
|
||||||
Directory.Delete(tempDir, true);
|
fileList.Add(entry.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<string> UnzipPackage(string package, string dest)
|
return fileList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CalcPsmdcpName(string libDir)
|
||||||
|
{
|
||||||
|
using (var sha = SHA256.Create())
|
||||||
{
|
{
|
||||||
var fileList = new List<string>();
|
foreach (string file in Directory.EnumerateFiles(libDir))
|
||||||
|
|
||||||
UnzipFiles(package, dest);
|
|
||||||
|
|
||||||
using (var s = new ZipInputStream(File.OpenRead(package)))
|
|
||||||
{
|
{
|
||||||
ZipEntry entry;
|
byte[] data = File.ReadAllBytes(file);
|
||||||
while ((entry = s.GetNextEntry()) != null)
|
sha.TransformBlock(data, 0, data.Length, data, 0);
|
||||||
{
|
|
||||||
fileList.Add(entry.Name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileList;
|
sha.TransformFinalBlock(new byte[0], 0, 0);
|
||||||
}
|
|
||||||
|
|
||||||
public static string CalcPsmdcpName(string libDir)
|
return ToHexString(sha.Hash).ToLower().Substring(0, 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string RenamePsmdcp(string packageDir, string name)
|
||||||
|
{
|
||||||
|
string fileName = Directory.EnumerateFiles(packageDir, "*.psmdcp", SearchOption.AllDirectories).Single();
|
||||||
|
string newFileName = Path.Combine(Path.GetDirectoryName(fileName), name + ".psmdcp");
|
||||||
|
Directory.Move(fileName, newFileName);
|
||||||
|
|
||||||
|
return Path.GetRelativePath(packageDir, newFileName).Replace('\\', '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "PossibleNullReferenceException")]
|
||||||
|
public void EditManifestRelationships(string path, string psmdcpPath)
|
||||||
|
{
|
||||||
|
XDocument doc = XDocument.Load(path);
|
||||||
|
XNamespace ns = doc.Root.GetDefaultNamespace();
|
||||||
|
|
||||||
|
foreach (XElement rs in doc.Root.Elements(ns + "Relationship"))
|
||||||
{
|
{
|
||||||
using (var sha = SHA256.Create())
|
using (var sha = SHA256.Create())
|
||||||
{
|
{
|
||||||
foreach (string file in Directory.EnumerateFiles(libDir))
|
if (rs.Attribute("Target").Value.Contains(".psmdcp"))
|
||||||
{
|
{
|
||||||
byte[] data = File.ReadAllBytes(file);
|
rs.Attribute("Target").Value = "/" + psmdcpPath;
|
||||||
sha.TransformBlock(data, 0, data.Length, data, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sha.TransformFinalBlock(new byte[0], 0, 0);
|
string s = "/" + psmdcpPath + rs.Attribute("Target").Value;
|
||||||
|
byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(s));
|
||||||
return ToHexString(sha.Hash).ToLower().Substring(0, 32);
|
string id = "R" + ToHexString(hash).Substring(0, 16);
|
||||||
|
rs.Attribute("Id").Value = id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string RenamePsmdcp(string packageDir, string name)
|
doc.Save(path);
|
||||||
{
|
}
|
||||||
string fileName = Directory.EnumerateFiles(packageDir, "*.psmdcp", SearchOption.AllDirectories).Single();
|
|
||||||
string newFileName = Path.Combine(Path.GetDirectoryName(fileName), name + ".psmdcp");
|
|
||||||
Directory.Move(fileName, newFileName);
|
|
||||||
|
|
||||||
return Path.GetRelativePath(packageDir, newFileName).Replace('\\', '/');
|
public void SignNupkg(string pkgPath, string password)
|
||||||
}
|
{
|
||||||
|
NuGetTasks.NuGet(
|
||||||
|
$"sign \"{pkgPath}\" -CertificatePath cert.pfx -CertificatePassword {password} -Timestamper http://timestamp.digicert.com",
|
||||||
|
outputFilter: x => x.Replace(password, "hunter2"));
|
||||||
|
}
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "PossibleNullReferenceException")]
|
public static string ToHexString(byte[] arr)
|
||||||
public void EditManifestRelationships(string path, string psmdcpPath)
|
{
|
||||||
{
|
return BitConverter.ToString(arr).ToLower().Replace("-", "");
|
||||||
XDocument doc = XDocument.Load(path);
|
|
||||||
XNamespace ns = doc.Root.GetDefaultNamespace();
|
|
||||||
|
|
||||||
foreach (XElement rs in doc.Root.Elements(ns + "Relationship"))
|
|
||||||
{
|
|
||||||
using (var sha = SHA256.Create())
|
|
||||||
{
|
|
||||||
if (rs.Attribute("Target").Value.Contains(".psmdcp"))
|
|
||||||
{
|
|
||||||
rs.Attribute("Target").Value = "/" + psmdcpPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
string s = "/" + psmdcpPath + rs.Attribute("Target").Value;
|
|
||||||
byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(s));
|
|
||||||
string id = "R" + ToHexString(hash).Substring(0, 16);
|
|
||||||
rs.Attribute("Id").Value = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
doc.Save(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SignNupkg(string pkgPath, string password)
|
|
||||||
{
|
|
||||||
NuGetTasks.NuGet(
|
|
||||||
$"sign \"{pkgPath}\" -CertificatePath cert.pfx -CertificatePassword {password} -Timestamper http://timestamp.digicert.com",
|
|
||||||
outputFilter: x => x.Replace(password, "hunter2"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string ToHexString(byte[] arr)
|
|
||||||
{
|
|
||||||
return BitConverter.ToString(arr).ToLower().Replace("-", "");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
<LangVersion>10.0</LangVersion>
|
||||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
<RootNamespace>LibHacBuild</RootNamespace>
|
<RootNamespace>LibHacBuild</RootNamespace>
|
||||||
<IsPackable>False</IsPackable>
|
<IsPackable>False</IsPackable>
|
||||||
|
@ -3,57 +3,56 @@ using System.Diagnostics;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
|
||||||
namespace LibHac.Account
|
namespace LibHac.Account;
|
||||||
|
|
||||||
|
[DebuggerDisplay("0x{ToString(),nq}")]
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||||
|
public struct Uid : IEquatable<Uid>, IComparable<Uid>, IComparable
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("0x{ToString(),nq}")]
|
public static Uid Zero => default;
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
|
||||||
public struct Uid : IEquatable<Uid>, IComparable<Uid>, IComparable
|
public readonly Id128 Id;
|
||||||
|
|
||||||
|
public Uid(ulong high, ulong low)
|
||||||
{
|
{
|
||||||
public static Uid Zero => default;
|
Id = new Id128(high, low);
|
||||||
|
|
||||||
public readonly Id128 Id;
|
|
||||||
|
|
||||||
public Uid(ulong high, ulong low)
|
|
||||||
{
|
|
||||||
Id = new Id128(high, low);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Uid(ReadOnlySpan<byte> uid)
|
|
||||||
{
|
|
||||||
Id = new Id128(uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return $"{Id.High:X16}{Id.Low:X16}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(Uid other) => Id == other.Id;
|
|
||||||
public override bool Equals(object obj) => obj is Uid other && Equals(other);
|
|
||||||
|
|
||||||
public override int GetHashCode() => Id.GetHashCode();
|
|
||||||
|
|
||||||
public int CompareTo(Uid other) => Id.CompareTo(other.Id);
|
|
||||||
|
|
||||||
public int CompareTo(object obj)
|
|
||||||
{
|
|
||||||
if (obj is null) return 1;
|
|
||||||
return obj is Uid other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(Uid)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ToBytes(Span<byte> output) => Id.ToBytes(output);
|
|
||||||
|
|
||||||
public ReadOnlySpan<byte> AsBytes()
|
|
||||||
{
|
|
||||||
return SpanHelpers.AsByteSpan(ref this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(Uid left, Uid right) => left.Equals(right);
|
|
||||||
public static bool operator !=(Uid left, Uid right) => !left.Equals(right);
|
|
||||||
|
|
||||||
public static bool operator <(Uid left, Uid right) => left.CompareTo(right) < 0;
|
|
||||||
public static bool operator >(Uid left, Uid right) => left.CompareTo(right) > 0;
|
|
||||||
public static bool operator <=(Uid left, Uid right) => left.CompareTo(right) <= 0;
|
|
||||||
public static bool operator >=(Uid left, Uid right) => left.CompareTo(right) >= 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Uid(ReadOnlySpan<byte> uid)
|
||||||
|
{
|
||||||
|
Id = new Id128(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{Id.High:X16}{Id.Low:X16}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(Uid other) => Id == other.Id;
|
||||||
|
public override bool Equals(object obj) => obj is Uid other && Equals(other);
|
||||||
|
|
||||||
|
public override int GetHashCode() => Id.GetHashCode();
|
||||||
|
|
||||||
|
public int CompareTo(Uid other) => Id.CompareTo(other.Id);
|
||||||
|
|
||||||
|
public int CompareTo(object obj)
|
||||||
|
{
|
||||||
|
if (obj is null) return 1;
|
||||||
|
return obj is Uid other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(Uid)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ToBytes(Span<byte> output) => Id.ToBytes(output);
|
||||||
|
|
||||||
|
public ReadOnlySpan<byte> AsBytes()
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsByteSpan(ref this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(Uid left, Uid right) => left.Equals(right);
|
||||||
|
public static bool operator !=(Uid left, Uid right) => !left.Equals(right);
|
||||||
|
|
||||||
|
public static bool operator <(Uid left, Uid right) => left.CompareTo(right) < 0;
|
||||||
|
public static bool operator >(Uid left, Uid right) => left.CompareTo(right) > 0;
|
||||||
|
public static bool operator <=(Uid left, Uid right) => left.CompareTo(right) <= 0;
|
||||||
|
public static bool operator >=(Uid left, Uid right) => left.CompareTo(right) >= 0;
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,22 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace LibHac
|
namespace LibHac;
|
||||||
|
|
||||||
|
public readonly struct ApplicationId : IEquatable<ApplicationId>
|
||||||
{
|
{
|
||||||
public readonly struct ApplicationId : IEquatable<ApplicationId>
|
public static ApplicationId InvalidId => default;
|
||||||
|
|
||||||
|
public readonly ulong Value;
|
||||||
|
|
||||||
|
public ApplicationId(ulong value)
|
||||||
{
|
{
|
||||||
public static ApplicationId InvalidId => default;
|
Value = value;
|
||||||
|
|
||||||
public readonly ulong Value;
|
|
||||||
|
|
||||||
public ApplicationId(ulong value)
|
|
||||||
{
|
|
||||||
Value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(ApplicationId left, ApplicationId right) => left.Value == right.Value;
|
|
||||||
public static bool operator !=(ApplicationId left, ApplicationId right) => left.Value != right.Value;
|
|
||||||
|
|
||||||
public override bool Equals(object obj) => obj is ApplicationId id && Equals(id);
|
|
||||||
public bool Equals(ApplicationId other) => Value == other.Value;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(Value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(ApplicationId left, ApplicationId right) => left.Value == right.Value;
|
||||||
|
public static bool operator !=(ApplicationId left, ApplicationId right) => left.Value != right.Value;
|
||||||
|
|
||||||
|
public override bool Equals(object obj) => obj is ApplicationId id && Equals(id);
|
||||||
|
public bool Equals(ApplicationId other) => Value == other.Value;
|
||||||
|
public override int GetHashCode() => HashCode.Combine(Value);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Arp
|
namespace LibHac.Arp;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
|
||||||
|
public struct ApplicationLaunchProperty
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
|
[FieldOffset(0x0)] public ApplicationId ApplicationId;
|
||||||
public struct ApplicationLaunchProperty
|
[FieldOffset(0x8)] public uint Version;
|
||||||
{
|
[FieldOffset(0xC)] public Ncm.StorageId BaseStorageId;
|
||||||
[FieldOffset(0x0)] public ApplicationId ApplicationId;
|
[FieldOffset(0xD)] public Ncm.StorageId UpdateStorageId;
|
||||||
[FieldOffset(0x8)] public uint Version;
|
|
||||||
[FieldOffset(0xC)] public Ncm.StorageId BaseStorageId;
|
|
||||||
[FieldOffset(0xD)] public Ncm.StorageId UpdateStorageId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,73 +3,72 @@ using LibHac.Arp.Impl;
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
|
|
||||||
namespace LibHac.Arp
|
namespace LibHac.Arp;
|
||||||
|
|
||||||
|
public class ArpClient : IDisposable
|
||||||
{
|
{
|
||||||
public class ArpClient : IDisposable
|
private HorizonClient _hosClient;
|
||||||
|
private SharedRef<IReader> _reader;
|
||||||
|
|
||||||
|
private readonly object _readerInitLocker = new object();
|
||||||
|
|
||||||
|
internal ArpClient(HorizonClient horizonClient)
|
||||||
{
|
{
|
||||||
private HorizonClient _hosClient;
|
_hosClient = horizonClient;
|
||||||
private SharedRef<IReader> _reader;
|
}
|
||||||
|
|
||||||
private readonly object _readerInitLocker = new object();
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_reader.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
internal ArpClient(HorizonClient horizonClient)
|
public Result GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty, ulong processId)
|
||||||
{
|
{
|
||||||
_hosClient = horizonClient;
|
EnsureReaderInitialized();
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
return _reader.Get.GetApplicationLaunchProperty(out launchProperty, processId);
|
||||||
{
|
}
|
||||||
_reader.Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty, ulong processId)
|
public Result GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty, ApplicationId applicationId)
|
||||||
{
|
{
|
||||||
EnsureReaderInitialized();
|
EnsureReaderInitialized();
|
||||||
|
|
||||||
return _reader.Get.GetApplicationLaunchProperty(out launchProperty, processId);
|
return _reader.Get.GetApplicationLaunchPropertyWithApplicationId(out launchProperty, applicationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty, ApplicationId applicationId)
|
public Result GetApplicationControlProperty(out ApplicationControlProperty controlProperty, ulong processId)
|
||||||
{
|
{
|
||||||
EnsureReaderInitialized();
|
EnsureReaderInitialized();
|
||||||
|
|
||||||
return _reader.Get.GetApplicationLaunchPropertyWithApplicationId(out launchProperty, applicationId);
|
return _reader.Get.GetApplicationControlProperty(out controlProperty, processId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result GetApplicationControlProperty(out ApplicationControlProperty controlProperty, ulong processId)
|
public Result GetApplicationControlProperty(out ApplicationControlProperty controlProperty, ApplicationId applicationId)
|
||||||
{
|
{
|
||||||
EnsureReaderInitialized();
|
EnsureReaderInitialized();
|
||||||
|
|
||||||
return _reader.Get.GetApplicationControlProperty(out controlProperty, processId);
|
return _reader.Get.GetApplicationControlPropertyWithApplicationId(out controlProperty, applicationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result GetApplicationControlProperty(out ApplicationControlProperty controlProperty, ApplicationId applicationId)
|
private void EnsureReaderInitialized()
|
||||||
{
|
{
|
||||||
EnsureReaderInitialized();
|
if (_reader.HasValue)
|
||||||
|
return;
|
||||||
|
|
||||||
return _reader.Get.GetApplicationControlPropertyWithApplicationId(out controlProperty, applicationId);
|
lock (_readerInitLocker)
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureReaderInitialized()
|
|
||||||
{
|
{
|
||||||
if (_reader.HasValue)
|
if (_reader.HasValue)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
lock (_readerInitLocker)
|
using var reader = new SharedRef<IReader>();
|
||||||
|
Result rc = _hosClient.Sm.GetService(ref reader.Ref(), "arp:r");
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
{
|
{
|
||||||
if (_reader.HasValue)
|
throw new HorizonResultException(rc, "Failed to initialize arp reader.");
|
||||||
return;
|
|
||||||
|
|
||||||
using var reader = new SharedRef<IReader>();
|
|
||||||
Result rc = _hosClient.Sm.GetService(ref reader.Ref(), "arp:r");
|
|
||||||
|
|
||||||
if (rc.IsFailure())
|
|
||||||
{
|
|
||||||
throw new HorizonResultException(rc, "Failed to initialize arp reader.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_reader.SetByMove(ref reader.Ref());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_reader.SetByMove(ref reader.Ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
|
|
||||||
namespace LibHac.Arp.Impl
|
namespace LibHac.Arp.Impl;
|
||||||
|
|
||||||
|
public interface IReader : IDisposable
|
||||||
{
|
{
|
||||||
public interface IReader : IDisposable
|
Result GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty, ulong processId);
|
||||||
{
|
Result GetApplicationLaunchPropertyWithApplicationId(out ApplicationLaunchProperty launchProperty, ApplicationId applicationId);
|
||||||
Result GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty, ulong processId);
|
Result GetApplicationControlProperty(out ApplicationControlProperty controlProperty, ulong processId);
|
||||||
Result GetApplicationLaunchPropertyWithApplicationId(out ApplicationLaunchProperty launchProperty, ApplicationId applicationId);
|
Result GetApplicationControlPropertyWithApplicationId(out ApplicationControlProperty controlProperty, ApplicationId applicationId);
|
||||||
Result GetApplicationControlProperty(out ApplicationControlProperty controlProperty, ulong processId);
|
|
||||||
Result GetApplicationControlPropertyWithApplicationId(out ApplicationControlProperty controlProperty, ApplicationId applicationId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,86 +5,85 @@ using LibHac.Bcat.Impl.Service.Core;
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
|
|
||||||
namespace LibHac.Bcat
|
namespace LibHac.Bcat;
|
||||||
|
|
||||||
|
public class BcatServer
|
||||||
{
|
{
|
||||||
public class BcatServer
|
private const int ServiceTypeCount = 4;
|
||||||
|
|
||||||
|
internal HorizonClient Hos { get; }
|
||||||
|
private SharedRef<ServiceCreator>[] _serviceCreators;
|
||||||
|
|
||||||
|
private readonly object _bcatServiceInitLocker = new object();
|
||||||
|
private readonly object _storageManagerInitLocker = new object();
|
||||||
|
|
||||||
|
private DeliveryCacheStorageManager StorageManager { get; set; }
|
||||||
|
|
||||||
|
public BcatServer(HorizonClient horizonClient)
|
||||||
{
|
{
|
||||||
private const int ServiceTypeCount = 4;
|
Hos = horizonClient;
|
||||||
|
_serviceCreators = new SharedRef<ServiceCreator>[ServiceTypeCount];
|
||||||
|
|
||||||
internal HorizonClient Hos { get; }
|
InitBcatService(BcatServiceType.BcatU, "bcat:u", AccessControl.MountOwnDeliveryCacheStorage);
|
||||||
private SharedRef<ServiceCreator>[] _serviceCreators;
|
InitBcatService(BcatServiceType.BcatS, "bcat:s", AccessControl.MountOthersDeliveryCacheStorage);
|
||||||
|
InitBcatService(BcatServiceType.BcatM, "bcat:m", AccessControl.MountOthersDeliveryCacheStorage | AccessControl.DeliveryTaskManagement);
|
||||||
|
InitBcatService(BcatServiceType.BcatA, "bcat:a", AccessControl.All);
|
||||||
|
}
|
||||||
|
|
||||||
private readonly object _bcatServiceInitLocker = new object();
|
private void InitBcatService(BcatServiceType type, string name, AccessControl accessControl)
|
||||||
private readonly object _storageManagerInitLocker = new object();
|
{
|
||||||
|
InitServiceCreator(type, name, accessControl);
|
||||||
|
|
||||||
private DeliveryCacheStorageManager StorageManager { get; set; }
|
using SharedRef<IServiceCreator> service = GetServiceCreator(type);
|
||||||
|
|
||||||
public BcatServer(HorizonClient horizonClient)
|
Result rc = Hos.Sm.RegisterService(new BcatServiceObject(ref service.Ref()), name);
|
||||||
|
if (rc.IsFailure())
|
||||||
{
|
{
|
||||||
Hos = horizonClient;
|
throw new HorizonResultException(rc, "Abort");
|
||||||
_serviceCreators = new SharedRef<ServiceCreator>[ServiceTypeCount];
|
|
||||||
|
|
||||||
InitBcatService(BcatServiceType.BcatU, "bcat:u", AccessControl.MountOwnDeliveryCacheStorage);
|
|
||||||
InitBcatService(BcatServiceType.BcatS, "bcat:s", AccessControl.MountOthersDeliveryCacheStorage);
|
|
||||||
InitBcatService(BcatServiceType.BcatM, "bcat:m", AccessControl.MountOthersDeliveryCacheStorage | AccessControl.DeliveryTaskManagement);
|
|
||||||
InitBcatService(BcatServiceType.BcatA, "bcat:a", AccessControl.All);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void InitBcatService(BcatServiceType type, string name, AccessControl accessControl)
|
private void InitServiceCreator(BcatServiceType type, string name, AccessControl accessControl)
|
||||||
|
{
|
||||||
|
lock (_bcatServiceInitLocker)
|
||||||
{
|
{
|
||||||
InitServiceCreator(type, name, accessControl);
|
Debug.Assert((uint)type < ServiceTypeCount);
|
||||||
|
|
||||||
using SharedRef<IServiceCreator> service = GetServiceCreator(type);
|
_serviceCreators[(int)type].Reset(new ServiceCreator(this, name, accessControl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Result rc = Hos.Sm.RegisterService(new BcatServiceObject(ref service.Ref()), name);
|
private SharedRef<IServiceCreator> GetServiceCreator(BcatServiceType type)
|
||||||
if (rc.IsFailure())
|
{
|
||||||
|
lock (_bcatServiceInitLocker)
|
||||||
|
{
|
||||||
|
Debug.Assert((uint)type < ServiceTypeCount);
|
||||||
|
|
||||||
|
return SharedRef<IServiceCreator>.CreateCopy(in _serviceCreators[(int)type]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal DeliveryCacheStorageManager GetStorageManager()
|
||||||
|
{
|
||||||
|
return StorageManager ?? InitStorageManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal FileSystemClient GetFsClient()
|
||||||
|
{
|
||||||
|
return Hos.Fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeliveryCacheStorageManager InitStorageManager()
|
||||||
|
{
|
||||||
|
lock (_storageManagerInitLocker)
|
||||||
|
{
|
||||||
|
if (StorageManager != null)
|
||||||
{
|
{
|
||||||
throw new HorizonResultException(rc, "Abort");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitServiceCreator(BcatServiceType type, string name, AccessControl accessControl)
|
|
||||||
{
|
|
||||||
lock (_bcatServiceInitLocker)
|
|
||||||
{
|
|
||||||
Debug.Assert((uint)type < ServiceTypeCount);
|
|
||||||
|
|
||||||
_serviceCreators[(int)type].Reset(new ServiceCreator(this, name, accessControl));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SharedRef<IServiceCreator> GetServiceCreator(BcatServiceType type)
|
|
||||||
{
|
|
||||||
lock (_bcatServiceInitLocker)
|
|
||||||
{
|
|
||||||
Debug.Assert((uint)type < ServiceTypeCount);
|
|
||||||
|
|
||||||
return SharedRef<IServiceCreator>.CreateCopy(in _serviceCreators[(int)type]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal DeliveryCacheStorageManager GetStorageManager()
|
|
||||||
{
|
|
||||||
return StorageManager ?? InitStorageManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal FileSystemClient GetFsClient()
|
|
||||||
{
|
|
||||||
return Hos.Fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DeliveryCacheStorageManager InitStorageManager()
|
|
||||||
{
|
|
||||||
lock (_storageManagerInitLocker)
|
|
||||||
{
|
|
||||||
if (StorageManager != null)
|
|
||||||
{
|
|
||||||
return StorageManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageManager = new DeliveryCacheStorageManager(this);
|
|
||||||
return StorageManager;
|
return StorageManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StorageManager = new DeliveryCacheStorageManager(this);
|
||||||
|
return StorageManager;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
namespace LibHac.Bcat
|
namespace LibHac.Bcat;
|
||||||
|
|
||||||
|
public enum BcatServiceType
|
||||||
{
|
{
|
||||||
public enum BcatServiceType
|
BcatU,
|
||||||
{
|
BcatS,
|
||||||
BcatU,
|
BcatM,
|
||||||
BcatS,
|
BcatA
|
||||||
BcatM,
|
|
||||||
BcatA
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
using System.Runtime.InteropServices;
|
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 DeliveryCacheDirectoryEntry(ref FileName name, long size, ref Digest digest)
|
[StructLayout(LayoutKind.Explicit, Size = 0x38)]
|
||||||
{
|
public struct DeliveryCacheDirectoryEntry
|
||||||
Name = name;
|
{
|
||||||
Size = size;
|
[FieldOffset(0x00)] public FileName Name;
|
||||||
Digest = digest;
|
[FieldOffset(0x20)] public long Size;
|
||||||
}
|
[FieldOffset(0x28)] public Digest Digest;
|
||||||
|
|
||||||
|
public DeliveryCacheDirectoryEntry(ref FileName name, long size, ref Digest digest)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Size = size;
|
||||||
|
Digest = digest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,26 +4,25 @@ using System.Runtime.InteropServices;
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Bcat
|
namespace LibHac.Bcat;
|
||||||
|
|
||||||
|
[DebuggerDisplay("{ToString()}")]
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 16)]
|
||||||
|
public struct Digest
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{ToString()}")]
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 16)]
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
|
||||||
public struct Digest
|
|
||||||
|
public byte this[int i]
|
||||||
{
|
{
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
|
get => Bytes[i];
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
|
set => Bytes[i] = value;
|
||||||
|
}
|
||||||
|
|
||||||
public byte this[int i]
|
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||||
{
|
|
||||||
get => Bytes[i];
|
|
||||||
set => Bytes[i] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
public override string ToString()
|
||||||
|
{
|
||||||
public override string ToString()
|
return Bytes.ToHexString();
|
||||||
{
|
|
||||||
return Bytes.ToHexString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,50 +4,49 @@ using System.Runtime.InteropServices;
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Bcat
|
namespace LibHac.Bcat;
|
||||||
|
|
||||||
|
[DebuggerDisplay("{ToString()}")]
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = MaxSize)]
|
||||||
|
public struct DirectoryName
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{ToString()}")]
|
private const int MaxSize = 0x20;
|
||||||
[StructLayout(LayoutKind.Sequential, Size = MaxSize)]
|
|
||||||
public struct DirectoryName
|
[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 byte this[int i]
|
||||||
{
|
{
|
||||||
private const int MaxSize = 0x20;
|
get => Bytes[i];
|
||||||
|
set => Bytes[i] = value;
|
||||||
|
}
|
||||||
|
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
|
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
|
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy2;
|
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy3;
|
|
||||||
|
|
||||||
public byte this[int i]
|
public bool IsValid()
|
||||||
|
{
|
||||||
|
Span<byte> name = Bytes;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < name.Length; i++)
|
||||||
{
|
{
|
||||||
get => Bytes[i];
|
if (name[i] == 0)
|
||||||
set => Bytes[i] = value;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
if (!StringUtils.IsDigit(name[i]) && !StringUtils.IsAlpha(name[i]) && name[i] != '_' && name[i] != '-')
|
||||||
|
|
||||||
public bool IsValid()
|
|
||||||
{
|
|
||||||
Span<byte> name = Bytes;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < name.Length; i++)
|
|
||||||
{
|
|
||||||
if (name[i] == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (!StringUtils.IsDigit(name[i]) && !StringUtils.IsAlpha(name[i]) && name[i] != '_' && name[i] != '-')
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0 || i == MaxSize)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return name[i] == 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
if (i == 0 || i == MaxSize)
|
||||||
{
|
return false;
|
||||||
return StringUtils.Utf8ZToString(Bytes);
|
|
||||||
}
|
return name[i] == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return StringUtils.Utf8ZToString(Bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,53 +4,52 @@ using System.Runtime.InteropServices;
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Bcat
|
namespace LibHac.Bcat;
|
||||||
|
|
||||||
|
[DebuggerDisplay("{ToString()}")]
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = MaxSize)]
|
||||||
|
public struct FileName
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{ToString()}")]
|
private const int MaxSize = 0x20;
|
||||||
[StructLayout(LayoutKind.Sequential, Size = MaxSize)]
|
|
||||||
public struct FileName
|
[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 byte this[int i]
|
||||||
{
|
{
|
||||||
private const int MaxSize = 0x20;
|
get => Bytes[i];
|
||||||
|
set => Bytes[i] = value;
|
||||||
[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 byte this[int i]
|
|
||||||
{
|
|
||||||
get => Bytes[i];
|
|
||||||
set => Bytes[i] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
|
||||||
|
|
||||||
public bool IsValid()
|
|
||||||
{
|
|
||||||
Span<byte> name = Bytes;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < name.Length; i++)
|
|
||||||
{
|
|
||||||
if (name[i] == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (!StringUtils.IsDigit(name[i]) && !StringUtils.IsAlpha(name[i]) && name[i] != '_' && name[i] != '.')
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0 || i == MaxSize)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (name[i] != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return name[i - 1] != '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return StringUtils.Utf8ZToString(Bytes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||||
|
|
||||||
|
public bool IsValid()
|
||||||
|
{
|
||||||
|
Span<byte> name = Bytes;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < name.Length; i++)
|
||||||
|
{
|
||||||
|
if (name[i] == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!StringUtils.IsDigit(name[i]) && !StringUtils.IsAlpha(name[i]) && name[i] != '_' && name[i] != '.')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0 || i == MaxSize)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (name[i] != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return name[i - 1] != '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return StringUtils.Utf8ZToString(Bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,26 +2,25 @@
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Sm;
|
using LibHac.Sm;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Impl.Ipc
|
namespace LibHac.Bcat.Impl.Ipc;
|
||||||
|
|
||||||
|
internal class BcatServiceObject : IServiceObject
|
||||||
{
|
{
|
||||||
internal class BcatServiceObject : IServiceObject
|
private SharedRef<IServiceCreator> _serviceCreator;
|
||||||
|
|
||||||
|
public BcatServiceObject(ref SharedRef<IServiceCreator> serviceCreator)
|
||||||
{
|
{
|
||||||
private SharedRef<IServiceCreator> _serviceCreator;
|
_serviceCreator = SharedRef<IServiceCreator>.CreateMove(ref serviceCreator);
|
||||||
|
}
|
||||||
|
|
||||||
public BcatServiceObject(ref SharedRef<IServiceCreator> serviceCreator)
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_serviceCreator = SharedRef<IServiceCreator>.CreateMove(ref serviceCreator);
|
_serviceCreator.Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public Result GetServiceObject(ref SharedRef<IDisposable> serviceObject)
|
||||||
{
|
{
|
||||||
_serviceCreator.Destroy();
|
serviceObject.SetByCopy(in _serviceCreator);
|
||||||
}
|
return Result.Success;
|
||||||
|
|
||||||
public Result GetServiceObject(ref SharedRef<IDisposable> serviceObject)
|
|
||||||
{
|
|
||||||
serviceObject.SetByCopy(in _serviceCreator);
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Impl.Ipc
|
namespace LibHac.Bcat.Impl.Ipc;
|
||||||
|
|
||||||
|
public interface IDeliveryCacheDirectoryService : IDisposable
|
||||||
{
|
{
|
||||||
public interface IDeliveryCacheDirectoryService : IDisposable
|
Result Open(ref DirectoryName name);
|
||||||
{
|
Result Read(out int entriesRead, Span<DeliveryCacheDirectoryEntry> entryBuffer);
|
||||||
Result Open(ref DirectoryName name);
|
Result GetCount(out int count);
|
||||||
Result Read(out int entriesRead, Span<DeliveryCacheDirectoryEntry> entryBuffer);
|
|
||||||
Result GetCount(out int count);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Impl.Ipc
|
namespace LibHac.Bcat.Impl.Ipc;
|
||||||
|
|
||||||
|
public interface IDeliveryCacheFileService : IDisposable
|
||||||
{
|
{
|
||||||
public interface IDeliveryCacheFileService : IDisposable
|
Result Open(ref DirectoryName directoryName, ref FileName fileName);
|
||||||
{
|
Result Read(out long bytesRead, long offset, Span<byte> destination);
|
||||||
Result Open(ref DirectoryName directoryName, ref FileName fileName);
|
Result GetSize(out long size);
|
||||||
Result Read(out long bytesRead, long offset, Span<byte> destination);
|
Result GetDigest(out Digest digest);
|
||||||
Result GetSize(out long size);
|
|
||||||
Result GetDigest(out Digest digest);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Impl.Ipc
|
namespace LibHac.Bcat.Impl.Ipc;
|
||||||
|
|
||||||
|
public interface IDeliveryCacheStorageService : IDisposable
|
||||||
{
|
{
|
||||||
public interface IDeliveryCacheStorageService : IDisposable
|
Result CreateFileService(ref SharedRef<IDeliveryCacheFileService> outFileService);
|
||||||
{
|
Result CreateDirectoryService(ref SharedRef<IDeliveryCacheDirectoryService> outDirectoryService);
|
||||||
Result CreateFileService(ref SharedRef<IDeliveryCacheFileService> outFileService);
|
Result EnumerateDeliveryCacheDirectory(out int namesRead, Span<DirectoryName> nameBuffer);
|
||||||
Result CreateDirectoryService(ref SharedRef<IDeliveryCacheDirectoryService> outDirectoryService);
|
|
||||||
Result EnumerateDeliveryCacheDirectory(out int namesRead, Span<DirectoryName> nameBuffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Impl.Ipc
|
namespace LibHac.Bcat.Impl.Ipc;
|
||||||
{
|
|
||||||
public interface IServiceCreator : IDisposable
|
|
||||||
{
|
|
||||||
Result CreateDeliveryCacheStorageService(ref SharedRef<IDeliveryCacheStorageService> outService,
|
|
||||||
ulong processId);
|
|
||||||
|
|
||||||
Result CreateDeliveryCacheStorageServiceWithApplicationId(
|
public interface IServiceCreator : IDisposable
|
||||||
ref SharedRef<IDeliveryCacheStorageService> outService, ApplicationId applicationId);
|
{
|
||||||
}
|
Result CreateDeliveryCacheStorageService(ref SharedRef<IDeliveryCacheStorageService> outService,
|
||||||
|
ulong processId);
|
||||||
|
|
||||||
|
Result CreateDeliveryCacheStorageServiceWithApplicationId(
|
||||||
|
ref SharedRef<IDeliveryCacheStorageService> outService, ApplicationId applicationId);
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Impl.Service
|
namespace LibHac.Bcat.Impl.Service;
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
internal enum AccessControl
|
||||||
{
|
{
|
||||||
[Flags]
|
None = 0,
|
||||||
internal enum AccessControl
|
MountOwnDeliveryCacheStorage = 1 << 1,
|
||||||
{
|
MountOthersDeliveryCacheStorage = 1 << 2,
|
||||||
None = 0,
|
DeliveryTaskManagement = 1 << 3,
|
||||||
MountOwnDeliveryCacheStorage = 1 << 1,
|
Debug = 1 << 4,
|
||||||
MountOthersDeliveryCacheStorage = 1 << 2,
|
All = ~0
|
||||||
DeliveryTaskManagement = 1 << 3,
|
|
||||||
Debug = 1 << 4,
|
|
||||||
All = ~0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,96 +5,95 @@ using LibHac.Common;
|
|||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Impl.Service.Core
|
namespace LibHac.Bcat.Impl.Service.Core;
|
||||||
|
|
||||||
|
internal class DeliveryCacheDirectoryMetaAccessor
|
||||||
{
|
{
|
||||||
internal class DeliveryCacheDirectoryMetaAccessor
|
private const int MaxEntryCount = 100;
|
||||||
|
private const int MetaFileHeaderValue = 1;
|
||||||
|
|
||||||
|
private BcatServer Server { get; }
|
||||||
|
private object Locker { get; } = new object();
|
||||||
|
private DeliveryCacheDirectoryMetaEntry[] Entries { get; } = new DeliveryCacheDirectoryMetaEntry[MaxEntryCount];
|
||||||
|
public int Count { get; private set; }
|
||||||
|
|
||||||
|
public DeliveryCacheDirectoryMetaAccessor(BcatServer server)
|
||||||
{
|
{
|
||||||
private const int MaxEntryCount = 100;
|
Server = server;
|
||||||
private const int MetaFileHeaderValue = 1;
|
}
|
||||||
|
|
||||||
private BcatServer Server { get; }
|
public Result ReadApplicationDirectoryMeta(ulong applicationId, bool allowMissingMetaFile)
|
||||||
private object Locker { get; } = new object();
|
{
|
||||||
private DeliveryCacheDirectoryMetaEntry[] Entries { get; } = new DeliveryCacheDirectoryMetaEntry[MaxEntryCount];
|
Span<byte> metaPath = stackalloc byte[0x50];
|
||||||
public int Count { get; private set; }
|
Server.GetStorageManager().GetDirectoriesMetaPath(metaPath, applicationId);
|
||||||
|
|
||||||
public DeliveryCacheDirectoryMetaAccessor(BcatServer server)
|
return Read(new U8Span(metaPath), allowMissingMetaFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result GetEntry(out DeliveryCacheDirectoryMetaEntry entry, int index)
|
||||||
|
{
|
||||||
|
UnsafeHelpers.SkipParamInit(out entry);
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
{
|
{
|
||||||
Server = server;
|
if (index >= Count)
|
||||||
}
|
|
||||||
|
|
||||||
public Result ReadApplicationDirectoryMeta(ulong applicationId, bool allowMissingMetaFile)
|
|
||||||
{
|
|
||||||
Span<byte> metaPath = stackalloc byte[0x50];
|
|
||||||
Server.GetStorageManager().GetDirectoriesMetaPath(metaPath, applicationId);
|
|
||||||
|
|
||||||
return Read(new U8Span(metaPath), allowMissingMetaFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result GetEntry(out DeliveryCacheDirectoryMetaEntry entry, int index)
|
|
||||||
{
|
|
||||||
UnsafeHelpers.SkipParamInit(out entry);
|
|
||||||
|
|
||||||
lock (Locker)
|
|
||||||
{
|
{
|
||||||
if (index >= Count)
|
return ResultBcat.NotFound.Log();
|
||||||
{
|
|
||||||
return ResultBcat.NotFound.Log();
|
|
||||||
}
|
|
||||||
|
|
||||||
entry = Entries[index];
|
|
||||||
return Result.Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entry = Entries[index];
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Result Read(U8Span path, bool allowMissingMetaFile)
|
private Result Read(U8Span path, bool allowMissingMetaFile)
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
{
|
{
|
||||||
lock (Locker)
|
FileSystemClient fs = Server.GetFsClient();
|
||||||
|
|
||||||
|
Result rc = fs.OpenFile(out FileHandle handle, path, OpenMode.Read);
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
{
|
{
|
||||||
FileSystemClient fs = Server.GetFsClient();
|
if (ResultFs.PathNotFound.Includes(rc))
|
||||||
|
|
||||||
Result rc = fs.OpenFile(out FileHandle handle, path, OpenMode.Read);
|
|
||||||
|
|
||||||
if (rc.IsFailure())
|
|
||||||
{
|
{
|
||||||
if (ResultFs.PathNotFound.Includes(rc))
|
if (allowMissingMetaFile)
|
||||||
{
|
{
|
||||||
if (allowMissingMetaFile)
|
Count = 0;
|
||||||
{
|
return Result.Success;
|
||||||
Count = 0;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultBcat.NotFound.LogConverted(rc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return ResultBcat.NotFound.LogConverted(rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
return rc;
|
||||||
{
|
}
|
||||||
Count = 0;
|
|
||||||
int header = 0;
|
|
||||||
|
|
||||||
// Verify the header value
|
try
|
||||||
rc = fs.ReadFile(out long bytesRead, handle, 0, SpanHelpers.AsByteSpan(ref header));
|
{
|
||||||
if (rc.IsFailure()) return rc;
|
Count = 0;
|
||||||
|
int header = 0;
|
||||||
|
|
||||||
if (bytesRead != sizeof(int) || header != MetaFileHeaderValue)
|
// Verify the header value
|
||||||
return ResultBcat.InvalidDeliveryCacheStorageFile.Log();
|
rc = fs.ReadFile(out long bytesRead, handle, 0, SpanHelpers.AsByteSpan(ref header));
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Read all the directory entries
|
if (bytesRead != sizeof(int) || header != MetaFileHeaderValue)
|
||||||
Span<byte> buffer = MemoryMarshal.Cast<DeliveryCacheDirectoryMetaEntry, byte>(Entries);
|
return ResultBcat.InvalidDeliveryCacheStorageFile.Log();
|
||||||
rc = fs.ReadFile(out bytesRead, handle, 4, buffer);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
Count = (int)((uint)bytesRead / Unsafe.SizeOf<DeliveryCacheDirectoryMetaEntry>());
|
// Read all the directory entries
|
||||||
|
Span<byte> buffer = MemoryMarshal.Cast<DeliveryCacheDirectoryMetaEntry, byte>(Entries);
|
||||||
|
rc = fs.ReadFile(out bytesRead, handle, 4, buffer);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
return Result.Success;
|
Count = (int)((uint)bytesRead / Unsafe.SizeOf<DeliveryCacheDirectoryMetaEntry>());
|
||||||
}
|
|
||||||
finally
|
return Result.Success;
|
||||||
{
|
}
|
||||||
fs.CloseFile(handle);
|
finally
|
||||||
}
|
{
|
||||||
|
fs.CloseFile(handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Impl.Service.Core
|
namespace LibHac.Bcat.Impl.Service.Core;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 0x40)]
|
||||||
|
internal struct DeliveryCacheDirectoryMetaEntry
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x40)]
|
[FieldOffset(0x00)] public DirectoryName Name;
|
||||||
internal struct DeliveryCacheDirectoryMetaEntry
|
[FieldOffset(0x20)] public Digest Digest;
|
||||||
{
|
|
||||||
[FieldOffset(0x00)] public DirectoryName Name;
|
|
||||||
[FieldOffset(0x20)] public Digest Digest;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,116 +6,115 @@ using LibHac.Fs;
|
|||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Impl.Service.Core
|
namespace LibHac.Bcat.Impl.Service.Core;
|
||||||
|
|
||||||
|
internal class DeliveryCacheFileMetaAccessor
|
||||||
{
|
{
|
||||||
internal class DeliveryCacheFileMetaAccessor
|
private const int MaxEntryCount = 100;
|
||||||
|
private const int MetaFileHeaderValue = 1;
|
||||||
|
|
||||||
|
private BcatServer Server { get; }
|
||||||
|
private object Locker { get; } = new object();
|
||||||
|
private DeliveryCacheFileMetaEntry[] Entries { get; } = new DeliveryCacheFileMetaEntry[MaxEntryCount];
|
||||||
|
public int Count { get; private set; }
|
||||||
|
|
||||||
|
public DeliveryCacheFileMetaAccessor(BcatServer server)
|
||||||
{
|
{
|
||||||
private const int MaxEntryCount = 100;
|
Server = server;
|
||||||
private const int MetaFileHeaderValue = 1;
|
}
|
||||||
|
|
||||||
private BcatServer Server { get; }
|
public Result ReadApplicationFileMeta(ulong applicationId, ref DirectoryName directoryName,
|
||||||
private object Locker { get; } = new object();
|
bool allowMissingMetaFile)
|
||||||
private DeliveryCacheFileMetaEntry[] Entries { get; } = new DeliveryCacheFileMetaEntry[MaxEntryCount];
|
{
|
||||||
public int Count { get; private set; }
|
Span<byte> metaPath = stackalloc byte[0x50];
|
||||||
|
Server.GetStorageManager().GetFilesMetaPath(metaPath, applicationId, ref directoryName);
|
||||||
|
|
||||||
public DeliveryCacheFileMetaAccessor(BcatServer server)
|
return Read(new U8Span(metaPath), allowMissingMetaFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result GetEntry(out DeliveryCacheFileMetaEntry entry, int index)
|
||||||
|
{
|
||||||
|
UnsafeHelpers.SkipParamInit(out entry);
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
{
|
{
|
||||||
Server = server;
|
if (index >= Count)
|
||||||
}
|
|
||||||
|
|
||||||
public Result ReadApplicationFileMeta(ulong applicationId, ref DirectoryName directoryName,
|
|
||||||
bool allowMissingMetaFile)
|
|
||||||
{
|
|
||||||
Span<byte> metaPath = stackalloc byte[0x50];
|
|
||||||
Server.GetStorageManager().GetFilesMetaPath(metaPath, applicationId, ref directoryName);
|
|
||||||
|
|
||||||
return Read(new U8Span(metaPath), allowMissingMetaFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result GetEntry(out DeliveryCacheFileMetaEntry entry, int index)
|
|
||||||
{
|
|
||||||
UnsafeHelpers.SkipParamInit(out entry);
|
|
||||||
|
|
||||||
lock (Locker)
|
|
||||||
{
|
{
|
||||||
if (index >= Count)
|
|
||||||
{
|
|
||||||
return ResultBcat.NotFound.Log();
|
|
||||||
}
|
|
||||||
|
|
||||||
entry = Entries[index];
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result FindEntry(out DeliveryCacheFileMetaEntry entry, ref FileName fileName)
|
|
||||||
{
|
|
||||||
UnsafeHelpers.SkipParamInit(out entry);
|
|
||||||
|
|
||||||
lock (Locker)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < Count; i++)
|
|
||||||
{
|
|
||||||
if (StringUtils.CompareCaseInsensitive(Entries[i].Name.Bytes, fileName.Bytes) == 0)
|
|
||||||
{
|
|
||||||
entry = Entries[i];
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultBcat.NotFound.Log();
|
return ResultBcat.NotFound.Log();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entry = Entries[index];
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Result Read(U8Span path, bool allowMissingMetaFile)
|
public Result FindEntry(out DeliveryCacheFileMetaEntry entry, ref FileName fileName)
|
||||||
|
{
|
||||||
|
UnsafeHelpers.SkipParamInit(out entry);
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
{
|
{
|
||||||
lock (Locker)
|
for (int i = 0; i < Count; i++)
|
||||||
{
|
{
|
||||||
FileSystemClient fs = Server.GetFsClient();
|
if (StringUtils.CompareCaseInsensitive(Entries[i].Name.Bytes, fileName.Bytes) == 0)
|
||||||
|
|
||||||
Result rc = fs.OpenFile(out FileHandle handle, path, OpenMode.Read);
|
|
||||||
|
|
||||||
if (rc.IsFailure())
|
|
||||||
{
|
{
|
||||||
if (ResultFs.PathNotFound.Includes(rc))
|
entry = Entries[i];
|
||||||
{
|
|
||||||
if (allowMissingMetaFile)
|
|
||||||
{
|
|
||||||
Count = 0;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultBcat.NotFound.LogConverted(rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Count = 0;
|
|
||||||
int header = 0;
|
|
||||||
|
|
||||||
// Verify the header value
|
|
||||||
rc = fs.ReadFile(out long bytesRead, handle, 0, SpanHelpers.AsByteSpan(ref header));
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
if (bytesRead != sizeof(int) || header != MetaFileHeaderValue)
|
|
||||||
return ResultBcat.InvalidDeliveryCacheStorageFile.Log();
|
|
||||||
|
|
||||||
// Read all the file entries
|
|
||||||
Span<byte> buffer = MemoryMarshal.Cast<DeliveryCacheFileMetaEntry, byte>(Entries);
|
|
||||||
rc = fs.ReadFile(out bytesRead, handle, 4, buffer);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
Count = (int)((uint)bytesRead / Unsafe.SizeOf<DeliveryCacheFileMetaEntry>());
|
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
finally
|
}
|
||||||
|
|
||||||
|
return ResultBcat.NotFound.Log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result Read(U8Span path, bool allowMissingMetaFile)
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
FileSystemClient fs = Server.GetFsClient();
|
||||||
|
|
||||||
|
Result rc = fs.OpenFile(out FileHandle handle, path, OpenMode.Read);
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
if (ResultFs.PathNotFound.Includes(rc))
|
||||||
{
|
{
|
||||||
fs.CloseFile(handle);
|
if (allowMissingMetaFile)
|
||||||
|
{
|
||||||
|
Count = 0;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultBcat.NotFound.LogConverted(rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Count = 0;
|
||||||
|
int header = 0;
|
||||||
|
|
||||||
|
// Verify the header value
|
||||||
|
rc = fs.ReadFile(out long bytesRead, handle, 0, SpanHelpers.AsByteSpan(ref header));
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
if (bytesRead != sizeof(int) || header != MetaFileHeaderValue)
|
||||||
|
return ResultBcat.InvalidDeliveryCacheStorageFile.Log();
|
||||||
|
|
||||||
|
// Read all the file entries
|
||||||
|
Span<byte> buffer = MemoryMarshal.Cast<DeliveryCacheFileMetaEntry, byte>(Entries);
|
||||||
|
rc = fs.ReadFile(out bytesRead, handle, 4, buffer);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
Count = (int)((uint)bytesRead / Unsafe.SizeOf<DeliveryCacheFileMetaEntry>());
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
fs.CloseFile(handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Impl.Service.Core
|
namespace LibHac.Bcat.Impl.Service.Core;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 0x80)]
|
||||||
|
internal struct DeliveryCacheFileMetaEntry
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x80)]
|
[FieldOffset(0x00)] public FileName Name;
|
||||||
internal struct DeliveryCacheFileMetaEntry
|
[FieldOffset(0x20)] public long Id;
|
||||||
{
|
[FieldOffset(0x28)] public long Size;
|
||||||
[FieldOffset(0x00)] public FileName Name;
|
[FieldOffset(0x30)] public Digest Digest;
|
||||||
[FieldOffset(0x20)] public long Id;
|
|
||||||
[FieldOffset(0x28)] public long Size;
|
|
||||||
[FieldOffset(0x30)] public Digest Digest;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,394 +6,393 @@ using LibHac.Fs.Fsa;
|
|||||||
using LibHac.Fs.Shim;
|
using LibHac.Fs.Shim;
|
||||||
using static LibHac.Fs.StringTraits;
|
using static LibHac.Fs.StringTraits;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Impl.Service.Core
|
namespace LibHac.Bcat.Impl.Service.Core;
|
||||||
|
|
||||||
|
internal class DeliveryCacheStorageManager
|
||||||
{
|
{
|
||||||
internal class DeliveryCacheStorageManager
|
private const int MaxEntryCount = 4;
|
||||||
|
|
||||||
|
private BcatServer Server { get; }
|
||||||
|
|
||||||
|
private readonly object _locker = new object();
|
||||||
|
private Entry[] Entries { get; } = new Entry[MaxEntryCount];
|
||||||
|
private bool DisableStorage { get; set; }
|
||||||
|
|
||||||
|
private struct Entry
|
||||||
{
|
{
|
||||||
private const int MaxEntryCount = 4;
|
public ulong ApplicationId { get; set; }
|
||||||
|
public long RefCount { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
private BcatServer Server { get; }
|
public DeliveryCacheStorageManager(BcatServer server)
|
||||||
|
{
|
||||||
|
Server = server;
|
||||||
|
DisableStorage = false;
|
||||||
|
}
|
||||||
|
|
||||||
private readonly object _locker = new object();
|
public Result Open(ulong applicationId)
|
||||||
private Entry[] Entries { get; } = new Entry[MaxEntryCount];
|
{
|
||||||
private bool DisableStorage { get; set; }
|
lock (_locker)
|
||||||
|
|
||||||
private struct Entry
|
|
||||||
{
|
{
|
||||||
public ulong ApplicationId { get; set; }
|
// Find an existing storage entry for this application ID or get an empty one
|
||||||
public long RefCount { get; set; }
|
Result rc = FindOrGetUnusedEntry(out int index, applicationId);
|
||||||
}
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
public DeliveryCacheStorageManager(BcatServer server)
|
ref Entry entry = ref Entries[index];
|
||||||
{
|
|
||||||
Server = server;
|
|
||||||
DisableStorage = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result Open(ulong applicationId)
|
if (entry.RefCount != 0)
|
||||||
{
|
|
||||||
lock (_locker)
|
|
||||||
{
|
{
|
||||||
// Find an existing storage entry for this application ID or get an empty one
|
return ResultBcat.TargetLocked.Log();
|
||||||
Result rc = FindOrGetUnusedEntry(out int index, applicationId);
|
}
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
ref Entry entry = ref Entries[index];
|
// Get the mount name
|
||||||
|
var mountName = new MountName();
|
||||||
|
|
||||||
if (entry.RefCount != 0)
|
var sb = new U8StringBuilder(mountName.Name);
|
||||||
|
sb.Append(DeliveryCacheMountNamePrefix)
|
||||||
|
.AppendFormat(index, 'd', 2);
|
||||||
|
|
||||||
|
// Mount the save if enabled
|
||||||
|
if (!DisableStorage)
|
||||||
|
{
|
||||||
|
rc = Server.GetFsClient()
|
||||||
|
.MountBcatSaveData(new U8Span(mountName.Name), new Ncm.ApplicationId(applicationId));
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
{
|
{
|
||||||
return ResultBcat.TargetLocked.Log();
|
if (ResultFs.TargetNotFound.Includes(rc))
|
||||||
}
|
return ResultBcat.SaveDataNotFound.LogConverted(rc);
|
||||||
|
|
||||||
// Get the mount name
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the storage entry
|
||||||
|
entry.ApplicationId = applicationId;
|
||||||
|
entry.RefCount++;
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Release(ulong applicationId)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
int index = FindEntry(applicationId);
|
||||||
|
ref Entry entry = ref Entries[index];
|
||||||
|
|
||||||
|
entry.RefCount--;
|
||||||
|
|
||||||
|
// Free the entry if there are no more references
|
||||||
|
if (entry.RefCount == 0)
|
||||||
|
{
|
||||||
var mountName = new MountName();
|
var mountName = new MountName();
|
||||||
|
|
||||||
var sb = new U8StringBuilder(mountName.Name);
|
var sb = new U8StringBuilder(mountName.Name);
|
||||||
sb.Append(DeliveryCacheMountNamePrefix)
|
sb.Append(DeliveryCacheMountNamePrefix)
|
||||||
.AppendFormat(index, 'd', 2);
|
.AppendFormat(index, 'd', 2);
|
||||||
|
|
||||||
// Mount the save if enabled
|
// Unmount the entry's savedata
|
||||||
if (!DisableStorage)
|
if (!DisableStorage)
|
||||||
{
|
{
|
||||||
rc = Server.GetFsClient()
|
Server.GetFsClient().Unmount(new U8Span(mountName.Name));
|
||||||
.MountBcatSaveData(new U8Span(mountName.Name), new Ncm.ApplicationId(applicationId));
|
|
||||||
|
|
||||||
if (rc.IsFailure())
|
|
||||||
{
|
|
||||||
if (ResultFs.TargetNotFound.Includes(rc))
|
|
||||||
return ResultBcat.SaveDataNotFound.LogConverted(rc);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the storage entry
|
// Clear the entry
|
||||||
entry.ApplicationId = applicationId;
|
entry.ApplicationId = 0;
|
||||||
entry.RefCount++;
|
|
||||||
|
|
||||||
|
// todo: Call nn::bcat::detail::service::core::PassphraseManager::Remove
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Commit(ulong applicationId)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
int index = FindEntry(applicationId);
|
||||||
|
|
||||||
|
var mountName = new MountName();
|
||||||
|
|
||||||
|
var sb = new U8StringBuilder(mountName.Name);
|
||||||
|
sb.Append(DeliveryCacheMountNamePrefix)
|
||||||
|
.AppendFormat(index, 'd', 2);
|
||||||
|
|
||||||
|
if (!DisableStorage)
|
||||||
|
{
|
||||||
|
Result rc = Server.GetFsClient().Commit(new U8Span(mountName.Name));
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
throw new HorizonResultException(rc, "Abort");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result GetFreeSpaceSize(out long size, ulong applicationId)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
Span<byte> path = stackalloc byte[0x20];
|
||||||
|
|
||||||
|
var sb = new U8StringBuilder(path);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
sb.Append(RootPath);
|
||||||
|
|
||||||
|
Result rc;
|
||||||
|
|
||||||
|
if (DisableStorage)
|
||||||
|
{
|
||||||
|
size = 0x4400000;
|
||||||
|
rc = Result.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rc = Server.GetFsClient().GetFreeSpaceSize(out size, new U8Span(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetPassphrasePath(Span<byte> pathBuffer, ulong applicationId)
|
||||||
|
{
|
||||||
|
// returns "mount:/passphrase.bin"
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
sb.Append(PassphrasePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetDeliveryListPath(Span<byte> pathBuffer, ulong applicationId)
|
||||||
|
{
|
||||||
|
// returns "mount:/list.msgpack"
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
sb.Append(DeliveryListPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetEtagFilePath(Span<byte> pathBuffer, ulong applicationId)
|
||||||
|
{
|
||||||
|
// returns "mount:/etag.bin"
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
sb.Append(EtagPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetNaRequiredPath(Span<byte> pathBuffer, ulong applicationId)
|
||||||
|
{
|
||||||
|
// returns "mount:/na_required"
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
sb.Append(NaRequiredPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetIndexLockPath(Span<byte> pathBuffer, ulong applicationId)
|
||||||
|
{
|
||||||
|
// returns "mount:/index.lock"
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
sb.Append(IndexLockPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetFilePath(Span<byte> pathBuffer, ulong applicationId, ref DirectoryName directoryName,
|
||||||
|
ref FileName fileName)
|
||||||
|
{
|
||||||
|
// returns "mount:/directories/%s/files/%s", directoryName, fileName
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
|
||||||
|
sb.Append(DirectoriesPath)
|
||||||
|
.Append(DirectorySeparator).Append(directoryName.Bytes)
|
||||||
|
.Append(DirectorySeparator).Append(FilesDirectoryName)
|
||||||
|
.Append(DirectorySeparator).Append(fileName.Bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetFilesMetaPath(Span<byte> pathBuffer, ulong applicationId, ref DirectoryName directoryName)
|
||||||
|
{
|
||||||
|
// returns "mount:/directories/%s/files.meta", directoryName
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
|
||||||
|
sb.Append(DirectoriesPath)
|
||||||
|
.Append(DirectorySeparator).Append(directoryName.Bytes)
|
||||||
|
.Append(DirectorySeparator).Append(FilesMetaFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetDirectoriesPath(Span<byte> pathBuffer, ulong applicationId)
|
||||||
|
{
|
||||||
|
// returns "mount:/directories"
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
sb.Append(DirectoriesPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetDirectoryPath(Span<byte> pathBuffer, ulong applicationId, ref DirectoryName directoryName)
|
||||||
|
{
|
||||||
|
// returns "mount:/directories/%s", directoryName
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
|
||||||
|
sb.Append(DirectoriesPath)
|
||||||
|
.Append(DirectorySeparator).Append(directoryName.Bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetDirectoriesMetaPath(Span<byte> pathBuffer, ulong applicationId)
|
||||||
|
{
|
||||||
|
// returns "mount:/directories.meta"
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var sb = new U8StringBuilder(pathBuffer);
|
||||||
|
AppendMountName(ref sb, applicationId);
|
||||||
|
|
||||||
|
sb.Append(DirectoriesMetaPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendMountName(ref U8StringBuilder sb, ulong applicationId)
|
||||||
|
{
|
||||||
|
int index = FindEntry(applicationId);
|
||||||
|
|
||||||
|
sb.Append(DeliveryCacheMountNamePrefix)
|
||||||
|
.AppendFormat(index, 'd', 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result FindOrGetUnusedEntry(out int entryIndex, ulong applicationId)
|
||||||
|
{
|
||||||
|
UnsafeHelpers.SkipParamInit(out entryIndex);
|
||||||
|
|
||||||
|
// Try to find an existing entry
|
||||||
|
for (int i = 0; i < Entries.Length; i++)
|
||||||
|
{
|
||||||
|
if (Entries[i].ApplicationId == applicationId)
|
||||||
|
{
|
||||||
|
entryIndex = i;
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Release(ulong applicationId)
|
// Try to find an unused entry
|
||||||
|
for (int i = 0; i < Entries.Length; i++)
|
||||||
{
|
{
|
||||||
lock (_locker)
|
if (Entries[i].ApplicationId == 0)
|
||||||
{
|
{
|
||||||
int index = FindEntry(applicationId);
|
entryIndex = i;
|
||||||
ref Entry entry = ref Entries[index];
|
return Result.Success;
|
||||||
|
|
||||||
entry.RefCount--;
|
|
||||||
|
|
||||||
// Free the entry if there are no more references
|
|
||||||
if (entry.RefCount == 0)
|
|
||||||
{
|
|
||||||
var mountName = new MountName();
|
|
||||||
|
|
||||||
var sb = new U8StringBuilder(mountName.Name);
|
|
||||||
sb.Append(DeliveryCacheMountNamePrefix)
|
|
||||||
.AppendFormat(index, 'd', 2);
|
|
||||||
|
|
||||||
// Unmount the entry's savedata
|
|
||||||
if (!DisableStorage)
|
|
||||||
{
|
|
||||||
Server.GetFsClient().Unmount(new U8Span(mountName.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the entry
|
|
||||||
entry.ApplicationId = 0;
|
|
||||||
|
|
||||||
// todo: Call nn::bcat::detail::service::core::PassphraseManager::Remove
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Commit(ulong applicationId)
|
return ResultBcat.StorageOpenLimitReached.Log();
|
||||||
{
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
int index = FindEntry(applicationId);
|
|
||||||
|
|
||||||
var mountName = new MountName();
|
|
||||||
|
|
||||||
var sb = new U8StringBuilder(mountName.Name);
|
|
||||||
sb.Append(DeliveryCacheMountNamePrefix)
|
|
||||||
.AppendFormat(index, 'd', 2);
|
|
||||||
|
|
||||||
if (!DisableStorage)
|
|
||||||
{
|
|
||||||
Result rc = Server.GetFsClient().Commit(new U8Span(mountName.Name));
|
|
||||||
|
|
||||||
if (rc.IsFailure())
|
|
||||||
{
|
|
||||||
throw new HorizonResultException(rc, "Abort");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result GetFreeSpaceSize(out long size, ulong applicationId)
|
|
||||||
{
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
Span<byte> path = stackalloc byte[0x20];
|
|
||||||
|
|
||||||
var sb = new U8StringBuilder(path);
|
|
||||||
AppendMountName(ref sb, applicationId);
|
|
||||||
sb.Append(RootPath);
|
|
||||||
|
|
||||||
Result rc;
|
|
||||||
|
|
||||||
if (DisableStorage)
|
|
||||||
{
|
|
||||||
size = 0x4400000;
|
|
||||||
rc = Result.Success;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rc = Server.GetFsClient().GetFreeSpaceSize(out size, new U8Span(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetPassphrasePath(Span<byte> pathBuffer, ulong applicationId)
|
|
||||||
{
|
|
||||||
// returns "mount:/passphrase.bin"
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
var sb = new U8StringBuilder(pathBuffer);
|
|
||||||
AppendMountName(ref sb, applicationId);
|
|
||||||
sb.Append(PassphrasePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetDeliveryListPath(Span<byte> pathBuffer, ulong applicationId)
|
|
||||||
{
|
|
||||||
// returns "mount:/list.msgpack"
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
var sb = new U8StringBuilder(pathBuffer);
|
|
||||||
AppendMountName(ref sb, applicationId);
|
|
||||||
sb.Append(DeliveryListPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetEtagFilePath(Span<byte> pathBuffer, ulong applicationId)
|
|
||||||
{
|
|
||||||
// returns "mount:/etag.bin"
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
var sb = new U8StringBuilder(pathBuffer);
|
|
||||||
AppendMountName(ref sb, applicationId);
|
|
||||||
sb.Append(EtagPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetNaRequiredPath(Span<byte> pathBuffer, ulong applicationId)
|
|
||||||
{
|
|
||||||
// returns "mount:/na_required"
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
var sb = new U8StringBuilder(pathBuffer);
|
|
||||||
AppendMountName(ref sb, applicationId);
|
|
||||||
sb.Append(NaRequiredPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetIndexLockPath(Span<byte> pathBuffer, ulong applicationId)
|
|
||||||
{
|
|
||||||
// returns "mount:/index.lock"
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
var sb = new U8StringBuilder(pathBuffer);
|
|
||||||
AppendMountName(ref sb, applicationId);
|
|
||||||
sb.Append(IndexLockPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetFilePath(Span<byte> pathBuffer, ulong applicationId, ref DirectoryName directoryName,
|
|
||||||
ref FileName fileName)
|
|
||||||
{
|
|
||||||
// returns "mount:/directories/%s/files/%s", directoryName, fileName
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
var sb = new U8StringBuilder(pathBuffer);
|
|
||||||
AppendMountName(ref sb, applicationId);
|
|
||||||
|
|
||||||
sb.Append(DirectoriesPath)
|
|
||||||
.Append(DirectorySeparator).Append(directoryName.Bytes)
|
|
||||||
.Append(DirectorySeparator).Append(FilesDirectoryName)
|
|
||||||
.Append(DirectorySeparator).Append(fileName.Bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetFilesMetaPath(Span<byte> pathBuffer, ulong applicationId, ref DirectoryName directoryName)
|
|
||||||
{
|
|
||||||
// returns "mount:/directories/%s/files.meta", directoryName
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
var sb = new U8StringBuilder(pathBuffer);
|
|
||||||
AppendMountName(ref sb, applicationId);
|
|
||||||
|
|
||||||
sb.Append(DirectoriesPath)
|
|
||||||
.Append(DirectorySeparator).Append(directoryName.Bytes)
|
|
||||||
.Append(DirectorySeparator).Append(FilesMetaFileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetDirectoriesPath(Span<byte> pathBuffer, ulong applicationId)
|
|
||||||
{
|
|
||||||
// returns "mount:/directories"
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
var sb = new U8StringBuilder(pathBuffer);
|
|
||||||
AppendMountName(ref sb, applicationId);
|
|
||||||
sb.Append(DirectoriesPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetDirectoryPath(Span<byte> pathBuffer, ulong applicationId, ref DirectoryName directoryName)
|
|
||||||
{
|
|
||||||
// returns "mount:/directories/%s", directoryName
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
var sb = new U8StringBuilder(pathBuffer);
|
|
||||||
AppendMountName(ref sb, applicationId);
|
|
||||||
|
|
||||||
sb.Append(DirectoriesPath)
|
|
||||||
.Append(DirectorySeparator).Append(directoryName.Bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetDirectoriesMetaPath(Span<byte> pathBuffer, ulong applicationId)
|
|
||||||
{
|
|
||||||
// returns "mount:/directories.meta"
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
var sb = new U8StringBuilder(pathBuffer);
|
|
||||||
AppendMountName(ref sb, applicationId);
|
|
||||||
|
|
||||||
sb.Append(DirectoriesMetaPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AppendMountName(ref U8StringBuilder sb, ulong applicationId)
|
|
||||||
{
|
|
||||||
int index = FindEntry(applicationId);
|
|
||||||
|
|
||||||
sb.Append(DeliveryCacheMountNamePrefix)
|
|
||||||
.AppendFormat(index, 'd', 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result FindOrGetUnusedEntry(out int entryIndex, ulong applicationId)
|
|
||||||
{
|
|
||||||
UnsafeHelpers.SkipParamInit(out entryIndex);
|
|
||||||
|
|
||||||
// Try to find an existing entry
|
|
||||||
for (int i = 0; i < Entries.Length; i++)
|
|
||||||
{
|
|
||||||
if (Entries[i].ApplicationId == applicationId)
|
|
||||||
{
|
|
||||||
entryIndex = i;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to find an unused entry
|
|
||||||
for (int i = 0; i < Entries.Length; i++)
|
|
||||||
{
|
|
||||||
if (Entries[i].ApplicationId == 0)
|
|
||||||
{
|
|
||||||
entryIndex = i;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultBcat.StorageOpenLimitReached.Log();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int FindEntry(ulong applicationId)
|
|
||||||
{
|
|
||||||
Entry[] entries = Entries;
|
|
||||||
|
|
||||||
for (int i = 0; i < entries.Length; i++)
|
|
||||||
{
|
|
||||||
if (entries[i].ApplicationId == applicationId)
|
|
||||||
{
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nintendo uses 1 as the entry index if it wasn't found
|
|
||||||
Debug.Assert(false, "Entry not found.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> DeliveryCacheMountNamePrefix => // bcat-dc-
|
|
||||||
new[] { (byte)'b', (byte)'c', (byte)'a', (byte)'t', (byte)'-', (byte)'d', (byte)'c', (byte)'-' };
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> RootPath => // :/
|
|
||||||
new[] { (byte)':', (byte)'/' };
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> PassphrasePath => // :/passphrase.bin
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
(byte) ':', (byte) '/', (byte) 'p', (byte) 'a', (byte) 's', (byte) 's', (byte) 'p', (byte) 'h',
|
|
||||||
(byte) 'r', (byte) 'a', (byte) 's', (byte) 'e', (byte) '.', (byte) 'b', (byte) 'i', (byte) 'n'
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> DeliveryListPath => // :/list.msgpack
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
(byte) ':', (byte) '/', (byte) 'l', (byte) 'i', (byte) 's', (byte) 't', (byte) '.', (byte) 'm',
|
|
||||||
(byte) 's', (byte) 'g', (byte) 'p', (byte) 'a', (byte) 'c', (byte) 'k'
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> EtagPath => // :/etag.bin
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
(byte) ':', (byte) '/', (byte) 'e', (byte) 't', (byte) 'a', (byte) 'g', (byte) '.', (byte) 'b',
|
|
||||||
(byte) 'i', (byte) 'n'
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> NaRequiredPath => // :/na_required
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
(byte) ':', (byte) '/', (byte) 'n', (byte) 'a', (byte) '_', (byte) 'r', (byte) 'e', (byte) 'q',
|
|
||||||
(byte) 'u', (byte) 'i', (byte) 'r', (byte) 'e', (byte) 'd'
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> IndexLockPath => // :/index.lock
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
(byte) ':', (byte) '/', (byte) 'i', (byte) 'n', (byte) 'd', (byte) 'e', (byte) 'x', (byte) '.',
|
|
||||||
(byte) 'l', (byte) 'o', (byte) 'c', (byte) 'k'
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> DirectoriesPath => // :/directories
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
(byte) ':', (byte) '/', (byte) 'd', (byte) 'i', (byte) 'r', (byte) 'e', (byte) 'c', (byte) 't',
|
|
||||||
(byte) 'o', (byte) 'r', (byte) 'i', (byte) 'e', (byte) 's'
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> FilesMetaFileName => // files.meta
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
(byte) 'f', (byte) 'i', (byte) 'l', (byte) 'e', (byte) 's', (byte) '.', (byte) 'm', (byte) 'e',
|
|
||||||
(byte) 't', (byte) 'a'
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> DirectoriesMetaPath => // :/directories.meta
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
(byte) ':', (byte) '/', (byte) 'd', (byte) 'i', (byte) 'r', (byte) 'e', (byte) 'c', (byte) 't',
|
|
||||||
(byte) 'o', (byte) 'r', (byte) 'i', (byte) 'e', (byte) 's', (byte) '.', (byte) 'm', (byte) 'e',
|
|
||||||
(byte) 't', (byte) 'a'
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> FilesDirectoryName => // files
|
|
||||||
new[] { (byte)'f', (byte)'i', (byte)'l', (byte)'e', (byte)'s' };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int FindEntry(ulong applicationId)
|
||||||
|
{
|
||||||
|
Entry[] entries = Entries;
|
||||||
|
|
||||||
|
for (int i = 0; i < entries.Length; i++)
|
||||||
|
{
|
||||||
|
if (entries[i].ApplicationId == applicationId)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nintendo uses 1 as the entry index if it wasn't found
|
||||||
|
Debug.Assert(false, "Entry not found.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> DeliveryCacheMountNamePrefix => // bcat-dc-
|
||||||
|
new[] { (byte)'b', (byte)'c', (byte)'a', (byte)'t', (byte)'-', (byte)'d', (byte)'c', (byte)'-' };
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> RootPath => // :/
|
||||||
|
new[] { (byte)':', (byte)'/' };
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> PassphrasePath => // :/passphrase.bin
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte) ':', (byte) '/', (byte) 'p', (byte) 'a', (byte) 's', (byte) 's', (byte) 'p', (byte) 'h',
|
||||||
|
(byte) 'r', (byte) 'a', (byte) 's', (byte) 'e', (byte) '.', (byte) 'b', (byte) 'i', (byte) 'n'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> DeliveryListPath => // :/list.msgpack
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte) ':', (byte) '/', (byte) 'l', (byte) 'i', (byte) 's', (byte) 't', (byte) '.', (byte) 'm',
|
||||||
|
(byte) 's', (byte) 'g', (byte) 'p', (byte) 'a', (byte) 'c', (byte) 'k'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> EtagPath => // :/etag.bin
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte) ':', (byte) '/', (byte) 'e', (byte) 't', (byte) 'a', (byte) 'g', (byte) '.', (byte) 'b',
|
||||||
|
(byte) 'i', (byte) 'n'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> NaRequiredPath => // :/na_required
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte) ':', (byte) '/', (byte) 'n', (byte) 'a', (byte) '_', (byte) 'r', (byte) 'e', (byte) 'q',
|
||||||
|
(byte) 'u', (byte) 'i', (byte) 'r', (byte) 'e', (byte) 'd'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> IndexLockPath => // :/index.lock
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte) ':', (byte) '/', (byte) 'i', (byte) 'n', (byte) 'd', (byte) 'e', (byte) 'x', (byte) '.',
|
||||||
|
(byte) 'l', (byte) 'o', (byte) 'c', (byte) 'k'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> DirectoriesPath => // :/directories
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte) ':', (byte) '/', (byte) 'd', (byte) 'i', (byte) 'r', (byte) 'e', (byte) 'c', (byte) 't',
|
||||||
|
(byte) 'o', (byte) 'r', (byte) 'i', (byte) 'e', (byte) 's'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> FilesMetaFileName => // files.meta
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte) 'f', (byte) 'i', (byte) 'l', (byte) 'e', (byte) 's', (byte) '.', (byte) 'm', (byte) 'e',
|
||||||
|
(byte) 't', (byte) 'a'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> DirectoriesMetaPath => // :/directories.meta
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
(byte) ':', (byte) '/', (byte) 'd', (byte) 'i', (byte) 'r', (byte) 'e', (byte) 'c', (byte) 't',
|
||||||
|
(byte) 'o', (byte) 'r', (byte) 'i', (byte) 'e', (byte) 's', (byte) '.', (byte) 'm', (byte) 'e',
|
||||||
|
(byte) 't', (byte) 'a'
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> FilesDirectoryName => // files
|
||||||
|
new[] { (byte)'f', (byte)'i', (byte)'l', (byte)'e', (byte)'s' };
|
||||||
}
|
}
|
||||||
|
@ -3,105 +3,104 @@ using LibHac.Bcat.Impl.Ipc;
|
|||||||
using LibHac.Bcat.Impl.Service.Core;
|
using LibHac.Bcat.Impl.Service.Core;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Impl.Service
|
namespace LibHac.Bcat.Impl.Service;
|
||||||
|
|
||||||
|
internal class DeliveryCacheDirectoryService : IDeliveryCacheDirectoryService
|
||||||
{
|
{
|
||||||
internal class DeliveryCacheDirectoryService : IDeliveryCacheDirectoryService
|
private BcatServer Server { get; }
|
||||||
|
private object Locker { get; } = new object();
|
||||||
|
private DeliveryCacheStorageService Parent { get; }
|
||||||
|
|
||||||
|
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||||
|
private AccessControl Access { get; }
|
||||||
|
private ulong ApplicationId { get; }
|
||||||
|
private DirectoryName _name;
|
||||||
|
private bool IsDirectoryOpen { get; set; }
|
||||||
|
private int Count { get; set; }
|
||||||
|
|
||||||
|
public DeliveryCacheDirectoryService(BcatServer server, DeliveryCacheStorageService parent, ulong applicationId,
|
||||||
|
AccessControl accessControl)
|
||||||
{
|
{
|
||||||
private BcatServer Server { get; }
|
Server = server;
|
||||||
private object Locker { get; } = new object();
|
Parent = parent;
|
||||||
private DeliveryCacheStorageService Parent { get; }
|
ApplicationId = applicationId;
|
||||||
|
Access = accessControl;
|
||||||
|
}
|
||||||
|
|
||||||
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
public Result Open(ref DirectoryName name)
|
||||||
private AccessControl Access { get; }
|
{
|
||||||
private ulong ApplicationId { get; }
|
if (!name.IsValid())
|
||||||
private DirectoryName _name;
|
return ResultBcat.InvalidArgument.Log();
|
||||||
private bool IsDirectoryOpen { get; set; }
|
|
||||||
private int Count { get; set; }
|
|
||||||
|
|
||||||
public DeliveryCacheDirectoryService(BcatServer server, DeliveryCacheStorageService parent, ulong applicationId,
|
lock (Locker)
|
||||||
AccessControl accessControl)
|
|
||||||
{
|
{
|
||||||
Server = server;
|
if (IsDirectoryOpen)
|
||||||
Parent = parent;
|
return ResultBcat.AlreadyOpen.Log();
|
||||||
ApplicationId = applicationId;
|
|
||||||
Access = accessControl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result Open(ref DirectoryName name)
|
var metaReader = new DeliveryCacheFileMetaAccessor(Server);
|
||||||
{
|
Result rc = metaReader.ReadApplicationFileMeta(ApplicationId, ref name, false);
|
||||||
if (!name.IsValid())
|
if (rc.IsFailure()) return rc;
|
||||||
return ResultBcat.InvalidArgument.Log();
|
|
||||||
|
|
||||||
lock (Locker)
|
Count = metaReader.Count;
|
||||||
{
|
_name = name;
|
||||||
if (IsDirectoryOpen)
|
IsDirectoryOpen = true;
|
||||||
return ResultBcat.AlreadyOpen.Log();
|
|
||||||
|
|
||||||
var metaReader = new DeliveryCacheFileMetaAccessor(Server);
|
return Result.Success;
|
||||||
Result rc = metaReader.ReadApplicationFileMeta(ApplicationId, ref name, false);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
Count = metaReader.Count;
|
|
||||||
_name = name;
|
|
||||||
IsDirectoryOpen = true;
|
|
||||||
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result Read(out int entriesRead, Span<DeliveryCacheDirectoryEntry> entryBuffer)
|
|
||||||
{
|
|
||||||
UnsafeHelpers.SkipParamInit(out entriesRead);
|
|
||||||
|
|
||||||
lock (Locker)
|
|
||||||
{
|
|
||||||
if (!IsDirectoryOpen)
|
|
||||||
return ResultBcat.NotOpen.Log();
|
|
||||||
|
|
||||||
var metaReader = new DeliveryCacheFileMetaAccessor(Server);
|
|
||||||
Result rc = metaReader.ReadApplicationFileMeta(ApplicationId, ref _name, true);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < entryBuffer.Length; i++)
|
|
||||||
{
|
|
||||||
rc = metaReader.GetEntry(out DeliveryCacheFileMetaEntry entry, i);
|
|
||||||
|
|
||||||
if (rc.IsFailure())
|
|
||||||
{
|
|
||||||
if (!ResultBcat.NotFound.Includes(rc))
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
entryBuffer[i] = new DeliveryCacheDirectoryEntry(ref entry.Name, entry.Size, ref entry.Digest);
|
|
||||||
}
|
|
||||||
|
|
||||||
entriesRead = i;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result GetCount(out int count)
|
|
||||||
{
|
|
||||||
UnsafeHelpers.SkipParamInit(out count);
|
|
||||||
|
|
||||||
lock (Locker)
|
|
||||||
{
|
|
||||||
if (!IsDirectoryOpen)
|
|
||||||
{
|
|
||||||
return ResultBcat.NotOpen.Log();
|
|
||||||
}
|
|
||||||
|
|
||||||
count = Count;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Parent.NotifyCloseDirectory();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Result Read(out int entriesRead, Span<DeliveryCacheDirectoryEntry> entryBuffer)
|
||||||
|
{
|
||||||
|
UnsafeHelpers.SkipParamInit(out entriesRead);
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
if (!IsDirectoryOpen)
|
||||||
|
return ResultBcat.NotOpen.Log();
|
||||||
|
|
||||||
|
var metaReader = new DeliveryCacheFileMetaAccessor(Server);
|
||||||
|
Result rc = metaReader.ReadApplicationFileMeta(ApplicationId, ref _name, true);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < entryBuffer.Length; i++)
|
||||||
|
{
|
||||||
|
rc = metaReader.GetEntry(out DeliveryCacheFileMetaEntry entry, i);
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
if (!ResultBcat.NotFound.Includes(rc))
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
entryBuffer[i] = new DeliveryCacheDirectoryEntry(ref entry.Name, entry.Size, ref entry.Digest);
|
||||||
|
}
|
||||||
|
|
||||||
|
entriesRead = i;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result GetCount(out int count)
|
||||||
|
{
|
||||||
|
UnsafeHelpers.SkipParamInit(out count);
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
if (!IsDirectoryOpen)
|
||||||
|
{
|
||||||
|
return ResultBcat.NotOpen.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
count = Count;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Parent.NotifyCloseDirectory();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,119 +5,118 @@ using LibHac.Common;
|
|||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Impl.Service
|
namespace LibHac.Bcat.Impl.Service;
|
||||||
|
|
||||||
|
internal class DeliveryCacheFileService : IDeliveryCacheFileService
|
||||||
{
|
{
|
||||||
internal class DeliveryCacheFileService : IDeliveryCacheFileService
|
private BcatServer Server { get; }
|
||||||
|
private object Locker { get; } = new object();
|
||||||
|
private DeliveryCacheStorageService Parent { get; }
|
||||||
|
|
||||||
|
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||||
|
private AccessControl Access { get; }
|
||||||
|
private ulong ApplicationId { get; }
|
||||||
|
private FileHandle _handle;
|
||||||
|
private DeliveryCacheFileMetaEntry _metaEntry;
|
||||||
|
private bool IsFileOpen { get; set; }
|
||||||
|
|
||||||
|
public DeliveryCacheFileService(BcatServer server, DeliveryCacheStorageService parent, ulong applicationId,
|
||||||
|
AccessControl accessControl)
|
||||||
{
|
{
|
||||||
private BcatServer Server { get; }
|
Server = server;
|
||||||
private object Locker { get; } = new object();
|
Parent = parent;
|
||||||
private DeliveryCacheStorageService Parent { get; }
|
ApplicationId = applicationId;
|
||||||
|
Access = accessControl;
|
||||||
|
}
|
||||||
|
|
||||||
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
public Result Open(ref DirectoryName directoryName, ref FileName fileName)
|
||||||
private AccessControl Access { get; }
|
{
|
||||||
private ulong ApplicationId { get; }
|
if (!directoryName.IsValid())
|
||||||
private FileHandle _handle;
|
return ResultBcat.InvalidArgument.Log();
|
||||||
private DeliveryCacheFileMetaEntry _metaEntry;
|
|
||||||
private bool IsFileOpen { get; set; }
|
|
||||||
|
|
||||||
public DeliveryCacheFileService(BcatServer server, DeliveryCacheStorageService parent, ulong applicationId,
|
if (!fileName.IsValid())
|
||||||
AccessControl accessControl)
|
return ResultBcat.InvalidArgument.Log();
|
||||||
{
|
|
||||||
Server = server;
|
|
||||||
Parent = parent;
|
|
||||||
ApplicationId = applicationId;
|
|
||||||
Access = accessControl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result Open(ref DirectoryName directoryName, ref FileName fileName)
|
lock (Locker)
|
||||||
{
|
|
||||||
if (!directoryName.IsValid())
|
|
||||||
return ResultBcat.InvalidArgument.Log();
|
|
||||||
|
|
||||||
if (!fileName.IsValid())
|
|
||||||
return ResultBcat.InvalidArgument.Log();
|
|
||||||
|
|
||||||
lock (Locker)
|
|
||||||
{
|
|
||||||
if (IsFileOpen)
|
|
||||||
return ResultBcat.AlreadyOpen.Log();
|
|
||||||
|
|
||||||
var metaReader = new DeliveryCacheFileMetaAccessor(Server);
|
|
||||||
Result rc = metaReader.ReadApplicationFileMeta(ApplicationId, ref directoryName, true);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
rc = metaReader.FindEntry(out DeliveryCacheFileMetaEntry entry, ref fileName);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
Span<byte> filePath = stackalloc byte[0x80];
|
|
||||||
Server.GetStorageManager().GetFilePath(filePath, ApplicationId, ref directoryName, ref fileName);
|
|
||||||
|
|
||||||
rc = Server.GetFsClient().OpenFile(out _handle, new U8Span(filePath), OpenMode.Read);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
_metaEntry = entry;
|
|
||||||
IsFileOpen = true;
|
|
||||||
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result Read(out long bytesRead, long offset, Span<byte> destination)
|
|
||||||
{
|
|
||||||
lock (Locker)
|
|
||||||
{
|
|
||||||
bytesRead = 0;
|
|
||||||
|
|
||||||
if (!IsFileOpen)
|
|
||||||
return ResultBcat.NotOpen.Log();
|
|
||||||
|
|
||||||
Result rc = Server.GetFsClient().ReadFile(out long read, _handle, offset, destination);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
bytesRead = read;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result GetSize(out long size)
|
|
||||||
{
|
|
||||||
UnsafeHelpers.SkipParamInit(out size);
|
|
||||||
|
|
||||||
lock (Locker)
|
|
||||||
{
|
|
||||||
if (!IsFileOpen)
|
|
||||||
{
|
|
||||||
return ResultBcat.NotOpen.Log();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Server.GetFsClient().GetFileSize(out size, _handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result GetDigest(out Digest digest)
|
|
||||||
{
|
|
||||||
UnsafeHelpers.SkipParamInit(out digest);
|
|
||||||
|
|
||||||
lock (Locker)
|
|
||||||
{
|
|
||||||
if (!IsFileOpen)
|
|
||||||
{
|
|
||||||
return ResultBcat.NotOpen.Log();
|
|
||||||
}
|
|
||||||
|
|
||||||
digest = _metaEntry.Digest;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
{
|
||||||
if (IsFileOpen)
|
if (IsFileOpen)
|
||||||
{
|
return ResultBcat.AlreadyOpen.Log();
|
||||||
Server.GetFsClient().CloseFile(_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
Parent.NotifyCloseFile();
|
var metaReader = new DeliveryCacheFileMetaAccessor(Server);
|
||||||
|
Result rc = metaReader.ReadApplicationFileMeta(ApplicationId, ref directoryName, true);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = metaReader.FindEntry(out DeliveryCacheFileMetaEntry entry, ref fileName);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
Span<byte> filePath = stackalloc byte[0x80];
|
||||||
|
Server.GetStorageManager().GetFilePath(filePath, ApplicationId, ref directoryName, ref fileName);
|
||||||
|
|
||||||
|
rc = Server.GetFsClient().OpenFile(out _handle, new U8Span(filePath), OpenMode.Read);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
_metaEntry = entry;
|
||||||
|
IsFileOpen = true;
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Result Read(out long bytesRead, long offset, Span<byte> destination)
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
bytesRead = 0;
|
||||||
|
|
||||||
|
if (!IsFileOpen)
|
||||||
|
return ResultBcat.NotOpen.Log();
|
||||||
|
|
||||||
|
Result rc = Server.GetFsClient().ReadFile(out long read, _handle, offset, destination);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
bytesRead = read;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result GetSize(out long size)
|
||||||
|
{
|
||||||
|
UnsafeHelpers.SkipParamInit(out size);
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
if (!IsFileOpen)
|
||||||
|
{
|
||||||
|
return ResultBcat.NotOpen.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Server.GetFsClient().GetFileSize(out size, _handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result GetDigest(out Digest digest)
|
||||||
|
{
|
||||||
|
UnsafeHelpers.SkipParamInit(out digest);
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
if (!IsFileOpen)
|
||||||
|
{
|
||||||
|
return ResultBcat.NotOpen.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
digest = _metaEntry.Digest;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (IsFileOpen)
|
||||||
|
{
|
||||||
|
Server.GetFsClient().CloseFile(_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Parent.NotifyCloseFile();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,108 +5,107 @@ using LibHac.Bcat.Impl.Service.Core;
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Impl.Service
|
namespace LibHac.Bcat.Impl.Service;
|
||||||
|
|
||||||
|
internal class DeliveryCacheStorageService : IDeliveryCacheStorageService
|
||||||
{
|
{
|
||||||
internal class DeliveryCacheStorageService : IDeliveryCacheStorageService
|
private const int MaxOpenCount = 8;
|
||||||
|
private BcatServer Server { get; }
|
||||||
|
|
||||||
|
private object Locker { get; } = new object();
|
||||||
|
private AccessControl Access { get; }
|
||||||
|
private ulong ApplicationId { get; }
|
||||||
|
private int FileServiceOpenCount { get; set; }
|
||||||
|
private int DirectoryServiceOpenCount { get; set; }
|
||||||
|
|
||||||
|
public DeliveryCacheStorageService(BcatServer server, ulong applicationId, AccessControl accessControl)
|
||||||
{
|
{
|
||||||
private const int MaxOpenCount = 8;
|
Server = server;
|
||||||
private BcatServer Server { get; }
|
ApplicationId = applicationId;
|
||||||
|
Access = accessControl;
|
||||||
|
}
|
||||||
|
|
||||||
private object Locker { get; } = new object();
|
public Result CreateFileService(ref SharedRef<IDeliveryCacheFileService> service)
|
||||||
private AccessControl Access { get; }
|
{
|
||||||
private ulong ApplicationId { get; }
|
lock (Locker)
|
||||||
private int FileServiceOpenCount { get; set; }
|
|
||||||
private int DirectoryServiceOpenCount { get; set; }
|
|
||||||
|
|
||||||
public DeliveryCacheStorageService(BcatServer server, ulong applicationId, AccessControl accessControl)
|
|
||||||
{
|
{
|
||||||
Server = server;
|
if (FileServiceOpenCount >= MaxOpenCount)
|
||||||
ApplicationId = applicationId;
|
return ResultBcat.ServiceOpenLimitReached.Log();
|
||||||
Access = accessControl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result CreateFileService(ref SharedRef<IDeliveryCacheFileService> service)
|
service.Reset(new DeliveryCacheFileService(Server, this, ApplicationId, Access));
|
||||||
{
|
|
||||||
lock (Locker)
|
|
||||||
{
|
|
||||||
if (FileServiceOpenCount >= MaxOpenCount)
|
|
||||||
return ResultBcat.ServiceOpenLimitReached.Log();
|
|
||||||
|
|
||||||
service.Reset(new DeliveryCacheFileService(Server, this, ApplicationId, Access));
|
FileServiceOpenCount++;
|
||||||
|
return Result.Success;
|
||||||
FileServiceOpenCount++;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result CreateDirectoryService(ref SharedRef<IDeliveryCacheDirectoryService> service)
|
|
||||||
{
|
|
||||||
lock (Locker)
|
|
||||||
{
|
|
||||||
if (DirectoryServiceOpenCount >= MaxOpenCount)
|
|
||||||
return ResultBcat.ServiceOpenLimitReached.Log();
|
|
||||||
|
|
||||||
service.Reset(new DeliveryCacheDirectoryService(Server, this, ApplicationId, Access));
|
|
||||||
|
|
||||||
DirectoryServiceOpenCount++;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result EnumerateDeliveryCacheDirectory(out int namesRead, Span<DirectoryName> nameBuffer)
|
|
||||||
{
|
|
||||||
UnsafeHelpers.SkipParamInit(out namesRead);
|
|
||||||
|
|
||||||
lock (Locker)
|
|
||||||
{
|
|
||||||
var metaReader = new DeliveryCacheDirectoryMetaAccessor(Server);
|
|
||||||
Result rc = metaReader.ReadApplicationDirectoryMeta(ApplicationId, true);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < nameBuffer.Length; i++)
|
|
||||||
{
|
|
||||||
rc = metaReader.GetEntry(out DeliveryCacheDirectoryMetaEntry entry, i);
|
|
||||||
|
|
||||||
if (rc.IsFailure())
|
|
||||||
{
|
|
||||||
if (!ResultBcat.NotFound.Includes(rc))
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringUtils.Copy(nameBuffer[i].Bytes, entry.Name.Bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
namesRead = i;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void NotifyCloseFile()
|
|
||||||
{
|
|
||||||
lock (Locker)
|
|
||||||
{
|
|
||||||
FileServiceOpenCount--;
|
|
||||||
|
|
||||||
Debug.Assert(FileServiceOpenCount >= 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void NotifyCloseDirectory()
|
|
||||||
{
|
|
||||||
lock (Locker)
|
|
||||||
{
|
|
||||||
DirectoryServiceOpenCount--;
|
|
||||||
|
|
||||||
Debug.Assert(DirectoryServiceOpenCount >= 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Server.GetStorageManager().Release(ApplicationId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Result CreateDirectoryService(ref SharedRef<IDeliveryCacheDirectoryService> service)
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
if (DirectoryServiceOpenCount >= MaxOpenCount)
|
||||||
|
return ResultBcat.ServiceOpenLimitReached.Log();
|
||||||
|
|
||||||
|
service.Reset(new DeliveryCacheDirectoryService(Server, this, ApplicationId, Access));
|
||||||
|
|
||||||
|
DirectoryServiceOpenCount++;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result EnumerateDeliveryCacheDirectory(out int namesRead, Span<DirectoryName> nameBuffer)
|
||||||
|
{
|
||||||
|
UnsafeHelpers.SkipParamInit(out namesRead);
|
||||||
|
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
var metaReader = new DeliveryCacheDirectoryMetaAccessor(Server);
|
||||||
|
Result rc = metaReader.ReadApplicationDirectoryMeta(ApplicationId, true);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < nameBuffer.Length; i++)
|
||||||
|
{
|
||||||
|
rc = metaReader.GetEntry(out DeliveryCacheDirectoryMetaEntry entry, i);
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
if (!ResultBcat.NotFound.Includes(rc))
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringUtils.Copy(nameBuffer[i].Bytes, entry.Name.Bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
namesRead = i;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void NotifyCloseFile()
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
FileServiceOpenCount--;
|
||||||
|
|
||||||
|
Debug.Assert(FileServiceOpenCount >= 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void NotifyCloseDirectory()
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
DirectoryServiceOpenCount--;
|
||||||
|
|
||||||
|
Debug.Assert(DirectoryServiceOpenCount >= 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Server.GetStorageManager().Release(ApplicationId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,58 +2,57 @@
|
|||||||
using LibHac.Bcat.Impl.Ipc;
|
using LibHac.Bcat.Impl.Ipc;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
|
||||||
namespace LibHac.Bcat.Impl.Service
|
namespace LibHac.Bcat.Impl.Service;
|
||||||
|
|
||||||
|
// Todo: Update BCAT service object management
|
||||||
|
internal class ServiceCreator : IServiceCreator
|
||||||
{
|
{
|
||||||
// Todo: Update BCAT service object management
|
private BcatServer Server { get; }
|
||||||
internal class ServiceCreator : IServiceCreator
|
|
||||||
|
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||||
|
private string ServiceName { get; }
|
||||||
|
private AccessControl AccessControl { get; }
|
||||||
|
|
||||||
|
public ServiceCreator(BcatServer server, string serviceName, AccessControl accessControl)
|
||||||
{
|
{
|
||||||
private BcatServer Server { get; }
|
Server = server;
|
||||||
|
ServiceName = serviceName;
|
||||||
|
AccessControl = accessControl;
|
||||||
|
}
|
||||||
|
|
||||||
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
public void Dispose() { }
|
||||||
private string ServiceName { get; }
|
|
||||||
private AccessControl AccessControl { get; }
|
|
||||||
|
|
||||||
public ServiceCreator(BcatServer server, string serviceName, AccessControl accessControl)
|
public Result CreateDeliveryCacheStorageService(ref SharedRef<IDeliveryCacheStorageService> outService,
|
||||||
{
|
ulong processId)
|
||||||
Server = server;
|
{
|
||||||
ServiceName = serviceName;
|
Result rc = Server.Hos.Arp.GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty,
|
||||||
AccessControl = accessControl;
|
processId);
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() { }
|
if (rc.IsFailure())
|
||||||
|
return ResultBcat.NotFound.LogConverted(rc);
|
||||||
|
|
||||||
public Result CreateDeliveryCacheStorageService(ref SharedRef<IDeliveryCacheStorageService> outService,
|
return CreateDeliveryCacheStorageServiceImpl(ref outService, launchProperty.ApplicationId);
|
||||||
ulong processId)
|
}
|
||||||
{
|
|
||||||
Result rc = Server.Hos.Arp.GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty,
|
|
||||||
processId);
|
|
||||||
|
|
||||||
if (rc.IsFailure())
|
public Result CreateDeliveryCacheStorageServiceWithApplicationId(
|
||||||
return ResultBcat.NotFound.LogConverted(rc);
|
ref SharedRef<IDeliveryCacheStorageService> outService, ApplicationId applicationId)
|
||||||
|
{
|
||||||
|
if (!AccessControl.HasFlag(AccessControl.MountOthersDeliveryCacheStorage))
|
||||||
|
return ResultBcat.PermissionDenied.Log();
|
||||||
|
|
||||||
return CreateDeliveryCacheStorageServiceImpl(ref outService, launchProperty.ApplicationId);
|
return CreateDeliveryCacheStorageServiceImpl(ref outService, applicationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result CreateDeliveryCacheStorageServiceWithApplicationId(
|
private Result CreateDeliveryCacheStorageServiceImpl(ref SharedRef<IDeliveryCacheStorageService> outService,
|
||||||
ref SharedRef<IDeliveryCacheStorageService> outService, ApplicationId applicationId)
|
ApplicationId applicationId)
|
||||||
{
|
{
|
||||||
if (!AccessControl.HasFlag(AccessControl.MountOthersDeliveryCacheStorage))
|
Result rc = Server.GetStorageManager().Open(applicationId.Value);
|
||||||
return ResultBcat.PermissionDenied.Log();
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
return CreateDeliveryCacheStorageServiceImpl(ref outService, applicationId);
|
// todo: Check if network account required
|
||||||
}
|
|
||||||
|
|
||||||
private Result CreateDeliveryCacheStorageServiceImpl(ref SharedRef<IDeliveryCacheStorageService> outService,
|
outService.Reset(new DeliveryCacheStorageService(Server, applicationId.Value, AccessControl));
|
||||||
ApplicationId applicationId)
|
|
||||||
{
|
|
||||||
Result rc = Server.GetStorageManager().Open(applicationId.Value);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
// todo: Check if network account required
|
return Result.Success;
|
||||||
|
|
||||||
outService.Reset(new DeliveryCacheStorageService(Server, applicationId.Value, AccessControl));
|
|
||||||
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,47 +9,46 @@
|
|||||||
// code generation portion of the build.
|
// code generation portion of the build.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace LibHac.Bcat
|
namespace LibHac.Bcat;
|
||||||
{
|
|
||||||
public static class ResultBcat
|
|
||||||
{
|
|
||||||
public const int ModuleBcat = 122;
|
|
||||||
|
|
||||||
/// <summary>Error code: 2122-0001; Inner value: 0x27a</summary>
|
public static class ResultBcat
|
||||||
public static Result.Base InvalidArgument => new Result.Base(ModuleBcat, 1);
|
{
|
||||||
/// <summary>Error code: 2122-0002; Inner value: 0x47a</summary>
|
public const int ModuleBcat = 122;
|
||||||
public static Result.Base NotFound => new Result.Base(ModuleBcat, 2);
|
|
||||||
/// <summary>Error code: 2122-0003; Inner value: 0x67a</summary>
|
/// <summary>Error code: 2122-0001; Inner value: 0x27a</summary>
|
||||||
public static Result.Base TargetLocked => new Result.Base(ModuleBcat, 3);
|
public static Result.Base InvalidArgument => new Result.Base(ModuleBcat, 1);
|
||||||
/// <summary>Error code: 2122-0004; Inner value: 0x87a</summary>
|
/// <summary>Error code: 2122-0002; Inner value: 0x47a</summary>
|
||||||
public static Result.Base TargetAlreadyMounted => new Result.Base(ModuleBcat, 4);
|
public static Result.Base NotFound => new Result.Base(ModuleBcat, 2);
|
||||||
/// <summary>Error code: 2122-0005; Inner value: 0xa7a</summary>
|
/// <summary>Error code: 2122-0003; Inner value: 0x67a</summary>
|
||||||
public static Result.Base TargetNotMounted => new Result.Base(ModuleBcat, 5);
|
public static Result.Base TargetLocked => new Result.Base(ModuleBcat, 3);
|
||||||
/// <summary>Error code: 2122-0006; Inner value: 0xc7a</summary>
|
/// <summary>Error code: 2122-0004; Inner value: 0x87a</summary>
|
||||||
public static Result.Base AlreadyOpen => new Result.Base(ModuleBcat, 6);
|
public static Result.Base TargetAlreadyMounted => new Result.Base(ModuleBcat, 4);
|
||||||
/// <summary>Error code: 2122-0007; Inner value: 0xe7a</summary>
|
/// <summary>Error code: 2122-0005; Inner value: 0xa7a</summary>
|
||||||
public static Result.Base NotOpen => new Result.Base(ModuleBcat, 7);
|
public static Result.Base TargetNotMounted => new Result.Base(ModuleBcat, 5);
|
||||||
/// <summary>Error code: 2122-0008; Inner value: 0x107a</summary>
|
/// <summary>Error code: 2122-0006; Inner value: 0xc7a</summary>
|
||||||
public static Result.Base InternetRequestDenied => new Result.Base(ModuleBcat, 8);
|
public static Result.Base AlreadyOpen => new Result.Base(ModuleBcat, 6);
|
||||||
/// <summary>Error code: 2122-0009; Inner value: 0x127a</summary>
|
/// <summary>Error code: 2122-0007; Inner value: 0xe7a</summary>
|
||||||
public static Result.Base ServiceOpenLimitReached => new Result.Base(ModuleBcat, 9);
|
public static Result.Base NotOpen => new Result.Base(ModuleBcat, 7);
|
||||||
/// <summary>Error code: 2122-0010; Inner value: 0x147a</summary>
|
/// <summary>Error code: 2122-0008; Inner value: 0x107a</summary>
|
||||||
public static Result.Base SaveDataNotFound => new Result.Base(ModuleBcat, 10);
|
public static Result.Base InternetRequestDenied => new Result.Base(ModuleBcat, 8);
|
||||||
/// <summary>Error code: 2122-0031; Inner value: 0x3e7a</summary>
|
/// <summary>Error code: 2122-0009; Inner value: 0x127a</summary>
|
||||||
public static Result.Base NetworkServiceAccountNotAvailable => new Result.Base(ModuleBcat, 31);
|
public static Result.Base ServiceOpenLimitReached => new Result.Base(ModuleBcat, 9);
|
||||||
/// <summary>Error code: 2122-0080; Inner value: 0xa07a</summary>
|
/// <summary>Error code: 2122-0010; Inner value: 0x147a</summary>
|
||||||
public static Result.Base PassphrasePathNotFound => new Result.Base(ModuleBcat, 80);
|
public static Result.Base SaveDataNotFound => new Result.Base(ModuleBcat, 10);
|
||||||
/// <summary>Error code: 2122-0081; Inner value: 0xa27a</summary>
|
/// <summary>Error code: 2122-0031; Inner value: 0x3e7a</summary>
|
||||||
public static Result.Base DataVerificationFailed => new Result.Base(ModuleBcat, 81);
|
public static Result.Base NetworkServiceAccountNotAvailable => new Result.Base(ModuleBcat, 31);
|
||||||
/// <summary>Error code: 2122-0090; Inner value: 0xb47a</summary>
|
/// <summary>Error code: 2122-0080; Inner value: 0xa07a</summary>
|
||||||
public static Result.Base PermissionDenied => new Result.Base(ModuleBcat, 90);
|
public static Result.Base PassphrasePathNotFound => new Result.Base(ModuleBcat, 80);
|
||||||
/// <summary>Error code: 2122-0091; Inner value: 0xb67a</summary>
|
/// <summary>Error code: 2122-0081; Inner value: 0xa27a</summary>
|
||||||
public static Result.Base AllocationFailed => new Result.Base(ModuleBcat, 91);
|
public static Result.Base DataVerificationFailed => new Result.Base(ModuleBcat, 81);
|
||||||
/// <summary>Error code: 2122-0098; Inner value: 0xc47a</summary>
|
/// <summary>Error code: 2122-0090; Inner value: 0xb47a</summary>
|
||||||
public static Result.Base InvalidOperation => new Result.Base(ModuleBcat, 98);
|
public static Result.Base PermissionDenied => new Result.Base(ModuleBcat, 90);
|
||||||
/// <summary>Error code: 2122-0204; Inner value: 0x1987a</summary>
|
/// <summary>Error code: 2122-0091; Inner value: 0xb67a</summary>
|
||||||
public static Result.Base InvalidDeliveryCacheStorageFile => new Result.Base(ModuleBcat, 204);
|
public static Result.Base AllocationFailed => new Result.Base(ModuleBcat, 91);
|
||||||
/// <summary>Error code: 2122-0205; Inner value: 0x19a7a</summary>
|
/// <summary>Error code: 2122-0098; Inner value: 0xc47a</summary>
|
||||||
public static Result.Base StorageOpenLimitReached => new Result.Base(ModuleBcat, 205);
|
public static Result.Base InvalidOperation => new Result.Base(ModuleBcat, 98);
|
||||||
}
|
/// <summary>Error code: 2122-0204; Inner value: 0x1987a</summary>
|
||||||
|
public static Result.Base InvalidDeliveryCacheStorageFile => new Result.Base(ModuleBcat, 204);
|
||||||
|
/// <summary>Error code: 2122-0205; Inner value: 0x19a7a</summary>
|
||||||
|
public static Result.Base StorageOpenLimitReached => new Result.Base(ModuleBcat, 205);
|
||||||
}
|
}
|
||||||
|
@ -1,133 +1,132 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace LibHac
|
namespace LibHac;
|
||||||
|
|
||||||
|
public class BitReader
|
||||||
{
|
{
|
||||||
public class BitReader
|
public byte[] Buffer { get; private set; }
|
||||||
|
public int LengthBits { get; private set; }
|
||||||
|
public int Position { get; set; }
|
||||||
|
public int Remaining => LengthBits - Position;
|
||||||
|
|
||||||
|
public BitReader(byte[] buffer) => SetBuffer(buffer);
|
||||||
|
|
||||||
|
public void SetBuffer(byte[] buffer)
|
||||||
{
|
{
|
||||||
public byte[] Buffer { get; private set; }
|
Buffer = buffer;
|
||||||
public int LengthBits { get; private set; }
|
LengthBits = Buffer?.Length * 8 ?? 0;
|
||||||
public int Position { get; set; }
|
Position = 0;
|
||||||
public int Remaining => LengthBits - Position;
|
}
|
||||||
|
|
||||||
public BitReader(byte[] buffer) => SetBuffer(buffer);
|
public int ReadInt(int bitCount)
|
||||||
|
{
|
||||||
|
int value = PeekInt(bitCount);
|
||||||
|
Position += bitCount;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetBuffer(byte[] buffer)
|
//public int ReadSignedInt(int bitCount)
|
||||||
|
//{
|
||||||
|
// int value = PeekInt(bitCount);
|
||||||
|
// Position += bitCount;
|
||||||
|
// return Bit.SignExtend32(value, bitCount);
|
||||||
|
//}
|
||||||
|
|
||||||
|
public bool ReadBool() => ReadInt(1) == 1;
|
||||||
|
|
||||||
|
public int ReadOffsetBinary(int bitCount, OffsetBias bias)
|
||||||
|
{
|
||||||
|
int offset = (1 << (bitCount - 1)) - (int)bias;
|
||||||
|
int value = PeekInt(bitCount) - offset;
|
||||||
|
Position += bitCount;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//public void AlignPosition(int multiple)
|
||||||
|
//{
|
||||||
|
// Position = Helpers.GetNextMultiple(Position, multiple);
|
||||||
|
//}
|
||||||
|
|
||||||
|
public int PeekInt(int bitCount)
|
||||||
|
{
|
||||||
|
Debug.Assert(bitCount >= 0 && bitCount <= 32);
|
||||||
|
|
||||||
|
if (bitCount > Remaining)
|
||||||
{
|
{
|
||||||
Buffer = buffer;
|
if (Position >= LengthBits) return 0;
|
||||||
LengthBits = Buffer?.Length * 8 ?? 0;
|
|
||||||
Position = 0;
|
int extraBits = bitCount - Remaining;
|
||||||
|
return PeekIntFallback(Remaining) << extraBits;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ReadInt(int bitCount)
|
int byteIndex = Position / 8;
|
||||||
|
int bitIndex = Position % 8;
|
||||||
|
|
||||||
|
if (bitCount <= 9 && Remaining >= 16)
|
||||||
{
|
{
|
||||||
int value = PeekInt(bitCount);
|
int value = Buffer[byteIndex] << 8 | Buffer[byteIndex + 1];
|
||||||
Position += bitCount;
|
value &= 0xFFFF >> bitIndex;
|
||||||
|
value >>= 16 - bitCount - bitIndex;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//public int ReadSignedInt(int bitCount)
|
if (bitCount <= 17 && Remaining >= 24)
|
||||||
//{
|
|
||||||
// int value = PeekInt(bitCount);
|
|
||||||
// Position += bitCount;
|
|
||||||
// return Bit.SignExtend32(value, bitCount);
|
|
||||||
//}
|
|
||||||
|
|
||||||
public bool ReadBool() => ReadInt(1) == 1;
|
|
||||||
|
|
||||||
public int ReadOffsetBinary(int bitCount, OffsetBias bias)
|
|
||||||
{
|
{
|
||||||
int offset = (1 << (bitCount - 1)) - (int)bias;
|
int value = Buffer[byteIndex] << 16 | Buffer[byteIndex + 1] << 8 | Buffer[byteIndex + 2];
|
||||||
int value = PeekInt(bitCount) - offset;
|
value &= 0xFFFFFF >> bitIndex;
|
||||||
Position += bitCount;
|
value >>= 24 - bitCount - bitIndex;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//public void AlignPosition(int multiple)
|
if (bitCount <= 25 && Remaining >= 32)
|
||||||
//{
|
|
||||||
// Position = Helpers.GetNextMultiple(Position, multiple);
|
|
||||||
//}
|
|
||||||
|
|
||||||
public int PeekInt(int bitCount)
|
|
||||||
{
|
{
|
||||||
Debug.Assert(bitCount >= 0 && bitCount <= 32);
|
int value = Buffer[byteIndex] << 24 | Buffer[byteIndex + 1] << 16 | Buffer[byteIndex + 2] << 8 | Buffer[byteIndex + 3];
|
||||||
|
value &= (int)(0xFFFFFFFF >> bitIndex);
|
||||||
if (bitCount > Remaining)
|
value >>= 32 - bitCount - bitIndex;
|
||||||
{
|
|
||||||
if (Position >= LengthBits) return 0;
|
|
||||||
|
|
||||||
int extraBits = bitCount - Remaining;
|
|
||||||
return PeekIntFallback(Remaining) << extraBits;
|
|
||||||
}
|
|
||||||
|
|
||||||
int byteIndex = Position / 8;
|
|
||||||
int bitIndex = Position % 8;
|
|
||||||
|
|
||||||
if (bitCount <= 9 && Remaining >= 16)
|
|
||||||
{
|
|
||||||
int value = Buffer[byteIndex] << 8 | Buffer[byteIndex + 1];
|
|
||||||
value &= 0xFFFF >> bitIndex;
|
|
||||||
value >>= 16 - bitCount - bitIndex;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bitCount <= 17 && Remaining >= 24)
|
|
||||||
{
|
|
||||||
int value = Buffer[byteIndex] << 16 | Buffer[byteIndex + 1] << 8 | Buffer[byteIndex + 2];
|
|
||||||
value &= 0xFFFFFF >> bitIndex;
|
|
||||||
value >>= 24 - bitCount - bitIndex;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bitCount <= 25 && Remaining >= 32)
|
|
||||||
{
|
|
||||||
int value = Buffer[byteIndex] << 24 | Buffer[byteIndex + 1] << 16 | Buffer[byteIndex + 2] << 8 | Buffer[byteIndex + 3];
|
|
||||||
value &= (int)(0xFFFFFFFF >> bitIndex);
|
|
||||||
value >>= 32 - bitCount - bitIndex;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return PeekIntFallback(bitCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int PeekIntFallback(int bitCount)
|
|
||||||
{
|
|
||||||
int value = 0;
|
|
||||||
int byteIndex = Position / 8;
|
|
||||||
int bitIndex = Position % 8;
|
|
||||||
|
|
||||||
while (bitCount > 0)
|
|
||||||
{
|
|
||||||
if (bitIndex >= 8)
|
|
||||||
{
|
|
||||||
bitIndex = 0;
|
|
||||||
byteIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bitsToRead = Math.Min(bitCount, 8 - bitIndex);
|
|
||||||
int mask = 0xFF >> bitIndex;
|
|
||||||
int currentByte = (mask & Buffer[byteIndex]) >> (8 - bitIndex - bitsToRead);
|
|
||||||
|
|
||||||
value = (value << bitsToRead) | currentByte;
|
|
||||||
bitIndex += bitsToRead;
|
|
||||||
bitCount -= bitsToRead;
|
|
||||||
}
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
return PeekIntFallback(bitCount);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
private int PeekIntFallback(int bitCount)
|
||||||
/// Specifies the bias of an offset binary value. A positive bias can represent one more
|
{
|
||||||
/// positive value than negative value, and a negative bias can represent one more
|
int value = 0;
|
||||||
/// negative value than positive value.
|
int byteIndex = Position / 8;
|
||||||
/// </summary>
|
int bitIndex = Position % 8;
|
||||||
/// <remarks>Example:
|
|
||||||
/// A 4-bit offset binary value with a positive bias can store
|
while (bitCount > 0)
|
||||||
/// the values 8 through -7 inclusive.
|
|
||||||
/// A 4-bit offset binary value with a negative bias can store
|
|
||||||
/// the values 7 through -8 inclusive.</remarks>
|
|
||||||
public enum OffsetBias
|
|
||||||
{
|
{
|
||||||
Positive = 1,
|
if (bitIndex >= 8)
|
||||||
Negative = 0
|
{
|
||||||
|
bitIndex = 0;
|
||||||
|
byteIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bitsToRead = Math.Min(bitCount, 8 - bitIndex);
|
||||||
|
int mask = 0xFF >> bitIndex;
|
||||||
|
int currentByte = (mask & Buffer[byteIndex]) >> (8 - bitIndex - bitsToRead);
|
||||||
|
|
||||||
|
value = (value << bitsToRead) | currentByte;
|
||||||
|
bitIndex += bitsToRead;
|
||||||
|
bitCount -= bitsToRead;
|
||||||
}
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the bias of an offset binary value. A positive bias can represent one more
|
||||||
|
/// positive value than negative value, and a negative bias can represent one more
|
||||||
|
/// negative value than positive value.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Example:
|
||||||
|
/// A 4-bit offset binary value with a positive bias can store
|
||||||
|
/// the values 8 through -7 inclusive.
|
||||||
|
/// A 4-bit offset binary value with a negative bias can store
|
||||||
|
/// the values 7 through -8 inclusive.</remarks>
|
||||||
|
public enum OffsetBias
|
||||||
|
{
|
||||||
|
Positive = 1,
|
||||||
|
Negative = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
namespace LibHac
|
namespace LibHac;
|
||||||
|
|
||||||
|
public static class BitTools
|
||||||
{
|
{
|
||||||
public static class BitTools
|
public static int SignExtend32(int value, int bits)
|
||||||
{
|
{
|
||||||
public static int SignExtend32(int value, int bits)
|
int shift = 8 * sizeof(int) - bits;
|
||||||
{
|
return (value << shift) >> shift;
|
||||||
int shift = 8 * sizeof(int) - bits;
|
|
||||||
return (value << shift) >> shift;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,89 +6,88 @@ using LibHac.Common;
|
|||||||
using LibHac.Crypto;
|
using LibHac.Crypto;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Boot
|
namespace LibHac.Boot;
|
||||||
|
|
||||||
|
[DebuggerDisplay("{ToString()}")]
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 0xB0)]
|
||||||
|
public struct EncryptedKeyBlob
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{ToString()}")]
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0xB0)]
|
|
||||||
public struct EncryptedKeyBlob
|
|
||||||
{
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
[FieldOffset(0x00)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy1;
|
[FieldOffset(0x00)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy1;
|
||||||
[FieldOffset(0x20)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy2;
|
[FieldOffset(0x20)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy2;
|
||||||
[FieldOffset(0x40)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy3;
|
[FieldOffset(0x40)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy3;
|
||||||
[FieldOffset(0x60)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy4;
|
[FieldOffset(0x60)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy4;
|
||||||
[FieldOffset(0x80)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy5;
|
[FieldOffset(0x80)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy5;
|
||||||
[FieldOffset(0xA0)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer16 _dummy6;
|
[FieldOffset(0xA0)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer16 _dummy6;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
[FieldOffset(0x00)] public AesCmac Cmac;
|
[FieldOffset(0x00)] public AesCmac Cmac;
|
||||||
[FieldOffset(0x10)] public AesIv Counter;
|
[FieldOffset(0x10)] public AesIv Counter;
|
||||||
|
|
||||||
public Span<byte> Payload => Bytes.Slice(0x20, Unsafe.SizeOf<KeyBlob>());
|
public Span<byte> Payload => Bytes.Slice(0x20, Unsafe.SizeOf<KeyBlob>());
|
||||||
|
|
||||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||||
public readonly ReadOnlySpan<byte> ReadOnlyBytes => SpanHelpers.AsReadOnlyByteSpan(in this);
|
public readonly ReadOnlySpan<byte> ReadOnlyBytes => SpanHelpers.AsReadOnlyByteSpan(in this);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public readonly bool IsZeros()
|
public readonly bool IsZeros()
|
||||||
{
|
|
||||||
ReadOnlySpan<ulong> ulongSpan = MemoryMarshal.Cast<byte, ulong>(ReadOnlyBytes);
|
|
||||||
|
|
||||||
for (int i = 0; i < ulongSpan.Length; i++)
|
|
||||||
{
|
|
||||||
if (ulongSpan[i] != 0)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static implicit operator ReadOnlySpan<byte>(in EncryptedKeyBlob value)
|
|
||||||
{
|
|
||||||
return SpanHelpers.AsReadOnlyByteSpan(in value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override readonly string ToString() => ReadOnlyBytes.ToHexString();
|
|
||||||
}
|
|
||||||
|
|
||||||
[DebuggerDisplay("{ToString()}")]
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x90)]
|
|
||||||
public struct KeyBlob
|
|
||||||
{
|
{
|
||||||
#if DEBUG
|
ReadOnlySpan<ulong> ulongSpan = MemoryMarshal.Cast<byte, ulong>(ReadOnlyBytes);
|
||||||
[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;
|
for (int i = 0; i < ulongSpan.Length; i++)
|
||||||
[FieldOffset(0x80)] public AesKey Package1Key;
|
|
||||||
|
|
||||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
|
||||||
public readonly ReadOnlySpan<byte> ReadOnlyBytes => SpanHelpers.AsReadOnlyByteSpan(in this);
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly bool IsZeros()
|
|
||||||
{
|
{
|
||||||
ReadOnlySpan<ulong> ulongSpan = MemoryMarshal.Cast<byte, ulong>(ReadOnlyBytes);
|
if (ulongSpan[i] != 0)
|
||||||
|
return false;
|
||||||
for (int i = 0; i < ulongSpan.Length; i++)
|
|
||||||
{
|
|
||||||
if (ulongSpan[i] != 0)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
return true;
|
||||||
public static implicit operator ReadOnlySpan<byte>(in KeyBlob value)
|
|
||||||
{
|
|
||||||
return SpanHelpers.AsReadOnlyByteSpan(in value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override readonly string ToString() => ReadOnlyBytes.ToHexString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static implicit operator ReadOnlySpan<byte>(in EncryptedKeyBlob value)
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsReadOnlyByteSpan(in value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override readonly string ToString() => ReadOnlyBytes.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 Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||||
|
public readonly ReadOnlySpan<byte> ReadOnlyBytes => 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++)
|
||||||
|
{
|
||||||
|
if (ulongSpan[i] != 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static implicit operator ReadOnlySpan<byte>(in KeyBlob value)
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsReadOnlyByteSpan(in value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override readonly string ToString() => ReadOnlyBytes.ToHexString();
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -8,157 +8,156 @@ using LibHac.Util;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace LibHac.Boot
|
namespace LibHac.Boot;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 0x200)]
|
||||||
|
public struct Package2Header
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x200)]
|
internal const int Package2SizeMax = (1024 * 1024 * 8) - (1024 * 16); // 8MB - 16KB
|
||||||
public struct Package2Header
|
internal const int PayloadAlignment = 4;
|
||||||
|
internal const int PayloadCount = 3;
|
||||||
|
|
||||||
|
internal const int SignatureSize = 0x100;
|
||||||
|
private ReadOnlySpan<byte> RsaPublicKeyExponent => new byte[] { 0x00, 0x01, 0x00, 0x01 };
|
||||||
|
|
||||||
|
[FieldOffset(0x00)] private byte _signature;
|
||||||
|
[FieldOffset(0x100)] public Package2Meta Meta;
|
||||||
|
|
||||||
|
public ReadOnlySpan<byte> Signature => SpanHelpers.CreateSpan(ref _signature, SignatureSize);
|
||||||
|
|
||||||
|
public Result VerifySignature(ReadOnlySpan<byte> modulus, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
internal const int Package2SizeMax = (1024 * 1024 * 8) - (1024 * 16); // 8MB - 16KB
|
if (!Rsa.VerifyRsa2048PssSha256(Signature, modulus, RsaPublicKeyExponent, data))
|
||||||
internal const int PayloadAlignment = 4;
|
|
||||||
internal const int PayloadCount = 3;
|
|
||||||
|
|
||||||
internal const int SignatureSize = 0x100;
|
|
||||||
private ReadOnlySpan<byte> RsaPublicKeyExponent => new byte[] { 0x00, 0x01, 0x00, 0x01 };
|
|
||||||
|
|
||||||
[FieldOffset(0x00)] private byte _signature;
|
|
||||||
[FieldOffset(0x100)] public Package2Meta Meta;
|
|
||||||
|
|
||||||
public ReadOnlySpan<byte> Signature => SpanHelpers.CreateSpan(ref _signature, SignatureSize);
|
|
||||||
|
|
||||||
public Result VerifySignature(ReadOnlySpan<byte> modulus, ReadOnlySpan<byte> data)
|
|
||||||
{
|
{
|
||||||
if (!Rsa.VerifyRsa2048PssSha256(Signature, modulus, RsaPublicKeyExponent, data))
|
return ResultLibHac.InvalidPackage2HeaderSignature.Log();
|
||||||
{
|
|
||||||
return ResultLibHac.InvalidPackage2HeaderSignature.Log();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
return Result.Success;
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] [FieldOffset(0x00)] private readonly Padding100 PaddingForVsDebugging;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x100)]
|
|
||||||
public struct Package2Meta
|
|
||||||
{
|
|
||||||
public const uint ExpectedMagicValue = 0x31324B50; // PK21
|
|
||||||
|
|
||||||
[FieldOffset(0x00)] private Buffer16 _headerIv;
|
|
||||||
|
|
||||||
[FieldOffset(0x00)] private uint _package2Size;
|
|
||||||
[FieldOffset(0x04)] private byte _keyGeneration;
|
|
||||||
|
|
||||||
[FieldOffset(0x06)] private byte _keyGenerationXor1;
|
|
||||||
[FieldOffset(0x07)] private byte _keyGenerationXor2;
|
|
||||||
[FieldOffset(0x08)] private uint _sizeXor1;
|
|
||||||
[FieldOffset(0x0C)] private uint _sizeXor2;
|
|
||||||
|
|
||||||
[FieldOffset(0x10)] private Buffer16 _payloadIvs;
|
|
||||||
|
|
||||||
[FieldOffset(0x50)] private readonly uint _magic;
|
|
||||||
[FieldOffset(0x54)] private readonly uint _entryPoint;
|
|
||||||
[FieldOffset(0x5C)] private readonly byte _package2Version;
|
|
||||||
[FieldOffset(0x5D)] private readonly byte _bootloaderVersion;
|
|
||||||
|
|
||||||
[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)
|
|
||||||
{
|
|
||||||
if ((uint)index >= Package2Header.PayloadCount)
|
|
||||||
throw new IndexOutOfRangeException("Invalid payload index.");
|
|
||||||
|
|
||||||
int offset = Unsafe.SizeOf<Package2Header>();
|
|
||||||
|
|
||||||
for (int i = 0; i < index; i++)
|
|
||||||
{
|
|
||||||
offset += (int)PayloadSizes[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result Verify()
|
|
||||||
{
|
|
||||||
// Get the obfuscated metadata.
|
|
||||||
uint size = Size;
|
|
||||||
byte keyGeneration = KeyGeneration;
|
|
||||||
|
|
||||||
// Check that size is big enough for the header.
|
|
||||||
if (size < Unsafe.SizeOf<Package2Header>())
|
|
||||||
return ResultLibHac.InvalidPackage2MetaSizeA.Log();
|
|
||||||
|
|
||||||
// Check that the size isn't larger than what we allow.
|
|
||||||
if (size > Package2Header.Package2SizeMax)
|
|
||||||
return ResultLibHac.InvalidPackage2MetaSizeB.Log();
|
|
||||||
|
|
||||||
// Check that the key generation is one that we can use.
|
|
||||||
if (keyGeneration >= 0x20)
|
|
||||||
return ResultLibHac.InvalidPackage2MetaKeyGeneration.Log();
|
|
||||||
|
|
||||||
// Check the magic number.
|
|
||||||
if (Magic != ExpectedMagicValue)
|
|
||||||
return ResultLibHac.InvalidPackage2MetaMagic.Log();
|
|
||||||
|
|
||||||
// Check the payload alignments.
|
|
||||||
if (EntryPoint % Package2Header.PayloadAlignment != 0)
|
|
||||||
return ResultLibHac.InvalidPackage2MetaEntryPointAlignment.Log();
|
|
||||||
|
|
||||||
for (int i = 0; i < Package2Header.PayloadCount; i++)
|
|
||||||
{
|
|
||||||
if (PayloadSizes[i] % Package2Header.PayloadAlignment != 0)
|
|
||||||
return ResultLibHac.InvalidPackage2MetaPayloadSizeAlignment.Log();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that the sizes sum to the total.
|
|
||||||
if (Size != Unsafe.SizeOf<Package2Header>() + PayloadSizes[0] + PayloadSizes[1] + PayloadSizes[2])
|
|
||||||
return ResultLibHac.InvalidPackage2MetaTotalSize.Log();
|
|
||||||
|
|
||||||
// Check that the payloads do not overflow.
|
|
||||||
for (int i = 0; i < Package2Header.PayloadCount; i++)
|
|
||||||
{
|
|
||||||
if (PayloadOffsets[i] > PayloadOffsets[i] + PayloadSizes[i])
|
|
||||||
return ResultLibHac.InvalidPackage2MetaPayloadSize.Log();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that no payloads overlap.
|
|
||||||
for (int i = 0; i < Package2Header.PayloadCount - 1; i++)
|
|
||||||
for (int j = i + 1; j < Package2Header.PayloadCount; j++)
|
|
||||||
{
|
|
||||||
if (Overlap.HasOverlap(PayloadOffsets[i], PayloadSizes[i], PayloadOffsets[j], PayloadSizes[j]))
|
|
||||||
return ResultLibHac.InvalidPackage2MetaPayloadsOverlap.Log();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether any payload contains the entrypoint.
|
|
||||||
for (int i = 0; i < Package2Header.PayloadCount; i++)
|
|
||||||
{
|
|
||||||
if (Overlap.Contains(PayloadOffsets[i], PayloadSizes[i], EntryPoint))
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No payload contains the entrypoint, so we're not valid.
|
|
||||||
return ResultLibHac.InvalidPackage2MetaEntryPointNotFound.Log();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] [FieldOffset(0x00)] private readonly Padding100 PaddingForVsDebugging;
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] [FieldOffset(0x00)] private readonly Padding100 PaddingForVsDebugging;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 0x100)]
|
||||||
|
public struct Package2Meta
|
||||||
|
{
|
||||||
|
public const uint ExpectedMagicValue = 0x31324B50; // PK21
|
||||||
|
|
||||||
|
[FieldOffset(0x00)] private Buffer16 _headerIv;
|
||||||
|
|
||||||
|
[FieldOffset(0x00)] private uint _package2Size;
|
||||||
|
[FieldOffset(0x04)] private byte _keyGeneration;
|
||||||
|
|
||||||
|
[FieldOffset(0x06)] private byte _keyGenerationXor1;
|
||||||
|
[FieldOffset(0x07)] private byte _keyGenerationXor2;
|
||||||
|
[FieldOffset(0x08)] private uint _sizeXor1;
|
||||||
|
[FieldOffset(0x0C)] private uint _sizeXor2;
|
||||||
|
|
||||||
|
[FieldOffset(0x10)] private Buffer16 _payloadIvs;
|
||||||
|
|
||||||
|
[FieldOffset(0x50)] private readonly uint _magic;
|
||||||
|
[FieldOffset(0x54)] private readonly uint _entryPoint;
|
||||||
|
[FieldOffset(0x5C)] private readonly byte _package2Version;
|
||||||
|
[FieldOffset(0x5D)] private readonly byte _bootloaderVersion;
|
||||||
|
|
||||||
|
[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)
|
||||||
|
{
|
||||||
|
if ((uint)index >= Package2Header.PayloadCount)
|
||||||
|
throw new IndexOutOfRangeException("Invalid payload index.");
|
||||||
|
|
||||||
|
int offset = Unsafe.SizeOf<Package2Header>();
|
||||||
|
|
||||||
|
for (int i = 0; i < index; i++)
|
||||||
|
{
|
||||||
|
offset += (int)PayloadSizes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result Verify()
|
||||||
|
{
|
||||||
|
// Get the obfuscated metadata.
|
||||||
|
uint size = Size;
|
||||||
|
byte keyGeneration = KeyGeneration;
|
||||||
|
|
||||||
|
// Check that size is big enough for the header.
|
||||||
|
if (size < Unsafe.SizeOf<Package2Header>())
|
||||||
|
return ResultLibHac.InvalidPackage2MetaSizeA.Log();
|
||||||
|
|
||||||
|
// Check that the size isn't larger than what we allow.
|
||||||
|
if (size > Package2Header.Package2SizeMax)
|
||||||
|
return ResultLibHac.InvalidPackage2MetaSizeB.Log();
|
||||||
|
|
||||||
|
// Check that the key generation is one that we can use.
|
||||||
|
if (keyGeneration >= 0x20)
|
||||||
|
return ResultLibHac.InvalidPackage2MetaKeyGeneration.Log();
|
||||||
|
|
||||||
|
// Check the magic number.
|
||||||
|
if (Magic != ExpectedMagicValue)
|
||||||
|
return ResultLibHac.InvalidPackage2MetaMagic.Log();
|
||||||
|
|
||||||
|
// Check the payload alignments.
|
||||||
|
if (EntryPoint % Package2Header.PayloadAlignment != 0)
|
||||||
|
return ResultLibHac.InvalidPackage2MetaEntryPointAlignment.Log();
|
||||||
|
|
||||||
|
for (int i = 0; i < Package2Header.PayloadCount; i++)
|
||||||
|
{
|
||||||
|
if (PayloadSizes[i] % Package2Header.PayloadAlignment != 0)
|
||||||
|
return ResultLibHac.InvalidPackage2MetaPayloadSizeAlignment.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the sizes sum to the total.
|
||||||
|
if (Size != Unsafe.SizeOf<Package2Header>() + PayloadSizes[0] + PayloadSizes[1] + PayloadSizes[2])
|
||||||
|
return ResultLibHac.InvalidPackage2MetaTotalSize.Log();
|
||||||
|
|
||||||
|
// Check that the payloads do not overflow.
|
||||||
|
for (int i = 0; i < Package2Header.PayloadCount; i++)
|
||||||
|
{
|
||||||
|
if (PayloadOffsets[i] > PayloadOffsets[i] + PayloadSizes[i])
|
||||||
|
return ResultLibHac.InvalidPackage2MetaPayloadSize.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that no payloads overlap.
|
||||||
|
for (int i = 0; i < Package2Header.PayloadCount - 1; i++)
|
||||||
|
for (int j = i + 1; j < Package2Header.PayloadCount; j++)
|
||||||
|
{
|
||||||
|
if (Overlap.HasOverlap(PayloadOffsets[i], PayloadSizes[i], PayloadOffsets[j], PayloadSizes[j]))
|
||||||
|
return ResultLibHac.InvalidPackage2MetaPayloadsOverlap.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether any payload contains the entrypoint.
|
||||||
|
for (int i = 0; i < Package2Header.PayloadCount; i++)
|
||||||
|
{
|
||||||
|
if (Overlap.Contains(PayloadOffsets[i], PayloadSizes[i], EntryPoint))
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
#endif
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -8,263 +8,262 @@ using LibHac.Fs;
|
|||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using LibHac.Kernel;
|
using LibHac.Kernel;
|
||||||
|
|
||||||
namespace LibHac.Boot
|
namespace LibHac.Boot;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses a package2 file and opens the payloads within.
|
||||||
|
/// </summary>
|
||||||
|
public class Package2StorageReader : IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
private const int KernelPayloadIndex = 0;
|
||||||
/// Parses a package2 file and opens the payloads within.
|
private const int IniPayloadIndex = 1;
|
||||||
/// </summary>
|
|
||||||
public class Package2StorageReader : IDisposable
|
private SharedRef<IStorage> _storage;
|
||||||
|
private Package2Header _header;
|
||||||
|
private KeySet _keySet;
|
||||||
|
private Crypto.AesKey _key;
|
||||||
|
|
||||||
|
public ref readonly Package2Header Header => ref _header;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
{
|
{
|
||||||
private const int KernelPayloadIndex = 0;
|
_storage.Destroy();
|
||||||
private const int IniPayloadIndex = 1;
|
}
|
||||||
|
|
||||||
private SharedRef<IStorage> _storage;
|
/// <summary>
|
||||||
private Package2Header _header;
|
/// Initializes the <see cref="Package2StorageReader"/>.
|
||||||
private KeySet _keySet;
|
/// </summary>
|
||||||
private Crypto.AesKey _key;
|
/// <param name="keySet">The keyset to use for decrypting the package.</param>
|
||||||
|
/// <param name="storage">An <see cref="IStorage"/> of the encrypted package2.</param>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
|
public Result Initialize(KeySet keySet, in SharedRef<IStorage> storage)
|
||||||
|
{
|
||||||
|
Result rc = storage.Get.Read(0, SpanHelpers.AsByteSpan(ref _header));
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
public ref readonly Package2Header Header => ref _header;
|
_key = keySet.Package2Keys[_header.Meta.KeyGeneration];
|
||||||
|
DecryptHeader(_key, ref _header.Meta, ref _header.Meta);
|
||||||
|
|
||||||
public void Dispose()
|
_storage.SetByCopy(in storage);
|
||||||
|
_keySet = keySet;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens a decrypted <see cref="IStorage"/> of one of the payloads in the package.
|
||||||
|
/// </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>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
|
public Result OpenPayload(ref UniqueRef<IStorage> outPayloadStorage, int index)
|
||||||
|
{
|
||||||
|
if ((uint)index >= Package2Header.PayloadCount)
|
||||||
|
return ResultLibHac.ArgumentOutOfRange.Log();
|
||||||
|
|
||||||
|
int offset = _header.Meta.GetPayloadFileOffset(index);
|
||||||
|
int size = (int)_header.Meta.PayloadSizes[index];
|
||||||
|
|
||||||
|
var payloadSubStorage = new SubStorage(_storage, offset, size);
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
{
|
{
|
||||||
_storage.Destroy();
|
outPayloadStorage.Reset(payloadSubStorage);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the <see cref="Package2StorageReader"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keySet">The keyset to use for decrypting the package.</param>
|
|
||||||
/// <param name="storage">An <see cref="IStorage"/> of the encrypted package2.</param>
|
|
||||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
|
||||||
public Result Initialize(KeySet keySet, in SharedRef<IStorage> storage)
|
|
||||||
{
|
|
||||||
Result rc = storage.Get.Read(0, SpanHelpers.AsByteSpan(ref _header));
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
_key = keySet.Package2Keys[_header.Meta.KeyGeneration];
|
|
||||||
DecryptHeader(_key, ref _header.Meta, ref _header.Meta);
|
|
||||||
|
|
||||||
_storage.SetByCopy(in storage);
|
|
||||||
_keySet = keySet;
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
byte[] iv = _header.Meta.PayloadIvs[index].Bytes.ToArray();
|
||||||
/// Opens a decrypted <see cref="IStorage"/> of one of the payloads in the package.
|
outPayloadStorage.Reset(new CachedStorage(new Aes128CtrStorage(payloadSubStorage, _key.DataRo.ToArray(), iv, true), 0x4000, 1, true));
|
||||||
/// </summary>
|
return Result.Success;
|
||||||
/// <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>
|
/// <summary>
|
||||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
/// Opens an <see cref="IStorage"/> of the kernel payload.
|
||||||
public Result OpenPayload(ref UniqueRef<IStorage> outPayloadStorage, int index)
|
/// </summary>
|
||||||
|
/// <param name="outKernelStorage">If the method returns successfully, contains an <see cref="IStorage"/>
|
||||||
|
/// of the kernel payload.</param>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
|
public Result OpenKernel(ref UniqueRef<IStorage> outKernelStorage)
|
||||||
|
{
|
||||||
|
return OpenPayload(ref outKernelStorage, KernelPayloadIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens an <see cref="IStorage"/> of the initial process binary. If the binary is embedded in
|
||||||
|
/// the kernel, this method will attempt to locate and return the embedded binary.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="outIniStorage">If the method returns successfully, contains an <see cref="IStorage"/>
|
||||||
|
/// of the initial process binary.</param>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
|
public Result OpenIni(ref UniqueRef<IStorage> outIniStorage)
|
||||||
|
{
|
||||||
|
if (HasIniPayload())
|
||||||
{
|
{
|
||||||
if ((uint)index >= Package2Header.PayloadCount)
|
return OpenPayload(ref outIniStorage, IniPayloadIndex);
|
||||||
return ResultLibHac.ArgumentOutOfRange.Log();
|
|
||||||
|
|
||||||
int offset = _header.Meta.GetPayloadFileOffset(index);
|
|
||||||
int size = (int)_header.Meta.PayloadSizes[index];
|
|
||||||
|
|
||||||
var payloadSubStorage = new SubStorage(_storage, offset, size);
|
|
||||||
|
|
||||||
if (size == 0)
|
|
||||||
{
|
|
||||||
outPayloadStorage.Reset(payloadSubStorage);
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] iv = _header.Meta.PayloadIvs[index].Bytes.ToArray();
|
|
||||||
outPayloadStorage.Reset(new CachedStorage(new Aes128CtrStorage(payloadSubStorage, _key.DataRo.ToArray(), iv, true), 0x4000, 1, true));
|
|
||||||
return Result.Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
// Ini is embedded in the kernel
|
||||||
/// Opens an <see cref="IStorage"/> of the kernel payload.
|
using var kernelStorage = new UniqueRef<IStorage>();
|
||||||
/// </summary>
|
Result rc = OpenKernel(ref kernelStorage.Ref());
|
||||||
/// <param name="outKernelStorage">If the method returns successfully, contains an <see cref="IStorage"/>
|
if (rc.IsFailure()) return rc;
|
||||||
/// of the kernel payload.</param>
|
|
||||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
if (!IniExtract.TryGetIni1Offset(out int offset, out int size, kernelStorage.Get))
|
||||||
public Result OpenKernel(ref UniqueRef<IStorage> outKernelStorage)
|
|
||||||
{
|
{
|
||||||
return OpenPayload(ref outKernelStorage, KernelPayloadIndex);
|
// Unable to find the ini. Could be a new, unsupported layout.
|
||||||
|
return ResultLibHac.NotImplemented.Log();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
outIniStorage.Reset(new SubStorage(kernelStorage.Release(), offset, size));
|
||||||
/// Opens an <see cref="IStorage"/> of the initial process binary. If the binary is embedded in
|
return Result.Success;
|
||||||
/// the kernel, this method will attempt to locate and return the embedded binary.
|
}
|
||||||
/// </summary>
|
|
||||||
/// <param name="outIniStorage">If the method returns successfully, contains an <see cref="IStorage"/>
|
/// <summary>
|
||||||
/// of the initial process binary.</param>
|
/// Verifies the signature, metadata and payloads in the package.
|
||||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
/// </summary>
|
||||||
public Result OpenIni(ref UniqueRef<IStorage> outIniStorage)
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
|
public Result Verify()
|
||||||
|
{
|
||||||
|
Result rc = VerifySignature();
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = VerifyMeta();
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
return VerifyPayloads();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies the signature of the package.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.
|
||||||
|
/// <see cref="Result.Success"/> if the signature is valid.</returns>
|
||||||
|
public Result VerifySignature()
|
||||||
|
{
|
||||||
|
Unsafe.SkipInit(out Package2Meta meta);
|
||||||
|
Span<byte> metaBytes = SpanHelpers.AsByteSpan(ref meta);
|
||||||
|
|
||||||
|
Result rc = _storage.Get.Read(Package2Header.SignatureSize, metaBytes);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
return _header.VerifySignature(_keySet.Package2SigningKeyParams.Modulus, metaBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies the package metadata.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.
|
||||||
|
/// <see cref="Result.Success"/> if the metadata is valid.</returns>
|
||||||
|
public Result VerifyMeta() => _header.Meta.Verify();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies the hashes of all the payloads in the metadata.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.
|
||||||
|
/// <see cref="Result.Success"/> if all the hashes are valid.</returns>
|
||||||
|
public Result VerifyPayloads()
|
||||||
|
{
|
||||||
|
using (var buffer = new RentedArray<byte>(0x10000))
|
||||||
{
|
{
|
||||||
if (HasIniPayload())
|
byte[] array = buffer.Array;
|
||||||
{
|
var hashBuffer = new Buffer32();
|
||||||
return OpenPayload(ref outIniStorage, IniPayloadIndex);
|
var sha = new Sha256Generator();
|
||||||
}
|
|
||||||
|
|
||||||
// Ini is embedded in the kernel
|
// Verify hashes match for all payloads.
|
||||||
using var kernelStorage = new UniqueRef<IStorage>();
|
|
||||||
Result rc = OpenKernel(ref kernelStorage.Ref());
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
if (!IniExtract.TryGetIni1Offset(out int offset, out int size, kernelStorage.Get))
|
|
||||||
{
|
|
||||||
// Unable to find the ini. Could be a new, unsupported layout.
|
|
||||||
return ResultLibHac.NotImplemented.Log();
|
|
||||||
}
|
|
||||||
|
|
||||||
outIniStorage.Reset(new SubStorage(kernelStorage.Release(), offset, size));
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Verifies the signature, metadata and payloads in the package.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
|
||||||
public Result Verify()
|
|
||||||
{
|
|
||||||
Result rc = VerifySignature();
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
rc = VerifyMeta();
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
return VerifyPayloads();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Verifies the signature of the package.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The <see cref="Result"/> of the operation.
|
|
||||||
/// <see cref="Result.Success"/> if the signature is valid.</returns>
|
|
||||||
public Result VerifySignature()
|
|
||||||
{
|
|
||||||
Unsafe.SkipInit(out Package2Meta meta);
|
|
||||||
Span<byte> metaBytes = SpanHelpers.AsByteSpan(ref meta);
|
|
||||||
|
|
||||||
Result rc = _storage.Get.Read(Package2Header.SignatureSize, metaBytes);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
return _header.VerifySignature(_keySet.Package2SigningKeyParams.Modulus, metaBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Verifies the package metadata.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The <see cref="Result"/> of the operation.
|
|
||||||
/// <see cref="Result.Success"/> if the metadata is valid.</returns>
|
|
||||||
public Result VerifyMeta() => _header.Meta.Verify();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Verifies the hashes of all the payloads in the metadata.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The <see cref="Result"/> of the operation.
|
|
||||||
/// <see cref="Result.Success"/> if all the hashes are valid.</returns>
|
|
||||||
public Result VerifyPayloads()
|
|
||||||
{
|
|
||||||
using (var buffer = new RentedArray<byte>(0x10000))
|
|
||||||
{
|
|
||||||
byte[] array = buffer.Array;
|
|
||||||
var hashBuffer = new Buffer32();
|
|
||||||
var sha = new Sha256Generator();
|
|
||||||
|
|
||||||
// Verify hashes match for all payloads.
|
|
||||||
for (int i = 0; i < Package2Header.PayloadCount; i++)
|
|
||||||
{
|
|
||||||
if (_header.Meta.PayloadSizes[i] == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int offset = _header.Meta.GetPayloadFileOffset(i);
|
|
||||||
int size = (int)_header.Meta.PayloadSizes[i];
|
|
||||||
|
|
||||||
var payloadSubStorage = new SubStorage(_storage, offset, size);
|
|
||||||
|
|
||||||
offset = 0;
|
|
||||||
sha.Initialize();
|
|
||||||
|
|
||||||
while (size > 0)
|
|
||||||
{
|
|
||||||
int toRead = Math.Min(array.Length, size);
|
|
||||||
Span<byte> span = array.AsSpan(0, toRead);
|
|
||||||
|
|
||||||
Result rc = payloadSubStorage.Read(offset, span);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
sha.Update(span);
|
|
||||||
|
|
||||||
offset += toRead;
|
|
||||||
size -= toRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
sha.GetHash(hashBuffer);
|
|
||||||
|
|
||||||
if (!CryptoUtil.IsSameBytes(hashBuffer, _header.Meta.PayloadHashes[i], 0x20))
|
|
||||||
{
|
|
||||||
return ResultLibHac.InvalidPackage2PayloadCorrupted.Log();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Opens a decrypted <see cref="IStorage"/> of the entire package.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="outPackageStorage">If the method returns successfully, contains a decrypted
|
|
||||||
/// <see cref="IStorage"/> of the package.</param>
|
|
||||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
|
||||||
public Result OpenDecryptedPackage(ref UniqueRef<IStorage> outPackageStorage)
|
|
||||||
{
|
|
||||||
var storages = new List<IStorage>(4);
|
|
||||||
|
|
||||||
// The signature and IV are unencrypted
|
|
||||||
int unencryptedHeaderSize = Package2Header.SignatureSize + Unsafe.SizeOf<Buffer16>();
|
|
||||||
int encryptedHeaderSize = Unsafe.SizeOf<Package2Header>() - unencryptedHeaderSize;
|
|
||||||
|
|
||||||
// Get signature and IV
|
|
||||||
storages.Add(new SubStorage(_storage, 0, unencryptedHeaderSize));
|
|
||||||
|
|
||||||
// Open decrypted meta
|
|
||||||
var encMetaStorage = new SubStorage(_storage, unencryptedHeaderSize, encryptedHeaderSize);
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
Utilities.IncrementByteArray(iv);
|
|
||||||
|
|
||||||
storages.Add(new CachedStorage(new Aes128CtrStorage(encMetaStorage, _key.DataRo.ToArray(), iv, true), 0x100, 1, true));
|
|
||||||
|
|
||||||
// Open all the payloads
|
|
||||||
for (int i = 0; i < Package2Header.PayloadCount; i++)
|
for (int i = 0; i < Package2Header.PayloadCount; i++)
|
||||||
{
|
{
|
||||||
if (_header.Meta.PayloadSizes[i] == 0)
|
if (_header.Meta.PayloadSizes[i] == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
using var payloadStorage = new UniqueRef<IStorage>();
|
int offset = _header.Meta.GetPayloadFileOffset(i);
|
||||||
Result rc = OpenPayload(ref payloadStorage.Ref(), i);
|
int size = (int)_header.Meta.PayloadSizes[i];
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
|
||||||
|
|
||||||
storages.Add(payloadStorage.Release());
|
var payloadSubStorage = new SubStorage(_storage, offset, size);
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
sha.Initialize();
|
||||||
|
|
||||||
|
while (size > 0)
|
||||||
|
{
|
||||||
|
int toRead = Math.Min(array.Length, size);
|
||||||
|
Span<byte> span = array.AsSpan(0, toRead);
|
||||||
|
|
||||||
|
Result rc = payloadSubStorage.Read(offset, span);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
sha.Update(span);
|
||||||
|
|
||||||
|
offset += toRead;
|
||||||
|
size -= toRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
sha.GetHash(hashBuffer);
|
||||||
|
|
||||||
|
if (!CryptoUtil.IsSameBytes(hashBuffer, _header.Meta.PayloadHashes[i], 0x20))
|
||||||
|
{
|
||||||
|
return ResultLibHac.InvalidPackage2PayloadCorrupted.Log();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
outPackageStorage.Reset(new ConcatenationStorage(storages, true));
|
|
||||||
return Result.Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DecryptHeader(ReadOnlySpan<byte> key, ref Package2Meta source, ref Package2Meta dest)
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens a decrypted <see cref="IStorage"/> of the entire package.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="outPackageStorage">If the method returns successfully, contains a decrypted
|
||||||
|
/// <see cref="IStorage"/> of the package.</param>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
|
public Result OpenDecryptedPackage(ref UniqueRef<IStorage> outPackageStorage)
|
||||||
|
{
|
||||||
|
var storages = new List<IStorage>(4);
|
||||||
|
|
||||||
|
// The signature and IV are unencrypted
|
||||||
|
int unencryptedHeaderSize = Package2Header.SignatureSize + Unsafe.SizeOf<Buffer16>();
|
||||||
|
int encryptedHeaderSize = Unsafe.SizeOf<Package2Header>() - unencryptedHeaderSize;
|
||||||
|
|
||||||
|
// Get signature and IV
|
||||||
|
storages.Add(new SubStorage(_storage, 0, unencryptedHeaderSize));
|
||||||
|
|
||||||
|
// Open decrypted meta
|
||||||
|
var encMetaStorage = new SubStorage(_storage, unencryptedHeaderSize, encryptedHeaderSize);
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
Utilities.IncrementByteArray(iv);
|
||||||
|
|
||||||
|
storages.Add(new CachedStorage(new Aes128CtrStorage(encMetaStorage, _key.DataRo.ToArray(), iv, true), 0x100, 1, true));
|
||||||
|
|
||||||
|
// Open all the payloads
|
||||||
|
for (int i = 0; i < Package2Header.PayloadCount; i++)
|
||||||
{
|
{
|
||||||
Buffer16 iv = source.HeaderIv;
|
if (_header.Meta.PayloadSizes[i] == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
Aes.DecryptCtr128(SpanHelpers.AsByteSpan(ref source), SpanHelpers.AsByteSpan(ref dest), key, iv);
|
using var payloadStorage = new UniqueRef<IStorage>();
|
||||||
|
Result rc = OpenPayload(ref payloadStorage.Ref(), i);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
// Copy the IV to the output because the IV field will be garbage after "decrypting" it
|
storages.Add(payloadStorage.Release());
|
||||||
Unsafe.As<Package2Meta, Buffer16>(ref dest) = iv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HasIniPayload()
|
outPackageStorage.Reset(new ConcatenationStorage(storages, true));
|
||||||
{
|
return Result.Success;
|
||||||
return _header.Meta.PayloadSizes[IniPayloadIndex] != 0;
|
}
|
||||||
}
|
|
||||||
|
private void DecryptHeader(ReadOnlySpan<byte> key, ref Package2Meta source, ref Package2Meta dest)
|
||||||
|
{
|
||||||
|
Buffer16 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasIniPayload()
|
||||||
|
{
|
||||||
|
return _header.Meta.PayloadSizes[IniPayloadIndex] != 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,275 +1,274 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace LibHac
|
namespace LibHac;
|
||||||
|
|
||||||
|
public class Calibration
|
||||||
{
|
{
|
||||||
public class Calibration
|
public const string ExpectedMagic = "CAL0";
|
||||||
|
|
||||||
|
public string Magic;
|
||||||
|
public int Version;
|
||||||
|
public int CalibDataSize;
|
||||||
|
public short Model;
|
||||||
|
public short Revision;
|
||||||
|
public byte[] CalibDataSha256;
|
||||||
|
public string ConfigId1;
|
||||||
|
public byte[] Reserved;
|
||||||
|
public int WlanCountryCodesNum;
|
||||||
|
public int WlanCountryCodesLastIdx;
|
||||||
|
public string[] WlanCountryCodes;
|
||||||
|
public byte[] WlanMacAddr;
|
||||||
|
public byte[] BdAddr;
|
||||||
|
public byte[] AccelerometerOffset;
|
||||||
|
public byte[] AccelerometerScale;
|
||||||
|
public byte[] GyroscopeOffset;
|
||||||
|
public byte[] GyroscopeScale;
|
||||||
|
public string SerialNumber;
|
||||||
|
public byte[] DeviceKeyEccP256;
|
||||||
|
public byte[] DeviceCertEccP256;
|
||||||
|
public byte[] DeviceKeyEccB233;
|
||||||
|
public byte[] DeviceCertEccB233;
|
||||||
|
public byte[] EticketKeyEccP256;
|
||||||
|
public byte[] EticketCertEccP256;
|
||||||
|
public byte[] EticketKeyEccB233;
|
||||||
|
public byte[] EticketCertEccB233;
|
||||||
|
public byte[] SslKey;
|
||||||
|
public int SslCertSize;
|
||||||
|
public byte[] SslCert;
|
||||||
|
public byte[] SslCertSha256;
|
||||||
|
public byte[] RandomNumber;
|
||||||
|
public byte[] RandomNumberSha256;
|
||||||
|
public byte[] GamecardKey;
|
||||||
|
public byte[] GamecardCert;
|
||||||
|
public byte[] GamecardCertSha256;
|
||||||
|
public byte[] EticketKeyRsa;
|
||||||
|
public byte[] EticketCertRsa;
|
||||||
|
public string BatteryLot;
|
||||||
|
public byte[] SpeakerCalibValue;
|
||||||
|
public int RegionCode;
|
||||||
|
public byte[] AmiiboKey;
|
||||||
|
public byte[] AmiiboCertEcqv;
|
||||||
|
public byte[] AmiiboCertEcdsa;
|
||||||
|
public byte[] AmiiboKeyEcqvBls;
|
||||||
|
public byte[] AmiiboCertEcqvBls;
|
||||||
|
public byte[] AmiiboRootCertEcqvBls;
|
||||||
|
public int ProductModel;
|
||||||
|
public byte[] ColorVariation;
|
||||||
|
public byte[] LcdBacklightBrightnessMapping;
|
||||||
|
public byte[] DeviceExtKeyEccB233;
|
||||||
|
public byte[] EticketExtKeyEccP256;
|
||||||
|
public byte[] EticketExtKeyEccB233;
|
||||||
|
public byte[] EticketExtKeyRsa;
|
||||||
|
public byte[] SslExtKey;
|
||||||
|
public byte[] GamecardExtKey;
|
||||||
|
public int LcdVendorId;
|
||||||
|
public byte[] ExtendedRsa2048DeviceKey;
|
||||||
|
public byte[] Rsa2048DeviceCertificate;
|
||||||
|
public byte[] UsbTypeCPowerSourceCircuitVersion;
|
||||||
|
public int HomeMenuSchemeSubColor;
|
||||||
|
public int HomeMenuSchemeBezelColor;
|
||||||
|
public int HomeMenuSchemeMainColor1;
|
||||||
|
public int HomeMenuSchemeMainColor2;
|
||||||
|
public int HomeMenuSchemeMainColor3;
|
||||||
|
public byte[] AnalogStickModuleTypeL;
|
||||||
|
public byte[] AnalogStickModelParameterL;
|
||||||
|
public byte[] AnalogStickFactoryCalibrationL;
|
||||||
|
public byte[] AnalogStickModuleTypeR;
|
||||||
|
public byte[] AnalogStickModelParameterR;
|
||||||
|
public byte[] AnalogStickFactoryCalibrationR;
|
||||||
|
public byte[] ConsoleSixAxisSensorModuleType;
|
||||||
|
public byte[] ConsoleSixAxisSensorHorizontalOffset;
|
||||||
|
public byte[] BatteryVersion;
|
||||||
|
public int HomeMenuSchemeModel;
|
||||||
|
public byte[] ConsoleSixAxisSensorMountType;
|
||||||
|
|
||||||
|
public Calibration(Stream stream)
|
||||||
{
|
{
|
||||||
public const string ExpectedMagic = "CAL0";
|
using var reader = new BinaryReader(stream, Encoding.Default, true);
|
||||||
|
|
||||||
public string Magic;
|
stream.Position = 0x0;
|
||||||
public int Version;
|
Magic = reader.ReadUtf8(0x4);
|
||||||
public int CalibDataSize;
|
|
||||||
public short Model;
|
|
||||||
public short Revision;
|
|
||||||
public byte[] CalibDataSha256;
|
|
||||||
public string ConfigId1;
|
|
||||||
public byte[] Reserved;
|
|
||||||
public int WlanCountryCodesNum;
|
|
||||||
public int WlanCountryCodesLastIdx;
|
|
||||||
public string[] WlanCountryCodes;
|
|
||||||
public byte[] WlanMacAddr;
|
|
||||||
public byte[] BdAddr;
|
|
||||||
public byte[] AccelerometerOffset;
|
|
||||||
public byte[] AccelerometerScale;
|
|
||||||
public byte[] GyroscopeOffset;
|
|
||||||
public byte[] GyroscopeScale;
|
|
||||||
public string SerialNumber;
|
|
||||||
public byte[] DeviceKeyEccP256;
|
|
||||||
public byte[] DeviceCertEccP256;
|
|
||||||
public byte[] DeviceKeyEccB233;
|
|
||||||
public byte[] DeviceCertEccB233;
|
|
||||||
public byte[] EticketKeyEccP256;
|
|
||||||
public byte[] EticketCertEccP256;
|
|
||||||
public byte[] EticketKeyEccB233;
|
|
||||||
public byte[] EticketCertEccB233;
|
|
||||||
public byte[] SslKey;
|
|
||||||
public int SslCertSize;
|
|
||||||
public byte[] SslCert;
|
|
||||||
public byte[] SslCertSha256;
|
|
||||||
public byte[] RandomNumber;
|
|
||||||
public byte[] RandomNumberSha256;
|
|
||||||
public byte[] GamecardKey;
|
|
||||||
public byte[] GamecardCert;
|
|
||||||
public byte[] GamecardCertSha256;
|
|
||||||
public byte[] EticketKeyRsa;
|
|
||||||
public byte[] EticketCertRsa;
|
|
||||||
public string BatteryLot;
|
|
||||||
public byte[] SpeakerCalibValue;
|
|
||||||
public int RegionCode;
|
|
||||||
public byte[] AmiiboKey;
|
|
||||||
public byte[] AmiiboCertEcqv;
|
|
||||||
public byte[] AmiiboCertEcdsa;
|
|
||||||
public byte[] AmiiboKeyEcqvBls;
|
|
||||||
public byte[] AmiiboCertEcqvBls;
|
|
||||||
public byte[] AmiiboRootCertEcqvBls;
|
|
||||||
public int ProductModel;
|
|
||||||
public byte[] ColorVariation;
|
|
||||||
public byte[] LcdBacklightBrightnessMapping;
|
|
||||||
public byte[] DeviceExtKeyEccB233;
|
|
||||||
public byte[] EticketExtKeyEccP256;
|
|
||||||
public byte[] EticketExtKeyEccB233;
|
|
||||||
public byte[] EticketExtKeyRsa;
|
|
||||||
public byte[] SslExtKey;
|
|
||||||
public byte[] GamecardExtKey;
|
|
||||||
public int LcdVendorId;
|
|
||||||
public byte[] ExtendedRsa2048DeviceKey;
|
|
||||||
public byte[] Rsa2048DeviceCertificate;
|
|
||||||
public byte[] UsbTypeCPowerSourceCircuitVersion;
|
|
||||||
public int HomeMenuSchemeSubColor;
|
|
||||||
public int HomeMenuSchemeBezelColor;
|
|
||||||
public int HomeMenuSchemeMainColor1;
|
|
||||||
public int HomeMenuSchemeMainColor2;
|
|
||||||
public int HomeMenuSchemeMainColor3;
|
|
||||||
public byte[] AnalogStickModuleTypeL;
|
|
||||||
public byte[] AnalogStickModelParameterL;
|
|
||||||
public byte[] AnalogStickFactoryCalibrationL;
|
|
||||||
public byte[] AnalogStickModuleTypeR;
|
|
||||||
public byte[] AnalogStickModelParameterR;
|
|
||||||
public byte[] AnalogStickFactoryCalibrationR;
|
|
||||||
public byte[] ConsoleSixAxisSensorModuleType;
|
|
||||||
public byte[] ConsoleSixAxisSensorHorizontalOffset;
|
|
||||||
public byte[] BatteryVersion;
|
|
||||||
public int HomeMenuSchemeModel;
|
|
||||||
public byte[] ConsoleSixAxisSensorMountType;
|
|
||||||
|
|
||||||
public Calibration(Stream stream)
|
stream.Position = 0x4;
|
||||||
|
Version = reader.ReadInt32();
|
||||||
|
|
||||||
|
stream.Position = 0x8;
|
||||||
|
CalibDataSize = reader.ReadInt32();
|
||||||
|
|
||||||
|
stream.Position = 0xC;
|
||||||
|
Model = reader.ReadInt16();
|
||||||
|
|
||||||
|
stream.Position = 0xE;
|
||||||
|
Revision = reader.ReadInt16();
|
||||||
|
|
||||||
|
stream.Position = 0x20;
|
||||||
|
CalibDataSha256 = reader.ReadBytes(0x20);
|
||||||
|
|
||||||
|
stream.Position = 0x40;
|
||||||
|
ConfigId1 = reader.ReadUtf8Z(0x1E);
|
||||||
|
|
||||||
|
stream.Position = 0x60;
|
||||||
|
Reserved = reader.ReadBytes(0x20);
|
||||||
|
|
||||||
|
stream.Position = 0x80;
|
||||||
|
WlanCountryCodesNum = reader.ReadInt32();
|
||||||
|
WlanCountryCodesLastIdx = reader.ReadInt32();
|
||||||
|
WlanCountryCodes = new string[WlanCountryCodesNum];
|
||||||
|
for (int i = 0; i < WlanCountryCodesNum; i++)
|
||||||
{
|
{
|
||||||
using var reader = new BinaryReader(stream, Encoding.Default, true);
|
stream.Position = 0x88 + i * 4;
|
||||||
|
WlanCountryCodes[i] = reader.ReadUtf8Z();
|
||||||
stream.Position = 0x0;
|
|
||||||
Magic = reader.ReadUtf8(0x4);
|
|
||||||
|
|
||||||
stream.Position = 0x4;
|
|
||||||
Version = reader.ReadInt32();
|
|
||||||
|
|
||||||
stream.Position = 0x8;
|
|
||||||
CalibDataSize = reader.ReadInt32();
|
|
||||||
|
|
||||||
stream.Position = 0xC;
|
|
||||||
Model = reader.ReadInt16();
|
|
||||||
|
|
||||||
stream.Position = 0xE;
|
|
||||||
Revision = reader.ReadInt16();
|
|
||||||
|
|
||||||
stream.Position = 0x20;
|
|
||||||
CalibDataSha256 = reader.ReadBytes(0x20);
|
|
||||||
|
|
||||||
stream.Position = 0x40;
|
|
||||||
ConfigId1 = reader.ReadUtf8Z(0x1E);
|
|
||||||
|
|
||||||
stream.Position = 0x60;
|
|
||||||
Reserved = reader.ReadBytes(0x20);
|
|
||||||
|
|
||||||
stream.Position = 0x80;
|
|
||||||
WlanCountryCodesNum = reader.ReadInt32();
|
|
||||||
WlanCountryCodesLastIdx = reader.ReadInt32();
|
|
||||||
WlanCountryCodes = new string[WlanCountryCodesNum];
|
|
||||||
for (int i = 0; i < WlanCountryCodesNum; i++)
|
|
||||||
{
|
|
||||||
stream.Position = 0x88 + i * 4;
|
|
||||||
WlanCountryCodes[i] = reader.ReadUtf8Z();
|
|
||||||
}
|
|
||||||
stream.Position = 0x210;
|
|
||||||
WlanMacAddr = reader.ReadBytes(0x6);
|
|
||||||
|
|
||||||
stream.Position = 0x220;
|
|
||||||
BdAddr = reader.ReadBytes(0x6);
|
|
||||||
stream.Position = 0x230;
|
|
||||||
AccelerometerOffset = reader.ReadBytes(0x6);
|
|
||||||
stream.Position = 0x238;
|
|
||||||
AccelerometerScale = reader.ReadBytes(0x6);
|
|
||||||
stream.Position = 0x240;
|
|
||||||
GyroscopeOffset = reader.ReadBytes(0x6);
|
|
||||||
stream.Position = 0x248;
|
|
||||||
GyroscopeScale = reader.ReadBytes(0x6);
|
|
||||||
|
|
||||||
stream.Position = 0x250;
|
|
||||||
SerialNumber = reader.ReadUtf8Z(0x18);
|
|
||||||
|
|
||||||
stream.Position = 0x270;
|
|
||||||
DeviceKeyEccP256 = reader.ReadBytes(0x30);
|
|
||||||
stream.Position = 0x2B0;
|
|
||||||
DeviceCertEccP256 = reader.ReadBytes(0x180);
|
|
||||||
stream.Position = 0x440;
|
|
||||||
DeviceKeyEccB233 = reader.ReadBytes(0x30);
|
|
||||||
stream.Position = 0x480;
|
|
||||||
DeviceCertEccB233 = reader.ReadBytes(0x180);
|
|
||||||
stream.Position = 0x610;
|
|
||||||
EticketKeyEccP256 = reader.ReadBytes(0x30);
|
|
||||||
stream.Position = 0x650;
|
|
||||||
EticketCertEccP256 = reader.ReadBytes(0x180);
|
|
||||||
stream.Position = 0x7E0;
|
|
||||||
EticketKeyEccB233 = reader.ReadBytes(0x30);
|
|
||||||
stream.Position = 0x820;
|
|
||||||
EticketCertEccB233 = reader.ReadBytes(0x180);
|
|
||||||
|
|
||||||
stream.Position = 0x9B0;
|
|
||||||
SslKey = reader.ReadBytes(0x110);
|
|
||||||
stream.Position = 0xAD0;
|
|
||||||
SslCertSize = reader.ReadInt32();
|
|
||||||
stream.Position = 0x0AE0;
|
|
||||||
SslCert = reader.ReadBytes(SslCertSize);
|
|
||||||
stream.Position = 0x12E0;
|
|
||||||
SslCertSha256 = reader.ReadBytes(0x20);
|
|
||||||
|
|
||||||
stream.Position = 0x1300;
|
|
||||||
RandomNumber = reader.ReadBytes(0x1000);
|
|
||||||
stream.Position = 0x2300;
|
|
||||||
RandomNumberSha256 = reader.ReadBytes(0x20);
|
|
||||||
|
|
||||||
stream.Position = 0x2320;
|
|
||||||
GamecardKey = reader.ReadBytes(0x110);
|
|
||||||
stream.Position = 0x2440;
|
|
||||||
GamecardCert = reader.ReadBytes(0x400);
|
|
||||||
stream.Position = 0x2840;
|
|
||||||
GamecardCertSha256 = reader.ReadBytes(0x20);
|
|
||||||
|
|
||||||
stream.Position = 0x2860;
|
|
||||||
EticketKeyRsa = reader.ReadBytes(0x220);
|
|
||||||
stream.Position = 0x2A90;
|
|
||||||
EticketCertRsa = reader.ReadBytes(0x240);
|
|
||||||
|
|
||||||
stream.Position = 0x2CE0;
|
|
||||||
BatteryLot = reader.ReadUtf8Z(0x18);
|
|
||||||
|
|
||||||
stream.Position = 0x2D00;
|
|
||||||
SpeakerCalibValue = reader.ReadBytes(0x800);
|
|
||||||
|
|
||||||
stream.Position = 0x3510;
|
|
||||||
RegionCode = reader.ReadInt32();
|
|
||||||
|
|
||||||
stream.Position = 0x3520;
|
|
||||||
AmiiboKey = reader.ReadBytes(0x50);
|
|
||||||
stream.Position = 0x3580;
|
|
||||||
AmiiboCertEcqv = reader.ReadBytes(0x14);
|
|
||||||
stream.Position = 0x35A0;
|
|
||||||
AmiiboCertEcdsa = reader.ReadBytes(0x70);
|
|
||||||
stream.Position = 0x3620;
|
|
||||||
AmiiboKeyEcqvBls = reader.ReadBytes(0x40);
|
|
||||||
stream.Position = 0x3670;
|
|
||||||
AmiiboCertEcqvBls = reader.ReadBytes(0x20);
|
|
||||||
stream.Position = 0x36A0;
|
|
||||||
AmiiboRootCertEcqvBls = reader.ReadBytes(0x90);
|
|
||||||
|
|
||||||
stream.Position = 0x3740;
|
|
||||||
ProductModel = reader.ReadInt32();
|
|
||||||
stream.Position = 0x3750;
|
|
||||||
ColorVariation = reader.ReadBytes(0x06);
|
|
||||||
stream.Position = 0x3760;
|
|
||||||
LcdBacklightBrightnessMapping = reader.ReadBytes(0x0C);
|
|
||||||
|
|
||||||
stream.Position = 0x3770;
|
|
||||||
DeviceExtKeyEccB233 = reader.ReadBytes(0x50);
|
|
||||||
stream.Position = 0x37D0;
|
|
||||||
EticketExtKeyEccP256 = reader.ReadBytes(0x50);
|
|
||||||
stream.Position = 0x3830;
|
|
||||||
EticketExtKeyEccB233 = reader.ReadBytes(0x50);
|
|
||||||
stream.Position = 0x3890;
|
|
||||||
EticketExtKeyRsa = reader.ReadBytes(0x240);
|
|
||||||
stream.Position = 0x3AE0;
|
|
||||||
SslExtKey = reader.ReadBytes(0x130);
|
|
||||||
stream.Position = 0x3C20;
|
|
||||||
GamecardExtKey = reader.ReadBytes(0x130);
|
|
||||||
|
|
||||||
stream.Position = 0x3D60;
|
|
||||||
LcdVendorId = reader.ReadInt32();
|
|
||||||
|
|
||||||
stream.Position = 0x3D70;
|
|
||||||
ExtendedRsa2048DeviceKey = reader.ReadBytes(0x240);
|
|
||||||
stream.Position = 0x3FC0;
|
|
||||||
Rsa2048DeviceCertificate = reader.ReadBytes(0x240);
|
|
||||||
|
|
||||||
stream.Position = 0x4210;
|
|
||||||
UsbTypeCPowerSourceCircuitVersion = reader.ReadBytes(0x1);
|
|
||||||
|
|
||||||
stream.Position = 0x4220;
|
|
||||||
HomeMenuSchemeSubColor = reader.ReadInt32();
|
|
||||||
stream.Position = 0x4230;
|
|
||||||
HomeMenuSchemeBezelColor = reader.ReadInt32();
|
|
||||||
stream.Position = 0x4240;
|
|
||||||
HomeMenuSchemeMainColor1 = reader.ReadInt32();
|
|
||||||
stream.Position = 0x4250;
|
|
||||||
HomeMenuSchemeMainColor2 = reader.ReadInt32();
|
|
||||||
stream.Position = 0x4260;
|
|
||||||
HomeMenuSchemeMainColor3 = reader.ReadInt32();
|
|
||||||
|
|
||||||
stream.Position = 0x4270;
|
|
||||||
AnalogStickModuleTypeL = reader.ReadBytes(0x1);
|
|
||||||
stream.Position = 0x4280;
|
|
||||||
AnalogStickModelParameterL = reader.ReadBytes(0x12);
|
|
||||||
stream.Position = 0x42A0;
|
|
||||||
AnalogStickFactoryCalibrationL = reader.ReadBytes(0x9);
|
|
||||||
stream.Position = 0x42B0;
|
|
||||||
AnalogStickModuleTypeR = reader.ReadBytes(0x1);
|
|
||||||
stream.Position = 0x42C0;
|
|
||||||
AnalogStickModelParameterR = reader.ReadBytes(0x12);
|
|
||||||
stream.Position = 0x42E0;
|
|
||||||
AnalogStickFactoryCalibrationR = reader.ReadBytes(0x9);
|
|
||||||
|
|
||||||
stream.Position = 0x42F0;
|
|
||||||
ConsoleSixAxisSensorModuleType = reader.ReadBytes(0x1);
|
|
||||||
stream.Position = 0x4300;
|
|
||||||
ConsoleSixAxisSensorHorizontalOffset = reader.ReadBytes(0x6);
|
|
||||||
|
|
||||||
stream.Position = 0x4310;
|
|
||||||
BatteryVersion = reader.ReadBytes(0x1);
|
|
||||||
|
|
||||||
stream.Position = 0x4330;
|
|
||||||
HomeMenuSchemeModel = reader.ReadInt32();
|
|
||||||
|
|
||||||
stream.Position = 0x4340;
|
|
||||||
ConsoleSixAxisSensorMountType = reader.ReadBytes(0x1);
|
|
||||||
}
|
}
|
||||||
|
stream.Position = 0x210;
|
||||||
|
WlanMacAddr = reader.ReadBytes(0x6);
|
||||||
|
|
||||||
|
stream.Position = 0x220;
|
||||||
|
BdAddr = reader.ReadBytes(0x6);
|
||||||
|
stream.Position = 0x230;
|
||||||
|
AccelerometerOffset = reader.ReadBytes(0x6);
|
||||||
|
stream.Position = 0x238;
|
||||||
|
AccelerometerScale = reader.ReadBytes(0x6);
|
||||||
|
stream.Position = 0x240;
|
||||||
|
GyroscopeOffset = reader.ReadBytes(0x6);
|
||||||
|
stream.Position = 0x248;
|
||||||
|
GyroscopeScale = reader.ReadBytes(0x6);
|
||||||
|
|
||||||
|
stream.Position = 0x250;
|
||||||
|
SerialNumber = reader.ReadUtf8Z(0x18);
|
||||||
|
|
||||||
|
stream.Position = 0x270;
|
||||||
|
DeviceKeyEccP256 = reader.ReadBytes(0x30);
|
||||||
|
stream.Position = 0x2B0;
|
||||||
|
DeviceCertEccP256 = reader.ReadBytes(0x180);
|
||||||
|
stream.Position = 0x440;
|
||||||
|
DeviceKeyEccB233 = reader.ReadBytes(0x30);
|
||||||
|
stream.Position = 0x480;
|
||||||
|
DeviceCertEccB233 = reader.ReadBytes(0x180);
|
||||||
|
stream.Position = 0x610;
|
||||||
|
EticketKeyEccP256 = reader.ReadBytes(0x30);
|
||||||
|
stream.Position = 0x650;
|
||||||
|
EticketCertEccP256 = reader.ReadBytes(0x180);
|
||||||
|
stream.Position = 0x7E0;
|
||||||
|
EticketKeyEccB233 = reader.ReadBytes(0x30);
|
||||||
|
stream.Position = 0x820;
|
||||||
|
EticketCertEccB233 = reader.ReadBytes(0x180);
|
||||||
|
|
||||||
|
stream.Position = 0x9B0;
|
||||||
|
SslKey = reader.ReadBytes(0x110);
|
||||||
|
stream.Position = 0xAD0;
|
||||||
|
SslCertSize = reader.ReadInt32();
|
||||||
|
stream.Position = 0x0AE0;
|
||||||
|
SslCert = reader.ReadBytes(SslCertSize);
|
||||||
|
stream.Position = 0x12E0;
|
||||||
|
SslCertSha256 = reader.ReadBytes(0x20);
|
||||||
|
|
||||||
|
stream.Position = 0x1300;
|
||||||
|
RandomNumber = reader.ReadBytes(0x1000);
|
||||||
|
stream.Position = 0x2300;
|
||||||
|
RandomNumberSha256 = reader.ReadBytes(0x20);
|
||||||
|
|
||||||
|
stream.Position = 0x2320;
|
||||||
|
GamecardKey = reader.ReadBytes(0x110);
|
||||||
|
stream.Position = 0x2440;
|
||||||
|
GamecardCert = reader.ReadBytes(0x400);
|
||||||
|
stream.Position = 0x2840;
|
||||||
|
GamecardCertSha256 = reader.ReadBytes(0x20);
|
||||||
|
|
||||||
|
stream.Position = 0x2860;
|
||||||
|
EticketKeyRsa = reader.ReadBytes(0x220);
|
||||||
|
stream.Position = 0x2A90;
|
||||||
|
EticketCertRsa = reader.ReadBytes(0x240);
|
||||||
|
|
||||||
|
stream.Position = 0x2CE0;
|
||||||
|
BatteryLot = reader.ReadUtf8Z(0x18);
|
||||||
|
|
||||||
|
stream.Position = 0x2D00;
|
||||||
|
SpeakerCalibValue = reader.ReadBytes(0x800);
|
||||||
|
|
||||||
|
stream.Position = 0x3510;
|
||||||
|
RegionCode = reader.ReadInt32();
|
||||||
|
|
||||||
|
stream.Position = 0x3520;
|
||||||
|
AmiiboKey = reader.ReadBytes(0x50);
|
||||||
|
stream.Position = 0x3580;
|
||||||
|
AmiiboCertEcqv = reader.ReadBytes(0x14);
|
||||||
|
stream.Position = 0x35A0;
|
||||||
|
AmiiboCertEcdsa = reader.ReadBytes(0x70);
|
||||||
|
stream.Position = 0x3620;
|
||||||
|
AmiiboKeyEcqvBls = reader.ReadBytes(0x40);
|
||||||
|
stream.Position = 0x3670;
|
||||||
|
AmiiboCertEcqvBls = reader.ReadBytes(0x20);
|
||||||
|
stream.Position = 0x36A0;
|
||||||
|
AmiiboRootCertEcqvBls = reader.ReadBytes(0x90);
|
||||||
|
|
||||||
|
stream.Position = 0x3740;
|
||||||
|
ProductModel = reader.ReadInt32();
|
||||||
|
stream.Position = 0x3750;
|
||||||
|
ColorVariation = reader.ReadBytes(0x06);
|
||||||
|
stream.Position = 0x3760;
|
||||||
|
LcdBacklightBrightnessMapping = reader.ReadBytes(0x0C);
|
||||||
|
|
||||||
|
stream.Position = 0x3770;
|
||||||
|
DeviceExtKeyEccB233 = reader.ReadBytes(0x50);
|
||||||
|
stream.Position = 0x37D0;
|
||||||
|
EticketExtKeyEccP256 = reader.ReadBytes(0x50);
|
||||||
|
stream.Position = 0x3830;
|
||||||
|
EticketExtKeyEccB233 = reader.ReadBytes(0x50);
|
||||||
|
stream.Position = 0x3890;
|
||||||
|
EticketExtKeyRsa = reader.ReadBytes(0x240);
|
||||||
|
stream.Position = 0x3AE0;
|
||||||
|
SslExtKey = reader.ReadBytes(0x130);
|
||||||
|
stream.Position = 0x3C20;
|
||||||
|
GamecardExtKey = reader.ReadBytes(0x130);
|
||||||
|
|
||||||
|
stream.Position = 0x3D60;
|
||||||
|
LcdVendorId = reader.ReadInt32();
|
||||||
|
|
||||||
|
stream.Position = 0x3D70;
|
||||||
|
ExtendedRsa2048DeviceKey = reader.ReadBytes(0x240);
|
||||||
|
stream.Position = 0x3FC0;
|
||||||
|
Rsa2048DeviceCertificate = reader.ReadBytes(0x240);
|
||||||
|
|
||||||
|
stream.Position = 0x4210;
|
||||||
|
UsbTypeCPowerSourceCircuitVersion = reader.ReadBytes(0x1);
|
||||||
|
|
||||||
|
stream.Position = 0x4220;
|
||||||
|
HomeMenuSchemeSubColor = reader.ReadInt32();
|
||||||
|
stream.Position = 0x4230;
|
||||||
|
HomeMenuSchemeBezelColor = reader.ReadInt32();
|
||||||
|
stream.Position = 0x4240;
|
||||||
|
HomeMenuSchemeMainColor1 = reader.ReadInt32();
|
||||||
|
stream.Position = 0x4250;
|
||||||
|
HomeMenuSchemeMainColor2 = reader.ReadInt32();
|
||||||
|
stream.Position = 0x4260;
|
||||||
|
HomeMenuSchemeMainColor3 = reader.ReadInt32();
|
||||||
|
|
||||||
|
stream.Position = 0x4270;
|
||||||
|
AnalogStickModuleTypeL = reader.ReadBytes(0x1);
|
||||||
|
stream.Position = 0x4280;
|
||||||
|
AnalogStickModelParameterL = reader.ReadBytes(0x12);
|
||||||
|
stream.Position = 0x42A0;
|
||||||
|
AnalogStickFactoryCalibrationL = reader.ReadBytes(0x9);
|
||||||
|
stream.Position = 0x42B0;
|
||||||
|
AnalogStickModuleTypeR = reader.ReadBytes(0x1);
|
||||||
|
stream.Position = 0x42C0;
|
||||||
|
AnalogStickModelParameterR = reader.ReadBytes(0x12);
|
||||||
|
stream.Position = 0x42E0;
|
||||||
|
AnalogStickFactoryCalibrationR = reader.ReadBytes(0x9);
|
||||||
|
|
||||||
|
stream.Position = 0x42F0;
|
||||||
|
ConsoleSixAxisSensorModuleType = reader.ReadBytes(0x1);
|
||||||
|
stream.Position = 0x4300;
|
||||||
|
ConsoleSixAxisSensorHorizontalOffset = reader.ReadBytes(0x6);
|
||||||
|
|
||||||
|
stream.Position = 0x4310;
|
||||||
|
BatteryVersion = reader.ReadBytes(0x1);
|
||||||
|
|
||||||
|
stream.Position = 0x4330;
|
||||||
|
HomeMenuSchemeModel = reader.ReadInt32();
|
||||||
|
|
||||||
|
stream.Position = 0x4340;
|
||||||
|
ConsoleSixAxisSensorMountType = reader.ReadBytes(0x1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,323 +4,322 @@ using LibHac.FsSystem.NcaUtils;
|
|||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
using ContentType = LibHac.Ncm.ContentType;
|
using ContentType = LibHac.Ncm.ContentType;
|
||||||
|
|
||||||
namespace LibHac
|
namespace LibHac;
|
||||||
|
|
||||||
|
public class Cnmt
|
||||||
{
|
{
|
||||||
public class Cnmt
|
public ulong TitleId { get; }
|
||||||
|
public TitleVersion TitleVersion { get; }
|
||||||
|
public ContentMetaType Type { get; }
|
||||||
|
public byte FieldD { get; }
|
||||||
|
public int TableOffset { get; }
|
||||||
|
public int ContentEntryCount { get; }
|
||||||
|
public int MetaEntryCount { get; }
|
||||||
|
|
||||||
|
public CnmtContentEntry[] ContentEntries { get; }
|
||||||
|
public CnmtContentMetaEntry[] MetaEntries { get; }
|
||||||
|
|
||||||
|
public ulong ApplicationTitleId { get; }
|
||||||
|
public ulong PatchTitleId { get; }
|
||||||
|
public TitleVersion MinimumSystemVersion { get; }
|
||||||
|
public TitleVersion MinimumApplicationVersion { get; }
|
||||||
|
|
||||||
|
public CnmtExtended ExtendedData { get; }
|
||||||
|
|
||||||
|
public byte[] Hash { get; }
|
||||||
|
|
||||||
|
public Cnmt() { }
|
||||||
|
|
||||||
|
public Cnmt(Stream file)
|
||||||
{
|
{
|
||||||
public ulong TitleId { get; }
|
using (var reader = new BinaryReader(file))
|
||||||
public TitleVersion TitleVersion { get; }
|
|
||||||
public ContentMetaType Type { get; }
|
|
||||||
public byte FieldD { get; }
|
|
||||||
public int TableOffset { get; }
|
|
||||||
public int ContentEntryCount { get; }
|
|
||||||
public int MetaEntryCount { get; }
|
|
||||||
|
|
||||||
public CnmtContentEntry[] ContentEntries { get; }
|
|
||||||
public CnmtContentMetaEntry[] MetaEntries { get; }
|
|
||||||
|
|
||||||
public ulong ApplicationTitleId { get; }
|
|
||||||
public ulong PatchTitleId { get; }
|
|
||||||
public TitleVersion MinimumSystemVersion { get; }
|
|
||||||
public TitleVersion MinimumApplicationVersion { get; }
|
|
||||||
|
|
||||||
public CnmtExtended ExtendedData { get; }
|
|
||||||
|
|
||||||
public byte[] Hash { get; }
|
|
||||||
|
|
||||||
public Cnmt() { }
|
|
||||||
|
|
||||||
public Cnmt(Stream file)
|
|
||||||
{
|
|
||||||
using (var reader = new BinaryReader(file))
|
|
||||||
{
|
|
||||||
TitleId = reader.ReadUInt64();
|
|
||||||
uint version = reader.ReadUInt32();
|
|
||||||
Type = (ContentMetaType)reader.ReadByte();
|
|
||||||
TitleVersion = new TitleVersion(version, Type < ContentMetaType.Application);
|
|
||||||
FieldD = reader.ReadByte();
|
|
||||||
TableOffset = reader.ReadUInt16();
|
|
||||||
ContentEntryCount = reader.ReadUInt16();
|
|
||||||
MetaEntryCount = reader.ReadUInt16();
|
|
||||||
|
|
||||||
// Old, pre-release cnmt files don't have the "required system version" field.
|
|
||||||
// Try to detect this by reading the padding after that field.
|
|
||||||
// The old format usually contains hashes there.
|
|
||||||
file.Position += 8;
|
|
||||||
int padding = reader.ReadInt32();
|
|
||||||
bool isOldCnmtFormat = padding != 0;
|
|
||||||
|
|
||||||
switch (Type)
|
|
||||||
{
|
|
||||||
case ContentMetaType.Application:
|
|
||||||
ApplicationTitleId = TitleId;
|
|
||||||
PatchTitleId = reader.ReadUInt64();
|
|
||||||
MinimumSystemVersion = new TitleVersion(reader.ReadUInt32(), true);
|
|
||||||
break;
|
|
||||||
case ContentMetaType.Patch:
|
|
||||||
ApplicationTitleId = reader.ReadUInt64();
|
|
||||||
MinimumSystemVersion = new TitleVersion(reader.ReadUInt32(), true);
|
|
||||||
break;
|
|
||||||
case ContentMetaType.AddOnContent:
|
|
||||||
ApplicationTitleId = reader.ReadUInt64();
|
|
||||||
MinimumApplicationVersion = new TitleVersion(reader.ReadUInt32());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int baseOffset = isOldCnmtFormat ? 0x18 : 0x20;
|
|
||||||
file.Position = baseOffset + TableOffset;
|
|
||||||
|
|
||||||
ContentEntries = new CnmtContentEntry[ContentEntryCount];
|
|
||||||
MetaEntries = new CnmtContentMetaEntry[MetaEntryCount];
|
|
||||||
|
|
||||||
for (int i = 0; i < ContentEntryCount; i++)
|
|
||||||
{
|
|
||||||
ContentEntries[i] = new CnmtContentEntry(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < MetaEntryCount; i++)
|
|
||||||
{
|
|
||||||
MetaEntries[i] = new CnmtContentMetaEntry(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Type == ContentMetaType.Patch)
|
|
||||||
{
|
|
||||||
ExtendedData = new CnmtExtended(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
Hash = reader.ReadBytes(0x20);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CnmtContentEntry
|
|
||||||
{
|
|
||||||
public byte[] Hash { get; set; }
|
|
||||||
public byte[] NcaId { get; set; }
|
|
||||||
public long Size { get; set; }
|
|
||||||
public ContentType Type { get; set; }
|
|
||||||
|
|
||||||
public CnmtContentEntry() { }
|
|
||||||
|
|
||||||
public CnmtContentEntry(BinaryReader reader)
|
|
||||||
{
|
|
||||||
Hash = reader.ReadBytes(0x20);
|
|
||||||
NcaId = reader.ReadBytes(0x10);
|
|
||||||
Size = reader.ReadUInt32();
|
|
||||||
Size |= (long)reader.ReadUInt16() << 32;
|
|
||||||
Type = (ContentType)reader.ReadByte();
|
|
||||||
reader.BaseStream.Position += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CnmtContentMetaEntry
|
|
||||||
{
|
|
||||||
public ulong TitleId { get; }
|
|
||||||
public TitleVersion Version { get; }
|
|
||||||
public ContentType Type { get; }
|
|
||||||
|
|
||||||
public CnmtContentMetaEntry() { }
|
|
||||||
|
|
||||||
public CnmtContentMetaEntry(BinaryReader reader)
|
|
||||||
{
|
{
|
||||||
TitleId = reader.ReadUInt64();
|
TitleId = reader.ReadUInt64();
|
||||||
Version = new TitleVersion(reader.ReadUInt32(), true);
|
uint version = reader.ReadUInt32();
|
||||||
Type = (ContentType)reader.ReadByte();
|
|
||||||
reader.BaseStream.Position += 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CnmtExtended
|
|
||||||
{
|
|
||||||
public int PrevMetaCount { get; }
|
|
||||||
public int PrevDeltaSetCount { get; }
|
|
||||||
public int DeltaSetCount { get; }
|
|
||||||
public int FragmentSetCount { get; }
|
|
||||||
public int PrevContentCount { get; }
|
|
||||||
public int DeltaContentCount { get; }
|
|
||||||
|
|
||||||
public CnmtPrevMetaEntry[] PrevMetas { get; }
|
|
||||||
public CnmtPrevDelta[] PrevDeltaSets { get; }
|
|
||||||
public CnmtDeltaSetInfo[] DeltaSets { get; }
|
|
||||||
public CnmtFragmentSetInfo[] FragmentSets { get; }
|
|
||||||
public CnmtPrevContent[] PrevContents { get; }
|
|
||||||
public CnmtContentEntry[] DeltaContents { get; }
|
|
||||||
public FragmentMapEntry[] FragmentMap { get; }
|
|
||||||
|
|
||||||
public CnmtExtended(BinaryReader reader)
|
|
||||||
{
|
|
||||||
PrevMetaCount = reader.ReadInt32(); // Lists all previous content meta files
|
|
||||||
PrevDeltaSetCount = reader.ReadInt32(); // Lists all previous delta sets
|
|
||||||
DeltaSetCount = reader.ReadInt32(); // Lists the delta set for the current title version
|
|
||||||
FragmentSetCount = reader.ReadInt32(); // Groups fragments into full deltas
|
|
||||||
PrevContentCount = reader.ReadInt32(); // Lists all previous NCAs for the title
|
|
||||||
DeltaContentCount = reader.ReadInt32(); // Lists all NCAs containing delta fragments
|
|
||||||
reader.BaseStream.Position += 4;
|
|
||||||
|
|
||||||
PrevMetas = new CnmtPrevMetaEntry[PrevMetaCount];
|
|
||||||
PrevDeltaSets = new CnmtPrevDelta[PrevDeltaSetCount];
|
|
||||||
DeltaSets = new CnmtDeltaSetInfo[DeltaSetCount];
|
|
||||||
FragmentSets = new CnmtFragmentSetInfo[FragmentSetCount];
|
|
||||||
PrevContents = new CnmtPrevContent[PrevContentCount];
|
|
||||||
DeltaContents = new CnmtContentEntry[DeltaContentCount];
|
|
||||||
|
|
||||||
for (int i = 0; i < PrevMetaCount; i++)
|
|
||||||
{
|
|
||||||
PrevMetas[i] = new CnmtPrevMetaEntry(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < PrevDeltaSetCount; i++)
|
|
||||||
{
|
|
||||||
PrevDeltaSets[i] = new CnmtPrevDelta(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < DeltaSetCount; i++)
|
|
||||||
{
|
|
||||||
DeltaSets[i] = new CnmtDeltaSetInfo(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < FragmentSetCount; i++)
|
|
||||||
{
|
|
||||||
FragmentSets[i] = new CnmtFragmentSetInfo(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < PrevContentCount; i++)
|
|
||||||
{
|
|
||||||
PrevContents[i] = new CnmtPrevContent(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < DeltaContentCount; i++)
|
|
||||||
{
|
|
||||||
DeltaContents[i] = new CnmtContentEntry(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
int fragmentCount = FragmentSets.Sum(x => x.FragmentCount);
|
|
||||||
FragmentMap = new FragmentMapEntry[fragmentCount];
|
|
||||||
|
|
||||||
for (int i = 0; i < fragmentCount; i++)
|
|
||||||
{
|
|
||||||
FragmentMap[i] = new FragmentMapEntry(reader);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CnmtPrevMetaEntry
|
|
||||||
{
|
|
||||||
public ulong TitleId { get; }
|
|
||||||
public TitleVersion Version { get; }
|
|
||||||
public ContentMetaType Type { get; }
|
|
||||||
public byte[] Hash { get; }
|
|
||||||
public short ContentCount { get; }
|
|
||||||
public short CnmtPrevMetaEntryField32 { get; }
|
|
||||||
public int CnmtPrevMetaEntryField34 { get; }
|
|
||||||
|
|
||||||
public CnmtPrevMetaEntry(BinaryReader reader)
|
|
||||||
{
|
|
||||||
TitleId = reader.ReadUInt64();
|
|
||||||
Version = new TitleVersion(reader.ReadUInt32());
|
|
||||||
Type = (ContentMetaType)reader.ReadByte();
|
Type = (ContentMetaType)reader.ReadByte();
|
||||||
reader.BaseStream.Position += 3;
|
TitleVersion = new TitleVersion(version, Type < ContentMetaType.Application);
|
||||||
|
FieldD = reader.ReadByte();
|
||||||
|
TableOffset = reader.ReadUInt16();
|
||||||
|
ContentEntryCount = reader.ReadUInt16();
|
||||||
|
MetaEntryCount = reader.ReadUInt16();
|
||||||
|
|
||||||
|
// Old, pre-release cnmt files don't have the "required system version" field.
|
||||||
|
// Try to detect this by reading the padding after that field.
|
||||||
|
// The old format usually contains hashes there.
|
||||||
|
file.Position += 8;
|
||||||
|
int padding = reader.ReadInt32();
|
||||||
|
bool isOldCnmtFormat = padding != 0;
|
||||||
|
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case ContentMetaType.Application:
|
||||||
|
ApplicationTitleId = TitleId;
|
||||||
|
PatchTitleId = reader.ReadUInt64();
|
||||||
|
MinimumSystemVersion = new TitleVersion(reader.ReadUInt32(), true);
|
||||||
|
break;
|
||||||
|
case ContentMetaType.Patch:
|
||||||
|
ApplicationTitleId = reader.ReadUInt64();
|
||||||
|
MinimumSystemVersion = new TitleVersion(reader.ReadUInt32(), true);
|
||||||
|
break;
|
||||||
|
case ContentMetaType.AddOnContent:
|
||||||
|
ApplicationTitleId = reader.ReadUInt64();
|
||||||
|
MinimumApplicationVersion = new TitleVersion(reader.ReadUInt32());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int baseOffset = isOldCnmtFormat ? 0x18 : 0x20;
|
||||||
|
file.Position = baseOffset + TableOffset;
|
||||||
|
|
||||||
|
ContentEntries = new CnmtContentEntry[ContentEntryCount];
|
||||||
|
MetaEntries = new CnmtContentMetaEntry[MetaEntryCount];
|
||||||
|
|
||||||
|
for (int i = 0; i < ContentEntryCount; i++)
|
||||||
|
{
|
||||||
|
ContentEntries[i] = new CnmtContentEntry(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < MetaEntryCount; i++)
|
||||||
|
{
|
||||||
|
MetaEntries[i] = new CnmtContentMetaEntry(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Type == ContentMetaType.Patch)
|
||||||
|
{
|
||||||
|
ExtendedData = new CnmtExtended(reader);
|
||||||
|
}
|
||||||
|
|
||||||
Hash = reader.ReadBytes(0x20);
|
Hash = reader.ReadBytes(0x20);
|
||||||
ContentCount = reader.ReadInt16();
|
|
||||||
CnmtPrevMetaEntryField32 = reader.ReadInt16();
|
|
||||||
CnmtPrevMetaEntryField34 = reader.ReadInt32();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CnmtPrevDelta
|
|
||||||
{
|
|
||||||
public ulong TitleIdOld { get; }
|
|
||||||
public ulong TitleIdNew { get; }
|
|
||||||
public TitleVersion VersionOld { get; }
|
|
||||||
public TitleVersion VersionNew { get; }
|
|
||||||
public long Size { get; }
|
|
||||||
public long CnmtPrevDeltaField20 { get; }
|
|
||||||
|
|
||||||
public CnmtPrevDelta(BinaryReader reader)
|
|
||||||
{
|
|
||||||
TitleIdOld = reader.ReadUInt64();
|
|
||||||
TitleIdNew = reader.ReadUInt64();
|
|
||||||
VersionOld = new TitleVersion(reader.ReadUInt32());
|
|
||||||
VersionNew = new TitleVersion(reader.ReadUInt32());
|
|
||||||
Size = reader.ReadInt64();
|
|
||||||
CnmtPrevDeltaField20 = reader.ReadInt64();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CnmtDeltaSetInfo
|
|
||||||
{
|
|
||||||
public ulong TitleIdOld { get; }
|
|
||||||
public ulong TitleIdNew { get; }
|
|
||||||
public TitleVersion VersionOld { get; }
|
|
||||||
public TitleVersion VersionNew { get; }
|
|
||||||
public long FragmentSetCount { get; }
|
|
||||||
public long DeltaContentCount { get; }
|
|
||||||
|
|
||||||
public CnmtDeltaSetInfo(BinaryReader reader)
|
|
||||||
{
|
|
||||||
TitleIdOld = reader.ReadUInt64();
|
|
||||||
TitleIdNew = reader.ReadUInt64();
|
|
||||||
VersionOld = new TitleVersion(reader.ReadUInt32());
|
|
||||||
VersionNew = new TitleVersion(reader.ReadUInt32());
|
|
||||||
FragmentSetCount = reader.ReadInt64();
|
|
||||||
DeltaContentCount = reader.ReadInt64();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CnmtFragmentSetInfo
|
|
||||||
{
|
|
||||||
public byte[] NcaIdOld { get; }
|
|
||||||
public byte[] NcaIdNew { get; }
|
|
||||||
public long SizeOld { get; }
|
|
||||||
public long SizeNew { get; }
|
|
||||||
public short FragmentCount { get; }
|
|
||||||
public ContentType Type { get; }
|
|
||||||
public UpdateType DeltaType { get; }
|
|
||||||
public int FragmentSetInfoField30 { get; }
|
|
||||||
|
|
||||||
|
|
||||||
public CnmtFragmentSetInfo(BinaryReader reader)
|
|
||||||
{
|
|
||||||
NcaIdOld = reader.ReadBytes(0x10);
|
|
||||||
NcaIdNew = reader.ReadBytes(0x10);
|
|
||||||
|
|
||||||
SizeOld = reader.ReadUInt32();
|
|
||||||
SizeOld |= (long)reader.ReadUInt16() << 32;
|
|
||||||
SizeNew |= (long)reader.ReadUInt16() << 32;
|
|
||||||
SizeNew = reader.ReadUInt32();
|
|
||||||
|
|
||||||
FragmentCount = reader.ReadInt16();
|
|
||||||
Type = (ContentType)reader.ReadByte();
|
|
||||||
DeltaType = (UpdateType)reader.ReadByte();
|
|
||||||
FragmentSetInfoField30 = reader.ReadInt32();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CnmtPrevContent
|
|
||||||
{
|
|
||||||
public byte[] NcaId { get; }
|
|
||||||
public long Size { get; }
|
|
||||||
public ContentType Type { get; }
|
|
||||||
|
|
||||||
public CnmtPrevContent(BinaryReader reader)
|
|
||||||
{
|
|
||||||
NcaId = reader.ReadBytes(0x10);
|
|
||||||
Size = reader.ReadUInt32();
|
|
||||||
Size |= (long)reader.ReadUInt16() << 32;
|
|
||||||
Type = (ContentType)reader.ReadByte();
|
|
||||||
reader.BaseStream.Position += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FragmentMapEntry
|
|
||||||
{
|
|
||||||
public short ContentIndex { get; }
|
|
||||||
public short FragmentIndex { get; }
|
|
||||||
|
|
||||||
public FragmentMapEntry(BinaryReader reader)
|
|
||||||
{
|
|
||||||
ContentIndex = reader.ReadInt16();
|
|
||||||
FragmentIndex = reader.ReadInt16();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CnmtContentEntry
|
||||||
|
{
|
||||||
|
public byte[] Hash { get; set; }
|
||||||
|
public byte[] NcaId { get; set; }
|
||||||
|
public long Size { get; set; }
|
||||||
|
public ContentType Type { get; set; }
|
||||||
|
|
||||||
|
public CnmtContentEntry() { }
|
||||||
|
|
||||||
|
public CnmtContentEntry(BinaryReader reader)
|
||||||
|
{
|
||||||
|
Hash = reader.ReadBytes(0x20);
|
||||||
|
NcaId = reader.ReadBytes(0x10);
|
||||||
|
Size = reader.ReadUInt32();
|
||||||
|
Size |= (long)reader.ReadUInt16() << 32;
|
||||||
|
Type = (ContentType)reader.ReadByte();
|
||||||
|
reader.BaseStream.Position += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CnmtContentMetaEntry
|
||||||
|
{
|
||||||
|
public ulong TitleId { get; }
|
||||||
|
public TitleVersion Version { get; }
|
||||||
|
public ContentType Type { get; }
|
||||||
|
|
||||||
|
public CnmtContentMetaEntry() { }
|
||||||
|
|
||||||
|
public CnmtContentMetaEntry(BinaryReader reader)
|
||||||
|
{
|
||||||
|
TitleId = reader.ReadUInt64();
|
||||||
|
Version = new TitleVersion(reader.ReadUInt32(), true);
|
||||||
|
Type = (ContentType)reader.ReadByte();
|
||||||
|
reader.BaseStream.Position += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CnmtExtended
|
||||||
|
{
|
||||||
|
public int PrevMetaCount { get; }
|
||||||
|
public int PrevDeltaSetCount { get; }
|
||||||
|
public int DeltaSetCount { get; }
|
||||||
|
public int FragmentSetCount { get; }
|
||||||
|
public int PrevContentCount { get; }
|
||||||
|
public int DeltaContentCount { get; }
|
||||||
|
|
||||||
|
public CnmtPrevMetaEntry[] PrevMetas { get; }
|
||||||
|
public CnmtPrevDelta[] PrevDeltaSets { get; }
|
||||||
|
public CnmtDeltaSetInfo[] DeltaSets { get; }
|
||||||
|
public CnmtFragmentSetInfo[] FragmentSets { get; }
|
||||||
|
public CnmtPrevContent[] PrevContents { get; }
|
||||||
|
public CnmtContentEntry[] DeltaContents { get; }
|
||||||
|
public FragmentMapEntry[] FragmentMap { get; }
|
||||||
|
|
||||||
|
public CnmtExtended(BinaryReader reader)
|
||||||
|
{
|
||||||
|
PrevMetaCount = reader.ReadInt32(); // Lists all previous content meta files
|
||||||
|
PrevDeltaSetCount = reader.ReadInt32(); // Lists all previous delta sets
|
||||||
|
DeltaSetCount = reader.ReadInt32(); // Lists the delta set for the current title version
|
||||||
|
FragmentSetCount = reader.ReadInt32(); // Groups fragments into full deltas
|
||||||
|
PrevContentCount = reader.ReadInt32(); // Lists all previous NCAs for the title
|
||||||
|
DeltaContentCount = reader.ReadInt32(); // Lists all NCAs containing delta fragments
|
||||||
|
reader.BaseStream.Position += 4;
|
||||||
|
|
||||||
|
PrevMetas = new CnmtPrevMetaEntry[PrevMetaCount];
|
||||||
|
PrevDeltaSets = new CnmtPrevDelta[PrevDeltaSetCount];
|
||||||
|
DeltaSets = new CnmtDeltaSetInfo[DeltaSetCount];
|
||||||
|
FragmentSets = new CnmtFragmentSetInfo[FragmentSetCount];
|
||||||
|
PrevContents = new CnmtPrevContent[PrevContentCount];
|
||||||
|
DeltaContents = new CnmtContentEntry[DeltaContentCount];
|
||||||
|
|
||||||
|
for (int i = 0; i < PrevMetaCount; i++)
|
||||||
|
{
|
||||||
|
PrevMetas[i] = new CnmtPrevMetaEntry(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < PrevDeltaSetCount; i++)
|
||||||
|
{
|
||||||
|
PrevDeltaSets[i] = new CnmtPrevDelta(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < DeltaSetCount; i++)
|
||||||
|
{
|
||||||
|
DeltaSets[i] = new CnmtDeltaSetInfo(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < FragmentSetCount; i++)
|
||||||
|
{
|
||||||
|
FragmentSets[i] = new CnmtFragmentSetInfo(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < PrevContentCount; i++)
|
||||||
|
{
|
||||||
|
PrevContents[i] = new CnmtPrevContent(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < DeltaContentCount; i++)
|
||||||
|
{
|
||||||
|
DeltaContents[i] = new CnmtContentEntry(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fragmentCount = FragmentSets.Sum(x => x.FragmentCount);
|
||||||
|
FragmentMap = new FragmentMapEntry[fragmentCount];
|
||||||
|
|
||||||
|
for (int i = 0; i < fragmentCount; i++)
|
||||||
|
{
|
||||||
|
FragmentMap[i] = new FragmentMapEntry(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CnmtPrevMetaEntry
|
||||||
|
{
|
||||||
|
public ulong TitleId { get; }
|
||||||
|
public TitleVersion Version { get; }
|
||||||
|
public ContentMetaType Type { get; }
|
||||||
|
public byte[] Hash { get; }
|
||||||
|
public short ContentCount { get; }
|
||||||
|
public short CnmtPrevMetaEntryField32 { get; }
|
||||||
|
public int CnmtPrevMetaEntryField34 { get; }
|
||||||
|
|
||||||
|
public CnmtPrevMetaEntry(BinaryReader reader)
|
||||||
|
{
|
||||||
|
TitleId = reader.ReadUInt64();
|
||||||
|
Version = new TitleVersion(reader.ReadUInt32());
|
||||||
|
Type = (ContentMetaType)reader.ReadByte();
|
||||||
|
reader.BaseStream.Position += 3;
|
||||||
|
Hash = reader.ReadBytes(0x20);
|
||||||
|
ContentCount = reader.ReadInt16();
|
||||||
|
CnmtPrevMetaEntryField32 = reader.ReadInt16();
|
||||||
|
CnmtPrevMetaEntryField34 = reader.ReadInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CnmtPrevDelta
|
||||||
|
{
|
||||||
|
public ulong TitleIdOld { get; }
|
||||||
|
public ulong TitleIdNew { get; }
|
||||||
|
public TitleVersion VersionOld { get; }
|
||||||
|
public TitleVersion VersionNew { get; }
|
||||||
|
public long Size { get; }
|
||||||
|
public long CnmtPrevDeltaField20 { get; }
|
||||||
|
|
||||||
|
public CnmtPrevDelta(BinaryReader reader)
|
||||||
|
{
|
||||||
|
TitleIdOld = reader.ReadUInt64();
|
||||||
|
TitleIdNew = reader.ReadUInt64();
|
||||||
|
VersionOld = new TitleVersion(reader.ReadUInt32());
|
||||||
|
VersionNew = new TitleVersion(reader.ReadUInt32());
|
||||||
|
Size = reader.ReadInt64();
|
||||||
|
CnmtPrevDeltaField20 = reader.ReadInt64();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CnmtDeltaSetInfo
|
||||||
|
{
|
||||||
|
public ulong TitleIdOld { get; }
|
||||||
|
public ulong TitleIdNew { get; }
|
||||||
|
public TitleVersion VersionOld { get; }
|
||||||
|
public TitleVersion VersionNew { get; }
|
||||||
|
public long FragmentSetCount { get; }
|
||||||
|
public long DeltaContentCount { get; }
|
||||||
|
|
||||||
|
public CnmtDeltaSetInfo(BinaryReader reader)
|
||||||
|
{
|
||||||
|
TitleIdOld = reader.ReadUInt64();
|
||||||
|
TitleIdNew = reader.ReadUInt64();
|
||||||
|
VersionOld = new TitleVersion(reader.ReadUInt32());
|
||||||
|
VersionNew = new TitleVersion(reader.ReadUInt32());
|
||||||
|
FragmentSetCount = reader.ReadInt64();
|
||||||
|
DeltaContentCount = reader.ReadInt64();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CnmtFragmentSetInfo
|
||||||
|
{
|
||||||
|
public byte[] NcaIdOld { get; }
|
||||||
|
public byte[] NcaIdNew { get; }
|
||||||
|
public long SizeOld { get; }
|
||||||
|
public long SizeNew { get; }
|
||||||
|
public short FragmentCount { get; }
|
||||||
|
public ContentType Type { get; }
|
||||||
|
public UpdateType DeltaType { get; }
|
||||||
|
public int FragmentSetInfoField30 { get; }
|
||||||
|
|
||||||
|
|
||||||
|
public CnmtFragmentSetInfo(BinaryReader reader)
|
||||||
|
{
|
||||||
|
NcaIdOld = reader.ReadBytes(0x10);
|
||||||
|
NcaIdNew = reader.ReadBytes(0x10);
|
||||||
|
|
||||||
|
SizeOld = reader.ReadUInt32();
|
||||||
|
SizeOld |= (long)reader.ReadUInt16() << 32;
|
||||||
|
SizeNew |= (long)reader.ReadUInt16() << 32;
|
||||||
|
SizeNew = reader.ReadUInt32();
|
||||||
|
|
||||||
|
FragmentCount = reader.ReadInt16();
|
||||||
|
Type = (ContentType)reader.ReadByte();
|
||||||
|
DeltaType = (UpdateType)reader.ReadByte();
|
||||||
|
FragmentSetInfoField30 = reader.ReadInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CnmtPrevContent
|
||||||
|
{
|
||||||
|
public byte[] NcaId { get; }
|
||||||
|
public long Size { get; }
|
||||||
|
public ContentType Type { get; }
|
||||||
|
|
||||||
|
public CnmtPrevContent(BinaryReader reader)
|
||||||
|
{
|
||||||
|
NcaId = reader.ReadBytes(0x10);
|
||||||
|
Size = reader.ReadUInt32();
|
||||||
|
Size |= (long)reader.ReadUInt16() << 32;
|
||||||
|
Type = (ContentType)reader.ReadByte();
|
||||||
|
reader.BaseStream.Position += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FragmentMapEntry
|
||||||
|
{
|
||||||
|
public short ContentIndex { get; }
|
||||||
|
public short FragmentIndex { get; }
|
||||||
|
|
||||||
|
public FragmentMapEntry(BinaryReader reader)
|
||||||
|
{
|
||||||
|
ContentIndex = reader.ReadInt16();
|
||||||
|
FragmentIndex = reader.ReadInt16();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,108 +3,107 @@ using System.Diagnostics;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a representation of a region of memory as if it were a series of blittable structs
|
||||||
|
/// of type <typeparamref name="T"/>. Also allows viewing the memory as a <see cref="Span{T}"/> of bytes.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The element type.</typeparam>
|
||||||
|
public ref struct BlitSpan<T> where T : unmanaged
|
||||||
{
|
{
|
||||||
|
private readonly Span<T> _buffer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides a representation of a region of memory as if it were a series of blittable structs
|
/// The number of elements of type <typeparamref name="T"/> in the <see cref="BlitSpan{T}"/>.
|
||||||
/// of type <typeparamref name="T"/>. Also allows viewing the memory as a <see cref="Span{T}"/> of bytes.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The element type.</typeparam>
|
public int Length => _buffer.Length;
|
||||||
public ref struct BlitSpan<T> where T : unmanaged
|
|
||||||
|
/// <summary>
|
||||||
|
/// A reference to the first element in this collection.
|
||||||
|
/// </summary>
|
||||||
|
public ref T Value
|
||||||
{
|
{
|
||||||
private readonly Span<T> _buffer;
|
get
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number of elements of type <typeparamref name="T"/> in the <see cref="BlitSpan{T}"/>.
|
|
||||||
/// </summary>
|
|
||||||
public int Length => _buffer.Length;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A reference to the first element in this collection.
|
|
||||||
/// </summary>
|
|
||||||
public ref T Value
|
|
||||||
{
|
{
|
||||||
get
|
Debug.Assert(_buffer.Length > 0);
|
||||||
{
|
|
||||||
Debug.Assert(_buffer.Length > 0);
|
|
||||||
|
|
||||||
return ref MemoryMarshal.GetReference(_buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
return ref MemoryMarshal.GetReference(_buffer);
|
||||||
/// A reference to the element at index <paramref name="index"/>.
|
|
||||||
/// </summary>
|
|
||||||
public ref T this[int index] => ref _buffer[index];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new <see cref="BlitSpan{T}"/> using the provided <see cref="Span{T}"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data">The span from which to create the <see cref="BlitSpan{T}"/>.
|
|
||||||
/// Must have a length of at least 1.</param>
|
|
||||||
public BlitSpan(Span<T> data)
|
|
||||||
{
|
|
||||||
if (data.Length == 0)
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException();
|
|
||||||
|
|
||||||
_buffer = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new <see cref="BlitSpan{T}"/> using the provided <see cref="Span{T}"/> of bytes.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data">The byte span from which to create the <see cref="BlitSpan{T}"/>.
|
|
||||||
/// Must be long enough to hold at least one struct of type <typeparamref name="T"/></param>
|
|
||||||
public BlitSpan(Span<byte> data)
|
|
||||||
{
|
|
||||||
if (data.Length < Unsafe.SizeOf<T>())
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException();
|
|
||||||
|
|
||||||
_buffer = MemoryMarshal.Cast<byte, T>(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new <see cref="BlitSpan{T}"/> over a struct of type <typeparamref name="T"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data">The struct from which to create the <see cref="BlitSpan{T}"/>.</param>
|
|
||||||
public BlitSpan(ref T data)
|
|
||||||
{
|
|
||||||
_buffer = SpanHelpers.AsSpan(ref data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A <see cref="Span{T}"/> of the elements in the <see cref="BlitSpan{T}"/>.
|
|
||||||
/// </summary>
|
|
||||||
public Span<T> Span => _buffer;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a view of the <see cref="BlitSpan{T}"/> as a <see cref="Span{T}"/> of bytes.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A byte span representation of the <see cref="BlitSpan{T}"/>.</returns>
|
|
||||||
public Span<byte> GetByteSpan()
|
|
||||||
{
|
|
||||||
return MemoryMarshal.Cast<T, byte>(_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a view of the element at index <paramref name="elementIndex"/> as a <see cref="Span{T}"/> of bytes.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="elementIndex">The zero-based index of the element.</param>
|
|
||||||
/// <returns>A byte span representation of the element.</returns>
|
|
||||||
public Span<byte> GetByteSpan(int elementIndex)
|
|
||||||
{
|
|
||||||
return SpanHelpers.AsByteSpan(ref _buffer[elementIndex]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calculates the length of memory in bytes that would be needed to store <paramref name="elementCount"/>
|
|
||||||
/// elements of type <typeparamref name="T"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="elementCount">The number of elements.</param>
|
|
||||||
/// <returns>The number of bytes required.</returns>
|
|
||||||
public static int QueryByteLength(int elementCount)
|
|
||||||
{
|
|
||||||
return Unsafe.SizeOf<T>() * elementCount;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A reference to the element at index <paramref name="index"/>.
|
||||||
|
/// </summary>
|
||||||
|
public ref T this[int index] => ref _buffer[index];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="BlitSpan{T}"/> using the provided <see cref="Span{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The span from which to create the <see cref="BlitSpan{T}"/>.
|
||||||
|
/// Must have a length of at least 1.</param>
|
||||||
|
public BlitSpan(Span<T> data)
|
||||||
|
{
|
||||||
|
if (data.Length == 0)
|
||||||
|
ThrowHelper.ThrowArgumentOutOfRangeException();
|
||||||
|
|
||||||
|
_buffer = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="BlitSpan{T}"/> using the provided <see cref="Span{T}"/> of bytes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The byte span from which to create the <see cref="BlitSpan{T}"/>.
|
||||||
|
/// Must be long enough to hold at least one struct of type <typeparamref name="T"/></param>
|
||||||
|
public BlitSpan(Span<byte> data)
|
||||||
|
{
|
||||||
|
if (data.Length < Unsafe.SizeOf<T>())
|
||||||
|
ThrowHelper.ThrowArgumentOutOfRangeException();
|
||||||
|
|
||||||
|
_buffer = MemoryMarshal.Cast<byte, T>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="BlitSpan{T}"/> over a struct of type <typeparamref name="T"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The struct from which to create the <see cref="BlitSpan{T}"/>.</param>
|
||||||
|
public BlitSpan(ref T data)
|
||||||
|
{
|
||||||
|
_buffer = SpanHelpers.AsSpan(ref data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="Span{T}"/> of the elements in the <see cref="BlitSpan{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
public Span<T> Span => _buffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a view of the <see cref="BlitSpan{T}"/> as a <see cref="Span{T}"/> of bytes.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A byte span representation of the <see cref="BlitSpan{T}"/>.</returns>
|
||||||
|
public Span<byte> GetByteSpan()
|
||||||
|
{
|
||||||
|
return MemoryMarshal.Cast<T, byte>(_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a view of the element at index <paramref name="elementIndex"/> as a <see cref="Span{T}"/> of bytes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="elementIndex">The zero-based index of the element.</param>
|
||||||
|
/// <returns>A byte span representation of the element.</returns>
|
||||||
|
public Span<byte> GetByteSpan(int elementIndex)
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsByteSpan(ref _buffer[elementIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the length of memory in bytes that would be needed to store <paramref name="elementCount"/>
|
||||||
|
/// elements of type <typeparamref name="T"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="elementCount">The number of elements.</param>
|
||||||
|
/// <returns>The number of bytes required.</returns>
|
||||||
|
public static int QueryByteLength(int elementCount)
|
||||||
|
{
|
||||||
|
return Unsafe.SizeOf<T>() * elementCount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,68 +3,67 @@ using System.Diagnostics;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles storing a blittable struct or a series of blittable structs in a byte array.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The element type.</typeparam>
|
||||||
|
public readonly struct BlitStruct<T> where T : unmanaged
|
||||||
{
|
{
|
||||||
|
private readonly byte[] _buffer;
|
||||||
|
|
||||||
|
public int Length => _buffer.Length / Unsafe.SizeOf<T>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles storing a blittable struct or a series of blittable structs in a byte array.
|
/// A reference to the first element in this collection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The element type.</typeparam>
|
public ref T Value
|
||||||
public readonly struct BlitStruct<T> where T : unmanaged
|
|
||||||
{
|
{
|
||||||
private readonly byte[] _buffer;
|
get
|
||||||
|
|
||||||
public int Length => _buffer.Length / Unsafe.SizeOf<T>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A reference to the first element in this collection.
|
|
||||||
/// </summary>
|
|
||||||
public ref T Value
|
|
||||||
{
|
{
|
||||||
get
|
Debug.Assert(_buffer.Length >= Unsafe.SizeOf<T>());
|
||||||
{
|
|
||||||
Debug.Assert(_buffer.Length >= Unsafe.SizeOf<T>());
|
|
||||||
|
|
||||||
return ref Unsafe.As<byte, T>(ref _buffer[0]);
|
return ref Unsafe.As<byte, T>(ref _buffer[0]);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new <see cref="BlitStruct{T}"/> that can hold the specified number
|
|
||||||
/// of elements of type <typeparamref name="T"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="elementCount">The number of elements the <see cref="BlitStruct{T}"/> will be able to store.</param>
|
|
||||||
public BlitStruct(int elementCount)
|
|
||||||
{
|
|
||||||
if (elementCount <= 0)
|
|
||||||
ThrowHelper.ThrowArgumentOutOfRangeException();
|
|
||||||
|
|
||||||
_buffer = new byte[QueryByteLength(elementCount)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a <see cref="Span"/> view of the elements in the current <see cref="BlitStruct{T}"/> as type <typeparamref name="T"/>.
|
|
||||||
/// </summary>
|
|
||||||
public Span<T> Span => MemoryMarshal.Cast<byte, T>(_buffer);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a <see cref="Span"/> view of the elements in the current <see cref="BlitStruct{T}"/> as <see cref="byte"/>s.
|
|
||||||
/// </summary>
|
|
||||||
public Span<byte> ByteSpan => _buffer;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a <see cref="BlitSpan{T}"/> from the current <see cref="BlitStruct{T}"/>.
|
|
||||||
/// </summary>
|
|
||||||
public BlitSpan<T> BlitSpan => new BlitSpan<T>(_buffer);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calculates the length of memory in bytes that would be needed to store <paramref name="elementCount"/>
|
|
||||||
/// elements of type <typeparamref name="T"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="elementCount">The number of elements.</param>
|
|
||||||
/// <returns>The number of bytes required.</returns>
|
|
||||||
public static int QueryByteLength(int elementCount)
|
|
||||||
{
|
|
||||||
return Unsafe.SizeOf<T>() * elementCount;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new <see cref="BlitStruct{T}"/> that can hold the specified number
|
||||||
|
/// of elements of type <typeparamref name="T"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="elementCount">The number of elements the <see cref="BlitStruct{T}"/> will be able to store.</param>
|
||||||
|
public BlitStruct(int elementCount)
|
||||||
|
{
|
||||||
|
if (elementCount <= 0)
|
||||||
|
ThrowHelper.ThrowArgumentOutOfRangeException();
|
||||||
|
|
||||||
|
_buffer = new byte[QueryByteLength(elementCount)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a <see cref="Span"/> view of the elements in the current <see cref="BlitStruct{T}"/> as type <typeparamref name="T"/>.
|
||||||
|
/// </summary>
|
||||||
|
public Span<T> Span => MemoryMarshal.Cast<byte, T>(_buffer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a <see cref="Span"/> view of the elements in the current <see cref="BlitStruct{T}"/> as <see cref="byte"/>s.
|
||||||
|
/// </summary>
|
||||||
|
public Span<byte> ByteSpan => _buffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="BlitSpan{T}"/> from the current <see cref="BlitStruct{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
public BlitSpan<T> BlitSpan => new BlitSpan<T>(_buffer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the length of memory in bytes that would be needed to store <paramref name="elementCount"/>
|
||||||
|
/// elements of type <typeparamref name="T"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="elementCount">The number of elements.</param>
|
||||||
|
/// <returns>The number of bytes required.</returns>
|
||||||
|
public static int QueryByteLength(int elementCount)
|
||||||
|
{
|
||||||
|
return Unsafe.SizeOf<T>() * elementCount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,125 +4,124 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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
|
||||||
{
|
{
|
||||||
/// <summary>
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
|
||||||
/// Represents a buffer of 16 bytes.
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
|
||||||
/// Contains functions that assist with common operations on small buffers.
|
|
||||||
/// </summary>
|
public byte this[int i]
|
||||||
[DebuggerDisplay("{ToString()}")]
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 16)]
|
|
||||||
public struct Buffer16
|
|
||||||
{
|
{
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
|
get => Bytes[i];
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
|
set => Bytes[i] = value;
|
||||||
|
|
||||||
public byte this[int i]
|
|
||||||
{
|
|
||||||
get => Bytes[i];
|
|
||||||
set => Bytes[i] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
|
||||||
|
|
||||||
// Prevent a defensive copy by changing the read-only in reference to a reference with Unsafe.AsRef()
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static implicit operator Span<byte>(in Buffer16 value)
|
|
||||||
{
|
|
||||||
return SpanHelpers.AsByteSpan(ref Unsafe.AsRef(in value));
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static implicit operator ReadOnlySpan<byte>(in Buffer16 value)
|
|
||||||
{
|
|
||||||
return SpanHelpers.AsReadOnlyByteSpan(in value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ref T As<T>() where T : unmanaged
|
|
||||||
{
|
|
||||||
if (Unsafe.SizeOf<T>() > (uint)Unsafe.SizeOf<Buffer16>())
|
|
||||||
{
|
|
||||||
throw new ArgumentException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ref MemoryMarshal.GetReference(AsSpan<T>());
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public Span<T> AsSpan<T>() where T : unmanaged
|
|
||||||
{
|
|
||||||
return SpanHelpers.AsSpan<Buffer16, T>(ref this);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly ReadOnlySpan<T> AsReadOnlySpan<T>() where T : unmanaged
|
|
||||||
{
|
|
||||||
return SpanHelpers.AsReadOnlySpan<Buffer16, T>(in this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Bytes.ToHexString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[DebuggerDisplay("{ToString()}")]
|
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 32)]
|
|
||||||
public struct Buffer32
|
// Prevent a defensive copy by changing the read-only in reference to a reference with Unsafe.AsRef()
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static implicit operator Span<byte>(in Buffer16 value)
|
||||||
{
|
{
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
|
return SpanHelpers.AsByteSpan(ref Unsafe.AsRef(in value));
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
|
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy2;
|
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy3;
|
|
||||||
|
|
||||||
public byte this[int i]
|
|
||||||
{
|
|
||||||
get => Bytes[i];
|
|
||||||
set => Bytes[i] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
|
||||||
|
|
||||||
// Prevent a defensive copy by changing the read-only in reference to a reference with Unsafe.AsRef()
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static implicit operator Span<byte>(in Buffer32 value)
|
|
||||||
{
|
|
||||||
return SpanHelpers.AsByteSpan(ref Unsafe.AsRef(in value));
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static implicit operator ReadOnlySpan<byte>(in Buffer32 value)
|
|
||||||
{
|
|
||||||
return SpanHelpers.AsReadOnlyByteSpan(in value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ref T As<T>() where T : unmanaged
|
|
||||||
{
|
|
||||||
if (Unsafe.SizeOf<T>() > (uint)Unsafe.SizeOf<Buffer32>())
|
|
||||||
{
|
|
||||||
throw new ArgumentException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ref MemoryMarshal.GetReference(AsSpan<T>());
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public Span<T> AsSpan<T>() where T : unmanaged
|
|
||||||
{
|
|
||||||
return SpanHelpers.AsSpan<Buffer32, T>(ref this);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public readonly ReadOnlySpan<T> AsReadOnlySpan<T>() where T : unmanaged
|
|
||||||
{
|
|
||||||
return SpanHelpers.AsReadOnlySpan<Buffer32, T>(in this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return Bytes.ToHexString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static implicit operator ReadOnlySpan<byte>(in Buffer16 value)
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsReadOnlyByteSpan(in value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ref T As<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
if (Unsafe.SizeOf<T>() > (uint)Unsafe.SizeOf<Buffer16>())
|
||||||
|
{
|
||||||
|
throw new ArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ref MemoryMarshal.GetReference(AsSpan<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Span<T> AsSpan<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsSpan<Buffer16, T>(ref this);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly ReadOnlySpan<T> AsReadOnlySpan<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsReadOnlySpan<Buffer16, T>(in this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Bytes.ToHexString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerDisplay("{ToString()}")]
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 32)]
|
||||||
|
public struct Buffer32
|
||||||
|
{
|
||||||
|
[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 byte this[int i]
|
||||||
|
{
|
||||||
|
get => Bytes[i];
|
||||||
|
set => Bytes[i] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||||
|
|
||||||
|
// Prevent a defensive copy by changing the read-only in reference to a reference with Unsafe.AsRef()
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static implicit operator Span<byte>(in Buffer32 value)
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsByteSpan(ref Unsafe.AsRef(in value));
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static implicit operator ReadOnlySpan<byte>(in Buffer32 value)
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsReadOnlyByteSpan(in value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ref T As<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
if (Unsafe.SizeOf<T>() > (uint)Unsafe.SizeOf<Buffer32>())
|
||||||
|
{
|
||||||
|
throw new ArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ref MemoryMarshal.GetReference(AsSpan<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Span<T> AsSpan<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsSpan<Buffer32, T>(ref this);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly ReadOnlySpan<T> AsReadOnlySpan<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsReadOnlySpan<Buffer32, T>(in this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Bytes.ToHexString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,21 +2,20 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common.FixedArrays
|
namespace LibHac.Common.FixedArrays;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Array1<T>
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public const int Length = 1;
|
||||||
public struct Array1<T>
|
|
||||||
{
|
|
||||||
public const int Length = 1;
|
|
||||||
|
|
||||||
private T _1;
|
private T _1;
|
||||||
|
|
||||||
public ref T this[int i] => ref Items[i];
|
public ref T this[int i] => ref Items[i];
|
||||||
|
|
||||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static implicit operator ReadOnlySpan<T>(in Array1<T> value) => value.ItemsRo;
|
public static implicit operator ReadOnlySpan<T>(in Array1<T> value) => value.ItemsRo;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -2,32 +2,31 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common.FixedArrays
|
namespace LibHac.Common.FixedArrays;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Array12<T>
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public const int Length = 12;
|
||||||
public struct Array12<T>
|
|
||||||
{
|
|
||||||
public const int Length = 12;
|
|
||||||
|
|
||||||
private T _1;
|
private T _1;
|
||||||
private T _2;
|
private T _2;
|
||||||
private T _3;
|
private T _3;
|
||||||
private T _4;
|
private T _4;
|
||||||
private T _5;
|
private T _5;
|
||||||
private T _6;
|
private T _6;
|
||||||
private T _7;
|
private T _7;
|
||||||
private T _8;
|
private T _8;
|
||||||
private T _9;
|
private T _9;
|
||||||
private T _10;
|
private T _10;
|
||||||
private T _11;
|
private T _11;
|
||||||
private T _12;
|
private T _12;
|
||||||
|
|
||||||
public ref T this[int i] => ref Items[i];
|
public ref T this[int i] => ref Items[i];
|
||||||
|
|
||||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static implicit operator ReadOnlySpan<T>(in Array12<T> value) => value.ItemsRo;
|
public static implicit operator ReadOnlySpan<T>(in Array12<T> value) => value.ItemsRo;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -2,31 +2,30 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common.FixedArrays
|
namespace LibHac.Common.FixedArrays;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Array128<T>
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public const int Length = 128;
|
||||||
public struct Array128<T>
|
|
||||||
|
private Array64<T> _0;
|
||||||
|
private Array64<T> _64;
|
||||||
|
|
||||||
|
public ref T this[int i] => ref Items[i];
|
||||||
|
|
||||||
|
public Span<T> Items
|
||||||
{
|
{
|
||||||
public const int Length = 128;
|
|
||||||
|
|
||||||
private Array64<T> _0;
|
|
||||||
private Array64<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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static implicit operator ReadOnlySpan<T>(in Array128<T> value) => value.ItemsRo;
|
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 Array128<T> value) => value.ItemsRo;
|
||||||
|
}
|
||||||
|
@ -2,31 +2,30 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common.FixedArrays
|
namespace LibHac.Common.FixedArrays;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Array16<T>
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public const int Length = 16;
|
||||||
public struct Array16<T>
|
|
||||||
|
private Array8<T> _0;
|
||||||
|
private Array8<T> _8;
|
||||||
|
|
||||||
|
public ref T this[int i] => ref Items[i];
|
||||||
|
|
||||||
|
public Span<T> Items
|
||||||
{
|
{
|
||||||
public const int Length = 16;
|
|
||||||
|
|
||||||
private Array8<T> _0;
|
|
||||||
private Array8<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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static implicit operator ReadOnlySpan<T>(in Array16<T> value) => value.ItemsRo;
|
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 Array16<T> value) => value.ItemsRo;
|
||||||
|
}
|
||||||
|
@ -2,22 +2,21 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common.FixedArrays
|
namespace LibHac.Common.FixedArrays;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Array2<T>
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public const int Length = 2;
|
||||||
public struct Array2<T>
|
|
||||||
{
|
|
||||||
public const int Length = 2;
|
|
||||||
|
|
||||||
private T _1;
|
private T _1;
|
||||||
private T _2;
|
private T _2;
|
||||||
|
|
||||||
public ref T this[int i] => ref Items[i];
|
public ref T this[int i] => ref Items[i];
|
||||||
|
|
||||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static implicit operator ReadOnlySpan<T>(in Array2<T> value) => value.ItemsRo;
|
public static implicit operator ReadOnlySpan<T>(in Array2<T> value) => value.ItemsRo;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -2,31 +2,30 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common.FixedArrays
|
namespace LibHac.Common.FixedArrays;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Array256<T>
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public const int Length = 256;
|
||||||
public struct Array256<T>
|
|
||||||
|
private Array128<T> _0;
|
||||||
|
private Array128<T> _128;
|
||||||
|
|
||||||
|
public ref T this[int i] => ref Items[i];
|
||||||
|
|
||||||
|
public Span<T> Items
|
||||||
{
|
{
|
||||||
public const int Length = 256;
|
|
||||||
|
|
||||||
private Array128<T> _0;
|
|
||||||
private Array128<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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static implicit operator ReadOnlySpan<T>(in Array256<T> value) => value.ItemsRo;
|
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 Array256<T> value) => value.ItemsRo;
|
||||||
|
}
|
||||||
|
@ -2,23 +2,22 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common.FixedArrays
|
namespace LibHac.Common.FixedArrays;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Array3<T>
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public const int Length = 3;
|
||||||
public struct Array3<T>
|
|
||||||
{
|
|
||||||
public const int Length = 3;
|
|
||||||
|
|
||||||
private T _1;
|
private T _1;
|
||||||
private T _2;
|
private T _2;
|
||||||
private T _3;
|
private T _3;
|
||||||
|
|
||||||
public ref T this[int i] => ref Items[i];
|
public ref T this[int i] => ref Items[i];
|
||||||
|
|
||||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static implicit operator ReadOnlySpan<T>(in Array3<T> value) => value.ItemsRo;
|
public static implicit operator ReadOnlySpan<T>(in Array3<T> value) => value.ItemsRo;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -2,31 +2,30 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common.FixedArrays
|
namespace LibHac.Common.FixedArrays;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Array32<T>
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public const int Length = 32;
|
||||||
public struct Array32<T>
|
|
||||||
|
private Array16<T> _0;
|
||||||
|
private Array16<T> _16;
|
||||||
|
|
||||||
|
public ref T this[int i] => ref Items[i];
|
||||||
|
|
||||||
|
public Span<T> Items
|
||||||
{
|
{
|
||||||
public const int Length = 32;
|
|
||||||
|
|
||||||
private Array16<T> _0;
|
|
||||||
private Array16<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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static implicit operator ReadOnlySpan<T>(in Array32<T> value) => value.ItemsRo;
|
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 Array32<T> value) => value.ItemsRo;
|
||||||
|
}
|
||||||
|
@ -2,24 +2,23 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common.FixedArrays
|
namespace LibHac.Common.FixedArrays;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Array4<T>
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public const int Length = 4;
|
||||||
public struct Array4<T>
|
|
||||||
{
|
|
||||||
public const int Length = 4;
|
|
||||||
|
|
||||||
private T _1;
|
private T _1;
|
||||||
private T _2;
|
private T _2;
|
||||||
private T _3;
|
private T _3;
|
||||||
private T _4;
|
private T _4;
|
||||||
|
|
||||||
public ref T this[int i] => ref Items[i];
|
public ref T this[int i] => ref Items[i];
|
||||||
|
|
||||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static implicit operator ReadOnlySpan<T>(in Array4<T> value) => value.ItemsRo;
|
public static implicit operator ReadOnlySpan<T>(in Array4<T> value) => value.ItemsRo;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -2,25 +2,24 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common.FixedArrays
|
namespace LibHac.Common.FixedArrays;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Array5<T>
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public const int Length = 5;
|
||||||
public struct Array5<T>
|
|
||||||
{
|
|
||||||
public const int Length = 5;
|
|
||||||
|
|
||||||
private T _1;
|
private T _1;
|
||||||
private T _2;
|
private T _2;
|
||||||
private T _3;
|
private T _3;
|
||||||
private T _4;
|
private T _4;
|
||||||
private T _5;
|
private T _5;
|
||||||
|
|
||||||
public ref T this[int i] => ref Items[i];
|
public ref T this[int i] => ref Items[i];
|
||||||
|
|
||||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static implicit operator ReadOnlySpan<T>(in Array5<T> value) => value.ItemsRo;
|
public static implicit operator ReadOnlySpan<T>(in Array5<T> value) => value.ItemsRo;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -2,31 +2,30 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common.FixedArrays
|
namespace LibHac.Common.FixedArrays;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Array64<T>
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public const int Length = 64;
|
||||||
public struct Array64<T>
|
|
||||||
|
private Array32<T> _0;
|
||||||
|
private Array32<T> _32;
|
||||||
|
|
||||||
|
public ref T this[int i] => ref Items[i];
|
||||||
|
|
||||||
|
public Span<T> Items
|
||||||
{
|
{
|
||||||
public const int Length = 64;
|
|
||||||
|
|
||||||
private Array32<T> _0;
|
|
||||||
private Array32<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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static implicit operator ReadOnlySpan<T>(in Array64<T> value) => value.ItemsRo;
|
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 Array64<T> value) => value.ItemsRo;
|
||||||
|
}
|
||||||
|
@ -2,28 +2,27 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common.FixedArrays
|
namespace LibHac.Common.FixedArrays;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Array8<T>
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public const int Length = 8;
|
||||||
public struct Array8<T>
|
|
||||||
{
|
|
||||||
public const int Length = 8;
|
|
||||||
|
|
||||||
private T _1;
|
private T _1;
|
||||||
private T _2;
|
private T _2;
|
||||||
private T _3;
|
private T _3;
|
||||||
private T _4;
|
private T _4;
|
||||||
private T _5;
|
private T _5;
|
||||||
private T _6;
|
private T _6;
|
||||||
private T _7;
|
private T _7;
|
||||||
private T _8;
|
private T _8;
|
||||||
|
|
||||||
public ref T this[int i] => ref Items[i];
|
public ref T this[int i] => ref Items[i];
|
||||||
|
|
||||||
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
|
||||||
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static implicit operator ReadOnlySpan<T>(in Array8<T> value) => value.ItemsRo;
|
public static implicit operator ReadOnlySpan<T>(in Array8<T> value) => value.ItemsRo;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -1,41 +1,40 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
{
|
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
|
||||||
internal static class HResult
|
|
||||||
{
|
|
||||||
public const int ERROR_FILE_NOT_FOUND = unchecked((int)0x80070002);
|
|
||||||
public const int ERROR_PATH_NOT_FOUND = unchecked((int)0x80070003);
|
|
||||||
public const int ERROR_ACCESS_DENIED = unchecked((int)0x80070005);
|
|
||||||
public const int ERROR_SHARING_VIOLATION = unchecked((int)0x80070020);
|
|
||||||
public const int ERROR_HANDLE_EOF = unchecked((int)0x80070026);
|
|
||||||
public const int ERROR_HANDLE_DISK_FULL = unchecked((int)0x80070027);
|
|
||||||
public const int ERROR_FILE_EXISTS = unchecked((int)0x80070050);
|
|
||||||
public const int ERROR_DISK_FULL = unchecked((int)0x80070070);
|
|
||||||
public const int ERROR_INVALID_NAME = unchecked((int)0x8007007B);
|
|
||||||
public const int ERROR_DIR_NOT_EMPTY = unchecked((int)0x80070091);
|
|
||||||
public const int ERROR_ALREADY_EXISTS = unchecked((int)0x800700B7);
|
|
||||||
public const int ERROR_DIRECTORY = unchecked((int)0x8007010B);
|
|
||||||
public const int ERROR_SPACES_NOT_ENOUGH_DRIVES = unchecked((int)0x80E7000B);
|
|
||||||
|
|
||||||
public static Result HResultToHorizonResult(int hResult) => hResult switch
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
{
|
internal static class HResult
|
||||||
ERROR_FILE_NOT_FOUND => ResultFs.PathNotFound.Value,
|
{
|
||||||
ERROR_PATH_NOT_FOUND => ResultFs.PathNotFound.Value,
|
public const int ERROR_FILE_NOT_FOUND = unchecked((int)0x80070002);
|
||||||
ERROR_ACCESS_DENIED => ResultFs.TargetLocked.Value,
|
public const int ERROR_PATH_NOT_FOUND = unchecked((int)0x80070003);
|
||||||
ERROR_SHARING_VIOLATION => ResultFs.TargetLocked.Value,
|
public const int ERROR_ACCESS_DENIED = unchecked((int)0x80070005);
|
||||||
ERROR_HANDLE_EOF => ResultFs.OutOfRange.Value,
|
public const int ERROR_SHARING_VIOLATION = unchecked((int)0x80070020);
|
||||||
ERROR_HANDLE_DISK_FULL => ResultFs.UsableSpaceNotEnough.Value,
|
public const int ERROR_HANDLE_EOF = unchecked((int)0x80070026);
|
||||||
ERROR_FILE_EXISTS => ResultFs.PathAlreadyExists.Value,
|
public const int ERROR_HANDLE_DISK_FULL = unchecked((int)0x80070027);
|
||||||
ERROR_DISK_FULL => ResultFs.UsableSpaceNotEnough.Value,
|
public const int ERROR_FILE_EXISTS = unchecked((int)0x80070050);
|
||||||
ERROR_INVALID_NAME => ResultFs.PathNotFound.Value,
|
public const int ERROR_DISK_FULL = unchecked((int)0x80070070);
|
||||||
ERROR_DIR_NOT_EMPTY => ResultFs.DirectoryNotEmpty.Value,
|
public const int ERROR_INVALID_NAME = unchecked((int)0x8007007B);
|
||||||
ERROR_ALREADY_EXISTS => ResultFs.PathAlreadyExists.Value,
|
public const int ERROR_DIR_NOT_EMPTY = unchecked((int)0x80070091);
|
||||||
ERROR_DIRECTORY => ResultFs.PathNotFound.Value,
|
public const int ERROR_ALREADY_EXISTS = unchecked((int)0x800700B7);
|
||||||
ERROR_SPACES_NOT_ENOUGH_DRIVES => ResultFs.UsableSpaceNotEnough.Value,
|
public const int ERROR_DIRECTORY = unchecked((int)0x8007010B);
|
||||||
_ => ResultFs.UnexpectedInLocalFileSystemE.Value
|
public const int ERROR_SPACES_NOT_ENOUGH_DRIVES = unchecked((int)0x80E7000B);
|
||||||
};
|
|
||||||
}
|
public static Result HResultToHorizonResult(int hResult) => hResult switch
|
||||||
|
{
|
||||||
|
ERROR_FILE_NOT_FOUND => ResultFs.PathNotFound.Value,
|
||||||
|
ERROR_PATH_NOT_FOUND => ResultFs.PathNotFound.Value,
|
||||||
|
ERROR_ACCESS_DENIED => ResultFs.TargetLocked.Value,
|
||||||
|
ERROR_SHARING_VIOLATION => ResultFs.TargetLocked.Value,
|
||||||
|
ERROR_HANDLE_EOF => ResultFs.OutOfRange.Value,
|
||||||
|
ERROR_HANDLE_DISK_FULL => ResultFs.UsableSpaceNotEnough.Value,
|
||||||
|
ERROR_FILE_EXISTS => ResultFs.PathAlreadyExists.Value,
|
||||||
|
ERROR_DISK_FULL => ResultFs.UsableSpaceNotEnough.Value,
|
||||||
|
ERROR_INVALID_NAME => ResultFs.PathNotFound.Value,
|
||||||
|
ERROR_DIR_NOT_EMPTY => ResultFs.DirectoryNotEmpty.Value,
|
||||||
|
ERROR_ALREADY_EXISTS => ResultFs.PathAlreadyExists.Value,
|
||||||
|
ERROR_DIRECTORY => ResultFs.PathNotFound.Value,
|
||||||
|
ERROR_SPACES_NOT_ENOUGH_DRIVES => ResultFs.UsableSpaceNotEnough.Value,
|
||||||
|
_ => ResultFs.UnexpectedInLocalFileSystemE.Value
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -3,83 +3,82 @@ using System.Diagnostics;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Common
|
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
|
||||||
{
|
{
|
||||||
/// <summary>
|
public readonly ulong High;
|
||||||
/// A generic 128-bit ID value.
|
public readonly ulong Low;
|
||||||
/// </summary>
|
|
||||||
[DebuggerDisplay("{ToString()}")]
|
public static Id128 Zero => default;
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
|
||||||
public struct Id128 : IEquatable<Id128>, IComparable<Id128>, IComparable
|
public Id128(ulong high, ulong low)
|
||||||
{
|
{
|
||||||
public readonly ulong High;
|
High = high;
|
||||||
public readonly ulong Low;
|
Low = low;
|
||||||
|
|
||||||
public static Id128 Zero => default;
|
|
||||||
|
|
||||||
public Id128(ulong high, ulong low)
|
|
||||||
{
|
|
||||||
High = high;
|
|
||||||
Low = low;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Id128(ReadOnlySpan<byte> uid)
|
|
||||||
{
|
|
||||||
ReadOnlySpan<ulong> longs = MemoryMarshal.Cast<byte, ulong>(uid);
|
|
||||||
|
|
||||||
High = longs[0];
|
|
||||||
Low = longs[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() => AsBytes().ToHexString();
|
|
||||||
|
|
||||||
public bool Equals(Id128 other)
|
|
||||||
{
|
|
||||||
return High == other.High && Low == other.Low;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
return obj is Id128 other && Equals(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return HashCode.Combine(High, Low);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int CompareTo(Id128 other)
|
|
||||||
{
|
|
||||||
int highComparison = High.CompareTo(other.High);
|
|
||||||
if (highComparison != 0) return highComparison;
|
|
||||||
return Low.CompareTo(other.Low);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int CompareTo(object obj)
|
|
||||||
{
|
|
||||||
if (obj is null) return 1;
|
|
||||||
return obj is Id128 other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(Id128)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly void ToBytes(Span<byte> output)
|
|
||||||
{
|
|
||||||
Span<ulong> longs = MemoryMarshal.Cast<byte, ulong>(output);
|
|
||||||
|
|
||||||
longs[0] = High;
|
|
||||||
longs[1] = Low;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlySpan<byte> AsBytes()
|
|
||||||
{
|
|
||||||
return SpanHelpers.AsByteSpan(ref this);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) => 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 Id128(ReadOnlySpan<byte> uid)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<ulong> longs = MemoryMarshal.Cast<byte, ulong>(uid);
|
||||||
|
|
||||||
|
High = longs[0];
|
||||||
|
Low = longs[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() => AsBytes().ToHexString();
|
||||||
|
|
||||||
|
public bool Equals(Id128 other)
|
||||||
|
{
|
||||||
|
return High == other.High && Low == other.Low;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is Id128 other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(High, Low);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CompareTo(Id128 other)
|
||||||
|
{
|
||||||
|
int highComparison = High.CompareTo(other.High);
|
||||||
|
if (highComparison != 0) return highComparison;
|
||||||
|
return Low.CompareTo(other.Low);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CompareTo(object obj)
|
||||||
|
{
|
||||||
|
if (obj is null) return 1;
|
||||||
|
return obj is Id128 other ? CompareTo(other) : throw new ArgumentException($"Object must be of type {nameof(Id128)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly void ToBytes(Span<byte> output)
|
||||||
|
{
|
||||||
|
Span<ulong> longs = MemoryMarshal.Cast<byte, ulong>(output);
|
||||||
|
|
||||||
|
longs[0] = High;
|
||||||
|
longs[1] = Low;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlySpan<byte> AsBytes()
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsByteSpan(ref this);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) => 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;
|
||||||
}
|
}
|
||||||
|
@ -2,94 +2,93 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
internal readonly ref struct InitializationGuard
|
||||||
{
|
{
|
||||||
internal readonly ref struct InitializationGuard
|
private readonly Ref<nint> _guard;
|
||||||
|
private readonly object _mutex;
|
||||||
|
public bool IsInitialized => _mutex == null;
|
||||||
|
|
||||||
|
private const byte GuardBitComplete = 1 << 0;
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
private enum InitStatus : byte
|
||||||
{
|
{
|
||||||
private readonly Ref<nint> _guard;
|
Complete = 1 << 0,
|
||||||
private readonly object _mutex;
|
Pending = 1 << 1,
|
||||||
public bool IsInitialized => _mutex == null;
|
Waiting = 1 << 2
|
||||||
|
}
|
||||||
|
|
||||||
private const byte GuardBitComplete = 1 << 0;
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public InitializationGuard(ref nint guard, object mutex)
|
||||||
[Flags]
|
{
|
||||||
private enum InitStatus : byte
|
if (IsGuardInitialized(guard) || !AcquireGuard(ref guard, mutex))
|
||||||
{
|
{
|
||||||
Complete = 1 << 0,
|
this = default;
|
||||||
Pending = 1 << 1,
|
return;
|
||||||
Waiting = 1 << 2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
_guard = new Ref<nint>(ref guard);
|
||||||
public InitializationGuard(ref nint guard, object mutex)
|
_mutex = mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!IsInitialized)
|
||||||
{
|
{
|
||||||
if (IsGuardInitialized(guard) || !AcquireGuard(ref guard, mutex))
|
ReleaseGuard(ref _guard.Value, _mutex);
|
||||||
{
|
|
||||||
this = default;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_guard = new Ref<nint>(ref guard);
|
|
||||||
_mutex = mutex;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (!IsInitialized)
|
|
||||||
{
|
|
||||||
ReleaseGuard(ref _guard.Value, _mutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool AcquireGuard(ref nint guard, object mutex)
|
|
||||||
{
|
|
||||||
if (SpanHelpers.AsByteSpan(ref guard)[0] == GuardBitComplete)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return AcquireInitByte(ref Unsafe.As<byte, InitStatus>(ref SpanHelpers.AsByteSpan(ref guard)[1]), mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ReleaseGuard(ref nint guard, object mutex)
|
|
||||||
{
|
|
||||||
SpanHelpers.AsByteSpan(ref guard)[0] = GuardBitComplete;
|
|
||||||
|
|
||||||
ReleaseInitByte(ref Unsafe.As<byte, InitStatus>(ref SpanHelpers.AsByteSpan(ref guard)[1]), mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool AcquireInitByte(ref InitStatus initByte, object mutex)
|
|
||||||
{
|
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
while (initByte.HasFlag(InitStatus.Pending))
|
|
||||||
{
|
|
||||||
initByte |= InitStatus.Waiting;
|
|
||||||
Monitor.Wait(mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (initByte == InitStatus.Complete)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
initByte = InitStatus.Pending;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ReleaseInitByte(ref InitStatus initByte, object mutex)
|
|
||||||
{
|
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
bool hasWaiting = initByte.HasFlag(InitStatus.Waiting);
|
|
||||||
initByte = InitStatus.Complete;
|
|
||||||
|
|
||||||
if (hasWaiting)
|
|
||||||
Monitor.PulseAll(mutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsGuardInitialized(nint guard)
|
|
||||||
{
|
|
||||||
return (guard & 1) != 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool AcquireGuard(ref nint guard, object mutex)
|
||||||
|
{
|
||||||
|
if (SpanHelpers.AsByteSpan(ref guard)[0] == GuardBitComplete)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return AcquireInitByte(ref Unsafe.As<byte, InitStatus>(ref SpanHelpers.AsByteSpan(ref guard)[1]), mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ReleaseGuard(ref nint guard, object mutex)
|
||||||
|
{
|
||||||
|
SpanHelpers.AsByteSpan(ref guard)[0] = GuardBitComplete;
|
||||||
|
|
||||||
|
ReleaseInitByte(ref Unsafe.As<byte, InitStatus>(ref SpanHelpers.AsByteSpan(ref guard)[1]), mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool AcquireInitByte(ref InitStatus initByte, object mutex)
|
||||||
|
{
|
||||||
|
lock (mutex)
|
||||||
|
{
|
||||||
|
while (initByte.HasFlag(InitStatus.Pending))
|
||||||
|
{
|
||||||
|
initByte |= InitStatus.Waiting;
|
||||||
|
Monitor.Wait(mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (initByte == InitStatus.Complete)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
initByte = InitStatus.Pending;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReleaseInitByte(ref InitStatus initByte, object mutex)
|
||||||
|
{
|
||||||
|
lock (mutex)
|
||||||
|
{
|
||||||
|
bool hasWaiting = initByte.HasFlag(InitStatus.Waiting);
|
||||||
|
initByte = InitStatus.Complete;
|
||||||
|
|
||||||
|
if (hasWaiting)
|
||||||
|
Monitor.PulseAll(mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsGuardInitialized(nint guard)
|
||||||
|
{
|
||||||
|
return (guard & 1) != 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,61 +4,60 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
#pragma warning disable 649
|
#pragma warning disable 649
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
public static unsafe class InteropWin32
|
||||||
{
|
{
|
||||||
public static unsafe class InteropWin32
|
[DllImport("kernel32.dll")]
|
||||||
|
private static extern int MultiByteToWideChar(uint codePage, uint dwFlags, byte* lpMultiByteStr,
|
||||||
|
int cbMultiByte, char* lpWideCharStr, int cchWideChar);
|
||||||
|
|
||||||
|
public static int MultiByteToWideChar(int codePage, ReadOnlySpan<byte> bytes, Span<char> chars)
|
||||||
{
|
{
|
||||||
[DllImport("kernel32.dll")]
|
fixed (byte* pBytes = bytes)
|
||||||
private static extern int MultiByteToWideChar(uint codePage, uint dwFlags, byte* lpMultiByteStr,
|
fixed (char* pChars = chars)
|
||||||
int cbMultiByte, char* lpWideCharStr, int cchWideChar);
|
|
||||||
|
|
||||||
public static int MultiByteToWideChar(int codePage, ReadOnlySpan<byte> bytes, Span<char> chars)
|
|
||||||
{
|
{
|
||||||
fixed (byte* pBytes = bytes)
|
return MultiByteToWideChar((uint)codePage, 0, pBytes, bytes.Length, pChars, chars.Length);
|
||||||
fixed (char* pChars = chars)
|
|
||||||
{
|
|
||||||
return MultiByteToWideChar((uint)codePage, 0, pBytes, bytes.Length, pChars, chars.Length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll")]
|
|
||||||
public static extern bool FindClose(IntPtr handle);
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll")]
|
|
||||||
public static extern IntPtr FindFirstFileW(char* lpFileName, Win32FindData* lpFindFileData);
|
|
||||||
|
|
||||||
public static IntPtr FindFirstFileW(ReadOnlySpan<char> fileName, out Win32FindData findFileData)
|
|
||||||
{
|
|
||||||
fixed (char* pfileName = fileName)
|
|
||||||
{
|
|
||||||
Unsafe.SkipInit(out findFileData);
|
|
||||||
return FindFirstFileW(pfileName, (Win32FindData*)Unsafe.AsPointer(ref findFileData));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Win32FindData
|
|
||||||
{
|
|
||||||
public uint FileAttributes;
|
|
||||||
private uint _creationTimeLow;
|
|
||||||
private uint _creationTimeHigh;
|
|
||||||
private uint _lastAccessLow;
|
|
||||||
private uint _lastAccessHigh;
|
|
||||||
private uint _lastWriteLow;
|
|
||||||
private uint _lastWriteHigh;
|
|
||||||
private uint _fileSizeHigh;
|
|
||||||
private uint _fileSizeLow;
|
|
||||||
public uint Reserved0;
|
|
||||||
public uint Reserved1;
|
|
||||||
private fixed char _fileName[260];
|
|
||||||
private fixed char _alternateFileName[14];
|
|
||||||
|
|
||||||
public long CreationTime => (long)((ulong)_creationTimeHigh << 32 | _creationTimeLow);
|
|
||||||
public long LastAccessTime => (long)((ulong)_lastAccessHigh << 32 | _lastAccessLow);
|
|
||||||
public long LastWriteTime => (long)((ulong)_lastWriteHigh << 32 | _lastWriteLow);
|
|
||||||
public long FileSize => (long)_fileSizeHigh << 32 | _fileSizeLow;
|
|
||||||
|
|
||||||
public Span<char> FileName => MemoryMarshal.CreateSpan(ref _fileName[0], 260);
|
|
||||||
public Span<char> AlternateFileName => MemoryMarshal.CreateSpan(ref _alternateFileName[0], 14);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
public static extern bool FindClose(IntPtr handle);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
public static extern IntPtr FindFirstFileW(char* lpFileName, Win32FindData* lpFindFileData);
|
||||||
|
|
||||||
|
public static IntPtr FindFirstFileW(ReadOnlySpan<char> fileName, out Win32FindData findFileData)
|
||||||
|
{
|
||||||
|
fixed (char* pfileName = fileName)
|
||||||
|
{
|
||||||
|
Unsafe.SkipInit(out findFileData);
|
||||||
|
return FindFirstFileW(pfileName, (Win32FindData*)Unsafe.AsPointer(ref findFileData));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Win32FindData
|
||||||
|
{
|
||||||
|
public uint FileAttributes;
|
||||||
|
private uint _creationTimeLow;
|
||||||
|
private uint _creationTimeHigh;
|
||||||
|
private uint _lastAccessLow;
|
||||||
|
private uint _lastAccessHigh;
|
||||||
|
private uint _lastWriteLow;
|
||||||
|
private uint _lastWriteHigh;
|
||||||
|
private uint _fileSizeHigh;
|
||||||
|
private uint _fileSizeLow;
|
||||||
|
public uint Reserved0;
|
||||||
|
public uint Reserved1;
|
||||||
|
private fixed char _fileName[260];
|
||||||
|
private fixed char _alternateFileName[14];
|
||||||
|
|
||||||
|
public long CreationTime => (long)((ulong)_creationTimeHigh << 32 | _creationTimeLow);
|
||||||
|
public long LastAccessTime => (long)((ulong)_lastAccessHigh << 32 | _lastAccessLow);
|
||||||
|
public long LastWriteTime => (long)((ulong)_lastWriteHigh << 32 | _lastWriteLow);
|
||||||
|
public long FileSize => (long)_fileSizeHigh << 32 | _fileSizeLow;
|
||||||
|
|
||||||
|
public Span<char> FileName => MemoryMarshal.CreateSpan(ref _fileName[0], 260);
|
||||||
|
public Span<char> AlternateFileName => MemoryMarshal.CreateSpan(ref _alternateFileName[0], 14);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,44 +3,43 @@ using System.Diagnostics;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
[DebuggerDisplay("{ToString()}")]
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||||
|
public struct Key128 : IEquatable<Key128>
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{ToString()}")]
|
private readonly ulong _dummy1;
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
private readonly ulong _dummy2;
|
||||||
public struct Key128 : IEquatable<Key128>
|
|
||||||
|
public Span<byte> Value => SpanHelpers.AsByteSpan(ref this);
|
||||||
|
|
||||||
|
public Key128(ReadOnlySpan<byte> bytes)
|
||||||
{
|
{
|
||||||
private readonly ulong _dummy1;
|
ReadOnlySpan<ulong> longs = MemoryMarshal.Cast<byte, ulong>(bytes);
|
||||||
private readonly ulong _dummy2;
|
|
||||||
|
|
||||||
public Span<byte> Value => SpanHelpers.AsByteSpan(ref this);
|
_dummy1 = longs[0];
|
||||||
|
_dummy2 = longs[1];
|
||||||
public Key128(ReadOnlySpan<byte> bytes)
|
|
||||||
{
|
|
||||||
ReadOnlySpan<ulong> longs = MemoryMarshal.Cast<byte, ulong>(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()
|
|
||||||
{
|
|
||||||
return HashCode.Combine(_dummy1, _dummy2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(Key128 left, Key128 right) => left.Equals(right);
|
|
||||||
public static bool operator !=(Key128 left, Key128 right) => !(left == right);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(_dummy1, _dummy2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(Key128 left, Key128 right) => left.Equals(right);
|
||||||
|
public static bool operator !=(Key128 left, Key128 right) => !(left == right);
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace LibHac.Common.Keys
|
namespace LibHac.Common.Keys;
|
||||||
|
|
||||||
|
internal static partial class DefaultKeySet
|
||||||
{
|
{
|
||||||
internal static partial class DefaultKeySet
|
private static ReadOnlySpan<byte> RootKeysDev => new byte[] { };
|
||||||
{
|
private static ReadOnlySpan<byte> RootKeysProd => new byte[] { };
|
||||||
private static ReadOnlySpan<byte> RootKeysDev => new byte[] { };
|
private static ReadOnlySpan<byte> KeySeeds => new byte[] { };
|
||||||
private static ReadOnlySpan<byte> RootKeysProd => new byte[] { };
|
private static ReadOnlySpan<byte> StoredKeysDev => new byte[] { };
|
||||||
private static ReadOnlySpan<byte> KeySeeds => new byte[] { };
|
private static ReadOnlySpan<byte> StoredKeysProd => new byte[] { };
|
||||||
private static ReadOnlySpan<byte> StoredKeysDev => new byte[] { };
|
private static ReadOnlySpan<byte> DerivedKeysDev => new byte[] { };
|
||||||
private static ReadOnlySpan<byte> StoredKeysProd => new byte[] { };
|
private static ReadOnlySpan<byte> DerivedKeysProd => new byte[] { };
|
||||||
private static ReadOnlySpan<byte> DerivedKeysDev => new byte[] { };
|
private static ReadOnlySpan<byte> DeviceKeys => new byte[] { };
|
||||||
private static ReadOnlySpan<byte> DerivedKeysProd => new byte[] { };
|
private static ReadOnlySpan<byte> RsaSigningKeysDev => new byte[] { };
|
||||||
private static ReadOnlySpan<byte> DeviceKeys => new byte[] { };
|
private static ReadOnlySpan<byte> RsaSigningKeysProd => new byte[] { };
|
||||||
private static ReadOnlySpan<byte> RsaSigningKeysDev => new byte[] { };
|
private static ReadOnlySpan<byte> RsaKeys => new byte[] { };
|
||||||
private static ReadOnlySpan<byte> RsaSigningKeysProd => new byte[] { };
|
|
||||||
private static ReadOnlySpan<byte> RsaKeys => new byte[] { };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,178 +4,177 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
using Type = LibHac.Common.Keys.KeyInfo.KeyType;
|
using Type = LibHac.Common.Keys.KeyInfo.KeyType;
|
||||||
|
|
||||||
namespace LibHac.Common.Keys
|
namespace LibHac.Common.Keys;
|
||||||
|
|
||||||
|
internal static partial class DefaultKeySet
|
||||||
{
|
{
|
||||||
internal static partial class DefaultKeySet
|
/// <summary>
|
||||||
|
/// Creates a <see cref="KeySet"/> that contains any keys that have been embedded in the library.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The created <see cref="KeySet"/>.</returns>
|
||||||
|
public static KeySet CreateDefaultKeySet()
|
||||||
{
|
{
|
||||||
/// <summary>
|
var keySet = new KeySet();
|
||||||
/// Creates a <see cref="KeySet"/> that contains any keys that have been embedded in the library.
|
|
||||||
/// </summary>
|
// Fill the key set with any key structs included in the library.
|
||||||
/// <returns>The created <see cref="KeySet"/>.</returns>
|
if (RootKeysDev.Length == Unsafe.SizeOf<RootKeys>())
|
||||||
public static KeySet CreateDefaultKeySet()
|
|
||||||
{
|
{
|
||||||
var keySet = new KeySet();
|
keySet.KeyStruct._rootKeysDev = MemoryMarshal.Cast<byte, RootKeys>(RootKeysDev)[0];
|
||||||
|
|
||||||
// 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];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RootKeysProd.Length == Unsafe.SizeOf<RootKeys>())
|
|
||||||
{
|
|
||||||
keySet.KeyStruct._rootKeysProd = MemoryMarshal.Cast<byte, RootKeys>(RootKeysProd)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (KeySeeds.Length == Unsafe.SizeOf<KeySeeds>())
|
|
||||||
{
|
|
||||||
keySet.KeyStruct._keySeeds = MemoryMarshal.Cast<byte, KeySeeds>(KeySeeds)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StoredKeysDev.Length == Unsafe.SizeOf<StoredKeys>())
|
|
||||||
{
|
|
||||||
keySet.KeyStruct._storedKeysDev = MemoryMarshal.Cast<byte, StoredKeys>(StoredKeysDev)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StoredKeysProd.Length == Unsafe.SizeOf<StoredKeys>())
|
|
||||||
{
|
|
||||||
keySet.KeyStruct._storedKeysProd = MemoryMarshal.Cast<byte, StoredKeys>(StoredKeysProd)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DerivedKeysDev.Length == Unsafe.SizeOf<DerivedKeys>())
|
|
||||||
{
|
|
||||||
keySet.KeyStruct._derivedKeysDev = MemoryMarshal.Cast<byte, DerivedKeys>(DerivedKeysDev)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DerivedKeysProd.Length == Unsafe.SizeOf<DerivedKeys>())
|
|
||||||
{
|
|
||||||
keySet.KeyStruct._derivedKeysProd = MemoryMarshal.Cast<byte, DerivedKeys>(DerivedKeysProd)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DeviceKeys.Length == Unsafe.SizeOf<DeviceKeys>())
|
|
||||||
{
|
|
||||||
keySet.KeyStruct._deviceKeys = MemoryMarshal.Cast<byte, DeviceKeys>(DeviceKeys)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RsaSigningKeysDev.Length == Unsafe.SizeOf<RsaSigningKeys>())
|
|
||||||
{
|
|
||||||
keySet.KeyStruct._rsaSigningKeysDev = MemoryMarshal.Cast<byte, RsaSigningKeys>(RsaSigningKeysDev)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RsaSigningKeysProd.Length == Unsafe.SizeOf<RsaSigningKeys>())
|
|
||||||
{
|
|
||||||
keySet.KeyStruct._rsaSigningKeysProd = MemoryMarshal.Cast<byte, RsaSigningKeys>(RsaSigningKeysProd)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RsaKeys.Length == Unsafe.SizeOf<RsaKeys>())
|
|
||||||
{
|
|
||||||
keySet.KeyStruct._rsaKeys = MemoryMarshal.Cast<byte, RsaKeys>(RsaKeys)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return keySet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
if (RootKeysProd.Length == Unsafe.SizeOf<RootKeys>())
|
||||||
/// Creates a <see cref="List{T}"/> of the <see cref="KeyInfo"/> of all keys that are loadable by default.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The created list.</returns>
|
|
||||||
public static List<KeyInfo> CreateKeyList()
|
|
||||||
{
|
{
|
||||||
// Update this value if more keys are added
|
keySet.KeyStruct._rootKeysProd = MemoryMarshal.Cast<byte, RootKeys>(RootKeysProd)[0];
|
||||||
var keys = new List<KeyInfo>(70);
|
|
||||||
|
|
||||||
// Keys with a group value of -1 are keys that will be read but not written.
|
|
||||||
// This is for compatibility since some keys had other names in the past.
|
|
||||||
|
|
||||||
// TSEC secrets aren't public yet, so the TSEC root keys will be treated as
|
|
||||||
// root keys even though they're derived.
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(10, Type.DeviceRoot, "secure_boot_key", (set, _) => set.SecureBootKey));
|
|
||||||
keys.Add(new KeyInfo(11, Type.DeviceRoot, "tsec_key", (set, _) => set.TsecKey));
|
|
||||||
keys.Add(new KeyInfo(12, Type.DeviceDrvd, "device_key", (set, _) => set.DeviceKey));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(20, Type.CommonRoot, "tsec_root_kek", (set, _) => set.TsecRootKek));
|
|
||||||
keys.Add(new KeyInfo(21, Type.CommonRoot, "package1_mac_kek", (set, _) => set.Package1MacKek));
|
|
||||||
keys.Add(new KeyInfo(22, Type.CommonRoot, "package1_kek", (set, _) => set.Package1Kek));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(30, Type.CommonRoot, "tsec_auth_signature", 0, 0x20, (set, i) => set.TsecAuthSignatures[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(40, Type.CommonRoot, "tsec_root_key", 0, 0x20, (set, i) => set.TsecRootKeys[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(50, Type.CommonSeed, "keyblob_mac_key_source", (set, _) => set.KeyBlobMacKeySource));
|
|
||||||
keys.Add(new KeyInfo(51, Type.CommonSeed, "keyblob_key_source", 0, 6, (set, i) => set.KeyBlobKeySources[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(55, Type.DeviceDrvd, "keyblob_key", 0, 6, (set, i) => set.KeyBlobKeys[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(60, Type.DeviceDrvd, "keyblob_mac_key", 0, 6, (set, i) => set.KeyBlobMacKeys[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(70, Type.DeviceRoot, "encrypted_keyblob", 0, 6, (set, i) => set.EncryptedKeyBlobs[i].Bytes));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(80, Type.CommonRoot, "keyblob", 0, 6, (set, i) => set.KeyBlobs[i].Bytes));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(90, Type.CommonSeed, "master_kek_source", 6, 0x20, (set, i) => set.MasterKekSources[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(100, Type.CommonRoot, "mariko_bek", (set, _) => set.MarikoBek));
|
|
||||||
keys.Add(new KeyInfo(101, Type.CommonRoot, "mariko_kek", (set, _) => set.MarikoKek));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(110, Type.CommonRoot, "mariko_aes_class_key", 0, 0xC, (set, i) => set.MarikoAesClassKeys[i]));
|
|
||||||
keys.Add(new KeyInfo(120, Type.CommonSeedDiff, "mariko_master_kek_source", 0, 0x20, (set, i) => set.MarikoMasterKekSources[i]));
|
|
||||||
keys.Add(new KeyInfo(130, Type.CommonDrvd, "master_kek", 0, 0x20, (set, i) => set.MasterKeks[i]));
|
|
||||||
keys.Add(new KeyInfo(140, Type.CommonSeed, "master_key_source", (set, _) => set.MasterKeySource));
|
|
||||||
keys.Add(new KeyInfo(150, Type.CommonDrvd, "master_key", 0, 0x20, (set, i) => set.MasterKeys[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(160, Type.CommonDrvd, "package1_key", 0, 0x20, (set, i) => set.Package1Keys[i]));
|
|
||||||
keys.Add(new KeyInfo(170, Type.CommonDrvd, "package1_mac_key", 6, 0x20, (set, i) => set.Package1MacKeys[i]));
|
|
||||||
keys.Add(new KeyInfo(180, Type.CommonSeed, "package2_key_source", (set, _) => set.Package2KeySource));
|
|
||||||
keys.Add(new KeyInfo(190, Type.CommonDrvd, "package2_key", 0, 0x20, (set, i) => set.Package2Keys[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(200, Type.CommonSeed, "bis_kek_source", (set, _) => set.BisKekSource));
|
|
||||||
keys.Add(new KeyInfo(201, Type.CommonSeed, "bis_key_source", 0, 4, (set, i) => set.BisKeySources[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(205, Type.DeviceDrvd, "bis_key", 0, 4, (set, i) => set.BisKeys[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(210, Type.CommonSeed, "per_console_key_source", (set, _) => set.PerConsoleKeySource));
|
|
||||||
keys.Add(new KeyInfo(211, Type.CommonSeed, "retail_specific_aes_key_source", (set, _) => set.RetailSpecificAesKeySource));
|
|
||||||
keys.Add(new KeyInfo(212, Type.CommonSeed, "aes_kek_generation_source", (set, _) => set.AesKekGenerationSource));
|
|
||||||
keys.Add(new KeyInfo(213, Type.CommonSeed, "aes_key_generation_source", (set, _) => set.AesKeyGenerationSource));
|
|
||||||
keys.Add(new KeyInfo(214, Type.CommonSeed, "titlekek_source", (set, _) => set.TitleKekSource));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(220, Type.CommonDrvd, "titlekek", 0, 0x20, (set, i) => set.TitleKeks[i]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(230, Type.CommonSeed, "header_kek_source", (set, _) => set.HeaderKekSource));
|
|
||||||
keys.Add(new KeyInfo(231, Type.CommonSeed, "header_key_source", (set, _) => set.HeaderKeySource));
|
|
||||||
keys.Add(new KeyInfo(232, Type.CommonDrvd, "header_key", (set, _) => set.HeaderKey));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(240, Type.CommonSeed, "key_area_key_application_source", (set, _) => set.KeyAreaKeyApplicationSource));
|
|
||||||
keys.Add(new KeyInfo(241, Type.CommonSeed, "key_area_key_ocean_source", (set, _) => set.KeyAreaKeyOceanSource));
|
|
||||||
keys.Add(new KeyInfo(242, Type.CommonSeed, "key_area_key_system_source", (set, _) => set.KeyAreaKeySystemSource));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(250, Type.CommonSeed, "save_mac_kek_source", (set, _) => set.DeviceUniqueSaveMacKekSource));
|
|
||||||
keys.Add(new KeyInfo(251, Type.CommonSeed, "save_mac_key_source", 0, 2, (set, i) => set.DeviceUniqueSaveMacKeySources[i]));
|
|
||||||
keys.Add(new KeyInfo(252, Type.DeviceDrvd, "save_mac_key", 0, 2, (set, i) => set.DeviceUniqueSaveMacKeys[i]));
|
|
||||||
keys.Add(new KeyInfo(-01, Type.CommonSeed, "save_mac_key_source", (set, _) => set.DeviceUniqueSaveMacKeySources[0]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(253, Type.CommonSeed, "save_mac_sd_card_kek_source", (set, _) => set.SeedUniqueSaveMacKekSource));
|
|
||||||
keys.Add(new KeyInfo(254, Type.CommonSeed, "save_mac_sd_card_key_source", (set, _) => set.SeedUniqueSaveMacKeySource));
|
|
||||||
keys.Add(new KeyInfo(255, Type.DeviceDrvd, "save_mac_sd_card_key", (set, _) => set.SeedUniqueSaveMacKey));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(260, Type.DeviceRoot, "sd_seed", (set, _) => set.SdCardEncryptionSeed));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(261, Type.CommonSeed, "sd_card_kek_source", (set, _) => set.SdCardKekSource));
|
|
||||||
keys.Add(new KeyInfo(262, Type.CommonSeed, "sd_card_save_key_source", (set, _) => set.SdCardKeySources[0]));
|
|
||||||
keys.Add(new KeyInfo(263, Type.CommonSeed, "sd_card_nca_key_source", (set, _) => set.SdCardKeySources[1]));
|
|
||||||
keys.Add(new KeyInfo(264, Type.CommonSeed, "sd_card_custom_storage_key_source", (set, _) => set.SdCardKeySources[2]));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(270, Type.CommonSeedDiff, "xci_header_key", (set, _) => set.XciHeaderKey));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(280, Type.CommonRoot, "eticket_rsa_kek", (set, _) => set.ETicketRsaKek));
|
|
||||||
keys.Add(new KeyInfo(281, Type.CommonRoot, "ssl_rsa_kek", (set, _) => set.SslRsaKek));
|
|
||||||
|
|
||||||
keys.Add(new KeyInfo(290, Type.CommonDrvd, "key_area_key_application", 0, 0x20, (set, i) => set.KeyAreaKeys[i][0]));
|
|
||||||
keys.Add(new KeyInfo(300, Type.CommonDrvd, "key_area_key_ocean", 0, 0x20, (set, i) => set.KeyAreaKeys[i][1]));
|
|
||||||
keys.Add(new KeyInfo(310, Type.CommonDrvd, "key_area_key_system", 0, 0x20, (set, i) => set.KeyAreaKeys[i][2]));
|
|
||||||
|
|
||||||
return keys;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (KeySeeds.Length == Unsafe.SizeOf<KeySeeds>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._keySeeds = MemoryMarshal.Cast<byte, KeySeeds>(KeySeeds)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StoredKeysDev.Length == Unsafe.SizeOf<StoredKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._storedKeysDev = MemoryMarshal.Cast<byte, StoredKeys>(StoredKeysDev)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StoredKeysProd.Length == Unsafe.SizeOf<StoredKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._storedKeysProd = MemoryMarshal.Cast<byte, StoredKeys>(StoredKeysProd)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DerivedKeysDev.Length == Unsafe.SizeOf<DerivedKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._derivedKeysDev = MemoryMarshal.Cast<byte, DerivedKeys>(DerivedKeysDev)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DerivedKeysProd.Length == Unsafe.SizeOf<DerivedKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._derivedKeysProd = MemoryMarshal.Cast<byte, DerivedKeys>(DerivedKeysProd)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DeviceKeys.Length == Unsafe.SizeOf<DeviceKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._deviceKeys = MemoryMarshal.Cast<byte, DeviceKeys>(DeviceKeys)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RsaSigningKeysDev.Length == Unsafe.SizeOf<RsaSigningKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._rsaSigningKeysDev = MemoryMarshal.Cast<byte, RsaSigningKeys>(RsaSigningKeysDev)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RsaSigningKeysProd.Length == Unsafe.SizeOf<RsaSigningKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._rsaSigningKeysProd = MemoryMarshal.Cast<byte, RsaSigningKeys>(RsaSigningKeysProd)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RsaKeys.Length == Unsafe.SizeOf<RsaKeys>())
|
||||||
|
{
|
||||||
|
keySet.KeyStruct._rsaKeys = MemoryMarshal.Cast<byte, RsaKeys>(RsaKeys)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return keySet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="List{T}"/> of the <see cref="KeyInfo"/> of all keys that are loadable by default.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The created list.</returns>
|
||||||
|
public static List<KeyInfo> CreateKeyList()
|
||||||
|
{
|
||||||
|
// Update this value if more keys are added
|
||||||
|
var keys = new List<KeyInfo>(70);
|
||||||
|
|
||||||
|
// Keys with a group value of -1 are keys that will be read but not written.
|
||||||
|
// This is for compatibility since some keys had other names in the past.
|
||||||
|
|
||||||
|
// TSEC secrets aren't public yet, so the TSEC root keys will be treated as
|
||||||
|
// root keys even though they're derived.
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(10, Type.DeviceRoot, "secure_boot_key", (set, _) => set.SecureBootKey));
|
||||||
|
keys.Add(new KeyInfo(11, Type.DeviceRoot, "tsec_key", (set, _) => set.TsecKey));
|
||||||
|
keys.Add(new KeyInfo(12, Type.DeviceDrvd, "device_key", (set, _) => set.DeviceKey));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(20, Type.CommonRoot, "tsec_root_kek", (set, _) => set.TsecRootKek));
|
||||||
|
keys.Add(new KeyInfo(21, Type.CommonRoot, "package1_mac_kek", (set, _) => set.Package1MacKek));
|
||||||
|
keys.Add(new KeyInfo(22, Type.CommonRoot, "package1_kek", (set, _) => set.Package1Kek));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(30, Type.CommonRoot, "tsec_auth_signature", 0, 0x20, (set, i) => set.TsecAuthSignatures[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(40, Type.CommonRoot, "tsec_root_key", 0, 0x20, (set, i) => set.TsecRootKeys[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(50, Type.CommonSeed, "keyblob_mac_key_source", (set, _) => set.KeyBlobMacKeySource));
|
||||||
|
keys.Add(new KeyInfo(51, Type.CommonSeed, "keyblob_key_source", 0, 6, (set, i) => set.KeyBlobKeySources[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(55, Type.DeviceDrvd, "keyblob_key", 0, 6, (set, i) => set.KeyBlobKeys[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(60, Type.DeviceDrvd, "keyblob_mac_key", 0, 6, (set, i) => set.KeyBlobMacKeys[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(70, Type.DeviceRoot, "encrypted_keyblob", 0, 6, (set, i) => set.EncryptedKeyBlobs[i].Bytes));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(80, Type.CommonRoot, "keyblob", 0, 6, (set, i) => set.KeyBlobs[i].Bytes));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(90, Type.CommonSeed, "master_kek_source", 6, 0x20, (set, i) => set.MasterKekSources[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(100, Type.CommonRoot, "mariko_bek", (set, _) => set.MarikoBek));
|
||||||
|
keys.Add(new KeyInfo(101, Type.CommonRoot, "mariko_kek", (set, _) => set.MarikoKek));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(110, Type.CommonRoot, "mariko_aes_class_key", 0, 0xC, (set, i) => set.MarikoAesClassKeys[i]));
|
||||||
|
keys.Add(new KeyInfo(120, Type.CommonSeedDiff, "mariko_master_kek_source", 0, 0x20, (set, i) => set.MarikoMasterKekSources[i]));
|
||||||
|
keys.Add(new KeyInfo(130, Type.CommonDrvd, "master_kek", 0, 0x20, (set, i) => set.MasterKeks[i]));
|
||||||
|
keys.Add(new KeyInfo(140, Type.CommonSeed, "master_key_source", (set, _) => set.MasterKeySource));
|
||||||
|
keys.Add(new KeyInfo(150, Type.CommonDrvd, "master_key", 0, 0x20, (set, i) => set.MasterKeys[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(160, Type.CommonDrvd, "package1_key", 0, 0x20, (set, i) => set.Package1Keys[i]));
|
||||||
|
keys.Add(new KeyInfo(170, Type.CommonDrvd, "package1_mac_key", 6, 0x20, (set, i) => set.Package1MacKeys[i]));
|
||||||
|
keys.Add(new KeyInfo(180, Type.CommonSeed, "package2_key_source", (set, _) => set.Package2KeySource));
|
||||||
|
keys.Add(new KeyInfo(190, Type.CommonDrvd, "package2_key", 0, 0x20, (set, i) => set.Package2Keys[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(200, Type.CommonSeed, "bis_kek_source", (set, _) => set.BisKekSource));
|
||||||
|
keys.Add(new KeyInfo(201, Type.CommonSeed, "bis_key_source", 0, 4, (set, i) => set.BisKeySources[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(205, Type.DeviceDrvd, "bis_key", 0, 4, (set, i) => set.BisKeys[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(210, Type.CommonSeed, "per_console_key_source", (set, _) => set.PerConsoleKeySource));
|
||||||
|
keys.Add(new KeyInfo(211, Type.CommonSeed, "retail_specific_aes_key_source", (set, _) => set.RetailSpecificAesKeySource));
|
||||||
|
keys.Add(new KeyInfo(212, Type.CommonSeed, "aes_kek_generation_source", (set, _) => set.AesKekGenerationSource));
|
||||||
|
keys.Add(new KeyInfo(213, Type.CommonSeed, "aes_key_generation_source", (set, _) => set.AesKeyGenerationSource));
|
||||||
|
keys.Add(new KeyInfo(214, Type.CommonSeed, "titlekek_source", (set, _) => set.TitleKekSource));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(220, Type.CommonDrvd, "titlekek", 0, 0x20, (set, i) => set.TitleKeks[i]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(230, Type.CommonSeed, "header_kek_source", (set, _) => set.HeaderKekSource));
|
||||||
|
keys.Add(new KeyInfo(231, Type.CommonSeed, "header_key_source", (set, _) => set.HeaderKeySource));
|
||||||
|
keys.Add(new KeyInfo(232, Type.CommonDrvd, "header_key", (set, _) => set.HeaderKey));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(240, Type.CommonSeed, "key_area_key_application_source", (set, _) => set.KeyAreaKeyApplicationSource));
|
||||||
|
keys.Add(new KeyInfo(241, Type.CommonSeed, "key_area_key_ocean_source", (set, _) => set.KeyAreaKeyOceanSource));
|
||||||
|
keys.Add(new KeyInfo(242, Type.CommonSeed, "key_area_key_system_source", (set, _) => set.KeyAreaKeySystemSource));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(250, Type.CommonSeed, "save_mac_kek_source", (set, _) => set.DeviceUniqueSaveMacKekSource));
|
||||||
|
keys.Add(new KeyInfo(251, Type.CommonSeed, "save_mac_key_source", 0, 2, (set, i) => set.DeviceUniqueSaveMacKeySources[i]));
|
||||||
|
keys.Add(new KeyInfo(252, Type.DeviceDrvd, "save_mac_key", 0, 2, (set, i) => set.DeviceUniqueSaveMacKeys[i]));
|
||||||
|
keys.Add(new KeyInfo(-01, Type.CommonSeed, "save_mac_key_source", (set, _) => set.DeviceUniqueSaveMacKeySources[0]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(253, Type.CommonSeed, "save_mac_sd_card_kek_source", (set, _) => set.SeedUniqueSaveMacKekSource));
|
||||||
|
keys.Add(new KeyInfo(254, Type.CommonSeed, "save_mac_sd_card_key_source", (set, _) => set.SeedUniqueSaveMacKeySource));
|
||||||
|
keys.Add(new KeyInfo(255, Type.DeviceDrvd, "save_mac_sd_card_key", (set, _) => set.SeedUniqueSaveMacKey));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(260, Type.DeviceRoot, "sd_seed", (set, _) => set.SdCardEncryptionSeed));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(261, Type.CommonSeed, "sd_card_kek_source", (set, _) => set.SdCardKekSource));
|
||||||
|
keys.Add(new KeyInfo(262, Type.CommonSeed, "sd_card_save_key_source", (set, _) => set.SdCardKeySources[0]));
|
||||||
|
keys.Add(new KeyInfo(263, Type.CommonSeed, "sd_card_nca_key_source", (set, _) => set.SdCardKeySources[1]));
|
||||||
|
keys.Add(new KeyInfo(264, Type.CommonSeed, "sd_card_custom_storage_key_source", (set, _) => set.SdCardKeySources[2]));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(270, Type.CommonSeedDiff, "xci_header_key", (set, _) => set.XciHeaderKey));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(280, Type.CommonRoot, "eticket_rsa_kek", (set, _) => set.ETicketRsaKek));
|
||||||
|
keys.Add(new KeyInfo(281, Type.CommonRoot, "ssl_rsa_kek", (set, _) => set.SslRsaKek));
|
||||||
|
|
||||||
|
keys.Add(new KeyInfo(290, Type.CommonDrvd, "key_area_key_application", 0, 0x20, (set, i) => set.KeyAreaKeys[i][0]));
|
||||||
|
keys.Add(new KeyInfo(300, Type.CommonDrvd, "key_area_key_ocean", 0, 0x20, (set, i) => set.KeyAreaKeys[i][1]));
|
||||||
|
keys.Add(new KeyInfo(310, Type.CommonDrvd, "key_area_key_system", 0, 0x20, (set, i) => set.KeyAreaKeys[i][2]));
|
||||||
|
|
||||||
|
return keys;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -9,165 +9,164 @@ using LibHac.Util;
|
|||||||
using Type = LibHac.Common.Keys.KeyInfo.KeyType;
|
using Type = LibHac.Common.Keys.KeyInfo.KeyType;
|
||||||
using RangeType = LibHac.Common.Keys.KeyInfo.KeyRangeType;
|
using RangeType = LibHac.Common.Keys.KeyInfo.KeyRangeType;
|
||||||
|
|
||||||
namespace LibHac.Common.Keys
|
namespace LibHac.Common.Keys;
|
||||||
|
|
||||||
|
public static class ExternalKeyWriter
|
||||||
{
|
{
|
||||||
public static class ExternalKeyWriter
|
public static void PrintKeys(KeySet keySet, StringBuilder sb, List<KeyInfo> keys, Type filter, bool isDev)
|
||||||
{
|
{
|
||||||
public static void PrintKeys(KeySet keySet, StringBuilder sb, List<KeyInfo> keys, Type filter, bool isDev)
|
if (keys.Count == 0) return;
|
||||||
|
|
||||||
|
string devSuffix = isDev ? "_dev" : string.Empty;
|
||||||
|
int maxNameLength = keys.Max(x => x.NameLength);
|
||||||
|
int currentGroup = 0;
|
||||||
|
|
||||||
|
// Todo: Better filtering
|
||||||
|
bool FilterMatches(KeyInfo keyInfo)
|
||||||
{
|
{
|
||||||
if (keys.Count == 0) return;
|
Type filter1 = filter & (Type.Common | Type.Device);
|
||||||
|
Type filter2 = filter & (Type.Root | Type.Seed | Type.Derived);
|
||||||
|
Type filter3 = filter & Type.DifferentDev;
|
||||||
|
|
||||||
string devSuffix = isDev ? "_dev" : string.Empty;
|
// Skip sub-filters that have no flags set
|
||||||
int maxNameLength = keys.Max(x => x.NameLength);
|
return (filter1 == 0 || (filter1 & keyInfo.Type) != 0) &&
|
||||||
int currentGroup = 0;
|
(filter2 == 0 || (filter2 & keyInfo.Type) != 0) &&
|
||||||
|
filter3 == (filter3 & keyInfo.Type) ||
|
||||||
|
isDev && keyInfo.Type.HasFlag(Type.DifferentDev);
|
||||||
|
}
|
||||||
|
|
||||||
// Todo: Better filtering
|
bool isFirstPrint = true;
|
||||||
bool FilterMatches(KeyInfo keyInfo)
|
|
||||||
|
foreach (KeyInfo info in keys.Where(x => x.Group >= 0).Where(FilterMatches)
|
||||||
|
.OrderBy(x => x.Group).ThenBy(x => x.Name))
|
||||||
|
{
|
||||||
|
bool isNewGroup = false;
|
||||||
|
|
||||||
|
if (info.Group == currentGroup + 1)
|
||||||
{
|
{
|
||||||
Type filter1 = filter & (Type.Common | Type.Device);
|
currentGroup = info.Group;
|
||||||
Type filter2 = filter & (Type.Root | Type.Seed | Type.Derived);
|
}
|
||||||
Type filter3 = filter & Type.DifferentDev;
|
else if (info.Group > currentGroup)
|
||||||
|
{
|
||||||
// Skip sub-filters that have no flags set
|
// Don't update the current group yet because if this key is empty and the next key
|
||||||
return (filter1 == 0 || (filter1 & keyInfo.Type) != 0) &&
|
// is in the same group, we need to be able to know to add a blank line before printing it.
|
||||||
(filter2 == 0 || (filter2 & keyInfo.Type) != 0) &&
|
isNewGroup = !isFirstPrint;
|
||||||
filter3 == (filter3 & keyInfo.Type) ||
|
|
||||||
isDev && keyInfo.Type.HasFlag(Type.DifferentDev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFirstPrint = true;
|
if (info.RangeType == RangeType.Single)
|
||||||
|
|
||||||
foreach (KeyInfo info in keys.Where(x => x.Group >= 0).Where(FilterMatches)
|
|
||||||
.OrderBy(x => x.Group).ThenBy(x => x.Name))
|
|
||||||
{
|
{
|
||||||
bool isNewGroup = false;
|
Span<byte> key = info.Getter(keySet, 0);
|
||||||
|
if (key.IsZeros())
|
||||||
|
continue;
|
||||||
|
|
||||||
if (info.Group == currentGroup + 1)
|
if (isNewGroup)
|
||||||
{
|
{
|
||||||
currentGroup = info.Group;
|
sb.AppendLine();
|
||||||
}
|
|
||||||
else if (info.Group > currentGroup)
|
|
||||||
{
|
|
||||||
// Don't update the current group yet because if this key is empty and the next key
|
|
||||||
// is in the same group, we need to be able to know to add a blank line before printing it.
|
|
||||||
isNewGroup = !isFirstPrint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.RangeType == RangeType.Single)
|
string keyName = $"{info.Name}{devSuffix}";
|
||||||
|
|
||||||
|
string line = $"{keyName.PadRight(maxNameLength)} = {key.ToHexString()}";
|
||||||
|
sb.AppendLine(line);
|
||||||
|
isFirstPrint = false;
|
||||||
|
currentGroup = info.Group;
|
||||||
|
}
|
||||||
|
else if (info.RangeType == RangeType.Range)
|
||||||
|
{
|
||||||
|
bool hasPrintedKey = false;
|
||||||
|
|
||||||
|
for (int i = info.RangeStart; i < info.RangeEnd; i++)
|
||||||
{
|
{
|
||||||
Span<byte> key = info.Getter(keySet, 0);
|
Span<byte> key = info.Getter(keySet, i);
|
||||||
if (key.IsZeros())
|
if (key.IsZeros())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (isNewGroup)
|
if (hasPrintedKey == false)
|
||||||
{
|
{
|
||||||
sb.AppendLine();
|
if (isNewGroup)
|
||||||
|
{
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPrintedKey = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
string keyName = $"{info.Name}{devSuffix}";
|
string keyName = $"{info.Name}{devSuffix}_{i:x2}";
|
||||||
|
|
||||||
string line = $"{keyName.PadRight(maxNameLength)} = {key.ToHexString()}";
|
string line = $"{keyName.PadRight(maxNameLength)} = {key.ToHexString()}";
|
||||||
sb.AppendLine(line);
|
sb.AppendLine(line);
|
||||||
isFirstPrint = false;
|
isFirstPrint = false;
|
||||||
currentGroup = info.Group;
|
currentGroup = info.Group;
|
||||||
}
|
}
|
||||||
else if (info.RangeType == RangeType.Range)
|
|
||||||
{
|
|
||||||
bool hasPrintedKey = false;
|
|
||||||
|
|
||||||
for (int i = info.RangeStart; i < info.RangeEnd; i++)
|
|
||||||
{
|
|
||||||
Span<byte> key = info.Getter(keySet, i);
|
|
||||||
if (key.IsZeros())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (hasPrintedKey == false)
|
|
||||||
{
|
|
||||||
if (isNewGroup)
|
|
||||||
{
|
|
||||||
sb.AppendLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
hasPrintedKey = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
string keyName = $"{info.Name}{devSuffix}_{i:x2}";
|
|
||||||
|
|
||||||
string line = $"{keyName.PadRight(maxNameLength)} = {key.ToHexString()}";
|
|
||||||
sb.AppendLine(line);
|
|
||||||
isFirstPrint = false;
|
|
||||||
currentGroup = info.Group;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public static string PrintTitleKeys(KeySet keySet)
|
|
||||||
{
|
public static string PrintTitleKeys(KeySet keySet)
|
||||||
var sb = new StringBuilder();
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
foreach ((RightsId rightsId, AccessKey key) kv in keySet.ExternalKeySet.ToList()
|
|
||||||
.OrderBy(x => x.rightsId.ToString()))
|
foreach ((RightsId rightsId, AccessKey key) kv in keySet.ExternalKeySet.ToList()
|
||||||
{
|
.OrderBy(x => x.rightsId.ToString()))
|
||||||
string line = $"{kv.rightsId} = {kv.key}";
|
{
|
||||||
sb.AppendLine(line);
|
string line = $"{kv.rightsId} = {kv.key}";
|
||||||
}
|
sb.AppendLine(line);
|
||||||
|
}
|
||||||
return sb.ToString();
|
|
||||||
}
|
return sb.ToString();
|
||||||
|
}
|
||||||
public static string PrintCommonKeys(KeySet keySet)
|
|
||||||
{
|
public static string PrintCommonKeys(KeySet keySet)
|
||||||
var sb = new StringBuilder();
|
{
|
||||||
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Seed | Type.Derived,
|
var sb = new StringBuilder();
|
||||||
false);
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Seed | Type.Derived,
|
||||||
return sb.ToString();
|
false);
|
||||||
}
|
return sb.ToString();
|
||||||
|
}
|
||||||
public static string PrintDeviceKeys(KeySet keySet)
|
|
||||||
{
|
public static string PrintDeviceKeys(KeySet keySet)
|
||||||
var sb = new StringBuilder();
|
{
|
||||||
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Device, false);
|
var sb = new StringBuilder();
|
||||||
return sb.ToString();
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Device, false);
|
||||||
}
|
return sb.ToString();
|
||||||
|
}
|
||||||
public static string PrintAllKeys(KeySet keySet)
|
|
||||||
{
|
public static string PrintAllKeys(KeySet keySet)
|
||||||
var sb = new StringBuilder();
|
{
|
||||||
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), 0, false);
|
var sb = new StringBuilder();
|
||||||
return sb.ToString();
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), 0, false);
|
||||||
}
|
return sb.ToString();
|
||||||
|
}
|
||||||
public static string PrintAllSeeds(KeySet keySet)
|
|
||||||
{
|
public static string PrintAllSeeds(KeySet keySet)
|
||||||
var sb = new StringBuilder();
|
{
|
||||||
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Seed, false);
|
var sb = new StringBuilder();
|
||||||
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Seed, false);
|
||||||
if (keySet.CurrentMode == KeySet.Mode.Prod)
|
|
||||||
{
|
if (keySet.CurrentMode == KeySet.Mode.Prod)
|
||||||
sb.AppendLine();
|
{
|
||||||
keySet.SetMode(KeySet.Mode.Dev);
|
sb.AppendLine();
|
||||||
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Seed | Type.DifferentDev, true);
|
keySet.SetMode(KeySet.Mode.Dev);
|
||||||
keySet.SetMode(KeySet.Mode.Prod);
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Seed | Type.DifferentDev, true);
|
||||||
}
|
keySet.SetMode(KeySet.Mode.Prod);
|
||||||
return sb.ToString();
|
}
|
||||||
}
|
return sb.ToString();
|
||||||
|
}
|
||||||
public static string PrintCommonKeysWithDev(KeySet keySet)
|
|
||||||
{
|
public static string PrintCommonKeysWithDev(KeySet keySet)
|
||||||
KeySet.Mode originalMode = keySet.CurrentMode;
|
{
|
||||||
var sb = new StringBuilder();
|
KeySet.Mode originalMode = keySet.CurrentMode;
|
||||||
|
var sb = new StringBuilder();
|
||||||
keySet.SetMode(KeySet.Mode.Prod);
|
|
||||||
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Seed | Type.Derived,
|
keySet.SetMode(KeySet.Mode.Prod);
|
||||||
false);
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Seed | Type.Derived,
|
||||||
|
false);
|
||||||
sb.AppendLine();
|
|
||||||
keySet.SetMode(KeySet.Mode.Dev);
|
sb.AppendLine();
|
||||||
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Derived, true);
|
keySet.SetMode(KeySet.Mode.Dev);
|
||||||
|
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Derived, true);
|
||||||
keySet.SetMode(originalMode);
|
|
||||||
return sb.ToString();
|
keySet.SetMode(originalMode);
|
||||||
}
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,397 +2,396 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.Crypto;
|
using LibHac.Crypto;
|
||||||
|
|
||||||
namespace LibHac.Common.Keys
|
namespace LibHac.Common.Keys;
|
||||||
|
|
||||||
|
internal static class KeyDerivation
|
||||||
{
|
{
|
||||||
internal static class KeyDerivation
|
public static void DeriveAllKeys(KeySet keySet, IProgressReport logger = null)
|
||||||
{
|
{
|
||||||
public static void DeriveAllKeys(KeySet keySet, IProgressReport logger = null)
|
DeriveKeyBlobKeys(keySet);
|
||||||
|
DecryptKeyBlobs(keySet, logger);
|
||||||
|
ReadKeyBlobs(keySet);
|
||||||
|
|
||||||
|
Derive620Keys(keySet);
|
||||||
|
Derive620MasterKeks(keySet);
|
||||||
|
DeriveMarikoMasterKeks(keySet);
|
||||||
|
DeriveMasterKeys(keySet);
|
||||||
|
PopulateOldMasterKeys(keySet);
|
||||||
|
|
||||||
|
DerivePerConsoleKeys(keySet);
|
||||||
|
DerivePerGenerationKeys(keySet);
|
||||||
|
DeriveNcaHeaderKey(keySet);
|
||||||
|
DeriveSdCardKeys(keySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DeriveKeyBlobKeys(KeySet s)
|
||||||
|
{
|
||||||
|
if (s.SecureBootKey.IsZeros() || s.TsecKey.IsZeros()) return;
|
||||||
|
|
||||||
|
bool haveKeyBlobMacKeySource = !s.MasterKeySource.IsZeros();
|
||||||
|
var temp = new AesKey();
|
||||||
|
|
||||||
|
for (int i = 0; i < KeySet.UsedKeyBlobCount; i++)
|
||||||
{
|
{
|
||||||
DeriveKeyBlobKeys(keySet);
|
if (s.KeyBlobKeySources[i].IsZeros()) continue;
|
||||||
DecryptKeyBlobs(keySet, logger);
|
|
||||||
ReadKeyBlobs(keySet);
|
|
||||||
|
|
||||||
Derive620Keys(keySet);
|
Aes.DecryptEcb128(s.KeyBlobKeySources[i], temp, s.TsecKey);
|
||||||
Derive620MasterKeks(keySet);
|
Aes.DecryptEcb128(temp, s.KeyBlobKeys[i], s.SecureBootKey);
|
||||||
DeriveMarikoMasterKeks(keySet);
|
|
||||||
DeriveMasterKeys(keySet);
|
|
||||||
PopulateOldMasterKeys(keySet);
|
|
||||||
|
|
||||||
DerivePerConsoleKeys(keySet);
|
if (!haveKeyBlobMacKeySource) continue;
|
||||||
DerivePerGenerationKeys(keySet);
|
|
||||||
DeriveNcaHeaderKey(keySet);
|
Aes.DecryptEcb128(s.KeyBlobMacKeySource, s.KeyBlobMacKeys[i], s.KeyBlobKeys[i]);
|
||||||
DeriveSdCardKeys(keySet);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void DeriveKeyBlobKeys(KeySet s)
|
private static void DecryptKeyBlobs(KeySet s, IProgressReport logger = null)
|
||||||
|
{
|
||||||
|
var cmac = new AesCmac();
|
||||||
|
|
||||||
|
for (int i = 0; i < KeySet.UsedKeyBlobCount; i++)
|
||||||
{
|
{
|
||||||
if (s.SecureBootKey.IsZeros() || s.TsecKey.IsZeros()) return;
|
if (s.KeyBlobKeys[i].IsZeros() || s.KeyBlobMacKeys[i].IsZeros() || s.EncryptedKeyBlobs[i].IsZeros())
|
||||||
|
|
||||||
bool haveKeyBlobMacKeySource = !s.MasterKeySource.IsZeros();
|
|
||||||
var temp = new AesKey();
|
|
||||||
|
|
||||||
for (int i = 0; i < KeySet.UsedKeyBlobCount; i++)
|
|
||||||
{
|
{
|
||||||
if (s.KeyBlobKeySources[i].IsZeros()) continue;
|
continue;
|
||||||
|
|
||||||
Aes.DecryptEcb128(s.KeyBlobKeySources[i], temp, s.TsecKey);
|
|
||||||
Aes.DecryptEcb128(temp, s.KeyBlobKeys[i], s.SecureBootKey);
|
|
||||||
|
|
||||||
if (!haveKeyBlobMacKeySource) continue;
|
|
||||||
|
|
||||||
Aes.DecryptEcb128(s.KeyBlobMacKeySource, s.KeyBlobMacKeys[i], s.KeyBlobKeys[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Aes.CalculateCmac(cmac, s.EncryptedKeyBlobs[i].Bytes.Slice(0x10, 0xA0), s.KeyBlobMacKeys[i]);
|
||||||
|
|
||||||
|
if (!Utilities.SpansEqual<byte>(cmac, s.EncryptedKeyBlobs[i].Cmac))
|
||||||
|
{
|
||||||
|
logger?.LogMessage($"Warning: Keyblob MAC {i:x2} is invalid. Are SBK/TSEC key correct?");
|
||||||
|
}
|
||||||
|
|
||||||
|
Aes.DecryptCtr128(s.EncryptedKeyBlobs[i].Bytes.Slice(0x20), s.KeyBlobs[i].Bytes, s.KeyBlobKeys[i],
|
||||||
|
s.EncryptedKeyBlobs[i].Counter);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void DecryptKeyBlobs(KeySet s, IProgressReport logger = null)
|
private static void ReadKeyBlobs(KeySet s)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < KeySet.UsedKeyBlobCount; i++)
|
||||||
{
|
{
|
||||||
var cmac = new AesCmac();
|
if (s.KeyBlobs[i].IsZeros()) continue;
|
||||||
|
|
||||||
for (int i = 0; i < KeySet.UsedKeyBlobCount; i++)
|
s.MasterKeks[i] = s.KeyBlobs[i].MasterKek;
|
||||||
{
|
s.Package1Keys[i] = s.KeyBlobs[i].Package1Key;
|
||||||
if (s.KeyBlobKeys[i].IsZeros() || s.KeyBlobMacKeys[i].IsZeros() || s.EncryptedKeyBlobs[i].IsZeros())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Aes.CalculateCmac(cmac, s.EncryptedKeyBlobs[i].Bytes.Slice(0x10, 0xA0), s.KeyBlobMacKeys[i]);
|
|
||||||
|
|
||||||
if (!Utilities.SpansEqual<byte>(cmac, s.EncryptedKeyBlobs[i].Cmac))
|
|
||||||
{
|
|
||||||
logger?.LogMessage($"Warning: Keyblob MAC {i:x2} is invalid. Are SBK/TSEC key correct?");
|
|
||||||
}
|
|
||||||
|
|
||||||
Aes.DecryptCtr128(s.EncryptedKeyBlobs[i].Bytes.Slice(0x20), s.KeyBlobs[i].Bytes, s.KeyBlobKeys[i],
|
|
||||||
s.EncryptedKeyBlobs[i].Counter);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void ReadKeyBlobs(KeySet s)
|
private static void Derive620Keys(KeySet s)
|
||||||
|
{
|
||||||
|
bool haveTsecRootKek = !s.TsecRootKek.IsZeros();
|
||||||
|
bool havePackage1MacKek = !s.Package1MacKek.IsZeros();
|
||||||
|
bool havePackage1Kek = !s.Package1Kek.IsZeros();
|
||||||
|
|
||||||
|
for (int i = KeySet.UsedKeyBlobCount; i < KeySet.KeyRevisionCount; i++)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < KeySet.UsedKeyBlobCount; i++)
|
if (s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount].IsZeros())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (haveTsecRootKek)
|
||||||
{
|
{
|
||||||
if (s.KeyBlobs[i].IsZeros()) continue;
|
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount],
|
||||||
|
s.TsecRootKeys[i - KeySet.UsedKeyBlobCount], s.TsecRootKek);
|
||||||
s.MasterKeks[i] = s.KeyBlobs[i].MasterKek;
|
|
||||||
s.Package1Keys[i] = s.KeyBlobs[i].Package1Key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Derive620Keys(KeySet s)
|
|
||||||
{
|
|
||||||
bool haveTsecRootKek = !s.TsecRootKek.IsZeros();
|
|
||||||
bool havePackage1MacKek = !s.Package1MacKek.IsZeros();
|
|
||||||
bool havePackage1Kek = !s.Package1Kek.IsZeros();
|
|
||||||
|
|
||||||
for (int i = KeySet.UsedKeyBlobCount; i < KeySet.KeyRevisionCount; i++)
|
|
||||||
{
|
|
||||||
if (s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount].IsZeros())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (haveTsecRootKek)
|
|
||||||
{
|
|
||||||
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount],
|
|
||||||
s.TsecRootKeys[i - KeySet.UsedKeyBlobCount], s.TsecRootKek);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (havePackage1MacKek)
|
|
||||||
{
|
|
||||||
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount], s.Package1MacKeys[i],
|
|
||||||
s.Package1MacKek);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (havePackage1Kek)
|
|
||||||
{
|
|
||||||
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount], s.Package1Keys[i], s.Package1Kek);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Derive620MasterKeks(KeySet s)
|
|
||||||
{
|
|
||||||
for (int i = KeySet.UsedKeyBlobCount; i < KeySet.KeyRevisionCount; i++)
|
|
||||||
{
|
|
||||||
// Key revisions >= 8 all use the same TSEC root key
|
|
||||||
int tsecRootKeyIndex = Math.Min(i, 8) - KeySet.UsedKeyBlobCount;
|
|
||||||
if (s.TsecRootKeys[tsecRootKeyIndex].IsZeros() || s.MasterKekSources[i].IsZeros()) continue;
|
|
||||||
|
|
||||||
Aes.DecryptEcb128(s.MasterKekSources[i], s.MasterKeks[i], s.TsecRootKeys[tsecRootKeyIndex]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeriveMarikoMasterKeks(KeySet s)
|
|
||||||
{
|
|
||||||
if (s.MarikoKek.IsZeros()) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < KeySet.KeyRevisionCount; i++)
|
|
||||||
{
|
|
||||||
if (s.MarikoMasterKekSources[i].IsZeros()) continue;
|
|
||||||
|
|
||||||
Aes.DecryptEcb128(s.MarikoMasterKekSources[i], s.MasterKeks[i], s.MarikoKek);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeriveMasterKeys(KeySet s)
|
|
||||||
{
|
|
||||||
if (s.MasterKeySource.IsZeros()) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < KeySet.KeyRevisionCount; i++)
|
|
||||||
{
|
|
||||||
if (s.MasterKeks[i].IsZeros()) continue;
|
|
||||||
|
|
||||||
Aes.DecryptEcb128(s.MasterKeySource, s.MasterKeys[i], s.MasterKeks[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void PopulateOldMasterKeys(KeySet s)
|
|
||||||
{
|
|
||||||
ReadOnlySpan<AesKey> keyVectors = MasterKeyVectors(s);
|
|
||||||
|
|
||||||
// Find the newest master key we have
|
|
||||||
int newestMasterKey = -1;
|
|
||||||
|
|
||||||
for (int i = keyVectors.Length - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
if (!s.MasterKeys[i].IsZeros())
|
|
||||||
{
|
|
||||||
newestMasterKey = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newestMasterKey == -1)
|
if (havePackage1MacKek)
|
||||||
return;
|
|
||||||
|
|
||||||
// Don't populate old master keys unless the newest master key is valid
|
|
||||||
if (!TestKeyGeneration(s, newestMasterKey))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Decrypt all previous master keys
|
|
||||||
for (int i = newestMasterKey; i > 0; i--)
|
|
||||||
{
|
{
|
||||||
Aes.DecryptEcb128(keyVectors[i], s.MasterKeys[i - 1], s.MasterKeys[i]);
|
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount], s.Package1MacKeys[i],
|
||||||
}
|
s.Package1MacKek);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check if the master key of the specified generation is correct.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="s">The <see cref="KeySet"/> to test.</param>
|
|
||||||
/// <param name="generation">The generation to test.</param>
|
|
||||||
/// <returns><see langword="true"/> if the key is correct.</returns>
|
|
||||||
private static bool TestKeyGeneration(KeySet s, int generation)
|
|
||||||
{
|
|
||||||
ReadOnlySpan<AesKey> keyVectors = MasterKeyVectors(s);
|
|
||||||
|
|
||||||
// Decrypt the vector chain until we get Master Key 0
|
|
||||||
AesKey key = s.MasterKeys[generation];
|
|
||||||
|
|
||||||
for (int i = generation; i > 0; i--)
|
|
||||||
{
|
|
||||||
Aes.DecryptEcb128(keyVectors[i], key, key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt the zeros with Master Key 0
|
if (havePackage1Kek)
|
||||||
Aes.DecryptEcb128(keyVectors[0], key, key);
|
|
||||||
|
|
||||||
// If we don't get zeros, MasterKeys[generation] is incorrect
|
|
||||||
return key.IsZeros();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ReadOnlySpan<AesKey> MasterKeyVectors(KeySet s) =>
|
|
||||||
MemoryMarshal.Cast<byte, AesKey>(s.CurrentMode == KeySet.Mode.Dev
|
|
||||||
? MasterKeyVectorsDev
|
|
||||||
: MasterKeyVectorsProd);
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> MasterKeyVectorsDev => new byte[]
|
|
||||||
{
|
|
||||||
0x46, 0x22, 0xB4, 0x51, 0x9A, 0x7E, 0xA7, 0x7F, 0x62, 0xA1, 0x1F, 0x8F, 0xC5, 0x3A, 0xDB, 0xFE, // Zeroes encrypted with Master Key 00.
|
|
||||||
0x39, 0x33, 0xF9, 0x31, 0xBA, 0xE4, 0xA7, 0x21, 0x2C, 0xDD, 0xB7, 0xD8, 0xB4, 0x4E, 0x37, 0x23, // Master key 00 encrypted with Master key 01.
|
|
||||||
0x97, 0x29, 0xB0, 0x32, 0x43, 0x14, 0x8C, 0xA6, 0x85, 0xE9, 0x5A, 0x94, 0x99, 0x39, 0xAC, 0x5D, // Master key 01 encrypted with Master key 02.
|
|
||||||
0x2C, 0xCA, 0x9C, 0x31, 0x1E, 0x07, 0xB0, 0x02, 0x97, 0x0A, 0xD8, 0x03, 0xA2, 0x76, 0x3F, 0xA3, // Master key 02 encrypted with Master key 03.
|
|
||||||
0x9B, 0x84, 0x76, 0x14, 0x72, 0x94, 0x52, 0xCB, 0x54, 0x92, 0x9B, 0xC4, 0x8C, 0x5B, 0x0F, 0xBA, // Master key 03 encrypted with Master key 04.
|
|
||||||
0x78, 0xD5, 0xF1, 0x20, 0x3D, 0x16, 0xE9, 0x30, 0x32, 0x27, 0x34, 0x6F, 0xCF, 0xE0, 0x27, 0xDC, // Master key 04 encrypted with Master key 05.
|
|
||||||
0x6F, 0xD2, 0x84, 0x1D, 0x05, 0xEC, 0x40, 0x94, 0x5F, 0x18, 0xB3, 0x81, 0x09, 0x98, 0x8D, 0x4E, // Master key 05 encrypted with Master key 06.
|
|
||||||
0x37, 0xAF, 0xAB, 0x35, 0x79, 0x09, 0xD9, 0x48, 0x29, 0xD2, 0xDB, 0xA5, 0xA5, 0xF5, 0x30, 0x19, // Master key 06 encrypted with Master key 07.
|
|
||||||
0xEC, 0xE1, 0x46, 0x89, 0x37, 0xFD, 0xD2, 0x15, 0x8C, 0x3F, 0x24, 0x82, 0xEF, 0x49, 0x68, 0x04, // Master key 07 encrypted with Master key 08.
|
|
||||||
0x43, 0x3D, 0xC5, 0x3B, 0xEF, 0x91, 0x02, 0x21, 0x61, 0x54, 0x63, 0x8A, 0x35, 0xE7, 0xCA, 0xEE, // Master key 08 encrypted with Master key 09.
|
|
||||||
0x6C, 0x2E, 0xCD, 0xB3, 0x34, 0x61, 0x77, 0xF5, 0xF9, 0xB1, 0xDD, 0x61, 0x98, 0x19, 0x3E, 0xD4, // Master key 09 encrypted with Master key 0A.
|
|
||||||
0x21, 0x88, 0x6B, 0x10, 0x9E, 0x83, 0xD6, 0x52, 0xAB, 0x08, 0xDB, 0x6D, 0x39, 0xFF, 0x1C, 0x9C // Master key 0A encrypted with Master key 0B.
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> MasterKeyVectorsProd => new byte[]
|
|
||||||
{
|
|
||||||
0x0C, 0xF0, 0x59, 0xAC, 0x85, 0xF6, 0x26, 0x65, 0xE1, 0xE9, 0x19, 0x55, 0xE6, 0xF2, 0x67, 0x3D, // Zeroes encrypted with Master Key 00.
|
|
||||||
0x29, 0x4C, 0x04, 0xC8, 0xEB, 0x10, 0xED, 0x9D, 0x51, 0x64, 0x97, 0xFB, 0xF3, 0x4D, 0x50, 0xDD, // Master key 00 encrypted with Master key 01.
|
|
||||||
0xDE, 0xCF, 0xEB, 0xEB, 0x10, 0xAE, 0x74, 0xD8, 0xAD, 0x7C, 0xF4, 0x9E, 0x62, 0xE0, 0xE8, 0x72, // Master key 01 encrypted with Master key 02.
|
|
||||||
0x0A, 0x0D, 0xDF, 0x34, 0x22, 0x06, 0x6C, 0xA4, 0xE6, 0xB1, 0xEC, 0x71, 0x85, 0xCA, 0x4E, 0x07, // Master key 02 encrypted with Master key 03.
|
|
||||||
0x6E, 0x7D, 0x2D, 0xC3, 0x0F, 0x59, 0xC8, 0xFA, 0x87, 0xA8, 0x2E, 0xD5, 0x89, 0x5E, 0xF3, 0xE9, // Master key 03 encrypted with Master key 04.
|
|
||||||
0xEB, 0xF5, 0x6F, 0x83, 0x61, 0x9E, 0xF8, 0xFA, 0xE0, 0x87, 0xD7, 0xA1, 0x4E, 0x25, 0x36, 0xEE, // Master key 04 encrypted with Master key 05.
|
|
||||||
0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57, // Master key 05 encrypted with Master key 06.
|
|
||||||
0xA4, 0xD4, 0x52, 0x6F, 0xD1, 0xE4, 0x36, 0xAA, 0x9F, 0xCB, 0x61, 0x27, 0x1C, 0x67, 0x65, 0x1F, // Master key 06 encrypted with Master key 07.
|
|
||||||
0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29, // Master key 07 encrypted with Master key 08.
|
|
||||||
0x4D, 0xD9, 0x98, 0x42, 0x45, 0x0D, 0xB1, 0x3C, 0x52, 0x0C, 0x9A, 0x44, 0xBB, 0xAD, 0xAF, 0x80, // Master key 08 encrypted with Master key 09.
|
|
||||||
0xB8, 0x96, 0x9E, 0x4A, 0x00, 0x0D, 0xD6, 0x28, 0xB3, 0xD1, 0xDB, 0x68, 0x5F, 0xFB, 0xE1, 0x2A, // Master key 09 encrypted with Master key 0A.
|
|
||||||
0xC1, 0x8D, 0x16, 0xBB, 0x2A, 0xE4, 0x1D, 0xD4, 0xC2, 0xC1, 0xB6, 0x40, 0x94, 0x35, 0x63, 0x98 // Master key 0A encrypted with Master key 0B.
|
|
||||||
};
|
|
||||||
|
|
||||||
private static void DerivePerConsoleKeys(KeySet s)
|
|
||||||
{
|
|
||||||
// Todo: Dev and newer key generations
|
|
||||||
var kek = new AesKey();
|
|
||||||
|
|
||||||
// Derive the device key
|
|
||||||
if (!s.PerConsoleKeySource.IsZeros() && !s.KeyBlobKeys[0].IsZeros())
|
|
||||||
{
|
{
|
||||||
Aes.DecryptEcb128(s.PerConsoleKeySource, s.DeviceKey, s.KeyBlobKeys[0]);
|
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount], s.Package1Keys[i], s.Package1Kek);
|
||||||
}
|
|
||||||
|
|
||||||
// Derive device-unique save keys
|
|
||||||
for (int i = 0; i < s.DeviceUniqueSaveMacKeySources.Length; i++)
|
|
||||||
{
|
|
||||||
if (!s.DeviceUniqueSaveMacKekSource.IsZeros() && !s.DeviceUniqueSaveMacKeySources[i].IsZeros() &&
|
|
||||||
!s.DeviceKey.IsZeros())
|
|
||||||
{
|
|
||||||
GenerateKek(s.DeviceKey, s.DeviceUniqueSaveMacKekSource, kek, s.AesKekGenerationSource, null);
|
|
||||||
Aes.DecryptEcb128(s.DeviceUniqueSaveMacKeySources[i], s.DeviceUniqueSaveMacKeys[i], kek);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Derive BIS keys
|
|
||||||
if (s.DeviceKey.IsZeros()
|
|
||||||
|| s.BisKekSource.IsZeros()
|
|
||||||
|| s.AesKekGenerationSource.IsZeros()
|
|
||||||
|| s.AesKeyGenerationSource.IsZeros()
|
|
||||||
|| s.RetailSpecificAesKeySource.IsZeros())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user doesn't provide bis_key_source_03 we can assume it's the same as bis_key_source_02
|
|
||||||
if (s.BisKeySources[3].IsZeros() && !s.BisKeySources[2].IsZeros())
|
|
||||||
{
|
|
||||||
s.BisKeySources[3] = s.BisKeySources[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
Aes.DecryptEcb128(s.RetailSpecificAesKeySource, kek, s.DeviceKey);
|
|
||||||
if (!s.BisKeySources[0].IsZeros()) Aes.DecryptEcb128(s.BisKeySources[0], s.BisKeys[0], kek);
|
|
||||||
|
|
||||||
GenerateKek(s.DeviceKey, s.BisKekSource, kek, s.AesKekGenerationSource, s.AesKeyGenerationSource);
|
|
||||||
|
|
||||||
for (int i = 1; i < 4; i++)
|
|
||||||
{
|
|
||||||
if (!s.BisKeySources[i].IsZeros())
|
|
||||||
Aes.DecryptEcb128(s.BisKeySources[i], s.BisKeys[i], kek);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DerivePerGenerationKeys(KeySet s)
|
|
||||||
{
|
|
||||||
bool haveKakSource0 = !s.KeyAreaKeyApplicationSource.IsZeros();
|
|
||||||
bool haveKakSource1 = !s.KeyAreaKeyOceanSource.IsZeros();
|
|
||||||
bool haveKakSource2 = !s.KeyAreaKeySystemSource.IsZeros();
|
|
||||||
bool haveTitleKekSource = !s.TitleKekSource.IsZeros();
|
|
||||||
bool havePackage2KeySource = !s.Package2KeySource.IsZeros();
|
|
||||||
|
|
||||||
for (int i = 0; i < KeySet.KeyRevisionCount; i++)
|
|
||||||
{
|
|
||||||
if (s.MasterKeys[i].IsZeros())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (haveKakSource0)
|
|
||||||
{
|
|
||||||
GenerateKek(s.MasterKeys[i], s.KeyAreaKeyApplicationSource, s.KeyAreaKeys[i][0],
|
|
||||||
s.AesKekGenerationSource, s.AesKeyGenerationSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (haveKakSource1)
|
|
||||||
{
|
|
||||||
GenerateKek(s.MasterKeys[i], s.KeyAreaKeyOceanSource, s.KeyAreaKeys[i][1], s.AesKekGenerationSource,
|
|
||||||
s.AesKeyGenerationSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (haveKakSource2)
|
|
||||||
{
|
|
||||||
GenerateKek(s.MasterKeys[i], s.KeyAreaKeySystemSource, s.KeyAreaKeys[i][2],
|
|
||||||
s.AesKekGenerationSource, s.AesKeyGenerationSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (haveTitleKekSource)
|
|
||||||
{
|
|
||||||
Aes.DecryptEcb128(s.TitleKekSource, s.TitleKeks[i], s.MasterKeys[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (havePackage2KeySource)
|
|
||||||
{
|
|
||||||
Aes.DecryptEcb128(s.Package2KeySource, s.Package2Keys[i], s.MasterKeys[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DeriveNcaHeaderKey(KeySet s)
|
|
||||||
{
|
|
||||||
if (s.HeaderKekSource.IsZeros() || s.HeaderKeySource.IsZeros() || s.MasterKeys[0].IsZeros()) return;
|
|
||||||
|
|
||||||
var headerKek = new AesKey();
|
|
||||||
|
|
||||||
GenerateKek(s.MasterKeys[0], s.HeaderKekSource, headerKek, s.AesKekGenerationSource,
|
|
||||||
s.AesKeyGenerationSource);
|
|
||||||
Aes.DecryptEcb128(s.HeaderKeySource, s.HeaderKey, headerKek);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DeriveSdCardKeys(KeySet s)
|
|
||||||
{
|
|
||||||
var sdKek = new AesKey();
|
|
||||||
var tempKey = new AesXtsKey();
|
|
||||||
GenerateKek(s.MasterKeys[0], s.SdCardKekSource, sdKek, s.AesKekGenerationSource, s.AesKeyGenerationSource);
|
|
||||||
|
|
||||||
for (int k = 0; k < KeySet.SdCardKeyIdCount; k++)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
tempKey.Data64[i] = s.SdCardKeySources[k].Data64[i] ^ s.SdCardEncryptionSeed.Data64[i & 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
tempKey.Data64[0] = s.SdCardKeySources[k].Data64[0] ^ s.SdCardEncryptionSeed.Data64[0];
|
|
||||||
tempKey.Data64[1] = s.SdCardKeySources[k].Data64[1] ^ s.SdCardEncryptionSeed.Data64[1];
|
|
||||||
tempKey.Data64[2] = s.SdCardKeySources[k].Data64[2] ^ s.SdCardEncryptionSeed.Data64[0];
|
|
||||||
tempKey.Data64[3] = s.SdCardKeySources[k].Data64[3] ^ s.SdCardEncryptionSeed.Data64[1];
|
|
||||||
|
|
||||||
Aes.DecryptEcb128(tempKey, s.SdCardEncryptionKeys[k], sdKek);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Derive sd card save key
|
|
||||||
if (!s.SeedUniqueSaveMacKekSource.IsZeros() && !s.SeedUniqueSaveMacKeySource.IsZeros())
|
|
||||||
{
|
|
||||||
var keySource = new AesKey();
|
|
||||||
|
|
||||||
keySource.Data64[0] = s.SeedUniqueSaveMacKeySource.Data64[0] ^ s.SdCardEncryptionSeed.Data64[0];
|
|
||||||
keySource.Data64[1] = s.SeedUniqueSaveMacKeySource.Data64[1] ^ s.SdCardEncryptionSeed.Data64[1];
|
|
||||||
|
|
||||||
GenerateKek(s.MasterKeys[0], s.SeedUniqueSaveMacKekSource, sdKek, s.AesKekGenerationSource, null);
|
|
||||||
Aes.DecryptEcb128(keySource, s.SeedUniqueSaveMacKey, sdKek);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GenerateKek(ReadOnlySpan<byte> key, ReadOnlySpan<byte> src, Span<byte> dest,
|
|
||||||
ReadOnlySpan<byte> kekSeed, ReadOnlySpan<byte> keySeed)
|
|
||||||
{
|
|
||||||
var kek = new AesKey();
|
|
||||||
var srcKek = new AesKey();
|
|
||||||
|
|
||||||
Aes.DecryptEcb128(kekSeed, kek, key);
|
|
||||||
Aes.DecryptEcb128(src, srcKek, kek);
|
|
||||||
|
|
||||||
if (!keySeed.IsZeros())
|
|
||||||
{
|
|
||||||
Aes.DecryptEcb128(keySeed, dest, srcKek);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
srcKek.Data.CopyTo(dest);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void Derive620MasterKeks(KeySet s)
|
||||||
|
{
|
||||||
|
for (int i = KeySet.UsedKeyBlobCount; i < KeySet.KeyRevisionCount; i++)
|
||||||
|
{
|
||||||
|
// Key revisions >= 8 all use the same TSEC root key
|
||||||
|
int tsecRootKeyIndex = Math.Min(i, 8) - KeySet.UsedKeyBlobCount;
|
||||||
|
if (s.TsecRootKeys[tsecRootKeyIndex].IsZeros() || s.MasterKekSources[i].IsZeros()) continue;
|
||||||
|
|
||||||
|
Aes.DecryptEcb128(s.MasterKekSources[i], s.MasterKeks[i], s.TsecRootKeys[tsecRootKeyIndex]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DeriveMarikoMasterKeks(KeySet s)
|
||||||
|
{
|
||||||
|
if (s.MarikoKek.IsZeros()) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < KeySet.KeyRevisionCount; i++)
|
||||||
|
{
|
||||||
|
if (s.MarikoMasterKekSources[i].IsZeros()) continue;
|
||||||
|
|
||||||
|
Aes.DecryptEcb128(s.MarikoMasterKekSources[i], s.MasterKeks[i], s.MarikoKek);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DeriveMasterKeys(KeySet s)
|
||||||
|
{
|
||||||
|
if (s.MasterKeySource.IsZeros()) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < KeySet.KeyRevisionCount; i++)
|
||||||
|
{
|
||||||
|
if (s.MasterKeks[i].IsZeros()) continue;
|
||||||
|
|
||||||
|
Aes.DecryptEcb128(s.MasterKeySource, s.MasterKeys[i], s.MasterKeks[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PopulateOldMasterKeys(KeySet s)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<AesKey> keyVectors = MasterKeyVectors(s);
|
||||||
|
|
||||||
|
// Find the newest master key we have
|
||||||
|
int newestMasterKey = -1;
|
||||||
|
|
||||||
|
for (int i = keyVectors.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (!s.MasterKeys[i].IsZeros())
|
||||||
|
{
|
||||||
|
newestMasterKey = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newestMasterKey == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Don't populate old master keys unless the newest master key is valid
|
||||||
|
if (!TestKeyGeneration(s, newestMasterKey))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Decrypt all previous master keys
|
||||||
|
for (int i = newestMasterKey; i > 0; i--)
|
||||||
|
{
|
||||||
|
Aes.DecryptEcb128(keyVectors[i], s.MasterKeys[i - 1], s.MasterKeys[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the master key of the specified generation is correct.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="s">The <see cref="KeySet"/> to test.</param>
|
||||||
|
/// <param name="generation">The generation to test.</param>
|
||||||
|
/// <returns><see langword="true"/> if the key is correct.</returns>
|
||||||
|
private static bool TestKeyGeneration(KeySet s, int generation)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<AesKey> keyVectors = MasterKeyVectors(s);
|
||||||
|
|
||||||
|
// Decrypt the vector chain until we get Master Key 0
|
||||||
|
AesKey key = s.MasterKeys[generation];
|
||||||
|
|
||||||
|
for (int i = generation; i > 0; i--)
|
||||||
|
{
|
||||||
|
Aes.DecryptEcb128(keyVectors[i], key, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt the zeros with Master Key 0
|
||||||
|
Aes.DecryptEcb128(keyVectors[0], key, key);
|
||||||
|
|
||||||
|
// If we don't get zeros, MasterKeys[generation] is incorrect
|
||||||
|
return key.IsZeros();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReadOnlySpan<AesKey> MasterKeyVectors(KeySet s) =>
|
||||||
|
MemoryMarshal.Cast<byte, AesKey>(s.CurrentMode == KeySet.Mode.Dev
|
||||||
|
? MasterKeyVectorsDev
|
||||||
|
: MasterKeyVectorsProd);
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> MasterKeyVectorsDev => new byte[]
|
||||||
|
{
|
||||||
|
0x46, 0x22, 0xB4, 0x51, 0x9A, 0x7E, 0xA7, 0x7F, 0x62, 0xA1, 0x1F, 0x8F, 0xC5, 0x3A, 0xDB, 0xFE, // Zeroes encrypted with Master Key 00.
|
||||||
|
0x39, 0x33, 0xF9, 0x31, 0xBA, 0xE4, 0xA7, 0x21, 0x2C, 0xDD, 0xB7, 0xD8, 0xB4, 0x4E, 0x37, 0x23, // Master key 00 encrypted with Master key 01.
|
||||||
|
0x97, 0x29, 0xB0, 0x32, 0x43, 0x14, 0x8C, 0xA6, 0x85, 0xE9, 0x5A, 0x94, 0x99, 0x39, 0xAC, 0x5D, // Master key 01 encrypted with Master key 02.
|
||||||
|
0x2C, 0xCA, 0x9C, 0x31, 0x1E, 0x07, 0xB0, 0x02, 0x97, 0x0A, 0xD8, 0x03, 0xA2, 0x76, 0x3F, 0xA3, // Master key 02 encrypted with Master key 03.
|
||||||
|
0x9B, 0x84, 0x76, 0x14, 0x72, 0x94, 0x52, 0xCB, 0x54, 0x92, 0x9B, 0xC4, 0x8C, 0x5B, 0x0F, 0xBA, // Master key 03 encrypted with Master key 04.
|
||||||
|
0x78, 0xD5, 0xF1, 0x20, 0x3D, 0x16, 0xE9, 0x30, 0x32, 0x27, 0x34, 0x6F, 0xCF, 0xE0, 0x27, 0xDC, // Master key 04 encrypted with Master key 05.
|
||||||
|
0x6F, 0xD2, 0x84, 0x1D, 0x05, 0xEC, 0x40, 0x94, 0x5F, 0x18, 0xB3, 0x81, 0x09, 0x98, 0x8D, 0x4E, // Master key 05 encrypted with Master key 06.
|
||||||
|
0x37, 0xAF, 0xAB, 0x35, 0x79, 0x09, 0xD9, 0x48, 0x29, 0xD2, 0xDB, 0xA5, 0xA5, 0xF5, 0x30, 0x19, // Master key 06 encrypted with Master key 07.
|
||||||
|
0xEC, 0xE1, 0x46, 0x89, 0x37, 0xFD, 0xD2, 0x15, 0x8C, 0x3F, 0x24, 0x82, 0xEF, 0x49, 0x68, 0x04, // Master key 07 encrypted with Master key 08.
|
||||||
|
0x43, 0x3D, 0xC5, 0x3B, 0xEF, 0x91, 0x02, 0x21, 0x61, 0x54, 0x63, 0x8A, 0x35, 0xE7, 0xCA, 0xEE, // Master key 08 encrypted with Master key 09.
|
||||||
|
0x6C, 0x2E, 0xCD, 0xB3, 0x34, 0x61, 0x77, 0xF5, 0xF9, 0xB1, 0xDD, 0x61, 0x98, 0x19, 0x3E, 0xD4, // Master key 09 encrypted with Master key 0A.
|
||||||
|
0x21, 0x88, 0x6B, 0x10, 0x9E, 0x83, 0xD6, 0x52, 0xAB, 0x08, 0xDB, 0x6D, 0x39, 0xFF, 0x1C, 0x9C // Master key 0A encrypted with Master key 0B.
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ReadOnlySpan<byte> MasterKeyVectorsProd => new byte[]
|
||||||
|
{
|
||||||
|
0x0C, 0xF0, 0x59, 0xAC, 0x85, 0xF6, 0x26, 0x65, 0xE1, 0xE9, 0x19, 0x55, 0xE6, 0xF2, 0x67, 0x3D, // Zeroes encrypted with Master Key 00.
|
||||||
|
0x29, 0x4C, 0x04, 0xC8, 0xEB, 0x10, 0xED, 0x9D, 0x51, 0x64, 0x97, 0xFB, 0xF3, 0x4D, 0x50, 0xDD, // Master key 00 encrypted with Master key 01.
|
||||||
|
0xDE, 0xCF, 0xEB, 0xEB, 0x10, 0xAE, 0x74, 0xD8, 0xAD, 0x7C, 0xF4, 0x9E, 0x62, 0xE0, 0xE8, 0x72, // Master key 01 encrypted with Master key 02.
|
||||||
|
0x0A, 0x0D, 0xDF, 0x34, 0x22, 0x06, 0x6C, 0xA4, 0xE6, 0xB1, 0xEC, 0x71, 0x85, 0xCA, 0x4E, 0x07, // Master key 02 encrypted with Master key 03.
|
||||||
|
0x6E, 0x7D, 0x2D, 0xC3, 0x0F, 0x59, 0xC8, 0xFA, 0x87, 0xA8, 0x2E, 0xD5, 0x89, 0x5E, 0xF3, 0xE9, // Master key 03 encrypted with Master key 04.
|
||||||
|
0xEB, 0xF5, 0x6F, 0x83, 0x61, 0x9E, 0xF8, 0xFA, 0xE0, 0x87, 0xD7, 0xA1, 0x4E, 0x25, 0x36, 0xEE, // Master key 04 encrypted with Master key 05.
|
||||||
|
0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57, // Master key 05 encrypted with Master key 06.
|
||||||
|
0xA4, 0xD4, 0x52, 0x6F, 0xD1, 0xE4, 0x36, 0xAA, 0x9F, 0xCB, 0x61, 0x27, 0x1C, 0x67, 0x65, 0x1F, // Master key 06 encrypted with Master key 07.
|
||||||
|
0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29, // Master key 07 encrypted with Master key 08.
|
||||||
|
0x4D, 0xD9, 0x98, 0x42, 0x45, 0x0D, 0xB1, 0x3C, 0x52, 0x0C, 0x9A, 0x44, 0xBB, 0xAD, 0xAF, 0x80, // Master key 08 encrypted with Master key 09.
|
||||||
|
0xB8, 0x96, 0x9E, 0x4A, 0x00, 0x0D, 0xD6, 0x28, 0xB3, 0xD1, 0xDB, 0x68, 0x5F, 0xFB, 0xE1, 0x2A, // Master key 09 encrypted with Master key 0A.
|
||||||
|
0xC1, 0x8D, 0x16, 0xBB, 0x2A, 0xE4, 0x1D, 0xD4, 0xC2, 0xC1, 0xB6, 0x40, 0x94, 0x35, 0x63, 0x98 // Master key 0A encrypted with Master key 0B.
|
||||||
|
};
|
||||||
|
|
||||||
|
private static void DerivePerConsoleKeys(KeySet s)
|
||||||
|
{
|
||||||
|
// Todo: Dev and newer key generations
|
||||||
|
var kek = new AesKey();
|
||||||
|
|
||||||
|
// Derive the device key
|
||||||
|
if (!s.PerConsoleKeySource.IsZeros() && !s.KeyBlobKeys[0].IsZeros())
|
||||||
|
{
|
||||||
|
Aes.DecryptEcb128(s.PerConsoleKeySource, s.DeviceKey, s.KeyBlobKeys[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive device-unique save keys
|
||||||
|
for (int i = 0; i < s.DeviceUniqueSaveMacKeySources.Length; i++)
|
||||||
|
{
|
||||||
|
if (!s.DeviceUniqueSaveMacKekSource.IsZeros() && !s.DeviceUniqueSaveMacKeySources[i].IsZeros() &&
|
||||||
|
!s.DeviceKey.IsZeros())
|
||||||
|
{
|
||||||
|
GenerateKek(s.DeviceKey, s.DeviceUniqueSaveMacKekSource, kek, s.AesKekGenerationSource, null);
|
||||||
|
Aes.DecryptEcb128(s.DeviceUniqueSaveMacKeySources[i], s.DeviceUniqueSaveMacKeys[i], kek);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive BIS keys
|
||||||
|
if (s.DeviceKey.IsZeros()
|
||||||
|
|| s.BisKekSource.IsZeros()
|
||||||
|
|| s.AesKekGenerationSource.IsZeros()
|
||||||
|
|| s.AesKeyGenerationSource.IsZeros()
|
||||||
|
|| s.RetailSpecificAesKeySource.IsZeros())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user doesn't provide bis_key_source_03 we can assume it's the same as bis_key_source_02
|
||||||
|
if (s.BisKeySources[3].IsZeros() && !s.BisKeySources[2].IsZeros())
|
||||||
|
{
|
||||||
|
s.BisKeySources[3] = s.BisKeySources[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
Aes.DecryptEcb128(s.RetailSpecificAesKeySource, kek, s.DeviceKey);
|
||||||
|
if (!s.BisKeySources[0].IsZeros()) Aes.DecryptEcb128(s.BisKeySources[0], s.BisKeys[0], kek);
|
||||||
|
|
||||||
|
GenerateKek(s.DeviceKey, s.BisKekSource, kek, s.AesKekGenerationSource, s.AesKeyGenerationSource);
|
||||||
|
|
||||||
|
for (int i = 1; i < 4; i++)
|
||||||
|
{
|
||||||
|
if (!s.BisKeySources[i].IsZeros())
|
||||||
|
Aes.DecryptEcb128(s.BisKeySources[i], s.BisKeys[i], kek);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DerivePerGenerationKeys(KeySet s)
|
||||||
|
{
|
||||||
|
bool haveKakSource0 = !s.KeyAreaKeyApplicationSource.IsZeros();
|
||||||
|
bool haveKakSource1 = !s.KeyAreaKeyOceanSource.IsZeros();
|
||||||
|
bool haveKakSource2 = !s.KeyAreaKeySystemSource.IsZeros();
|
||||||
|
bool haveTitleKekSource = !s.TitleKekSource.IsZeros();
|
||||||
|
bool havePackage2KeySource = !s.Package2KeySource.IsZeros();
|
||||||
|
|
||||||
|
for (int i = 0; i < KeySet.KeyRevisionCount; i++)
|
||||||
|
{
|
||||||
|
if (s.MasterKeys[i].IsZeros())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (haveKakSource0)
|
||||||
|
{
|
||||||
|
GenerateKek(s.MasterKeys[i], s.KeyAreaKeyApplicationSource, s.KeyAreaKeys[i][0],
|
||||||
|
s.AesKekGenerationSource, s.AesKeyGenerationSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (haveKakSource1)
|
||||||
|
{
|
||||||
|
GenerateKek(s.MasterKeys[i], s.KeyAreaKeyOceanSource, s.KeyAreaKeys[i][1], s.AesKekGenerationSource,
|
||||||
|
s.AesKeyGenerationSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (haveKakSource2)
|
||||||
|
{
|
||||||
|
GenerateKek(s.MasterKeys[i], s.KeyAreaKeySystemSource, s.KeyAreaKeys[i][2],
|
||||||
|
s.AesKekGenerationSource, s.AesKeyGenerationSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (haveTitleKekSource)
|
||||||
|
{
|
||||||
|
Aes.DecryptEcb128(s.TitleKekSource, s.TitleKeks[i], s.MasterKeys[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (havePackage2KeySource)
|
||||||
|
{
|
||||||
|
Aes.DecryptEcb128(s.Package2KeySource, s.Package2Keys[i], s.MasterKeys[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DeriveNcaHeaderKey(KeySet s)
|
||||||
|
{
|
||||||
|
if (s.HeaderKekSource.IsZeros() || s.HeaderKeySource.IsZeros() || s.MasterKeys[0].IsZeros()) return;
|
||||||
|
|
||||||
|
var headerKek = new AesKey();
|
||||||
|
|
||||||
|
GenerateKek(s.MasterKeys[0], s.HeaderKekSource, headerKek, s.AesKekGenerationSource,
|
||||||
|
s.AesKeyGenerationSource);
|
||||||
|
Aes.DecryptEcb128(s.HeaderKeySource, s.HeaderKey, headerKek);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DeriveSdCardKeys(KeySet s)
|
||||||
|
{
|
||||||
|
var sdKek = new AesKey();
|
||||||
|
var tempKey = new AesXtsKey();
|
||||||
|
GenerateKek(s.MasterKeys[0], s.SdCardKekSource, sdKek, s.AesKekGenerationSource, s.AesKeyGenerationSource);
|
||||||
|
|
||||||
|
for (int k = 0; k < KeySet.SdCardKeyIdCount; k++)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
tempKey.Data64[i] = s.SdCardKeySources[k].Data64[i] ^ s.SdCardEncryptionSeed.Data64[i & 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
tempKey.Data64[0] = s.SdCardKeySources[k].Data64[0] ^ s.SdCardEncryptionSeed.Data64[0];
|
||||||
|
tempKey.Data64[1] = s.SdCardKeySources[k].Data64[1] ^ s.SdCardEncryptionSeed.Data64[1];
|
||||||
|
tempKey.Data64[2] = s.SdCardKeySources[k].Data64[2] ^ s.SdCardEncryptionSeed.Data64[0];
|
||||||
|
tempKey.Data64[3] = s.SdCardKeySources[k].Data64[3] ^ s.SdCardEncryptionSeed.Data64[1];
|
||||||
|
|
||||||
|
Aes.DecryptEcb128(tempKey, s.SdCardEncryptionKeys[k], sdKek);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive sd card save key
|
||||||
|
if (!s.SeedUniqueSaveMacKekSource.IsZeros() && !s.SeedUniqueSaveMacKeySource.IsZeros())
|
||||||
|
{
|
||||||
|
var keySource = new AesKey();
|
||||||
|
|
||||||
|
keySource.Data64[0] = s.SeedUniqueSaveMacKeySource.Data64[0] ^ s.SdCardEncryptionSeed.Data64[0];
|
||||||
|
keySource.Data64[1] = s.SeedUniqueSaveMacKeySource.Data64[1] ^ s.SdCardEncryptionSeed.Data64[1];
|
||||||
|
|
||||||
|
GenerateKek(s.MasterKeys[0], s.SeedUniqueSaveMacKekSource, sdKek, s.AesKekGenerationSource, null);
|
||||||
|
Aes.DecryptEcb128(keySource, s.SeedUniqueSaveMacKey, sdKek);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateKek(ReadOnlySpan<byte> key, ReadOnlySpan<byte> src, Span<byte> dest,
|
||||||
|
ReadOnlySpan<byte> kekSeed, ReadOnlySpan<byte> keySeed)
|
||||||
|
{
|
||||||
|
var kek = new AesKey();
|
||||||
|
var srcKek = new AesKey();
|
||||||
|
|
||||||
|
Aes.DecryptEcb128(kekSeed, kek, key);
|
||||||
|
Aes.DecryptEcb128(src, srcKek, kek);
|
||||||
|
|
||||||
|
if (!keySeed.IsZeros())
|
||||||
|
{
|
||||||
|
Aes.DecryptEcb128(keySeed, dest, srcKek);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
srcKek.Data.CopyTo(dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,165 +3,164 @@ using System.Diagnostics;
|
|||||||
using LibHac.Diag;
|
using LibHac.Diag;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Common.Keys
|
namespace LibHac.Common.Keys;
|
||||||
|
|
||||||
|
[DebuggerDisplay("{" + nameof(Name) + "}")]
|
||||||
|
public readonly struct KeyInfo
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{" + nameof(Name) + "}")]
|
public enum KeyRangeType : byte
|
||||||
public readonly struct KeyInfo
|
|
||||||
{
|
{
|
||||||
public enum KeyRangeType : byte
|
Single,
|
||||||
|
Range
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum KeyType : byte
|
||||||
|
{
|
||||||
|
Common = 1 << 0,
|
||||||
|
Device = 1 << 1,
|
||||||
|
Root = 1 << 2,
|
||||||
|
Seed = 1 << 3,
|
||||||
|
Derived = 1 << 4,
|
||||||
|
|
||||||
|
/// <summary>Specifies that a seed is different in prod and dev.</summary>
|
||||||
|
DifferentDev = 1 << 5,
|
||||||
|
|
||||||
|
CommonRoot = Common | Root,
|
||||||
|
CommonSeed = Common | Seed,
|
||||||
|
CommonSeedDiff = Common | Seed | DifferentDev,
|
||||||
|
CommonDrvd = Common | Derived,
|
||||||
|
DeviceRoot = Device | Root,
|
||||||
|
DeviceDrvd = Device | Derived
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly string Name;
|
||||||
|
public readonly KeyGetter Getter;
|
||||||
|
public readonly int Group;
|
||||||
|
public readonly KeyRangeType RangeType;
|
||||||
|
public readonly KeyType Type;
|
||||||
|
public readonly byte RangeStart;
|
||||||
|
public readonly byte RangeEnd;
|
||||||
|
|
||||||
|
public int NameLength => Name.Length + (RangeType == KeyRangeType.Range ? 3 : 0);
|
||||||
|
|
||||||
|
public delegate Span<byte> KeyGetter(KeySet keySet, int i);
|
||||||
|
|
||||||
|
public KeyInfo(int group, KeyType type, string name, KeyGetter retrieveFunc)
|
||||||
|
{
|
||||||
|
Assert.SdkRequires(IsKeyTypeValid(type));
|
||||||
|
|
||||||
|
Name = name;
|
||||||
|
RangeType = KeyRangeType.Single;
|
||||||
|
Type = type;
|
||||||
|
RangeStart = default;
|
||||||
|
RangeEnd = default;
|
||||||
|
Group = group;
|
||||||
|
Getter = retrieveFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyInfo(int group, KeyType type, string name, byte rangeStart, byte rangeEnd, KeyGetter retrieveFunc)
|
||||||
|
{
|
||||||
|
Assert.SdkRequires(IsKeyTypeValid(type));
|
||||||
|
|
||||||
|
Name = name;
|
||||||
|
RangeType = KeyRangeType.Range;
|
||||||
|
Type = type;
|
||||||
|
RangeStart = rangeStart;
|
||||||
|
RangeEnd = rangeEnd;
|
||||||
|
Group = group;
|
||||||
|
Getter = retrieveFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Matches(ReadOnlySpan<char> keyName, out int keyIndex, out bool isDev)
|
||||||
|
{
|
||||||
|
keyIndex = default;
|
||||||
|
isDev = default;
|
||||||
|
|
||||||
|
return RangeType switch
|
||||||
{
|
{
|
||||||
Single,
|
KeyRangeType.Single => MatchesSingle(keyName, out isDev),
|
||||||
Range
|
KeyRangeType.Range => MatchesRangedKey(keyName, ref keyIndex, out isDev),
|
||||||
}
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
[Flags]
|
private bool MatchesSingle(ReadOnlySpan<char> keyName, out bool isDev)
|
||||||
public enum KeyType : byte
|
{
|
||||||
|
Assert.SdkRequiresEqual((int)KeyRangeType.Single, (int)RangeType);
|
||||||
|
|
||||||
|
isDev = false;
|
||||||
|
|
||||||
|
if (keyName.Length == NameLength + 4)
|
||||||
{
|
{
|
||||||
Common = 1 << 0,
|
// Might be a dev key. Check if "_dev" comes after the base key name
|
||||||
Device = 1 << 1,
|
if (!keyName.Slice(Name.Length, 4).SequenceEqual("_dev"))
|
||||||
Root = 1 << 2,
|
|
||||||
Seed = 1 << 3,
|
|
||||||
Derived = 1 << 4,
|
|
||||||
|
|
||||||
/// <summary>Specifies that a seed is different in prod and dev.</summary>
|
|
||||||
DifferentDev = 1 << 5,
|
|
||||||
|
|
||||||
CommonRoot = Common | Root,
|
|
||||||
CommonSeed = Common | Seed,
|
|
||||||
CommonSeedDiff = Common | Seed | DifferentDev,
|
|
||||||
CommonDrvd = Common | Derived,
|
|
||||||
DeviceRoot = Device | Root,
|
|
||||||
DeviceDrvd = Device | Derived
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly string Name;
|
|
||||||
public readonly KeyGetter Getter;
|
|
||||||
public readonly int Group;
|
|
||||||
public readonly KeyRangeType RangeType;
|
|
||||||
public readonly KeyType Type;
|
|
||||||
public readonly byte RangeStart;
|
|
||||||
public readonly byte RangeEnd;
|
|
||||||
|
|
||||||
public int NameLength => Name.Length + (RangeType == KeyRangeType.Range ? 3 : 0);
|
|
||||||
|
|
||||||
public delegate Span<byte> KeyGetter(KeySet keySet, int i);
|
|
||||||
|
|
||||||
public KeyInfo(int group, KeyType type, string name, KeyGetter retrieveFunc)
|
|
||||||
{
|
|
||||||
Assert.SdkRequires(IsKeyTypeValid(type));
|
|
||||||
|
|
||||||
Name = name;
|
|
||||||
RangeType = KeyRangeType.Single;
|
|
||||||
Type = type;
|
|
||||||
RangeStart = default;
|
|
||||||
RangeEnd = default;
|
|
||||||
Group = group;
|
|
||||||
Getter = retrieveFunc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeyInfo(int group, KeyType type, string name, byte rangeStart, byte rangeEnd, KeyGetter retrieveFunc)
|
|
||||||
{
|
|
||||||
Assert.SdkRequires(IsKeyTypeValid(type));
|
|
||||||
|
|
||||||
Name = name;
|
|
||||||
RangeType = KeyRangeType.Range;
|
|
||||||
Type = type;
|
|
||||||
RangeStart = rangeStart;
|
|
||||||
RangeEnd = rangeEnd;
|
|
||||||
Group = group;
|
|
||||||
Getter = retrieveFunc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Matches(ReadOnlySpan<char> keyName, out int keyIndex, out bool isDev)
|
|
||||||
{
|
|
||||||
keyIndex = default;
|
|
||||||
isDev = default;
|
|
||||||
|
|
||||||
return RangeType switch
|
|
||||||
{
|
|
||||||
KeyRangeType.Single => MatchesSingle(keyName, out isDev),
|
|
||||||
KeyRangeType.Range => MatchesRangedKey(keyName, ref keyIndex, out isDev),
|
|
||||||
_ => false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool MatchesSingle(ReadOnlySpan<char> keyName, out bool isDev)
|
|
||||||
{
|
|
||||||
Assert.SdkRequiresEqual((int)KeyRangeType.Single, (int)RangeType);
|
|
||||||
|
|
||||||
isDev = false;
|
|
||||||
|
|
||||||
if (keyName.Length == NameLength + 4)
|
|
||||||
{
|
|
||||||
// Might be a dev key. Check if "_dev" comes after the base key name
|
|
||||||
if (!keyName.Slice(Name.Length, 4).SequenceEqual("_dev"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
isDev = true;
|
|
||||||
}
|
|
||||||
else if (keyName.Length != NameLength)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the base name matches
|
|
||||||
if (!keyName.Slice(0, Name.Length).SequenceEqual(Name))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
isDev = true;
|
||||||
}
|
}
|
||||||
|
else if (keyName.Length != NameLength)
|
||||||
private bool MatchesRangedKey(ReadOnlySpan<char> keyName, ref int keyIndex, out bool isDev)
|
|
||||||
{
|
{
|
||||||
Assert.SdkRequiresEqual((int)KeyRangeType.Range, (int)RangeType);
|
return false;
|
||||||
|
|
||||||
isDev = false;
|
|
||||||
|
|
||||||
// Check if this is an explicit dev key
|
|
||||||
if (keyName.Length == Name.Length + 7)
|
|
||||||
{
|
|
||||||
// Check if "_dev" comes after the base key name
|
|
||||||
if (!keyName.Slice(Name.Length, 4).SequenceEqual("_dev"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
isDev = true;
|
|
||||||
}
|
|
||||||
// Not a dev key. Check that the length of the key name with the trailing index matches
|
|
||||||
else if (keyName.Length != Name.Length + 3)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check if the name before the "_XX" index matches
|
|
||||||
if (!keyName.Slice(0, Name.Length).SequenceEqual(Name))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// The name should have an underscore before the index value
|
|
||||||
if (keyName[keyName.Length - 3] != '_')
|
|
||||||
return false;
|
|
||||||
|
|
||||||
byte index = default;
|
|
||||||
|
|
||||||
// Try to get the index of the key name
|
|
||||||
if (!StringUtils.TryFromHexString(keyName.Slice(keyName.Length - 2, 2), SpanHelpers.AsSpan(ref index)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check if the index is in this key's range
|
|
||||||
if (index < RangeStart || index >= RangeEnd)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
keyIndex = index;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsKeyTypeValid(KeyType type)
|
// Check if the base name matches
|
||||||
|
if (!keyName.Slice(0, Name.Length).SequenceEqual(Name))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MatchesRangedKey(ReadOnlySpan<char> keyName, ref int keyIndex, out bool isDev)
|
||||||
|
{
|
||||||
|
Assert.SdkRequiresEqual((int)KeyRangeType.Range, (int)RangeType);
|
||||||
|
|
||||||
|
isDev = false;
|
||||||
|
|
||||||
|
// Check if this is an explicit dev key
|
||||||
|
if (keyName.Length == Name.Length + 7)
|
||||||
{
|
{
|
||||||
// Make sure the type has exactly one flag set for each type
|
// Check if "_dev" comes after the base key name
|
||||||
KeyType type1 = type & (KeyType.Common | KeyType.Device);
|
if (!keyName.Slice(Name.Length, 4).SequenceEqual("_dev"))
|
||||||
KeyType type2 = type & (KeyType.Root | KeyType.Seed | KeyType.Derived);
|
return false;
|
||||||
|
|
||||||
bool isValid1 = type1 == KeyType.Common || type1 == KeyType.Device;
|
isDev = true;
|
||||||
bool isValid2 = type2 == KeyType.Root || type2 == KeyType.Seed || type2 == KeyType.Derived;
|
|
||||||
|
|
||||||
return isValid1 && isValid2;
|
|
||||||
}
|
}
|
||||||
|
// Not a dev key. Check that the length of the key name with the trailing index matches
|
||||||
|
else if (keyName.Length != Name.Length + 3)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the name before the "_XX" index matches
|
||||||
|
if (!keyName.Slice(0, Name.Length).SequenceEqual(Name))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// The name should have an underscore before the index value
|
||||||
|
if (keyName[keyName.Length - 3] != '_')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
byte index = default;
|
||||||
|
|
||||||
|
// Try to get the index of the key name
|
||||||
|
if (!StringUtils.TryFromHexString(keyName.Slice(keyName.Length - 2, 2), SpanHelpers.AsSpan(ref index)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the index is in this key's range
|
||||||
|
if (index < RangeStart || index >= RangeEnd)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
keyIndex = index;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsKeyTypeValid(KeyType type)
|
||||||
|
{
|
||||||
|
// Make sure the type has exactly one flag set for each type
|
||||||
|
KeyType type1 = type & (KeyType.Common | KeyType.Device);
|
||||||
|
KeyType type2 = type & (KeyType.Root | KeyType.Seed | KeyType.Derived);
|
||||||
|
|
||||||
|
bool isValid1 = type1 == KeyType.Common || type1 == KeyType.Device;
|
||||||
|
bool isValid2 = type2 == KeyType.Root || type2 == KeyType.Seed || type2 == KeyType.Derived;
|
||||||
|
|
||||||
|
return isValid1 && isValid2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,377 +8,376 @@ using LibHac.Crypto;
|
|||||||
using LibHac.FsSrv;
|
using LibHac.FsSrv;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Common.Keys
|
namespace LibHac.Common.Keys;
|
||||||
|
|
||||||
|
public class KeySet
|
||||||
{
|
{
|
||||||
public class KeySet
|
public enum Mode
|
||||||
{
|
{
|
||||||
public enum Mode
|
Dev,
|
||||||
{
|
Prod
|
||||||
Dev,
|
|
||||||
Prod
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number of keyblobs that were used for < 6.2.0 crypto
|
|
||||||
/// </summary>
|
|
||||||
internal const int UsedKeyBlobCount = 6;
|
|
||||||
internal const int SdCardKeyIdCount = 3;
|
|
||||||
internal const int KeyRevisionCount = 0x20;
|
|
||||||
|
|
||||||
private AllKeys _keys;
|
|
||||||
private Mode _mode = Mode.Prod;
|
|
||||||
|
|
||||||
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 RsaSigningKeyParameters RsaSigningKeyParams => ref _mode == Mode.Dev
|
|
||||||
? ref _rsaSigningKeyParamsDev
|
|
||||||
: ref _rsaSigningKeyParamsProd;
|
|
||||||
|
|
||||||
public ExternalKeySet ExternalKeySet { get; } = new ExternalKeySet();
|
|
||||||
|
|
||||||
public Span<AesKey> MarikoAesClassKeys => RootKeys.MarikoAesClassKeys.Items;
|
|
||||||
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 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> MarikoMasterKekSources => _mode == Mode.Dev
|
|
||||||
? _keys._keySeeds.MarikoMasterKekSources_dev.Items
|
|
||||||
: _keys._keySeeds.MarikoMasterKekSources.Items;
|
|
||||||
|
|
||||||
public Span<AesKey> MasterKeks => DerivedKeys.MasterKeks.Items;
|
|
||||||
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 AesXtsKey HeaderKey => ref DerivedKeys.HeaderKey;
|
|
||||||
public Span<AesKey> TitleKeks => DerivedKeys.TitleKeks.Items;
|
|
||||||
public Span<Array3<AesKey>> KeyAreaKeys => DerivedKeys.KeyAreaKeys.Items;
|
|
||||||
public ref AesKey XciHeaderKey => ref StoredKeys.XciHeaderKey;
|
|
||||||
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;
|
|
||||||
|
|
||||||
// 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<RsaKey> NcaHeaderSigningKeys => RsaSigningKeys.NcaHeaderSigningKeys.Items;
|
|
||||||
public Span<RsaKey> AcidSigningKeys => RsaSigningKeys.AcidSigningKeys.Items;
|
|
||||||
public ref RsaKey Package2SigningKey => ref RsaSigningKeys.Package2SigningKey;
|
|
||||||
public ref RsaFullKey BetaNca0KeyAreaKey => ref RsaKeys.BetaNca0KeyAreaKey;
|
|
||||||
|
|
||||||
private RsaSigningKeyParameters _rsaSigningKeyParamsDev;
|
|
||||||
private RsaSigningKeyParameters _rsaSigningKeyParamsProd;
|
|
||||||
private RsaKeyParameters _rsaKeyParams;
|
|
||||||
|
|
||||||
public RSAParameters ETicketExtKeyRsa { get; set; }
|
|
||||||
|
|
||||||
public Span<RSAParameters> NcaHeaderSigningKeyParams
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
ref Optional<Array2<RSAParameters>> keys = ref RsaSigningKeyParams.NcaHeaderSigningKeys;
|
|
||||||
|
|
||||||
if (!keys.HasValue)
|
|
||||||
{
|
|
||||||
keys.Set(new Array2<RSAParameters>());
|
|
||||||
keys.Value[0] = CreateRsaParameters(in NcaHeaderSigningKeys[0]);
|
|
||||||
keys.Value[1] = CreateRsaParameters(in NcaHeaderSigningKeys[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return keys.Value.Items;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Span<RSAParameters> AcidSigningKeyParams
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
ref Optional<Array2<RSAParameters>> keys = ref RsaSigningKeyParams.AcidSigningKeys;
|
|
||||||
|
|
||||||
if (!keys.HasValue)
|
|
||||||
{
|
|
||||||
keys.Set(new Array2<RSAParameters>());
|
|
||||||
keys.Value[0] = CreateRsaParameters(in AcidSigningKeys[0]);
|
|
||||||
keys.Value[1] = CreateRsaParameters(in AcidSigningKeys[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return keys.Value.Items;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ref RSAParameters Package2SigningKeyParams
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
ref Optional<RSAParameters> keys = ref RsaSigningKeyParams.Package2SigningKey;
|
|
||||||
|
|
||||||
if (!keys.HasValue)
|
|
||||||
{
|
|
||||||
keys.Set(new RSAParameters());
|
|
||||||
keys.Value = CreateRsaParameters(in Package2SigningKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ref keys.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ref RSAParameters BetaNca0KeyAreaKeyParams
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
ref Optional<RSAParameters> keys = ref _rsaKeyParams.BetaNca0KeyAreaKey;
|
|
||||||
|
|
||||||
if (!keys.HasValue)
|
|
||||||
{
|
|
||||||
keys.Set(CreateRsaParameters(in BetaNca0KeyAreaKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ref keys.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetSdSeed(ReadOnlySpan<byte> sdSeed)
|
|
||||||
{
|
|
||||||
if (sdSeed.Length != 0x10)
|
|
||||||
throw new ArgumentException("Sd card encryption seed must be 16 bytes long.");
|
|
||||||
|
|
||||||
sdSeed.CopyTo(SdCardEncryptionSeed);
|
|
||||||
DeriveSdCardKeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetMode(Mode mode) => _mode = mode;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a new <see cref="KeySet"/> containing any keys that have been compiled into the library.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The created <see cref="KeySet"/>,</returns>
|
|
||||||
public static KeySet CreateDefaultKeySet()
|
|
||||||
{
|
|
||||||
return DefaultKeySet.CreateDefaultKeySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<KeyInfo> CreateKeyInfoList()
|
|
||||||
{
|
|
||||||
return DefaultKeySet.CreateKeyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeriveKeys(IProgressReport logger = null)
|
|
||||||
{
|
|
||||||
Mode originalMode = CurrentMode;
|
|
||||||
|
|
||||||
SetMode(Mode.Prod);
|
|
||||||
KeyDerivation.DeriveAllKeys(this, logger);
|
|
||||||
|
|
||||||
SetMode(Mode.Dev);
|
|
||||||
KeyDerivation.DeriveAllKeys(this, logger);
|
|
||||||
|
|
||||||
SetMode(originalMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeriveSdCardKeys() => KeyDerivation.DeriveSdCardKeys(this);
|
|
||||||
|
|
||||||
private static RSAParameters CreateRsaParameters(in RsaKey key)
|
|
||||||
{
|
|
||||||
return new RSAParameters
|
|
||||||
{
|
|
||||||
Exponent = key.PublicExponent.DataRo.ToArray(),
|
|
||||||
Modulus = key.Modulus.DataRo.ToArray()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RSAParameters CreateRsaParameters(in RsaFullKey key)
|
|
||||||
{
|
|
||||||
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()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct RsaSigningKeyParameters
|
|
||||||
{
|
|
||||||
public Optional<Array2<RSAParameters>> NcaHeaderSigningKeys;
|
|
||||||
public Optional<Array2<RSAParameters>> AcidSigningKeys;
|
|
||||||
public Optional<RSAParameters> Package2SigningKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct RsaKeyParameters
|
|
||||||
{
|
|
||||||
public Optional<RSAParameters> BetaNca0KeyAreaKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[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;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct RootKeys
|
|
||||||
{
|
|
||||||
// Mariko keys. The AES class keys are currently unused.
|
|
||||||
public AesKey MarikoKek;
|
|
||||||
public AesKey MarikoBek;
|
|
||||||
public Array12<AesKey> MarikoAesClassKeys;
|
|
||||||
|
|
||||||
// The key blobs are technically derived from the encrypted key blobs and their keys,
|
|
||||||
// however those keys are device-unique. The decrypted key blobs are basically the common root
|
|
||||||
// keys used by pre-6.2.0 Erista.
|
|
||||||
public Array32<KeyBlob> KeyBlobs;
|
|
||||||
|
|
||||||
// Used by TSEC in >= 6.2.0 Erista firmware
|
|
||||||
public Array32<AesKey> TsecAuthSignatures;
|
|
||||||
public AesKey TsecRootKek;
|
|
||||||
public AesKey Package1MacKek;
|
|
||||||
public AesKey Package1Kek;
|
|
||||||
|
|
||||||
// Derived by TSEC. This is the first public root key for >= 6.2.0 Erista
|
|
||||||
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 AesKey MasterKeySource;
|
|
||||||
public AesKey Package2KeySource;
|
|
||||||
public AesKey PerConsoleKeySource;
|
|
||||||
public AesKey RetailSpecificAesKeySource;
|
|
||||||
public AesKey BisKekSource;
|
|
||||||
public Array4<AesXtsKey> BisKeySources;
|
|
||||||
public AesKey AesKekGenerationSource;
|
|
||||||
public AesKey AesKeyGenerationSource;
|
|
||||||
public AesKey KeyAreaKeyApplicationSource;
|
|
||||||
public AesKey KeyAreaKeyOceanSource;
|
|
||||||
public AesKey KeyAreaKeySystemSource;
|
|
||||||
public AesKey TitleKekSource;
|
|
||||||
public AesKey HeaderKekSource;
|
|
||||||
public AesKey SdCardKekSource;
|
|
||||||
public Array3<AesXtsKey> SdCardKeySources;
|
|
||||||
public AesKey DeviceUniqueSaveMacKekSource;
|
|
||||||
public Array2<AesKey> DeviceUniqueSaveMacKeySources;
|
|
||||||
public AesKey SeedUniqueSaveMacKekSource;
|
|
||||||
public AesKey SeedUniqueSaveMacKeySource;
|
|
||||||
public AesXtsKey HeaderKeySource;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Holds keys that are stored directly in Horizon programs.
|
/// The number of keyblobs that were used for < 6.2.0 crypto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
internal const int UsedKeyBlobCount = 6;
|
||||||
public struct StoredKeys
|
internal const int SdCardKeyIdCount = 3;
|
||||||
|
internal const int KeyRevisionCount = 0x20;
|
||||||
|
|
||||||
|
private AllKeys _keys;
|
||||||
|
private Mode _mode = Mode.Prod;
|
||||||
|
|
||||||
|
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 RsaSigningKeyParameters RsaSigningKeyParams => ref _mode == Mode.Dev
|
||||||
|
? ref _rsaSigningKeyParamsDev
|
||||||
|
: ref _rsaSigningKeyParamsProd;
|
||||||
|
|
||||||
|
public ExternalKeySet ExternalKeySet { get; } = new ExternalKeySet();
|
||||||
|
|
||||||
|
public Span<AesKey> MarikoAesClassKeys => RootKeys.MarikoAesClassKeys.Items;
|
||||||
|
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 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> MarikoMasterKekSources => _mode == Mode.Dev
|
||||||
|
? _keys._keySeeds.MarikoMasterKekSources_dev.Items
|
||||||
|
: _keys._keySeeds.MarikoMasterKekSources.Items;
|
||||||
|
|
||||||
|
public Span<AesKey> MasterKeks => DerivedKeys.MasterKeks.Items;
|
||||||
|
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 AesXtsKey HeaderKey => ref DerivedKeys.HeaderKey;
|
||||||
|
public Span<AesKey> TitleKeks => DerivedKeys.TitleKeks.Items;
|
||||||
|
public Span<Array3<AesKey>> KeyAreaKeys => DerivedKeys.KeyAreaKeys.Items;
|
||||||
|
public ref AesKey XciHeaderKey => ref StoredKeys.XciHeaderKey;
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 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<RsaKey> NcaHeaderSigningKeys => RsaSigningKeys.NcaHeaderSigningKeys.Items;
|
||||||
|
public Span<RsaKey> AcidSigningKeys => RsaSigningKeys.AcidSigningKeys.Items;
|
||||||
|
public ref RsaKey Package2SigningKey => ref RsaSigningKeys.Package2SigningKey;
|
||||||
|
public ref RsaFullKey BetaNca0KeyAreaKey => ref RsaKeys.BetaNca0KeyAreaKey;
|
||||||
|
|
||||||
|
private RsaSigningKeyParameters _rsaSigningKeyParamsDev;
|
||||||
|
private RsaSigningKeyParameters _rsaSigningKeyParamsProd;
|
||||||
|
private RsaKeyParameters _rsaKeyParams;
|
||||||
|
|
||||||
|
public RSAParameters ETicketExtKeyRsa { get; set; }
|
||||||
|
|
||||||
|
public Span<RSAParameters> NcaHeaderSigningKeyParams
|
||||||
{
|
{
|
||||||
public AesKey XciHeaderKey;
|
get
|
||||||
|
{
|
||||||
|
ref Optional<Array2<RSAParameters>> keys = ref RsaSigningKeyParams.NcaHeaderSigningKeys;
|
||||||
|
|
||||||
|
if (!keys.HasValue)
|
||||||
|
{
|
||||||
|
keys.Set(new Array2<RSAParameters>());
|
||||||
|
keys.Value[0] = CreateRsaParameters(in NcaHeaderSigningKeys[0]);
|
||||||
|
keys.Value[1] = CreateRsaParameters(in NcaHeaderSigningKeys[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys.Value.Items;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public Span<RSAParameters> AcidSigningKeyParams
|
||||||
public struct DerivedKeys
|
|
||||||
{
|
{
|
||||||
public Array32<AesKey> MasterKeks;
|
get
|
||||||
public Array32<AesKey> MasterKeys;
|
{
|
||||||
public Array32<AesKey> Package1MacKeys;
|
ref Optional<Array2<RSAParameters>> keys = ref RsaSigningKeyParams.AcidSigningKeys;
|
||||||
public Array32<AesKey> Package1Keys;
|
|
||||||
public Array32<AesKey> Package2Keys;
|
if (!keys.HasValue)
|
||||||
public Array32<Array3<AesKey>> KeyAreaKeys;
|
{
|
||||||
public Array32<AesKey> TitleKeks;
|
keys.Set(new Array2<RSAParameters>());
|
||||||
public AesXtsKey HeaderKey;
|
keys.Value[0] = CreateRsaParameters(in AcidSigningKeys[0]);
|
||||||
public AesKey ETicketRsaKek;
|
keys.Value[1] = CreateRsaParameters(in AcidSigningKeys[1]);
|
||||||
public AesKey SslRsaKek;
|
}
|
||||||
|
|
||||||
|
return keys.Value.Items;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public ref RSAParameters Package2SigningKeyParams
|
||||||
public struct DeviceKeys
|
|
||||||
{
|
{
|
||||||
public AesKey SecureBootKey;
|
get
|
||||||
public AesKey TsecKey;
|
{
|
||||||
public Array32<AesKey> KeyBlobKeys;
|
ref Optional<RSAParameters> keys = ref RsaSigningKeyParams.Package2SigningKey;
|
||||||
public Array32<AesKey> KeyBlobMacKeys;
|
|
||||||
public Array32<EncryptedKeyBlob> EncryptedKeyBlobs;
|
if (!keys.HasValue)
|
||||||
public AesKey DeviceKey;
|
{
|
||||||
public Array4<AesXtsKey> BisKeys;
|
keys.Set(new RSAParameters());
|
||||||
public Array2<AesKey> DeviceUniqueSaveMacKeys;
|
keys.Value = CreateRsaParameters(in Package2SigningKey);
|
||||||
public AesKey SeedUniqueSaveMacKey;
|
}
|
||||||
public AesKey SdCardEncryptionSeed;
|
|
||||||
public Array3<AesXtsKey> SdCardEncryptionKeys;
|
return ref keys.Value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public ref RSAParameters BetaNca0KeyAreaKeyParams
|
||||||
public struct RsaSigningKeys
|
|
||||||
{
|
{
|
||||||
public Array2<RsaKey> NcaHeaderSigningKeys;
|
get
|
||||||
public Array2<RsaKey> AcidSigningKeys;
|
{
|
||||||
public RsaKey Package2SigningKey;
|
ref Optional<RSAParameters> keys = ref _rsaKeyParams.BetaNca0KeyAreaKey;
|
||||||
|
|
||||||
|
if (!keys.HasValue)
|
||||||
|
{
|
||||||
|
keys.Set(CreateRsaParameters(in BetaNca0KeyAreaKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ref keys.Value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
public void SetSdSeed(ReadOnlySpan<byte> sdSeed)
|
||||||
public struct RsaKeys
|
|
||||||
{
|
{
|
||||||
public RsaFullKey BetaNca0KeyAreaKey;
|
if (sdSeed.Length != 0x10)
|
||||||
|
throw new ArgumentException("Sd card encryption seed must be 16 bytes long.");
|
||||||
|
|
||||||
|
sdSeed.CopyTo(SdCardEncryptionSeed);
|
||||||
|
DeriveSdCardKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMode(Mode mode) => _mode = mode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a new <see cref="KeySet"/> containing any keys that have been compiled into the library.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The created <see cref="KeySet"/>,</returns>
|
||||||
|
public static KeySet CreateDefaultKeySet()
|
||||||
|
{
|
||||||
|
return DefaultKeySet.CreateDefaultKeySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<KeyInfo> CreateKeyInfoList()
|
||||||
|
{
|
||||||
|
return DefaultKeySet.CreateKeyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeriveKeys(IProgressReport logger = null)
|
||||||
|
{
|
||||||
|
Mode originalMode = CurrentMode;
|
||||||
|
|
||||||
|
SetMode(Mode.Prod);
|
||||||
|
KeyDerivation.DeriveAllKeys(this, logger);
|
||||||
|
|
||||||
|
SetMode(Mode.Dev);
|
||||||
|
KeyDerivation.DeriveAllKeys(this, logger);
|
||||||
|
|
||||||
|
SetMode(originalMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeriveSdCardKeys() => KeyDerivation.DeriveSdCardKeys(this);
|
||||||
|
|
||||||
|
private static RSAParameters CreateRsaParameters(in RsaKey key)
|
||||||
|
{
|
||||||
|
return new RSAParameters
|
||||||
|
{
|
||||||
|
Exponent = key.PublicExponent.DataRo.ToArray(),
|
||||||
|
Modulus = key.Modulus.DataRo.ToArray()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RSAParameters CreateRsaParameters(in RsaFullKey key)
|
||||||
|
{
|
||||||
|
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()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct RsaSigningKeyParameters
|
||||||
|
{
|
||||||
|
public Optional<Array2<RSAParameters>> NcaHeaderSigningKeys;
|
||||||
|
public Optional<Array2<RSAParameters>> AcidSigningKeys;
|
||||||
|
public Optional<RSAParameters> Package2SigningKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct RsaKeyParameters
|
||||||
|
{
|
||||||
|
public Optional<RSAParameters> BetaNca0KeyAreaKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[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;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct RootKeys
|
||||||
|
{
|
||||||
|
// Mariko keys. The AES class keys are currently unused.
|
||||||
|
public AesKey MarikoKek;
|
||||||
|
public AesKey MarikoBek;
|
||||||
|
public Array12<AesKey> MarikoAesClassKeys;
|
||||||
|
|
||||||
|
// The key blobs are technically derived from the encrypted key blobs and their keys,
|
||||||
|
// however those keys are device-unique. The decrypted key blobs are basically the common root
|
||||||
|
// keys used by pre-6.2.0 Erista.
|
||||||
|
public Array32<KeyBlob> KeyBlobs;
|
||||||
|
|
||||||
|
// Used by TSEC in >= 6.2.0 Erista firmware
|
||||||
|
public Array32<AesKey> TsecAuthSignatures;
|
||||||
|
public AesKey TsecRootKek;
|
||||||
|
public AesKey Package1MacKek;
|
||||||
|
public AesKey Package1Kek;
|
||||||
|
|
||||||
|
// Derived by TSEC. This is the first public root key for >= 6.2.0 Erista
|
||||||
|
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 AesKey MasterKeySource;
|
||||||
|
public AesKey Package2KeySource;
|
||||||
|
public AesKey PerConsoleKeySource;
|
||||||
|
public AesKey RetailSpecificAesKeySource;
|
||||||
|
public AesKey BisKekSource;
|
||||||
|
public Array4<AesXtsKey> BisKeySources;
|
||||||
|
public AesKey AesKekGenerationSource;
|
||||||
|
public AesKey AesKeyGenerationSource;
|
||||||
|
public AesKey KeyAreaKeyApplicationSource;
|
||||||
|
public AesKey KeyAreaKeyOceanSource;
|
||||||
|
public AesKey KeyAreaKeySystemSource;
|
||||||
|
public AesKey TitleKekSource;
|
||||||
|
public AesKey HeaderKekSource;
|
||||||
|
public AesKey SdCardKekSource;
|
||||||
|
public Array3<AesXtsKey> SdCardKeySources;
|
||||||
|
public AesKey DeviceUniqueSaveMacKekSource;
|
||||||
|
public Array2<AesKey> DeviceUniqueSaveMacKeySources;
|
||||||
|
public AesKey SeedUniqueSaveMacKekSource;
|
||||||
|
public AesKey SeedUniqueSaveMacKeySource;
|
||||||
|
public AesXtsKey HeaderKeySource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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;
|
||||||
|
public Array32<AesKey> MasterKeys;
|
||||||
|
public Array32<AesKey> Package1MacKeys;
|
||||||
|
public Array32<AesKey> Package1Keys;
|
||||||
|
public Array32<AesKey> Package2Keys;
|
||||||
|
public Array32<Array3<AesKey>> KeyAreaKeys;
|
||||||
|
public Array32<AesKey> TitleKeks;
|
||||||
|
public AesXtsKey HeaderKey;
|
||||||
|
public AesKey ETicketRsaKek;
|
||||||
|
public AesKey SslRsaKek;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct DeviceKeys
|
||||||
|
{
|
||||||
|
public AesKey SecureBootKey;
|
||||||
|
public AesKey TsecKey;
|
||||||
|
public Array32<AesKey> KeyBlobKeys;
|
||||||
|
public Array32<AesKey> KeyBlobMacKeys;
|
||||||
|
public Array32<EncryptedKeyBlob> EncryptedKeyBlobs;
|
||||||
|
public AesKey DeviceKey;
|
||||||
|
public Array4<AesXtsKey> BisKeys;
|
||||||
|
public Array2<AesKey> DeviceUniqueSaveMacKeys;
|
||||||
|
public AesKey SeedUniqueSaveMacKey;
|
||||||
|
public AesKey SdCardEncryptionSeed;
|
||||||
|
public Array3<AesXtsKey> SdCardEncryptionKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct RsaSigningKeys
|
||||||
|
{
|
||||||
|
public Array2<RsaKey> NcaHeaderSigningKeys;
|
||||||
|
public Array2<RsaKey> AcidSigningKeys;
|
||||||
|
public RsaKey Package2SigningKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct RsaKeys
|
||||||
|
{
|
||||||
|
public RsaFullKey BetaNca0KeyAreaKey;
|
||||||
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
{
|
|
||||||
[AttributeUsage(AttributeTargets.Struct)]
|
|
||||||
public sealed class NonCopyableAttribute : Attribute { }
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Struct)]
|
[AttributeUsage(AttributeTargets.Struct)]
|
||||||
public sealed class NonCopyableDisposableAttribute : Attribute { }
|
public sealed class NonCopyableAttribute : Attribute { }
|
||||||
}
|
|
||||||
|
[AttributeUsage(AttributeTargets.Struct)]
|
||||||
|
public sealed class NonCopyableDisposableAttribute : Attribute { }
|
||||||
|
@ -1,52 +1,51 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common
|
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
|
||||||
{
|
{
|
||||||
// In order for the Visual Studio debugger to accurately display a struct, every offset
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding00;
|
||||||
// in the struct that is used for the debugger display must be part of a field.
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding08;
|
||||||
// These padding structs make it easier to accomplish that.
|
}
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
|
||||||
internal struct Padding10
|
[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 Padding00;
|
||||||
}
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding08;
|
||||||
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding10;
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding18;
|
||||||
internal struct Padding20
|
}
|
||||||
{
|
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding00;
|
[StructLayout(LayoutKind.Sequential, Size = 0x40)]
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding08;
|
internal struct Padding40
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding10;
|
{
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding18;
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding20 Padding00;
|
||||||
}
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding20 Padding20;
|
||||||
|
}
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x40)]
|
|
||||||
internal struct Padding40
|
[StructLayout(LayoutKind.Sequential, Size = 0x80)]
|
||||||
{
|
internal struct Padding80
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding20 Padding00;
|
{
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding20 Padding20;
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding40 Padding00;
|
||||||
}
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding40 Padding40;
|
||||||
|
}
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x80)]
|
|
||||||
internal struct Padding80
|
[StructLayout(LayoutKind.Sequential, Size = 0x100)]
|
||||||
{
|
internal struct Padding100
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding40 Padding00;
|
{
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding40 Padding40;
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding00;
|
||||||
}
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding80;
|
||||||
|
}
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x100)]
|
|
||||||
internal struct Padding100
|
[StructLayout(LayoutKind.Sequential, Size = 0x200)]
|
||||||
{
|
internal struct Padding200
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding00;
|
{
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding80;
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000;
|
||||||
}
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 0x200)]
|
|
||||||
internal struct Padding200
|
|
||||||
{
|
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000;
|
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,120 +5,119 @@ using LibHac.Fs;
|
|||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
[DebuggerDisplay("{ToString()}")]
|
||||||
|
internal ref struct PathBuilder
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{ToString()}")]
|
private Span<byte> _buffer;
|
||||||
internal ref struct PathBuilder
|
private int _pos;
|
||||||
|
|
||||||
|
public int Length
|
||||||
{
|
{
|
||||||
private Span<byte> _buffer;
|
get => _pos;
|
||||||
private int _pos;
|
set
|
||||||
|
|
||||||
public int Length
|
|
||||||
{
|
{
|
||||||
get => _pos;
|
Debug.Assert(value >= 0);
|
||||||
set
|
Debug.Assert(value <= Capacity);
|
||||||
{
|
_pos = value;
|
||||||
Debug.Assert(value >= 0);
|
|
||||||
Debug.Assert(value <= Capacity);
|
|
||||||
_pos = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Capacity => _buffer.Length - 1;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public PathBuilder(Span<byte> buffer)
|
|
||||||
{
|
|
||||||
_buffer = buffer;
|
|
||||||
_pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public Result Append(byte value)
|
|
||||||
{
|
|
||||||
int pos = _pos;
|
|
||||||
if (pos >= Capacity)
|
|
||||||
{
|
|
||||||
return ResultFs.TooLongPath.Log();
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer[pos] = value;
|
|
||||||
_pos = pos + 1;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public Result Append(ReadOnlySpan<byte> value)
|
|
||||||
{
|
|
||||||
int pos = _pos;
|
|
||||||
if (pos + value.Length >= Capacity)
|
|
||||||
{
|
|
||||||
return ResultFs.TooLongPath.Log();
|
|
||||||
}
|
|
||||||
|
|
||||||
value.CopyTo(_buffer.Slice(pos));
|
|
||||||
_pos = pos + value.Length;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public Result AppendWithPrecedingSeparator(byte value)
|
|
||||||
{
|
|
||||||
int pos = _pos;
|
|
||||||
if (pos + 1 >= Capacity)
|
|
||||||
{
|
|
||||||
// Append the separator if there's enough space
|
|
||||||
if (pos < Capacity)
|
|
||||||
{
|
|
||||||
_buffer[pos] = (byte)'/';
|
|
||||||
_pos = pos + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultFs.TooLongPath.Log();
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer[pos] = (byte)'/';
|
|
||||||
_buffer[pos + 1] = value;
|
|
||||||
_pos = pos + 2;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public Result GoUpLevels(int count)
|
|
||||||
{
|
|
||||||
Debug.Assert(count > 0);
|
|
||||||
|
|
||||||
int separators = 0;
|
|
||||||
int pos = _pos - 1;
|
|
||||||
|
|
||||||
for (; pos >= 0; pos--)
|
|
||||||
{
|
|
||||||
if (PathTools.IsDirectorySeparator(_buffer[pos]))
|
|
||||||
{
|
|
||||||
separators++;
|
|
||||||
|
|
||||||
if (separators == count) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (separators != count) return ResultFs.DirectoryUnobtainable.Log();
|
|
||||||
|
|
||||||
_pos = pos;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void Terminate()
|
|
||||||
{
|
|
||||||
if (_buffer.Length > _pos)
|
|
||||||
{
|
|
||||||
_buffer[_pos] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return StringUtils.Utf8ZToString(_buffer.Slice(0, Length));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int Capacity => _buffer.Length - 1;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public PathBuilder(Span<byte> buffer)
|
||||||
|
{
|
||||||
|
_buffer = buffer;
|
||||||
|
_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Result Append(byte value)
|
||||||
|
{
|
||||||
|
int pos = _pos;
|
||||||
|
if (pos >= Capacity)
|
||||||
|
{
|
||||||
|
return ResultFs.TooLongPath.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
_buffer[pos] = value;
|
||||||
|
_pos = pos + 1;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Result Append(ReadOnlySpan<byte> value)
|
||||||
|
{
|
||||||
|
int pos = _pos;
|
||||||
|
if (pos + value.Length >= Capacity)
|
||||||
|
{
|
||||||
|
return ResultFs.TooLongPath.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
value.CopyTo(_buffer.Slice(pos));
|
||||||
|
_pos = pos + value.Length;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Result AppendWithPrecedingSeparator(byte value)
|
||||||
|
{
|
||||||
|
int pos = _pos;
|
||||||
|
if (pos + 1 >= Capacity)
|
||||||
|
{
|
||||||
|
// Append the separator if there's enough space
|
||||||
|
if (pos < Capacity)
|
||||||
|
{
|
||||||
|
_buffer[pos] = (byte)'/';
|
||||||
|
_pos = pos + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultFs.TooLongPath.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
_buffer[pos] = (byte)'/';
|
||||||
|
_buffer[pos + 1] = value;
|
||||||
|
_pos = pos + 2;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Result GoUpLevels(int count)
|
||||||
|
{
|
||||||
|
Debug.Assert(count > 0);
|
||||||
|
|
||||||
|
int separators = 0;
|
||||||
|
int pos = _pos - 1;
|
||||||
|
|
||||||
|
for (; pos >= 0; pos--)
|
||||||
|
{
|
||||||
|
if (PathTools.IsDirectorySeparator(_buffer[pos]))
|
||||||
|
{
|
||||||
|
separators++;
|
||||||
|
|
||||||
|
if (separators == count) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (separators != count) return ResultFs.DirectoryUnobtainable.Log();
|
||||||
|
|
||||||
|
_pos = pos;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Terminate()
|
||||||
|
{
|
||||||
|
if (_buffer.Length > _pos)
|
||||||
|
{
|
||||||
|
_buffer[_pos] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return StringUtils.Utf8ZToString(_buffer.Slice(0, Length));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,129 +6,128 @@ using System;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A <see langword="struct"/> that can store a reference to a value of a specified type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of value to reference.</typeparam>
|
||||||
|
public readonly ref struct Ref<T>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A <see langword="struct"/> that can store a reference to a value of a specified type.
|
/// The 1-length <see cref="Span{T}"/> instance used to track the target <typeparamref name="T"/> value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of value to reference.</typeparam>
|
private readonly Span<T> _span;
|
||||||
public readonly ref struct Ref<T>
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Ref{T}"/> struct.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The reference to the target <typeparamref name="T"/> value.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Ref(ref T value)
|
||||||
{
|
{
|
||||||
/// <summary>
|
_span = MemoryMarshal.CreateSpan(ref value, 1);
|
||||||
/// The 1-length <see cref="Span{T}"/> instance used to track the target <typeparamref name="T"/> value.
|
|
||||||
/// </summary>
|
|
||||||
private readonly Span<T> _span;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Ref{T}"/> struct.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The reference to the target <typeparamref name="T"/> value.</param>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public Ref(ref T value)
|
|
||||||
{
|
|
||||||
_span = MemoryMarshal.CreateSpan(ref value, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Ref{T}"/> struct.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pointer">The pointer to the target value.</param>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public unsafe Ref(void* pointer)
|
|
||||||
: this(ref Unsafe.AsRef<T>(pointer))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the <typeparamref name="T"/> reference represented by the current <see cref="Ref{T}"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
public ref T Value
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get => ref MemoryMarshal.GetReference(_span);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a value that indicates whether the current <see cref="Ref{T}"/> is <see langword="null"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns><see langword="true"/> if the held reference is <see langword="null"/>;
|
|
||||||
/// otherwise <see langword="false"/>.</returns>
|
|
||||||
public bool IsNull
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get => Unsafe.IsNullRef(ref Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Implicitly gets the <typeparamref name="T"/> value from a given <see cref="Ref{T}"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="reference">The input <see cref="Ref{T}"/> instance.</param>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static implicit operator T(Ref<T> reference)
|
|
||||||
{
|
|
||||||
return reference.Value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A <see langword="struct"/> that can store a reference to a value of a specified type.
|
/// Initializes a new instance of the <see cref="Ref{T}"/> struct.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of value to reference.</typeparam>
|
/// <param name="pointer">The pointer to the target value.</param>
|
||||||
public readonly ref struct ReadOnlyRef<T>
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public unsafe Ref(void* pointer)
|
||||||
|
: this(ref Unsafe.AsRef<T>(pointer))
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The 1-length <see cref="ReadOnlySpan{T}"/> instance used to track the target <typeparamref name="T"/> value.
|
|
||||||
/// </summary>
|
|
||||||
private readonly ReadOnlySpan<T> _span;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Ref{T}"/> struct.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The reference to the target <typeparamref name="T"/> value.</param>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ReadOnlyRef(in T value)
|
|
||||||
{
|
|
||||||
_span = MemoryMarshal.CreateSpan(ref Unsafe.AsRef(in value), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Ref{T}"/> struct.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pointer">The pointer to the target value.</param>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public unsafe ReadOnlyRef(void* pointer)
|
|
||||||
: this(in Unsafe.AsRef<T>(pointer))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the <typeparamref name="T"/> reference represented by the current <see cref="Ref{T}"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
public ref readonly T Value
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get => ref MemoryMarshal.GetReference(_span);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a value that indicates whether the current <see cref="Ref{T}"/> is <see langword="null"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns><see langword="true"/> if the held reference is <see langword="null"/>;
|
|
||||||
/// otherwise <see langword="false"/>.</returns>
|
|
||||||
public bool IsNull
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get => Unsafe.IsNullRef(ref Unsafe.AsRef(in Value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Implicitly gets the <typeparamref name="T"/> value from a given <see cref="Ref{T}"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="reference">The input <see cref="Ref{T}"/> instance.</param>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static implicit operator T(ReadOnlyRef<T> reference)
|
|
||||||
{
|
|
||||||
return reference.Value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <typeparamref name="T"/> reference represented by the current <see cref="Ref{T}"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
public ref T Value
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => ref MemoryMarshal.GetReference(_span);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a value that indicates whether the current <see cref="Ref{T}"/> is <see langword="null"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see langword="true"/> if the held reference is <see langword="null"/>;
|
||||||
|
/// otherwise <see langword="false"/>.</returns>
|
||||||
|
public bool IsNull
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => Unsafe.IsNullRef(ref Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implicitly gets the <typeparamref name="T"/> value from a given <see cref="Ref{T}"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reference">The input <see cref="Ref{T}"/> instance.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static implicit operator T(Ref<T> reference)
|
||||||
|
{
|
||||||
|
return reference.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A <see langword="struct"/> that can store a reference to a value of a specified type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of value to reference.</typeparam>
|
||||||
|
public readonly ref struct ReadOnlyRef<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The 1-length <see cref="ReadOnlySpan{T}"/> instance used to track the target <typeparamref name="T"/> value.
|
||||||
|
/// </summary>
|
||||||
|
private readonly ReadOnlySpan<T> _span;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Ref{T}"/> struct.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The reference to the target <typeparamref name="T"/> value.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ReadOnlyRef(in T value)
|
||||||
|
{
|
||||||
|
_span = MemoryMarshal.CreateSpan(ref Unsafe.AsRef(in value), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Ref{T}"/> struct.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pointer">The pointer to the target value.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public unsafe ReadOnlyRef(void* pointer)
|
||||||
|
: this(in Unsafe.AsRef<T>(pointer))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <typeparamref name="T"/> reference represented by the current <see cref="Ref{T}"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
public ref readonly T Value
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => ref MemoryMarshal.GetReference(_span);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a value that indicates whether the current <see cref="Ref{T}"/> is <see langword="null"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see langword="true"/> if the held reference is <see langword="null"/>;
|
||||||
|
/// otherwise <see langword="false"/>.</returns>
|
||||||
|
public bool IsNull
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => Unsafe.IsNullRef(ref Unsafe.AsRef(in Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implicitly gets the <typeparamref name="T"/> value from a given <see cref="Ref{T}"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reference">The input <see cref="Ref{T}"/> instance.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static implicit operator T(ReadOnlyRef<T> reference)
|
||||||
|
{
|
||||||
|
return reference.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,40 +2,39 @@
|
|||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
public readonly ref struct RentedArray<T>
|
||||||
{
|
{
|
||||||
public readonly ref struct RentedArray<T>
|
// It's faster to create new smaller arrays than rent them
|
||||||
|
private const int RentThresholdBytes = 512;
|
||||||
|
private static int RentThresholdElements => RentThresholdBytes / Unsafe.SizeOf<T>();
|
||||||
|
|
||||||
|
private readonly Span<T> _span;
|
||||||
|
|
||||||
|
public T[] Array { get; }
|
||||||
|
public Span<T> Span => _span;
|
||||||
|
|
||||||
|
public RentedArray(int minimumSize)
|
||||||
{
|
{
|
||||||
// It's faster to create new smaller arrays than rent them
|
if (minimumSize >= RentThresholdElements)
|
||||||
private const int RentThresholdBytes = 512;
|
|
||||||
private static int RentThresholdElements => RentThresholdBytes / Unsafe.SizeOf<T>();
|
|
||||||
|
|
||||||
private readonly Span<T> _span;
|
|
||||||
|
|
||||||
public T[] Array { get; }
|
|
||||||
public Span<T> Span => _span;
|
|
||||||
|
|
||||||
public RentedArray(int minimumSize)
|
|
||||||
{
|
{
|
||||||
if (minimumSize >= RentThresholdElements)
|
Array = ArrayPool<T>.Shared.Rent(minimumSize);
|
||||||
{
|
}
|
||||||
Array = ArrayPool<T>.Shared.Rent(minimumSize);
|
else
|
||||||
}
|
{
|
||||||
else
|
Array = new T[minimumSize];
|
||||||
{
|
|
||||||
Array = new T[minimumSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
_span = Array.AsSpan(0, minimumSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
_span = Array.AsSpan(0, minimumSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Only return if array was rented
|
||||||
|
if (_span.Length >= RentThresholdElements)
|
||||||
{
|
{
|
||||||
// Only return if array was rented
|
ArrayPool<T>.Shared.Return(Array);
|
||||||
if (_span.Length >= RentThresholdElements)
|
|
||||||
{
|
|
||||||
ArrayPool<T>.Shared.Return(Array);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,86 +11,85 @@
|
|||||||
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
public static class ResultLibHac
|
||||||
{
|
{
|
||||||
public static class ResultLibHac
|
public const int ModuleLibHac = 428;
|
||||||
{
|
|
||||||
public const int ModuleLibHac = 428;
|
|
||||||
|
|
||||||
/// <summary>Error code: 2428-0001; Range: 1-49; Inner value: 0x3ac</summary>
|
/// <summary>Error code: 2428-0001; Range: 1-49; Inner value: 0x3ac</summary>
|
||||||
public static Result.Base InvalidArgument => new Result.Base(ModuleLibHac, 1, 49);
|
public static Result.Base InvalidArgument => new Result.Base(ModuleLibHac, 1, 49);
|
||||||
/// <summary>Error code: 2428-0002; Inner value: 0x5ac</summary>
|
/// <summary>Error code: 2428-0002; Inner value: 0x5ac</summary>
|
||||||
public static Result.Base NullArgument => new Result.Base(ModuleLibHac, 2);
|
public static Result.Base NullArgument => new Result.Base(ModuleLibHac, 2);
|
||||||
/// <summary>Error code: 2428-0003; Inner value: 0x7ac</summary>
|
/// <summary>Error code: 2428-0003; Inner value: 0x7ac</summary>
|
||||||
public static Result.Base ArgumentOutOfRange => new Result.Base(ModuleLibHac, 3);
|
public static Result.Base ArgumentOutOfRange => new Result.Base(ModuleLibHac, 3);
|
||||||
/// <summary>Error code: 2428-0004; Inner value: 0x9ac</summary>
|
/// <summary>Error code: 2428-0004; Inner value: 0x9ac</summary>
|
||||||
public static Result.Base BufferTooSmall => new Result.Base(ModuleLibHac, 4);
|
public static Result.Base BufferTooSmall => new Result.Base(ModuleLibHac, 4);
|
||||||
|
|
||||||
/// <summary>Error code: 2428-0051; Inner value: 0x67ac</summary>
|
/// <summary>Error code: 2428-0051; Inner value: 0x67ac</summary>
|
||||||
public static Result.Base ServiceNotInitialized => new Result.Base(ModuleLibHac, 51);
|
public static Result.Base ServiceNotInitialized => new Result.Base(ModuleLibHac, 51);
|
||||||
/// <summary>Error code: 2428-0101; Inner value: 0xcbac</summary>
|
/// <summary>Error code: 2428-0101; Inner value: 0xcbac</summary>
|
||||||
public static Result.Base NotImplemented => new Result.Base(ModuleLibHac, 101);
|
public static Result.Base NotImplemented => new Result.Base(ModuleLibHac, 101);
|
||||||
|
|
||||||
/// <summary>Error code: 2428-1000; Range: 1000-1999; Inner value: 0x7d1ac</summary>
|
/// <summary>Error code: 2428-1000; Range: 1000-1999; Inner value: 0x7d1ac</summary>
|
||||||
public static Result.Base InvalidData { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleLibHac, 1000, 1999); }
|
public static Result.Base InvalidData { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleLibHac, 1000, 1999); }
|
||||||
/// <summary>Error code: 2428-1001; Range: 1001-1019; Inner value: 0x7d3ac</summary>
|
/// <summary>Error code: 2428-1001; Range: 1001-1019; Inner value: 0x7d3ac</summary>
|
||||||
public static Result.Base InvalidInitialProcessData { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleLibHac, 1001, 1019); }
|
public static Result.Base InvalidInitialProcessData { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleLibHac, 1001, 1019); }
|
||||||
/// <summary>Error code: 2428-1002; Range: 1002-1009; Inner value: 0x7d5ac</summary>
|
/// <summary>Error code: 2428-1002; Range: 1002-1009; Inner value: 0x7d5ac</summary>
|
||||||
public static Result.Base InvalidKip { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleLibHac, 1002, 1009); }
|
public static Result.Base InvalidKip { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleLibHac, 1002, 1009); }
|
||||||
/// <summary>The size of the KIP file was smaller than expected.<br/>Error code: 2428-1003; Inner value: 0x7d7ac</summary>
|
/// <summary>The size of the KIP file was smaller than expected.<br/>Error code: 2428-1003; Inner value: 0x7d7ac</summary>
|
||||||
public static Result.Base InvalidKipFileSize => new Result.Base(ModuleLibHac, 1003);
|
public static Result.Base InvalidKipFileSize => new Result.Base(ModuleLibHac, 1003);
|
||||||
/// <summary>The magic value of the KIP file was not KIP1.<br/>Error code: 2428-1004; Inner value: 0x7d9ac</summary>
|
/// <summary>The magic value of the KIP file was not KIP1.<br/>Error code: 2428-1004; Inner value: 0x7d9ac</summary>
|
||||||
public static Result.Base InvalidKipMagic => new Result.Base(ModuleLibHac, 1004);
|
public static Result.Base InvalidKipMagic => new Result.Base(ModuleLibHac, 1004);
|
||||||
/// <summary>The size of the compressed KIP segment was smaller than expected.<br/>Error code: 2428-1005; Inner value: 0x7dbac</summary>
|
/// <summary>The size of the compressed KIP segment was smaller than expected.<br/>Error code: 2428-1005; Inner value: 0x7dbac</summary>
|
||||||
public static Result.Base InvalidKipSegmentSize => new Result.Base(ModuleLibHac, 1005);
|
public static Result.Base InvalidKipSegmentSize => new Result.Base(ModuleLibHac, 1005);
|
||||||
/// <summary>An error occurred while decompressing a KIP segment.<br/>Error code: 2428-1006; Inner value: 0x7ddac</summary>
|
/// <summary>An error occurred while decompressing a KIP segment.<br/>Error code: 2428-1006; Inner value: 0x7ddac</summary>
|
||||||
public static Result.Base KipSegmentDecompressionFailed => new Result.Base(ModuleLibHac, 1006);
|
public static Result.Base KipSegmentDecompressionFailed => new Result.Base(ModuleLibHac, 1006);
|
||||||
|
|
||||||
/// <summary>Error code: 2428-1010; Range: 1010-1019; Inner value: 0x7e5ac</summary>
|
/// <summary>Error code: 2428-1010; Range: 1010-1019; Inner value: 0x7e5ac</summary>
|
||||||
public static Result.Base InvalidIni { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleLibHac, 1010, 1019); }
|
public static Result.Base InvalidIni { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleLibHac, 1010, 1019); }
|
||||||
/// <summary>The size of the INI file was smaller than expected.<br/>Error code: 2428-1011; Inner value: 0x7e7ac</summary>
|
/// <summary>The size of the INI file was smaller than expected.<br/>Error code: 2428-1011; Inner value: 0x7e7ac</summary>
|
||||||
public static Result.Base InvalidIniFileSize => new Result.Base(ModuleLibHac, 1011);
|
public static Result.Base InvalidIniFileSize => new Result.Base(ModuleLibHac, 1011);
|
||||||
/// <summary>The magic value of the INI file was not INI1.<br/>Error code: 2428-1012; Inner value: 0x7e9ac</summary>
|
/// <summary>The magic value of the INI file was not INI1.<br/>Error code: 2428-1012; Inner value: 0x7e9ac</summary>
|
||||||
public static Result.Base InvalidIniMagic => new Result.Base(ModuleLibHac, 1012);
|
public static Result.Base InvalidIniMagic => new Result.Base(ModuleLibHac, 1012);
|
||||||
/// <summary>The INI had an invalid process count.<br/>Error code: 2428-1013; Inner value: 0x7ebac</summary>
|
/// <summary>The INI had an invalid process count.<br/>Error code: 2428-1013; Inner value: 0x7ebac</summary>
|
||||||
public static Result.Base InvalidIniProcessCount => new Result.Base(ModuleLibHac, 1013);
|
public static Result.Base InvalidIniProcessCount => new Result.Base(ModuleLibHac, 1013);
|
||||||
|
|
||||||
/// <summary>Error code: 2428-1020; Range: 1020-1039; Inner value: 0x7f9ac</summary>
|
/// <summary>Error code: 2428-1020; Range: 1020-1039; Inner value: 0x7f9ac</summary>
|
||||||
public static Result.Base InvalidPackage2 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleLibHac, 1020, 1039); }
|
public static Result.Base InvalidPackage2 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleLibHac, 1020, 1039); }
|
||||||
/// <summary>Error code: 2428-1021; Inner value: 0x7fbac</summary>
|
/// <summary>Error code: 2428-1021; Inner value: 0x7fbac</summary>
|
||||||
public static Result.Base InvalidPackage2HeaderSignature => new Result.Base(ModuleLibHac, 1021);
|
public static Result.Base InvalidPackage2HeaderSignature => new Result.Base(ModuleLibHac, 1021);
|
||||||
/// <summary>Error code: 2428-1022; Inner value: 0x7fdac</summary>
|
/// <summary>Error code: 2428-1022; Inner value: 0x7fdac</summary>
|
||||||
public static Result.Base InvalidPackage2MetaSizeA => new Result.Base(ModuleLibHac, 1022);
|
public static Result.Base InvalidPackage2MetaSizeA => new Result.Base(ModuleLibHac, 1022);
|
||||||
/// <summary>Error code: 2428-1023; Inner value: 0x7ffac</summary>
|
/// <summary>Error code: 2428-1023; Inner value: 0x7ffac</summary>
|
||||||
public static Result.Base InvalidPackage2MetaSizeB => new Result.Base(ModuleLibHac, 1023);
|
public static Result.Base InvalidPackage2MetaSizeB => new Result.Base(ModuleLibHac, 1023);
|
||||||
/// <summary>Error code: 2428-1024; Inner value: 0x801ac</summary>
|
/// <summary>Error code: 2428-1024; Inner value: 0x801ac</summary>
|
||||||
public static Result.Base InvalidPackage2MetaKeyGeneration => new Result.Base(ModuleLibHac, 1024);
|
public static Result.Base InvalidPackage2MetaKeyGeneration => new Result.Base(ModuleLibHac, 1024);
|
||||||
/// <summary>Error code: 2428-1025; Inner value: 0x803ac</summary>
|
/// <summary>Error code: 2428-1025; Inner value: 0x803ac</summary>
|
||||||
public static Result.Base InvalidPackage2MetaMagic => new Result.Base(ModuleLibHac, 1025);
|
public static Result.Base InvalidPackage2MetaMagic => new Result.Base(ModuleLibHac, 1025);
|
||||||
/// <summary>Error code: 2428-1026; Inner value: 0x805ac</summary>
|
/// <summary>Error code: 2428-1026; Inner value: 0x805ac</summary>
|
||||||
public static Result.Base InvalidPackage2MetaEntryPointAlignment => new Result.Base(ModuleLibHac, 1026);
|
public static Result.Base InvalidPackage2MetaEntryPointAlignment => new Result.Base(ModuleLibHac, 1026);
|
||||||
/// <summary>Error code: 2428-1027; Inner value: 0x807ac</summary>
|
/// <summary>Error code: 2428-1027; Inner value: 0x807ac</summary>
|
||||||
public static Result.Base InvalidPackage2MetaPayloadAlignment => new Result.Base(ModuleLibHac, 1027);
|
public static Result.Base InvalidPackage2MetaPayloadAlignment => new Result.Base(ModuleLibHac, 1027);
|
||||||
/// <summary>Error code: 2428-1028; Inner value: 0x809ac</summary>
|
/// <summary>Error code: 2428-1028; Inner value: 0x809ac</summary>
|
||||||
public static Result.Base InvalidPackage2MetaPayloadSizeAlignment => new Result.Base(ModuleLibHac, 1028);
|
public static Result.Base InvalidPackage2MetaPayloadSizeAlignment => new Result.Base(ModuleLibHac, 1028);
|
||||||
/// <summary>Error code: 2428-1029; Inner value: 0x80bac</summary>
|
/// <summary>Error code: 2428-1029; Inner value: 0x80bac</summary>
|
||||||
public static Result.Base InvalidPackage2MetaTotalSize => new Result.Base(ModuleLibHac, 1029);
|
public static Result.Base InvalidPackage2MetaTotalSize => new Result.Base(ModuleLibHac, 1029);
|
||||||
/// <summary>Error code: 2428-1030; Inner value: 0x80dac</summary>
|
/// <summary>Error code: 2428-1030; Inner value: 0x80dac</summary>
|
||||||
public static Result.Base InvalidPackage2MetaPayloadSize => new Result.Base(ModuleLibHac, 1030);
|
public static Result.Base InvalidPackage2MetaPayloadSize => new Result.Base(ModuleLibHac, 1030);
|
||||||
/// <summary>Error code: 2428-1031; Inner value: 0x80fac</summary>
|
/// <summary>Error code: 2428-1031; Inner value: 0x80fac</summary>
|
||||||
public static Result.Base InvalidPackage2MetaPayloadsOverlap => new Result.Base(ModuleLibHac, 1031);
|
public static Result.Base InvalidPackage2MetaPayloadsOverlap => new Result.Base(ModuleLibHac, 1031);
|
||||||
/// <summary>Error code: 2428-1032; Inner value: 0x811ac</summary>
|
/// <summary>Error code: 2428-1032; Inner value: 0x811ac</summary>
|
||||||
public static Result.Base InvalidPackage2MetaEntryPointNotFound => new Result.Base(ModuleLibHac, 1032);
|
public static Result.Base InvalidPackage2MetaEntryPointNotFound => new Result.Base(ModuleLibHac, 1032);
|
||||||
/// <summary>Error code: 2428-1033; Inner value: 0x813ac</summary>
|
/// <summary>Error code: 2428-1033; Inner value: 0x813ac</summary>
|
||||||
public static Result.Base InvalidPackage2PayloadCorrupted => new Result.Base(ModuleLibHac, 1033);
|
public static Result.Base InvalidPackage2PayloadCorrupted => new Result.Base(ModuleLibHac, 1033);
|
||||||
|
|
||||||
/// <summary>Error code: 2428-1040; Range: 1040-1059; Inner value: 0x821ac</summary>
|
/// <summary>Error code: 2428-1040; Range: 1040-1059; Inner value: 0x821ac</summary>
|
||||||
public static Result.Base InvalidPackage1 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleLibHac, 1040, 1059); }
|
public static Result.Base InvalidPackage1 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleLibHac, 1040, 1059); }
|
||||||
/// <summary>Error code: 2428-1041; Inner value: 0x823ac</summary>
|
/// <summary>Error code: 2428-1041; Inner value: 0x823ac</summary>
|
||||||
public static Result.Base InvalidPackage1SectionSize => new Result.Base(ModuleLibHac, 1041);
|
public static Result.Base InvalidPackage1SectionSize => new Result.Base(ModuleLibHac, 1041);
|
||||||
/// <summary>Error code: 2428-1042; Inner value: 0x825ac</summary>
|
/// <summary>Error code: 2428-1042; Inner value: 0x825ac</summary>
|
||||||
public static Result.Base InvalidPackage1MarikoBodySize => new Result.Base(ModuleLibHac, 1042);
|
public static Result.Base InvalidPackage1MarikoBodySize => new Result.Base(ModuleLibHac, 1042);
|
||||||
/// <summary>Error code: 2428-1043; Inner value: 0x827ac</summary>
|
/// <summary>Error code: 2428-1043; Inner value: 0x827ac</summary>
|
||||||
public static Result.Base InvalidPackage1Pk11Size => new Result.Base(ModuleLibHac, 1043);
|
public static Result.Base InvalidPackage1Pk11Size => new Result.Base(ModuleLibHac, 1043);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
{
|
|
||||||
public static class Shared
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static T Move<T>(ref T value)
|
|
||||||
{
|
|
||||||
T tmp = value;
|
|
||||||
value = default;
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
public static class Shared
|
||||||
public static void Move<T>(out T dest, ref T value)
|
{
|
||||||
{
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
dest = value;
|
public static T Move<T>(ref T value)
|
||||||
value = default;
|
{
|
||||||
}
|
T tmp = value;
|
||||||
|
value = default;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void Move<T>(out T dest, ref T value)
|
||||||
|
{
|
||||||
|
dest = value;
|
||||||
|
value = default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,235 +6,283 @@ using LibHac.Diag;
|
|||||||
|
|
||||||
#pragma warning disable LH0001
|
#pragma warning disable LH0001
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
public static class SharedRefExtensions
|
||||||
{
|
{
|
||||||
public static class SharedRefExtensions
|
// ReSharper disable once EntityNameCapturedOnly.Global
|
||||||
|
public static ref SharedRef<T> Ref<T>(this in SharedRef<T> value) where T : class, IDisposable
|
||||||
{
|
{
|
||||||
// ReSharper disable once EntityNameCapturedOnly.Global
|
IL.Emit.Ldarg(nameof(value));
|
||||||
public static ref SharedRef<T> Ref<T>(this in SharedRef<T> value) where T : class, IDisposable
|
IL.Emit.Ret();
|
||||||
|
throw IL.Unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once EntityNameCapturedOnly.Global
|
||||||
|
public static ref WeakRef<T> Ref<T>(this in WeakRef<T> value) where T : class, IDisposable
|
||||||
|
{
|
||||||
|
IL.Emit.Ldarg(nameof(value));
|
||||||
|
IL.Emit.Ret();
|
||||||
|
throw IL.Unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class RefCount
|
||||||
|
{
|
||||||
|
private int _count;
|
||||||
|
private int _weakCount;
|
||||||
|
private IDisposable _value;
|
||||||
|
|
||||||
|
public RefCount(IDisposable value)
|
||||||
|
{
|
||||||
|
_count = 1;
|
||||||
|
_weakCount = 1;
|
||||||
|
_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UseCount()
|
||||||
|
{
|
||||||
|
return _count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Increment()
|
||||||
|
{
|
||||||
|
// This function shouldn't ever be called after the ref count is reduced to zero.
|
||||||
|
// SharedRef clearing their RefCountBase field after decrementing the ref count *should* already
|
||||||
|
// prevent this if everything works properly.
|
||||||
|
Assert.SdkRequiresGreater(_count, 0);
|
||||||
|
|
||||||
|
Interlocked.Increment(ref _count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void IncrementWeak()
|
||||||
|
{
|
||||||
|
Assert.SdkRequiresGreater(_count, 0);
|
||||||
|
|
||||||
|
Interlocked.Increment(ref _weakCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IncrementIfNotZero()
|
||||||
|
{
|
||||||
|
int count = Volatile.Read(ref _count);
|
||||||
|
|
||||||
|
while (count != 0)
|
||||||
{
|
{
|
||||||
IL.Emit.Ldarg(nameof(value));
|
int oldValue = Interlocked.CompareExchange(ref _count, count + 1, count);
|
||||||
IL.Emit.Ret();
|
if (oldValue == count)
|
||||||
throw IL.Unreachable();
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = oldValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReSharper disable once EntityNameCapturedOnly.Global
|
return false;
|
||||||
public static ref WeakRef<T> Ref<T>(this in WeakRef<T> value) where T : class, IDisposable
|
}
|
||||||
|
|
||||||
|
public void Decrement()
|
||||||
|
{
|
||||||
|
if (Interlocked.Decrement(ref _count) == 0)
|
||||||
{
|
{
|
||||||
IL.Emit.Ldarg(nameof(value));
|
Destroy();
|
||||||
IL.Emit.Ret();
|
DecrementWeak();
|
||||||
throw IL.Unreachable();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class RefCount
|
public void DecrementWeak()
|
||||||
{
|
{
|
||||||
private int _count;
|
if (Interlocked.Decrement(ref _weakCount) == 0)
|
||||||
private int _weakCount;
|
|
||||||
private IDisposable _value;
|
|
||||||
|
|
||||||
public RefCount(IDisposable value)
|
|
||||||
{
|
{
|
||||||
_count = 1;
|
// Deallocate
|
||||||
_weakCount = 1;
|
|
||||||
_value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int UseCount()
|
|
||||||
{
|
|
||||||
return _count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Increment()
|
|
||||||
{
|
|
||||||
// This function shouldn't ever be called after the ref count is reduced to zero.
|
|
||||||
// SharedRef clearing their RefCountBase field after decrementing the ref count *should* already
|
|
||||||
// prevent this if everything works properly.
|
|
||||||
Assert.SdkRequiresGreater(_count, 0);
|
|
||||||
|
|
||||||
Interlocked.Increment(ref _count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void IncrementWeak()
|
|
||||||
{
|
|
||||||
Assert.SdkRequiresGreater(_count, 0);
|
|
||||||
|
|
||||||
Interlocked.Increment(ref _weakCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IncrementIfNotZero()
|
|
||||||
{
|
|
||||||
int count = Volatile.Read(ref _count);
|
|
||||||
|
|
||||||
while (count != 0)
|
|
||||||
{
|
|
||||||
int oldValue = Interlocked.CompareExchange(ref _count, count + 1, count);
|
|
||||||
if (oldValue == count)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
count = oldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Decrement()
|
|
||||||
{
|
|
||||||
if (Interlocked.Decrement(ref _count) == 0)
|
|
||||||
{
|
|
||||||
Destroy();
|
|
||||||
DecrementWeak();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DecrementWeak()
|
|
||||||
{
|
|
||||||
if (Interlocked.Decrement(ref _weakCount) == 0)
|
|
||||||
{
|
|
||||||
// Deallocate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Destroy()
|
|
||||||
{
|
|
||||||
if (_value is not null)
|
|
||||||
{
|
|
||||||
_value.Dispose();
|
|
||||||
_value = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[NonCopyableDisposable]
|
private void Destroy()
|
||||||
public struct SharedRef<T> : IDisposable where T : class, IDisposable
|
|
||||||
{
|
{
|
||||||
// SharedRef and WeakRef should share a base type, but struct inheritance doesn't exist in C#.
|
if (_value is not null)
|
||||||
// This presents a problem because C# also doesn't have friend classes, these two types need to
|
|
||||||
// access each other's fields and we'd rather the fields' visibility stay private. Because the
|
|
||||||
// two types have the same layout we can hack around this with some Unsafe.As shenanigans.
|
|
||||||
private T _value;
|
|
||||||
private RefCount _refCount;
|
|
||||||
|
|
||||||
public SharedRef(T value)
|
|
||||||
{
|
{
|
||||||
_value = value;
|
_value.Dispose();
|
||||||
_refCount = new RefCount(value);
|
_value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[NonCopyableDisposable]
|
||||||
|
public struct SharedRef<T> : IDisposable where T : class, IDisposable
|
||||||
|
{
|
||||||
|
// SharedRef and WeakRef should share a base type, but struct inheritance doesn't exist in C#.
|
||||||
|
// This presents a problem because C# also doesn't have friend classes, these two types need to
|
||||||
|
// access each other's fields and we'd rather the fields' visibility stay private. Because the
|
||||||
|
// two types have the same layout we can hack around this with some Unsafe.As shenanigans.
|
||||||
|
private T _value;
|
||||||
|
private RefCount _refCount;
|
||||||
|
|
||||||
|
public SharedRef(T value)
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
_refCount = new RefCount(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("This method should never be manually called. Use the Destroy method instead.", true)]
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// This function shouldn't be called manually and should always be called at the end of a using block.
|
||||||
|
// This means we don't need to clear any fields because we're going out of scope anyway.
|
||||||
|
_refCount?.Decrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to manually dispose the <see cref="SharedRef{T}"/> from the Dispose methods of other types.
|
||||||
|
/// </summary>
|
||||||
|
public void Destroy()
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly T Get => _value;
|
||||||
|
public readonly bool HasValue => Get is not null;
|
||||||
|
public readonly int UseCount => _refCount?.UseCount() ?? 0;
|
||||||
|
|
||||||
|
public static SharedRef<T> CreateMove<TFrom>(ref SharedRef<TFrom> other) where TFrom : class, T
|
||||||
|
{
|
||||||
|
var sharedRef = new SharedRef<T>();
|
||||||
|
|
||||||
|
sharedRef._value = Unsafe.As<TFrom, T>(ref other._value);
|
||||||
|
sharedRef._refCount = other._refCount;
|
||||||
|
|
||||||
|
other._value = null;
|
||||||
|
other._refCount = null;
|
||||||
|
|
||||||
|
return sharedRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SharedRef<T> CreateCopy<TFrom>(in SharedRef<TFrom> other) where TFrom : class, T
|
||||||
|
{
|
||||||
|
var sharedRef = new SharedRef<T>();
|
||||||
|
|
||||||
|
sharedRef._value = Unsafe.As<TFrom, T>(ref other.Ref()._value);
|
||||||
|
sharedRef._refCount = other._refCount;
|
||||||
|
|
||||||
|
sharedRef._refCount?.Increment();
|
||||||
|
|
||||||
|
return sharedRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SharedRef<T> Create<TFrom>(in WeakRef<TFrom> other) where TFrom : class, T
|
||||||
|
{
|
||||||
|
ref SharedRef<TFrom> otherShared = ref Unsafe.As<WeakRef<TFrom>, SharedRef<TFrom>>(ref other.Ref());
|
||||||
|
|
||||||
|
var sharedRef = new SharedRef<T>();
|
||||||
|
|
||||||
|
if (otherShared._refCount is not null && otherShared._refCount.IncrementIfNotZero())
|
||||||
|
{
|
||||||
|
sharedRef._value = Unsafe.As<TFrom, T>(ref otherShared._value);
|
||||||
|
sharedRef._refCount = otherShared._refCount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ThrowBadWeakPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("This method should never be manually called. Use the Destroy method instead.", true)]
|
return sharedRef;
|
||||||
public void Dispose()
|
}
|
||||||
|
|
||||||
|
public static SharedRef<T> Create<TFrom>(ref UniqueRef<TFrom> other) where TFrom : class, T
|
||||||
|
{
|
||||||
|
var sharedRef = new SharedRef<T>();
|
||||||
|
TFrom value = other.Get;
|
||||||
|
|
||||||
|
if (value is not null)
|
||||||
{
|
{
|
||||||
// This function shouldn't be called manually and should always be called at the end of a using block.
|
sharedRef._value = value;
|
||||||
// This means we don't need to clear any fields because we're going out of scope anyway.
|
sharedRef._refCount = new RefCount(value);
|
||||||
_refCount?.Decrement();
|
other.Release();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sharedRef._value = null;
|
||||||
|
sharedRef._refCount = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
return sharedRef;
|
||||||
/// Used to manually dispose the <see cref="SharedRef{T}"/> from the Dispose methods of other types.
|
}
|
||||||
/// </summary>
|
|
||||||
public void Destroy()
|
public void Swap(ref SharedRef<T> other)
|
||||||
|
{
|
||||||
|
(other._value, _value) = (_value, other._value);
|
||||||
|
(other._refCount, _refCount) = (_refCount, other._refCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_value = null;
|
||||||
|
RefCount oldRefCount = _refCount;
|
||||||
|
_refCount = null;
|
||||||
|
|
||||||
|
oldRefCount?.Decrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset(T value)
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
RefCount oldRefCount = _refCount;
|
||||||
|
_refCount = new RefCount(value);
|
||||||
|
|
||||||
|
oldRefCount?.Decrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetByMove<TFrom>(ref SharedRef<TFrom> other) where TFrom : class, T
|
||||||
|
{
|
||||||
|
RefCount oldRefCount = _refCount;
|
||||||
|
|
||||||
|
_value = Unsafe.As<TFrom, T>(ref other._value);
|
||||||
|
_refCount = other._refCount;
|
||||||
|
|
||||||
|
other._value = null;
|
||||||
|
other._refCount = null;
|
||||||
|
|
||||||
|
oldRefCount?.Decrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetByCopy<TFrom>(in SharedRef<TFrom> other) where TFrom : class, T
|
||||||
|
{
|
||||||
|
RefCount oldRefCount = _refCount;
|
||||||
|
RefCount otherRef = other._refCount;
|
||||||
|
|
||||||
|
otherRef?.Increment();
|
||||||
|
|
||||||
|
_value = Unsafe.As<TFrom, T>(ref other.Ref()._value);
|
||||||
|
_refCount = otherRef;
|
||||||
|
|
||||||
|
oldRefCount?.Decrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set<TFrom>(ref UniqueRef<TFrom> other) where TFrom : class, T
|
||||||
|
{
|
||||||
|
RefCount oldRefCount = _refCount;
|
||||||
|
TFrom otherValue = other.Release();
|
||||||
|
|
||||||
|
if (otherValue is not null)
|
||||||
{
|
{
|
||||||
Reset();
|
_value = Unsafe.As<TFrom, T>(ref otherValue);
|
||||||
|
_refCount = new RefCount(otherValue);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
public readonly T Get => _value;
|
|
||||||
public readonly bool HasValue => Get is not null;
|
|
||||||
public readonly int UseCount => _refCount?.UseCount() ?? 0;
|
|
||||||
|
|
||||||
public static SharedRef<T> CreateMove<TFrom>(ref SharedRef<TFrom> other) where TFrom : class, T
|
|
||||||
{
|
|
||||||
var sharedRef = new SharedRef<T>();
|
|
||||||
|
|
||||||
sharedRef._value = Unsafe.As<TFrom, T>(ref other._value);
|
|
||||||
sharedRef._refCount = other._refCount;
|
|
||||||
|
|
||||||
other._value = null;
|
|
||||||
other._refCount = null;
|
|
||||||
|
|
||||||
return sharedRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SharedRef<T> CreateCopy<TFrom>(in SharedRef<TFrom> other) where TFrom : class, T
|
|
||||||
{
|
|
||||||
var sharedRef = new SharedRef<T>();
|
|
||||||
|
|
||||||
sharedRef._value = Unsafe.As<TFrom, T>(ref other.Ref()._value);
|
|
||||||
sharedRef._refCount = other._refCount;
|
|
||||||
|
|
||||||
sharedRef._refCount?.Increment();
|
|
||||||
|
|
||||||
return sharedRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SharedRef<T> Create<TFrom>(in WeakRef<TFrom> other) where TFrom : class, T
|
|
||||||
{
|
|
||||||
ref SharedRef<TFrom> otherShared = ref Unsafe.As<WeakRef<TFrom>, SharedRef<TFrom>>(ref other.Ref());
|
|
||||||
|
|
||||||
var sharedRef = new SharedRef<T>();
|
|
||||||
|
|
||||||
if (otherShared._refCount is not null && otherShared._refCount.IncrementIfNotZero())
|
|
||||||
{
|
|
||||||
sharedRef._value = Unsafe.As<TFrom, T>(ref otherShared._value);
|
|
||||||
sharedRef._refCount = otherShared._refCount;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ThrowBadWeakPtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
return sharedRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SharedRef<T> Create<TFrom>(ref UniqueRef<TFrom> other) where TFrom : class, T
|
|
||||||
{
|
|
||||||
var sharedRef = new SharedRef<T>();
|
|
||||||
TFrom value = other.Get;
|
|
||||||
|
|
||||||
if (value is not null)
|
|
||||||
{
|
|
||||||
sharedRef._value = value;
|
|
||||||
sharedRef._refCount = new RefCount(value);
|
|
||||||
other.Release();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sharedRef._value = null;
|
|
||||||
sharedRef._refCount = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sharedRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Swap(ref SharedRef<T> other)
|
|
||||||
{
|
|
||||||
(other._value, _value) = (_value, other._value);
|
|
||||||
(other._refCount, _refCount) = (_refCount, other._refCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
{
|
||||||
_value = null;
|
_value = null;
|
||||||
RefCount oldRefCount = _refCount;
|
|
||||||
_refCount = null;
|
_refCount = null;
|
||||||
|
|
||||||
oldRefCount?.Decrement();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset(T value)
|
oldRefCount?.Decrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryCastSet<TFrom>(ref SharedRef<TFrom> other) where TFrom : class, IDisposable
|
||||||
|
{
|
||||||
|
RefCount oldRefCount = _refCount;
|
||||||
|
TFrom otherValue = other.Get;
|
||||||
|
|
||||||
|
if (otherValue is T)
|
||||||
{
|
{
|
||||||
_value = value;
|
|
||||||
RefCount oldRefCount = _refCount;
|
|
||||||
_refCount = new RefCount(value);
|
|
||||||
|
|
||||||
oldRefCount?.Decrement();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetByMove<TFrom>(ref SharedRef<TFrom> other) where TFrom : class, T
|
|
||||||
{
|
|
||||||
RefCount oldRefCount = _refCount;
|
|
||||||
|
|
||||||
_value = Unsafe.As<TFrom, T>(ref other._value);
|
_value = Unsafe.As<TFrom, T>(ref other._value);
|
||||||
_refCount = other._refCount;
|
_refCount = other._refCount;
|
||||||
|
|
||||||
@ -242,200 +290,151 @@ namespace LibHac.Common
|
|||||||
other._refCount = null;
|
other._refCount = null;
|
||||||
|
|
||||||
oldRefCount?.Decrement();
|
oldRefCount?.Decrement();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetByCopy<TFrom>(in SharedRef<TFrom> other) where TFrom : class, T
|
return false;
|
||||||
{
|
|
||||||
RefCount oldRefCount = _refCount;
|
|
||||||
RefCount otherRef = other._refCount;
|
|
||||||
|
|
||||||
otherRef?.Increment();
|
|
||||||
|
|
||||||
_value = Unsafe.As<TFrom, T>(ref other.Ref()._value);
|
|
||||||
_refCount = otherRef;
|
|
||||||
|
|
||||||
oldRefCount?.Decrement();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Set<TFrom>(ref UniqueRef<TFrom> other) where TFrom : class, T
|
|
||||||
{
|
|
||||||
RefCount oldRefCount = _refCount;
|
|
||||||
TFrom otherValue = other.Release();
|
|
||||||
|
|
||||||
if (otherValue is not null)
|
|
||||||
{
|
|
||||||
_value = Unsafe.As<TFrom, T>(ref otherValue);
|
|
||||||
_refCount = new RefCount(otherValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_value = null;
|
|
||||||
_refCount = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
oldRefCount?.Decrement();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryCastSet<TFrom>(ref SharedRef<TFrom> other) where TFrom : class, IDisposable
|
|
||||||
{
|
|
||||||
RefCount oldRefCount = _refCount;
|
|
||||||
TFrom otherValue = other.Get;
|
|
||||||
|
|
||||||
if (otherValue is T)
|
|
||||||
{
|
|
||||||
_value = Unsafe.As<TFrom, T>(ref other._value);
|
|
||||||
_refCount = other._refCount;
|
|
||||||
|
|
||||||
other._value = null;
|
|
||||||
other._refCount = null;
|
|
||||||
|
|
||||||
oldRefCount?.Decrement();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ThrowBadWeakPtr()
|
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(string.Empty, "bad_weak_ptr");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[NonCopyableDisposable]
|
private static void ThrowBadWeakPtr()
|
||||||
public struct WeakRef<T> : IDisposable where T : class, IDisposable
|
|
||||||
{
|
{
|
||||||
private T _value;
|
throw new ObjectDisposedException(string.Empty, "bad_weak_ptr");
|
||||||
private RefCount _refCount;
|
}
|
||||||
|
}
|
||||||
public WeakRef(in SharedRef<T> other)
|
|
||||||
{
|
[NonCopyableDisposable]
|
||||||
this = Create(in other);
|
public struct WeakRef<T> : IDisposable where T : class, IDisposable
|
||||||
}
|
{
|
||||||
|
private T _value;
|
||||||
[Obsolete("This method should never be manually called. Use the Destroy method instead.", true)]
|
private RefCount _refCount;
|
||||||
public void Dispose()
|
|
||||||
{
|
public WeakRef(in SharedRef<T> other)
|
||||||
_refCount?.DecrementWeak();
|
{
|
||||||
}
|
this = Create(in other);
|
||||||
|
}
|
||||||
// A copy of Dispose so we can call it ourselves inside the struct
|
|
||||||
private void DisposeInternal()
|
[Obsolete("This method should never be manually called. Use the Destroy method instead.", true)]
|
||||||
{
|
public void Dispose()
|
||||||
_refCount?.DecrementWeak();
|
{
|
||||||
}
|
_refCount?.DecrementWeak();
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// Used to manually dispose the <see cref="WeakRef{T}"/> from the Dispose methods of other types.
|
// A copy of Dispose so we can call it ourselves inside the struct
|
||||||
/// </summary>
|
private void DisposeInternal()
|
||||||
public void Destroy()
|
{
|
||||||
{
|
_refCount?.DecrementWeak();
|
||||||
Reset();
|
}
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
public readonly int UseCount => _refCount?.UseCount() ?? 0;
|
/// Used to manually dispose the <see cref="WeakRef{T}"/> from the Dispose methods of other types.
|
||||||
public readonly bool Expired => UseCount == 0;
|
/// </summary>
|
||||||
|
public void Destroy()
|
||||||
public static WeakRef<T> CreateMove<TFrom>(ref WeakRef<TFrom> other) where TFrom : class, T
|
{
|
||||||
{
|
Reset();
|
||||||
var weakRef = new WeakRef<T>();
|
}
|
||||||
|
|
||||||
weakRef._value = Unsafe.As<TFrom, T>(ref other._value);
|
public readonly int UseCount => _refCount?.UseCount() ?? 0;
|
||||||
weakRef._refCount = other._refCount;
|
public readonly bool Expired => UseCount == 0;
|
||||||
|
|
||||||
other._value = null;
|
public static WeakRef<T> CreateMove<TFrom>(ref WeakRef<TFrom> other) where TFrom : class, T
|
||||||
other._refCount = null;
|
{
|
||||||
|
var weakRef = new WeakRef<T>();
|
||||||
return weakRef;
|
|
||||||
}
|
weakRef._value = Unsafe.As<TFrom, T>(ref other._value);
|
||||||
|
weakRef._refCount = other._refCount;
|
||||||
public static WeakRef<T> CreateCopy<TFrom>(in WeakRef<TFrom> other) where TFrom : class, T
|
|
||||||
{
|
other._value = null;
|
||||||
var weakRef = new WeakRef<T>();
|
other._refCount = null;
|
||||||
|
|
||||||
if (other._refCount is not null)
|
return weakRef;
|
||||||
{
|
}
|
||||||
weakRef._refCount = other._refCount;
|
|
||||||
weakRef._refCount.IncrementWeak();
|
public static WeakRef<T> CreateCopy<TFrom>(in WeakRef<TFrom> other) where TFrom : class, T
|
||||||
|
{
|
||||||
if (weakRef._refCount.IncrementIfNotZero())
|
var weakRef = new WeakRef<T>();
|
||||||
{
|
|
||||||
weakRef._value = Unsafe.As<TFrom, T>(ref other.Ref()._value);
|
if (other._refCount is not null)
|
||||||
weakRef._refCount.Decrement();
|
{
|
||||||
}
|
weakRef._refCount = other._refCount;
|
||||||
}
|
weakRef._refCount.IncrementWeak();
|
||||||
|
|
||||||
return weakRef;
|
if (weakRef._refCount.IncrementIfNotZero())
|
||||||
}
|
{
|
||||||
|
weakRef._value = Unsafe.As<TFrom, T>(ref other.Ref()._value);
|
||||||
public static WeakRef<T> Create<TFrom>(in SharedRef<TFrom> other) where TFrom : class, T
|
weakRef._refCount.Decrement();
|
||||||
{
|
}
|
||||||
ref WeakRef<TFrom> otherWeak = ref Unsafe.As<SharedRef<TFrom>, WeakRef<TFrom>>(ref other.Ref());
|
}
|
||||||
|
|
||||||
var weakRef = new WeakRef<T>();
|
return weakRef;
|
||||||
|
}
|
||||||
if (otherWeak._refCount is not null)
|
|
||||||
{
|
public static WeakRef<T> Create<TFrom>(in SharedRef<TFrom> other) where TFrom : class, T
|
||||||
weakRef._value = Unsafe.As<TFrom, T>(ref otherWeak._value);
|
{
|
||||||
weakRef._refCount = otherWeak._refCount;
|
ref WeakRef<TFrom> otherWeak = ref Unsafe.As<SharedRef<TFrom>, WeakRef<TFrom>>(ref other.Ref());
|
||||||
|
|
||||||
weakRef._refCount.IncrementWeak();
|
var weakRef = new WeakRef<T>();
|
||||||
}
|
|
||||||
else
|
if (otherWeak._refCount is not null)
|
||||||
{
|
{
|
||||||
weakRef._value = null;
|
weakRef._value = Unsafe.As<TFrom, T>(ref otherWeak._value);
|
||||||
weakRef._refCount = null;
|
weakRef._refCount = otherWeak._refCount;
|
||||||
}
|
|
||||||
|
weakRef._refCount.IncrementWeak();
|
||||||
return weakRef;
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
public void Swap(ref WeakRef<T> other)
|
weakRef._value = null;
|
||||||
{
|
weakRef._refCount = null;
|
||||||
(other._value, _value) = (_value, other._value);
|
}
|
||||||
(other._refCount, _refCount) = (_refCount, other._refCount);
|
|
||||||
}
|
return weakRef;
|
||||||
|
}
|
||||||
public void Reset()
|
|
||||||
{
|
public void Swap(ref WeakRef<T> other)
|
||||||
var temp = new WeakRef<T>();
|
{
|
||||||
Swap(ref temp);
|
(other._value, _value) = (_value, other._value);
|
||||||
temp.DisposeInternal();
|
(other._refCount, _refCount) = (_refCount, other._refCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetMove<TFrom>(ref WeakRef<TFrom> other) where TFrom : class, T
|
public void Reset()
|
||||||
{
|
{
|
||||||
WeakRef<T> temp = CreateMove(ref other);
|
var temp = new WeakRef<T>();
|
||||||
Swap(ref temp);
|
Swap(ref temp);
|
||||||
temp.DisposeInternal();
|
temp.DisposeInternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCopy<TFrom>(in WeakRef<TFrom> other) where TFrom : class, T
|
public void SetMove<TFrom>(ref WeakRef<TFrom> other) where TFrom : class, T
|
||||||
{
|
{
|
||||||
WeakRef<T> temp = CreateCopy(in other);
|
WeakRef<T> temp = CreateMove(ref other);
|
||||||
Swap(ref temp);
|
Swap(ref temp);
|
||||||
temp.DisposeInternal();
|
temp.DisposeInternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Set<TFrom>(in SharedRef<TFrom> other) where TFrom : class, T
|
public void SetCopy<TFrom>(in WeakRef<TFrom> other) where TFrom : class, T
|
||||||
{
|
{
|
||||||
WeakRef<T> temp = Create(in other);
|
WeakRef<T> temp = CreateCopy(in other);
|
||||||
Swap(ref temp);
|
Swap(ref temp);
|
||||||
temp.DisposeInternal();
|
temp.DisposeInternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly SharedRef<T> Lock()
|
public void Set<TFrom>(in SharedRef<TFrom> other) where TFrom : class, T
|
||||||
{
|
{
|
||||||
var sharedRef = new SharedRef<T>();
|
WeakRef<T> temp = Create(in other);
|
||||||
|
Swap(ref temp);
|
||||||
if (_refCount is not null && _refCount.IncrementIfNotZero())
|
temp.DisposeInternal();
|
||||||
{
|
}
|
||||||
Unsafe.As<SharedRef<T>, WeakRef<T>>(ref sharedRef)._value = _value;
|
|
||||||
Unsafe.As<SharedRef<T>, WeakRef<T>>(ref sharedRef)._refCount = _refCount;
|
public readonly SharedRef<T> Lock()
|
||||||
}
|
{
|
||||||
|
var sharedRef = new SharedRef<T>();
|
||||||
return sharedRef;
|
|
||||||
}
|
if (_refCount is not null && _refCount.IncrementIfNotZero())
|
||||||
|
{
|
||||||
|
Unsafe.As<SharedRef<T>, WeakRef<T>>(ref sharedRef)._value = _value;
|
||||||
|
Unsafe.As<SharedRef<T>, WeakRef<T>>(ref sharedRef)._refCount = _refCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sharedRef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,116 +2,115 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
{
|
|
||||||
public static class SpanExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the element at the specified zero-based index or gets 0 if the index is out-of-bounds.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="span">The <see cref="ReadOnlySpan{T}"/> containing the element to get.</param>
|
|
||||||
/// <param name="i">The zero-based index of the element.</param>
|
|
||||||
/// <returns>The element at the specified index or 0 if out-of-bounds.</returns>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static byte At(in this ReadOnlySpan<byte> span, int i)
|
|
||||||
{
|
|
||||||
return (uint)i >= (uint)span.Length ? (byte)0 : span[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
public static class SpanExtensions
|
||||||
/// Gets the element at the specified zero-based index or gets 0 if the index is out-of-bounds.
|
{
|
||||||
/// </summary>
|
/// <summary>
|
||||||
/// <param name="span">The <see cref="Span{T}"/> containing the element to get.</param>
|
/// Gets the element at the specified zero-based index or gets 0 if the index is out-of-bounds.
|
||||||
/// <param name="i">The zero-based index of the element.</param>
|
/// </summary>
|
||||||
/// <returns>The element at the specified index or 0 if out-of-bounds.</returns>
|
/// <param name="span">The <see cref="ReadOnlySpan{T}"/> containing the element to get.</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
/// <param name="i">The zero-based index of the element.</param>
|
||||||
public static byte At(in this Span<byte> span, int i)
|
/// <returns>The element at the specified index or 0 if out-of-bounds.</returns>
|
||||||
{
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
return (uint)i >= (uint)span.Length ? (byte)0 : span[i];
|
public static byte At(in this ReadOnlySpan<byte> span, int i)
|
||||||
}
|
{
|
||||||
|
return (uint)i >= (uint)span.Length ? (byte)0 : span[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SpanHelpers
|
/// <summary>
|
||||||
|
/// Gets the element at the specified zero-based index or gets 0 if the index is out-of-bounds.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="span">The <see cref="Span{T}"/> containing the element to get.</param>
|
||||||
|
/// <param name="i">The zero-based index of the element.</param>
|
||||||
|
/// <returns>The element at the specified index or 0 if out-of-bounds.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static byte At(in this Span<byte> span, int i)
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
return (uint)i >= (uint)span.Length ? (byte)0 : span[i];
|
||||||
public static Span<T> CreateSpan<T>(ref T reference, int length)
|
}
|
||||||
{
|
}
|
||||||
return MemoryMarshal.CreateSpan(ref reference, length);
|
|
||||||
}
|
public static class SpanHelpers
|
||||||
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static Span<T> AsSpan<T>(ref T reference) where T : unmanaged
|
public static Span<T> CreateSpan<T>(ref T reference, int length)
|
||||||
{
|
{
|
||||||
return CreateSpan(ref reference, 1);
|
return MemoryMarshal.CreateSpan(ref reference, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static Span<TSpan> AsSpan<TStruct, TSpan>(ref TStruct reference)
|
public static Span<T> AsSpan<T>(ref T reference) where T : unmanaged
|
||||||
where TStruct : unmanaged where TSpan : unmanaged
|
{
|
||||||
{
|
return CreateSpan(ref reference, 1);
|
||||||
return CreateSpan(ref Unsafe.As<TStruct, TSpan>(ref reference),
|
}
|
||||||
Unsafe.SizeOf<TStruct>() / Unsafe.SizeOf<TSpan>());
|
|
||||||
}
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Span<TSpan> AsSpan<TStruct, TSpan>(ref TStruct reference)
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
where TStruct : unmanaged where TSpan : unmanaged
|
||||||
public static Span<byte> AsByteSpan<T>(ref T reference) where T : unmanaged
|
{
|
||||||
{
|
return CreateSpan(ref Unsafe.As<TStruct, TSpan>(ref reference),
|
||||||
return CreateSpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>());
|
Unsafe.SizeOf<TStruct>() / Unsafe.SizeOf<TSpan>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ReadOnlySpan<T> CreateReadOnlySpan<T>(in T reference, int length)
|
public static Span<byte> AsByteSpan<T>(ref T reference) where T : unmanaged
|
||||||
{
|
{
|
||||||
return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in reference), length);
|
return CreateSpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ReadOnlySpan<T> AsReadOnlySpan<T>(in T reference) where T : unmanaged
|
public static ReadOnlySpan<T> CreateReadOnlySpan<T>(in T reference, int length)
|
||||||
{
|
{
|
||||||
return CreateReadOnlySpan(in reference, 1);
|
return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in reference), length);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ReadOnlySpan<TSpan> AsReadOnlySpan<TStruct, TSpan>(in TStruct reference)
|
public static ReadOnlySpan<T> AsReadOnlySpan<T>(in T reference) where T : unmanaged
|
||||||
where TStruct : unmanaged where TSpan : unmanaged
|
{
|
||||||
{
|
return CreateReadOnlySpan(in reference, 1);
|
||||||
return CreateReadOnlySpan(in Unsafe.As<TStruct, TSpan>(ref Unsafe.AsRef(in reference)),
|
}
|
||||||
Unsafe.SizeOf<TStruct>() / Unsafe.SizeOf<TSpan>());
|
|
||||||
}
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ReadOnlySpan<TSpan> AsReadOnlySpan<TStruct, TSpan>(in TStruct reference)
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
where TStruct : unmanaged where TSpan : unmanaged
|
||||||
public static ReadOnlySpan<byte> AsReadOnlyByteSpan<T>(in T reference) where T : unmanaged
|
{
|
||||||
{
|
return CreateReadOnlySpan(in Unsafe.As<TStruct, TSpan>(ref Unsafe.AsRef(in reference)),
|
||||||
return CreateReadOnlySpan(in Unsafe.As<T, byte>(ref Unsafe.AsRef(in reference)), Unsafe.SizeOf<T>());
|
Unsafe.SizeOf<TStruct>() / Unsafe.SizeOf<TSpan>());
|
||||||
}
|
}
|
||||||
|
|
||||||
// All AsStruct methods do bounds checks on the input
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
public static ReadOnlySpan<byte> AsReadOnlyByteSpan<T>(in T reference) where T : unmanaged
|
||||||
public static ref T AsStruct<T>(Span<byte> span) where T : unmanaged
|
{
|
||||||
{
|
return CreateReadOnlySpan(in Unsafe.As<T, byte>(ref Unsafe.AsRef(in reference)), Unsafe.SizeOf<T>());
|
||||||
return ref MemoryMarshal.Cast<byte, T>(span)[0];
|
}
|
||||||
}
|
|
||||||
|
// All AsStruct methods do bounds checks on the input
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ref readonly T AsReadOnlyStruct<T>(ReadOnlySpan<byte> span) where T : unmanaged
|
public static ref T AsStruct<T>(Span<byte> span) where T : unmanaged
|
||||||
{
|
{
|
||||||
return ref MemoryMarshal.Cast<byte, T>(span)[0];
|
return ref MemoryMarshal.Cast<byte, T>(span)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static ref TTo AsStruct<TFrom, TTo>(Span<TFrom> span)
|
public static ref readonly T AsReadOnlyStruct<T>(ReadOnlySpan<byte> span) where T : unmanaged
|
||||||
where TFrom : unmanaged
|
{
|
||||||
where TTo : unmanaged
|
return ref MemoryMarshal.Cast<byte, T>(span)[0];
|
||||||
{
|
}
|
||||||
return ref MemoryMarshal.Cast<TFrom, TTo>(span)[0];
|
|
||||||
}
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ref TTo AsStruct<TFrom, TTo>(Span<TFrom> span)
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
where TFrom : unmanaged
|
||||||
public static ref readonly TTo AsStruct<TFrom, TTo>(ReadOnlySpan<TFrom> span)
|
where TTo : unmanaged
|
||||||
where TFrom : unmanaged
|
{
|
||||||
where TTo : unmanaged
|
return ref MemoryMarshal.Cast<TFrom, TTo>(span)[0];
|
||||||
{
|
}
|
||||||
return ref MemoryMarshal.Cast<TFrom, TTo>(span)[0];
|
|
||||||
}
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ref readonly TTo AsStruct<TFrom, TTo>(ReadOnlySpan<TFrom> span)
|
||||||
|
where TFrom : unmanaged
|
||||||
|
where TTo : unmanaged
|
||||||
|
{
|
||||||
|
return ref MemoryMarshal.Cast<TFrom, TTo>(span)[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,99 +5,98 @@ using System.Runtime.InteropServices;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
[DebuggerDisplay("{ToString()}")]
|
||||||
|
public readonly ref struct U8Span
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{ToString()}")]
|
private readonly ReadOnlySpan<byte> _buffer;
|
||||||
public readonly ref struct U8Span
|
|
||||||
|
public ReadOnlySpan<byte> Value => _buffer;
|
||||||
|
public int Length => _buffer.Length;
|
||||||
|
|
||||||
|
public static U8Span Empty => default;
|
||||||
|
|
||||||
|
public byte this[int i]
|
||||||
{
|
{
|
||||||
private readonly ReadOnlySpan<byte> _buffer;
|
get => _buffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
public ReadOnlySpan<byte> Value => _buffer;
|
public U8Span(ReadOnlySpan<byte> value)
|
||||||
public int Length => _buffer.Length;
|
{
|
||||||
|
_buffer = value;
|
||||||
|
}
|
||||||
|
|
||||||
public static U8Span Empty => default;
|
public U8Span(string value)
|
||||||
|
{
|
||||||
|
_buffer = Encoding.UTF8.GetBytes(value);
|
||||||
|
}
|
||||||
|
|
||||||
public byte this[int i]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public byte GetOrNull(int i)
|
||||||
|
{
|
||||||
|
byte value = 0;
|
||||||
|
|
||||||
|
if ((uint)i < (uint)_buffer.Length)
|
||||||
{
|
{
|
||||||
get => _buffer[i];
|
value = GetUnsafe(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
public U8Span(ReadOnlySpan<byte> value)
|
return value;
|
||||||
{
|
}
|
||||||
_buffer = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public U8Span(string value)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
{
|
public byte GetUnsafe(int i)
|
||||||
_buffer = Encoding.UTF8.GetBytes(value);
|
{
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public byte GetOrNull(int i)
|
|
||||||
{
|
|
||||||
byte value = 0;
|
|
||||||
|
|
||||||
if ((uint)i < (uint)_buffer.Length)
|
|
||||||
{
|
|
||||||
value = GetUnsafe(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public byte GetUnsafe(int i)
|
|
||||||
{
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
return _buffer[i];
|
return _buffer[i];
|
||||||
#else
|
#else
|
||||||
return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i);
|
return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i);
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
public U8Span Slice(int start)
|
|
||||||
{
|
|
||||||
return new U8Span(_buffer.Slice(start));
|
|
||||||
}
|
|
||||||
|
|
||||||
public U8Span Slice(int start, int length)
|
|
||||||
{
|
|
||||||
return new U8Span(_buffer.Slice(start, length));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static implicit operator ReadOnlySpan<byte>(in U8Span value) => value.Value;
|
|
||||||
|
|
||||||
public static explicit operator string(in U8Span value) => value.ToString();
|
|
||||||
public static explicit operator U8Span(string value) => new U8Span(value);
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return StringUtils.Utf8ZToString(_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public U8String ToU8String()
|
|
||||||
{
|
|
||||||
int length = StringUtils.GetLength(_buffer);
|
|
||||||
|
|
||||||
// Allocate an extra byte for the null terminator
|
|
||||||
byte[] buffer = new byte[length + 1];
|
|
||||||
_buffer.Slice(0, length).CopyTo(buffer);
|
|
||||||
|
|
||||||
return new U8String(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the <see cref="U8Span"/> has no buffer.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns><see langword="true"/> if the span has no buffer.
|
|
||||||
/// Otherwise, <see langword="false"/>.</returns>
|
|
||||||
public bool IsNull() => Unsafe.IsNullRef(ref MemoryMarshal.GetReference(_buffer));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the <see cref="U8Span"/> has no buffer or begins with a null terminator.
|
|
||||||
/// </summary>
|
|
||||||
/// <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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public U8Span Slice(int start)
|
||||||
|
{
|
||||||
|
return new U8Span(_buffer.Slice(start));
|
||||||
|
}
|
||||||
|
|
||||||
|
public U8Span Slice(int start, int length)
|
||||||
|
{
|
||||||
|
return new U8Span(_buffer.Slice(start, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator ReadOnlySpan<byte>(in U8Span value) => value.Value;
|
||||||
|
|
||||||
|
public static explicit operator string(in U8Span value) => value.ToString();
|
||||||
|
public static explicit operator U8Span(string value) => new U8Span(value);
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return StringUtils.Utf8ZToString(_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public U8String ToU8String()
|
||||||
|
{
|
||||||
|
int length = StringUtils.GetLength(_buffer);
|
||||||
|
|
||||||
|
// Allocate an extra byte for the null terminator
|
||||||
|
byte[] buffer = new byte[length + 1];
|
||||||
|
_buffer.Slice(0, length).CopyTo(buffer);
|
||||||
|
|
||||||
|
return new U8String(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the <see cref="U8Span"/> has no buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see langword="true"/> if the span has no buffer.
|
||||||
|
/// Otherwise, <see langword="false"/>.</returns>
|
||||||
|
public bool IsNull() => Unsafe.IsNullRef(ref MemoryMarshal.GetReference(_buffer));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the <see cref="U8Span"/> has no buffer or begins with a null terminator.
|
||||||
|
/// </summary>
|
||||||
|
/// <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;
|
||||||
}
|
}
|
||||||
|
@ -5,95 +5,94 @@ using System.Runtime.InteropServices;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
[DebuggerDisplay("{ToString()}")]
|
||||||
|
public readonly ref struct U8SpanMutable
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{ToString()}")]
|
private readonly Span<byte> _buffer;
|
||||||
public readonly ref struct U8SpanMutable
|
|
||||||
|
public Span<byte> Value => _buffer;
|
||||||
|
public int Length => _buffer.Length;
|
||||||
|
|
||||||
|
public byte this[int i]
|
||||||
{
|
{
|
||||||
private readonly Span<byte> _buffer;
|
get => _buffer[i];
|
||||||
|
set => _buffer[i] = value;
|
||||||
|
}
|
||||||
|
|
||||||
public Span<byte> Value => _buffer;
|
public U8SpanMutable(Span<byte> value)
|
||||||
public int Length => _buffer.Length;
|
{
|
||||||
|
_buffer = value;
|
||||||
|
}
|
||||||
|
|
||||||
public byte this[int i]
|
public U8SpanMutable(string value)
|
||||||
|
{
|
||||||
|
_buffer = Encoding.UTF8.GetBytes(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public byte GetOrNull(int i)
|
||||||
|
{
|
||||||
|
byte value = 0;
|
||||||
|
|
||||||
|
if ((uint)i < (uint)_buffer.Length)
|
||||||
{
|
{
|
||||||
get => _buffer[i];
|
value = GetUnsafe(i);
|
||||||
set => _buffer[i] = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public U8SpanMutable(Span<byte> value)
|
return value;
|
||||||
{
|
}
|
||||||
_buffer = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public U8SpanMutable(string value)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
{
|
public byte GetUnsafe(int i)
|
||||||
_buffer = Encoding.UTF8.GetBytes(value);
|
{
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public byte GetOrNull(int i)
|
|
||||||
{
|
|
||||||
byte value = 0;
|
|
||||||
|
|
||||||
if ((uint)i < (uint)_buffer.Length)
|
|
||||||
{
|
|
||||||
value = GetUnsafe(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public byte GetUnsafe(int i)
|
|
||||||
{
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
return _buffer[i];
|
return _buffer[i];
|
||||||
#else
|
#else
|
||||||
return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i);
|
return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i);
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
public U8SpanMutable Slice(int start)
|
|
||||||
{
|
|
||||||
return new U8SpanMutable(_buffer.Slice(start));
|
|
||||||
}
|
|
||||||
|
|
||||||
public U8SpanMutable Slice(int start, int length)
|
|
||||||
{
|
|
||||||
return new U8SpanMutable(_buffer.Slice(start, length));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static implicit operator U8Span(U8SpanMutable value) => new U8Span(value._buffer);
|
|
||||||
|
|
||||||
public static implicit operator ReadOnlySpan<byte>(U8SpanMutable value) => value.Value;
|
|
||||||
public static implicit operator Span<byte>(U8SpanMutable value) => value.Value;
|
|
||||||
|
|
||||||
public static explicit operator string(U8SpanMutable value) => value.ToString();
|
|
||||||
public static explicit operator U8SpanMutable(string value) => new U8SpanMutable(value);
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return StringUtils.Utf8ZToString(_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public U8StringMutable ToU8String()
|
|
||||||
{
|
|
||||||
return new U8StringMutable(_buffer.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the <see cref="U8StringMutable"/> has no buffer.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns><see langword="true"/> if the span has no buffer.
|
|
||||||
/// Otherwise, <see langword="false"/>.</returns>
|
|
||||||
public bool IsNull() => Unsafe.IsNullRef(ref MemoryMarshal.GetReference(_buffer));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the <see cref="U8StringMutable"/> has no buffer or begins with a null terminator.
|
|
||||||
/// </summary>
|
|
||||||
/// <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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public U8SpanMutable Slice(int start)
|
||||||
|
{
|
||||||
|
return new U8SpanMutable(_buffer.Slice(start));
|
||||||
|
}
|
||||||
|
|
||||||
|
public U8SpanMutable Slice(int start, int length)
|
||||||
|
{
|
||||||
|
return new U8SpanMutable(_buffer.Slice(start, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator U8Span(U8SpanMutable value) => new U8Span(value._buffer);
|
||||||
|
|
||||||
|
public static implicit operator ReadOnlySpan<byte>(U8SpanMutable value) => value.Value;
|
||||||
|
public static implicit operator Span<byte>(U8SpanMutable value) => value.Value;
|
||||||
|
|
||||||
|
public static explicit operator string(U8SpanMutable value) => value.ToString();
|
||||||
|
public static explicit operator U8SpanMutable(string value) => new U8SpanMutable(value);
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return StringUtils.Utf8ZToString(_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public U8StringMutable ToU8String()
|
||||||
|
{
|
||||||
|
return new U8StringMutable(_buffer.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the <see cref="U8StringMutable"/> has no buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see langword="true"/> if the span has no buffer.
|
||||||
|
/// Otherwise, <see langword="false"/>.</returns>
|
||||||
|
public bool IsNull() => Unsafe.IsNullRef(ref MemoryMarshal.GetReference(_buffer));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the <see cref="U8StringMutable"/> has no buffer or begins with a null terminator.
|
||||||
|
/// </summary>
|
||||||
|
/// <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;
|
||||||
}
|
}
|
||||||
|
@ -3,62 +3,61 @@ using System.Diagnostics;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
[DebuggerDisplay("{ToString()}")]
|
||||||
|
public readonly struct U8String
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{ToString()}")]
|
private readonly byte[] _buffer;
|
||||||
public readonly struct U8String
|
|
||||||
|
public ReadOnlySpan<byte> Value => _buffer;
|
||||||
|
public int Length => _buffer.Length;
|
||||||
|
|
||||||
|
public byte this[int i] => _buffer[i];
|
||||||
|
|
||||||
|
public U8String(byte[] value)
|
||||||
{
|
{
|
||||||
private readonly byte[] _buffer;
|
_buffer = value;
|
||||||
|
|
||||||
public ReadOnlySpan<byte> Value => _buffer;
|
|
||||||
public int Length => _buffer.Length;
|
|
||||||
|
|
||||||
public byte this[int i] => _buffer[i];
|
|
||||||
|
|
||||||
public U8String(byte[] value)
|
|
||||||
{
|
|
||||||
_buffer = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public U8String(string value)
|
|
||||||
{
|
|
||||||
_buffer = Encoding.UTF8.GetBytes(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public U8String Slice(int start)
|
|
||||||
{
|
|
||||||
return new U8String(_buffer.AsSpan(start).ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public U8String Slice(int start, int length)
|
|
||||||
{
|
|
||||||
return new U8String(_buffer.AsSpan(start, length).ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static implicit operator U8Span(U8String value) => new U8Span(value._buffer);
|
|
||||||
|
|
||||||
public static implicit operator ReadOnlySpan<byte>(U8String value) => value.Value;
|
|
||||||
|
|
||||||
public static explicit operator string(U8String value) => value.ToString();
|
|
||||||
public static explicit operator U8String(string value) => new U8String(value);
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return StringUtils.Utf8ZToString(_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the <see cref="U8String"/> has no buffer.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns><see langword="true"/> if the string has no buffer.
|
|
||||||
/// Otherwise, <see langword="false"/>.</returns>
|
|
||||||
public bool IsNull() => _buffer == null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the <see cref="U8String"/> has no buffer or begins with a null terminator.
|
|
||||||
/// </summary>
|
|
||||||
/// <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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public U8String(string value)
|
||||||
|
{
|
||||||
|
_buffer = Encoding.UTF8.GetBytes(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public U8String Slice(int start)
|
||||||
|
{
|
||||||
|
return new U8String(_buffer.AsSpan(start).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public U8String Slice(int start, int length)
|
||||||
|
{
|
||||||
|
return new U8String(_buffer.AsSpan(start, length).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator U8Span(U8String value) => new U8Span(value._buffer);
|
||||||
|
|
||||||
|
public static implicit operator ReadOnlySpan<byte>(U8String value) => value.Value;
|
||||||
|
|
||||||
|
public static explicit operator string(U8String value) => value.ToString();
|
||||||
|
public static explicit operator U8String(string value) => new U8String(value);
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return StringUtils.Utf8ZToString(_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the <see cref="U8String"/> has no buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see langword="true"/> if the string has no buffer.
|
||||||
|
/// Otherwise, <see langword="false"/>.</returns>
|
||||||
|
public bool IsNull() => _buffer == null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the <see cref="U8String"/> has no buffer or begins with a null terminator.
|
||||||
|
/// </summary>
|
||||||
|
/// <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;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,15 +1,14 @@
|
|||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
{
|
|
||||||
public static class U8StringHelpers
|
|
||||||
{
|
|
||||||
public static U8String ToU8String(this string value)
|
|
||||||
{
|
|
||||||
return new U8String(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static U8Span ToU8Span(this string value)
|
public static class U8StringHelpers
|
||||||
{
|
{
|
||||||
return new U8Span(value);
|
public static U8String ToU8String(this string value)
|
||||||
}
|
{
|
||||||
|
return new U8String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static U8Span ToU8Span(this string value)
|
||||||
|
{
|
||||||
|
return new U8Span(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,69 +3,68 @@ using System.Diagnostics;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
[DebuggerDisplay("{ToString()}")]
|
||||||
|
public readonly struct U8StringMutable
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{ToString()}")]
|
private readonly byte[] _buffer;
|
||||||
public readonly struct U8StringMutable
|
|
||||||
|
public Span<byte> Value => _buffer;
|
||||||
|
public int Length => _buffer.Length;
|
||||||
|
|
||||||
|
public byte this[int i]
|
||||||
{
|
{
|
||||||
private readonly byte[] _buffer;
|
get => _buffer[i];
|
||||||
|
set => _buffer[i] = value;
|
||||||
public Span<byte> Value => _buffer;
|
|
||||||
public int Length => _buffer.Length;
|
|
||||||
|
|
||||||
public byte this[int i]
|
|
||||||
{
|
|
||||||
get => _buffer[i];
|
|
||||||
set => _buffer[i] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public U8StringMutable(byte[] value)
|
|
||||||
{
|
|
||||||
_buffer = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public U8StringMutable(string value)
|
|
||||||
{
|
|
||||||
_buffer = Encoding.UTF8.GetBytes(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public U8StringMutable Slice(int start)
|
|
||||||
{
|
|
||||||
return new U8StringMutable(_buffer.AsSpan(start).ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public U8StringMutable Slice(int start, int length)
|
|
||||||
{
|
|
||||||
return new U8StringMutable(_buffer.AsSpan(start, length).ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static implicit operator U8String(U8StringMutable value) => new U8String(value._buffer);
|
|
||||||
public static implicit operator U8SpanMutable(U8StringMutable value) => new U8SpanMutable(value._buffer);
|
|
||||||
public static implicit operator U8Span(U8StringMutable value) => new U8Span(value._buffer);
|
|
||||||
|
|
||||||
public static implicit operator ReadOnlySpan<byte>(U8StringMutable value) => value.Value;
|
|
||||||
public static implicit operator Span<byte>(U8StringMutable value) => value.Value;
|
|
||||||
|
|
||||||
public static explicit operator string(U8StringMutable value) => value.ToString();
|
|
||||||
public static explicit operator U8StringMutable(string value) => new U8StringMutable(value);
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return StringUtils.Utf8ZToString(_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the <see cref="U8String"/> has no buffer.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns><see langword="true"/> if the string has no buffer.
|
|
||||||
/// Otherwise, <see langword="false"/>.</returns>
|
|
||||||
public bool IsNull() => _buffer == null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the <see cref="U8String"/> has no buffer or begins with a null terminator.
|
|
||||||
/// </summary>
|
|
||||||
/// <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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public U8StringMutable(byte[] value)
|
||||||
|
{
|
||||||
|
_buffer = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public U8StringMutable(string value)
|
||||||
|
{
|
||||||
|
_buffer = Encoding.UTF8.GetBytes(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public U8StringMutable Slice(int start)
|
||||||
|
{
|
||||||
|
return new U8StringMutable(_buffer.AsSpan(start).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public U8StringMutable Slice(int start, int length)
|
||||||
|
{
|
||||||
|
return new U8StringMutable(_buffer.AsSpan(start, length).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator U8String(U8StringMutable value) => new U8String(value._buffer);
|
||||||
|
public static implicit operator U8SpanMutable(U8StringMutable value) => new U8SpanMutable(value._buffer);
|
||||||
|
public static implicit operator U8Span(U8StringMutable value) => new U8Span(value._buffer);
|
||||||
|
|
||||||
|
public static implicit operator ReadOnlySpan<byte>(U8StringMutable value) => value.Value;
|
||||||
|
public static implicit operator Span<byte>(U8StringMutable value) => value.Value;
|
||||||
|
|
||||||
|
public static explicit operator string(U8StringMutable value) => value.ToString();
|
||||||
|
public static explicit operator U8StringMutable(string value) => new U8StringMutable(value);
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return StringUtils.Utf8ZToString(_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the <see cref="U8String"/> has no buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><see langword="true"/> if the string has no buffer.
|
||||||
|
/// Otherwise, <see langword="false"/>.</returns>
|
||||||
|
public bool IsNull() => _buffer == null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the <see cref="U8String"/> has no buffer or begins with a null terminator.
|
||||||
|
/// </summary>
|
||||||
|
/// <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;
|
||||||
}
|
}
|
||||||
|
@ -2,90 +2,89 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using static InlineIL.IL.Emit;
|
using static InlineIL.IL.Emit;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
public static class UniqueRefExtensions
|
||||||
{
|
{
|
||||||
public static class UniqueRefExtensions
|
// ReSharper disable once EntityNameCapturedOnly.Global
|
||||||
|
public static ref UniqueRef<T> Ref<T>(this in UniqueRef<T> value) where T : class, IDisposable
|
||||||
{
|
{
|
||||||
// ReSharper disable once EntityNameCapturedOnly.Global
|
Ldarg(nameof(value));
|
||||||
public static ref UniqueRef<T> Ref<T>(this in UniqueRef<T> value) where T : class, IDisposable
|
Ret();
|
||||||
{
|
throw InlineIL.IL.Unreachable();
|
||||||
Ldarg(nameof(value));
|
}
|
||||||
Ret();
|
}
|
||||||
throw InlineIL.IL.Unreachable();
|
|
||||||
}
|
[NonCopyableDisposable]
|
||||||
}
|
public struct UniqueRef<T> : IDisposable where T : class, IDisposable
|
||||||
|
{
|
||||||
[NonCopyableDisposable]
|
private T _value;
|
||||||
public struct UniqueRef<T> : IDisposable where T : class, IDisposable
|
|
||||||
{
|
public readonly T Get => _value;
|
||||||
private T _value;
|
public readonly bool HasValue => Get is not null;
|
||||||
|
|
||||||
public readonly T Get => _value;
|
public UniqueRef(T value)
|
||||||
public readonly bool HasValue => Get is not null;
|
{
|
||||||
|
_value = value;
|
||||||
public UniqueRef(T value)
|
}
|
||||||
{
|
|
||||||
_value = value;
|
public UniqueRef(ref UniqueRef<T> other)
|
||||||
}
|
{
|
||||||
|
_value = other.Release();
|
||||||
public UniqueRef(ref UniqueRef<T> other)
|
}
|
||||||
{
|
|
||||||
_value = other.Release();
|
[Obsolete("This method should never be manually called. Use the Destroy method instead.", true)]
|
||||||
}
|
public void Dispose()
|
||||||
|
{
|
||||||
[Obsolete("This method should never be manually called. Use the Destroy method instead.", true)]
|
_value?.Dispose();
|
||||||
public void Dispose()
|
}
|
||||||
{
|
|
||||||
_value?.Dispose();
|
/// <summary>
|
||||||
}
|
/// Used to manually dispose the <see cref="UniqueRef{T}"/> from the Dispose methods of other types.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public void Destroy()
|
||||||
/// Used to manually dispose the <see cref="UniqueRef{T}"/> from the Dispose methods of other types.
|
{
|
||||||
/// </summary>
|
Reset();
|
||||||
public void Destroy()
|
}
|
||||||
{
|
|
||||||
Reset();
|
public static UniqueRef<T> Create<TFrom>(ref UniqueRef<TFrom> other) where TFrom : class, T
|
||||||
}
|
{
|
||||||
|
return new UniqueRef<T>(other.Release());
|
||||||
public static UniqueRef<T> Create<TFrom>(ref UniqueRef<TFrom> other) where TFrom : class, T
|
}
|
||||||
{
|
|
||||||
return new UniqueRef<T>(other.Release());
|
public void Swap(ref UniqueRef<T> other)
|
||||||
}
|
{
|
||||||
|
(other._value, _value) = (_value, other._value);
|
||||||
public void Swap(ref UniqueRef<T> other)
|
}
|
||||||
{
|
|
||||||
(other._value, _value) = (_value, other._value);
|
public void Reset() => Reset(null);
|
||||||
}
|
|
||||||
|
public void Reset(T value)
|
||||||
public void Reset() => Reset(null);
|
{
|
||||||
|
T oldValue = _value;
|
||||||
public void Reset(T value)
|
_value = value;
|
||||||
{
|
|
||||||
T oldValue = _value;
|
oldValue?.Dispose();
|
||||||
_value = value;
|
}
|
||||||
|
|
||||||
oldValue?.Dispose();
|
public void Set(ref UniqueRef<T> other)
|
||||||
}
|
{
|
||||||
|
if (Unsafe.AreSame(ref this, ref other))
|
||||||
public void Set(ref UniqueRef<T> other)
|
return;
|
||||||
{
|
|
||||||
if (Unsafe.AreSame(ref this, ref other))
|
Reset(other.Release());
|
||||||
return;
|
}
|
||||||
|
|
||||||
Reset(other.Release());
|
public void Set<TFrom>(ref UniqueRef<TFrom> other) where TFrom : class, T
|
||||||
}
|
{
|
||||||
|
Reset(other.Release());
|
||||||
public void Set<TFrom>(ref UniqueRef<TFrom> other) where TFrom : class, T
|
}
|
||||||
{
|
|
||||||
Reset(other.Release());
|
public T Release()
|
||||||
}
|
{
|
||||||
|
T oldValue = _value;
|
||||||
public T Release()
|
_value = null;
|
||||||
{
|
|
||||||
T oldValue = _value;
|
return oldValue;
|
||||||
_value = null;
|
|
||||||
|
|
||||||
return oldValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,67 +1,66 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace LibHac.Common
|
namespace LibHac.Common;
|
||||||
|
|
||||||
|
public static class UnsafeHelpers
|
||||||
{
|
{
|
||||||
public static class UnsafeHelpers
|
/// <summary>
|
||||||
|
/// Bypasses definite assignment rules for a given unmanaged value,
|
||||||
|
/// or zeros a managed value to avoid having invalid references.
|
||||||
|
/// <br/>Used in instances where an out value in the original code isn't set due to an error condition.
|
||||||
|
/// <br/>Behaves the same as <see cref="Unsafe.SkipInit{T}"/>, except it zeros managed values.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the object.</typeparam>
|
||||||
|
/// <param name="value">The object.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void SkipParamInit<T>(out T value)
|
||||||
{
|
{
|
||||||
/// <summary>
|
if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
||||||
/// Bypasses definite assignment rules for a given unmanaged value,
|
|
||||||
/// or zeros a managed value to avoid having invalid references.
|
|
||||||
/// <br/>Used in instances where an out value in the original code isn't set due to an error condition.
|
|
||||||
/// <br/>Behaves the same as <see cref="Unsafe.SkipInit{T}"/>, except it zeros managed values.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of the object.</typeparam>
|
|
||||||
/// <param name="value">The object.</param>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void SkipParamInit<T>(out T value)
|
|
||||||
{
|
{
|
||||||
if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
Unsafe.SkipInit(out value);
|
||||||
{
|
|
||||||
Unsafe.SkipInit(out value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
value = default;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
/// <summary>
|
|
||||||
/// Bypasses definite assignment rules for the given unmanaged values,
|
|
||||||
/// zeroing any managed values to avoid having invalid references.
|
|
||||||
/// <br/>Used in instances where out values in the original code aren't set due to an error condition.
|
|
||||||
/// <br/>Behaves the same as calling <see cref="Unsafe.SkipInit{T}"/>
|
|
||||||
/// on each value, except managed values will be zeroed.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T1">The type of the first object.</typeparam>
|
|
||||||
/// <typeparam name="T2">The type of the second object.</typeparam>
|
|
||||||
/// <param name="value1">The first object.</param>
|
|
||||||
/// <param name="value2">The second object.</param>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void SkipParamInit<T1, T2>(out T1 value1, out T2 value2)
|
|
||||||
{
|
{
|
||||||
SkipParamInit(out value1);
|
value = default;
|
||||||
SkipParamInit(out value2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Bypasses definite assignment rules for the given unmanaged values,
|
|
||||||
/// zeroing any managed values to avoid having invalid references.
|
|
||||||
/// <br/>Used in instances where out values in the original code aren't set due to an error condition.
|
|
||||||
/// <br/>Behaves the same as calling <see cref="Unsafe.SkipInit{T}"/>
|
|
||||||
/// on each value, except managed values will be zeroed.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T1">The type of the first object.</typeparam>
|
|
||||||
/// <typeparam name="T2">The type of the second object.</typeparam>
|
|
||||||
/// <typeparam name="T3">The type of the third object.</typeparam>
|
|
||||||
/// <param name="value1">The first object.</param>
|
|
||||||
/// <param name="value2">The second object.</param>
|
|
||||||
/// <param name="value3">The third object.</param>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void SkipParamInit<T1, T2, T3>(out T1 value1, out T2 value2, out T3 value3)
|
|
||||||
{
|
|
||||||
SkipParamInit(out value1);
|
|
||||||
SkipParamInit(out value2);
|
|
||||||
SkipParamInit(out value3);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bypasses definite assignment rules for the given unmanaged values,
|
||||||
|
/// zeroing any managed values to avoid having invalid references.
|
||||||
|
/// <br/>Used in instances where out values in the original code aren't set due to an error condition.
|
||||||
|
/// <br/>Behaves the same as calling <see cref="Unsafe.SkipInit{T}"/>
|
||||||
|
/// on each value, except managed values will be zeroed.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T1">The type of the first object.</typeparam>
|
||||||
|
/// <typeparam name="T2">The type of the second object.</typeparam>
|
||||||
|
/// <param name="value1">The first object.</param>
|
||||||
|
/// <param name="value2">The second object.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void SkipParamInit<T1, T2>(out T1 value1, out T2 value2)
|
||||||
|
{
|
||||||
|
SkipParamInit(out value1);
|
||||||
|
SkipParamInit(out value2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bypasses definite assignment rules for the given unmanaged values,
|
||||||
|
/// zeroing any managed values to avoid having invalid references.
|
||||||
|
/// <br/>Used in instances where out values in the original code aren't set due to an error condition.
|
||||||
|
/// <br/>Behaves the same as calling <see cref="Unsafe.SkipInit{T}"/>
|
||||||
|
/// on each value, except managed values will be zeroed.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T1">The type of the first object.</typeparam>
|
||||||
|
/// <typeparam name="T2">The type of the second object.</typeparam>
|
||||||
|
/// <typeparam name="T3">The type of the third object.</typeparam>
|
||||||
|
/// <param name="value1">The first object.</param>
|
||||||
|
/// <param name="value2">The second object.</param>
|
||||||
|
/// <param name="value3">The third object.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void SkipParamInit<T1, T2, T3>(out T1 value1, out T2 value2, out T3 value3)
|
||||||
|
{
|
||||||
|
SkipParamInit(out value1);
|
||||||
|
SkipParamInit(out value2);
|
||||||
|
SkipParamInit(out value3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,305 +7,304 @@ using LibHac.Diag;
|
|||||||
|
|
||||||
using AesNi = System.Runtime.Intrinsics.X86.Aes;
|
using AesNi = System.Runtime.Intrinsics.X86.Aes;
|
||||||
|
|
||||||
namespace LibHac.Crypto
|
namespace LibHac.Crypto;
|
||||||
|
|
||||||
|
public static class Aes
|
||||||
{
|
{
|
||||||
public static class Aes
|
public const int KeySize128 = 0x10;
|
||||||
|
public const int BlockSize = 0x10;
|
||||||
|
|
||||||
|
public static bool IsAesNiSupported()
|
||||||
{
|
{
|
||||||
public const int KeySize128 = 0x10;
|
return AesNi.IsSupported;
|
||||||
public const int BlockSize = 0x10;
|
}
|
||||||
|
|
||||||
public static bool IsAesNiSupported()
|
public static ICipher CreateEcbDecryptor(ReadOnlySpan<byte> key, bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
{
|
{
|
||||||
return AesNi.IsSupported;
|
return new AesEcbDecryptorNi(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ICipher CreateEcbDecryptor(ReadOnlySpan<byte> key, bool preferDotNetCrypto = false)
|
return new AesEcbDecryptor(key);
|
||||||
{
|
}
|
||||||
if (IsAesNiSupported() && !preferDotNetCrypto)
|
|
||||||
{
|
|
||||||
return new AesEcbDecryptorNi(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AesEcbDecryptor(key);
|
public static ICipher CreateEcbEncryptor(ReadOnlySpan<byte> key, bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
|
{
|
||||||
|
return new AesEcbEncryptorNi(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ICipher CreateEcbEncryptor(ReadOnlySpan<byte> key, bool preferDotNetCrypto = false)
|
return new AesEcbEncryptor(key);
|
||||||
{
|
}
|
||||||
if (IsAesNiSupported() && !preferDotNetCrypto)
|
|
||||||
{
|
|
||||||
return new AesEcbEncryptorNi(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AesEcbEncryptor(key);
|
public static ICipher CreateCbcDecryptor(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
|
{
|
||||||
|
return new AesCbcDecryptorNi(key, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ICipher CreateCbcDecryptor(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
return new AesCbcDecryptor(key, iv);
|
||||||
{
|
}
|
||||||
if (IsAesNiSupported() && !preferDotNetCrypto)
|
|
||||||
{
|
|
||||||
return new AesCbcDecryptorNi(key, iv);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AesCbcDecryptor(key, iv);
|
public static ICipher CreateCbcEncryptor(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
|
{
|
||||||
|
return new AesCbcEncryptorNi(key, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ICipher CreateCbcEncryptor(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
return new AesCbcEncryptor(key, iv);
|
||||||
{
|
}
|
||||||
if (IsAesNiSupported() && !preferDotNetCrypto)
|
|
||||||
{
|
|
||||||
return new AesCbcEncryptorNi(key, iv);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AesCbcEncryptor(key, iv);
|
public static ICipherWithIv CreateCtrDecryptor(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
|
{
|
||||||
|
return new AesCtrCipherNi(key, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ICipherWithIv CreateCtrDecryptor(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
// Encryption and decryption in counter mode is the same operation
|
||||||
{
|
return CreateCtrEncryptor(key, iv, preferDotNetCrypto);
|
||||||
if (IsAesNiSupported() && !preferDotNetCrypto)
|
}
|
||||||
{
|
|
||||||
return new AesCtrCipherNi(key, iv);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encryption and decryption in counter mode is the same operation
|
public static ICipherWithIv CreateCtrEncryptor(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
||||||
return CreateCtrEncryptor(key, iv, preferDotNetCrypto);
|
{
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
|
{
|
||||||
|
return new AesCtrCipherNi(key, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ICipherWithIv CreateCtrEncryptor(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
return new AesCtrCipher(key, iv);
|
||||||
{
|
}
|
||||||
if (IsAesNiSupported() && !preferDotNetCrypto)
|
|
||||||
{
|
|
||||||
return new AesCtrCipherNi(key, iv);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AesCtrCipher(key, iv);
|
public static ICipherWithIv CreateXtsDecryptor(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2,
|
||||||
|
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
|
{
|
||||||
|
return new AesXtsDecryptorNi(key1, key2, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ICipherWithIv CreateXtsDecryptor(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2,
|
return new AesXtsDecryptor(key1, key2, iv);
|
||||||
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
}
|
||||||
{
|
|
||||||
if (IsAesNiSupported() && !preferDotNetCrypto)
|
|
||||||
{
|
|
||||||
return new AesXtsDecryptorNi(key1, key2, iv);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AesXtsDecryptor(key1, key2, iv);
|
public static ICipherWithIv CreateXtsEncryptor(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2,
|
||||||
|
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
|
{
|
||||||
|
return new AesXtsEncryptorNi(key1, key2, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ICipherWithIv CreateXtsEncryptor(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2,
|
return new AesXtsEncryptor(key1, key2, iv);
|
||||||
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
}
|
||||||
{
|
|
||||||
if (IsAesNiSupported() && !preferDotNetCrypto)
|
|
||||||
{
|
|
||||||
return new AesXtsEncryptorNi(key1, key2, iv);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AesXtsEncryptor(key1, key2, iv);
|
public static void EncryptEcb128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
|
||||||
|
bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
|
{
|
||||||
|
Unsafe.SkipInit(out AesEcbModeNi cipherNi);
|
||||||
|
|
||||||
|
cipherNi.Initialize(key, false);
|
||||||
|
cipherNi.Encrypt(input, output);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EncryptEcb128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
|
ICipher cipher = CreateEcbEncryptor(key, preferDotNetCrypto);
|
||||||
bool preferDotNetCrypto = false)
|
|
||||||
|
cipher.Transform(input, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DecryptEcb128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
|
||||||
|
bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
{
|
{
|
||||||
if (IsAesNiSupported() && !preferDotNetCrypto)
|
Unsafe.SkipInit(out AesEcbModeNi cipherNi);
|
||||||
{
|
|
||||||
Unsafe.SkipInit(out AesEcbModeNi cipherNi);
|
|
||||||
|
|
||||||
cipherNi.Initialize(key, false);
|
cipherNi.Initialize(key, true);
|
||||||
cipherNi.Encrypt(input, output);
|
cipherNi.Decrypt(input, output);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
ICipher cipher = CreateEcbEncryptor(key, preferDotNetCrypto);
|
|
||||||
|
|
||||||
cipher.Transform(input, output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DecryptEcb128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
|
ICipher cipher = CreateEcbDecryptor(key, preferDotNetCrypto);
|
||||||
bool preferDotNetCrypto = false)
|
|
||||||
|
cipher.Transform(input, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EncryptCbc128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
|
||||||
|
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
{
|
{
|
||||||
if (IsAesNiSupported() && !preferDotNetCrypto)
|
Unsafe.SkipInit(out AesCbcModeNi cipherNi);
|
||||||
{
|
|
||||||
Unsafe.SkipInit(out AesEcbModeNi cipherNi);
|
|
||||||
|
|
||||||
cipherNi.Initialize(key, true);
|
cipherNi.Initialize(key, iv, false);
|
||||||
cipherNi.Decrypt(input, output);
|
cipherNi.Encrypt(input, output);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
ICipher cipher = CreateEcbDecryptor(key, preferDotNetCrypto);
|
|
||||||
|
|
||||||
cipher.Transform(input, output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EncryptCbc128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
|
ICipher cipher = CreateCbcEncryptor(key, iv, preferDotNetCrypto);
|
||||||
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
|
||||||
|
cipher.Transform(input, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DecryptCbc128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
|
||||||
|
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
{
|
{
|
||||||
if (IsAesNiSupported() && !preferDotNetCrypto)
|
Unsafe.SkipInit(out AesCbcModeNi cipherNi);
|
||||||
{
|
|
||||||
Unsafe.SkipInit(out AesCbcModeNi cipherNi);
|
|
||||||
|
|
||||||
cipherNi.Initialize(key, iv, false);
|
cipherNi.Initialize(key, iv, true);
|
||||||
cipherNi.Encrypt(input, output);
|
cipherNi.Decrypt(input, output);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
ICipher cipher = CreateCbcEncryptor(key, iv, preferDotNetCrypto);
|
|
||||||
|
|
||||||
cipher.Transform(input, output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DecryptCbc128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
|
ICipher cipher = CreateCbcDecryptor(key, iv, preferDotNetCrypto);
|
||||||
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
|
||||||
|
cipher.Transform(input, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EncryptCtr128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
|
||||||
|
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
{
|
{
|
||||||
if (IsAesNiSupported() && !preferDotNetCrypto)
|
Unsafe.SkipInit(out AesCtrModeNi cipherNi);
|
||||||
{
|
|
||||||
Unsafe.SkipInit(out AesCbcModeNi cipherNi);
|
|
||||||
|
|
||||||
cipherNi.Initialize(key, iv, true);
|
cipherNi.Initialize(key, iv);
|
||||||
cipherNi.Decrypt(input, output);
|
cipherNi.Transform(input, output);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
ICipher cipher = CreateCbcDecryptor(key, iv, preferDotNetCrypto);
|
|
||||||
|
|
||||||
cipher.Transform(input, output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EncryptCtr128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
|
ICipher cipher = CreateCtrEncryptor(key, iv, preferDotNetCrypto);
|
||||||
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
|
||||||
|
cipher.Transform(input, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DecryptCtr128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
|
||||||
|
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
{
|
{
|
||||||
if (IsAesNiSupported() && !preferDotNetCrypto)
|
Unsafe.SkipInit(out AesCtrModeNi cipherNi);
|
||||||
{
|
|
||||||
Unsafe.SkipInit(out AesCtrModeNi cipherNi);
|
|
||||||
|
|
||||||
cipherNi.Initialize(key, iv);
|
cipherNi.Initialize(key, iv);
|
||||||
cipherNi.Transform(input, output);
|
cipherNi.Transform(input, output);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
ICipher cipher = CreateCtrEncryptor(key, iv, preferDotNetCrypto);
|
|
||||||
|
|
||||||
cipher.Transform(input, output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DecryptCtr128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
|
ICipher cipher = CreateCtrDecryptor(key, iv, preferDotNetCrypto);
|
||||||
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
|
||||||
|
cipher.Transform(input, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EncryptXts128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key1,
|
||||||
|
ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
{
|
{
|
||||||
if (IsAesNiSupported() && !preferDotNetCrypto)
|
Unsafe.SkipInit(out AesXtsModeNi cipherNi);
|
||||||
{
|
|
||||||
Unsafe.SkipInit(out AesCtrModeNi cipherNi);
|
|
||||||
|
|
||||||
cipherNi.Initialize(key, iv);
|
cipherNi.Initialize(key1, key2, iv, false);
|
||||||
cipherNi.Transform(input, output);
|
cipherNi.Encrypt(input, output);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
ICipher cipher = CreateCtrDecryptor(key, iv, preferDotNetCrypto);
|
|
||||||
|
|
||||||
cipher.Transform(input, output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EncryptXts128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key1,
|
ICipher cipher = CreateXtsEncryptor(key1, key2, iv, preferDotNetCrypto);
|
||||||
ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
|
||||||
|
cipher.Transform(input, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DecryptXts128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key1,
|
||||||
|
ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
||||||
|
{
|
||||||
|
if (IsAesNiSupported() && !preferDotNetCrypto)
|
||||||
{
|
{
|
||||||
if (IsAesNiSupported() && !preferDotNetCrypto)
|
Unsafe.SkipInit(out AesXtsModeNi cipherNi);
|
||||||
{
|
|
||||||
Unsafe.SkipInit(out AesXtsModeNi cipherNi);
|
|
||||||
|
|
||||||
cipherNi.Initialize(key1, key2, iv, false);
|
cipherNi.Initialize(key1, key2, iv, true);
|
||||||
cipherNi.Encrypt(input, output);
|
cipherNi.Decrypt(input, output);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
ICipher cipher = CreateXtsEncryptor(key1, key2, iv, preferDotNetCrypto);
|
|
||||||
|
|
||||||
cipher.Transform(input, output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DecryptXts128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key1,
|
ICipher cipher = CreateXtsDecryptor(key1, key2, iv, preferDotNetCrypto);
|
||||||
ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
|
|
||||||
|
cipher.Transform(input, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the CMAC of the provided data using AES-128.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mac">The buffer where the generated MAC will be placed. Must be at least 16 bytes long.</param>
|
||||||
|
/// <param name="data">The message on which the MAC will be calculated.</param>
|
||||||
|
/// <param name="key">The 128-bit AES key used to calculate the MAC.</param>
|
||||||
|
/// <remarks>https://tools.ietf.org/html/rfc4493</remarks>
|
||||||
|
public static void CalculateCmac(Span<byte> mac, ReadOnlySpan<byte> data, ReadOnlySpan<byte> key)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<byte> zero = new Buffer16();
|
||||||
|
int len = data.Length;
|
||||||
|
|
||||||
|
// Step 1, AES-128 with key K is applied to an all-zero input block.
|
||||||
|
Span<byte> l = stackalloc byte[16];
|
||||||
|
EncryptCbc128(zero, l, key, zero);
|
||||||
|
|
||||||
|
// Step 2, K1 is derived through the following operation:
|
||||||
|
Span<byte> k1 = stackalloc byte[16];
|
||||||
|
LeftShiftBytes(l, k1);
|
||||||
|
if ((l[0] & 0x80) == 0x80) // If the most significant bit of L is equal to 0, K1 is the left-shift of L by 1 bit.
|
||||||
|
k1[15] ^= 0x87; // Otherwise, K1 is the XOR of const_Rb and the left-shift of L by 1 bit.
|
||||||
|
|
||||||
|
// Step 3, K2 is derived through the following operation:
|
||||||
|
Span<byte> k2 = stackalloc byte[16];
|
||||||
|
LeftShiftBytes(k1, k2);
|
||||||
|
if ((k1[0] & 0x80) == 0x80) // If the most significant bit of K1 is equal to 0, K2 is the left-shift of K1 by 1 bit.
|
||||||
|
k2[15] ^= 0x87; // Otherwise, K2 is the XOR of const_Rb and the left-shift of K1 by 1 bit.
|
||||||
|
|
||||||
|
// ReSharper disable once RedundantAssignment
|
||||||
|
Span<byte> paddedMessage = l;
|
||||||
|
|
||||||
|
if (len != 0 && len % 16 == 0) // If the size of the input message block is equal to a positive multiple of the block size (namely, 128 bits),
|
||||||
|
{ // the last block shall be XOR'ed with K1 before processing
|
||||||
|
paddedMessage = len < 0x800 ? stackalloc byte[len] : new byte[len];
|
||||||
|
data.CopyTo(paddedMessage);
|
||||||
|
|
||||||
|
for (int j = 0; j < k1.Length; j++)
|
||||||
|
paddedMessage[paddedMessage.Length - 16 + j] ^= k1[j];
|
||||||
|
}
|
||||||
|
else // Otherwise, the last block shall be padded with 10^i and XOR'ed with K2.
|
||||||
{
|
{
|
||||||
if (IsAesNiSupported() && !preferDotNetCrypto)
|
int paddedLength = len + (16 - len % 16);
|
||||||
{
|
paddedMessage = paddedLength < 0x800 ? stackalloc byte[paddedLength] : new byte[paddedLength];
|
||||||
Unsafe.SkipInit(out AesXtsModeNi cipherNi);
|
|
||||||
|
|
||||||
cipherNi.Initialize(key1, key2, iv, true);
|
paddedMessage.Slice(len).Clear();
|
||||||
cipherNi.Decrypt(input, output);
|
paddedMessage[len] = 0x80;
|
||||||
return;
|
data.CopyTo(paddedMessage);
|
||||||
}
|
|
||||||
|
|
||||||
ICipher cipher = CreateXtsDecryptor(key1, key2, iv, preferDotNetCrypto);
|
for (int j = 0; j < k2.Length; j++)
|
||||||
|
paddedMessage[paddedMessage.Length - 16 + j] ^= k2[j];
|
||||||
cipher.Transform(input, output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
EncryptCbc128(paddedMessage, paddedMessage, key, zero); // The result of the previous process will be the input of the last encryption.
|
||||||
/// Computes the CMAC of the provided data using AES-128.
|
paddedMessage.Slice(paddedMessage.Length - 0x10).CopyTo(mac);
|
||||||
/// </summary>
|
}
|
||||||
/// <param name="mac">The buffer where the generated MAC will be placed. Must be at least 16 bytes long.</param>
|
|
||||||
/// <param name="data">The message on which the MAC will be calculated.</param>
|
private static void LeftShiftBytes(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
/// <param name="key">The 128-bit AES key used to calculate the MAC.</param>
|
{
|
||||||
/// <remarks>https://tools.ietf.org/html/rfc4493</remarks>
|
Assert.SdkRequiresGreaterEqual(output.Length, input.Length);
|
||||||
public static void CalculateCmac(Span<byte> mac, ReadOnlySpan<byte> data, ReadOnlySpan<byte> key)
|
|
||||||
|
byte carry = 0;
|
||||||
|
|
||||||
|
for (int i = input.Length - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> zero = new Buffer16();
|
ushort b = (ushort)(input[i] << 1);
|
||||||
int len = data.Length;
|
output[i] = (byte)((b & 0xff) + carry);
|
||||||
|
carry = (byte)((b & 0xff00) >> 8);
|
||||||
// Step 1, AES-128 with key K is applied to an all-zero input block.
|
|
||||||
Span<byte> l = stackalloc byte[16];
|
|
||||||
EncryptCbc128(zero, l, key, zero);
|
|
||||||
|
|
||||||
// Step 2, K1 is derived through the following operation:
|
|
||||||
Span<byte> k1 = stackalloc byte[16];
|
|
||||||
LeftShiftBytes(l, k1);
|
|
||||||
if ((l[0] & 0x80) == 0x80) // If the most significant bit of L is equal to 0, K1 is the left-shift of L by 1 bit.
|
|
||||||
k1[15] ^= 0x87; // Otherwise, K1 is the XOR of const_Rb and the left-shift of L by 1 bit.
|
|
||||||
|
|
||||||
// Step 3, K2 is derived through the following operation:
|
|
||||||
Span<byte> k2 = stackalloc byte[16];
|
|
||||||
LeftShiftBytes(k1, k2);
|
|
||||||
if ((k1[0] & 0x80) == 0x80) // If the most significant bit of K1 is equal to 0, K2 is the left-shift of K1 by 1 bit.
|
|
||||||
k2[15] ^= 0x87; // Otherwise, K2 is the XOR of const_Rb and the left-shift of K1 by 1 bit.
|
|
||||||
|
|
||||||
// ReSharper disable once RedundantAssignment
|
|
||||||
Span<byte> paddedMessage = l;
|
|
||||||
|
|
||||||
if (len != 0 && len % 16 == 0) // If the size of the input message block is equal to a positive multiple of the block size (namely, 128 bits),
|
|
||||||
{ // the last block shall be XOR'ed with K1 before processing
|
|
||||||
paddedMessage = len < 0x800 ? stackalloc byte[len] : new byte[len];
|
|
||||||
data.CopyTo(paddedMessage);
|
|
||||||
|
|
||||||
for (int j = 0; j < k1.Length; j++)
|
|
||||||
paddedMessage[paddedMessage.Length - 16 + j] ^= k1[j];
|
|
||||||
}
|
|
||||||
else // Otherwise, the last block shall be padded with 10^i and XOR'ed with K2.
|
|
||||||
{
|
|
||||||
int paddedLength = len + (16 - len % 16);
|
|
||||||
paddedMessage = paddedLength < 0x800 ? stackalloc byte[paddedLength] : new byte[paddedLength];
|
|
||||||
|
|
||||||
paddedMessage.Slice(len).Clear();
|
|
||||||
paddedMessage[len] = 0x80;
|
|
||||||
data.CopyTo(paddedMessage);
|
|
||||||
|
|
||||||
for (int j = 0; j < k2.Length; j++)
|
|
||||||
paddedMessage[paddedMessage.Length - 16 + j] ^= k2[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
EncryptCbc128(paddedMessage, paddedMessage, key, zero); // The result of the previous process will be the input of the last encryption.
|
|
||||||
paddedMessage.Slice(paddedMessage.Length - 0x10).CopyTo(mac);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void LeftShiftBytes(ReadOnlySpan<byte> input, Span<byte> output)
|
|
||||||
{
|
|
||||||
Assert.SdkRequiresGreaterEqual(output.Length, input.Length);
|
|
||||||
|
|
||||||
byte carry = 0;
|
|
||||||
|
|
||||||
for (int i = input.Length - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
ushort b = (ushort)(input[i] << 1);
|
|
||||||
output[i] = (byte)((b & 0xff) + carry);
|
|
||||||
carry = (byte)((b & 0xff00) >> 8);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,36 @@
|
|||||||
using System;
|
using System;
|
||||||
using LibHac.Crypto.Impl;
|
using LibHac.Crypto.Impl;
|
||||||
|
|
||||||
namespace LibHac.Crypto
|
namespace LibHac.Crypto;
|
||||||
|
|
||||||
|
public class AesCbcEncryptor : ICipher
|
||||||
{
|
{
|
||||||
public class AesCbcEncryptor : ICipher
|
private AesCbcMode _baseCipher;
|
||||||
|
|
||||||
|
public AesCbcEncryptor(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
|
||||||
{
|
{
|
||||||
private AesCbcMode _baseCipher;
|
_baseCipher = new AesCbcMode();
|
||||||
|
_baseCipher.Initialize(key, iv, false);
|
||||||
public AesCbcEncryptor(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
|
|
||||||
{
|
|
||||||
_baseCipher = new AesCbcMode();
|
|
||||||
_baseCipher.Initialize(key, iv, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
|
||||||
{
|
|
||||||
_baseCipher.Encrypt(input, output);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AesCbcDecryptor : ICipher
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
{
|
{
|
||||||
private AesCbcMode _baseCipher;
|
_baseCipher.Encrypt(input, output);
|
||||||
|
}
|
||||||
public AesCbcDecryptor(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
|
}
|
||||||
{
|
|
||||||
_baseCipher = new AesCbcMode();
|
public class AesCbcDecryptor : ICipher
|
||||||
_baseCipher.Initialize(key, iv, true);
|
{
|
||||||
}
|
private AesCbcMode _baseCipher;
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
public AesCbcDecryptor(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
|
||||||
{
|
{
|
||||||
_baseCipher.Decrypt(input, output);
|
_baseCipher = new AesCbcMode();
|
||||||
}
|
_baseCipher.Initialize(key, iv, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
|
{
|
||||||
|
_baseCipher.Decrypt(input, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,41 +4,40 @@ using System.Runtime.Intrinsics;
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Crypto.Impl;
|
using LibHac.Crypto.Impl;
|
||||||
|
|
||||||
namespace LibHac.Crypto
|
namespace LibHac.Crypto;
|
||||||
|
|
||||||
|
public class AesCbcEncryptorNi : ICipherWithIv
|
||||||
{
|
{
|
||||||
public class AesCbcEncryptorNi : ICipherWithIv
|
private AesCbcModeNi _baseCipher;
|
||||||
|
|
||||||
|
public ref Buffer16 Iv => ref Unsafe.As<Vector128<byte>, Buffer16>(ref _baseCipher.Iv);
|
||||||
|
|
||||||
|
public AesCbcEncryptorNi(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
|
||||||
{
|
{
|
||||||
private AesCbcModeNi _baseCipher;
|
_baseCipher = new AesCbcModeNi();
|
||||||
|
_baseCipher.Initialize(key, iv, false);
|
||||||
public ref Buffer16 Iv => ref Unsafe.As<Vector128<byte>, Buffer16>(ref _baseCipher.Iv);
|
|
||||||
|
|
||||||
public AesCbcEncryptorNi(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
|
|
||||||
{
|
|
||||||
_baseCipher = new AesCbcModeNi();
|
|
||||||
_baseCipher.Initialize(key, iv, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
|
||||||
{
|
|
||||||
_baseCipher.Encrypt(input, output);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AesCbcDecryptorNi : ICipherWithIv
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
{
|
{
|
||||||
private AesCbcModeNi _baseCipher;
|
_baseCipher.Encrypt(input, output);
|
||||||
|
}
|
||||||
public ref Buffer16 Iv => ref Unsafe.As<Vector128<byte>, Buffer16>(ref _baseCipher.Iv);
|
}
|
||||||
|
|
||||||
public AesCbcDecryptorNi(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
|
public class AesCbcDecryptorNi : ICipherWithIv
|
||||||
{
|
{
|
||||||
_baseCipher = new AesCbcModeNi();
|
private AesCbcModeNi _baseCipher;
|
||||||
_baseCipher.Initialize(key, iv, true);
|
|
||||||
}
|
public ref Buffer16 Iv => ref Unsafe.As<Vector128<byte>, Buffer16>(ref _baseCipher.Iv);
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
public AesCbcDecryptorNi(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
|
||||||
{
|
{
|
||||||
_baseCipher.Decrypt(input, output);
|
_baseCipher = new AesCbcModeNi();
|
||||||
}
|
_baseCipher.Initialize(key, iv, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
|
{
|
||||||
|
_baseCipher.Decrypt(input, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,23 +2,22 @@
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Crypto.Impl;
|
using LibHac.Crypto.Impl;
|
||||||
|
|
||||||
namespace LibHac.Crypto
|
namespace LibHac.Crypto;
|
||||||
|
|
||||||
|
public class AesCtrCipher : ICipherWithIv
|
||||||
{
|
{
|
||||||
public class AesCtrCipher : ICipherWithIv
|
private AesCtrMode _baseCipher;
|
||||||
|
|
||||||
|
public ref Buffer16 Iv => ref _baseCipher.Iv;
|
||||||
|
|
||||||
|
public AesCtrCipher(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
|
||||||
{
|
{
|
||||||
private AesCtrMode _baseCipher;
|
_baseCipher = new AesCtrMode();
|
||||||
|
_baseCipher.Initialize(key, iv);
|
||||||
|
}
|
||||||
|
|
||||||
public ref Buffer16 Iv => ref _baseCipher.Iv;
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
|
{
|
||||||
public AesCtrCipher(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
|
_baseCipher.Transform(input, output);
|
||||||
{
|
|
||||||
_baseCipher = new AesCtrMode();
|
|
||||||
_baseCipher.Initialize(key, iv);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
|
||||||
{
|
|
||||||
_baseCipher.Transform(input, output);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,23 +4,22 @@ using System.Runtime.Intrinsics;
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Crypto.Impl;
|
using LibHac.Crypto.Impl;
|
||||||
|
|
||||||
namespace LibHac.Crypto
|
namespace LibHac.Crypto;
|
||||||
|
|
||||||
|
public class AesCtrCipherNi : ICipherWithIv
|
||||||
{
|
{
|
||||||
public class AesCtrCipherNi : ICipherWithIv
|
private AesCtrModeNi _baseCipher;
|
||||||
|
|
||||||
|
public ref Buffer16 Iv => ref Unsafe.As<Vector128<byte>, Buffer16>(ref _baseCipher.Iv);
|
||||||
|
|
||||||
|
public AesCtrCipherNi(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
|
||||||
{
|
{
|
||||||
private AesCtrModeNi _baseCipher;
|
_baseCipher = new AesCtrModeNi();
|
||||||
|
_baseCipher.Initialize(key, iv);
|
||||||
|
}
|
||||||
|
|
||||||
public ref Buffer16 Iv => ref Unsafe.As<Vector128<byte>, Buffer16>(ref _baseCipher.Iv);
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
|
{
|
||||||
public AesCtrCipherNi(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
|
_baseCipher.Transform(input, output);
|
||||||
{
|
|
||||||
_baseCipher = new AesCtrModeNi();
|
|
||||||
_baseCipher.Initialize(key, iv);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
|
||||||
{
|
|
||||||
_baseCipher.Transform(input, output);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,36 @@
|
|||||||
using System;
|
using System;
|
||||||
using LibHac.Crypto.Impl;
|
using LibHac.Crypto.Impl;
|
||||||
|
|
||||||
namespace LibHac.Crypto
|
namespace LibHac.Crypto;
|
||||||
|
|
||||||
|
public class AesEcbEncryptor : ICipher
|
||||||
{
|
{
|
||||||
public class AesEcbEncryptor : ICipher
|
private AesEcbMode _baseCipher;
|
||||||
|
|
||||||
|
public AesEcbEncryptor(ReadOnlySpan<byte> key)
|
||||||
{
|
{
|
||||||
private AesEcbMode _baseCipher;
|
_baseCipher = new AesEcbMode();
|
||||||
|
_baseCipher.Initialize(key, false);
|
||||||
public AesEcbEncryptor(ReadOnlySpan<byte> key)
|
|
||||||
{
|
|
||||||
_baseCipher = new AesEcbMode();
|
|
||||||
_baseCipher.Initialize(key, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
|
||||||
{
|
|
||||||
_baseCipher.Encrypt(input, output);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AesEcbDecryptor : ICipher
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
{
|
{
|
||||||
private AesEcbMode _baseCipher;
|
_baseCipher.Encrypt(input, output);
|
||||||
|
}
|
||||||
public AesEcbDecryptor(ReadOnlySpan<byte> key)
|
}
|
||||||
{
|
|
||||||
_baseCipher = new AesEcbMode();
|
public class AesEcbDecryptor : ICipher
|
||||||
_baseCipher.Initialize(key, true);
|
{
|
||||||
}
|
private AesEcbMode _baseCipher;
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
public AesEcbDecryptor(ReadOnlySpan<byte> key)
|
||||||
{
|
{
|
||||||
_baseCipher.Decrypt(input, output);
|
_baseCipher = new AesEcbMode();
|
||||||
}
|
_baseCipher.Initialize(key, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
|
{
|
||||||
|
_baseCipher.Decrypt(input, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,36 @@
|
|||||||
using System;
|
using System;
|
||||||
using LibHac.Crypto.Impl;
|
using LibHac.Crypto.Impl;
|
||||||
|
|
||||||
namespace LibHac.Crypto
|
namespace LibHac.Crypto;
|
||||||
|
|
||||||
|
public class AesEcbEncryptorNi : ICipher
|
||||||
{
|
{
|
||||||
public class AesEcbEncryptorNi : ICipher
|
private AesEcbModeNi _baseCipher;
|
||||||
|
|
||||||
|
public AesEcbEncryptorNi(ReadOnlySpan<byte> key)
|
||||||
{
|
{
|
||||||
private AesEcbModeNi _baseCipher;
|
_baseCipher = new AesEcbModeNi();
|
||||||
|
_baseCipher.Initialize(key, false);
|
||||||
public AesEcbEncryptorNi(ReadOnlySpan<byte> key)
|
|
||||||
{
|
|
||||||
_baseCipher = new AesEcbModeNi();
|
|
||||||
_baseCipher.Initialize(key, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
|
||||||
{
|
|
||||||
_baseCipher.Encrypt(input, output);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AesEcbDecryptorNi : ICipher
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
{
|
{
|
||||||
private AesEcbModeNi _baseCipher;
|
_baseCipher.Encrypt(input, output);
|
||||||
|
}
|
||||||
public AesEcbDecryptorNi(ReadOnlySpan<byte> key)
|
}
|
||||||
{
|
|
||||||
_baseCipher = new AesEcbModeNi();
|
public class AesEcbDecryptorNi : ICipher
|
||||||
_baseCipher.Initialize(key, true);
|
{
|
||||||
}
|
private AesEcbModeNi _baseCipher;
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
public AesEcbDecryptorNi(ReadOnlySpan<byte> key)
|
||||||
{
|
{
|
||||||
_baseCipher.Decrypt(input, output);
|
_baseCipher = new AesEcbModeNi();
|
||||||
}
|
_baseCipher.Initialize(key, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
|
{
|
||||||
|
_baseCipher.Decrypt(input, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,41 +2,40 @@
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Crypto.Impl;
|
using LibHac.Crypto.Impl;
|
||||||
|
|
||||||
namespace LibHac.Crypto
|
namespace LibHac.Crypto;
|
||||||
|
|
||||||
|
public class AesXtsEncryptor : ICipherWithIv
|
||||||
{
|
{
|
||||||
public class AesXtsEncryptor : ICipherWithIv
|
private AesXtsMode _baseCipher;
|
||||||
|
|
||||||
|
public ref Buffer16 Iv => ref _baseCipher.Iv;
|
||||||
|
|
||||||
|
public AesXtsEncryptor(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv)
|
||||||
{
|
{
|
||||||
private AesXtsMode _baseCipher;
|
_baseCipher = new AesXtsMode();
|
||||||
|
_baseCipher.Initialize(key1, key2, iv, false);
|
||||||
public ref Buffer16 Iv => ref _baseCipher.Iv;
|
|
||||||
|
|
||||||
public AesXtsEncryptor(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv)
|
|
||||||
{
|
|
||||||
_baseCipher = new AesXtsMode();
|
|
||||||
_baseCipher.Initialize(key1, key2, iv, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
|
||||||
{
|
|
||||||
_baseCipher.Encrypt(input, output);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AesXtsDecryptor : ICipherWithIv
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
{
|
{
|
||||||
private AesXtsMode _baseCipher;
|
_baseCipher.Encrypt(input, output);
|
||||||
|
}
|
||||||
public ref Buffer16 Iv => ref _baseCipher.Iv;
|
}
|
||||||
|
|
||||||
public AesXtsDecryptor(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv)
|
public class AesXtsDecryptor : ICipherWithIv
|
||||||
{
|
{
|
||||||
_baseCipher = new AesXtsMode();
|
private AesXtsMode _baseCipher;
|
||||||
_baseCipher.Initialize(key1, key2, iv, true);
|
|
||||||
}
|
public ref Buffer16 Iv => ref _baseCipher.Iv;
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
public AesXtsDecryptor(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv)
|
||||||
{
|
{
|
||||||
_baseCipher.Decrypt(input, output);
|
_baseCipher = new AesXtsMode();
|
||||||
}
|
_baseCipher.Initialize(key1, key2, iv, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
|
{
|
||||||
|
_baseCipher.Decrypt(input, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,41 +4,40 @@ using System.Runtime.Intrinsics;
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Crypto.Impl;
|
using LibHac.Crypto.Impl;
|
||||||
|
|
||||||
namespace LibHac.Crypto
|
namespace LibHac.Crypto;
|
||||||
|
|
||||||
|
public class AesXtsEncryptorNi : ICipherWithIv
|
||||||
{
|
{
|
||||||
public class AesXtsEncryptorNi : ICipherWithIv
|
private AesXtsModeNi _baseCipher;
|
||||||
|
|
||||||
|
public ref Buffer16 Iv => ref Unsafe.As<Vector128<byte>, Buffer16>(ref _baseCipher.Iv);
|
||||||
|
|
||||||
|
public AesXtsEncryptorNi(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv)
|
||||||
{
|
{
|
||||||
private AesXtsModeNi _baseCipher;
|
_baseCipher = new AesXtsModeNi();
|
||||||
|
_baseCipher.Initialize(key1, key2, iv, false);
|
||||||
public ref Buffer16 Iv => ref Unsafe.As<Vector128<byte>, Buffer16>(ref _baseCipher.Iv);
|
|
||||||
|
|
||||||
public AesXtsEncryptorNi(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv)
|
|
||||||
{
|
|
||||||
_baseCipher = new AesXtsModeNi();
|
|
||||||
_baseCipher.Initialize(key1, key2, iv, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
|
||||||
{
|
|
||||||
_baseCipher.Encrypt(input, output);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AesXtsDecryptorNi : ICipherWithIv
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
{
|
{
|
||||||
private AesXtsModeNi _baseCipher;
|
_baseCipher.Encrypt(input, output);
|
||||||
|
}
|
||||||
public ref Buffer16 Iv => ref Unsafe.As<Vector128<byte>, Buffer16>(ref _baseCipher.Iv);
|
}
|
||||||
|
|
||||||
public AesXtsDecryptorNi(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv)
|
public class AesXtsDecryptorNi : ICipherWithIv
|
||||||
{
|
{
|
||||||
_baseCipher = new AesXtsModeNi();
|
private AesXtsModeNi _baseCipher;
|
||||||
_baseCipher.Initialize(key1, key2, iv, true);
|
|
||||||
}
|
public ref Buffer16 Iv => ref Unsafe.As<Vector128<byte>, Buffer16>(ref _baseCipher.Iv);
|
||||||
|
|
||||||
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
public AesXtsDecryptorNi(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv)
|
||||||
{
|
{
|
||||||
_baseCipher.Decrypt(input, output);
|
_baseCipher = new AesXtsModeNi();
|
||||||
}
|
_baseCipher.Initialize(key1, key2, iv, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
|
||||||
|
{
|
||||||
|
_baseCipher.Decrypt(input, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,29 +2,28 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace LibHac.Crypto
|
namespace LibHac.Crypto;
|
||||||
|
|
||||||
|
internal static class CryptoUtil
|
||||||
{
|
{
|
||||||
internal static class CryptoUtil
|
public static bool IsSameBytes(ReadOnlySpan<byte> buffer1, ReadOnlySpan<byte> buffer2, int length)
|
||||||
{
|
{
|
||||||
public static bool IsSameBytes(ReadOnlySpan<byte> buffer1, ReadOnlySpan<byte> buffer2, int length)
|
if (buffer1.Length < (uint)length || buffer2.Length < (uint)length)
|
||||||
{
|
throw new ArgumentOutOfRangeException(nameof(length));
|
||||||
if (buffer1.Length < (uint)length || buffer2.Length < (uint)length)
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(length));
|
|
||||||
|
|
||||||
return IsSameBytes(ref MemoryMarshal.GetReference(buffer1), ref MemoryMarshal.GetReference(buffer2), length);
|
return IsSameBytes(ref MemoryMarshal.GetReference(buffer1), ref MemoryMarshal.GetReference(buffer2), length);
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static bool IsSameBytes(ref byte p1, ref byte p2, int length)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
result |= Unsafe.Add(ref p1, i) ^ Unsafe.Add(ref p2, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result == 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static bool IsSameBytes(ref byte p1, ref byte p2, int length)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
result |= Unsafe.Add(ref p1, i) ^ Unsafe.Add(ref p2, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
|
||||||
namespace LibHac.Crypto
|
namespace LibHac.Crypto;
|
||||||
{
|
|
||||||
public interface ICipher
|
|
||||||
{
|
|
||||||
void Transform(ReadOnlySpan<byte> input, Span<byte> output);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ICipherWithIv : ICipher
|
public interface ICipher
|
||||||
{
|
{
|
||||||
ref Buffer16 Iv { get; }
|
void Transform(ReadOnlySpan<byte> input, Span<byte> output);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public interface ICipherWithIv : ICipher
|
||||||
|
{
|
||||||
|
ref Buffer16 Iv { get; }
|
||||||
|
}
|
||||||
|
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