Use file-scoped namespaces

This commit is contained in:
Alex Barney 2021-11-14 12:08:57 -07:00
parent 6fe89a2966
commit 34dda02c38
688 changed files with 81696 additions and 82374 deletions

View File

@ -1 +1 @@
5.0.402
6.0.100

File diff suppressed because it is too large Load Diff

View File

@ -5,105 +5,104 @@ using System.Reflection;
using System.Text;
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;
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 +
"//-----------------------------------------------------------------------------";
Directory.CreateDirectory(directoryName);
}
// 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)
// 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))
{
if (string.IsNullOrWhiteSpace(relativePath))
return;
oldFile = File.ReadAllBytes(fullPath);
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))
{
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);
if (oldFile.Length >= 3)
hasBom = oldFile.AsSpan(0, 3).SequenceEqual(bom);
}
public static Stream GetResource(string name)
// Make line endings the same on Windows and Unix
if (Environment.NewLine == "\n")
{
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;
text = text.Replace("\n", "\r\n");
}
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;
}
currentDir = Path.GetDirectoryName(currentDir);
break;
}
if (currentDir == null)
throw new DirectoryNotFoundException("Unable to find project directory.");
return Path.Combine(currentDir, "src");
currentDir = Path.GetDirectoryName(currentDir);
}
if (currentDir == null)
throw new DirectoryNotFoundException("Unable to find project directory.");
return Path.Combine(currentDir, "src");
}
}

View File

@ -1,89 +1,88 @@
using System;
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;
public int Level { get; private set; }
Level = Math.Max(level, 0);
_indentation = new string(' ', Level * LevelSize);
}
private StringBuilder _sb = new StringBuilder();
private string _indentation = string.Empty;
private bool _hasIndentedCurrentLine;
private bool _lastLineWasEmpty;
public void IncreaseLevel() => SetLevel(Level + 1);
public void DecreaseLevel() => SetLevel(Level - 1);
public IndentingStringBuilder() { }
public IndentingStringBuilder(int levelSize) => LevelSize = levelSize;
public IndentingStringBuilder AppendLine()
{
_sb.AppendLine();
_hasIndentedCurrentLine = false;
_lastLineWasEmpty = true;
return this;
}
public void SetLevel(int level)
{
Level = Math.Max(level, 0);
_indentation = new string(' ', Level * LevelSize);
}
public void IncreaseLevel() => SetLevel(Level + 1);
public void DecreaseLevel() => SetLevel(Level - 1);
public IndentingStringBuilder AppendLine()
public IndentingStringBuilder AppendSpacerLine()
{
if (!_lastLineWasEmpty)
{
_sb.AppendLine();
_hasIndentedCurrentLine = false;
_lastLineWasEmpty = true;
return this;
}
public IndentingStringBuilder AppendSpacerLine()
{
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();
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();
}

File diff suppressed because it is too large Load Diff

View File

@ -8,386 +8,385 @@ using LibHac.Common.Keys;
using LibHac.Crypto;
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";
private static string GeneratedFilePath = "LibHac/Common/Keys/DefaultKeySet.Generated.cs";
KeySet keySet = CreateKeySet();
public static void Run()
{
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
};
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
};
}

View File

@ -2,25 +2,24 @@
using System.IO;
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.
// 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
private const string SolutionFileName = "LibHac.sln";
public static int Main(string[] args)
{
private const string SolutionFileName = "LibHac.sln";
public static int Main(string[] args)
if (!File.Exists(SolutionFileName))
{
if (!File.Exists(SolutionFileName))
{
Console.Error.WriteLine($"Could not find the solution file {SolutionFileName}.");
return 1;
}
KeysCodeGen.Run();
return 0;
Console.Error.WriteLine($"Could not find the solution file {SolutionFileName}.");
return 1;
}
KeysCodeGen.Run();
return 0;
}
}

View File

@ -3,6 +3,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>10.0</LangVersion>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>LibHacBuild.CodeGen</RootNamespace>
<IsPackable>False</IsPackable>

View File

@ -11,126 +11,125 @@ using Nuke.Common.IO;
using Nuke.Common.Tools.NuGet;
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);
AbsolutePath libDir = tempDir / "lib";
AbsolutePath relsFile = tempDir / "_rels" / ".rels";
EnsureCleanDirectory(tempDir);
List<string> fileList = UnzipPackage(path, tempDir);
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);
List<string> fileList = UnzipPackage(path, tempDir);
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);
Console.WriteLine(filename);
ReplaceLineEndings(filename);
}
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>();
UnzipFiles(package, dest);
using (var s = new ZipInputStream(File.OpenRead(package)))
foreach (string file in Directory.EnumerateFiles(libDir))
{
ZipEntry entry;
while ((entry = s.GetNextEntry()) != null)
{
fileList.Add(entry.Name);
}
byte[] data = File.ReadAllBytes(file);
sha.TransformBlock(data, 0, data.Length, data, 0);
}
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())
{
foreach (string file in Directory.EnumerateFiles(libDir))
if (rs.Attribute("Target").Value.Contains(".psmdcp"))
{
byte[] data = File.ReadAllBytes(file);
sha.TransformBlock(data, 0, data.Length, data, 0);
rs.Attribute("Target").Value = "/" + psmdcpPath;
}
sha.TransformFinalBlock(new byte[0], 0, 0);
return ToHexString(sha.Hash).ToLower().Substring(0, 32);
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;
}
}
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);
doc.Save(path);
}
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 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())
{
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("-", "");
}
public static string ToHexString(byte[] arr)
{
return BitConverter.ToString(arr).ToLower().Replace("-", "");
}
}

View File

@ -3,6 +3,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>10.0</LangVersion>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>LibHacBuild</RootNamespace>
<IsPackable>False</IsPackable>

View File

@ -3,57 +3,56 @@ using System.Diagnostics;
using System.Runtime.InteropServices;
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}")]
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct Uid : IEquatable<Uid>, IComparable<Uid>, IComparable
public static Uid Zero => default;
public readonly Id128 Id;
public Uid(ulong high, ulong low)
{
public static Uid Zero => default;
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;
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;
}

View File

@ -1,23 +1,22 @@
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;
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);
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);
}

View File

@ -1,13 +1,12 @@
using System.Runtime.InteropServices;
namespace LibHac.Arp
namespace LibHac.Arp;
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
public struct ApplicationLaunchProperty
{
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
public struct ApplicationLaunchProperty
{
[FieldOffset(0x0)] public ApplicationId ApplicationId;
[FieldOffset(0x8)] public uint Version;
[FieldOffset(0xC)] public Ncm.StorageId BaseStorageId;
[FieldOffset(0xD)] public Ncm.StorageId UpdateStorageId;
}
[FieldOffset(0x0)] public ApplicationId ApplicationId;
[FieldOffset(0x8)] public uint Version;
[FieldOffset(0xC)] public Ncm.StorageId BaseStorageId;
[FieldOffset(0xD)] public Ncm.StorageId UpdateStorageId;
}

View File

@ -3,73 +3,72 @@ using LibHac.Arp.Impl;
using LibHac.Common;
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;
private SharedRef<IReader> _reader;
_hosClient = horizonClient;
}
private readonly object _readerInitLocker = new object();
public void Dispose()
{
_reader.Destroy();
}
internal ArpClient(HorizonClient horizonClient)
{
_hosClient = horizonClient;
}
public Result GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty, ulong processId)
{
EnsureReaderInitialized();
public void Dispose()
{
_reader.Destroy();
}
return _reader.Get.GetApplicationLaunchProperty(out launchProperty, processId);
}
public Result GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty, ulong processId)
{
EnsureReaderInitialized();
public Result GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty, ApplicationId applicationId)
{
EnsureReaderInitialized();
return _reader.Get.GetApplicationLaunchProperty(out launchProperty, processId);
}
return _reader.Get.GetApplicationLaunchPropertyWithApplicationId(out launchProperty, applicationId);
}
public Result GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty, ApplicationId applicationId)
{
EnsureReaderInitialized();
public Result GetApplicationControlProperty(out ApplicationControlProperty controlProperty, ulong processId)
{
EnsureReaderInitialized();
return _reader.Get.GetApplicationLaunchPropertyWithApplicationId(out launchProperty, applicationId);
}
return _reader.Get.GetApplicationControlProperty(out controlProperty, processId);
}
public Result GetApplicationControlProperty(out ApplicationControlProperty controlProperty, ulong processId)
{
EnsureReaderInitialized();
public Result GetApplicationControlProperty(out ApplicationControlProperty controlProperty, ApplicationId applicationId)
{
EnsureReaderInitialized();
return _reader.Get.GetApplicationControlProperty(out controlProperty, processId);
}
return _reader.Get.GetApplicationControlPropertyWithApplicationId(out controlProperty, applicationId);
}
public Result GetApplicationControlProperty(out ApplicationControlProperty controlProperty, ApplicationId applicationId)
{
EnsureReaderInitialized();
private void EnsureReaderInitialized()
{
if (_reader.HasValue)
return;
return _reader.Get.GetApplicationControlPropertyWithApplicationId(out controlProperty, applicationId);
}
private void EnsureReaderInitialized()
lock (_readerInitLocker)
{
if (_reader.HasValue)
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)
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());
throw new HorizonResultException(rc, "Failed to initialize arp reader.");
}
_reader.SetByMove(ref reader.Ref());
}
}
}

View File

@ -1,13 +1,12 @@
using System;
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 GetApplicationControlProperty(out ApplicationControlProperty controlProperty, ulong processId);
Result GetApplicationControlPropertyWithApplicationId(out ApplicationControlProperty controlProperty, ApplicationId applicationId);
}
Result GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty, ulong processId);
Result GetApplicationLaunchPropertyWithApplicationId(out ApplicationLaunchProperty launchProperty, ApplicationId applicationId);
Result GetApplicationControlProperty(out ApplicationControlProperty controlProperty, ulong processId);
Result GetApplicationControlPropertyWithApplicationId(out ApplicationControlProperty controlProperty, ApplicationId applicationId);
}

View File

@ -5,86 +5,85 @@ using LibHac.Bcat.Impl.Service.Core;
using LibHac.Common;
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; }
private SharedRef<ServiceCreator>[] _serviceCreators;
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 readonly object _bcatServiceInitLocker = new object();
private readonly object _storageManagerInitLocker = new object();
private void InitBcatService(BcatServiceType type, string name, AccessControl accessControl)
{
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;
_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);
throw new HorizonResultException(rc, "Abort");
}
}
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);
if (rc.IsFailure())
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)
{
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;
}
StorageManager = new DeliveryCacheStorageManager(this);
return StorageManager;
}
}
}

View File

@ -1,10 +1,9 @@
namespace LibHac.Bcat
namespace LibHac.Bcat;
public enum BcatServiceType
{
public enum BcatServiceType
{
BcatU,
BcatS,
BcatM,
BcatA
}
BcatU,
BcatS,
BcatM,
BcatA
}

View File

@ -1,19 +1,18 @@
using System.Runtime.InteropServices;
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;
namespace LibHac.Bcat;
public DeliveryCacheDirectoryEntry(ref FileName name, long size, ref Digest digest)
{
Name = name;
Size = size;
Digest = digest;
}
[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)
{
Name = name;
Size = size;
Digest = digest;
}
}

View File

@ -4,26 +4,25 @@ using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Util;
namespace LibHac.Bcat
namespace LibHac.Bcat;
[DebuggerDisplay("{ToString()}")]
[StructLayout(LayoutKind.Sequential, Size = 16)]
public struct Digest
{
[DebuggerDisplay("{ToString()}")]
[StructLayout(LayoutKind.Sequential, Size = 16)]
public struct Digest
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
public byte this[int i]
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
get => Bytes[i];
set => Bytes[i] = value;
}
public byte this[int i]
{
get => Bytes[i];
set => Bytes[i] = value;
}
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
public override string ToString()
{
return Bytes.ToHexString();
}
public override string ToString()
{
return Bytes.ToHexString();
}
}

View File

@ -4,50 +4,49 @@ using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Util;
namespace LibHac.Bcat
namespace LibHac.Bcat;
[DebuggerDisplay("{ToString()}")]
[StructLayout(LayoutKind.Sequential, Size = MaxSize)]
public struct DirectoryName
{
[DebuggerDisplay("{ToString()}")]
[StructLayout(LayoutKind.Sequential, Size = MaxSize)]
public struct DirectoryName
private const int MaxSize = 0x20;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy2;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy3;
public 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 Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
public byte this[int i]
public bool IsValid()
{
Span<byte> name = Bytes;
int i;
for (i = 0; i < name.Length; i++)
{
get => Bytes[i];
set => Bytes[i] = value;
}
if (name[i] == 0)
break;
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)
if (!StringUtils.IsDigit(name[i]) && !StringUtils.IsAlpha(name[i]) && name[i] != '_' && name[i] != '-')
return false;
return name[i] == 0;
}
public override string ToString()
{
return StringUtils.Utf8ZToString(Bytes);
}
if (i == 0 || i == MaxSize)
return false;
return name[i] == 0;
}
public override string ToString()
{
return StringUtils.Utf8ZToString(Bytes);
}
}

View File

@ -4,53 +4,52 @@ using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Util;
namespace LibHac.Bcat
namespace LibHac.Bcat;
[DebuggerDisplay("{ToString()}")]
[StructLayout(LayoutKind.Sequential, Size = MaxSize)]
public struct FileName
{
[DebuggerDisplay("{ToString()}")]
[StructLayout(LayoutKind.Sequential, Size = MaxSize)]
public struct FileName
private const int MaxSize = 0x20;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy2;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy3;
public byte this[int i]
{
private const int MaxSize = 0x20;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy2;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy3;
public 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);
}
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);
}
}

View File

@ -2,26 +2,25 @@
using LibHac.Common;
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)
{
_serviceCreator = SharedRef<IServiceCreator>.CreateMove(ref serviceCreator);
}
public void Dispose()
{
_serviceCreator.Destroy();
}
public void Dispose()
{
_serviceCreator.Destroy();
}
public Result GetServiceObject(ref SharedRef<IDisposable> serviceObject)
{
serviceObject.SetByCopy(in _serviceCreator);
return Result.Success;
}
public Result GetServiceObject(ref SharedRef<IDisposable> serviceObject)
{
serviceObject.SetByCopy(in _serviceCreator);
return Result.Success;
}
}

View File

@ -1,11 +1,10 @@
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 GetCount(out int count);
}
Result Open(ref DirectoryName name);
Result Read(out int entriesRead, Span<DeliveryCacheDirectoryEntry> entryBuffer);
Result GetCount(out int count);
}

View File

@ -1,12 +1,11 @@
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 GetSize(out long size);
Result GetDigest(out Digest digest);
}
Result Open(ref DirectoryName directoryName, ref FileName fileName);
Result Read(out long bytesRead, long offset, Span<byte> destination);
Result GetSize(out long size);
Result GetDigest(out Digest digest);
}

View File

@ -1,12 +1,11 @@
using System;
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 EnumerateDeliveryCacheDirectory(out int namesRead, Span<DirectoryName> nameBuffer);
}
Result CreateFileService(ref SharedRef<IDeliveryCacheFileService> outFileService);
Result CreateDirectoryService(ref SharedRef<IDeliveryCacheDirectoryService> outDirectoryService);
Result EnumerateDeliveryCacheDirectory(out int namesRead, Span<DirectoryName> nameBuffer);
}

View File

@ -1,14 +1,13 @@
using System;
using LibHac.Common;
namespace LibHac.Bcat.Impl.Ipc
{
public interface IServiceCreator : IDisposable
{
Result CreateDeliveryCacheStorageService(ref SharedRef<IDeliveryCacheStorageService> outService,
ulong processId);
namespace LibHac.Bcat.Impl.Ipc;
Result CreateDeliveryCacheStorageServiceWithApplicationId(
ref SharedRef<IDeliveryCacheStorageService> outService, ApplicationId applicationId);
}
public interface IServiceCreator : IDisposable
{
Result CreateDeliveryCacheStorageService(ref SharedRef<IDeliveryCacheStorageService> outService,
ulong processId);
Result CreateDeliveryCacheStorageServiceWithApplicationId(
ref SharedRef<IDeliveryCacheStorageService> outService, ApplicationId applicationId);
}

View File

@ -1,15 +1,14 @@
using System;
namespace LibHac.Bcat.Impl.Service
namespace LibHac.Bcat.Impl.Service;
[Flags]
internal enum AccessControl
{
[Flags]
internal enum AccessControl
{
None = 0,
MountOwnDeliveryCacheStorage = 1 << 1,
MountOthersDeliveryCacheStorage = 1 << 2,
DeliveryTaskManagement = 1 << 3,
Debug = 1 << 4,
All = ~0
}
None = 0,
MountOwnDeliveryCacheStorage = 1 << 1,
MountOthersDeliveryCacheStorage = 1 << 2,
DeliveryTaskManagement = 1 << 3,
Debug = 1 << 4,
All = ~0
}

View File

@ -5,96 +5,95 @@ using LibHac.Common;
using LibHac.Fs;
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;
private const int MetaFileHeaderValue = 1;
Server = server;
}
private BcatServer Server { get; }
private object Locker { get; } = new object();
private DeliveryCacheDirectoryMetaEntry[] Entries { get; } = new DeliveryCacheDirectoryMetaEntry[MaxEntryCount];
public int Count { get; private set; }
public Result ReadApplicationDirectoryMeta(ulong applicationId, bool allowMissingMetaFile)
{
Span<byte> metaPath = stackalloc byte[0x50];
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;
}
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)
{
if (index >= Count)
{
return ResultBcat.NotFound.Log();
}
entry = Entries[index];
return Result.Success;
return ResultBcat.NotFound.Log();
}
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();
Result rc = fs.OpenFile(out FileHandle handle, path, OpenMode.Read);
if (rc.IsFailure())
if (ResultFs.PathNotFound.Includes(rc))
{
if (ResultFs.PathNotFound.Includes(rc))
if (allowMissingMetaFile)
{
if (allowMissingMetaFile)
{
Count = 0;
return Result.Success;
}
return ResultBcat.NotFound.LogConverted(rc);
Count = 0;
return Result.Success;
}
return rc;
return ResultBcat.NotFound.LogConverted(rc);
}
try
{
Count = 0;
int header = 0;
return rc;
}
// Verify the header value
rc = fs.ReadFile(out long bytesRead, handle, 0, SpanHelpers.AsByteSpan(ref header));
if (rc.IsFailure()) return rc;
try
{
Count = 0;
int header = 0;
if (bytesRead != sizeof(int) || header != MetaFileHeaderValue)
return ResultBcat.InvalidDeliveryCacheStorageFile.Log();
// Verify the header value
rc = fs.ReadFile(out long bytesRead, handle, 0, SpanHelpers.AsByteSpan(ref header));
if (rc.IsFailure()) return rc;
// 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;
if (bytesRead != sizeof(int) || header != MetaFileHeaderValue)
return ResultBcat.InvalidDeliveryCacheStorageFile.Log();
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;
}
finally
{
fs.CloseFile(handle);
}
Count = (int)((uint)bytesRead / Unsafe.SizeOf<DeliveryCacheDirectoryMetaEntry>());
return Result.Success;
}
finally
{
fs.CloseFile(handle);
}
}
}

View File

@ -1,11 +1,10 @@
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)]
internal struct DeliveryCacheDirectoryMetaEntry
{
[FieldOffset(0x00)] public DirectoryName Name;
[FieldOffset(0x20)] public Digest Digest;
}
[FieldOffset(0x00)] public DirectoryName Name;
[FieldOffset(0x20)] public Digest Digest;
}

View File

@ -6,116 +6,115 @@ using LibHac.Fs;
using LibHac.Fs.Fsa;
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;
private const int MetaFileHeaderValue = 1;
Server = server;
}
private BcatServer Server { get; }
private object Locker { get; } = new object();
private DeliveryCacheFileMetaEntry[] Entries { get; } = new DeliveryCacheFileMetaEntry[MaxEntryCount];
public int Count { get; private set; }
public Result ReadApplicationFileMeta(ulong applicationId, ref DirectoryName directoryName,
bool allowMissingMetaFile)
{
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;
}
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)
{
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();
}
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();
Result rc = fs.OpenFile(out FileHandle handle, path, OpenMode.Read);
if (rc.IsFailure())
if (StringUtils.CompareCaseInsensitive(Entries[i].Name.Bytes, fileName.Bytes) == 0)
{
if (ResultFs.PathNotFound.Includes(rc))
{
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>());
entry = Entries[i];
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);
}
}
}

View File

@ -1,13 +1,12 @@
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)]
internal struct DeliveryCacheFileMetaEntry
{
[FieldOffset(0x00)] public FileName Name;
[FieldOffset(0x20)] public long Id;
[FieldOffset(0x28)] public long Size;
[FieldOffset(0x30)] public Digest Digest;
}
[FieldOffset(0x00)] public FileName Name;
[FieldOffset(0x20)] public long Id;
[FieldOffset(0x28)] public long Size;
[FieldOffset(0x30)] public Digest Digest;
}

View File

@ -6,394 +6,393 @@ using LibHac.Fs.Fsa;
using LibHac.Fs.Shim;
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();
private Entry[] Entries { get; } = new Entry[MaxEntryCount];
private bool DisableStorage { get; set; }
private struct Entry
public Result Open(ulong applicationId)
{
lock (_locker)
{
public ulong ApplicationId { get; set; }
public long RefCount { get; set; }
}
// Find an existing storage entry for this application ID or get an empty one
Result rc = FindOrGetUnusedEntry(out int index, applicationId);
if (rc.IsFailure()) return rc;
public DeliveryCacheStorageManager(BcatServer server)
{
Server = server;
DisableStorage = false;
}
ref Entry entry = ref Entries[index];
public Result Open(ulong applicationId)
{
lock (_locker)
if (entry.RefCount != 0)
{
// Find an existing storage entry for this application ID or get an empty one
Result rc = FindOrGetUnusedEntry(out int index, applicationId);
if (rc.IsFailure()) return rc;
return ResultBcat.TargetLocked.Log();
}
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 sb = new U8StringBuilder(mountName.Name);
sb.Append(DeliveryCacheMountNamePrefix)
.AppendFormat(index, 'd', 2);
// Mount the save if enabled
// Unmount the entry's savedata
if (!DisableStorage)
{
rc = Server.GetFsClient()
.MountBcatSaveData(new U8Span(mountName.Name), new Ncm.ApplicationId(applicationId));
if (rc.IsFailure())
{
if (ResultFs.TargetNotFound.Includes(rc))
return ResultBcat.SaveDataNotFound.LogConverted(rc);
return rc;
}
Server.GetFsClient().Unmount(new U8Span(mountName.Name));
}
// Update the storage entry
entry.ApplicationId = applicationId;
entry.RefCount++;
// Clear the entry
entry.ApplicationId = 0;
// 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;
}
}
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);
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 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
}
entryIndex = i;
return Result.Success;
}
}
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;
}
}
// 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' };
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' };
}

View File

@ -3,105 +3,104 @@ using LibHac.Bcat.Impl.Ipc;
using LibHac.Bcat.Impl.Service.Core;
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; }
private object Locker { get; } = new object();
private DeliveryCacheStorageService Parent { get; }
Server = server;
Parent = parent;
ApplicationId = applicationId;
Access = accessControl;
}
// 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 Result Open(ref DirectoryName name)
{
if (!name.IsValid())
return ResultBcat.InvalidArgument.Log();
public DeliveryCacheDirectoryService(BcatServer server, DeliveryCacheStorageService parent, ulong applicationId,
AccessControl accessControl)
lock (Locker)
{
Server = server;
Parent = parent;
ApplicationId = applicationId;
Access = accessControl;
}
if (IsDirectoryOpen)
return ResultBcat.AlreadyOpen.Log();
public Result Open(ref DirectoryName name)
{
if (!name.IsValid())
return ResultBcat.InvalidArgument.Log();
var metaReader = new DeliveryCacheFileMetaAccessor(Server);
Result rc = metaReader.ReadApplicationFileMeta(ApplicationId, ref name, false);
if (rc.IsFailure()) return rc;
lock (Locker)
{
if (IsDirectoryOpen)
return ResultBcat.AlreadyOpen.Log();
Count = metaReader.Count;
_name = name;
IsDirectoryOpen = true;
var metaReader = new DeliveryCacheFileMetaAccessor(Server);
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();
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();
}
}

View File

@ -5,119 +5,118 @@ using LibHac.Common;
using LibHac.Fs;
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; }
private object Locker { get; } = new object();
private DeliveryCacheStorageService Parent { get; }
Server = server;
Parent = parent;
ApplicationId = applicationId;
Access = accessControl;
}
// 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 Result Open(ref DirectoryName directoryName, ref FileName fileName)
{
if (!directoryName.IsValid())
return ResultBcat.InvalidArgument.Log();
public DeliveryCacheFileService(BcatServer server, DeliveryCacheStorageService parent, ulong applicationId,
AccessControl accessControl)
{
Server = server;
Parent = parent;
ApplicationId = applicationId;
Access = accessControl;
}
if (!fileName.IsValid())
return ResultBcat.InvalidArgument.Log();
public Result Open(ref DirectoryName directoryName, ref FileName fileName)
{
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()
lock (Locker)
{
if (IsFileOpen)
{
Server.GetFsClient().CloseFile(_handle);
}
return ResultBcat.AlreadyOpen.Log();
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();
}
}

View File

@ -5,108 +5,107 @@ using LibHac.Bcat.Impl.Service.Core;
using LibHac.Common;
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;
private BcatServer Server { get; }
Server = server;
ApplicationId = applicationId;
Access = accessControl;
}
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)
public Result CreateFileService(ref SharedRef<IDeliveryCacheFileService> service)
{
lock (Locker)
{
Server = server;
ApplicationId = applicationId;
Access = accessControl;
}
if (FileServiceOpenCount >= MaxOpenCount)
return ResultBcat.ServiceOpenLimitReached.Log();
public Result CreateFileService(ref SharedRef<IDeliveryCacheFileService> service)
{
lock (Locker)
{
if (FileServiceOpenCount >= MaxOpenCount)
return ResultBcat.ServiceOpenLimitReached.Log();
service.Reset(new DeliveryCacheFileService(Server, this, ApplicationId, Access));
service.Reset(new DeliveryCacheFileService(Server, this, ApplicationId, Access));
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);
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);
}
}

View File

@ -2,58 +2,57 @@
using LibHac.Bcat.Impl.Ipc;
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
internal class ServiceCreator : IServiceCreator
private BcatServer Server { get; }
// 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
private string ServiceName { get; }
private AccessControl AccessControl { get; }
public void Dispose() { }
public ServiceCreator(BcatServer server, string serviceName, AccessControl accessControl)
{
Server = server;
ServiceName = serviceName;
AccessControl = accessControl;
}
public Result CreateDeliveryCacheStorageService(ref SharedRef<IDeliveryCacheStorageService> outService,
ulong processId)
{
Result rc = Server.Hos.Arp.GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty,
processId);
public void Dispose() { }
if (rc.IsFailure())
return ResultBcat.NotFound.LogConverted(rc);
public Result CreateDeliveryCacheStorageService(ref SharedRef<IDeliveryCacheStorageService> outService,
ulong processId)
{
Result rc = Server.Hos.Arp.GetApplicationLaunchProperty(out ApplicationLaunchProperty launchProperty,
processId);
return CreateDeliveryCacheStorageServiceImpl(ref outService, launchProperty.ApplicationId);
}
if (rc.IsFailure())
return ResultBcat.NotFound.LogConverted(rc);
public Result CreateDeliveryCacheStorageServiceWithApplicationId(
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(
ref SharedRef<IDeliveryCacheStorageService> outService, ApplicationId applicationId)
{
if (!AccessControl.HasFlag(AccessControl.MountOthersDeliveryCacheStorage))
return ResultBcat.PermissionDenied.Log();
private Result CreateDeliveryCacheStorageServiceImpl(ref SharedRef<IDeliveryCacheStorageService> outService,
ApplicationId applicationId)
{
Result rc = Server.GetStorageManager().Open(applicationId.Value);
if (rc.IsFailure()) return rc;
return CreateDeliveryCacheStorageServiceImpl(ref outService, applicationId);
}
// todo: Check if network account required
private Result CreateDeliveryCacheStorageServiceImpl(ref SharedRef<IDeliveryCacheStorageService> outService,
ApplicationId applicationId)
{
Result rc = Server.GetStorageManager().Open(applicationId.Value);
if (rc.IsFailure()) return rc;
outService.Reset(new DeliveryCacheStorageService(Server, applicationId.Value, AccessControl));
// todo: Check if network account required
outService.Reset(new DeliveryCacheStorageService(Server, applicationId.Value, AccessControl));
return Result.Success;
}
return Result.Success;
}
}

View File

@ -9,47 +9,46 @@
// code generation portion of the build.
//-----------------------------------------------------------------------------
namespace LibHac.Bcat
{
public static class ResultBcat
{
public const int ModuleBcat = 122;
namespace LibHac.Bcat;
/// <summary>Error code: 2122-0001; Inner value: 0x27a</summary>
public static Result.Base InvalidArgument => new Result.Base(ModuleBcat, 1);
/// <summary>Error code: 2122-0002; Inner value: 0x47a</summary>
public static Result.Base NotFound => new Result.Base(ModuleBcat, 2);
/// <summary>Error code: 2122-0003; Inner value: 0x67a</summary>
public static Result.Base TargetLocked => new Result.Base(ModuleBcat, 3);
/// <summary>Error code: 2122-0004; Inner value: 0x87a</summary>
public static Result.Base TargetAlreadyMounted => new Result.Base(ModuleBcat, 4);
/// <summary>Error code: 2122-0005; Inner value: 0xa7a</summary>
public static Result.Base TargetNotMounted => new Result.Base(ModuleBcat, 5);
/// <summary>Error code: 2122-0006; Inner value: 0xc7a</summary>
public static Result.Base AlreadyOpen => new Result.Base(ModuleBcat, 6);
/// <summary>Error code: 2122-0007; Inner value: 0xe7a</summary>
public static Result.Base NotOpen => new Result.Base(ModuleBcat, 7);
/// <summary>Error code: 2122-0008; Inner value: 0x107a</summary>
public static Result.Base InternetRequestDenied => new Result.Base(ModuleBcat, 8);
/// <summary>Error code: 2122-0009; Inner value: 0x127a</summary>
public static Result.Base ServiceOpenLimitReached => new Result.Base(ModuleBcat, 9);
/// <summary>Error code: 2122-0010; Inner value: 0x147a</summary>
public static Result.Base SaveDataNotFound => new Result.Base(ModuleBcat, 10);
/// <summary>Error code: 2122-0031; Inner value: 0x3e7a</summary>
public static Result.Base NetworkServiceAccountNotAvailable => new Result.Base(ModuleBcat, 31);
/// <summary>Error code: 2122-0080; Inner value: 0xa07a</summary>
public static Result.Base PassphrasePathNotFound => new Result.Base(ModuleBcat, 80);
/// <summary>Error code: 2122-0081; Inner value: 0xa27a</summary>
public static Result.Base DataVerificationFailed => new Result.Base(ModuleBcat, 81);
/// <summary>Error code: 2122-0090; Inner value: 0xb47a</summary>
public static Result.Base PermissionDenied => new Result.Base(ModuleBcat, 90);
/// <summary>Error code: 2122-0091; Inner value: 0xb67a</summary>
public static Result.Base AllocationFailed => new Result.Base(ModuleBcat, 91);
/// <summary>Error code: 2122-0098; Inner value: 0xc47a</summary>
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);
}
public static class ResultBcat
{
public const int ModuleBcat = 122;
/// <summary>Error code: 2122-0001; Inner value: 0x27a</summary>
public static Result.Base InvalidArgument => new Result.Base(ModuleBcat, 1);
/// <summary>Error code: 2122-0002; Inner value: 0x47a</summary>
public static Result.Base NotFound => new Result.Base(ModuleBcat, 2);
/// <summary>Error code: 2122-0003; Inner value: 0x67a</summary>
public static Result.Base TargetLocked => new Result.Base(ModuleBcat, 3);
/// <summary>Error code: 2122-0004; Inner value: 0x87a</summary>
public static Result.Base TargetAlreadyMounted => new Result.Base(ModuleBcat, 4);
/// <summary>Error code: 2122-0005; Inner value: 0xa7a</summary>
public static Result.Base TargetNotMounted => new Result.Base(ModuleBcat, 5);
/// <summary>Error code: 2122-0006; Inner value: 0xc7a</summary>
public static Result.Base AlreadyOpen => new Result.Base(ModuleBcat, 6);
/// <summary>Error code: 2122-0007; Inner value: 0xe7a</summary>
public static Result.Base NotOpen => new Result.Base(ModuleBcat, 7);
/// <summary>Error code: 2122-0008; Inner value: 0x107a</summary>
public static Result.Base InternetRequestDenied => new Result.Base(ModuleBcat, 8);
/// <summary>Error code: 2122-0009; Inner value: 0x127a</summary>
public static Result.Base ServiceOpenLimitReached => new Result.Base(ModuleBcat, 9);
/// <summary>Error code: 2122-0010; Inner value: 0x147a</summary>
public static Result.Base SaveDataNotFound => new Result.Base(ModuleBcat, 10);
/// <summary>Error code: 2122-0031; Inner value: 0x3e7a</summary>
public static Result.Base NetworkServiceAccountNotAvailable => new Result.Base(ModuleBcat, 31);
/// <summary>Error code: 2122-0080; Inner value: 0xa07a</summary>
public static Result.Base PassphrasePathNotFound => new Result.Base(ModuleBcat, 80);
/// <summary>Error code: 2122-0081; Inner value: 0xa27a</summary>
public static Result.Base DataVerificationFailed => new Result.Base(ModuleBcat, 81);
/// <summary>Error code: 2122-0090; Inner value: 0xb47a</summary>
public static Result.Base PermissionDenied => new Result.Base(ModuleBcat, 90);
/// <summary>Error code: 2122-0091; Inner value: 0xb67a</summary>
public static Result.Base AllocationFailed => new Result.Base(ModuleBcat, 91);
/// <summary>Error code: 2122-0098; Inner value: 0xc47a</summary>
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);
}

View File

@ -1,133 +1,132 @@
using System;
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; }
public int LengthBits { get; private set; }
public int Position { get; set; }
public int Remaining => LengthBits - Position;
Buffer = buffer;
LengthBits = Buffer?.Length * 8 ?? 0;
Position = 0;
}
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;
LengthBits = Buffer?.Length * 8 ?? 0;
Position = 0;
if (Position >= LengthBits) return 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);
Position += bitCount;
int value = Buffer[byteIndex] << 8 | Buffer[byteIndex + 1];
value &= 0xFFFF >> bitIndex;
value >>= 16 - bitCount - bitIndex;
return value;
}
//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)
if (bitCount <= 17 && Remaining >= 24)
{
int offset = (1 << (bitCount - 1)) - (int)bias;
int value = PeekInt(bitCount) - offset;
Position += bitCount;
int value = Buffer[byteIndex] << 16 | Buffer[byteIndex + 1] << 8 | Buffer[byteIndex + 2];
value &= 0xFFFFFF >> bitIndex;
value >>= 24 - bitCount - bitIndex;
return value;
}
//public void AlignPosition(int multiple)
//{
// Position = Helpers.GetNextMultiple(Position, multiple);
//}
public int PeekInt(int bitCount)
if (bitCount <= 25 && Remaining >= 32)
{
Debug.Assert(bitCount >= 0 && bitCount <= 32);
if (bitCount > Remaining)
{
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;
}
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);
}
/// <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
private int PeekIntFallback(int bitCount)
{
int value = 0;
int byteIndex = Position / 8;
int bitIndex = Position % 8;
while (bitCount > 0)
{
Positive = 1,
Negative = 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;
}
/// <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
}
}

View File

@ -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;
}
}

View File

@ -6,89 +6,88 @@ using LibHac.Common;
using LibHac.Crypto;
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
[FieldOffset(0x00)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy1;
[FieldOffset(0x20)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy2;
[FieldOffset(0x40)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy3;
[FieldOffset(0x60)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy4;
[FieldOffset(0x80)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy5;
[FieldOffset(0xA0)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer16 _dummy6;
[FieldOffset(0x00)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy1;
[FieldOffset(0x20)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy2;
[FieldOffset(0x40)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy3;
[FieldOffset(0x60)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy4;
[FieldOffset(0x80)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer32 _dummy5;
[FieldOffset(0xA0)] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Buffer16 _dummy6;
#endif
[FieldOffset(0x00)] public AesCmac Cmac;
[FieldOffset(0x10)] public AesIv Counter;
[FieldOffset(0x00)] public AesCmac Cmac;
[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 readonly ReadOnlySpan<byte> ReadOnlyBytes => SpanHelpers.AsReadOnlyByteSpan(in this);
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 EncryptedKeyBlob value)
{
return SpanHelpers.AsReadOnlyByteSpan(in value);
}
public override readonly string ToString() => ReadOnlyBytes.ToHexString();
}
[DebuggerDisplay("{ToString()}")]
[StructLayout(LayoutKind.Explicit, Size = 0x90)]
public struct KeyBlob
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool IsZeros()
{
#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
ReadOnlySpan<ulong> ulongSpan = MemoryMarshal.Cast<byte, ulong>(ReadOnlyBytes);
[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()
for (int i = 0; i < ulongSpan.Length; i++)
{
ReadOnlySpan<ulong> ulongSpan = MemoryMarshal.Cast<byte, ulong>(ReadOnlyBytes);
for (int i = 0; i < ulongSpan.Length; i++)
{
if (ulongSpan[i] != 0)
return false;
}
return true;
if (ulongSpan[i] != 0)
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<byte>(in KeyBlob value)
{
return SpanHelpers.AsReadOnlyByteSpan(in value);
}
public override readonly string ToString() => ReadOnlyBytes.ToHexString();
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
[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

View File

@ -8,157 +8,156 @@ using LibHac.Util;
using System.Diagnostics;
#endif
namespace LibHac.Boot
namespace LibHac.Boot;
[StructLayout(LayoutKind.Explicit, Size = 0x200)]
public struct Package2Header
{
[StructLayout(LayoutKind.Explicit, Size = 0x200)]
public struct Package2Header
internal const int Package2SizeMax = (1024 * 1024 * 8) - (1024 * 16); // 8MB - 16KB
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
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))
{
if (!Rsa.VerifyRsa2048PssSha256(Signature, modulus, RsaPublicKeyExponent, data))
{
return ResultLibHac.InvalidPackage2HeaderSignature.Log();
}
return Result.Success;
return ResultLibHac.InvalidPackage2HeaderSignature.Log();
}
#if DEBUG
[DebuggerBrowsable(DebuggerBrowsableState.Never)] [FieldOffset(0x00)] private readonly Padding100 PaddingForVsDebugging;
#endif
return Result.Success;
}
[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;
[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
}
}

View File

@ -8,263 +8,262 @@ using LibHac.Fs;
using LibHac.FsSystem;
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>
/// Parses a package2 file and opens the payloads within.
/// </summary>
public class Package2StorageReader : IDisposable
private const int KernelPayloadIndex = 0;
private const int IniPayloadIndex = 1;
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;
private const int IniPayloadIndex = 1;
_storage.Destroy();
}
private SharedRef<IStorage> _storage;
private Package2Header _header;
private KeySet _keySet;
private Crypto.AesKey _key;
/// <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;
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();
}
/// <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;
outPayloadStorage.Reset(payloadSubStorage);
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)
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>
/// Opens an <see cref="IStorage"/> of the kernel payload.
/// </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 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;
return OpenPayload(ref outIniStorage, IniPayloadIndex);
}
/// <summary>
/// Opens an <see cref="IStorage"/> of the kernel payload.
/// </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)
// Ini is embedded in the kernel
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))
{
return OpenPayload(ref outKernelStorage, KernelPayloadIndex);
// Unable to find the ini. Could be a new, unsupported layout.
return ResultLibHac.NotImplemented.Log();
}
/// <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)
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))
{
if (HasIniPayload())
{
return OpenPayload(ref outIniStorage, IniPayloadIndex);
}
byte[] array = buffer.Array;
var hashBuffer = new Buffer32();
var sha = new Sha256Generator();
// Ini is embedded in the kernel
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
// Verify hashes match for all payloads.
for (int i = 0; i < Package2Header.PayloadCount; i++)
{
if (_header.Meta.PayloadSizes[i] == 0)
continue;
using var payloadStorage = new UniqueRef<IStorage>();
Result rc = OpenPayload(ref payloadStorage.Ref(), i);
if (rc.IsFailure()) return rc.Miss();
int offset = _header.Meta.GetPayloadFileOffset(i);
int size = (int)_header.Meta.PayloadSizes[i];
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
Unsafe.As<Package2Meta, Buffer16>(ref dest) = iv;
storages.Add(payloadStorage.Release());
}
private bool HasIniPayload()
{
return _header.Meta.PayloadSizes[IniPayloadIndex] != 0;
}
outPackageStorage.Reset(new ConcatenationStorage(storages, true));
return Result.Success;
}
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;
}
}

View File

@ -1,275 +1,274 @@
using System.IO;
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;
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;
stream.Position = 0x0;
Magic = reader.ReadUtf8(0x4);
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 = 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 = 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);
}
}
}

View File

@ -4,323 +4,322 @@ using LibHac.FsSystem.NcaUtils;
using LibHac.Ncm;
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; }
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)
using (var reader = new BinaryReader(file))
{
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());
uint version = reader.ReadUInt32();
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);
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();
}
}

View File

@ -3,108 +3,107 @@ using System.Diagnostics;
using System.Runtime.CompilerServices;
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>
/// 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.
/// The number of elements of type <typeparamref name="T"/> in the <see cref="BlitSpan{T}"/>.
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
public ref struct BlitSpan<T> where T : unmanaged
public int Length => _buffer.Length;
/// <summary>
/// A reference to the first element in this collection.
/// </summary>
public ref T Value
{
private readonly Span<T> _buffer;
/// <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
{
get
{
Debug.Assert(_buffer.Length > 0);
return ref MemoryMarshal.GetReference(_buffer);
}
}
Debug.Assert(_buffer.Length > 0);
/// <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;
return ref MemoryMarshal.GetReference(_buffer);
}
}
/// <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;
}
}

View File

@ -3,68 +3,67 @@ using System.Diagnostics;
using System.Runtime.CompilerServices;
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>
/// 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>
/// <typeparam name="T">The element type.</typeparam>
public readonly struct BlitStruct<T> where T : unmanaged
public ref T Value
{
private readonly byte[] _buffer;
public int Length => _buffer.Length / Unsafe.SizeOf<T>();
/// <summary>
/// A reference to the first element in this collection.
/// </summary>
public ref T Value
get
{
get
{
Debug.Assert(_buffer.Length >= Unsafe.SizeOf<T>());
Debug.Assert(_buffer.Length >= Unsafe.SizeOf<T>());
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;
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;
}
}

View File

@ -4,125 +4,124 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
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>
/// 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
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
public byte this[int i]
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
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();
}
get => Bytes[i];
set => Bytes[i] = value;
}
[DebuggerDisplay("{ToString()}")]
[StructLayout(LayoutKind.Sequential, Size = 32)]
public struct Buffer32
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)
{
[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();
}
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()}")]
[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();
}
}

View File

@ -2,21 +2,20 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays
namespace LibHac.Common.FixedArrays;
[StructLayout(LayoutKind.Sequential)]
public struct Array1<T>
{
[StructLayout(LayoutKind.Sequential)]
public struct Array1<T>
{
public const int Length = 1;
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 readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array1<T> value) => value.ItemsRo;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array1<T> value) => value.ItemsRo;
}

View File

@ -2,32 +2,31 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays
namespace LibHac.Common.FixedArrays;
[StructLayout(LayoutKind.Sequential)]
public struct Array12<T>
{
[StructLayout(LayoutKind.Sequential)]
public struct Array12<T>
{
public const int Length = 12;
public const int Length = 12;
private T _1;
private T _2;
private T _3;
private T _4;
private T _5;
private T _6;
private T _7;
private T _8;
private T _9;
private T _10;
private T _11;
private T _12;
private T _1;
private T _2;
private T _3;
private T _4;
private T _5;
private T _6;
private T _7;
private T _8;
private T _9;
private T _10;
private T _11;
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 readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array12<T> value) => value.ItemsRo;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array12<T> value) => value.ItemsRo;
}

View File

@ -2,31 +2,30 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays
namespace LibHac.Common.FixedArrays;
[StructLayout(LayoutKind.Sequential)]
public struct Array128<T>
{
[StructLayout(LayoutKind.Sequential)]
public struct Array128<T>
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
{
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)]
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;
}

View File

@ -2,31 +2,30 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays
namespace LibHac.Common.FixedArrays;
[StructLayout(LayoutKind.Sequential)]
public struct Array16<T>
{
[StructLayout(LayoutKind.Sequential)]
public struct Array16<T>
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
{
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)]
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;
}

View File

@ -2,22 +2,21 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays
namespace LibHac.Common.FixedArrays;
[StructLayout(LayoutKind.Sequential)]
public struct Array2<T>
{
[StructLayout(LayoutKind.Sequential)]
public struct Array2<T>
{
public const int Length = 2;
public const int Length = 2;
private T _1;
private T _2;
private T _1;
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 readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array2<T> value) => value.ItemsRo;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array2<T> value) => value.ItemsRo;
}

View File

@ -2,31 +2,30 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays
namespace LibHac.Common.FixedArrays;
[StructLayout(LayoutKind.Sequential)]
public struct Array256<T>
{
[StructLayout(LayoutKind.Sequential)]
public struct Array256<T>
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
{
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)]
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;
}

View File

@ -2,23 +2,22 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays
namespace LibHac.Common.FixedArrays;
[StructLayout(LayoutKind.Sequential)]
public struct Array3<T>
{
[StructLayout(LayoutKind.Sequential)]
public struct Array3<T>
{
public const int Length = 3;
public const int Length = 3;
private T _1;
private T _2;
private T _3;
private T _1;
private T _2;
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 readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array3<T> value) => value.ItemsRo;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array3<T> value) => value.ItemsRo;
}

View File

@ -2,31 +2,30 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays
namespace LibHac.Common.FixedArrays;
[StructLayout(LayoutKind.Sequential)]
public struct Array32<T>
{
[StructLayout(LayoutKind.Sequential)]
public struct Array32<T>
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
{
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)]
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;
}

View File

@ -2,24 +2,23 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays
namespace LibHac.Common.FixedArrays;
[StructLayout(LayoutKind.Sequential)]
public struct Array4<T>
{
[StructLayout(LayoutKind.Sequential)]
public struct Array4<T>
{
public const int Length = 4;
public const int Length = 4;
private T _1;
private T _2;
private T _3;
private T _4;
private T _1;
private T _2;
private T _3;
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 readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array4<T> value) => value.ItemsRo;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array4<T> value) => value.ItemsRo;
}

View File

@ -2,25 +2,24 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays
namespace LibHac.Common.FixedArrays;
[StructLayout(LayoutKind.Sequential)]
public struct Array5<T>
{
[StructLayout(LayoutKind.Sequential)]
public struct Array5<T>
{
public const int Length = 5;
public const int Length = 5;
private T _1;
private T _2;
private T _3;
private T _4;
private T _5;
private T _1;
private T _2;
private T _3;
private T _4;
private T _5;
public ref T this[int i] => ref Items[i];
public ref T this[int i] => ref Items[i];
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array5<T> value) => value.ItemsRo;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array5<T> value) => value.ItemsRo;
}

View File

@ -2,31 +2,30 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays
namespace LibHac.Common.FixedArrays;
[StructLayout(LayoutKind.Sequential)]
public struct Array64<T>
{
[StructLayout(LayoutKind.Sequential)]
public struct Array64<T>
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
{
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)]
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;
}

View File

@ -2,28 +2,27 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays
namespace LibHac.Common.FixedArrays;
[StructLayout(LayoutKind.Sequential)]
public struct Array8<T>
{
[StructLayout(LayoutKind.Sequential)]
public struct Array8<T>
{
public const int Length = 8;
public const int Length = 8;
private T _1;
private T _2;
private T _3;
private T _4;
private T _5;
private T _6;
private T _7;
private T _8;
private T _1;
private T _2;
private T _3;
private T _4;
private T _5;
private T _6;
private T _7;
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 readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
public Span<T> Items => SpanHelpers.CreateSpan(ref _1, Length);
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _1, Length);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array8<T> value) => value.ItemsRo;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array8<T> value) => value.ItemsRo;
}

View File

@ -1,41 +1,40 @@
using System.Diagnostics.CodeAnalysis;
using LibHac.Fs;
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);
namespace LibHac.Common;
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
};
}
[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
{
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
};
}

View File

@ -3,83 +3,82 @@ using System.Diagnostics;
using System.Runtime.InteropServices;
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>
/// A generic 128-bit ID value.
/// </summary>
[DebuggerDisplay("{ToString()}")]
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct Id128 : IEquatable<Id128>, IComparable<Id128>, IComparable
public readonly ulong High;
public readonly ulong Low;
public static Id128 Zero => default;
public Id128(ulong high, ulong low)
{
public readonly ulong High;
public readonly ulong 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;
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;
}

View File

@ -2,94 +2,93 @@
using System.Runtime.CompilerServices;
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;
private readonly object _mutex;
public bool IsInitialized => _mutex == null;
Complete = 1 << 0,
Pending = 1 << 1,
Waiting = 1 << 2
}
private const byte GuardBitComplete = 1 << 0;
[Flags]
private enum InitStatus : byte
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public InitializationGuard(ref nint guard, object mutex)
{
if (IsGuardInitialized(guard) || !AcquireGuard(ref guard, mutex))
{
Complete = 1 << 0,
Pending = 1 << 1,
Waiting = 1 << 2
this = default;
return;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public InitializationGuard(ref nint guard, object mutex)
_guard = new Ref<nint>(ref guard);
_mutex = mutex;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (!IsInitialized)
{
if (IsGuardInitialized(guard) || !AcquireGuard(ref guard, 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;
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;
}
}

View File

@ -4,61 +4,60 @@ using System.Runtime.InteropServices;
#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")]
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)
fixed (byte* pBytes = bytes)
fixed (char* pChars = chars)
{
fixed (byte* pBytes = bytes)
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);
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);
}
}

View File

@ -3,44 +3,43 @@ using System.Diagnostics;
using System.Runtime.InteropServices;
using LibHac.Util;
namespace LibHac.Common
namespace LibHac.Common;
[DebuggerDisplay("{ToString()}")]
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct Key128 : IEquatable<Key128>
{
[DebuggerDisplay("{ToString()}")]
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct Key128 : IEquatable<Key128>
private readonly ulong _dummy1;
private readonly ulong _dummy2;
public Span<byte> Value => SpanHelpers.AsByteSpan(ref this);
public Key128(ReadOnlySpan<byte> bytes)
{
private readonly ulong _dummy1;
private readonly ulong _dummy2;
ReadOnlySpan<ulong> longs = MemoryMarshal.Cast<byte, ulong>(bytes);
public Span<byte> Value => SpanHelpers.AsByteSpan(ref this);
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);
_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);
}

View File

@ -1,19 +1,18 @@
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> KeySeeds => new byte[] { };
private static ReadOnlySpan<byte> StoredKeysDev => new byte[] { };
private static ReadOnlySpan<byte> StoredKeysProd => new byte[] { };
private static ReadOnlySpan<byte> DerivedKeysDev => new byte[] { };
private static ReadOnlySpan<byte> DerivedKeysProd => new byte[] { };
private static ReadOnlySpan<byte> DeviceKeys => new byte[] { };
private static ReadOnlySpan<byte> RsaSigningKeysDev => new byte[] { };
private static ReadOnlySpan<byte> RsaSigningKeysProd => new byte[] { };
private static ReadOnlySpan<byte> RsaKeys => new byte[] { };
}
private static ReadOnlySpan<byte> RootKeysDev => new byte[] { };
private static ReadOnlySpan<byte> RootKeysProd => new byte[] { };
private static ReadOnlySpan<byte> KeySeeds => new byte[] { };
private static ReadOnlySpan<byte> StoredKeysDev => new byte[] { };
private static ReadOnlySpan<byte> StoredKeysProd => new byte[] { };
private static ReadOnlySpan<byte> DerivedKeysDev => new byte[] { };
private static ReadOnlySpan<byte> DerivedKeysProd => new byte[] { };
private static ReadOnlySpan<byte> DeviceKeys => new byte[] { };
private static ReadOnlySpan<byte> RsaSigningKeysDev => new byte[] { };
private static ReadOnlySpan<byte> RsaSigningKeysProd => new byte[] { };
private static ReadOnlySpan<byte> RsaKeys => new byte[] { };
}

View File

@ -4,178 +4,177 @@ using System.Runtime.InteropServices;
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>
/// 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()
var keySet = new KeySet();
// Fill the key set with any key structs included in the library.
if (RootKeysDev.Length == Unsafe.SizeOf<RootKeys>())
{
var keySet = new KeySet();
// 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;
keySet.KeyStruct._rootKeysDev = MemoryMarshal.Cast<byte, RootKeys>(RootKeysDev)[0];
}
/// <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()
if (RootKeysProd.Length == Unsafe.SizeOf<RootKeys>())
{
// 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;
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>
/// 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

View File

@ -9,165 +9,164 @@ using LibHac.Util;
using Type = LibHac.Common.Keys.KeyInfo.KeyType;
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;
int maxNameLength = keys.Max(x => x.NameLength);
int currentGroup = 0;
// Skip sub-filters that have no flags set
return (filter1 == 0 || (filter1 & keyInfo.Type) != 0) &&
(filter2 == 0 || (filter2 & keyInfo.Type) != 0) &&
filter3 == (filter3 & keyInfo.Type) ||
isDev && keyInfo.Type.HasFlag(Type.DifferentDev);
}
// Todo: Better filtering
bool FilterMatches(KeyInfo keyInfo)
bool isFirstPrint = true;
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);
Type filter2 = filter & (Type.Root | Type.Seed | Type.Derived);
Type filter3 = filter & Type.DifferentDev;
// Skip sub-filters that have no flags set
return (filter1 == 0 || (filter1 & keyInfo.Type) != 0) &&
(filter2 == 0 || (filter2 & keyInfo.Type) != 0) &&
filter3 == (filter3 & keyInfo.Type) ||
isDev && keyInfo.Type.HasFlag(Type.DifferentDev);
currentGroup = info.Group;
}
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;
}
bool isFirstPrint = true;
foreach (KeyInfo info in keys.Where(x => x.Group >= 0).Where(FilterMatches)
.OrderBy(x => x.Group).ThenBy(x => x.Name))
if (info.RangeType == RangeType.Single)
{
bool isNewGroup = false;
Span<byte> key = info.Getter(keySet, 0);
if (key.IsZeros())
continue;
if (info.Group == currentGroup + 1)
if (isNewGroup)
{
currentGroup = info.Group;
}
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;
sb.AppendLine();
}
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())
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()}";
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, 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)
{
var sb = new StringBuilder();
foreach ((RightsId rightsId, AccessKey key) kv in keySet.ExternalKeySet.ToList()
.OrderBy(x => x.rightsId.ToString()))
{
string line = $"{kv.rightsId} = {kv.key}";
sb.AppendLine(line);
}
return sb.ToString();
}
public static string PrintCommonKeys(KeySet keySet)
{
var sb = new StringBuilder();
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Seed | Type.Derived,
false);
return sb.ToString();
}
public static string PrintDeviceKeys(KeySet keySet)
{
var sb = new StringBuilder();
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Device, false);
return sb.ToString();
}
public static string PrintAllKeys(KeySet keySet)
{
var sb = new StringBuilder();
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), 0, false);
return sb.ToString();
}
public static string PrintAllSeeds(KeySet keySet)
{
var sb = new StringBuilder();
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Seed, false);
if (keySet.CurrentMode == KeySet.Mode.Prod)
{
sb.AppendLine();
keySet.SetMode(KeySet.Mode.Dev);
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Seed | Type.DifferentDev, true);
keySet.SetMode(KeySet.Mode.Prod);
}
return sb.ToString();
}
public static string PrintCommonKeysWithDev(KeySet keySet)
{
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,
false);
sb.AppendLine();
keySet.SetMode(KeySet.Mode.Dev);
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Derived, true);
keySet.SetMode(originalMode);
return sb.ToString();
}
}
public static string PrintTitleKeys(KeySet keySet)
{
var sb = new StringBuilder();
foreach ((RightsId rightsId, AccessKey key) kv in keySet.ExternalKeySet.ToList()
.OrderBy(x => x.rightsId.ToString()))
{
string line = $"{kv.rightsId} = {kv.key}";
sb.AppendLine(line);
}
return sb.ToString();
}
public static string PrintCommonKeys(KeySet keySet)
{
var sb = new StringBuilder();
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Seed | Type.Derived,
false);
return sb.ToString();
}
public static string PrintDeviceKeys(KeySet keySet)
{
var sb = new StringBuilder();
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Device, false);
return sb.ToString();
}
public static string PrintAllKeys(KeySet keySet)
{
var sb = new StringBuilder();
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), 0, false);
return sb.ToString();
}
public static string PrintAllSeeds(KeySet keySet)
{
var sb = new StringBuilder();
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Seed, false);
if (keySet.CurrentMode == KeySet.Mode.Prod)
{
sb.AppendLine();
keySet.SetMode(KeySet.Mode.Dev);
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Seed | Type.DifferentDev, true);
keySet.SetMode(KeySet.Mode.Prod);
}
return sb.ToString();
}
public static string PrintCommonKeysWithDev(KeySet keySet)
{
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,
false);
sb.AppendLine();
keySet.SetMode(KeySet.Mode.Dev);
PrintKeys(keySet, sb, DefaultKeySet.CreateKeyList(), Type.Common | Type.Root | Type.Derived, true);
keySet.SetMode(originalMode);
return sb.ToString();
}
}

View File

@ -2,397 +2,396 @@
using System.Runtime.InteropServices;
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);
DecryptKeyBlobs(keySet, logger);
ReadKeyBlobs(keySet);
if (s.KeyBlobKeySources[i].IsZeros()) continue;
Derive620Keys(keySet);
Derive620MasterKeks(keySet);
DeriveMarikoMasterKeks(keySet);
DeriveMasterKeys(keySet);
PopulateOldMasterKeys(keySet);
Aes.DecryptEcb128(s.KeyBlobKeySources[i], temp, s.TsecKey);
Aes.DecryptEcb128(temp, s.KeyBlobKeys[i], s.SecureBootKey);
DerivePerConsoleKeys(keySet);
DerivePerGenerationKeys(keySet);
DeriveNcaHeaderKey(keySet);
DeriveSdCardKeys(keySet);
if (!haveKeyBlobMacKeySource) continue;
Aes.DecryptEcb128(s.KeyBlobMacKeySource, s.KeyBlobMacKeys[i], s.KeyBlobKeys[i]);
}
}
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;
bool haveKeyBlobMacKeySource = !s.MasterKeySource.IsZeros();
var temp = new AesKey();
for (int i = 0; i < KeySet.UsedKeyBlobCount; i++)
if (s.KeyBlobKeys[i].IsZeros() || s.KeyBlobMacKeys[i].IsZeros() || s.EncryptedKeyBlobs[i].IsZeros())
{
if (s.KeyBlobKeySources[i].IsZeros()) 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]);
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 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++)
{
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);
}
s.MasterKeks[i] = s.KeyBlobs[i].MasterKek;
s.Package1Keys[i] = s.KeyBlobs[i].Package1Key;
}
}
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;
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;
}
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount],
s.TsecRootKeys[i - KeySet.UsedKeyBlobCount], s.TsecRootKek);
}
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--)
if (havePackage1MacKek)
{
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);
Aes.EncryptEcb128(s.TsecAuthSignatures[i - KeySet.UsedKeyBlobCount], s.Package1MacKeys[i],
s.Package1MacKek);
}
// 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())
if (havePackage1Kek)
{
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);
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)
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);
}
}
}

View File

@ -3,165 +3,164 @@ using System.Diagnostics;
using LibHac.Diag;
using LibHac.Util;
namespace LibHac.Common.Keys
namespace LibHac.Common.Keys;
[DebuggerDisplay("{" + nameof(Name) + "}")]
public readonly struct KeyInfo
{
[DebuggerDisplay("{" + nameof(Name) + "}")]
public readonly struct KeyInfo
public enum KeyRangeType : byte
{
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,
Range
}
KeyRangeType.Single => MatchesSingle(keyName, out isDev),
KeyRangeType.Range => MatchesRangedKey(keyName, ref keyIndex, out isDev),
_ => false
};
}
[Flags]
public enum KeyType : byte
private bool MatchesSingle(ReadOnlySpan<char> keyName, out bool isDev)
{
Assert.SdkRequiresEqual((int)KeyRangeType.Single, (int)RangeType);
isDev = false;
if (keyName.Length == NameLength + 4)
{
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
{
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))
// Might be a dev key. Check if "_dev" comes after the base key name
if (!keyName.Slice(Name.Length, 4).SequenceEqual("_dev"))
return false;
return true;
isDev = true;
}
private bool MatchesRangedKey(ReadOnlySpan<char> keyName, ref int keyIndex, out bool isDev)
else if (keyName.Length != NameLength)
{
Assert.SdkRequiresEqual((int)KeyRangeType.Range, (int)RangeType);
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;
return false;
}
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
KeyType type1 = type & (KeyType.Common | KeyType.Device);
KeyType type2 = type & (KeyType.Root | KeyType.Seed | KeyType.Derived);
// Check if "_dev" comes after the base key name
if (!keyName.Slice(Name.Length, 4).SequenceEqual("_dev"))
return false;
bool isValid1 = type1 == KeyType.Common || type1 == KeyType.Device;
bool isValid2 = type2 == KeyType.Root || type2 == KeyType.Seed || type2 == KeyType.Derived;
return isValid1 && isValid2;
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)
{
// 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;
}
}

View File

@ -8,377 +8,376 @@ using LibHac.Crypto;
using LibHac.FsSrv;
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
}
/// <summary>
/// The number of keyblobs that were used for &lt; 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;
Dev,
Prod
}
/// <summary>
/// Holds keys that are stored directly in Horizon programs.
/// The number of keyblobs that were used for &lt; 6.2.0 crypto
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct StoredKeys
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
{
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 struct DerivedKeys
public Span<RSAParameters> AcidSigningKeyParams
{
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;
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;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct DeviceKeys
public ref RSAParameters Package2SigningKeyParams
{
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;
get
{
ref Optional<RSAParameters> keys = ref RsaSigningKeyParams.Package2SigningKey;
if (!keys.HasValue)
{
keys.Set(new RSAParameters());
keys.Value = CreateRsaParameters(in Package2SigningKey);
}
return ref keys.Value;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RsaSigningKeys
public ref RSAParameters BetaNca0KeyAreaKeyParams
{
public Array2<RsaKey> NcaHeaderSigningKeys;
public Array2<RsaKey> AcidSigningKeys;
public RsaKey Package2SigningKey;
get
{
ref Optional<RSAParameters> keys = ref _rsaKeyParams.BetaNca0KeyAreaKey;
if (!keys.HasValue)
{
keys.Set(CreateRsaParameters(in BetaNca0KeyAreaKey));
}
return ref keys.Value;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RsaKeys
public void SetSdSeed(ReadOnlySpan<byte> sdSeed)
{
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;
}

View File

@ -1,10 +1,9 @@
using System;
namespace LibHac.Common
{
[AttributeUsage(AttributeTargets.Struct)]
public sealed class NonCopyableAttribute : Attribute { }
namespace LibHac.Common;
[AttributeUsage(AttributeTargets.Struct)]
public sealed class NonCopyableDisposableAttribute : Attribute { }
}
[AttributeUsage(AttributeTargets.Struct)]
public sealed class NonCopyableAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Struct)]
public sealed class NonCopyableDisposableAttribute : Attribute { }

View File

@ -1,52 +1,51 @@
using System.Diagnostics;
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
// in the struct that is used for the debugger display must be part of a field.
// These padding structs make it easier to accomplish that.
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
internal struct Padding10
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding00;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding08;
}
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
internal struct Padding20
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding00;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding08;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding10;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding18;
}
[StructLayout(LayoutKind.Sequential, Size = 0x40)]
internal struct Padding40
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding20 Padding00;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding20 Padding20;
}
[StructLayout(LayoutKind.Sequential, Size = 0x80)]
internal struct Padding80
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding40 Padding00;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding40 Padding40;
}
[StructLayout(LayoutKind.Sequential, Size = 0x100)]
internal struct Padding100
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding00;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding80;
}
[StructLayout(LayoutKind.Sequential, Size = 0x200)]
internal struct Padding200
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100;
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding00;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding08;
}
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
internal struct Padding20
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding00;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding08;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding10;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly ulong Padding18;
}
[StructLayout(LayoutKind.Sequential, Size = 0x40)]
internal struct Padding40
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding20 Padding00;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding20 Padding20;
}
[StructLayout(LayoutKind.Sequential, Size = 0x80)]
internal struct Padding80
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding40 Padding00;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding40 Padding40;
}
[StructLayout(LayoutKind.Sequential, Size = 0x100)]
internal struct Padding100
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding00;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding80 Padding80;
}
[StructLayout(LayoutKind.Sequential, Size = 0x200)]
internal struct Padding200
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100;
}

View File

@ -5,120 +5,119 @@ using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.Util;
namespace LibHac.Common
namespace LibHac.Common;
[DebuggerDisplay("{ToString()}")]
internal ref struct PathBuilder
{
[DebuggerDisplay("{ToString()}")]
internal ref struct PathBuilder
private Span<byte> _buffer;
private int _pos;
public int Length
{
private Span<byte> _buffer;
private int _pos;
public int Length
get => _pos;
set
{
get => _pos;
set
{
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));
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));
}
}

View File

@ -6,129 +6,128 @@ using System;
using System.Runtime.CompilerServices;
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>
/// 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>
/// <typeparam name="T">The type of value to reference.</typeparam>
public readonly ref struct Ref<T>
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)
{
/// <summary>
/// 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;
}
_span = MemoryMarshal.CreateSpan(ref value, 1);
}
/// <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>
/// <typeparam name="T">The type of value to reference.</typeparam>
public readonly ref struct ReadOnlyRef<T>
/// <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>
/// 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;
}
}

View File

@ -2,40 +2,39 @@
using System.Buffers;
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
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)
{
if (minimumSize >= RentThresholdElements)
{
Array = ArrayPool<T>.Shared.Rent(minimumSize);
}
else
{
Array = new T[minimumSize];
}
_span = Array.AsSpan(0, minimumSize);
Array = ArrayPool<T>.Shared.Rent(minimumSize);
}
else
{
Array = new T[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
if (_span.Length >= RentThresholdElements)
{
ArrayPool<T>.Shared.Return(Array);
}
ArrayPool<T>.Shared.Return(Array);
}
}
}

View File

@ -11,86 +11,85 @@
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>
public static Result.Base InvalidArgument => new Result.Base(ModuleLibHac, 1, 49);
/// <summary>Error code: 2428-0002; Inner value: 0x5ac</summary>
public static Result.Base NullArgument => new Result.Base(ModuleLibHac, 2);
/// <summary>Error code: 2428-0003; Inner value: 0x7ac</summary>
public static Result.Base ArgumentOutOfRange => new Result.Base(ModuleLibHac, 3);
/// <summary>Error code: 2428-0004; Inner value: 0x9ac</summary>
public static Result.Base BufferTooSmall => new Result.Base(ModuleLibHac, 4);
/// <summary>Error code: 2428-0001; Range: 1-49; Inner value: 0x3ac</summary>
public static Result.Base InvalidArgument => new Result.Base(ModuleLibHac, 1, 49);
/// <summary>Error code: 2428-0002; Inner value: 0x5ac</summary>
public static Result.Base NullArgument => new Result.Base(ModuleLibHac, 2);
/// <summary>Error code: 2428-0003; Inner value: 0x7ac</summary>
public static Result.Base ArgumentOutOfRange => new Result.Base(ModuleLibHac, 3);
/// <summary>Error code: 2428-0004; Inner value: 0x9ac</summary>
public static Result.Base BufferTooSmall => new Result.Base(ModuleLibHac, 4);
/// <summary>Error code: 2428-0051; Inner value: 0x67ac</summary>
public static Result.Base ServiceNotInitialized => new Result.Base(ModuleLibHac, 51);
/// <summary>Error code: 2428-0101; Inner value: 0xcbac</summary>
public static Result.Base NotImplemented => new Result.Base(ModuleLibHac, 101);
/// <summary>Error code: 2428-0051; Inner value: 0x67ac</summary>
public static Result.Base ServiceNotInitialized => new Result.Base(ModuleLibHac, 51);
/// <summary>Error code: 2428-0101; Inner value: 0xcbac</summary>
public static Result.Base NotImplemented => new Result.Base(ModuleLibHac, 101);
/// <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); }
/// <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); }
/// <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); }
/// <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);
/// <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);
/// <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);
/// <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);
/// <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); }
/// <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); }
/// <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); }
/// <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);
/// <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);
/// <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);
/// <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);
/// <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); }
/// <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);
/// <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);
/// <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);
/// <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); }
/// <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);
/// <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);
/// <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);
/// <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); }
/// <summary>Error code: 2428-1021; Inner value: 0x7fbac</summary>
public static Result.Base InvalidPackage2HeaderSignature => new Result.Base(ModuleLibHac, 1021);
/// <summary>Error code: 2428-1022; Inner value: 0x7fdac</summary>
public static Result.Base InvalidPackage2MetaSizeA => new Result.Base(ModuleLibHac, 1022);
/// <summary>Error code: 2428-1023; Inner value: 0x7ffac</summary>
public static Result.Base InvalidPackage2MetaSizeB => new Result.Base(ModuleLibHac, 1023);
/// <summary>Error code: 2428-1024; Inner value: 0x801ac</summary>
public static Result.Base InvalidPackage2MetaKeyGeneration => new Result.Base(ModuleLibHac, 1024);
/// <summary>Error code: 2428-1025; Inner value: 0x803ac</summary>
public static Result.Base InvalidPackage2MetaMagic => new Result.Base(ModuleLibHac, 1025);
/// <summary>Error code: 2428-1026; Inner value: 0x805ac</summary>
public static Result.Base InvalidPackage2MetaEntryPointAlignment => new Result.Base(ModuleLibHac, 1026);
/// <summary>Error code: 2428-1027; Inner value: 0x807ac</summary>
public static Result.Base InvalidPackage2MetaPayloadAlignment => new Result.Base(ModuleLibHac, 1027);
/// <summary>Error code: 2428-1028; Inner value: 0x809ac</summary>
public static Result.Base InvalidPackage2MetaPayloadSizeAlignment => new Result.Base(ModuleLibHac, 1028);
/// <summary>Error code: 2428-1029; Inner value: 0x80bac</summary>
public static Result.Base InvalidPackage2MetaTotalSize => new Result.Base(ModuleLibHac, 1029);
/// <summary>Error code: 2428-1030; Inner value: 0x80dac</summary>
public static Result.Base InvalidPackage2MetaPayloadSize => new Result.Base(ModuleLibHac, 1030);
/// <summary>Error code: 2428-1031; Inner value: 0x80fac</summary>
public static Result.Base InvalidPackage2MetaPayloadsOverlap => new Result.Base(ModuleLibHac, 1031);
/// <summary>Error code: 2428-1032; Inner value: 0x811ac</summary>
public static Result.Base InvalidPackage2MetaEntryPointNotFound => new Result.Base(ModuleLibHac, 1032);
/// <summary>Error code: 2428-1033; Inner value: 0x813ac</summary>
public static Result.Base InvalidPackage2PayloadCorrupted => new Result.Base(ModuleLibHac, 1033);
/// <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); }
/// <summary>Error code: 2428-1021; Inner value: 0x7fbac</summary>
public static Result.Base InvalidPackage2HeaderSignature => new Result.Base(ModuleLibHac, 1021);
/// <summary>Error code: 2428-1022; Inner value: 0x7fdac</summary>
public static Result.Base InvalidPackage2MetaSizeA => new Result.Base(ModuleLibHac, 1022);
/// <summary>Error code: 2428-1023; Inner value: 0x7ffac</summary>
public static Result.Base InvalidPackage2MetaSizeB => new Result.Base(ModuleLibHac, 1023);
/// <summary>Error code: 2428-1024; Inner value: 0x801ac</summary>
public static Result.Base InvalidPackage2MetaKeyGeneration => new Result.Base(ModuleLibHac, 1024);
/// <summary>Error code: 2428-1025; Inner value: 0x803ac</summary>
public static Result.Base InvalidPackage2MetaMagic => new Result.Base(ModuleLibHac, 1025);
/// <summary>Error code: 2428-1026; Inner value: 0x805ac</summary>
public static Result.Base InvalidPackage2MetaEntryPointAlignment => new Result.Base(ModuleLibHac, 1026);
/// <summary>Error code: 2428-1027; Inner value: 0x807ac</summary>
public static Result.Base InvalidPackage2MetaPayloadAlignment => new Result.Base(ModuleLibHac, 1027);
/// <summary>Error code: 2428-1028; Inner value: 0x809ac</summary>
public static Result.Base InvalidPackage2MetaPayloadSizeAlignment => new Result.Base(ModuleLibHac, 1028);
/// <summary>Error code: 2428-1029; Inner value: 0x80bac</summary>
public static Result.Base InvalidPackage2MetaTotalSize => new Result.Base(ModuleLibHac, 1029);
/// <summary>Error code: 2428-1030; Inner value: 0x80dac</summary>
public static Result.Base InvalidPackage2MetaPayloadSize => new Result.Base(ModuleLibHac, 1030);
/// <summary>Error code: 2428-1031; Inner value: 0x80fac</summary>
public static Result.Base InvalidPackage2MetaPayloadsOverlap => new Result.Base(ModuleLibHac, 1031);
/// <summary>Error code: 2428-1032; Inner value: 0x811ac</summary>
public static Result.Base InvalidPackage2MetaEntryPointNotFound => new Result.Base(ModuleLibHac, 1032);
/// <summary>Error code: 2428-1033; Inner value: 0x813ac</summary>
public static Result.Base InvalidPackage2PayloadCorrupted => new Result.Base(ModuleLibHac, 1033);
/// <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); }
/// <summary>Error code: 2428-1041; Inner value: 0x823ac</summary>
public static Result.Base InvalidPackage1SectionSize => new Result.Base(ModuleLibHac, 1041);
/// <summary>Error code: 2428-1042; Inner value: 0x825ac</summary>
public static Result.Base InvalidPackage1MarikoBodySize => new Result.Base(ModuleLibHac, 1042);
/// <summary>Error code: 2428-1043; Inner value: 0x827ac</summary>
public static Result.Base InvalidPackage1Pk11Size => new Result.Base(ModuleLibHac, 1043);
}
/// <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); }
/// <summary>Error code: 2428-1041; Inner value: 0x823ac</summary>
public static Result.Base InvalidPackage1SectionSize => new Result.Base(ModuleLibHac, 1041);
/// <summary>Error code: 2428-1042; Inner value: 0x825ac</summary>
public static Result.Base InvalidPackage1MarikoBodySize => new Result.Base(ModuleLibHac, 1042);
/// <summary>Error code: 2428-1043; Inner value: 0x827ac</summary>
public static Result.Base InvalidPackage1Pk11Size => new Result.Base(ModuleLibHac, 1043);
}

View File

@ -1,22 +1,21 @@
using System.Runtime.CompilerServices;
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;
}
namespace LibHac.Common;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Move<T>(out T dest, ref T value)
{
dest = value;
value = default;
}
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 void Move<T>(out T dest, ref T value)
{
dest = value;
value = default;
}
}

View File

@ -6,235 +6,283 @@ using LibHac.Diag;
#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
public static ref SharedRef<T> Ref<T>(this in SharedRef<T> value) where T : class, IDisposable
IL.Emit.Ldarg(nameof(value));
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));
IL.Emit.Ret();
throw IL.Unreachable();
int oldValue = Interlocked.CompareExchange(ref _count, count + 1, count);
if (oldValue == count)
{
return true;
}
count = oldValue;
}
// ReSharper disable once EntityNameCapturedOnly.Global
public static ref WeakRef<T> Ref<T>(this in WeakRef<T> value) where T : class, IDisposable
return false;
}
public void Decrement()
{
if (Interlocked.Decrement(ref _count) == 0)
{
IL.Emit.Ldarg(nameof(value));
IL.Emit.Ret();
throw IL.Unreachable();
Destroy();
DecrementWeak();
}
}
internal class RefCount
public void DecrementWeak()
{
private int _count;
private int _weakCount;
private IDisposable _value;
public RefCount(IDisposable value)
if (Interlocked.Decrement(ref _weakCount) == 0)
{
_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)
{
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;
}
// Deallocate
}
}
[NonCopyableDisposable]
public struct SharedRef<T> : IDisposable where T : class, IDisposable
private void Destroy()
{
// 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)
if (_value is not null)
{
_value = value;
_refCount = new RefCount(value);
_value.Dispose();
_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)]
public void Dispose()
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)
{
// 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();
sharedRef._value = value;
sharedRef._refCount = new RefCount(value);
other.Release();
}
else
{
sharedRef._value = null;
sharedRef._refCount = null;
}
/// <summary>
/// Used to manually dispose the <see cref="SharedRef{T}"/> from the Dispose methods of other types.
/// </summary>
public void Destroy()
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;
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);
}
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()
else
{
_value = null;
RefCount oldRefCount = _refCount;
_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);
_refCount = other._refCount;
@ -242,200 +290,151 @@ namespace LibHac.Common
other._refCount = null;
oldRefCount?.Decrement();
return true;
}
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)
{
_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");
}
return false;
}
[NonCopyableDisposable]
public struct WeakRef<T> : IDisposable where T : class, IDisposable
private static void ThrowBadWeakPtr()
{
private T _value;
private RefCount _refCount;
public WeakRef(in SharedRef<T> other)
{
this = Create(in other);
}
[Obsolete("This method should never be manually called. Use the Destroy method instead.", true)]
public void Dispose()
{
_refCount?.DecrementWeak();
}
// A copy of Dispose so we can call it ourselves inside the struct
private void DisposeInternal()
{
_refCount?.DecrementWeak();
}
/// <summary>
/// Used to manually dispose the <see cref="WeakRef{T}"/> from the Dispose methods of other types.
/// </summary>
public void Destroy()
{
Reset();
}
public readonly int UseCount => _refCount?.UseCount() ?? 0;
public readonly bool Expired => UseCount == 0;
public static WeakRef<T> CreateMove<TFrom>(ref WeakRef<TFrom> other) where TFrom : class, T
{
var weakRef = new WeakRef<T>();
weakRef._value = Unsafe.As<TFrom, T>(ref other._value);
weakRef._refCount = other._refCount;
other._value = null;
other._refCount = null;
return weakRef;
}
public static WeakRef<T> CreateCopy<TFrom>(in WeakRef<TFrom> other) where TFrom : class, T
{
var weakRef = new WeakRef<T>();
if (other._refCount is not null)
{
weakRef._refCount = other._refCount;
weakRef._refCount.IncrementWeak();
if (weakRef._refCount.IncrementIfNotZero())
{
weakRef._value = Unsafe.As<TFrom, T>(ref other.Ref()._value);
weakRef._refCount.Decrement();
}
}
return weakRef;
}
public static WeakRef<T> Create<TFrom>(in SharedRef<TFrom> other) where TFrom : class, T
{
ref WeakRef<TFrom> otherWeak = ref Unsafe.As<SharedRef<TFrom>, WeakRef<TFrom>>(ref other.Ref());
var weakRef = new WeakRef<T>();
if (otherWeak._refCount is not null)
{
weakRef._value = Unsafe.As<TFrom, T>(ref otherWeak._value);
weakRef._refCount = otherWeak._refCount;
weakRef._refCount.IncrementWeak();
}
else
{
weakRef._value = null;
weakRef._refCount = null;
}
return weakRef;
}
public void Swap(ref WeakRef<T> other)
{
(other._value, _value) = (_value, other._value);
(other._refCount, _refCount) = (_refCount, other._refCount);
}
public void Reset()
{
var temp = new WeakRef<T>();
Swap(ref temp);
temp.DisposeInternal();
}
public void SetMove<TFrom>(ref WeakRef<TFrom> other) where TFrom : class, T
{
WeakRef<T> temp = CreateMove(ref other);
Swap(ref temp);
temp.DisposeInternal();
}
public void SetCopy<TFrom>(in WeakRef<TFrom> other) where TFrom : class, T
{
WeakRef<T> temp = CreateCopy(in other);
Swap(ref temp);
temp.DisposeInternal();
}
public void Set<TFrom>(in SharedRef<TFrom> other) where TFrom : class, T
{
WeakRef<T> temp = Create(in other);
Swap(ref temp);
temp.DisposeInternal();
}
public readonly SharedRef<T> Lock()
{
var sharedRef = new SharedRef<T>();
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;
}
throw new ObjectDisposedException(string.Empty, "bad_weak_ptr");
}
}
[NonCopyableDisposable]
public struct WeakRef<T> : IDisposable where T : class, IDisposable
{
private T _value;
private RefCount _refCount;
public WeakRef(in SharedRef<T> other)
{
this = Create(in other);
}
[Obsolete("This method should never be manually called. Use the Destroy method instead.", true)]
public void Dispose()
{
_refCount?.DecrementWeak();
}
// A copy of Dispose so we can call it ourselves inside the struct
private void DisposeInternal()
{
_refCount?.DecrementWeak();
}
/// <summary>
/// Used to manually dispose the <see cref="WeakRef{T}"/> from the Dispose methods of other types.
/// </summary>
public void Destroy()
{
Reset();
}
public readonly int UseCount => _refCount?.UseCount() ?? 0;
public readonly bool Expired => UseCount == 0;
public static WeakRef<T> CreateMove<TFrom>(ref WeakRef<TFrom> other) where TFrom : class, T
{
var weakRef = new WeakRef<T>();
weakRef._value = Unsafe.As<TFrom, T>(ref other._value);
weakRef._refCount = other._refCount;
other._value = null;
other._refCount = null;
return weakRef;
}
public static WeakRef<T> CreateCopy<TFrom>(in WeakRef<TFrom> other) where TFrom : class, T
{
var weakRef = new WeakRef<T>();
if (other._refCount is not null)
{
weakRef._refCount = other._refCount;
weakRef._refCount.IncrementWeak();
if (weakRef._refCount.IncrementIfNotZero())
{
weakRef._value = Unsafe.As<TFrom, T>(ref other.Ref()._value);
weakRef._refCount.Decrement();
}
}
return weakRef;
}
public static WeakRef<T> Create<TFrom>(in SharedRef<TFrom> other) where TFrom : class, T
{
ref WeakRef<TFrom> otherWeak = ref Unsafe.As<SharedRef<TFrom>, WeakRef<TFrom>>(ref other.Ref());
var weakRef = new WeakRef<T>();
if (otherWeak._refCount is not null)
{
weakRef._value = Unsafe.As<TFrom, T>(ref otherWeak._value);
weakRef._refCount = otherWeak._refCount;
weakRef._refCount.IncrementWeak();
}
else
{
weakRef._value = null;
weakRef._refCount = null;
}
return weakRef;
}
public void Swap(ref WeakRef<T> other)
{
(other._value, _value) = (_value, other._value);
(other._refCount, _refCount) = (_refCount, other._refCount);
}
public void Reset()
{
var temp = new WeakRef<T>();
Swap(ref temp);
temp.DisposeInternal();
}
public void SetMove<TFrom>(ref WeakRef<TFrom> other) where TFrom : class, T
{
WeakRef<T> temp = CreateMove(ref other);
Swap(ref temp);
temp.DisposeInternal();
}
public void SetCopy<TFrom>(in WeakRef<TFrom> other) where TFrom : class, T
{
WeakRef<T> temp = CreateCopy(in other);
Swap(ref temp);
temp.DisposeInternal();
}
public void Set<TFrom>(in SharedRef<TFrom> other) where TFrom : class, T
{
WeakRef<T> temp = Create(in other);
Swap(ref temp);
temp.DisposeInternal();
}
public readonly SharedRef<T> Lock()
{
var sharedRef = new SharedRef<T>();
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;
}
}

View File

@ -2,116 +2,115 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
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];
}
namespace LibHac.Common;
/// <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)
{
return (uint)i >= (uint)span.Length ? (byte)0 : span[i];
}
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];
}
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)]
public static Span<T> CreateSpan<T>(ref T reference, int length)
{
return MemoryMarshal.CreateSpan(ref reference, length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> AsSpan<T>(ref T reference) where T : unmanaged
{
return CreateSpan(ref reference, 1);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<TSpan> AsSpan<TStruct, TSpan>(ref TStruct reference)
where TStruct : unmanaged where TSpan : unmanaged
{
return CreateSpan(ref Unsafe.As<TStruct, TSpan>(ref reference),
Unsafe.SizeOf<TStruct>() / Unsafe.SizeOf<TSpan>());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<byte> AsByteSpan<T>(ref T reference) where T : unmanaged
{
return CreateSpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<T> CreateReadOnlySpan<T>(in T reference, int length)
{
return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in reference), length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<T> AsReadOnlySpan<T>(in T reference) where T : unmanaged
{
return CreateReadOnlySpan(in reference, 1);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<TSpan> AsReadOnlySpan<TStruct, TSpan>(in TStruct reference)
where TStruct : unmanaged where TSpan : unmanaged
{
return CreateReadOnlySpan(in Unsafe.As<TStruct, TSpan>(ref Unsafe.AsRef(in reference)),
Unsafe.SizeOf<TStruct>() / Unsafe.SizeOf<TSpan>());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<byte> AsReadOnlyByteSpan<T>(in T reference) where T : unmanaged
{
return CreateReadOnlySpan(in Unsafe.As<T, byte>(ref Unsafe.AsRef(in reference)), Unsafe.SizeOf<T>());
}
// All AsStruct methods do bounds checks on the input
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T AsStruct<T>(Span<byte> span) where T : unmanaged
{
return ref MemoryMarshal.Cast<byte, T>(span)[0];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref readonly T AsReadOnlyStruct<T>(ReadOnlySpan<byte> span) where T : unmanaged
{
return ref MemoryMarshal.Cast<byte, T>(span)[0];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TTo AsStruct<TFrom, TTo>(Span<TFrom> span)
where TFrom : unmanaged
where TTo : unmanaged
{
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];
}
return (uint)i >= (uint)span.Length ? (byte)0 : span[i];
}
}
public static class SpanHelpers
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> CreateSpan<T>(ref T reference, int length)
{
return MemoryMarshal.CreateSpan(ref reference, length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> AsSpan<T>(ref T reference) where T : unmanaged
{
return CreateSpan(ref reference, 1);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<TSpan> AsSpan<TStruct, TSpan>(ref TStruct reference)
where TStruct : unmanaged where TSpan : unmanaged
{
return CreateSpan(ref Unsafe.As<TStruct, TSpan>(ref reference),
Unsafe.SizeOf<TStruct>() / Unsafe.SizeOf<TSpan>());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<byte> AsByteSpan<T>(ref T reference) where T : unmanaged
{
return CreateSpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<T> CreateReadOnlySpan<T>(in T reference, int length)
{
return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in reference), length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<T> AsReadOnlySpan<T>(in T reference) where T : unmanaged
{
return CreateReadOnlySpan(in reference, 1);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<TSpan> AsReadOnlySpan<TStruct, TSpan>(in TStruct reference)
where TStruct : unmanaged where TSpan : unmanaged
{
return CreateReadOnlySpan(in Unsafe.As<TStruct, TSpan>(ref Unsafe.AsRef(in reference)),
Unsafe.SizeOf<TStruct>() / Unsafe.SizeOf<TSpan>());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<byte> AsReadOnlyByteSpan<T>(in T reference) where T : unmanaged
{
return CreateReadOnlySpan(in Unsafe.As<T, byte>(ref Unsafe.AsRef(in reference)), Unsafe.SizeOf<T>());
}
// All AsStruct methods do bounds checks on the input
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T AsStruct<T>(Span<byte> span) where T : unmanaged
{
return ref MemoryMarshal.Cast<byte, T>(span)[0];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref readonly T AsReadOnlyStruct<T>(ReadOnlySpan<byte> span) where T : unmanaged
{
return ref MemoryMarshal.Cast<byte, T>(span)[0];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TTo AsStruct<TFrom, TTo>(Span<TFrom> span)
where TFrom : unmanaged
where TTo : unmanaged
{
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];
}
}

View File

@ -5,99 +5,98 @@ using System.Runtime.InteropServices;
using System.Text;
using LibHac.Util;
namespace LibHac.Common
namespace LibHac.Common;
[DebuggerDisplay("{ToString()}")]
public readonly ref struct U8Span
{
[DebuggerDisplay("{ToString()}")]
public readonly ref struct U8Span
private readonly ReadOnlySpan<byte> _buffer;
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 int Length => _buffer.Length;
public U8Span(ReadOnlySpan<byte> value)
{
_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)
{
_buffer = value;
}
return value;
}
public U8Span(string value)
{
_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)
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte GetUnsafe(int i)
{
#if DEBUG
return _buffer[i];
return _buffer[i];
#else
return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i);
#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;
}

View File

@ -5,95 +5,94 @@ using System.Runtime.InteropServices;
using System.Text;
using LibHac.Util;
namespace LibHac.Common
namespace LibHac.Common;
[DebuggerDisplay("{ToString()}")]
public readonly ref struct U8SpanMutable
{
[DebuggerDisplay("{ToString()}")]
public readonly ref struct U8SpanMutable
private readonly Span<byte> _buffer;
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 int Length => _buffer.Length;
public U8SpanMutable(Span<byte> value)
{
_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];
set => _buffer[i] = value;
value = GetUnsafe(i);
}
public U8SpanMutable(Span<byte> value)
{
_buffer = value;
}
return value;
}
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)
{
value = GetUnsafe(i);
}
return value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte GetUnsafe(int i)
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte GetUnsafe(int i)
{
#if DEBUG
return _buffer[i];
return _buffer[i];
#else
return Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), i);
#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;
}

View File

@ -3,62 +3,61 @@ using System.Diagnostics;
using System.Text;
using LibHac.Util;
namespace LibHac.Common
namespace LibHac.Common;
[DebuggerDisplay("{ToString()}")]
public readonly struct U8String
{
[DebuggerDisplay("{ToString()}")]
public readonly struct U8String
private readonly byte[] _buffer;
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;
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;
_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;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,14 @@
namespace LibHac.Common
{
public static class U8StringHelpers
{
public static U8String ToU8String(this string value)
{
return new U8String(value);
}
namespace LibHac.Common;
public static U8Span ToU8Span(this string value)
{
return new U8Span(value);
}
public static class U8StringHelpers
{
public static U8String ToU8String(this string value)
{
return new U8String(value);
}
public static U8Span ToU8Span(this string value)
{
return new U8Span(value);
}
}

View File

@ -3,69 +3,68 @@ using System.Diagnostics;
using System.Text;
using LibHac.Util;
namespace LibHac.Common
namespace LibHac.Common;
[DebuggerDisplay("{ToString()}")]
public readonly struct U8StringMutable
{
[DebuggerDisplay("{ToString()}")]
public readonly struct U8StringMutable
private readonly byte[] _buffer;
public Span<byte> Value => _buffer;
public int Length => _buffer.Length;
public byte this[int i]
{
private readonly byte[] _buffer;
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;
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;
}

View File

@ -2,90 +2,89 @@
using System.Runtime.CompilerServices;
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
public static ref UniqueRef<T> Ref<T>(this in UniqueRef<T> value) where T : class, IDisposable
{
Ldarg(nameof(value));
Ret();
throw InlineIL.IL.Unreachable();
}
}
[NonCopyableDisposable]
public struct UniqueRef<T> : IDisposable where T : class, IDisposable
{
private T _value;
public readonly T Get => _value;
public readonly bool HasValue => Get is not null;
public UniqueRef(T value)
{
_value = value;
}
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()
{
_value?.Dispose();
}
/// <summary>
/// Used to manually dispose the <see cref="UniqueRef{T}"/> from the Dispose methods of other types.
/// </summary>
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 void Swap(ref UniqueRef<T> other)
{
(other._value, _value) = (_value, other._value);
}
public void Reset() => Reset(null);
public void Reset(T value)
{
T oldValue = _value;
_value = value;
oldValue?.Dispose();
}
public void Set(ref UniqueRef<T> other)
{
if (Unsafe.AreSame(ref this, ref other))
return;
Reset(other.Release());
}
public void Set<TFrom>(ref UniqueRef<TFrom> other) where TFrom : class, T
{
Reset(other.Release());
}
public T Release()
{
T oldValue = _value;
_value = null;
return oldValue;
}
Ldarg(nameof(value));
Ret();
throw InlineIL.IL.Unreachable();
}
}
[NonCopyableDisposable]
public struct UniqueRef<T> : IDisposable where T : class, IDisposable
{
private T _value;
public readonly T Get => _value;
public readonly bool HasValue => Get is not null;
public UniqueRef(T value)
{
_value = value;
}
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()
{
_value?.Dispose();
}
/// <summary>
/// Used to manually dispose the <see cref="UniqueRef{T}"/> from the Dispose methods of other types.
/// </summary>
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 void Swap(ref UniqueRef<T> other)
{
(other._value, _value) = (_value, other._value);
}
public void Reset() => Reset(null);
public void Reset(T value)
{
T oldValue = _value;
_value = value;
oldValue?.Dispose();
}
public void Set(ref UniqueRef<T> other)
{
if (Unsafe.AreSame(ref this, ref other))
return;
Reset(other.Release());
}
public void Set<TFrom>(ref UniqueRef<TFrom> other) where TFrom : class, T
{
Reset(other.Release());
}
public T Release()
{
T oldValue = _value;
_value = null;
return oldValue;
}
}

View File

@ -1,67 +1,66 @@
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>
/// 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>())
{
if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
Unsafe.SkipInit(out value);
}
else
{
value = default;
}
Unsafe.SkipInit(out value);
}
/// <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)
else
{
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);
value = default;
}
}
/// <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);
}
}

View File

@ -7,305 +7,304 @@ using LibHac.Diag;
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;
public const int BlockSize = 0x10;
return AesNi.IsSupported;
}
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)
{
if (IsAesNiSupported() && !preferDotNetCrypto)
{
return new AesEcbDecryptorNi(key);
}
return new AesEcbDecryptor(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)
{
if (IsAesNiSupported() && !preferDotNetCrypto)
{
return new AesEcbEncryptorNi(key);
}
return new AesEcbEncryptor(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)
{
if (IsAesNiSupported() && !preferDotNetCrypto)
{
return new AesCbcDecryptorNi(key, iv);
}
return new AesCbcDecryptor(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)
{
if (IsAesNiSupported() && !preferDotNetCrypto)
{
return new AesCbcEncryptorNi(key, iv);
}
return new AesCbcEncryptor(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)
{
if (IsAesNiSupported() && !preferDotNetCrypto)
{
return new AesCtrCipherNi(key, iv);
}
// Encryption and decryption in counter mode is the same operation
return CreateCtrEncryptor(key, iv, preferDotNetCrypto);
}
// Encryption and decryption in counter mode is the same operation
return CreateCtrEncryptor(key, iv, preferDotNetCrypto);
public static ICipherWithIv CreateCtrEncryptor(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
{
if (IsAesNiSupported() && !preferDotNetCrypto)
{
return new AesCtrCipherNi(key, iv);
}
public static ICipherWithIv CreateCtrEncryptor(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
{
if (IsAesNiSupported() && !preferDotNetCrypto)
{
return new AesCtrCipherNi(key, iv);
}
return new AesCtrCipher(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,
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
{
if (IsAesNiSupported() && !preferDotNetCrypto)
{
return new AesXtsDecryptorNi(key1, key2, iv);
}
return new AesXtsDecryptor(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,
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
{
if (IsAesNiSupported() && !preferDotNetCrypto)
{
return new AesXtsEncryptorNi(key1, key2, iv);
}
return new AesXtsEncryptor(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,
bool preferDotNetCrypto = false)
ICipher cipher = CreateEcbEncryptor(key, preferDotNetCrypto);
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.Encrypt(input, output);
return;
}
ICipher cipher = CreateEcbEncryptor(key, preferDotNetCrypto);
cipher.Transform(input, output);
cipherNi.Initialize(key, true);
cipherNi.Decrypt(input, output);
return;
}
public static void DecryptEcb128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
bool preferDotNetCrypto = false)
ICipher cipher = CreateEcbDecryptor(key, preferDotNetCrypto);
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 AesEcbModeNi cipherNi);
Unsafe.SkipInit(out AesCbcModeNi cipherNi);
cipherNi.Initialize(key, true);
cipherNi.Decrypt(input, output);
return;
}
ICipher cipher = CreateEcbDecryptor(key, preferDotNetCrypto);
cipher.Transform(input, output);
cipherNi.Initialize(key, iv, false);
cipherNi.Encrypt(input, output);
return;
}
public static void EncryptCbc128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
ICipher cipher = CreateCbcEncryptor(key, iv, preferDotNetCrypto);
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.Encrypt(input, output);
return;
}
ICipher cipher = CreateCbcEncryptor(key, iv, preferDotNetCrypto);
cipher.Transform(input, output);
cipherNi.Initialize(key, iv, true);
cipherNi.Decrypt(input, output);
return;
}
public static void DecryptCbc128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
ICipher cipher = CreateCbcDecryptor(key, iv, preferDotNetCrypto);
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 AesCbcModeNi cipherNi);
Unsafe.SkipInit(out AesCtrModeNi cipherNi);
cipherNi.Initialize(key, iv, true);
cipherNi.Decrypt(input, output);
return;
}
ICipher cipher = CreateCbcDecryptor(key, iv, preferDotNetCrypto);
cipher.Transform(input, output);
cipherNi.Initialize(key, iv);
cipherNi.Transform(input, output);
return;
}
public static void EncryptCtr128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
ICipher cipher = CreateCtrEncryptor(key, iv, preferDotNetCrypto);
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.Transform(input, output);
return;
}
ICipher cipher = CreateCtrEncryptor(key, iv, preferDotNetCrypto);
cipher.Transform(input, output);
cipherNi.Initialize(key, iv);
cipherNi.Transform(input, output);
return;
}
public static void DecryptCtr128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key,
ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
ICipher cipher = CreateCtrDecryptor(key, iv, preferDotNetCrypto);
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 AesCtrModeNi cipherNi);
Unsafe.SkipInit(out AesXtsModeNi cipherNi);
cipherNi.Initialize(key, iv);
cipherNi.Transform(input, output);
return;
}
ICipher cipher = CreateCtrDecryptor(key, iv, preferDotNetCrypto);
cipher.Transform(input, output);
cipherNi.Initialize(key1, key2, iv, false);
cipherNi.Encrypt(input, output);
return;
}
public static void EncryptXts128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key1,
ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
ICipher cipher = CreateXtsEncryptor(key1, key2, iv, preferDotNetCrypto);
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.Encrypt(input, output);
return;
}
ICipher cipher = CreateXtsEncryptor(key1, key2, iv, preferDotNetCrypto);
cipher.Transform(input, output);
cipherNi.Initialize(key1, key2, iv, true);
cipherNi.Decrypt(input, output);
return;
}
public static void DecryptXts128(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> key1,
ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv, bool preferDotNetCrypto = false)
ICipher cipher = CreateXtsDecryptor(key1, key2, iv, preferDotNetCrypto);
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)
{
Unsafe.SkipInit(out AesXtsModeNi cipherNi);
int paddedLength = len + (16 - len % 16);
paddedMessage = paddedLength < 0x800 ? stackalloc byte[paddedLength] : new byte[paddedLength];
cipherNi.Initialize(key1, key2, iv, true);
cipherNi.Decrypt(input, output);
return;
}
paddedMessage.Slice(len).Clear();
paddedMessage[len] = 0x80;
data.CopyTo(paddedMessage);
ICipher cipher = CreateXtsDecryptor(key1, key2, iv, preferDotNetCrypto);
cipher.Transform(input, output);
for (int j = 0; j < k2.Length; j++)
paddedMessage[paddedMessage.Length - 16 + j] ^= k2[j];
}
/// <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)
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--)
{
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.
{
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);
}
ushort b = (ushort)(input[i] << 1);
output[i] = (byte)((b & 0xff) + carry);
carry = (byte)((b & 0xff00) >> 8);
}
}
}

View File

@ -1,37 +1,36 @@
using System;
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;
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);
}
_baseCipher = new AesCbcMode();
_baseCipher.Initialize(key, iv, false);
}
public class AesCbcDecryptor : ICipher
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
private AesCbcMode _baseCipher;
public AesCbcDecryptor(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
{
_baseCipher = new AesCbcMode();
_baseCipher.Initialize(key, iv, true);
}
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
_baseCipher.Decrypt(input, output);
}
_baseCipher.Encrypt(input, output);
}
}
public class AesCbcDecryptor : ICipher
{
private AesCbcMode _baseCipher;
public AesCbcDecryptor(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
{
_baseCipher = new AesCbcMode();
_baseCipher.Initialize(key, iv, true);
}
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
_baseCipher.Decrypt(input, output);
}
}

View File

@ -4,41 +4,40 @@ using System.Runtime.Intrinsics;
using LibHac.Common;
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;
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);
}
_baseCipher = new AesCbcModeNi();
_baseCipher.Initialize(key, iv, false);
}
public class AesCbcDecryptorNi : ICipherWithIv
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
private AesCbcModeNi _baseCipher;
public ref Buffer16 Iv => ref Unsafe.As<Vector128<byte>, Buffer16>(ref _baseCipher.Iv);
public AesCbcDecryptorNi(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
{
_baseCipher = new AesCbcModeNi();
_baseCipher.Initialize(key, iv, true);
}
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
_baseCipher.Decrypt(input, output);
}
_baseCipher.Encrypt(input, output);
}
}
public class AesCbcDecryptorNi : ICipherWithIv
{
private AesCbcModeNi _baseCipher;
public ref Buffer16 Iv => ref Unsafe.As<Vector128<byte>, Buffer16>(ref _baseCipher.Iv);
public AesCbcDecryptorNi(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
{
_baseCipher = new AesCbcModeNi();
_baseCipher.Initialize(key, iv, true);
}
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
_baseCipher.Decrypt(input, output);
}
}

View File

@ -2,23 +2,22 @@
using LibHac.Common;
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 AesCtrCipher(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
{
_baseCipher = new AesCtrMode();
_baseCipher.Initialize(key, iv);
}
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
_baseCipher.Transform(input, output);
}
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
_baseCipher.Transform(input, output);
}
}

View File

@ -4,23 +4,22 @@ using System.Runtime.Intrinsics;
using LibHac.Common;
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 AesCtrCipherNi(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
{
_baseCipher = new AesCtrModeNi();
_baseCipher.Initialize(key, iv);
}
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
_baseCipher.Transform(input, output);
}
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
_baseCipher.Transform(input, output);
}
}

View File

@ -1,37 +1,36 @@
using System;
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;
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);
}
_baseCipher = new AesEcbMode();
_baseCipher.Initialize(key, false);
}
public class AesEcbDecryptor : ICipher
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
private AesEcbMode _baseCipher;
public AesEcbDecryptor(ReadOnlySpan<byte> key)
{
_baseCipher = new AesEcbMode();
_baseCipher.Initialize(key, true);
}
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
_baseCipher.Decrypt(input, output);
}
_baseCipher.Encrypt(input, output);
}
}
public class AesEcbDecryptor : ICipher
{
private AesEcbMode _baseCipher;
public AesEcbDecryptor(ReadOnlySpan<byte> key)
{
_baseCipher = new AesEcbMode();
_baseCipher.Initialize(key, true);
}
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
_baseCipher.Decrypt(input, output);
}
}

View File

@ -1,37 +1,36 @@
using System;
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;
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);
}
_baseCipher = new AesEcbModeNi();
_baseCipher.Initialize(key, false);
}
public class AesEcbDecryptorNi : ICipher
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
private AesEcbModeNi _baseCipher;
public AesEcbDecryptorNi(ReadOnlySpan<byte> key)
{
_baseCipher = new AesEcbModeNi();
_baseCipher.Initialize(key, true);
}
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
_baseCipher.Decrypt(input, output);
}
_baseCipher.Encrypt(input, output);
}
}
public class AesEcbDecryptorNi : ICipher
{
private AesEcbModeNi _baseCipher;
public AesEcbDecryptorNi(ReadOnlySpan<byte> key)
{
_baseCipher = new AesEcbModeNi();
_baseCipher.Initialize(key, true);
}
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
_baseCipher.Decrypt(input, output);
}
}

View File

@ -2,41 +2,40 @@
using LibHac.Common;
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;
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);
}
_baseCipher = new AesXtsMode();
_baseCipher.Initialize(key1, key2, iv, false);
}
public class AesXtsDecryptor : ICipherWithIv
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
private AesXtsMode _baseCipher;
public ref Buffer16 Iv => ref _baseCipher.Iv;
public AesXtsDecryptor(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv)
{
_baseCipher = new AesXtsMode();
_baseCipher.Initialize(key1, key2, iv, true);
}
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
_baseCipher.Decrypt(input, output);
}
_baseCipher.Encrypt(input, output);
}
}
public class AesXtsDecryptor : ICipherWithIv
{
private AesXtsMode _baseCipher;
public ref Buffer16 Iv => ref _baseCipher.Iv;
public AesXtsDecryptor(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv)
{
_baseCipher = new AesXtsMode();
_baseCipher.Initialize(key1, key2, iv, true);
}
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
_baseCipher.Decrypt(input, output);
}
}

View File

@ -4,41 +4,40 @@ using System.Runtime.Intrinsics;
using LibHac.Common;
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;
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);
}
_baseCipher = new AesXtsModeNi();
_baseCipher.Initialize(key1, key2, iv, false);
}
public class AesXtsDecryptorNi : ICipherWithIv
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
private AesXtsModeNi _baseCipher;
public ref Buffer16 Iv => ref Unsafe.As<Vector128<byte>, Buffer16>(ref _baseCipher.Iv);
public AesXtsDecryptorNi(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv)
{
_baseCipher = new AesXtsModeNi();
_baseCipher.Initialize(key1, key2, iv, true);
}
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
_baseCipher.Decrypt(input, output);
}
_baseCipher.Encrypt(input, output);
}
}
public class AesXtsDecryptorNi : ICipherWithIv
{
private AesXtsModeNi _baseCipher;
public ref Buffer16 Iv => ref Unsafe.As<Vector128<byte>, Buffer16>(ref _baseCipher.Iv);
public AesXtsDecryptorNi(ReadOnlySpan<byte> key1, ReadOnlySpan<byte> key2, ReadOnlySpan<byte> iv)
{
_baseCipher = new AesXtsModeNi();
_baseCipher.Initialize(key1, key2, iv, true);
}
public void Transform(ReadOnlySpan<byte> input, Span<byte> output)
{
_baseCipher.Decrypt(input, output);
}
}

View File

@ -2,29 +2,28 @@
using System.Runtime.CompilerServices;
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);
}
[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;
}
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;
}
}

View File

@ -1,15 +1,14 @@
using System;
using LibHac.Common;
namespace LibHac.Crypto
{
public interface ICipher
{
void Transform(ReadOnlySpan<byte> input, Span<byte> output);
}
namespace LibHac.Crypto;
public interface ICipherWithIv : ICipher
{
ref Buffer16 Iv { get; }
}
}
public interface ICipher
{
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