Add console unique data (SecureInfo, LocalFriendCodeSeed, CTCert) (#6)
* Add console unique secure data * Add CTCert and DeviceID support * Fix AM_U::GetDeviceID Co-authored-by: Daniel López Guimaraes <112760654+DaniElectra@users.noreply.github.com> * Update to latest master changes. --------- Co-authored-by: Daniel López Guimaraes <112760654+DaniElectra@users.noreply.github.com>
This commit is contained in:
parent
f112d975b9
commit
813d0c2a30
@ -3,12 +3,14 @@
|
|||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <QFileDialog>
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QProgressDialog>
|
#include <QProgressDialog>
|
||||||
#include <QtConcurrent/QtConcurrentMap>
|
#include <QtConcurrent/QtConcurrentMap>
|
||||||
#include "citra_qt/configuration/configuration_shared.h"
|
#include "citra_qt/configuration/configuration_shared.h"
|
||||||
#include "citra_qt/configuration/configure_system.h"
|
#include "citra_qt/configuration/configure_system.h"
|
||||||
|
#include "common/file_util.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/service/am/am.h"
|
#include "core/hle/service/am/am.h"
|
||||||
@ -236,6 +238,32 @@ ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent)
|
|||||||
&ConfigureSystem::RefreshConsoleID);
|
&ConfigureSystem::RefreshConsoleID);
|
||||||
connect(ui->button_start_download, &QPushButton::clicked, this,
|
connect(ui->button_start_download, &QPushButton::clicked, this,
|
||||||
&ConfigureSystem::DownloadFromNUS);
|
&ConfigureSystem::DownloadFromNUS);
|
||||||
|
|
||||||
|
connect(ui->button_secure_info, &QPushButton::clicked, this, [this] {
|
||||||
|
ui->button_secure_info->setEnabled(false);
|
||||||
|
const QString file_path_qtstr = QFileDialog::getOpenFileName(
|
||||||
|
this, tr("Select SecureInfo_A/B"), QString(),
|
||||||
|
tr("SecureInfo_A/B (SecureInfo_A SecureInfo_B);;All Files (*.*)"));
|
||||||
|
ui->button_secure_info->setEnabled(true);
|
||||||
|
InstallSecureData(file_path_qtstr.toStdString(), cfg->GetSecureInfoAPath());
|
||||||
|
});
|
||||||
|
connect(ui->button_friend_code_seed, &QPushButton::clicked, this, [this] {
|
||||||
|
ui->button_friend_code_seed->setEnabled(false);
|
||||||
|
const QString file_path_qtstr =
|
||||||
|
QFileDialog::getOpenFileName(this, tr("Select LocalFriendCodeSeed_A/B"), QString(),
|
||||||
|
tr("LocalFriendCodeSeed_A/B (LocalFriendCodeSeed_A "
|
||||||
|
"LocalFriendCodeSeed_B);;All Files (*.*)"));
|
||||||
|
ui->button_friend_code_seed->setEnabled(true);
|
||||||
|
InstallSecureData(file_path_qtstr.toStdString(), cfg->GetLocalFriendCodeSeedBPath());
|
||||||
|
});
|
||||||
|
connect(ui->button_ct_cert, &QPushButton::clicked, this, [this] {
|
||||||
|
ui->button_ct_cert->setEnabled(false);
|
||||||
|
const QString file_path_qtstr = QFileDialog::getOpenFileName(
|
||||||
|
this, tr("Select CTCert"), QString(), tr("CTCert.bin (*.bin);;All Files (*.*)"));
|
||||||
|
ui->button_ct_cert->setEnabled(true);
|
||||||
|
InstallCTCert(file_path_qtstr.toStdString());
|
||||||
|
});
|
||||||
|
|
||||||
for (u8 i = 0; i < country_names.size(); i++) {
|
for (u8 i = 0; i < country_names.size(); i++) {
|
||||||
if (std::strcmp(country_names.at(i), "") != 0) {
|
if (std::strcmp(country_names.at(i), "") != 0) {
|
||||||
ui->combo_country->addItem(tr(country_names.at(i)), i);
|
ui->combo_country->addItem(tr(country_names.at(i)), i);
|
||||||
@ -304,6 +332,7 @@ void ConfigureSystem::SetConfiguration() {
|
|||||||
ReadSystemSettings();
|
ReadSystemSettings();
|
||||||
|
|
||||||
ui->group_system_settings->setEnabled(enabled);
|
ui->group_system_settings->setEnabled(enabled);
|
||||||
|
ui->group_real_console_unique_data->setEnabled(enabled);
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
ui->label_disable_info->hide();
|
ui->label_disable_info->hide();
|
||||||
}
|
}
|
||||||
@ -354,6 +383,9 @@ void ConfigureSystem::ReadSystemSettings() {
|
|||||||
|
|
||||||
// set firmware download region
|
// set firmware download region
|
||||||
ui->combo_download_region->setCurrentIndex(static_cast<int>(cfg->GetRegionValue()));
|
ui->combo_download_region->setCurrentIndex(static_cast<int>(cfg->GetRegionValue()));
|
||||||
|
|
||||||
|
// Refresh secure data status
|
||||||
|
RefreshSecureDataStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureSystem::ApplyConfiguration() {
|
void ConfigureSystem::ApplyConfiguration() {
|
||||||
@ -522,6 +554,59 @@ void ConfigureSystem::RefreshConsoleID() {
|
|||||||
tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper()));
|
tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigureSystem::InstallSecureData(const std::string& from_path, const std::string& to_path) {
|
||||||
|
std::string from =
|
||||||
|
FileUtil::SanitizePath(from_path, FileUtil::DirectorySeparator::PlatformDefault);
|
||||||
|
std::string to = FileUtil::SanitizePath(to_path, FileUtil::DirectorySeparator::PlatformDefault);
|
||||||
|
if (from.empty() || from == to) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FileUtil::CreateFullPath(to_path);
|
||||||
|
FileUtil::Copy(from, to);
|
||||||
|
cfg->InvalidateSecureData();
|
||||||
|
RefreshSecureDataStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureSystem::InstallCTCert(const std::string& from_path) {
|
||||||
|
std::string from =
|
||||||
|
FileUtil::SanitizePath(from_path, FileUtil::DirectorySeparator::PlatformDefault);
|
||||||
|
std::string to = FileUtil::SanitizePath(Service::AM::Module::GetCTCertPath(),
|
||||||
|
FileUtil::DirectorySeparator::PlatformDefault);
|
||||||
|
if (from.empty() || from == to) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FileUtil::Copy(from, to);
|
||||||
|
RefreshSecureDataStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureSystem::RefreshSecureDataStatus() {
|
||||||
|
auto status_to_str = [](Service::CFG::SecureDataLoadStatus status) {
|
||||||
|
switch (status) {
|
||||||
|
case Service::CFG::SecureDataLoadStatus::Loaded:
|
||||||
|
return "Loaded";
|
||||||
|
case Service::CFG::SecureDataLoadStatus::NotFound:
|
||||||
|
return "Not Found";
|
||||||
|
case Service::CFG::SecureDataLoadStatus::Invalid:
|
||||||
|
return "Invalid";
|
||||||
|
case Service::CFG::SecureDataLoadStatus::IOError:
|
||||||
|
return "IO Error";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Service::AM::CTCert ct_cert;
|
||||||
|
|
||||||
|
ui->label_secure_info_status->setText(
|
||||||
|
tr((std::string("Status: ") + status_to_str(cfg->LoadSecureInfoAFile())).c_str()));
|
||||||
|
ui->label_friend_code_seed_status->setText(
|
||||||
|
tr((std::string("Status: ") + status_to_str(cfg->LoadLocalFriendCodeSeedBFile())).c_str()));
|
||||||
|
ui->label_ct_cert_status->setText(
|
||||||
|
tr((std::string("Status: ") + status_to_str(static_cast<Service::CFG::SecureDataLoadStatus>(
|
||||||
|
Service::AM::Module::LoadCTCertFile(ct_cert))))
|
||||||
|
.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
void ConfigureSystem::RetranslateUI() {
|
void ConfigureSystem::RetranslateUI() {
|
||||||
ui->retranslateUi(this);
|
ui->retranslateUi(this);
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,12 @@ namespace Core {
|
|||||||
class System;
|
class System;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Service {
|
||||||
|
namespace AM {
|
||||||
|
class Module;
|
||||||
|
} // namespace AM
|
||||||
|
} // namespace Service
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
namespace CFG {
|
namespace CFG {
|
||||||
class Module;
|
class Module;
|
||||||
@ -46,6 +52,10 @@ private:
|
|||||||
void UpdateInitTicks(int init_ticks_type);
|
void UpdateInitTicks(int init_ticks_type);
|
||||||
void RefreshConsoleID();
|
void RefreshConsoleID();
|
||||||
|
|
||||||
|
void InstallSecureData(const std::string& from_path, const std::string& to_path);
|
||||||
|
void InstallCTCert(const std::string& from_path);
|
||||||
|
void RefreshSecureDataStatus();
|
||||||
|
|
||||||
void SetupPerGameUI();
|
void SetupPerGameUI();
|
||||||
|
|
||||||
void DownloadFromNUS();
|
void DownloadFromNUS();
|
||||||
|
@ -506,6 +506,123 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="group_real_console_unique_data">
|
||||||
|
<property name="title">
|
||||||
|
<string>Real Console Unique Data</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout1">
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_secure_info">
|
||||||
|
<property name="text">
|
||||||
|
<string>SecureInfo_A/B</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QWidget" name="secure_info">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_secure_info">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_secure_info_status">
|
||||||
|
<property name="text">
|
||||||
|
<string></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="button_secure_info">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="layoutDirection">
|
||||||
|
<enum>Qt::RightToLeft</enum>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Choose</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_friend_code_seed">
|
||||||
|
<property name="text">
|
||||||
|
<string>LocalFriendCodeSeed_A/B</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QWidget" name="friend_code_seed">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_friend_code_seed">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_friend_code_seed_status">
|
||||||
|
<property name="text">
|
||||||
|
<string></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="button_friend_code_seed">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="layoutDirection">
|
||||||
|
<enum>Qt::RightToLeft</enum>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Choose</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_ct_cert">
|
||||||
|
<property name="text">
|
||||||
|
<string>CTCert</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QWidget" name="ct_cert">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_ct_cert">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_ct_cert_status">
|
||||||
|
<property name="text">
|
||||||
|
<string></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="button_ct_cert">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="layoutDirection">
|
||||||
|
<enum>Qt::RightToLeft</enum>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Choose</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_disable_info">
|
<widget class="QLabel" name="label_disable_info">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -80,6 +80,35 @@ struct TicketInfo {
|
|||||||
|
|
||||||
static_assert(sizeof(TicketInfo) == 0x18, "Ticket info structure size is wrong");
|
static_assert(sizeof(TicketInfo) == 0x18, "Ticket info structure size is wrong");
|
||||||
|
|
||||||
|
bool CTCert::IsValid() const {
|
||||||
|
constexpr std::string_view expected_issuer_prod = "Nintendo CA - G3_NintendoCTR2prod";
|
||||||
|
constexpr std::string_view expected_issuer_dev = "Nintendo CA - G3_NintendoCTR2dev";
|
||||||
|
constexpr u32 expected_signature_type = 0x010005;
|
||||||
|
|
||||||
|
return signature_type == expected_signature_type &&
|
||||||
|
(std::string(issuer.data()) == expected_issuer_prod ||
|
||||||
|
std::string(issuer.data()) == expected_issuer_dev);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 CTCert::GetDeviceID() const {
|
||||||
|
constexpr std::string_view key_id_prefix = "CT";
|
||||||
|
|
||||||
|
const std::string key_id_str(key_id.data());
|
||||||
|
if (key_id_str.starts_with(key_id_prefix)) {
|
||||||
|
const std::string device_id =
|
||||||
|
key_id_str.substr(key_id_prefix.size(), key_id_str.find('-') - key_id_prefix.size());
|
||||||
|
char* end_ptr;
|
||||||
|
const u32 device_id_value = std::strtoul(device_id.c_str(), &end_ptr, 16);
|
||||||
|
if (*end_ptr == '\0') {
|
||||||
|
return device_id_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Error
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
class CIAFile::DecryptionState {
|
class CIAFile::DecryptionState {
|
||||||
public:
|
public:
|
||||||
std::vector<CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption> content;
|
std::vector<CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption> content;
|
||||||
@ -1213,6 +1242,21 @@ void Module::Interface::GetTicketList(Kernel::HLERequestContext& ctx) {
|
|||||||
ticket_list_count, ticket_index);
|
ticket_list_count, ticket_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Module::Interface::GetDeviceID(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
|
||||||
|
const u32 deviceID = am->ct_cert.IsValid() ? am->ct_cert.GetDeviceID() : 0;
|
||||||
|
|
||||||
|
if (deviceID == 0) {
|
||||||
|
LOG_ERROR(Service_AM, "Invalid or missing CTCert");
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(3, 0);
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push(0);
|
||||||
|
rb.Push(deviceID);
|
||||||
|
}
|
||||||
|
|
||||||
void Module::Interface::NeedsCleanup(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::NeedsCleanup(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
const auto media_type = rp.Pop<u8>();
|
const auto media_type = rp.Pop<u8>();
|
||||||
@ -1802,13 +1846,66 @@ void Module::serialize(Archive& ar, const unsigned int) {
|
|||||||
}
|
}
|
||||||
SERIALIZE_IMPL(Module)
|
SERIALIZE_IMPL(Module)
|
||||||
|
|
||||||
Module::Module(Core::System& system) : system(system) {
|
void Module::Interface::GetDeviceCert(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
[[maybe_unused]] u32 size = rp.Pop<u32>();
|
||||||
|
auto buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
if (!am->ct_cert.IsValid()) {
|
||||||
|
LOG_ERROR(Service_AM, "Invalid or missing CTCert");
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.Write(&am->ct_cert, 0, std::min(sizeof(CTCert), buffer.GetSize()));
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push(0);
|
||||||
|
rb.PushMappedBuffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Module::GetCTCertPath() {
|
||||||
|
return FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + "CTCert.bin";
|
||||||
|
}
|
||||||
|
|
||||||
|
CTCertLoadStatus Module::LoadCTCertFile(CTCert& output) {
|
||||||
|
if (output.IsValid()) {
|
||||||
|
return CTCertLoadStatus::Loaded;
|
||||||
|
}
|
||||||
|
std::string file_path = GetCTCertPath();
|
||||||
|
if (!FileUtil::Exists(file_path)) {
|
||||||
|
return CTCertLoadStatus::NotFound;
|
||||||
|
}
|
||||||
|
FileUtil::IOFile file(file_path, "rb");
|
||||||
|
if (!file.IsOpen()) {
|
||||||
|
return CTCertLoadStatus::IOError;
|
||||||
|
}
|
||||||
|
if (file.GetSize() != sizeof(CTCert)) {
|
||||||
|
return CTCertLoadStatus::Invalid;
|
||||||
|
}
|
||||||
|
if (file.ReadBytes(&output, sizeof(CTCert)) != sizeof(CTCert)) {
|
||||||
|
return CTCertLoadStatus::IOError;
|
||||||
|
}
|
||||||
|
if (!output.IsValid()) {
|
||||||
|
output = CTCert();
|
||||||
|
return CTCertLoadStatus::Invalid;
|
||||||
|
}
|
||||||
|
return CTCertLoadStatus::Loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
Module::Module(Core::System& _system) : system(_system) {
|
||||||
ScanForAllTitles();
|
ScanForAllTitles();
|
||||||
|
LoadCTCertFile(ct_cert);
|
||||||
system_updater_mutex = system.Kernel().CreateMutex(false, "AM::SystemUpdaterMutex");
|
system_updater_mutex = system.Kernel().CreateMutex(false, "AM::SystemUpdaterMutex");
|
||||||
}
|
}
|
||||||
|
|
||||||
Module::~Module() = default;
|
Module::~Module() = default;
|
||||||
|
|
||||||
|
std::shared_ptr<Module> GetModule(Core::System& system) {
|
||||||
|
auto am = system.ServiceManager().GetService<Service::AM::Module::Interface>("am:u");
|
||||||
|
if (!am)
|
||||||
|
return nullptr;
|
||||||
|
return am->GetModule();
|
||||||
|
}
|
||||||
|
|
||||||
void InstallInterfaces(Core::System& system) {
|
void InstallInterfaces(Core::System& system) {
|
||||||
auto& service_manager = system.ServiceManager();
|
auto& service_manager = system.ServiceManager();
|
||||||
auto am = std::make_shared<Module>(system);
|
auto am = std::make_shared<Module>(system);
|
||||||
|
@ -68,6 +68,31 @@ enum class InstallStatus : u32 {
|
|||||||
ErrorEncrypted,
|
ErrorEncrypted,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class CTCertLoadStatus {
|
||||||
|
Loaded,
|
||||||
|
NotFound,
|
||||||
|
Invalid,
|
||||||
|
IOError,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CTCert {
|
||||||
|
u32_be signature_type{};
|
||||||
|
std::array<u8, 0x1E> signature_r{};
|
||||||
|
std::array<u8, 0x1E> signature_s{};
|
||||||
|
INSERT_PADDING_BYTES(0x40){};
|
||||||
|
std::array<char, 0x40> issuer{};
|
||||||
|
u32_be key_type{};
|
||||||
|
std::array<char, 0x40> key_id{};
|
||||||
|
u32_be expiration_time{};
|
||||||
|
std::array<u8, 0x1E> public_key_x{};
|
||||||
|
std::array<u8, 0x1E> public_key_y{};
|
||||||
|
INSERT_PADDING_BYTES(0x3C){};
|
||||||
|
|
||||||
|
bool IsValid() const;
|
||||||
|
u32 GetDeviceID() const;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(CTCert) == 0x180, "Invalid CTCert size.");
|
||||||
|
|
||||||
// Title ID valid length
|
// Title ID valid length
|
||||||
constexpr std::size_t TITLE_ID_VALID_LENGTH = 16;
|
constexpr std::size_t TITLE_ID_VALID_LENGTH = 16;
|
||||||
|
|
||||||
@ -216,6 +241,10 @@ public:
|
|||||||
Interface(std::shared_ptr<Module> am, const char* name, u32 max_session);
|
Interface(std::shared_ptr<Module> am, const char* name, u32 max_session);
|
||||||
~Interface();
|
~Interface();
|
||||||
|
|
||||||
|
std::shared_ptr<Module> GetModule() const {
|
||||||
|
return am;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* AM::GetNumPrograms service function
|
* AM::GetNumPrograms service function
|
||||||
@ -415,6 +444,16 @@ public:
|
|||||||
*/
|
*/
|
||||||
void GetTicketList(Kernel::HLERequestContext& ctx);
|
void GetTicketList(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AM::GetDeviceID service function
|
||||||
|
* Inputs:
|
||||||
|
* Outputs:
|
||||||
|
* 1 : Result, 0 on success, otherwise error code
|
||||||
|
* 2 : Unknown
|
||||||
|
* 3 : DeviceID
|
||||||
|
*/
|
||||||
|
void GetDeviceID(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AM::NeedsCleanup service function
|
* AM::NeedsCleanup service function
|
||||||
* Inputs:
|
* Inputs:
|
||||||
@ -702,10 +741,32 @@ public:
|
|||||||
*/
|
*/
|
||||||
void EndImportTicket(Kernel::HLERequestContext& ctx);
|
void EndImportTicket(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AM::GetDeviceCert service function
|
||||||
|
* Inputs:
|
||||||
|
* Outputs:
|
||||||
|
* 1 : Result, 0 on success, otherwise error code
|
||||||
|
* 2 : Unknown
|
||||||
|
* 3-4 : Device cert
|
||||||
|
*/
|
||||||
|
void GetDeviceCert(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<Module> am;
|
std::shared_ptr<Module> am;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the CTCert.bin path in the host filesystem
|
||||||
|
* @returns std::string CTCert.bin path in the host filesystem
|
||||||
|
*/
|
||||||
|
static std::string GetCTCertPath();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the CTCert.bin file from the filesystem.
|
||||||
|
* @returns CTCertLoadStatus indicating the file load status.
|
||||||
|
*/
|
||||||
|
static CTCertLoadStatus LoadCTCertFile(CTCert& output);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Scans the for titles in a storage medium for listing.
|
* Scans the for titles in a storage medium for listing.
|
||||||
@ -722,12 +783,15 @@ private:
|
|||||||
bool cia_installing = false;
|
bool cia_installing = false;
|
||||||
std::array<std::vector<u64_le>, 3> am_title_list;
|
std::array<std::vector<u64_le>, 3> am_title_list;
|
||||||
std::shared_ptr<Kernel::Mutex> system_updater_mutex;
|
std::shared_ptr<Kernel::Mutex> system_updater_mutex;
|
||||||
|
CTCert ct_cert{};
|
||||||
|
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
void serialize(Archive& ar, const unsigned int);
|
void serialize(Archive& ar, const unsigned int);
|
||||||
friend class boost::serialization::access;
|
friend class boost::serialization::access;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<Module> GetModule(Core::System& system);
|
||||||
|
|
||||||
void InstallInterfaces(Core::System& system);
|
void InstallInterfaces(Core::System& system);
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
@ -19,7 +19,7 @@ AM_NET::AM_NET(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "a
|
|||||||
{0x0007, &AM_NET::DeleteTicket, "DeleteTicket"},
|
{0x0007, &AM_NET::DeleteTicket, "DeleteTicket"},
|
||||||
{0x0008, &AM_NET::GetNumTickets, "GetNumTickets"},
|
{0x0008, &AM_NET::GetNumTickets, "GetNumTickets"},
|
||||||
{0x0009, &AM_NET::GetTicketList, "GetTicketList"},
|
{0x0009, &AM_NET::GetTicketList, "GetTicketList"},
|
||||||
{0x000A, nullptr, "GetDeviceID"},
|
{0x000A, &AM_NET::GetDeviceID, "GetDeviceID"},
|
||||||
{0x000B, nullptr, "GetNumImportTitleContexts"},
|
{0x000B, nullptr, "GetNumImportTitleContexts"},
|
||||||
{0x000C, nullptr, "GetImportTitleContextList"},
|
{0x000C, nullptr, "GetImportTitleContextList"},
|
||||||
{0x000D, nullptr, "GetImportTitleContexts"},
|
{0x000D, nullptr, "GetImportTitleContexts"},
|
||||||
@ -103,7 +103,7 @@ AM_NET::AM_NET(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "a
|
|||||||
{0x0815, nullptr, "GetCurrentImportContentContexts"},
|
{0x0815, nullptr, "GetCurrentImportContentContexts"},
|
||||||
{0x0816, nullptr, "Sign"},
|
{0x0816, nullptr, "Sign"},
|
||||||
{0x0817, nullptr, "Verify"},
|
{0x0817, nullptr, "Verify"},
|
||||||
{0x0818, nullptr, "GetDeviceCert"},
|
{0x0818, &AM_NET::GetDeviceCert, "GetDeviceCert"},
|
||||||
{0x0819, nullptr, "ImportCertificates"},
|
{0x0819, nullptr, "ImportCertificates"},
|
||||||
{0x081A, nullptr, "ImportCertificate"},
|
{0x081A, nullptr, "ImportCertificate"},
|
||||||
{0x081B, nullptr, "CommitImportTitlesAndUpdateFirmwareAuto"},
|
{0x081B, nullptr, "CommitImportTitlesAndUpdateFirmwareAuto"},
|
||||||
|
@ -19,7 +19,7 @@ AM_SYS::AM_SYS(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "a
|
|||||||
{0x0007, &AM_SYS::DeleteTicket, "DeleteTicket"},
|
{0x0007, &AM_SYS::DeleteTicket, "DeleteTicket"},
|
||||||
{0x0008, &AM_SYS::GetNumTickets, "GetNumTickets"},
|
{0x0008, &AM_SYS::GetNumTickets, "GetNumTickets"},
|
||||||
{0x0009, &AM_SYS::GetTicketList, "GetTicketList"},
|
{0x0009, &AM_SYS::GetTicketList, "GetTicketList"},
|
||||||
{0x000A, nullptr, "GetDeviceID"},
|
{0x000A, &AM_SYS::GetDeviceID, "GetDeviceID"},
|
||||||
{0x000B, nullptr, "GetNumImportTitleContexts"},
|
{0x000B, nullptr, "GetNumImportTitleContexts"},
|
||||||
{0x000C, nullptr, "GetImportTitleContextList"},
|
{0x000C, nullptr, "GetImportTitleContextList"},
|
||||||
{0x000D, nullptr, "GetImportTitleContexts"},
|
{0x000D, nullptr, "GetImportTitleContexts"},
|
||||||
|
@ -19,7 +19,7 @@ AM_U::AM_U(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "am:u"
|
|||||||
{0x0007, &AM_U::DeleteTicket, "DeleteTicket"},
|
{0x0007, &AM_U::DeleteTicket, "DeleteTicket"},
|
||||||
{0x0008, &AM_U::GetNumTickets, "GetNumTickets"},
|
{0x0008, &AM_U::GetNumTickets, "GetNumTickets"},
|
||||||
{0x0009, &AM_U::GetTicketList, "GetTicketList"},
|
{0x0009, &AM_U::GetTicketList, "GetTicketList"},
|
||||||
{0x000A, nullptr, "GetDeviceID"},
|
{0x000A, &AM_U::GetDeviceID, "GetDeviceID"},
|
||||||
{0x000B, nullptr, "GetNumImportTitleContexts"},
|
{0x000B, nullptr, "GetNumImportTitleContexts"},
|
||||||
{0x000C, nullptr, "GetImportTitleContextList"},
|
{0x000C, nullptr, "GetImportTitleContextList"},
|
||||||
{0x000D, nullptr, "GetImportTitleContexts"},
|
{0x000D, nullptr, "GetImportTitleContexts"},
|
||||||
|
@ -217,12 +217,39 @@ void Module::Interface::GetRegion(Kernel::HLERequestContext& ctx) {
|
|||||||
void Module::Interface::SecureInfoGetByte101(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::SecureInfoGetByte101(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
|
|
||||||
LOG_DEBUG(Service_CFG, "(STUBBED) called");
|
u8 ret = 0;
|
||||||
|
if (cfg->secure_info_a_loaded) {
|
||||||
|
ret = cfg->secure_info_a.unknown;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
// According to 3dbrew this is normally 0.
|
rb.Push<u8>(ret);
|
||||||
rb.Push<u8>(0);
|
}
|
||||||
|
|
||||||
|
void Module::Interface::SecureInfoGetSerialNo(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
[[maybe_unused]] u32 out_size = rp.Pop<u32>();
|
||||||
|
auto out_buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
|
if (out_buffer.GetSize() < sizeof(SecureInfoA::serial_number)) {
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(Result(ErrorDescription::InvalidSize, ErrorModule::Config,
|
||||||
|
ErrorSummary::WrongArgument, ErrorLevel::Permanent));
|
||||||
|
}
|
||||||
|
// Never happens on real hardware, but may happen if user didn't supply a dump.
|
||||||
|
// Always make sure to have available both secure data kinds or error otherwise.
|
||||||
|
if (!cfg->secure_info_a_loaded || !cfg->local_friend_code_seed_b_loaded) {
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(Result(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::InvalidState,
|
||||||
|
ErrorLevel::Permanent));
|
||||||
|
}
|
||||||
|
|
||||||
|
out_buffer.Write(&cfg->secure_info_a.serial_number, 0, sizeof(SecureInfoA::serial_number));
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.PushMappedBuffer(out_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::SetUUIDClockSequence(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::SetUUIDClockSequence(Kernel::HLERequestContext& ctx) {
|
||||||
@ -365,6 +392,43 @@ void Module::Interface::UpdateConfigNANDSavegame(Kernel::HLERequestContext& ctx)
|
|||||||
rb.Push(cfg->UpdateConfigNANDSavegame());
|
rb.Push(cfg->UpdateConfigNANDSavegame());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Module::Interface::GetLocalFriendCodeSeedData(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
[[maybe_unused]] u32 out_size = rp.Pop<u32>();
|
||||||
|
auto out_buffer = rp.PopMappedBuffer();
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
|
||||||
|
if (out_buffer.GetSize() < sizeof(LocalFriendCodeSeedB)) {
|
||||||
|
rb.Push(Result(ErrorDescription::InvalidSize, ErrorModule::Config,
|
||||||
|
ErrorSummary::WrongArgument, ErrorLevel::Permanent));
|
||||||
|
}
|
||||||
|
// Never happens on real hardware, but may happen if user didn't supply a dump.
|
||||||
|
// Always make sure to have available both secure data kinds or error otherwise.
|
||||||
|
if (!cfg->secure_info_a_loaded || !cfg->local_friend_code_seed_b_loaded) {
|
||||||
|
rb.Push(Result(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::InvalidState,
|
||||||
|
ErrorLevel::Permanent));
|
||||||
|
}
|
||||||
|
|
||||||
|
out_buffer.Write(&cfg->local_friend_code_seed_b, 0, sizeof(LocalFriendCodeSeedB));
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::GetLocalFriendCodeSeed(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx);
|
||||||
|
|
||||||
|
// Never happens on real hardware, but may happen if user didn't supply a dump.
|
||||||
|
// Always make sure to have available both secure data kinds or error otherwise.
|
||||||
|
if (!cfg->secure_info_a_loaded || !cfg->local_friend_code_seed_b_loaded) {
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(Result(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::InvalidState,
|
||||||
|
ErrorLevel::Permanent));
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(3, 0);
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push<u64>(cfg->local_friend_code_seed_b.friend_code_seed);
|
||||||
|
}
|
||||||
|
|
||||||
void Module::Interface::FormatConfig(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::FormatConfig(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
@ -506,6 +570,14 @@ Result Module::UpdateConfigNANDSavegame() {
|
|||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Module::GetLocalFriendCodeSeedBPath() {
|
||||||
|
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "rw/sys/LocalFriendCodeSeed_B";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Module::GetSecureInfoAPath() {
|
||||||
|
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "rw/sys/SecureInfo_A";
|
||||||
|
}
|
||||||
|
|
||||||
Result Module::FormatConfig() {
|
Result Module::FormatConfig() {
|
||||||
Result res = DeleteConfigNANDSaveFile();
|
Result res = DeleteConfigNANDSaveFile();
|
||||||
// The delete command fails if the file doesn't exist, so we have to check that too
|
// The delete command fails if the file doesn't exist, so we have to check that too
|
||||||
@ -579,6 +651,55 @@ Result Module::LoadConfigNANDSaveFile() {
|
|||||||
return FormatConfig();
|
return FormatConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Module::InvalidateSecureData() {
|
||||||
|
secure_info_a_loaded = local_friend_code_seed_b_loaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SecureDataLoadStatus Module::LoadSecureInfoAFile() {
|
||||||
|
if (secure_info_a_loaded) {
|
||||||
|
return SecureDataLoadStatus::Loaded;
|
||||||
|
}
|
||||||
|
std::string file_path = GetSecureInfoAPath();
|
||||||
|
if (!FileUtil::Exists(file_path)) {
|
||||||
|
return SecureDataLoadStatus::NotFound;
|
||||||
|
}
|
||||||
|
FileUtil::IOFile file(file_path, "rb");
|
||||||
|
if (!file.IsOpen()) {
|
||||||
|
return SecureDataLoadStatus::IOError;
|
||||||
|
}
|
||||||
|
if (file.GetSize() != sizeof(SecureInfoA)) {
|
||||||
|
return SecureDataLoadStatus::Invalid;
|
||||||
|
}
|
||||||
|
if (file.ReadBytes(&secure_info_a, sizeof(SecureInfoA)) != sizeof(SecureInfoA)) {
|
||||||
|
return SecureDataLoadStatus::IOError;
|
||||||
|
}
|
||||||
|
secure_info_a_loaded = true;
|
||||||
|
return SecureDataLoadStatus::Loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
SecureDataLoadStatus Module::LoadLocalFriendCodeSeedBFile() {
|
||||||
|
if (local_friend_code_seed_b_loaded) {
|
||||||
|
return SecureDataLoadStatus::Loaded;
|
||||||
|
}
|
||||||
|
std::string file_path = GetLocalFriendCodeSeedBPath();
|
||||||
|
if (!FileUtil::Exists(file_path)) {
|
||||||
|
return SecureDataLoadStatus::NotFound;
|
||||||
|
}
|
||||||
|
FileUtil::IOFile file(file_path, "rb");
|
||||||
|
if (!file.IsOpen()) {
|
||||||
|
return SecureDataLoadStatus::IOError;
|
||||||
|
}
|
||||||
|
if (file.GetSize() != sizeof(LocalFriendCodeSeedB)) {
|
||||||
|
return SecureDataLoadStatus::Invalid;
|
||||||
|
}
|
||||||
|
if (file.ReadBytes(&local_friend_code_seed_b, sizeof(LocalFriendCodeSeedB)) !=
|
||||||
|
sizeof(LocalFriendCodeSeedB)) {
|
||||||
|
return SecureDataLoadStatus::IOError;
|
||||||
|
}
|
||||||
|
local_friend_code_seed_b_loaded = true;
|
||||||
|
return SecureDataLoadStatus::Loaded;
|
||||||
|
}
|
||||||
|
|
||||||
void Module::LoadMCUConfig() {
|
void Module::LoadMCUConfig() {
|
||||||
FileUtil::IOFile mcu_data_file(
|
FileUtil::IOFile mcu_data_file(
|
||||||
fmt::format("{}/mcu.dat", FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir)), "rb");
|
fmt::format("{}/mcu.dat", FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir)), "rb");
|
||||||
@ -616,6 +737,8 @@ Module::Module(Core::System& system_) : system(system_) {
|
|||||||
SetEULAVersion(default_version);
|
SetEULAVersion(default_version);
|
||||||
UpdateConfigNANDSavegame();
|
UpdateConfigNANDSavegame();
|
||||||
}
|
}
|
||||||
|
LoadSecureInfoAFile();
|
||||||
|
LoadLocalFriendCodeSeedBFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
Module::~Module() = default;
|
Module::~Module() = default;
|
||||||
|
@ -176,6 +176,28 @@ enum class AccessFlag : u16 {
|
|||||||
};
|
};
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(AccessFlag);
|
DECLARE_ENUM_FLAG_OPERATORS(AccessFlag);
|
||||||
|
|
||||||
|
struct SecureInfoA {
|
||||||
|
std::array<u8, 0x100> signature;
|
||||||
|
u8 region;
|
||||||
|
u8 unknown;
|
||||||
|
std::array<u8, 0xF> serial_number;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(SecureInfoA) == 0x111);
|
||||||
|
|
||||||
|
struct LocalFriendCodeSeedB {
|
||||||
|
std::array<u8, 0x100> signature;
|
||||||
|
u64 unknown;
|
||||||
|
u64 friend_code_seed;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(LocalFriendCodeSeedB) == 0x110);
|
||||||
|
|
||||||
|
enum class SecureDataLoadStatus {
|
||||||
|
Loaded,
|
||||||
|
NotFound,
|
||||||
|
Invalid,
|
||||||
|
IOError,
|
||||||
|
};
|
||||||
|
|
||||||
class Module final {
|
class Module final {
|
||||||
public:
|
public:
|
||||||
Module(Core::System& system_);
|
Module(Core::System& system_);
|
||||||
@ -230,6 +252,18 @@ public:
|
|||||||
*/
|
*/
|
||||||
void SecureInfoGetByte101(Kernel::HLERequestContext& ctx);
|
void SecureInfoGetByte101(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CFG::SecureInfoGetSerialNo service function
|
||||||
|
* Inputs:
|
||||||
|
* 1 : Buffer Size
|
||||||
|
* 2-3: Output mapped buffer
|
||||||
|
* Outputs:
|
||||||
|
* 0 : Result Header code
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
* 2-3 : Output mapped buffer
|
||||||
|
*/
|
||||||
|
void SecureInfoGetSerialNo(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CFG::SetUUIDClockSequence service function
|
* CFG::SetUUIDClockSequence service function
|
||||||
* Inputs:
|
* Inputs:
|
||||||
@ -345,6 +379,27 @@ public:
|
|||||||
*/
|
*/
|
||||||
void UpdateConfigNANDSavegame(Kernel::HLERequestContext& ctx);
|
void UpdateConfigNANDSavegame(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CFG::GetLocalFriendCodeSeedData service function
|
||||||
|
* Inputs:
|
||||||
|
* 1 : Buffer Size
|
||||||
|
* 2-3: Output mapped buffer
|
||||||
|
* Outputs:
|
||||||
|
* 0 : Result Header code
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void GetLocalFriendCodeSeedData(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CFG::GetLocalFriendCodeSeed service function
|
||||||
|
* Inputs:
|
||||||
|
* Outputs:
|
||||||
|
* 0 : Result Header code
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
* 2-3 : Friend code seed
|
||||||
|
*/
|
||||||
|
void GetLocalFriendCodeSeed(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CFG::FormatConfig service function
|
* CFG::FormatConfig service function
|
||||||
* Inputs:
|
* Inputs:
|
||||||
@ -575,6 +630,34 @@ public:
|
|||||||
*/
|
*/
|
||||||
Result UpdateConfigNANDSavegame();
|
Result UpdateConfigNANDSavegame();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates the loaded secure data so that it is loaded again.
|
||||||
|
*/
|
||||||
|
void InvalidateSecureData();
|
||||||
|
/**
|
||||||
|
* Loads the LocalFriendCodeSeed_B file from NAND.
|
||||||
|
* @returns LocalFriendCodeSeedBLoadStatus indicating the file load status.
|
||||||
|
*/
|
||||||
|
SecureDataLoadStatus LoadSecureInfoAFile();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the LocalFriendCodeSeed_B file from NAND.
|
||||||
|
* @returns LocalFriendCodeSeedBLoadStatus indicating the file load status.
|
||||||
|
*/
|
||||||
|
SecureDataLoadStatus LoadLocalFriendCodeSeedBFile();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the SecureInfo_A path in the host filesystem
|
||||||
|
* @returns std::string SecureInfo_A path in the host filesystem
|
||||||
|
*/
|
||||||
|
std::string GetSecureInfoAPath();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the LocalFriendCodeSeed_B path in the host filesystem
|
||||||
|
* @returns std::string LocalFriendCodeSeed_B path in the host filesystem
|
||||||
|
*/
|
||||||
|
std::string GetLocalFriendCodeSeedBPath();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves MCU specific data
|
* Saves MCU specific data
|
||||||
*/
|
*/
|
||||||
@ -590,6 +673,10 @@ private:
|
|||||||
std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer;
|
std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer;
|
||||||
std::unique_ptr<FileSys::ArchiveBackend> cfg_system_save_data_archive;
|
std::unique_ptr<FileSys::ArchiveBackend> cfg_system_save_data_archive;
|
||||||
u32 preferred_region_code = 0;
|
u32 preferred_region_code = 0;
|
||||||
|
bool secure_info_a_loaded = false;
|
||||||
|
SecureInfoA secure_info_a;
|
||||||
|
bool local_friend_code_seed_b_loaded = false;
|
||||||
|
LocalFriendCodeSeedB local_friend_code_seed_b;
|
||||||
bool preferred_region_chosen = false;
|
bool preferred_region_chosen = false;
|
||||||
MCUData mcu_data{};
|
MCUData mcu_data{};
|
||||||
|
|
||||||
|
@ -28,11 +28,11 @@ CFG_I::CFG_I(std::shared_ptr<Module> cfg) : Module::Interface(std::move(cfg), "c
|
|||||||
{0x0401, &CFG_I::GetSystemConfig, "GetSystemConfig"},
|
{0x0401, &CFG_I::GetSystemConfig, "GetSystemConfig"},
|
||||||
{0x0402, &CFG_I::SetSystemConfig, "SetSystemConfig"},
|
{0x0402, &CFG_I::SetSystemConfig, "SetSystemConfig"},
|
||||||
{0x0403, &CFG_I::UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
|
{0x0403, &CFG_I::UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
|
||||||
{0x0404, nullptr, "GetLocalFriendCodeSeedData"},
|
{0x0404, &CFG_I::GetLocalFriendCodeSeedData, "GetLocalFriendCodeSeedData"},
|
||||||
{0x0405, nullptr, "GetLocalFriendCodeSeed"},
|
{0x0405, &CFG_I::GetLocalFriendCodeSeed, "GetLocalFriendCodeSeed"},
|
||||||
{0x0406, &CFG_I::GetRegion, "GetRegion"},
|
{0x0406, &CFG_I::GetRegion, "GetRegion"},
|
||||||
{0x0407, &CFG_I::SecureInfoGetByte101, "SecureInfoGetByte101"},
|
{0x0407, &CFG_I::SecureInfoGetByte101, "SecureInfoGetByte101"},
|
||||||
{0x0408, nullptr, "SecureInfoGetSerialNo"},
|
{0x0408, &CFG_I::SecureInfoGetSerialNo, "SecureInfoGetSerialNo"},
|
||||||
{0x0409, nullptr, "UpdateConfigBlk00040003"},
|
{0x0409, nullptr, "UpdateConfigBlk00040003"},
|
||||||
// cfg:i
|
// cfg:i
|
||||||
{0x0801, &CFG_I::GetSystemConfig, "GetSystemConfig"},
|
{0x0801, &CFG_I::GetSystemConfig, "GetSystemConfig"},
|
||||||
|
@ -28,11 +28,11 @@ CFG_S::CFG_S(std::shared_ptr<Module> cfg) : Module::Interface(std::move(cfg), "c
|
|||||||
{0x0401, &CFG_S::GetSystemConfig, "GetSystemConfig"},
|
{0x0401, &CFG_S::GetSystemConfig, "GetSystemConfig"},
|
||||||
{0x0402, &CFG_S::SetSystemConfig, "SetSystemConfig"},
|
{0x0402, &CFG_S::SetSystemConfig, "SetSystemConfig"},
|
||||||
{0x0403, &CFG_S::UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
|
{0x0403, &CFG_S::UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
|
||||||
{0x0404, nullptr, "GetLocalFriendCodeSeedData"},
|
{0x0404, &CFG_S::GetLocalFriendCodeSeedData, "GetLocalFriendCodeSeedData"},
|
||||||
{0x0405, nullptr, "GetLocalFriendCodeSeed"},
|
{0x0405, &CFG_S::GetLocalFriendCodeSeed, "GetLocalFriendCodeSeed"},
|
||||||
{0x0406, &CFG_S::GetRegion, "GetRegion"},
|
{0x0406, &CFG_S::GetRegion, "GetRegion"},
|
||||||
{0x0407, &CFG_S::SecureInfoGetByte101, "SecureInfoGetByte101"},
|
{0x0407, &CFG_S::SecureInfoGetByte101, "SecureInfoGetByte101"},
|
||||||
{0x0408, nullptr, "SecureInfoGetSerialNo"},
|
{0x0408, &CFG_S::SecureInfoGetSerialNo, "SecureInfoGetSerialNo"},
|
||||||
{0x0409, nullptr, "UpdateConfigBlk00040003"},
|
{0x0409, nullptr, "UpdateConfigBlk00040003"},
|
||||||
{0x040D, &CFG_S::SetUUIDClockSequence, "SetUUIDClockSequence"},
|
{0x040D, &CFG_S::SetUUIDClockSequence, "SetUUIDClockSequence"},
|
||||||
{0x040E, &CFG_S::GetUUIDClockSequence, "GetUUIDClockSequence"},
|
{0x040E, &CFG_S::GetUUIDClockSequence, "GetUUIDClockSequence"},
|
||||||
|
Loading…
Reference in New Issue
Block a user