mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Use file-scoped namespaces
This commit is contained in:
parent
6fe89a2966
commit
34dda02c38
@ -1 +1 @@
|
||||
5.0.402
|
||||
6.0.100
|
1279
build/Build.cs
1279
build/Build.cs
File diff suppressed because it is too large
Load Diff
@ -5,105 +5,104 @@ using System.Reflection;
|
||||
using System.Text;
|
||||
using 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");
|
||||
}
|
||||
}
|
||||
|
@ -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
@ -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
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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("-", "");
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
namespace LibHac.Bcat
|
||||
namespace LibHac.Bcat;
|
||||
|
||||
public enum BcatServiceType
|
||||
{
|
||||
public enum BcatServiceType
|
||||
{
|
||||
BcatU,
|
||||
BcatS,
|
||||
BcatM,
|
||||
BcatA
|
||||
}
|
||||
BcatU,
|
||||
BcatS,
|
||||
BcatM,
|
||||
BcatA
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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' };
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
namespace LibHac
|
||||
namespace LibHac;
|
||||
|
||||
public static class BitTools
|
||||
{
|
||||
public static class BitTools
|
||||
public static int SignExtend32(int value, int bits)
|
||||
{
|
||||
public static int SignExtend32(int value, int bits)
|
||||
{
|
||||
int shift = 8 * sizeof(int) - bits;
|
||||
return (value << shift) >> shift;
|
||||
}
|
||||
int shift = 8 * sizeof(int) - bits;
|
||||
return (value << shift) >> shift;
|
||||
}
|
||||
}
|
||||
|
@ -6,89 +6,88 @@ using LibHac.Common;
|
||||
using LibHac.Crypto;
|
||||
using LibHac.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
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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[] { };
|
||||
}
|
||||
|
@ -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
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 < 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 < 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;
|
||||
}
|
||||
|
@ -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 { }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user